@elliots/typical 0.1.9 → 0.2.0-beta.1

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.
Files changed (55) hide show
  1. package/README.md +232 -96
  2. package/dist/src/cli.js +14 -60
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/cli.typical.ts +136 -0
  5. package/dist/src/config.d.ts +56 -0
  6. package/dist/src/config.js +124 -32
  7. package/dist/src/config.js.map +1 -1
  8. package/dist/src/config.typical.ts +287 -0
  9. package/dist/src/esm-loader-register.js.map +1 -1
  10. package/dist/src/esm-loader.d.ts +1 -0
  11. package/dist/src/esm-loader.js +31 -8
  12. package/dist/src/esm-loader.js.map +1 -1
  13. package/dist/src/file-filter.d.ts +1 -1
  14. package/dist/src/file-filter.js.map +1 -1
  15. package/dist/src/index.d.ts +4 -1
  16. package/dist/src/index.js +2 -1
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/program-manager.d.ts +27 -0
  19. package/dist/src/program-manager.js +121 -0
  20. package/dist/src/program-manager.js.map +1 -0
  21. package/dist/src/regex-hoister.d.ts +1 -1
  22. package/dist/src/regex-hoister.js +13 -19
  23. package/dist/src/regex-hoister.js.map +1 -1
  24. package/dist/src/setup.d.ts +1 -1
  25. package/dist/src/setup.js +3 -3
  26. package/dist/src/setup.js.map +1 -1
  27. package/dist/src/source-map.d.ts +78 -0
  28. package/dist/src/source-map.js +133 -0
  29. package/dist/src/source-map.js.map +1 -0
  30. package/dist/src/source-map.typical.ts +216 -0
  31. package/dist/src/timing.d.ts +19 -0
  32. package/dist/src/timing.js +65 -0
  33. package/dist/src/timing.js.map +1 -0
  34. package/dist/src/transformer.d.ts +28 -126
  35. package/dist/src/transformer.js +44 -1477
  36. package/dist/src/transformer.js.map +1 -1
  37. package/dist/src/transformer.typical.ts +2552 -0
  38. package/dist/src/tsc-plugin.d.ts +8 -1
  39. package/dist/src/tsc-plugin.js +11 -7
  40. package/dist/src/tsc-plugin.js.map +1 -1
  41. package/package.json +54 -44
  42. package/src/cli.ts +45 -98
  43. package/src/config.ts +200 -57
  44. package/src/esm-loader-register.ts +2 -2
  45. package/src/esm-loader.ts +46 -19
  46. package/src/index.ts +5 -2
  47. package/src/patch-fs.cjs +14 -14
  48. package/src/timing.ts +74 -0
  49. package/src/transformer.ts +52 -1969
  50. package/bin/ttsc +0 -12
  51. package/src/file-filter.ts +0 -49
  52. package/src/patch-tsconfig.cjs +0 -52
  53. package/src/regex-hoister.ts +0 -203
  54. package/src/setup.ts +0 -39
  55. package/src/tsc-plugin.ts +0 -12
@@ -1,3 +1,10 @@
1
1
  import type ts from 'typescript';
2
2
  import type { TransformerExtras, PluginConfig } from 'ts-patch';
3
- export default function (program: ts.Program, pluginConfig: PluginConfig, { ts: tsInstance }: TransformerExtras): ts.TransformerFactory<ts.SourceFile>;
3
+ /**
4
+ * TSC Plugin for typical.
5
+ *
6
+ * NOTE: This plugin has been moved to a separate package: @elliots/typical-tsc-plugin
7
+ * This stub is kept for backwards compatibility but will throw an error directing
8
+ * users to the new package.
9
+ */
10
+ export default function (_program: ts.Program, _pluginConfig: PluginConfig, _extras: TransformerExtras): void;
@@ -1,9 +1,13 @@
1
- import { TypicalTransformer } from './transformer.js';
2
- import { loadConfig } from './config.js';
3
- export default function (program, pluginConfig, { ts: tsInstance }) {
4
- const config = loadConfig();
5
- const transformer = new TypicalTransformer(config, program, tsInstance);
6
- // Create the typical transformer with typia integration
7
- return transformer.getTransformer(true);
1
+ /**
2
+ * TSC Plugin for typical.
3
+ *
4
+ * NOTE: This plugin has been moved to a separate package: @elliots/typical-tsc-plugin
5
+ * This stub is kept for backwards compatibility but will throw an error directing
6
+ * users to the new package.
7
+ */
8
+ export default function (_program, _pluginConfig, _extras) {
9
+ throw new Error('The typical tsc-plugin has moved to a separate package. ' +
10
+ 'Install @elliots/typical-tsc-plugin and update your tsconfig.json to use ' +
11
+ '"@elliots/typical-tsc-plugin" instead of "@elliots/typical/tsc-plugin".');
8
12
  }
9
13
  //# sourceMappingURL=tsc-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tsc-plugin.js","sourceRoot":"","sources":["../../src/tsc-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,OAAO,WAAW,OAAmB,EAAE,YAA0B,EAAE,EAAE,EAAE,EAAE,UAAU,EAAqB;IAC7G,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAExE,wDAAwD;IACxD,OAAO,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"tsc-plugin.js","sourceRoot":"","sources":["../../src/tsc-plugin.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,WAAW,QAAoB,EAAE,aAA2B,EAAE,OAA0B;IACpG,MAAM,IAAI,KAAK,CACb,0DAA0D;QACxD,2EAA2E;QAC3E,yEAAyE,CAC5E,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,74 +1,84 @@
1
1
  {
2
2
  "name": "@elliots/typical",
3
- "version": "0.1.9",
3
+ "version": "0.2.0-beta.1",
4
4
  "description": "Runtime safe TypeScript transformer using typia",
5
- "main": "dist/src/index.js",
6
- "type": "module",
5
+ "keywords": [
6
+ "runtime",
7
+ "transformer",
8
+ "typescript",
9
+ "typia",
10
+ "validation"
11
+ ],
12
+ "homepage": "https://github.com/elliots/typical#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/elliots/typical/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Elliot Shepherd <elliot@jarofworms.com>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/elliots/typical.git"
21
+ },
7
22
  "bin": {
8
- "typical": "bin/typical",
9
23
  "ttsx": "bin/ttsx",
10
- "ttsc": "bin/ttsc"
24
+ "typical": "bin/typical"
11
25
  },
26
+ "files": [
27
+ "bin",
28
+ "src",
29
+ "dist/src"
30
+ ],
31
+ "type": "module",
32
+ "main": "dist/src/index.js",
12
33
  "exports": {
13
34
  ".": "./dist/src/index.js",
14
- "./tsc-plugin": "./dist/src/tsc-plugin.js",
15
35
  "./esm": "./dist/src/esm-loader-register.js",
16
36
  "./loader": "./dist/src/esm-loader.js"
17
37
  },
18
- "files": [
19
- "dist/src",
20
- "bin",
21
- "src"
22
- ],
23
- "repository": {
24
- "type": "git",
25
- "url": "git+https://github.com/elliots/typical.git"
26
- },
27
- "bugs": {
28
- "url": "https://github.com/elliots/typical/issues"
29
- },
30
- "homepage": "https://github.com/elliots/typical#readme",
31
- "keywords": [
32
- "typescript",
33
- "runtime",
34
- "validation",
35
- "typia",
36
- "transformer"
37
- ],
38
- "author": "Elliot Shepherd <elliot@jarofworms.com>",
39
- "license": "MIT",
40
38
  "publishConfig": {
41
39
  "access": "public"
42
40
  },
43
41
  "dependencies": {
44
42
  "commander": "14.0.2",
45
- "glob": "13.0.0",
46
- "minimatch": "10.0.1",
47
- "strip-json-comments": "5.0.3",
48
- "ts-patch": "3.3.0",
49
- "tsx": "4.21.0",
50
- "typescript": "^5.0.0",
51
- "typia": "11.0.0",
52
- "unplugin": "2.3.11"
43
+ "@elliots/typical-compiler": "0.2.0-beta.1"
53
44
  },
54
45
  "devDependencies": {
55
- "@types/node": "22"
46
+ "@types/node": "22",
47
+ "c8": "10.1.3",
48
+ "oxfmt": "^0.21.0",
49
+ "oxlint": "1.36.0",
50
+ "oxlint-tsgolint": "0.10.1",
51
+ "typescript": "5.9.3"
56
52
  },
57
53
  "peerDependencies": {
58
54
  "typescript": "^5.0.0"
59
55
  },
56
+ "engines": {
57
+ "pnpm": ">=10"
58
+ },
60
59
  "scripts": {
60
+ "lint": "oxlint --type-aware",
61
+ "lint:fix": "oxlint --type-aware --fix",
62
+ "format": "oxfmt",
63
+ "format:check": "oxfmt --check",
61
64
  "build": "tsc",
65
+ "build:all": "pnpm run build:go:all && pnpm run build:ts",
66
+ "build:go": "cd packages/compiler && npm run build:go",
67
+ "build:go:all": "./scripts/build-binaries.sh",
68
+ "build:ts": "pnpm --filter './packages/*' run build && pnpm run build",
62
69
  "dev": "tsc --watch",
63
70
  "test": "rm -f test/test.temp.ts && tsc && node --import ./dist/src/esm-loader-register.js --test dist/test/*.test.js",
64
- "bench": "cd bench && npm install && npm run bench",
71
+ "test:coverage": "rm -f test/test.temp.ts && tsc && node --import ./dist/src/esm-loader-register.js --test --experimental-test-coverage dist/test/*.test.js",
72
+ "test:coverage:html": "rm -f test/test.temp.ts && tsc && c8 --reporter=html --reporter=text --include='dist/src/**' node --import ./dist/src/esm-loader-register.js --test dist/test/*.test.js",
73
+ "bench": "pnpm run build && cd bench && pnpm install && npm run bench",
65
74
  "test:all": "npm test && npm run bench",
66
- "sample:esm": "cd samples/esm && npm install && npm run start",
67
- "sample:ttsx": "cd samples/ttsx && npm install && npm run start",
68
- "sample:tsc": "cd samples/tsc && npm install && npm run build && npm run start",
69
- "sample:ttsc": "cd samples/ttsc && npm install && npm run build && npm run start",
75
+ "sample:esm": "cd samples/esm && pnpm install && npm run start",
76
+ "sample:ttsx": "cd samples/ttsx && pnpm install && npm run start",
77
+ "sample:tsc": "cd samples/tsc && pnpm install && npm run build && npm run start",
78
+ "sample:ttsc": "cd samples/ttsc && pnpm install && npm run build && npm run start",
70
79
  "sample:vite-react": "cd samples/vite-react && pnpm install && pnpm run test",
71
- "samples": "npm run sample:esm && npm run sample:ttsx && npm run sample:tsc && npm run sample:ttsc && npm run sample:vite-react",
72
- "publish:all": "node scripts/publish.js"
80
+ "sample:bun": "cd samples/bun && pnpm install && bun run start",
81
+ "samples": "npm run sample:esm && npm run sample:ttsx && npm run sample:vite-react && npm run sample:bun",
82
+ "publish:all": "npm run lint && npm run format:check && node scripts/publish.js"
73
83
  }
74
84
  }
package/src/cli.ts CHANGED
@@ -1,19 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from 'commander';
4
- import * as fs from 'fs';
5
- import * as path from 'path';
6
- import { TypicalTransformer } from './transformer.js';
7
- import * as ts from 'typescript';
8
- import { loadConfig } from './config.js';
9
- import { shouldIncludeFile } from './file-filter.js';
3
+ import { Command } from 'commander'
4
+ import * as fs from 'fs'
5
+ import * as path from 'path'
6
+ import { TypicalTransformer } from './transformer.js'
7
+ import { loadConfig, validateConfig } from './config.js'
10
8
 
11
- const program = new Command();
9
+ const program = new Command()
12
10
 
13
- program
14
- .name('typical')
15
- .description('Runtime safe TypeScript transformer using typia')
16
- .version('0.1.0');
11
+ program.name('typical').description('Runtime safe TypeScript transformer').version('0.1.0')
17
12
 
18
13
  program
19
14
  .command('transform')
@@ -21,91 +16,43 @@ program
21
16
  .argument('<file>', 'TypeScript file to transform')
22
17
  .option('-o, --output <file>', 'Output file')
23
18
  .option('-c, --config <file>', 'Config file path', 'typical.json')
24
- .option('-m, --mode <mode>', 'Transformation mode: basic, typia, js', 'basic')
25
- .action(async (file: string, options: { output?: string; config?: string; mode?: 'basic' | 'typia' | 'js' }) => {
26
- try {
27
- const config = loadConfig(options.config);
28
- const transformer = new TypicalTransformer(config);
29
-
30
- if (!fs.existsSync(file)) {
31
- console.error(`File not found: ${file}`);
32
- process.exit(1);
19
+ .option('-p, --project <file>', 'TypeScript config file path', 'tsconfig.json')
20
+ .action(
21
+ async (
22
+ file: string,
23
+ options: {
24
+ output?: string
25
+ config?: string
26
+ project?: string
27
+ },
28
+ ) => {
29
+ let transformer: TypicalTransformer | null = null
30
+ try {
31
+ const config = validateConfig(loadConfig(options.config))
32
+ transformer = new TypicalTransformer(config, options.project)
33
+
34
+ if (!fs.existsSync(file)) {
35
+ console.error(`File not found: ${file}`)
36
+ process.exit(1)
37
+ }
38
+
39
+ console.log(`Transforming ${file}...`)
40
+ const result = await transformer.transform(path.resolve(file), 'ts')
41
+
42
+ // Determine output file path
43
+ const outputFile = options.output ? path.resolve(options.output) : file + '.transformed.ts'
44
+
45
+ fs.writeFileSync(outputFile, result.code)
46
+ console.log(`Transformed code written to ${outputFile}`)
47
+ } catch (error) {
48
+ console.error('Transformation failed:', error)
49
+ process.exit(1)
50
+ } finally {
51
+ if (transformer) {
52
+ await transformer.close()
53
+ }
33
54
  }
55
+ },
56
+ )
34
57
 
35
- console.log(`Transforming ${file}...`);
36
- const transformedCode = transformer.transform(path.resolve(file), options.mode ?? 'basic');
37
-
38
- const outputFilename = options.output ? path.resolve(options.output) : options.mode === 'js' ? file + '.js' : file + '.transformed.ts';
39
-
40
- const outputFile = options.output ? path.resolve(options.output) : file + '.transformed.ts';
41
- fs.writeFileSync(outputFile, transformedCode);
42
-
43
- console.log(`Transformed code written to ${outputFile}`);
44
- } catch (error) {
45
- console.error('Transformation failed:', error);
46
- process.exit(1);
47
- }
48
- });
49
-
50
- // program
51
- // .command('build')
52
- // .description('Transform all TypeScript files in the project')
53
- // .option('-c, --config <file>', 'Config file path')
54
- // .option('--dry-run', 'Show what would be transformed without making changes')
55
- // .action(async (options: { config?: string, dryRun?: boolean }) => {
56
- // try {
57
- // const transformer = new TypicalTransformer();
58
-
59
- // const { glob } = await import('glob');
60
-
61
- // const config = loadConfig(options.config);
62
-
63
- // if (!config.include || config.include.length === 0) {
64
- // console.error('No include patterns specified in config');
65
- // process.exit(1);
66
- // }
67
-
68
- // const files: string[] = [];
69
-
70
- // for (const pattern of config.include) {
71
- // const matched = await glob(pattern, {
72
- // ignore: config.exclude,
73
- // absolute: true
74
- // });
75
- // files.push(...matched);
76
- // }
77
-
78
- // console.log(`Found ${files.length} files to transform`);
79
-
80
- // if (options.dryRun) {
81
- // files.forEach(file => console.log(`Would transform: ${file}`));
82
- // return;
83
- // }
84
-
85
- // let transformed = 0;
86
-
87
- // for (const file of files) {
88
- // // Double-check with our shared filtering logic
89
- // if (!shouldIncludeFile(file, config)) {
90
- // console.log(`Skipping ${file} (excluded by filters)`);
91
- // continue;
92
- // }
93
-
94
- // try {
95
- // console.log(`Transforming ${file}...`);
96
- // const transformedCode = transformer.transformFile(file, ts);
97
- // fs.writeFileSync(file, transformedCode);
98
- // transformed++;
99
- // } catch (error) {
100
- // console.error(`Failed to transform ${file}:`, error);
101
- // }
102
- // }
103
-
104
- // console.log(`Successfully transformed ${transformed}/${files.length} files`);
105
- // } catch (error) {
106
- // console.error('Build failed:', error);
107
- // process.exit(1);
108
- // }
109
- // });
110
-
111
- program.parse();
58
+ program.parse()
package/src/config.ts CHANGED
@@ -1,38 +1,74 @@
1
1
  export interface TypicalDebugConfig {
2
- writeIntermediateFiles?: boolean;
2
+ writeIntermediateFiles?: boolean
3
+ }
4
+
5
+ /**
6
+ * Configuration options for source map generation.
7
+ */
8
+ export interface TypicalSourceMapConfig {
9
+ /**
10
+ * Generate source maps. Default: true
11
+ */
12
+ enabled?: boolean
13
+ /**
14
+ * Include original source content in the map. Default: true
15
+ */
16
+ includeContent?: boolean
17
+ /**
18
+ * Use inline source maps (data URL) instead of external files. Default: false
19
+ */
20
+ inline?: boolean
3
21
  }
4
22
 
5
23
  export interface TypicalConfig {
6
- include?: string[];
7
- exclude?: string[];
8
- reusableValidators?: boolean;
9
- validateCasts?: boolean;
10
- hoistRegex?: boolean;
11
- debug?: TypicalDebugConfig;
24
+ include?: string[]
25
+ exclude?: string[]
26
+ reusableValidators?: boolean
27
+ validateCasts?: boolean
28
+ hoistRegex?: boolean
29
+ debug?: TypicalDebugConfig
12
30
  /**
13
31
  * Type patterns to skip validation for (supports wildcards).
14
32
  * Use this for types that typia cannot process (e.g., React event types).
15
33
  * Example: ["React.*", "Express.Request", "*.Event"]
16
34
  */
17
- ignoreTypes?: string[];
35
+ ignoreTypes?: string[]
18
36
  /**
19
37
  * Skip validation for DOM types (Document, Element, Node, etc.) and their subclasses.
20
38
  * These types have complex Window intersections that typia cannot process.
21
39
  * Default: true
22
40
  */
23
- ignoreDOMTypes?: boolean;
41
+ ignoreDOMTypes?: boolean
24
42
  /**
25
43
  * Validate function parameters and return types at runtime.
26
44
  * When enabled, typed function parameters get runtime validation calls injected.
27
45
  * Default: true
28
46
  */
29
- validateFunctions?: boolean;
47
+ validateFunctions?: boolean
48
+ /**
49
+ * Source map generation settings.
50
+ * Controls whether and how source maps are generated for transformed code.
51
+ */
52
+ sourceMap?: TypicalSourceMapConfig
53
+ }
54
+
55
+ /**
56
+ * Pre-compiled regex patterns for ignore type matching.
57
+ * This is populated during config loading for performance.
58
+ */
59
+ export interface CompiledIgnorePatterns {
60
+ /** Compiled patterns from user ignoreTypes config */
61
+ userPatterns: RegExp[]
62
+ /** Compiled patterns from DOM_TYPES_TO_IGNORE (when ignoreDOMTypes is true) */
63
+ domPatterns: RegExp[]
64
+ /** All patterns combined for quick checking */
65
+ allPatterns: RegExp[]
30
66
  }
31
67
 
32
68
  export const defaultConfig: TypicalConfig = {
33
- include: ["**/*.ts", "**/*.tsx"],
34
- exclude: ["node_modules/**", "**/*.d.ts", "dist/**", "build/**"],
35
- reusableValidators: true,
69
+ include: ['**/*.ts', '**/*.tsx'],
70
+ exclude: ['node_modules/**', '**/*.d.ts', 'dist/**', 'build/**'],
71
+ reusableValidators: false, // Off by default for accurate source maps (set to true for production)
36
72
  validateCasts: false,
37
73
  validateFunctions: true,
38
74
  hoistRegex: true,
@@ -40,7 +76,12 @@ export const defaultConfig: TypicalConfig = {
40
76
  debug: {
41
77
  writeIntermediateFiles: false,
42
78
  },
43
- };
79
+ sourceMap: {
80
+ enabled: true, // On by default for debugging (set to false for production)
81
+ includeContent: true,
82
+ inline: false,
83
+ },
84
+ }
44
85
 
45
86
  // FIXME: find a better way to work out which types to ignore
46
87
  /**
@@ -49,61 +90,163 @@ export const defaultConfig: TypicalConfig = {
49
90
  */
50
91
  export const DOM_TYPES_TO_IGNORE = [
51
92
  // Core DOM types
52
- "Document",
53
- "DocumentFragment",
54
- "Element",
55
- "Node",
56
- "ShadowRoot",
57
- "Window",
58
- "EventTarget",
93
+ 'Document',
94
+ 'DocumentFragment',
95
+ 'Element',
96
+ 'Node',
97
+ 'ShadowRoot',
98
+ 'Window',
99
+ 'EventTarget',
59
100
  // HTML Elements
60
- "HTML*Element",
61
- "HTMLElement",
62
- "HTMLCollection",
101
+ 'HTML*Element',
102
+ 'HTMLElement',
103
+ 'HTMLCollection',
63
104
  // SVG Elements
64
- "SVG*Element",
65
- "SVGElement",
105
+ 'SVG*Element',
106
+ 'SVGElement',
66
107
  // Events
67
- "*Event",
108
+ '*Event',
68
109
  // Other common DOM types
69
- "NodeList",
70
- "DOMTokenList",
71
- "NamedNodeMap",
72
- "CSSStyleDeclaration",
73
- "Selection",
74
- "Range",
75
- "Text",
76
- "Comment",
77
- "CDATASection",
78
- "ProcessingInstruction",
79
- "DocumentType",
80
- "Attr",
81
- "Table",
82
- "TableRow",
83
- "TableCell",
84
- "StyleSheet",
85
- ];
86
-
87
- import fs from 'fs';
88
- import path from 'path';
110
+ 'NodeList',
111
+ 'DOMTokenList',
112
+ 'NamedNodeMap',
113
+ 'CSSStyleDeclaration',
114
+ 'Selection',
115
+ 'Range',
116
+ 'Text',
117
+ 'Comment',
118
+ 'CDATASection',
119
+ 'ProcessingInstruction',
120
+ 'DocumentType',
121
+ 'Attr',
122
+ 'Table',
123
+ 'TableRow',
124
+ 'TableCell',
125
+ 'StyleSheet',
126
+ ]
127
+
128
+ import fs from 'fs'
129
+ import path from 'path'
130
+
131
+ /**
132
+ * Convert a glob pattern to a RegExp for type matching.
133
+ * Supports wildcards: "React.*" -> /^React\..*$/
134
+ */
135
+ export function compileIgnorePattern(pattern: string): RegExp | null {
136
+ try {
137
+ const regexStr =
138
+ '^' +
139
+ pattern
140
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
141
+ .replace(/\*/g, '.*') +
142
+ '$'
143
+ return new RegExp(regexStr)
144
+ } catch (error) {
145
+ console.warn(`TYPICAL: Invalid ignoreTypes pattern "${pattern}": ${(error as Error).message}`)
146
+ return null
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Pre-compile all ignore patterns for efficient matching.
152
+ */
153
+ export function compileIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
154
+ const userPatterns: RegExp[] = []
155
+ const domPatterns: RegExp[] = []
156
+
157
+ // Compile user patterns
158
+ for (const pattern of config.ignoreTypes ?? []) {
159
+ const compiled = compileIgnorePattern(pattern)
160
+ if (compiled) {
161
+ userPatterns.push(compiled)
162
+ }
163
+ }
164
+
165
+ // Compile DOM patterns if enabled (default: true)
166
+ if (config.ignoreDOMTypes !== false) {
167
+ for (const pattern of DOM_TYPES_TO_IGNORE) {
168
+ const compiled = compileIgnorePattern(pattern)
169
+ if (compiled) {
170
+ domPatterns.push(compiled)
171
+ }
172
+ }
173
+ }
174
+
175
+ return {
176
+ userPatterns,
177
+ domPatterns,
178
+ allPatterns: [...userPatterns, ...domPatterns],
179
+ }
180
+ }
181
+
182
+ // Cache for compiled patterns, keyed by config identity
183
+ let cachedPatterns: CompiledIgnorePatterns | null = null
184
+ let cachedConfig: TypicalConfig | null = null
185
+
186
+ /**
187
+ * Get compiled ignore patterns, using cache if config hasn't changed.
188
+ */
189
+ export function getCompiledIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
190
+ // Simple identity check - if same config object, use cache
191
+ if (cachedConfig === config && cachedPatterns) {
192
+ return cachedPatterns
193
+ }
194
+
195
+ cachedConfig = config
196
+ cachedPatterns = compileIgnorePatterns(config)
197
+ return cachedPatterns
198
+ }
89
199
 
90
200
  export function loadConfig(configPath?: string): TypicalConfig {
91
- const configFile = configPath || path.join(process.cwd(), 'typical.json');
92
-
201
+ const configFile = configPath || path.join(process.cwd(), 'typical.json')
202
+
93
203
  if (fs.existsSync(configFile)) {
94
204
  try {
95
- const configContent = fs.readFileSync(configFile, 'utf8');
96
- const userConfig: Partial<TypicalConfig> = JSON.parse(configContent);
97
-
205
+ const configContent = fs.readFileSync(configFile, 'utf8')
206
+ const userConfig: Partial<TypicalConfig> = JSON.parse(configContent)
207
+
98
208
  return {
99
209
  ...defaultConfig,
100
210
  ...userConfig,
101
- };
211
+ }
102
212
  } catch (error) {
103
- console.warn(`Failed to parse config file ${configFile}:`, error);
104
- return defaultConfig;
213
+ console.warn(`Failed to parse config file ${configFile}:`, error)
214
+ return defaultConfig
215
+ }
216
+ }
217
+
218
+ return defaultConfig
219
+ }
220
+
221
+ let warnedAboutSourceMaps = false
222
+
223
+ /**
224
+ * Validate and adjust config for consistency.
225
+ * Currently handles:
226
+ * - Disabling reusableValidators when source maps are enabled (required for accurate mappings)
227
+ *
228
+ * @param config The config to validate
229
+ * @returns Validated/adjusted config
230
+ */
231
+ export function validateConfig(config: TypicalConfig): TypicalConfig {
232
+ let result = config
233
+
234
+ // Source maps require inline validators (not reusable) because each validation
235
+ // call needs its own source map marker pointing to the correct type annotation.
236
+ // With reusable validators, the expanded typia code would all map to the validator
237
+ // declaration rather than the individual usage sites.
238
+ const sourceMapEnabled = config.sourceMap?.enabled !== false
239
+ const reusableValidatorsEnabled = config.reusableValidators === true
240
+
241
+ if (sourceMapEnabled && reusableValidatorsEnabled) {
242
+ if (!warnedAboutSourceMaps) {
243
+ warnedAboutSourceMaps = true
244
+ console.warn(
245
+ 'TYPICAL: Both sourceMap and reusableValidators are enabled. ' + 'Disabling reusableValidators for accurate source mapping. ' + 'For production builds, set sourceMap.enabled: false to use reusableValidators.',
246
+ )
105
247
  }
248
+ result = { ...result, reusableValidators: false }
106
249
  }
107
250
 
108
- return defaultConfig;
109
- }
251
+ return result
252
+ }
@@ -1,2 +1,2 @@
1
- import { register } from 'node:module';
2
- register('./esm-loader.js', { parentURL: import.meta.url });
1
+ import { register } from 'node:module'
2
+ register('./esm-loader.js', { parentURL: import.meta.url })