runtypex 0.1.8 → 0.1.9

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.
@@ -51,24 +51,39 @@ export default function tsTransformer(options) {
51
51
  };
52
52
  }
53
53
  function _resolveTypeByName(program, sf, checker, name) {
54
+ // 1️⃣ <string|number|boolean|null|undefined>
55
+ const primitiveNames = ["string", "number", "boolean", "bigint", "symbol", "null", "undefined"];
56
+ if (primitiveNames.includes(name)) {
57
+ const map = {
58
+ string: checker.getStringType(),
59
+ number: checker.getNumberType(),
60
+ boolean: checker.getBooleanType(),
61
+ bigint: checker.getBigIntType(),
62
+ symbol: checker.getESSymbolType(),
63
+ null: checker.getNullType(),
64
+ undefined: checker.getUndefinedType(),
65
+ };
66
+ return map[name];
67
+ }
68
+ // 2️⃣ <Type|Interface|Enum> declared in the same file or other files
54
69
  for (const file of program.getSourceFiles()) {
55
70
  const decl = _findLocalDeclaration(file, name);
56
71
  if (!decl)
57
72
  continue;
58
73
  if (ts.isInterfaceDeclaration(decl) || ts.isClassDeclaration(decl) || ts.isEnumDeclaration(decl)) {
59
- // @ts-ignore
60
- const sym = checker.getSymbolAtLocation(decl.name);
61
- if (sym)
62
- return checker.getDeclaredTypeOfSymbol(sym);
74
+ const symbol = decl.name ? checker.getSymbolAtLocation(decl.name) : null;
75
+ if (symbol)
76
+ return checker.getDeclaredTypeOfSymbol(symbol);
63
77
  }
64
78
  if (ts.isTypeAliasDeclaration(decl)) {
65
79
  return checker.getTypeFromTypeNode(decl.type);
66
80
  }
67
81
  }
68
- const sym = checker
82
+ // 3️⃣ <Type|Interface|Enum> imported from other modules
83
+ const symbol = checker
69
84
  .getSymbolsInScope(sf, ts.SymbolFlags.Type | ts.SymbolFlags.Alias | ts.SymbolFlags.Interface)
70
85
  .find((s) => s.name === name);
71
- return sym ? checker.getDeclaredTypeOfSymbol(sym) : null;
86
+ return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : null;
72
87
  }
73
88
  function _findLocalDeclaration(sf, name) {
74
89
  let found;
@@ -31,9 +31,9 @@ export default function vitePluginRuntypex(options) {
31
31
  if (!sf)
32
32
  return;
33
33
  let mutated = code;
34
- // makeAssert<T>()
34
+ // makeAssert<T>()
35
35
  mutated = mutated.replace(/makeAssert<\s*([^>]+)\s*>\s*\(\s*\)/g, (_m, typeName) => _emitMakeAssert({ program, checker, sf, typeName, prod, removeInProd }) ?? _m);
36
- // makeValidate<T>()
36
+ // makeValidate<T>()
37
37
  mutated = mutated.replace(/makeValidate<\s*([^>]+)\s*>\s*\(\s*\)/g, (_m, typeName) => _emitMakeValidate({ program, checker, sf, typeName, prod, removeInProd }) ?? _m);
38
38
  return mutated === code ? null : { code: mutated, map: null };
39
39
  },
@@ -45,9 +45,8 @@ export default function vitePluginRuntypex(options) {
45
45
  function _createProgramFor(file) {
46
46
  const tsconfig = _findNearestTsconfig(path.dirname(file));
47
47
  const cfg = ts.readConfigFile(tsconfig, ts.sys.readFile);
48
- if (cfg.error) {
48
+ if (cfg.error)
49
49
  throw new Error(ts.flattenDiagnosticMessageText(cfg.error.messageText, "\n"));
50
- }
51
50
  const parsed = ts.parseJsonConfigFileContent(cfg.config, ts.sys, path.dirname(tsconfig));
52
51
  const program = ts.createProgram({ rootNames: parsed.fileNames, options: parsed.options });
53
52
  const checker = program.getTypeChecker();
@@ -69,6 +68,9 @@ function _findNearestTsconfig(start) {
69
68
  return fallback;
70
69
  throw new Error("tsconfig.json not found");
71
70
  }
71
+ // ──────────────────────────────────────────────
72
+ // ② Emit Helpers
73
+ // ──────────────────────────────────────────────
72
74
  function _emitMakeValidate({ program, checker, sf, typeName, prod, removeInProd, }) {
73
75
  if (removeInProd && prod)
74
76
  return `((_)=>true)`;
@@ -87,32 +89,51 @@ function _emitMakeAssert({ program, checker, sf, typeName, prod, removeInProd, }
87
89
  return `(function(){const G=${guard};return(i)=>{if(!G(i))throw new TypeError("[runtypex] Validation failed.");};})()`;
88
90
  }
89
91
  // ──────────────────────────────────────────────
90
- // ③ Type Resolution (support interface/type/enum)
92
+ // ③ Type Resolution (support primitive/interface/type/enum)
91
93
  // ──────────────────────────────────────────────
92
94
  function _resolveTypeByName(program, sf, checker, name) {
93
- // scan all source files for local declaration
95
+ // -1️⃣ Primitive type fallback
96
+ const primitiveNames = ["string", "number", "boolean", "bigint", "symbol", "null", "undefined"];
97
+ if (primitiveNames.includes(name)) {
98
+ const map = {
99
+ string: checker.getStringType(),
100
+ number: checker.getNumberType(),
101
+ boolean: checker.getBooleanType(),
102
+ bigint: checker.getBigIntType(),
103
+ symbol: checker.getESSymbolType(),
104
+ null: checker.getNullType(),
105
+ undefined: checker.getUndefinedType(),
106
+ };
107
+ return map[name];
108
+ }
109
+ // 2️⃣ Scan source files
94
110
  for (const file of program.getSourceFiles()) {
95
111
  const decl = _findLocalDeclaration(file, name);
96
112
  if (!decl)
97
113
  continue;
98
- // interface / class / enum
99
- if (ts.isInterfaceDeclaration(decl) || ts.isClassDeclaration(decl) || ts.isEnumDeclaration(decl)) {
100
- // @ts-ignore
101
- const symbol = checker.getSymbolAtLocation(decl.name);
102
- if (symbol)
103
- return checker.getDeclaredTypeOfSymbol(symbol);
114
+ // ✅ type, interface, enum, class
115
+ if (ts.isInterfaceDeclaration(decl) ||
116
+ ts.isClassDeclaration(decl) ||
117
+ ts.isEnumDeclaration(decl)) {
118
+ if (decl.name) {
119
+ const symbol = checker.getSymbolAtLocation(decl.name);
120
+ if (symbol)
121
+ return checker.getDeclaredTypeOfSymbol(symbol);
122
+ }
104
123
  }
105
- // type alias
106
124
  if (ts.isTypeAliasDeclaration(decl)) {
107
125
  return checker.getTypeFromTypeNode(decl.type);
108
126
  }
109
127
  }
110
- // Scope-based fallback
128
+ // 3️⃣ Scope-based fallback
111
129
  const symbol = checker
112
130
  .getSymbolsInScope(sf, ts.SymbolFlags.Type | ts.SymbolFlags.Alias | ts.SymbolFlags.Interface)
113
131
  .find((s) => s.name === name);
114
132
  return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : null;
115
133
  }
134
+ // ──────────────────────────────────────────────
135
+ // ④ AST Utility
136
+ // ──────────────────────────────────────────────
116
137
  function _findLocalDeclaration(sf, name) {
117
138
  let found;
118
139
  (function walk(node) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runtypex",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Runtime type guards compiled from TypeScript types.",
5
5
  "license": "MIT",
6
6
  "author": "KumJungMin",
@@ -25,7 +25,8 @@
25
25
  },
26
26
  "files": ["dist"],
27
27
  "scripts": {
28
- "build": "tsc -p tsconfig.build.json"
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "test": "jest"
29
30
  },
30
31
  "devDependencies": {
31
32
  "typescript": "^5.6.3",