@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.
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +5 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/esm-loader.d.ts +4 -0
- package/dist/src/esm-loader.js +23 -1
- package/dist/src/esm-loader.js.map +1 -1
- package/dist/src/file-filter.js +4 -0
- package/dist/src/file-filter.js.map +1 -1
- package/dist/src/regex-hoister.d.ts +11 -0
- package/dist/src/regex-hoister.js +156 -0
- package/dist/src/regex-hoister.js.map +1 -0
- package/dist/src/setup.js +6 -0
- package/dist/src/setup.js.map +1 -1
- package/dist/src/transformer.d.ts +60 -0
- package/dist/src/transformer.js +471 -260
- package/dist/src/transformer.js.map +1 -1
- package/package.json +17 -17
- package/src/config.ts +18 -0
- package/src/esm-loader.ts +26 -1
- package/src/file-filter.ts +8 -3
- package/src/regex-hoister.ts +203 -0
- package/src/setup.ts +10 -0
- package/src/transformer.ts +557 -384
package/dist/src/config.d.ts
CHANGED
|
@@ -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;
|
package/dist/src/config.js
CHANGED
|
@@ -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';
|
package/dist/src/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/src/esm-loader.d.ts
CHANGED
package/dist/src/esm-loader.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/src/file-filter.js
CHANGED
|
@@ -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;
|
package/dist/src/setup.js.map
CHANGED
|
@@ -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
|
}
|