@elliots/typical 0.2.3 → 0.2.4

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,287 +0,0 @@
1
- import typia from "typia";
2
- //@L:1
3
- export interface TypicalDebugConfig {
4
- //@L:2
5
- writeIntermediateFiles?: boolean;
6
- }
7
- /**
8
- * Configuration options for source map generation.
9
- */
10
- //@L:8
11
- export interface TypicalSourceMapConfig {
12
- /**
13
- * Generate source maps. Default: true
14
- */
15
- //@L:12
16
- enabled?: boolean;
17
- /**
18
- * Include original source content in the map. Default: true
19
- */
20
- //@L:16
21
- includeContent?: boolean;
22
- /**
23
- * Use inline source maps (data URL) instead of external files. Default: false
24
- */
25
- //@L:20
26
- inline?: boolean;
27
- }
28
- //@L:23
29
- export interface TypicalConfig {
30
- //@L:24
31
- include?: string[];
32
- //@L:25
33
- exclude?: string[];
34
- //@L:26
35
- reusableValidators?: boolean;
36
- //@L:27
37
- validateCasts?: boolean;
38
- //@L:28
39
- hoistRegex?: boolean;
40
- //@L:29
41
- debug?: TypicalDebugConfig;
42
- /**
43
- * Type patterns to skip validation for (supports wildcards).
44
- * Use this for types that typia cannot process (e.g., React event types).
45
- * Example: ["React.*", "Express.Request", "*.Event"]
46
- */
47
- //@L:35
48
- ignoreTypes?: string[];
49
- /**
50
- * Skip validation for DOM types (Document, Element, Node, etc.) and their subclasses.
51
- * These types have complex Window intersections that typia cannot process.
52
- * Default: true
53
- */
54
- //@L:41
55
- ignoreDOMTypes?: boolean;
56
- /**
57
- * Validate function parameters and return types at runtime.
58
- * When enabled, typed function parameters get runtime validation calls injected.
59
- * Default: true
60
- */
61
- //@L:47
62
- validateFunctions?: boolean;
63
- /**
64
- * Source map generation settings.
65
- * Controls whether and how source maps are generated for transformed code.
66
- */
67
- //@L:52
68
- sourceMap?: TypicalSourceMapConfig;
69
- }
70
- /**
71
- * Pre-compiled regex patterns for ignore type matching.
72
- * This is populated during config loading for performance.
73
- */
74
- //@L:59
75
- export interface CompiledIgnorePatterns {
76
- /** Compiled patterns from user ignoreTypes config */
77
- //@L:61
78
- userPatterns: RegExp[];
79
- /** Compiled patterns from DOM_TYPES_TO_IGNORE (when ignoreDOMTypes is true) */
80
- //@L:63
81
- domPatterns: RegExp[];
82
- /** All patterns combined for quick checking */
83
- //@L:65
84
- allPatterns: RegExp[];
85
- }
86
- //@L:68
87
- export const defaultConfig: TypicalConfig = {
88
- include: ["**/*.ts", "**/*.tsx"],
89
- exclude: ["node_modules/**", "**/*.d.ts", "dist/**", "build/**"],
90
- reusableValidators: false, // Off by default for accurate source maps (set to true for production)
91
- validateCasts: false,
92
- validateFunctions: true,
93
- hoistRegex: true,
94
- ignoreDOMTypes: true,
95
- debug: {
96
- writeIntermediateFiles: false,
97
- },
98
- sourceMap: {
99
- enabled: true, // On by default for debugging (set to false for production)
100
- includeContent: true,
101
- inline: false,
102
- },
103
- };
104
- // FIXME: find a better way to work out which types to ignore
105
- /**
106
- * DOM types that typia cannot process due to Window global intersections.
107
- * These are the base DOM types - classes extending them are checked separately.
108
- */
109
- //@L:91
110
- export const DOM_TYPES_TO_IGNORE = [
111
- // Core DOM types
112
- "Document",
113
- "DocumentFragment",
114
- "Element",
115
- "Node",
116
- "ShadowRoot",
117
- "Window",
118
- "EventTarget",
119
- // HTML Elements
120
- "HTML*Element",
121
- "HTMLElement",
122
- "HTMLCollection",
123
- // SVG Elements
124
- "SVG*Element",
125
- "SVGElement",
126
- // Events
127
- "*Event",
128
- // Other common DOM types
129
- "NodeList",
130
- "DOMTokenList",
131
- "NamedNodeMap",
132
- "CSSStyleDeclaration",
133
- "Selection",
134
- "Range",
135
- "Text",
136
- "Comment",
137
- "CDATASection",
138
- "ProcessingInstruction",
139
- "DocumentType",
140
- "Attr",
141
- "Table",
142
- "TableRow",
143
- "TableCell",
144
- "StyleSheet",
145
- ];
146
- //@L:128
147
- import fs from 'fs';
148
- //@L:129
149
- import path from 'path';
150
- /**
151
- * Convert a glob pattern to a RegExp for type matching.
152
- * Supports wildcards: "React.*" -> /^React\..*$/
153
- */
154
- //@L:135
155
- export function compileIgnorePattern(pattern: string): RegExp | null {
156
- //@L:136
157
- try {
158
- const regexStr = '^' + pattern
159
- .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
160
- .replace(/\*/g, '.*') + '$';
161
- return new RegExp(regexStr);
162
- }
163
- catch (error) {
164
- console.warn(`TYPICAL: Invalid ignoreTypes pattern "${pattern}": ${error}`);
165
- return null;
166
- }
167
- }
168
- /**
169
- * Pre-compile all ignore patterns for efficient matching.
170
- */
171
- //@L:150
172
- export function compileIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
173
- //@L:151
174
- const userPatterns: RegExp[] = [];
175
- //@L:152
176
- const domPatterns: RegExp[] = [];
177
- // Compile user patterns
178
- //@L:155
179
- for (const pattern of config.ignoreTypes ?? []) {
180
- const compiled = compileIgnorePattern(pattern);
181
- if (compiled) {
182
- userPatterns.push(compiled);
183
- }
184
- }
185
- // Compile DOM patterns if enabled (default: true)
186
- //@L:163
187
- if (config.ignoreDOMTypes !== false) {
188
- //@L:164
189
- for (const pattern of DOM_TYPES_TO_IGNORE) {
190
- const compiled = compileIgnorePattern(pattern);
191
- if (compiled) {
192
- domPatterns.push(compiled);
193
- }
194
- }
195
- }
196
- //@L:172
197
- return {
198
- userPatterns,
199
- domPatterns,
200
- allPatterns: [...userPatterns, ...domPatterns],
201
- };
202
- }
203
- // Cache for compiled patterns, keyed by config identity
204
- //@L:180
205
- let cachedPatterns: CompiledIgnorePatterns | null = null;
206
- //@L:181
207
- let cachedConfig: TypicalConfig | null = null;
208
- /**
209
- * Get compiled ignore patterns, using cache if config hasn't changed.
210
- */
211
- //@L:186
212
- export function getCompiledIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
213
- // Simple identity check - if same config object, use cache
214
- //@L:188
215
- if (cachedConfig === config && cachedPatterns) {
216
- //@L:189
217
- return cachedPatterns;
218
- }
219
- //@L:192
220
- cachedConfig = config;
221
- //@L:193
222
- cachedPatterns = compileIgnorePatterns(config);
223
- //@L:194
224
- return cachedPatterns;
225
- }
226
- //@L:197
227
- export function loadConfig(configPath?: string): TypicalConfig {
228
- //@L:198
229
- const configFile = configPath || path.join(process.cwd(), 'typical.json');
230
- //@L:200
231
- if (fs.existsSync(configFile)) {
232
- //@L:201
233
- try {
234
- const configContent = fs.readFileSync(configFile, 'utf8');
235
- const userConfig: Partial<TypicalConfig> = typia.json.assertParse<Partial<TypicalConfig>>(configContent);
236
- return {
237
- ...defaultConfig,
238
- ...userConfig,
239
- };
240
- }
241
- catch (error) {
242
- console.warn(`Failed to parse config file ${configFile}:`, error);
243
- return defaultConfig;
244
- }
245
- }
246
- //@L:215
247
- return defaultConfig;
248
- }
249
- //@L:218
250
- let warnedAboutSourceMaps = false;
251
- /**
252
- * Validate and adjust config for consistency.
253
- * Currently handles:
254
- * - Disabling reusableValidators when source maps are enabled (required for accurate mappings)
255
- *
256
- * @param config The config to validate
257
- * @returns Validated/adjusted config
258
- */
259
- //@L:228
260
- export function validateConfig(config: TypicalConfig): TypicalConfig {
261
- //@L:229
262
- let result = config;
263
- // Source maps require inline validators (not reusable) because each validation
264
- // call needs its own source map marker pointing to the correct type annotation.
265
- // With reusable validators, the expanded typia code would all map to the validator
266
- // declaration rather than the individual usage sites.
267
- //@L:235
268
- const sourceMapEnabled = config.sourceMap?.enabled !== false;
269
- //@L:236
270
- const reusableValidatorsEnabled = config.reusableValidators === true;
271
- //@L:238
272
- if (sourceMapEnabled && reusableValidatorsEnabled) {
273
- //@L:239
274
- if (!warnedAboutSourceMaps) {
275
- //@L:240
276
- warnedAboutSourceMaps = true;
277
- //@L:241
278
- console.warn("TYPICAL: Both sourceMap and reusableValidators are enabled. " +
279
- "Disabling reusableValidators for accurate source mapping. " +
280
- "For production builds, set sourceMap.enabled: false to use reusableValidators.");
281
- }
282
- //@L:247
283
- result = { ...result, reusableValidators: false };
284
- }
285
- //@L:250
286
- return result;
287
- }
@@ -1,13 +0,0 @@
1
- import type { TypicalConfig } from './config.js';
2
- /**
3
- * Determines if a file should be transformed based on include/exclude patterns
4
- */
5
- export declare function shouldTransformFile(fileName: string, config: TypicalConfig): boolean;
6
- /**
7
- * Checks if a file is a TypeScript file that can be transformed
8
- */
9
- export declare function isTransformableTypeScriptFile(fileName: string): boolean;
10
- /**
11
- * Combined check for both file type and include/exclude patterns
12
- */
13
- export declare function shouldIncludeFile(fileName: string, config: TypicalConfig): boolean;
@@ -1,42 +0,0 @@
1
- import * as path from 'path';
2
- import { minimatch } from 'minimatch';
3
- /**
4
- * Determines if a file should be transformed based on include/exclude patterns
5
- */
6
- export function shouldTransformFile(fileName, config) {
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
- }
12
- // Check include patterns
13
- const isIncluded = config.include?.some(pattern => {
14
- return minimatch(relativePath, pattern);
15
- }) ?? true;
16
- if (!isIncluded)
17
- return false;
18
- // Check exclude patterns
19
- const isExcluded = config.exclude?.some(pattern => {
20
- return minimatch(relativePath, pattern);
21
- }) ?? false;
22
- return !isExcluded;
23
- }
24
- /**
25
- * Checks if a file is a TypeScript file that can be transformed
26
- */
27
- export function isTransformableTypeScriptFile(fileName) {
28
- // Only transform TypeScript files
29
- if (!/\.(ts|tsx)$/.test(fileName))
30
- return false;
31
- // Skip declaration files
32
- if (fileName.endsWith('.d.ts'))
33
- return false;
34
- return true;
35
- }
36
- /**
37
- * Combined check for both file type and include/exclude patterns
38
- */
39
- export function shouldIncludeFile(fileName, config) {
40
- return isTransformableTypeScriptFile(fileName) && shouldTransformFile(fileName, config);
41
- }
42
- //# sourceMappingURL=file-filter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-filter.js","sourceRoot":"","sources":["../../src/file-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGrC;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAqB;IACzE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;IAE3D,uFAAuF;IACvF,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,yBAAyB;IACzB,MAAM,UAAU,GACd,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAC7B,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACzC,CAAC,CAAC,IAAI,IAAI,CAAA;IAEZ,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAA;IAE7B,yBAAyB;IACzB,MAAM,UAAU,GACd,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;QAC7B,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACzC,CAAC,CAAC,IAAI,KAAK,CAAA;IAEb,OAAO,CAAC,UAAU,CAAA;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAgB;IAC5D,kCAAkC;IAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAA;IAE/C,yBAAyB;IACzB,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IAE5C,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,MAAqB;IACvE,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;AACzF,CAAC"}
@@ -1,27 +0,0 @@
1
- import ts from 'typescript';
2
- /**
3
- * Manages a shared TypeScript program across file transformations.
4
- * This avoids the expensive cost of creating a new program for each file.
5
- */
6
- export declare class ProgramManager {
7
- private program;
8
- private compilerOptions;
9
- private sourceContents;
10
- private sourceFileCache;
11
- private host;
12
- /**
13
- * Get or create a program with the given source content for a file.
14
- * Uses incremental compilation to reuse data from previous program.
15
- */
16
- getProgram(id: string, source: string): ts.Program;
17
- /**
18
- * Get the source file for a given ID from the current program.
19
- */
20
- getSourceFile(id: string): ts.SourceFile | undefined;
21
- /**
22
- * Reset the program manager state (e.g., at build start).
23
- */
24
- reset(): void;
25
- private loadCompilerOptions;
26
- private createHost;
27
- }
@@ -1,121 +0,0 @@
1
- import ts from 'typescript';
2
- import { resolve, dirname } from 'path';
3
- import { buildTimer } from './timing.js';
4
- /**
5
- * Manages a shared TypeScript program across file transformations.
6
- * This avoids the expensive cost of creating a new program for each file.
7
- */
8
- export class ProgramManager {
9
- program;
10
- compilerOptions;
11
- sourceContents = new Map(); // Virtual file contents (transformed by bundler)
12
- sourceFileCache = new Map(); // Cached source files from disk
13
- host;
14
- /**
15
- * Get or create a program with the given source content for a file.
16
- * Uses incremental compilation to reuse data from previous program.
17
- */
18
- getProgram(id, source) {
19
- const resolvedId = resolve(id);
20
- // Update virtual source content
21
- this.sourceContents.set(resolvedId, source);
22
- // Invalidate cached source file for this file (since content changed)
23
- this.sourceFileCache.delete(resolvedId);
24
- // Ensure we have compiler options and host
25
- if (!this.compilerOptions) {
26
- buildTimer.start('load-compiler-options');
27
- this.compilerOptions = this.loadCompilerOptions();
28
- buildTimer.end('load-compiler-options');
29
- }
30
- if (!this.host) {
31
- this.host = this.createHost();
32
- }
33
- // Get current root files, adding the new file if not present
34
- const rootFiles = this.program?.getRootFileNames() ?? [];
35
- const rootFileSet = new Set(rootFiles);
36
- if (!rootFileSet.has(resolvedId)) {
37
- rootFileSet.add(resolvedId);
38
- }
39
- // Create program, reusing old program for incremental compilation
40
- buildTimer.start('create-program-incremental');
41
- this.program = ts.createProgram(Array.from(rootFileSet), this.compilerOptions, this.host, this.program);
42
- buildTimer.end('create-program-incremental');
43
- return this.program;
44
- }
45
- /**
46
- * Get the source file for a given ID from the current program.
47
- */
48
- getSourceFile(id) {
49
- const resolvedId = resolve(id);
50
- return this.program?.getSourceFile(resolvedId);
51
- }
52
- /**
53
- * Reset the program manager state (e.g., at build start).
54
- */
55
- reset() {
56
- this.program = undefined;
57
- this.sourceContents.clear();
58
- // Keep sourceFileCache and compilerOptions since they don't change
59
- }
60
- loadCompilerOptions() {
61
- const configPath = ts.findConfigFile(process.cwd(), f => ts.sys.fileExists(f), 'tsconfig.json');
62
- if (!configPath) {
63
- return {
64
- target: ts.ScriptTarget.ES2020,
65
- module: ts.ModuleKind.ESNext,
66
- moduleResolution: ts.ModuleResolutionKind.Bundler,
67
- esModuleInterop: true,
68
- strict: true,
69
- };
70
- }
71
- const configFile = ts.readConfigFile(configPath, f => ts.sys.readFile(f));
72
- if (configFile.error) {
73
- throw new Error(ts.flattenDiagnosticMessageText(configFile.error.messageText, '\n'));
74
- }
75
- const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, dirname(configPath));
76
- return parsed.options;
77
- }
78
- createHost() {
79
- const baseHost = ts.createCompilerHost(this.compilerOptions);
80
- const originalGetSourceFile = baseHost.getSourceFile.bind(baseHost);
81
- return {
82
- ...baseHost,
83
- getSourceFile: (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
84
- const resolvedFileName = resolve(fileName);
85
- // Return virtual content if we have transformed source
86
- const virtualContent = this.sourceContents.get(resolvedFileName);
87
- if (virtualContent !== undefined) {
88
- // Check if we have a cached source file with the same content
89
- const cached = this.sourceFileCache.get(resolvedFileName);
90
- if (cached && cached.text === virtualContent) {
91
- return cached;
92
- }
93
- // Create new source file from virtual content
94
- const sourceFile = ts.createSourceFile(resolvedFileName, virtualContent, languageVersion, true);
95
- this.sourceFileCache.set(resolvedFileName, sourceFile);
96
- return sourceFile;
97
- }
98
- // Check cache for files loaded from disk
99
- const cachedDisk = this.sourceFileCache.get(resolvedFileName);
100
- if (cachedDisk) {
101
- return cachedDisk;
102
- }
103
- // Load from disk and cache
104
- const result = originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
105
- if (result) {
106
- this.sourceFileCache.set(resolvedFileName, result);
107
- }
108
- return result;
109
- },
110
- fileExists: fileName => {
111
- const resolvedFileName = resolve(fileName);
112
- return this.sourceContents.has(resolvedFileName) || baseHost.fileExists(fileName);
113
- },
114
- readFile: fileName => {
115
- const resolvedFileName = resolve(fileName);
116
- return this.sourceContents.get(resolvedFileName) ?? baseHost.readFile(fileName);
117
- },
118
- };
119
- }
120
- }
121
- //# sourceMappingURL=program-manager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"program-manager.js","sourceRoot":"","sources":["../../src/program-manager.ts"],"names":[],"mappings":"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"}
@@ -1,11 +0,0 @@
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;
@@ -1,150 +0,0 @@
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([factory.createVariableDeclaration(factory.createIdentifier(varName), undefined, undefined, factory.createRegularExpressionLiteral(regexLiteral))], tsInstance.NodeFlags.Const));
128
- hoistedDeclarations.push(constDecl);
129
- }
130
- // Transform all statements (replacing RegExp identifiers)
131
- const transformedStatements = [];
132
- for (const stmt of sourceFile.statements) {
133
- const transformed = replaceRegexIdentifiers(stmt);
134
- transformedStatements.push(transformed);
135
- }
136
- // Find insertion point: after imports but before other code
137
- let insertIndex = 0;
138
- for (let i = 0; i < transformedStatements.length; i++) {
139
- if (tsInstance.isImportDeclaration(transformedStatements[i])) {
140
- insertIndex = i + 1;
141
- }
142
- else {
143
- break;
144
- }
145
- }
146
- // Insert hoisted declarations after imports
147
- const finalStatements = [...transformedStatements.slice(0, insertIndex), ...hoistedDeclarations, ...transformedStatements.slice(insertIndex)];
148
- return factory.updateSourceFile(sourceFile, finalStatements, sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
149
- }
150
- //# sourceMappingURL=regex-hoister.js.map