@elliots/typical 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,19 @@
1
+ export interface TypicalDebugConfig {
2
+ writeIntermediateFiles?: boolean;
3
+ }
1
4
  export interface TypicalConfig {
2
5
  include?: string[];
3
6
  exclude?: string[];
4
7
  reusableValidators?: boolean;
8
+ validateCasts?: boolean;
9
+ hoistRegex?: boolean;
10
+ debug?: TypicalDebugConfig;
11
+ /**
12
+ * Type patterns to skip validation for (supports wildcards).
13
+ * Use this for types that typia cannot process (e.g., React event types).
14
+ * Example: ["React.*", "Express.Request", "*.Event"]
15
+ */
16
+ ignoreTypes?: string[];
5
17
  }
6
18
  export declare const defaultConfig: TypicalConfig;
7
19
  export declare function loadConfig(configPath?: string): TypicalConfig;
@@ -2,6 +2,11 @@ export const defaultConfig = {
2
2
  include: ["**/*.ts", "**/*.tsx"],
3
3
  exclude: ["node_modules/**", "**/*.d.ts", "dist/**", "build/**"],
4
4
  reusableValidators: true,
5
+ validateCasts: false,
6
+ hoistRegex: true,
7
+ debug: {
8
+ writeIntermediateFiles: false,
9
+ },
5
10
  };
6
11
  import fs from 'fs';
7
12
  import path from 'path';
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAChC,OAAO,EAAE,CAAC,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;IAChE,kBAAkB,EAAE,IAAI;CACzB,CAAC;AAEF,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,UAAU,UAAU,CAAC,UAAmB;IAC5C,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAC;IAE1E,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC1D,MAAM,UAAU,GAA2B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAErE,OAAO;gBACL,GAAG,aAAa;gBAChB,GAAG,UAAU;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,+BAA+B,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;YAClE,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAChC,OAAO,EAAE,CAAC,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;IAChE,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE;QACL,sBAAsB,EAAE,KAAK;KAC9B;CACF,CAAC;AAEF,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,UAAU,UAAU,CAAC,UAAmB;IAC5C,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAC;IAE1E,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC1D,MAAM,UAAU,GAA2B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAErE,OAAO;gBACL,GAAG,aAAa;gBAChB,GAAG,UAAU;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,+BAA+B,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;YAClE,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC"}
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Resolve hook - rewrites .js imports to .ts if the .ts file exists
3
+ */
4
+ export declare function resolve(specifier: string, context: any, nextResolve: any): Promise<any>;
1
5
  /**
2
6
  * Load hook - transforms TypeScript files on the fly
3
7
  */
@@ -1,6 +1,28 @@
1
- import { fileURLToPath } from "url";
1
+ import { fileURLToPath, pathToFileURL } from "url";
2
+ import { existsSync } from "fs";
2
3
  import { TypicalTransformer } from "./transformer.js";
3
4
  const transformer = new TypicalTransformer();
5
+ /**
6
+ * Resolve hook - rewrites .js imports to .ts if the .ts file exists
7
+ */
8
+ export async function resolve(specifier, context, nextResolve) {
9
+ // Only handle relative imports ending in .js
10
+ if (specifier.startsWith('.') && specifier.endsWith('.js')) {
11
+ const { parentURL } = context;
12
+ if (parentURL) {
13
+ const parentPath = fileURLToPath(parentURL);
14
+ const dir = parentPath.substring(0, parentPath.lastIndexOf('/'));
15
+ const tsPath = dir + '/' + specifier.slice(0, -3) + '.ts';
16
+ if (existsSync(tsPath)) {
17
+ return {
18
+ url: pathToFileURL(tsPath).href,
19
+ shortCircuit: true,
20
+ };
21
+ }
22
+ }
23
+ }
24
+ return nextResolve(specifier, context);
25
+ }
4
26
  /**
5
27
  * Load hook - transforms TypeScript files on the fly
6
28
  */
@@ -1 +1 @@
1
- {"version":3,"file":"esm-loader.js","sourceRoot":"","sources":["../../src/esm-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,GAAG,IAAI,kBAAkB,EAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,GAAW,EAAE,OAAY,EAAE,QAAa;IACjE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9D,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,eAAe;YACvB,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACxD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"esm-loader.js","sourceRoot":"","sources":["../../src/esm-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,GAAG,IAAI,kBAAkB,EAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,SAAiB,EAAE,OAAY,EAAE,WAAgB;IAC7E,6CAA6C;IAC7C,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;QAC9B,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;YAE1D,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,OAAO;oBACL,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI;oBAC/B,YAAY,EAAE,IAAI;iBACnB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,GAAW,EAAE,OAAY,EAAE,QAAa;IACjE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9D,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,eAAe;YACvB,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACxD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -5,6 +5,10 @@ import { minimatch } from 'minimatch';
5
5
  */
6
6
  export function shouldTransformFile(fileName, config) {
7
7
  const relativePath = path.relative(process.cwd(), fileName);
8
+ // Exclude files outside the project directory (e.g., resolved symlinks to parent dirs)
9
+ if (relativePath.startsWith('..')) {
10
+ return false;
11
+ }
8
12
  // Check include patterns
9
13
  const isIncluded = config.include?.some(pattern => {
10
14
  return minimatch(relativePath, pattern);
@@ -1 +1 @@
1
- {"version":3,"file":"file-filter.js","sourceRoot":"","sources":["../../src/file-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAqB;IACzE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAE5D,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAChD,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,IAAI,CAAC;IAEX,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAChD,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,KAAK,CAAC;IAEZ,OAAO,CAAC,UAAU,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAgB;IAC5D,kCAAkC;IAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhD,yBAAyB;IACzB,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,MAAqB;IACvE,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC1F,CAAC"}
1
+ {"version":3,"file":"file-filter.js","sourceRoot":"","sources":["../../src/file-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAqB;IACzE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAE5D,uFAAuF;IACvF,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAChD,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,IAAI,CAAC;IAEX,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAChD,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,KAAK,CAAC;IAEZ,OAAO,CAAC,UAAU,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAgB;IAC5D,kCAAkC;IAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhD,yBAAyB;IACzB,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,MAAqB;IACvE,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,11 @@
1
+ import ts from "typescript";
2
+ /**
3
+ * Hoists RegExp constructor calls to top-level constants.
4
+ * Transforms: RegExp(/pattern/).test(x) -> __regex_N.test(x)
5
+ * where __regex_N is a hoisted constant: const __regex_N = /pattern/;
6
+ *
7
+ * Due to typia's quirky AST structure where identifiers contain full expressions
8
+ * (e.g., identifier text is "RegExp(/pattern/).test"), we extract patterns from
9
+ * the identifier text directly.
10
+ */
11
+ export declare function hoistRegexConstructors(sourceFile: ts.SourceFile, tsInstance: typeof ts, factory: ts.NodeFactory): ts.SourceFile;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Hoists RegExp constructor calls to top-level constants.
3
+ * Transforms: RegExp(/pattern/).test(x) -> __regex_N.test(x)
4
+ * where __regex_N is a hoisted constant: const __regex_N = /pattern/;
5
+ *
6
+ * Due to typia's quirky AST structure where identifiers contain full expressions
7
+ * (e.g., identifier text is "RegExp(/pattern/).test"), we extract patterns from
8
+ * the identifier text directly.
9
+ */
10
+ export function hoistRegexConstructors(sourceFile, tsInstance, factory) {
11
+ const regexPatterns = new Map(); // full RegExp(...) -> variable name
12
+ let regexCounter = 0;
13
+ // Extract regex pattern from identifier text like "RegExp(/pattern/).test"
14
+ // The pattern can contain escaped characters and parentheses
15
+ function extractRegexFromIdText(idText) {
16
+ // Match RegExp(/.../) where the regex can contain any characters except unescaped /
17
+ // We look for RegExp( followed by / then find the matching closing / and )
18
+ if (!idText.startsWith("RegExp(/"))
19
+ return null;
20
+ let inCharClass = false;
21
+ let escaped = false;
22
+ // Start after "RegExp(/"
23
+ const start = 7; // "RegExp(/" = 7 chars, but we want to include the /
24
+ for (let i = 8; i < idText.length; i++) {
25
+ const char = idText[i];
26
+ if (escaped) {
27
+ escaped = false;
28
+ continue;
29
+ }
30
+ if (char === "\\") {
31
+ escaped = true;
32
+ continue;
33
+ }
34
+ if (char === "[" && !inCharClass) {
35
+ inCharClass = true;
36
+ continue;
37
+ }
38
+ if (char === "]" && inCharClass) {
39
+ inCharClass = false;
40
+ continue;
41
+ }
42
+ // End of regex pattern (unescaped /)
43
+ if (char === "/" && !inCharClass) {
44
+ // Check for flags after the /
45
+ let j = i + 1;
46
+ while (j < idText.length && /[gimsuy]/.test(idText[j])) {
47
+ j++;
48
+ }
49
+ // Should be followed by )
50
+ if (idText[j] === ")") {
51
+ return idText.substring(start, j); // Include /pattern/flags
52
+ }
53
+ return null;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ // First pass: collect all unique RegExp patterns from identifier texts
59
+ // Only collect from inside __typical_* declarations (our generated code)
60
+ function collectRegexPatterns(node, insideTypical) {
61
+ // Check if we're entering a __typical_* variable declaration
62
+ let nowInsideTypical = insideTypical;
63
+ if (tsInstance.isVariableDeclaration(node)) {
64
+ const name = node.name;
65
+ if (tsInstance.isIdentifier(name)) {
66
+ const varName = name.escapedText;
67
+ if (process.env.DEBUG && sourceFile.fileName.includes("object-types")) {
68
+ console.log(`REGEX HOISTER: found var decl: ${varName.substring(0, 50)}`);
69
+ }
70
+ if (varName.startsWith("__typical_") || varName.startsWith("___typical_")) {
71
+ nowInsideTypical = true;
72
+ if (process.env.DEBUG) {
73
+ console.log(`REGEX HOISTER: entering __typical_ declaration: ${varName}`);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ if (nowInsideTypical && tsInstance.isIdentifier(node)) {
79
+ const idText = node.escapedText;
80
+ if (idText.startsWith("RegExp(")) {
81
+ if (process.env.DEBUG) {
82
+ console.log(`REGEX HOISTER: found RegExp identifier: ${idText.substring(0, 50)}...`);
83
+ }
84
+ const pattern = extractRegexFromIdText(idText);
85
+ if (pattern) {
86
+ const fullMatch = `RegExp(${pattern})`;
87
+ if (!regexPatterns.has(fullMatch)) {
88
+ regexPatterns.set(fullMatch, `__regex_${regexCounter++}`);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ node.forEachChild((child) => collectRegexPatterns(child, nowInsideTypical));
94
+ }
95
+ collectRegexPatterns(sourceFile, false);
96
+ if (process.env.DEBUG) {
97
+ console.log(`REGEX HOISTER: ${sourceFile.fileName} - found ${regexPatterns.size} unique RegExp patterns`);
98
+ }
99
+ // No patterns found, return original
100
+ if (regexPatterns.size === 0) {
101
+ return sourceFile;
102
+ }
103
+ // Second pass: replace identifiers that start with RegExp(...)
104
+ function replaceRegexIdentifiers(node) {
105
+ if (tsInstance.isIdentifier(node)) {
106
+ const idText = node.escapedText;
107
+ if (idText.startsWith("RegExp(")) {
108
+ const pattern = extractRegexFromIdText(idText);
109
+ if (pattern) {
110
+ const fullMatch = `RegExp(${pattern})`;
111
+ const varName = regexPatterns.get(fullMatch);
112
+ if (varName) {
113
+ // Replace "RegExp(/pattern/).test" with "__regex_N.test"
114
+ const newIdText = idText.replace(fullMatch, varName);
115
+ return factory.createIdentifier(newIdText);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ return tsInstance.visitEachChild(node, replaceRegexIdentifiers, undefined);
121
+ }
122
+ // Create hoisted const declarations for each unique regex
123
+ const hoistedDeclarations = [];
124
+ for (const [fullMatch, varName] of regexPatterns) {
125
+ // Extract regex literal from "RegExp(/pattern/)"
126
+ const regexLiteral = fullMatch.slice(7, -1); // Remove "RegExp(" and ")"
127
+ const constDecl = factory.createVariableStatement(undefined, factory.createVariableDeclarationList([
128
+ factory.createVariableDeclaration(factory.createIdentifier(varName), undefined, undefined, factory.createRegularExpressionLiteral(regexLiteral)),
129
+ ], tsInstance.NodeFlags.Const));
130
+ hoistedDeclarations.push(constDecl);
131
+ }
132
+ // Transform all statements (replacing RegExp identifiers)
133
+ const transformedStatements = [];
134
+ for (const stmt of sourceFile.statements) {
135
+ const transformed = replaceRegexIdentifiers(stmt);
136
+ transformedStatements.push(transformed);
137
+ }
138
+ // Find insertion point: after imports but before other code
139
+ let insertIndex = 0;
140
+ for (let i = 0; i < transformedStatements.length; i++) {
141
+ if (tsInstance.isImportDeclaration(transformedStatements[i])) {
142
+ insertIndex = i + 1;
143
+ }
144
+ else {
145
+ break;
146
+ }
147
+ }
148
+ // Insert hoisted declarations after imports
149
+ const finalStatements = [
150
+ ...transformedStatements.slice(0, insertIndex),
151
+ ...hoistedDeclarations,
152
+ ...transformedStatements.slice(insertIndex),
153
+ ];
154
+ return factory.updateSourceFile(sourceFile, finalStatements, sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
155
+ }
156
+ //# sourceMappingURL=regex-hoister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex-hoister.js","sourceRoot":"","sources":["../../src/regex-hoister.ts"],"names":[],"mappings":"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"}
package/dist/src/setup.js CHANGED
@@ -4,9 +4,15 @@ export function setupTsProgram(tsInstance) {
4
4
  if (!tsConfigPath) {
5
5
  throw new Error("Could not find tsconfig.json");
6
6
  }
7
+ if (process.env.DEBUG) {
8
+ console.log(`SETUP: Using tsconfig at ${tsConfigPath}`);
9
+ }
7
10
  // Load and parse tsconfig.json
8
11
  const configFile = tsInstance.readConfigFile(tsConfigPath, tsInstance.sys.readFile);
9
12
  const parsedConfig = tsInstance.parseJsonConfigFileContent(configFile.config, tsInstance.sys, process.cwd());
13
+ if (process.env.DEBUG) {
14
+ console.log(`SETUP: Parsed tsconfig with ${parsedConfig.fileNames.length} files`);
15
+ }
10
16
  // Create the TypeScript program with all project files
11
17
  const tsProgram = tsInstance.createProgram(parsedConfig.fileNames, parsedConfig.options);
12
18
  return tsProgram;
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/setup.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,UAAqB;IAClD,qBAAqB;IACrB,MAAM,YAAY,GAAG,UAAU,CAAC,cAAc,CAC5C,OAAO,CAAC,GAAG,EAAE,EACb,UAAU,CAAC,GAAG,CAAC,UAAU,EACzB,eAAe,CAChB,CAAC;IACF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpF,MAAM,YAAY,GAAG,UAAU,CAAC,0BAA0B,CACxD,UAAU,CAAC,MAAM,EACjB,UAAU,CAAC,GAAG,EACd,OAAO,CAAC,GAAG,EAAE,CACd,CAAC;IAEF,uDAAuD;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,aAAa,CACxC,YAAY,CAAC,SAAS,EACtB,YAAY,CAAC,OAAO,CACrB,CAAC;IAEF,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/setup.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,UAAqB;IAClD,qBAAqB;IACrB,MAAM,YAAY,GAAG,UAAU,CAAC,cAAc,CAC5C,OAAO,CAAC,GAAG,EAAE,EACb,UAAU,CAAC,GAAG,CAAC,UAAU,EACzB,eAAe,CAChB,CAAC;IACF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,4BAA4B,YAAY,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpF,MAAM,YAAY,GAAG,UAAU,CAAC,0BAA0B,CACxD,UAAU,CAAC,MAAM,EACjB,UAAU,CAAC,GAAG,EACd,OAAO,CAAC,GAAG,EAAE,CACd,CAAC;IAEF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CACT,+BAA+B,YAAY,CAAC,SAAS,CAAC,MAAM,QAAQ,CACrE,CAAC;IACJ,CAAC;IAED,uDAAuD;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,aAAa,CACxC,YAAY,CAAC,SAAS,EACtB,YAAY,CAAC,OAAO,CACrB,CAAC;IAEF,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -27,9 +27,69 @@ export declare class TypicalTransformer {
27
27
  */
28
28
  private transformSourceFile;
29
29
  shouldTransformFile(fileName: string): boolean;
30
+ /**
31
+ * Check if a TypeNode represents any or unknown type.
32
+ * These types are intentional escape hatches and shouldn't be validated.
33
+ */
34
+ private isAnyOrUnknownType;
35
+ /**
36
+ * Check if a Type has any or unknown flags.
37
+ */
38
+ private isAnyOrUnknownTypeFlags;
39
+ /**
40
+ * Check if a type name matches any of the ignoreTypes patterns.
41
+ * Supports wildcards: "React.*" matches "React.FormEvent", "React.ChangeEvent", etc.
42
+ */
43
+ private isIgnoredType;
44
+ /**
45
+ * Find untransformed typia calls in the output code.
46
+ * These indicate types that typia could not process.
47
+ */
48
+ private findUntransformedTypiaCalls;
49
+ /**
50
+ * Infer type information from a JSON.stringify argument for creating a reusable stringifier.
51
+ */
52
+ private inferStringifyType;
53
+ /**
54
+ * Gets the root identifier from an expression.
55
+ * e.g., `user.address.city` -> "user"
56
+ */
57
+ private getRootIdentifier;
58
+ /**
59
+ * Check if a validated variable has been tainted (mutated) in the function body.
60
+ * A variable is tainted if it's reassigned, has properties modified, is passed
61
+ * to a function, has methods called on it, or if an await occurs.
62
+ */
63
+ private isTainted;
30
64
  private addTypiaImport;
65
+ /**
66
+ * Gets type text for use as a validator map key.
67
+ * Uses getText() to preserve local aliases (e.g., "User1" vs "User2"),
68
+ * but falls back to typeToString() for synthesized nodes without source positions.
69
+ *
70
+ * @param typeNode The TypeNode to get a key for
71
+ * @param typeChecker The TypeChecker to use
72
+ * @param typeObj Optional Type object - use this for synthesized nodes since
73
+ * getTypeFromTypeNode doesn't work correctly on them
74
+ */
75
+ private getTypeKey;
76
+ /**
77
+ * Creates a readable name suffix from a type string.
78
+ * For simple identifiers like "User" or "string", returns the name directly.
79
+ * For complex types, returns a numeric index.
80
+ */
81
+ private getTypeNameSuffix;
82
+ /**
83
+ * Generic method to get or create a typed function (validator, stringifier, or parser).
84
+ */
85
+ private getOrCreateTypedFunction;
31
86
  private getOrCreateValidator;
32
87
  private getOrCreateStringifier;
33
88
  private getOrCreateParser;
89
+ /**
90
+ * Creates a nested property access expression from an array of identifiers.
91
+ * e.g., ['typia', 'json', 'createStringify'] -> typia.json.createStringify
92
+ */
93
+ private createPropertyAccessChain;
34
94
  private createValidatorStatements;
35
95
  }