@nudojs/parser 0.5.1 → 1.0.0

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/index.d.ts CHANGED
@@ -18,7 +18,7 @@ declare function parse(source: string, opts?: {
18
18
  type CaseDirective = {
19
19
  kind: "case";
20
20
  name: string;
21
- /** 无损参数 Abs(case 文法唯一真理源;约束表达式与 T.* 均产出) */
21
+ /** 无损参数 Abs(case 文法唯一真理源:约束构建器 / 具体字面量) */
22
22
  argsAbs: Abs[];
23
23
  /** `=> expected` 断言 Abs */
24
24
  expected?: Abs;
@@ -82,14 +82,12 @@ type FunctionWithDirectives = {
82
82
  directives: Directive[];
83
83
  };
84
84
  /**
85
- * case 实参主路径:始终产出 Abs;约束表达式优先。
86
- * `T.*` 文法 **已弃用**,仅为旧 fixture 兼容保留——新代码用 `number()`/`lit()`。
85
+ * case 实参 / 指令类型表达式唯一文法:约束构建器优先,其余为具体字面量、
86
+ * 结构字面量与箭头函数。`T.*` 文法已物理删除。
87
87
  */
88
88
  declare function parseCaseArgExpr(expr: string): Abs;
89
- /** @deprecated 旧名兼容;等价 parseCaseArgExpr */
90
- declare function parseTypeValueExpr(expr: string): Abs;
91
89
  declare function extractDirectives(ast: Node): FunctionWithDirectives[];
92
90
  declare function extractInlineDirectives(node: Node): InlineDirective[];
93
91
  declare function extractFileDirectives(ast: Node): FileDirective[];
94
92
 
95
- export { type AsDirective, type CaseDirective, type Directive, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, extractDirectives, extractFileDirectives, extractInlineDirectives, parse, parseCaseArgExpr, parseTypeValueExpr };
93
+ export { type AsDirective, type CaseDirective, type Directive, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, extractDirectives, extractFileDirectives, extractInlineDirectives, parse, parseCaseArgExpr };
package/dist/index.js CHANGED
@@ -14,9 +14,6 @@ import {
14
14
  mock,
15
15
  abs as makeAbs,
16
16
  lit as termLit,
17
- num,
18
- str,
19
- bool,
20
17
  numLit,
21
18
  strLit,
22
19
  boolLit,
@@ -46,17 +43,13 @@ function absLit(v) {
46
43
  if (v === null) return absNullLit();
47
44
  return absUndefLit();
48
45
  }
49
- function absUnion(members) {
50
- if (members.length === 0) return absExact({ k: "never" });
51
- return members.reduce((acc, m) => joinAbs(acc, m));
52
- }
53
46
  var CASE_NAME_REGEX = /@nudo:case\s+"([^"]+)"\s*\(/g;
54
47
  var MOCK_INLINE_REGEX = /@nudo:mock\s+(\w+)\s*=\s*(.+)/g;
55
48
  var MOCK_FROM_REGEX = /@nudo:mock\s+(\w+)\s+from\s+"([^"]+)"/g;
56
49
  var PURE_REGEX = /@nudo:pure\b/g;
57
50
  var SKIP_REGEX = /@nudo:skip(?:\s+(.+))?/g;
58
51
  var SAMPLE_REGEX = /@nudo:sample\s+(\d+)/g;
59
- var CONSTRAINT_EXPR_RE = /^(number|string|boolean|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
52
+ var CONSTRAINT_EXPR_RE = /^(number|string|boolean|any|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
60
53
  function tryParseConstraint(expr) {
61
54
  const s = expr.trim();
62
55
  if (!CONSTRAINT_EXPR_RE.test(s)) return void 0;
@@ -85,15 +78,15 @@ function flattenPreds(preds) {
85
78
  function constraintSelfEqLit(c, selfIds) {
86
79
  const leaves = flattenPreds(c.preds);
87
80
  const eqs = leaves.filter((p2) => p2.op === "eq");
88
- if (eqs.length !== 1) return void 0;
81
+ if (eqs.length !== 1) return { found: false };
89
82
  const p = eqs[0];
90
- if (p.op !== "eq") return void 0;
83
+ if (p.op !== "eq") return { found: false };
91
84
  const isSelf = (t) => t.op === "var" && typeof t.id === "string" && selfIds.includes(t.id);
92
85
  const a = p.a;
93
86
  const b = p.b;
94
- if (isSelf(a) && b.op === "lit") return b.value;
95
- if (isSelf(b) && a.op === "lit") return a.value;
96
- return void 0;
87
+ if (isSelf(a) && b.op === "lit") return { found: true, value: b.value };
88
+ if (isSelf(b) && a.op === "lit") return { found: true, value: a.value };
89
+ return { found: false };
97
90
  }
98
91
  function constraintToCaseArgAbs(c) {
99
92
  if (c.members && c.members.length > 0) {
@@ -102,10 +95,32 @@ function constraintToCaseArgAbs(c) {
102
95
  return { shape: { k: "sum", members: parts }, conf: "path" };
103
96
  }
104
97
  const selfIds = [SELF, "__arg", "__nudo_self__"];
105
- const lv = constraintSelfEqLit(c, selfIds);
106
- if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
107
- if (typeof lv === "string") return strLit(lv);
108
- if (typeof lv === "boolean") return boolLit(lv);
98
+ const litRes = constraintSelfEqLit(c, selfIds);
99
+ if (litRes.found) {
100
+ const lv = litRes.value;
101
+ if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
102
+ if (typeof lv === "string") return strLit(lv);
103
+ if (typeof lv === "boolean") return boolLit(lv);
104
+ if (lv === null) return absNullLit();
105
+ return absUndefLit();
106
+ }
107
+ if (c.element) {
108
+ return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element) });
109
+ }
110
+ if (c.fields) {
111
+ const slots = {};
112
+ for (const [key, field] of Object.entries(c.fields)) {
113
+ slots[key] = { value: constraintToCaseArgAbs(field.constraint) };
114
+ }
115
+ return absExact({ k: "obj", slots });
116
+ }
117
+ const bareScalar = !c.fields && !c.element && !c.members && !c.fn && c.int !== true && c.preds.length === 0;
118
+ if (bareScalar) {
119
+ if (c.prim === "number" || c.prim === "string" || c.prim === "boolean") {
120
+ return absExact({ k: "prim", type: c.prim });
121
+ }
122
+ if (!c.prim) return absUnknown();
123
+ }
109
124
  return constraintToEntryAbs(c, "__arg");
110
125
  }
111
126
  function parseCaseArgExpr(expr) {
@@ -117,52 +132,16 @@ function parseCaseArgExpr(expr) {
117
132
  return absUnknown();
118
133
  }
119
134
  }
120
- return parseLegacyTypeExpr(expr);
121
- }
122
- function parseTypeValueExpr(expr) {
123
- return parseCaseArgExpr(expr);
135
+ return parseLiteralOrStructure(expr);
124
136
  }
125
- function parseLegacyTypeExpr(expr) {
137
+ function parseLiteralOrStructure(expr) {
126
138
  const s = expr.trim();
127
- if (s === "T.number") return num();
128
- if (s === "T.string") return str();
129
- if (s === "T.boolean") return bool();
130
- if (s === "T.unknown") return absUnknown();
131
- if (s === "T.never") return absExact({ k: "never" });
132
- if (s === "T.null") return absNullLit();
133
- if (s === "T.undefined") return absUndefLit();
134
139
  if (s === "true") return absLit(true);
135
140
  if (s === "false") return absLit(false);
136
141
  if (s === "null") return absLit(null);
137
142
  if (s === "undefined") return absLit(void 0);
138
- const literalMatch = s.match(/^T\.literal\((.+)\)$/);
139
- if (literalMatch) {
140
- return absLit(parsePrimitiveValue(literalMatch[1].trim()));
141
- }
142
- if (s.startsWith("T.union(") && s.endsWith(")")) {
143
- const inner = s.slice("T.union(".length, -1);
144
- const args = splitTopLevelArgs(inner);
145
- return absUnion(args.map(parseLegacyTypeExpr));
146
- }
147
- if (s.startsWith("T.array(") && s.endsWith(")")) {
148
- const inner = s.slice("T.array(".length, -1);
149
- return absExact({ k: "arr", element: parseLegacyTypeExpr(inner) });
150
- }
151
- if (s.startsWith("T.tuple(") && s.endsWith(")")) {
152
- const inner = s.slice("T.tuple(".length, -1).trim();
153
- if (inner.startsWith("[") && inner.endsWith("]")) {
154
- const elements = splitTopLevelArgs(inner.slice(1, -1));
155
- return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
156
- }
157
- return absExact({ k: "tuple", elements: [] });
158
- }
159
- if (s.startsWith("T.object(") && s.endsWith(")")) {
160
- const inner = s.slice("T.object(".length, -1).trim();
161
- if (inner.startsWith("{") && inner.endsWith("}")) {
162
- return parseObjectLiteral(inner.slice(1, -1).trim());
163
- }
164
- return absExact({ k: "obj", slots: {} });
165
- }
143
+ if (s === "unknown" || s === "any") return absUnknown();
144
+ if (s === "never") return absExact({ k: "never" });
166
145
  if (findTopLevelArrow(s) !== -1 || /^function\s*[\w$]*\s*\(/.test(s)) {
167
146
  const fnExpr = parseArrowFunctionExpr(s);
168
147
  if (fnExpr) {
@@ -180,7 +159,7 @@ function parseLegacyTypeExpr(expr) {
180
159
  const content = s.slice(1, -1).trim();
181
160
  if (!content) return absExact({ k: "tuple", elements: [] });
182
161
  const elements = splitTopLevelArgs(content);
183
- return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
162
+ return absExact({ k: "tuple", elements: elements.map((e) => parseCaseArgExpr(e)) });
184
163
  }
185
164
  return absUnknown();
186
165
  }
@@ -193,21 +172,10 @@ function parseObjectLiteral(content) {
193
172
  if (colonIdx === -1) continue;
194
173
  const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
195
174
  const val = entry.slice(colonIdx + 1).trim();
196
- slots[key] = { value: parseLegacyTypeExpr(val) };
175
+ slots[key] = { value: parseCaseArgExpr(val) };
197
176
  }
198
177
  return absExact({ k: "obj", slots });
199
178
  }
200
- function parsePrimitiveValue(s) {
201
- if (s === "true") return true;
202
- if (s === "false") return false;
203
- if (s === "null") return null;
204
- if (s === "undefined") return void 0;
205
- if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
206
- if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
207
- return s.slice(1, -1);
208
- }
209
- return s;
210
- }
211
179
  function splitTopLevelArgs(s) {
212
180
  const result = [];
213
181
  let depth = 0;
@@ -622,7 +590,7 @@ function findReplaceSeparator(raw) {
622
590
  }
623
591
  if (ch === " " && depth === 0) {
624
592
  const rest = raw.slice(i + 1).trimStart();
625
- if (rest.startsWith("T.") || rest.startsWith("{") || rest.startsWith("[") || rest.startsWith('"') || rest.startsWith("'") || /^-?\d/.test(rest) || rest === "true" || rest === "false" || rest === "null" || rest === "undefined") {
593
+ if (CONSTRAINT_EXPR_RE.test(rest) || rest.startsWith("{") || rest.startsWith("[") || rest.startsWith('"') || rest.startsWith("'") || rest === "true" || rest === "false" || rest === "null" || rest === "undefined" || rest === "unknown" || rest === "any" || rest === "never" || /^-?\d+(\.\d+)?$/.test(rest) || rest.includes("=>") || /^function\s*[\w$]*\s*\(/.test(rest)) {
626
594
  return i;
627
595
  }
628
596
  }
@@ -673,6 +641,5 @@ export {
673
641
  extractInlineDirectives,
674
642
  parse,
675
643
  parseCaseArgExpr,
676
- parseTypeValueExpr,
677
644
  stripTypes
678
645
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nudojs/parser",
3
- "version": "0.5.1",
3
+ "version": "1.0.0",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },
@@ -39,7 +39,7 @@
39
39
  "registry": "https://registry.npmjs.org"
40
40
  },
41
41
  "dependencies": {
42
- "@nudojs/core": "2.0.1"
42
+ "@nudojs/core": "2.1.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsup src/index.ts --format esm --dts"