@nudojs/parser 0.5.1 → 1.1.0-beta.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/README.md +3 -3
- package/dist/index.d.ts +90 -7
- package/dist/index.js +240 -73
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,15 +4,15 @@ Babel-based parser and directive extraction for [Nudo](https://github.com/nudojs
|
|
|
4
4
|
|
|
5
5
|
## What is Nudo?
|
|
6
6
|
|
|
7
|
-
Nudo is a type inference engine for JavaScript.
|
|
7
|
+
Nudo is a type inference engine for JavaScript. The type system is Abs (`shape × term × pred × conf`); analysis runs Abs-native via abstract interpretation — no TypeScript, no build step.
|
|
8
8
|
|
|
9
9
|
## This package
|
|
10
10
|
|
|
11
11
|
`@nudojs/parser` handles source code parsing and Nudo directive extraction:
|
|
12
12
|
|
|
13
13
|
- **Parsing** — wraps Babel parser for JavaScript/TypeScript source files
|
|
14
|
-
- **Directives** — extracts `@nudo:case`, `@nudo:mock`, `@nudo:pure`, `@nudo:skip`, `@nudo:sample`, `@nudo:env`, `@nudo:mock-module`, `@nudo:as`, and `@nudo:replace` from comments. `@nudo:
|
|
15
|
-
- **
|
|
14
|
+
- **Directives** — extracts `@nudo:case`, `@nudo:mock`, `@nudo:pure`, `@nudo:skip`, `@nudo:sample`, `@nudo:env`, `@nudo:mock-module`, `@nudo:as`, and `@nudo:replace` from comments. `@nudo:contract` / `@nudo:import` are parsed in `@nudojs/core` (`algebra/refine.ts`).
|
|
15
|
+
- **Case arg expressions** — parses constraint builders and concrete literals used in directives
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { File, Node } from '@babel/types';
|
|
1
|
+
import { File, Node, AssignmentExpression, Expression, Identifier, MemberExpression, Program, ClassBody, VariableDeclarator, ExportSpecifier, ExportDefaultSpecifier, ExportNamespaceSpecifier, Statement } from '@babel/types';
|
|
2
2
|
import { Abs, MockHelper } from '@nudojs/core';
|
|
3
3
|
export { stripTypes } from '@nudojs/core';
|
|
4
4
|
|
|
@@ -15,10 +15,95 @@ declare function parse(source: string, opts?: {
|
|
|
15
15
|
errorRecovery?: boolean;
|
|
16
16
|
}): File;
|
|
17
17
|
|
|
18
|
+
/** File → its Program; Program → itself. Replaces `(ast as File).program ?? ast`. */
|
|
19
|
+
declare function asProgram(ast: Node | File | Program | null | undefined): Program | undefined;
|
|
20
|
+
/** Top-level statements of a File/Program (empty when neither). */
|
|
21
|
+
declare function programBody(ast: Node | File | Program | null | undefined): Statement[];
|
|
22
|
+
declare function asIdentifier(node: Node | null | undefined): Identifier | undefined;
|
|
23
|
+
declare function asExpression(node: Node | null | undefined): Expression | undefined;
|
|
24
|
+
declare function asAssignmentExpression(node: Node | null | undefined): AssignmentExpression | undefined;
|
|
25
|
+
/** Identifier.name, else StringLiteral.value (module string export names). */
|
|
26
|
+
declare function nameOrStringValue(node: Node | null | undefined): string | undefined;
|
|
27
|
+
/** Identifier.name only (undefined for string export names). */
|
|
28
|
+
declare function identifierName(node: Node | null | undefined): string | undefined;
|
|
29
|
+
/** VariableDeclaration.declarations; empty for other nodes. */
|
|
30
|
+
declare function getDeclarations(node: Node | null | undefined): VariableDeclarator[];
|
|
31
|
+
/** First declarator's init (fn-binding extraction). */
|
|
32
|
+
declare function getDeclaratorInit(node: Node | null | undefined): Expression | undefined;
|
|
33
|
+
/** First declarator of a VariableDeclaration, narrowed to Identifier id. */
|
|
34
|
+
declare function getFirstDeclaratorId(node: Node | null | undefined): Identifier | undefined;
|
|
35
|
+
/** VariableDeclarator id narrowed to Identifier. */
|
|
36
|
+
declare function getDeclaratorId(node: Node | null | undefined): Identifier | undefined;
|
|
37
|
+
/** Inner declaration of `export default` / `export …` (undefined for other nodes). */
|
|
38
|
+
declare function getExportDeclaration(node: Node | null | undefined): Node | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Unwrap an export form: named/default exports yield their inner declaration
|
|
41
|
+
* with `exported: true`; any other statement is returned as-is with `exported: false`.
|
|
42
|
+
*/
|
|
43
|
+
declare function unwrapExport(node: Node | null | undefined): {
|
|
44
|
+
declaration: Node;
|
|
45
|
+
exported: boolean;
|
|
46
|
+
};
|
|
47
|
+
/** ExportNamedDeclaration.specifiers (empty for other nodes). */
|
|
48
|
+
declare function getExportSpecifiers(node: Node | null | undefined): (ExportSpecifier | ExportDefaultSpecifier | ExportNamespaceSpecifier)[];
|
|
49
|
+
/** ExportSpecifier local name — Identifier only (matches `local.name`). */
|
|
50
|
+
declare function exportSpecifierLocalName(spec: Node | null | undefined): string | undefined;
|
|
51
|
+
/** ExportSpecifier exported name (`exported.name ?? exported.value`). */
|
|
52
|
+
declare function exportSpecifierExportedName(spec: Node | null | undefined): string | number | undefined;
|
|
53
|
+
/** ExpressionStatement.expression. */
|
|
54
|
+
declare function getExpressionStatementExpression(node: Node | null | undefined): Expression | undefined;
|
|
55
|
+
/** Function/Class declaration or expression `id?.name`. */
|
|
56
|
+
declare function fnOrClassIdName(node: Node | null | undefined): string | undefined;
|
|
57
|
+
/** ClassDeclaration/ClassExpression `id?.name`. */
|
|
58
|
+
declare function classIdName(node: Node | null | undefined): string | undefined;
|
|
59
|
+
/** `id.loc` of a named function/class (undefined when anonymous or unlocated). */
|
|
60
|
+
declare function fnOrClassIdLoc(node: Node | null | undefined): Node["loc"];
|
|
61
|
+
/** Non-computed member property key text (Identifier.name or StringLiteral.value). */
|
|
62
|
+
declare function memberPropertyKey(member: Node | null | undefined): string | null;
|
|
63
|
+
/** Identifier name of a non-computed member's object (e.g. `module` in `module.exports`). */
|
|
64
|
+
declare function memberObjectName(member: Node | null | undefined): string | undefined;
|
|
65
|
+
/** Identifier name of a non-computed member's property (e.g. `exports`). */
|
|
66
|
+
declare function memberPropertyName(member: Node | null | undefined): string | undefined;
|
|
67
|
+
/** `module.exports` / `module["exports"]` is rejected here: computed keys return false. */
|
|
68
|
+
declare function isModuleExportsMember(member: Node | null | undefined): boolean;
|
|
69
|
+
declare function asMemberExpression(node: Node | null | undefined): MemberExpression | undefined;
|
|
70
|
+
/** Class body members; empty when node is not a class. */
|
|
71
|
+
declare function getClassMembers(node: Node | null | undefined): ClassBody["body"];
|
|
72
|
+
/**
|
|
73
|
+
* `key.name ?? key.value` on a class/object member key.
|
|
74
|
+
* Handles Identifier / StringLiteral / NumericLiteral and ESTree-shaped keys.
|
|
75
|
+
*/
|
|
76
|
+
declare function classMemberKeyName(member: Node | null | undefined): string | number | undefined;
|
|
77
|
+
/** Key node of a class member (for range selection). */
|
|
78
|
+
declare function classMemberKey(member: Node | null | undefined): Node | undefined;
|
|
79
|
+
type InstanceMethod = {
|
|
80
|
+
name: string;
|
|
81
|
+
node: Node;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Instance methods of a class-like node. Accepts Babel `ClassMethod` /
|
|
85
|
+
* `TSDeclareMethod` and ESTree `MethodDefinition` (whose function lives in `value`).
|
|
86
|
+
* Skips getters/setters/constructors and static members.
|
|
87
|
+
*/
|
|
88
|
+
declare function classInstanceMethods(node: Node | null | undefined): InstanceMethod[];
|
|
89
|
+
/** Class/TSDeclare method function node (ESTree MethodDefinition → its `value`). */
|
|
90
|
+
declare function methodFunctionNode(member: Node | null | undefined): Node | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Parameter display label: `name`, `...rest`, or `_`.
|
|
93
|
+
* Non-identifier rest arguments keep the historical `...undefined` string form.
|
|
94
|
+
*/
|
|
95
|
+
declare function paramDisplayName(param: Node | null | undefined): string;
|
|
96
|
+
/** RestElement argument name, else plain Identifier name. */
|
|
97
|
+
declare function paramName(param: Node | null | undefined): string | undefined;
|
|
98
|
+
/** CJS/ESM default interop: callable module or `{ default }` wrapper. */
|
|
99
|
+
declare function unwrapDefaultExport<T>(mod: T | {
|
|
100
|
+
default: T;
|
|
101
|
+
}): T;
|
|
102
|
+
|
|
18
103
|
type CaseDirective = {
|
|
19
104
|
kind: "case";
|
|
20
105
|
name: string;
|
|
21
|
-
/** 无损参数 Abs(case
|
|
106
|
+
/** 无损参数 Abs(case 文法唯一真理源:约束构建器 / 具体字面量) */
|
|
22
107
|
argsAbs: Abs[];
|
|
23
108
|
/** `=> expected` 断言 Abs */
|
|
24
109
|
expected?: Abs;
|
|
@@ -82,14 +167,12 @@ type FunctionWithDirectives = {
|
|
|
82
167
|
directives: Directive[];
|
|
83
168
|
};
|
|
84
169
|
/**
|
|
85
|
-
* case
|
|
86
|
-
*
|
|
170
|
+
* case 实参 / 指令类型表达式唯一文法:约束构建器优先,其余为具体字面量、
|
|
171
|
+
* 结构字面量与箭头函数。`T.*` 文法已物理删除。
|
|
87
172
|
*/
|
|
88
173
|
declare function parseCaseArgExpr(expr: string): Abs;
|
|
89
|
-
/** @deprecated 旧名兼容;等价 parseCaseArgExpr */
|
|
90
|
-
declare function parseTypeValueExpr(expr: string): Abs;
|
|
91
174
|
declare function extractDirectives(ast: Node): FunctionWithDirectives[];
|
|
92
175
|
declare function extractInlineDirectives(node: Node): InlineDirective[];
|
|
93
176
|
declare function extractFileDirectives(ast: Node): FileDirective[];
|
|
94
177
|
|
|
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,
|
|
178
|
+
export { type AsDirective, type CaseDirective, type Directive, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type InstanceMethod, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, asAssignmentExpression, asExpression, asIdentifier, asMemberExpression, asProgram, classIdName, classInstanceMethods, classMemberKey, classMemberKeyName, exportSpecifierExportedName, exportSpecifierLocalName, extractDirectives, extractFileDirectives, extractInlineDirectives, fnOrClassIdLoc, fnOrClassIdName, getClassMembers, getDeclarations, getDeclaratorId, getDeclaratorInit, getExportDeclaration, getExportSpecifiers, getExpressionStatementExpression, getFirstDeclaratorId, identifierName, isModuleExportsMember, memberObjectName, memberPropertyKey, memberPropertyName, methodFunctionNode, nameOrStringValue, paramDisplayName, paramName, parse, parseCaseArgExpr, programBody, unwrapDefaultExport, unwrapExport };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,173 @@ function parse(source, opts) {
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import { stripTypes } from "@nudojs/core";
|
|
9
9
|
|
|
10
|
+
// src/ast-guards.ts
|
|
11
|
+
import { isExpression } from "@babel/types";
|
|
12
|
+
function asProgram(ast) {
|
|
13
|
+
if (!ast) return void 0;
|
|
14
|
+
if (ast.type === "File") return ast.program;
|
|
15
|
+
if (ast.type === "Program") return ast;
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
function programBody(ast) {
|
|
19
|
+
return asProgram(ast)?.body ?? [];
|
|
20
|
+
}
|
|
21
|
+
function asIdentifier(node) {
|
|
22
|
+
return node?.type === "Identifier" ? node : void 0;
|
|
23
|
+
}
|
|
24
|
+
function asExpression(node) {
|
|
25
|
+
return node && isExpression(node) ? node : void 0;
|
|
26
|
+
}
|
|
27
|
+
function asAssignmentExpression(node) {
|
|
28
|
+
return node?.type === "AssignmentExpression" ? node : void 0;
|
|
29
|
+
}
|
|
30
|
+
function nameOrStringValue(node) {
|
|
31
|
+
if (!node) return void 0;
|
|
32
|
+
if (node.type === "Identifier") return node.name;
|
|
33
|
+
if (node.type === "StringLiteral") return node.value;
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
function identifierName(node) {
|
|
37
|
+
return node?.type === "Identifier" ? node.name : void 0;
|
|
38
|
+
}
|
|
39
|
+
function getDeclarations(node) {
|
|
40
|
+
return node?.type === "VariableDeclaration" ? node.declarations : [];
|
|
41
|
+
}
|
|
42
|
+
function getDeclaratorInit(node) {
|
|
43
|
+
const decl = node?.type === "VariableDeclarator" ? node : void 0;
|
|
44
|
+
return decl?.init ?? void 0;
|
|
45
|
+
}
|
|
46
|
+
function getFirstDeclaratorId(node) {
|
|
47
|
+
const decl = getDeclarations(node)[0];
|
|
48
|
+
return decl?.id.type === "Identifier" ? decl.id : void 0;
|
|
49
|
+
}
|
|
50
|
+
function getDeclaratorId(node) {
|
|
51
|
+
const decl = node?.type === "VariableDeclarator" ? node : void 0;
|
|
52
|
+
return decl?.id.type === "Identifier" ? decl.id : void 0;
|
|
53
|
+
}
|
|
54
|
+
function getExportDeclaration(node) {
|
|
55
|
+
if (!node) return void 0;
|
|
56
|
+
if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
|
|
57
|
+
return node.declaration ?? void 0;
|
|
58
|
+
}
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
function unwrapExport(node) {
|
|
62
|
+
if (node?.type === "ExportNamedDeclaration" && node.declaration) {
|
|
63
|
+
return { declaration: node.declaration, exported: true };
|
|
64
|
+
}
|
|
65
|
+
if (node?.type === "ExportDefaultDeclaration") {
|
|
66
|
+
return { declaration: node.declaration, exported: true };
|
|
67
|
+
}
|
|
68
|
+
return { declaration: node, exported: false };
|
|
69
|
+
}
|
|
70
|
+
function getExportSpecifiers(node) {
|
|
71
|
+
return node?.type === "ExportNamedDeclaration" ? node.specifiers : [];
|
|
72
|
+
}
|
|
73
|
+
function exportSpecifierLocalName(spec) {
|
|
74
|
+
return spec?.type === "ExportSpecifier" ? identifierName(spec.local) : void 0;
|
|
75
|
+
}
|
|
76
|
+
function exportSpecifierExportedName(spec) {
|
|
77
|
+
if (spec?.type !== "ExportSpecifier") return void 0;
|
|
78
|
+
const exported = spec.exported;
|
|
79
|
+
return exported.type === "Identifier" ? exported.name : exported.value;
|
|
80
|
+
}
|
|
81
|
+
function getExpressionStatementExpression(node) {
|
|
82
|
+
return node?.type === "ExpressionStatement" ? node.expression : void 0;
|
|
83
|
+
}
|
|
84
|
+
function fnOrClassIdName(node) {
|
|
85
|
+
if (!node) return void 0;
|
|
86
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ClassDeclaration" || node.type === "ClassExpression" || node.type === "TSDeclareFunction") {
|
|
87
|
+
return node.id?.name;
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
function classIdName(node) {
|
|
92
|
+
if (!node) return void 0;
|
|
93
|
+
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
|
|
94
|
+
return node.id?.name;
|
|
95
|
+
}
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
function fnOrClassIdLoc(node) {
|
|
99
|
+
if (!node) return void 0;
|
|
100
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ClassDeclaration" || node.type === "ClassExpression" || node.type === "TSDeclareFunction") {
|
|
101
|
+
return node.id?.loc ?? void 0;
|
|
102
|
+
}
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
function memberPropertyKey(member) {
|
|
106
|
+
if (member?.type !== "MemberExpression" || member.computed) return null;
|
|
107
|
+
return nameOrStringValue(member.property) ?? null;
|
|
108
|
+
}
|
|
109
|
+
function memberObjectName(member) {
|
|
110
|
+
if (member?.type !== "MemberExpression") return void 0;
|
|
111
|
+
return identifierName(member.object);
|
|
112
|
+
}
|
|
113
|
+
function memberPropertyName(member) {
|
|
114
|
+
if (member?.type !== "MemberExpression" || member.computed) return void 0;
|
|
115
|
+
return identifierName(member.property);
|
|
116
|
+
}
|
|
117
|
+
function isModuleExportsMember(member) {
|
|
118
|
+
return memberObjectName(member) === "module" && memberPropertyName(member) === "exports";
|
|
119
|
+
}
|
|
120
|
+
function asMemberExpression(node) {
|
|
121
|
+
return node?.type === "MemberExpression" ? node : void 0;
|
|
122
|
+
}
|
|
123
|
+
function getClassMembers(node) {
|
|
124
|
+
if (node?.type === "ClassDeclaration" || node?.type === "ClassExpression") {
|
|
125
|
+
return node.body.body;
|
|
126
|
+
}
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
function classMemberKeyName(member) {
|
|
130
|
+
const key = member?.key;
|
|
131
|
+
return key?.name ?? key?.value;
|
|
132
|
+
}
|
|
133
|
+
function classMemberKey(member) {
|
|
134
|
+
return member?.key ?? void 0;
|
|
135
|
+
}
|
|
136
|
+
function classInstanceMethods(node) {
|
|
137
|
+
const out = [];
|
|
138
|
+
for (const raw of getClassMembers(node)) {
|
|
139
|
+
const m = raw;
|
|
140
|
+
const isMethod = m.type === "MethodDefinition" || m.type === "ClassMethod" || m.type === "TSDeclareMethod";
|
|
141
|
+
if (!isMethod) continue;
|
|
142
|
+
if (m.kind && m.kind !== "method") continue;
|
|
143
|
+
if (m.static) continue;
|
|
144
|
+
const keyName = m.key?.type === "Identifier" ? m.key.name : void 0;
|
|
145
|
+
if (!keyName) continue;
|
|
146
|
+
const methodNode = m.type === "MethodDefinition" ? m.value ?? void 0 : raw;
|
|
147
|
+
if (!methodNode) continue;
|
|
148
|
+
out.push({ name: keyName, node: methodNode });
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
function methodFunctionNode(member) {
|
|
153
|
+
const m = member;
|
|
154
|
+
if (!m) return void 0;
|
|
155
|
+
return m.type === "MethodDefinition" ? m.value ?? void 0 : member;
|
|
156
|
+
}
|
|
157
|
+
function paramDisplayName(param) {
|
|
158
|
+
if (!param) return "_";
|
|
159
|
+
if (param.type === "Identifier") return param.name;
|
|
160
|
+
if (param.type === "RestElement") {
|
|
161
|
+
const arg = param.argument;
|
|
162
|
+
return `...${arg.name}`;
|
|
163
|
+
}
|
|
164
|
+
return "_";
|
|
165
|
+
}
|
|
166
|
+
function paramName(param) {
|
|
167
|
+
if (!param) return void 0;
|
|
168
|
+
if (param.type === "Identifier") return param.name;
|
|
169
|
+
if (param.type === "RestElement") return identifierName(param.argument);
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
function unwrapDefaultExport(mod) {
|
|
173
|
+
if (typeof mod === "function") return mod;
|
|
174
|
+
return mod.default;
|
|
175
|
+
}
|
|
176
|
+
|
|
10
177
|
// src/directives.ts
|
|
11
178
|
import {
|
|
12
179
|
stub,
|
|
@@ -14,9 +181,6 @@ import {
|
|
|
14
181
|
mock,
|
|
15
182
|
abs as makeAbs,
|
|
16
183
|
lit as termLit,
|
|
17
|
-
num,
|
|
18
|
-
str,
|
|
19
|
-
bool,
|
|
20
184
|
numLit,
|
|
21
185
|
strLit,
|
|
22
186
|
boolLit,
|
|
@@ -46,17 +210,13 @@ function absLit(v) {
|
|
|
46
210
|
if (v === null) return absNullLit();
|
|
47
211
|
return absUndefLit();
|
|
48
212
|
}
|
|
49
|
-
function absUnion(members) {
|
|
50
|
-
if (members.length === 0) return absExact({ k: "never" });
|
|
51
|
-
return members.reduce((acc, m) => joinAbs(acc, m));
|
|
52
|
-
}
|
|
53
213
|
var CASE_NAME_REGEX = /@nudo:case\s+"([^"]+)"\s*\(/g;
|
|
54
214
|
var MOCK_INLINE_REGEX = /@nudo:mock\s+(\w+)\s*=\s*(.+)/g;
|
|
55
215
|
var MOCK_FROM_REGEX = /@nudo:mock\s+(\w+)\s+from\s+"([^"]+)"/g;
|
|
56
216
|
var PURE_REGEX = /@nudo:pure\b/g;
|
|
57
217
|
var SKIP_REGEX = /@nudo:skip(?:\s+(.+))?/g;
|
|
58
218
|
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*\(/;
|
|
219
|
+
var CONSTRAINT_EXPR_RE = /^(number|string|boolean|any|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
|
|
60
220
|
function tryParseConstraint(expr) {
|
|
61
221
|
const s = expr.trim();
|
|
62
222
|
if (!CONSTRAINT_EXPR_RE.test(s)) return void 0;
|
|
@@ -85,15 +245,15 @@ function flattenPreds(preds) {
|
|
|
85
245
|
function constraintSelfEqLit(c, selfIds) {
|
|
86
246
|
const leaves = flattenPreds(c.preds);
|
|
87
247
|
const eqs = leaves.filter((p2) => p2.op === "eq");
|
|
88
|
-
if (eqs.length !== 1) return
|
|
248
|
+
if (eqs.length !== 1) return { found: false };
|
|
89
249
|
const p = eqs[0];
|
|
90
|
-
if (p.op !== "eq") return
|
|
250
|
+
if (p.op !== "eq") return { found: false };
|
|
91
251
|
const isSelf = (t) => t.op === "var" && typeof t.id === "string" && selfIds.includes(t.id);
|
|
92
252
|
const a = p.a;
|
|
93
253
|
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
|
|
254
|
+
if (isSelf(a) && b.op === "lit") return { found: true, value: b.value };
|
|
255
|
+
if (isSelf(b) && a.op === "lit") return { found: true, value: a.value };
|
|
256
|
+
return { found: false };
|
|
97
257
|
}
|
|
98
258
|
function constraintToCaseArgAbs(c) {
|
|
99
259
|
if (c.members && c.members.length > 0) {
|
|
@@ -102,10 +262,32 @@ function constraintToCaseArgAbs(c) {
|
|
|
102
262
|
return { shape: { k: "sum", members: parts }, conf: "path" };
|
|
103
263
|
}
|
|
104
264
|
const selfIds = [SELF, "__arg", "__nudo_self__"];
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
265
|
+
const litRes = constraintSelfEqLit(c, selfIds);
|
|
266
|
+
if (litRes.found) {
|
|
267
|
+
const lv = litRes.value;
|
|
268
|
+
if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
|
|
269
|
+
if (typeof lv === "string") return strLit(lv);
|
|
270
|
+
if (typeof lv === "boolean") return boolLit(lv);
|
|
271
|
+
if (lv === null) return absNullLit();
|
|
272
|
+
return absUndefLit();
|
|
273
|
+
}
|
|
274
|
+
if (c.element) {
|
|
275
|
+
return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element) });
|
|
276
|
+
}
|
|
277
|
+
if (c.fields) {
|
|
278
|
+
const slots = {};
|
|
279
|
+
for (const [key, field] of Object.entries(c.fields)) {
|
|
280
|
+
slots[key] = { value: constraintToCaseArgAbs(field.constraint) };
|
|
281
|
+
}
|
|
282
|
+
return absExact({ k: "obj", slots });
|
|
283
|
+
}
|
|
284
|
+
const bareScalar = !c.fields && !c.element && !c.members && !c.fn && c.int !== true && c.preds.length === 0;
|
|
285
|
+
if (bareScalar) {
|
|
286
|
+
if (c.prim === "number" || c.prim === "string" || c.prim === "boolean") {
|
|
287
|
+
return absExact({ k: "prim", type: c.prim });
|
|
288
|
+
}
|
|
289
|
+
if (!c.prim) return absUnknown();
|
|
290
|
+
}
|
|
109
291
|
return constraintToEntryAbs(c, "__arg");
|
|
110
292
|
}
|
|
111
293
|
function parseCaseArgExpr(expr) {
|
|
@@ -117,52 +299,16 @@ function parseCaseArgExpr(expr) {
|
|
|
117
299
|
return absUnknown();
|
|
118
300
|
}
|
|
119
301
|
}
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
function parseTypeValueExpr(expr) {
|
|
123
|
-
return parseCaseArgExpr(expr);
|
|
302
|
+
return parseLiteralOrStructure(expr);
|
|
124
303
|
}
|
|
125
|
-
function
|
|
304
|
+
function parseLiteralOrStructure(expr) {
|
|
126
305
|
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
306
|
if (s === "true") return absLit(true);
|
|
135
307
|
if (s === "false") return absLit(false);
|
|
136
308
|
if (s === "null") return absLit(null);
|
|
137
309
|
if (s === "undefined") return absLit(void 0);
|
|
138
|
-
|
|
139
|
-
if (
|
|
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
|
-
}
|
|
310
|
+
if (s === "unknown" || s === "any") return absUnknown();
|
|
311
|
+
if (s === "never") return absExact({ k: "never" });
|
|
166
312
|
if (findTopLevelArrow(s) !== -1 || /^function\s*[\w$]*\s*\(/.test(s)) {
|
|
167
313
|
const fnExpr = parseArrowFunctionExpr(s);
|
|
168
314
|
if (fnExpr) {
|
|
@@ -180,7 +326,7 @@ function parseLegacyTypeExpr(expr) {
|
|
|
180
326
|
const content = s.slice(1, -1).trim();
|
|
181
327
|
if (!content) return absExact({ k: "tuple", elements: [] });
|
|
182
328
|
const elements = splitTopLevelArgs(content);
|
|
183
|
-
return absExact({ k: "tuple", elements: elements.map(
|
|
329
|
+
return absExact({ k: "tuple", elements: elements.map((e) => parseCaseArgExpr(e)) });
|
|
184
330
|
}
|
|
185
331
|
return absUnknown();
|
|
186
332
|
}
|
|
@@ -193,21 +339,10 @@ function parseObjectLiteral(content) {
|
|
|
193
339
|
if (colonIdx === -1) continue;
|
|
194
340
|
const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
|
|
195
341
|
const val = entry.slice(colonIdx + 1).trim();
|
|
196
|
-
slots[key] = { value:
|
|
342
|
+
slots[key] = { value: parseCaseArgExpr(val) };
|
|
197
343
|
}
|
|
198
344
|
return absExact({ k: "obj", slots });
|
|
199
345
|
}
|
|
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
346
|
function splitTopLevelArgs(s) {
|
|
212
347
|
const result = [];
|
|
213
348
|
let depth = 0;
|
|
@@ -622,7 +757,7 @@ function findReplaceSeparator(raw) {
|
|
|
622
757
|
}
|
|
623
758
|
if (ch === " " && depth === 0) {
|
|
624
759
|
const rest = raw.slice(i + 1).trimStart();
|
|
625
|
-
if (
|
|
760
|
+
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
761
|
return i;
|
|
627
762
|
}
|
|
628
763
|
}
|
|
@@ -668,11 +803,43 @@ function extractFileDirectives(ast) {
|
|
|
668
803
|
return results;
|
|
669
804
|
}
|
|
670
805
|
export {
|
|
806
|
+
asAssignmentExpression,
|
|
807
|
+
asExpression,
|
|
808
|
+
asIdentifier,
|
|
809
|
+
asMemberExpression,
|
|
810
|
+
asProgram,
|
|
811
|
+
classIdName,
|
|
812
|
+
classInstanceMethods,
|
|
813
|
+
classMemberKey,
|
|
814
|
+
classMemberKeyName,
|
|
815
|
+
exportSpecifierExportedName,
|
|
816
|
+
exportSpecifierLocalName,
|
|
671
817
|
extractDirectives,
|
|
672
818
|
extractFileDirectives,
|
|
673
819
|
extractInlineDirectives,
|
|
820
|
+
fnOrClassIdLoc,
|
|
821
|
+
fnOrClassIdName,
|
|
822
|
+
getClassMembers,
|
|
823
|
+
getDeclarations,
|
|
824
|
+
getDeclaratorId,
|
|
825
|
+
getDeclaratorInit,
|
|
826
|
+
getExportDeclaration,
|
|
827
|
+
getExportSpecifiers,
|
|
828
|
+
getExpressionStatementExpression,
|
|
829
|
+
getFirstDeclaratorId,
|
|
830
|
+
identifierName,
|
|
831
|
+
isModuleExportsMember,
|
|
832
|
+
memberObjectName,
|
|
833
|
+
memberPropertyKey,
|
|
834
|
+
memberPropertyName,
|
|
835
|
+
methodFunctionNode,
|
|
836
|
+
nameOrStringValue,
|
|
837
|
+
paramDisplayName,
|
|
838
|
+
paramName,
|
|
674
839
|
parse,
|
|
675
840
|
parseCaseArgExpr,
|
|
676
|
-
|
|
677
|
-
stripTypes
|
|
841
|
+
programBody,
|
|
842
|
+
stripTypes,
|
|
843
|
+
unwrapDefaultExport,
|
|
844
|
+
unwrapExport
|
|
678
845
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.1.0-beta.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"registry": "https://registry.npmjs.org"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@
|
|
42
|
+
"@babel/types": "^8.0.6",
|
|
43
|
+
"@nudojs/core": "3.0.0-beta.0"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "tsup src/index.ts --format esm --dts"
|