jsxpression 0.1.28 → 0.1.29
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 +155 -188
- package/lib/analyze/analysis-report.d.ts +2 -2
- package/lib/analyze/analysis-report.d.ts.map +1 -1
- package/lib/analyze/analysis-report.js +4 -0
- package/lib/analyze/analysis-report.js.map +1 -1
- package/lib/analyze/data/data-access.d.ts.map +1 -1
- package/lib/analyze/data/data-access.js +45 -82
- package/lib/analyze/data/data-access.js.map +1 -1
- package/lib/analyze/data/method-calls.d.ts.map +1 -1
- package/lib/analyze/data/method-calls.js +4 -3
- package/lib/analyze/data/method-calls.js.map +1 -1
- package/lib/analyze/data/utils.d.ts +2 -3
- package/lib/analyze/data/utils.d.ts.map +1 -1
- package/lib/analyze/data/utils.js +10 -36
- package/lib/analyze/data/utils.js.map +1 -1
- package/lib/analyze/jsx/element-attributes.d.ts +1 -1
- package/lib/analyze/jsx/element-attributes.d.ts.map +1 -1
- package/lib/analyze/jsx/element-attributes.js +13 -3
- package/lib/analyze/jsx/element-attributes.js.map +1 -1
- package/lib/analyze/jsx/element-children.d.ts +1 -1
- package/lib/analyze/jsx/element-children.d.ts.map +1 -1
- package/lib/analyze/jsx/element-children.js +16 -5
- package/lib/analyze/jsx/element-children.js.map +1 -1
- package/lib/analyze/jsx/element-tags.d.ts +1 -1
- package/lib/analyze/jsx/element-tags.d.ts.map +1 -1
- package/lib/analyze/jsx/element-tags.js +12 -2
- package/lib/analyze/jsx/element-tags.js.map +1 -1
- package/lib/analyze/security/analyze.d.ts.map +1 -1
- package/lib/analyze/security/analyze.js +2 -4
- package/lib/analyze/security/analyze.js.map +1 -1
- package/lib/analyze/security/call-expressions.d.ts +2 -2
- package/lib/analyze/security/call-expressions.d.ts.map +1 -1
- package/lib/analyze/security/call-expressions.js +21 -5
- package/lib/analyze/security/call-expressions.js.map +1 -1
- package/lib/analyze/security/declarations.d.ts +1 -1
- package/lib/analyze/security/declarations.d.ts.map +1 -1
- package/lib/analyze/security/declarations.js +22 -2
- package/lib/analyze/security/declarations.js.map +1 -1
- package/lib/analyze/security/expressions.d.ts.map +1 -1
- package/lib/analyze/security/expressions.js +10 -2
- package/lib/analyze/security/expressions.js.map +1 -1
- package/lib/analyze/security/identifiers.d.ts +2 -2
- package/lib/analyze/security/identifiers.d.ts.map +1 -1
- package/lib/analyze/security/identifiers.js +159 -19
- package/lib/analyze/security/identifiers.js.map +1 -1
- package/lib/builtins.d.ts.map +1 -1
- package/lib/builtins.js +5 -0
- package/lib/builtins.js.map +1 -1
- package/lib/compile/compile.d.ts.map +1 -1
- package/lib/compile/compile.js +131 -61
- package/lib/compile/compile.js.map +1 -1
- package/lib/evaluate/evaluate.d.ts +3 -3
- package/lib/evaluate/evaluate.d.ts.map +1 -1
- package/lib/evaluate/evaluate.js +28 -7
- package/lib/evaluate/evaluate.js.map +1 -1
- package/lib/jsx.d.ts +2 -19
- package/lib/jsx.d.ts.map +1 -1
- package/lib/jsx.js +0 -6
- package/lib/jsx.js.map +1 -1
- package/lib/parse/parse.d.ts.map +1 -1
- package/lib/parse/parse.js +4 -3
- package/lib/parse/parse.js.map +1 -1
- package/lib/render.d.ts +4 -4
- package/lib/render.d.ts.map +1 -1
- package/lib/render.js +7 -9
- package/lib/render.js.map +1 -1
- package/lib/schema.d.ts +2 -7
- package/lib/schema.d.ts.map +1 -1
- package/lib/schema.js +0 -43
- package/lib/schema.js.map +1 -1
- package/lib/traverse.d.ts +4 -4
- package/lib/traverse.d.ts.map +1 -1
- package/lib/traverse.js +118 -40
- package/lib/traverse.js.map +1 -1
- package/lib/validate.d.ts +1 -1
- package/lib/validate.js +1 -1
- package/package.json +4 -3
- package/src/analyze/analysis-report.ts +6 -2
- package/src/analyze/analyze.test.ts +108 -69
- package/src/analyze/data/data-access.ts +45 -90
- package/src/analyze/data/method-calls.ts +4 -3
- package/src/analyze/data/utils.ts +11 -40
- package/src/analyze/jsx/element-attributes.ts +24 -9
- package/src/analyze/jsx/element-children.ts +20 -6
- package/src/analyze/jsx/element-tags.ts +15 -3
- package/src/analyze/security/analyze.ts +1 -3
- package/src/analyze/security/call-expressions.ts +37 -17
- package/src/analyze/security/declarations.ts +35 -9
- package/src/analyze/security/expressions.ts +17 -3
- package/src/analyze/security/identifiers.ts +172 -28
- package/src/builtins.ts +6 -0
- package/src/codegen/generate-typescript-definitions.test.ts +16 -23
- package/src/compile/compile.test.ts +153 -30
- package/src/compile/compile.ts +163 -73
- package/src/evaluate/evaluate.test.ts +276 -14
- package/src/evaluate/evaluate.ts +34 -10
- package/src/jsx.ts +2 -31
- package/src/parse/parse.test.ts +4 -4
- package/src/parse/parse.ts +4 -3
- package/src/render.test.ts +476 -27
- package/src/render.ts +7 -14
- package/src/schema.ts +1 -55
- package/src/traverse.ts +135 -49
- package/src/validate.ts +1 -1
- package/LICENSE +0 -21
- package/lib/analyze/analyze.test.d.ts +0 -2
- package/lib/analyze/analyze.test.d.ts.map +0 -1
- package/lib/analyze/analyze.test.js +0 -763
- package/lib/analyze/analyze.test.js.map +0 -1
- package/lib/analyze/security/member-access.d.ts +0 -6
- package/lib/codegen/generate-typescript-definitions.test.d.ts +0 -2
- package/lib/codegen/generate-typescript-definitions.test.d.ts.map +0 -1
- package/lib/codegen/generate-typescript-definitions.test.js +0 -649
- package/lib/codegen/generate-typescript-definitions.test.js.map +0 -1
- package/lib/compile/compile.test.d.ts +0 -2
- package/lib/compile/compile.test.d.ts.map +0 -1
- package/lib/compile/compile.test.js +0 -146
- package/lib/compile/compile.test.js.map +0 -1
- package/lib/evaluate/evaluate.test.d.ts +0 -2
- package/lib/evaluate/evaluate.test.d.ts.map +0 -1
- package/lib/evaluate/evaluate.test.js +0 -260
- package/lib/evaluate/evaluate.test.js.map +0 -1
- package/lib/parse/parse.test.d.ts +0 -2
- package/lib/parse/parse.test.d.ts.map +0 -1
- package/lib/parse/parse.test.js +0 -80
- package/lib/parse/parse.test.js.map +0 -1
- package/lib/render.test.d.ts +0 -2
- package/lib/render.test.d.ts.map +0 -1
- package/lib/render.test.js +0 -301
- package/lib/render.test.js.map +0 -1
- package/lib/traverse.test.d.ts +0 -2
- package/lib/traverse.test.d.ts.map +0 -1
- package/lib/traverse.test.js +0 -84
- package/lib/traverse.test.js.map +0 -1
- package/lib/types.d.ts +0 -12
- package/src/analyze/security/member-access.ts +0 -35
- package/src/types.ts +0 -17
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Program } from "acorn";
|
|
2
|
-
import {
|
|
2
|
+
import { isJsxRoot } from "../../jsx.js";
|
|
3
3
|
import type { Schema } from "../../schema.js";
|
|
4
|
-
import {
|
|
4
|
+
import { traverse } from "../../traverse.js";
|
|
5
5
|
import { AnalysisReport } from "../analysis-report.js";
|
|
6
6
|
import { getNodeRange } from "../utils.js";
|
|
7
|
+
import type { ValidationContext } from "../validation-context.js";
|
|
7
8
|
|
|
8
9
|
export function analyzeDeclarations(
|
|
9
10
|
ast: Program,
|
|
@@ -11,6 +12,7 @@ export function analyzeDeclarations(
|
|
|
11
12
|
validationContext: ValidationContext
|
|
12
13
|
): AnalysisReport {
|
|
13
14
|
const analysisReport = new AnalysisReport();
|
|
15
|
+
const topLevelNodes = new Set(ast.body);
|
|
14
16
|
|
|
15
17
|
traverse(ast, {
|
|
16
18
|
ImportDeclaration(node) {
|
|
@@ -38,12 +40,14 @@ export function analyzeDeclarations(
|
|
|
38
40
|
);
|
|
39
41
|
},
|
|
40
42
|
FunctionDeclaration(node) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if (!topLevelNodes.has(node as any)) {
|
|
44
|
+
analysisReport.addIssue(
|
|
45
|
+
"FUNCTION_NOT_ALLOWED",
|
|
46
|
+
"nested function declarations not allowed",
|
|
47
|
+
getNodeRange(node),
|
|
48
|
+
validationContext.getSnapshot()
|
|
49
|
+
);
|
|
50
|
+
}
|
|
47
51
|
},
|
|
48
52
|
ClassDeclaration(node) {
|
|
49
53
|
analysisReport.addIssue(
|
|
@@ -54,14 +58,36 @@ export function analyzeDeclarations(
|
|
|
54
58
|
);
|
|
55
59
|
},
|
|
56
60
|
VariableDeclaration(node) {
|
|
61
|
+
if (node.kind === "const") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
57
64
|
analysisReport.addIssue(
|
|
58
65
|
"VARIABLE_NOT_ALLOWED",
|
|
59
|
-
|
|
66
|
+
`${node.kind} not allowed, use const`,
|
|
60
67
|
getNodeRange(node),
|
|
61
68
|
validationContext.getSnapshot()
|
|
62
69
|
);
|
|
63
70
|
},
|
|
64
71
|
});
|
|
65
72
|
|
|
73
|
+
const hasDeclarations = ast.body.some((s) => s.type === "VariableDeclaration" || s.type === "FunctionDeclaration");
|
|
74
|
+
if (hasDeclarations) {
|
|
75
|
+
const lastStatement = ast.body[ast.body.length - 1];
|
|
76
|
+
if (!lastStatement || lastStatement.type !== "ReturnStatement") {
|
|
77
|
+
const isBareJsx = lastStatement?.type === "ExpressionStatement" && isJsxRoot(lastStatement.expression);
|
|
78
|
+
const range = lastStatement
|
|
79
|
+
? getNodeRange(lastStatement)
|
|
80
|
+
: { start: { line: 1, column: 0 }, end: { line: 1, column: 0 } };
|
|
81
|
+
analysisReport.addIssue(
|
|
82
|
+
"RETURN_REQUIRED",
|
|
83
|
+
isBareJsx
|
|
84
|
+
? "Templates with declarations must end with a return statement. Add 'return' before the JSX."
|
|
85
|
+
: "Templates with declarations must end with a return statement",
|
|
86
|
+
range,
|
|
87
|
+
validationContext.getSnapshot()
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
66
92
|
return analysisReport;
|
|
67
93
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Program } from "acorn";
|
|
1
|
+
import type { Program, Expression, Super } from "acorn";
|
|
2
2
|
import { traverse } from "../../traverse.js";
|
|
3
3
|
import type { Schema } from "../../schema.js";
|
|
4
4
|
import { ValidationContext } from "../validation-context.js";
|
|
@@ -76,10 +76,24 @@ export function analyzeExpressions(
|
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
MemberExpression(node) {
|
|
79
|
+
let depth = 0;
|
|
80
|
+
let currentNode: Expression | Super = node;
|
|
81
|
+
while (currentNode.type === "MemberExpression") {
|
|
82
|
+
depth += 1;
|
|
83
|
+
currentNode = currentNode.object;
|
|
84
|
+
}
|
|
85
|
+
if (depth > 5) {
|
|
86
|
+
analysisReport.addIssue(
|
|
87
|
+
"MEMBER_CHAIN_TOO_DEEP",
|
|
88
|
+
"member chain too deep",
|
|
89
|
+
getNodeRange(node),
|
|
90
|
+
validationContext.getSnapshot()
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
if (node.computed) {
|
|
80
|
-
// We allow numeric literals for array access (data.items[0])
|
|
81
95
|
if (node.property.type === "Literal" && typeof node.property.value === "number") {
|
|
82
|
-
return;
|
|
96
|
+
return;
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
analysisReport.addIssue(
|
|
@@ -1,36 +1,71 @@
|
|
|
1
|
-
import type { AnyNode, ArrowFunctionExpression, Identifier, Program } from "acorn";
|
|
2
|
-
import { traverse } from "../../traverse.js";
|
|
3
|
-
import type { Schema } from "../../schema.js";
|
|
1
|
+
import type { AnyNode, ArrowFunctionExpression, FunctionDeclaration, Identifier, Program } from "acorn";
|
|
4
2
|
import { getBuiltinGlobals } from "../../builtins.js";
|
|
5
|
-
import {
|
|
3
|
+
import type { Schema } from "../../schema.js";
|
|
4
|
+
import { traverse } from "../../traverse.js";
|
|
6
5
|
import { AnalysisReport } from "../analysis-report.js";
|
|
7
6
|
import { getNodeRange } from "../utils.js";
|
|
7
|
+
import type { ValidationContext } from "../validation-context.js";
|
|
8
8
|
|
|
9
|
-
export function analyzeIdentifiers(
|
|
10
|
-
ast: Program,
|
|
11
|
-
_schema: Schema,
|
|
12
|
-
validationContext: ValidationContext
|
|
13
|
-
): AnalysisReport {
|
|
9
|
+
export function analyzeIdentifiers(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
|
|
14
10
|
const analysisReport = new AnalysisReport();
|
|
15
11
|
const arrowParamScopes = getArrowParamScopes(ast);
|
|
12
|
+
const functionParamScopes = getFunctionParamScopes(ast);
|
|
13
|
+
const functionLocalConsts = getFunctionLocalConsts(ast);
|
|
14
|
+
const parentMap = buildParentMap(ast);
|
|
15
|
+
|
|
16
|
+
const constNames = getTopLevelConstNames(ast);
|
|
17
|
+
const functionNames = getTopLevelFunctionNames(ast);
|
|
18
|
+
const schemaFunctionNames = Object.keys(schema.functions ?? {});
|
|
16
19
|
|
|
17
20
|
traverse(ast, {
|
|
18
21
|
Identifier(node: Identifier) {
|
|
19
22
|
const { name } = node;
|
|
20
23
|
|
|
21
|
-
const
|
|
24
|
+
const parent = parentMap.get(node);
|
|
25
|
+
if (parent?.type === "MemberExpression" && parent.property === node && !parent.computed) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (parent?.type === "Property" && parent.key === node && !parent.computed) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (parent?.type === "VariableDeclarator" && parent.id === node) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (parent?.type === "FunctionDeclaration" && parent.id === node) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const dataKeys = Object.keys(schema.data ?? {});
|
|
39
|
+
const allowedRoots = [
|
|
40
|
+
"props",
|
|
41
|
+
...dataKeys,
|
|
42
|
+
...getBuiltinGlobals(),
|
|
43
|
+
...constNames,
|
|
44
|
+
...functionNames,
|
|
45
|
+
...schemaFunctionNames,
|
|
46
|
+
];
|
|
22
47
|
|
|
23
48
|
if (allowedRoots.includes(name)) {
|
|
24
49
|
return;
|
|
25
50
|
}
|
|
26
51
|
|
|
27
|
-
const
|
|
52
|
+
const scopeParent = findScopeParent(node, ast);
|
|
28
53
|
|
|
29
|
-
if (
|
|
30
|
-
const params =
|
|
54
|
+
if (scopeParent) {
|
|
55
|
+
const params =
|
|
56
|
+
scopeParent.type === "ArrowFunctionExpression"
|
|
57
|
+
? arrowParamScopes.get(scopeParent)
|
|
58
|
+
: functionParamScopes.get(scopeParent as FunctionDeclaration);
|
|
31
59
|
if (params?.has(name)) {
|
|
32
60
|
return;
|
|
33
61
|
}
|
|
62
|
+
|
|
63
|
+
if (scopeParent.type === "FunctionDeclaration") {
|
|
64
|
+
const localConsts = functionLocalConsts.get(scopeParent as FunctionDeclaration);
|
|
65
|
+
if (localConsts?.has(name)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
34
69
|
}
|
|
35
70
|
|
|
36
71
|
analysisReport.addIssue(
|
|
@@ -46,10 +81,6 @@ export function analyzeIdentifiers(
|
|
|
46
81
|
return analysisReport;
|
|
47
82
|
}
|
|
48
83
|
|
|
49
|
-
// Build a map of arrow functions to their parameter names so we can check if an
|
|
50
|
-
// identifier is a legit arrow function param or something shady. We do this in
|
|
51
|
-
// a separate traversal because it's cleaner than trying to track scope during the
|
|
52
|
-
// main validation pass.
|
|
53
84
|
function getArrowParamScopes(ast: Program): Map<ArrowFunctionExpression, Set<string>> {
|
|
54
85
|
const arrowParamScopes = new Map<ArrowFunctionExpression, Set<string>>();
|
|
55
86
|
traverse(ast, {
|
|
@@ -58,9 +89,7 @@ function getArrowParamScopes(ast: Program): Map<ArrowFunctionExpression, Set<str
|
|
|
58
89
|
if (Array.isArray(params)) {
|
|
59
90
|
const paramNames = new Set<string>();
|
|
60
91
|
params.forEach((p) => {
|
|
61
|
-
|
|
62
|
-
paramNames.add(p.name);
|
|
63
|
-
}
|
|
92
|
+
collectParamNames(p, paramNames);
|
|
64
93
|
});
|
|
65
94
|
arrowParamScopes.set(node, paramNames);
|
|
66
95
|
}
|
|
@@ -69,20 +98,107 @@ function getArrowParamScopes(ast: Program): Map<ArrowFunctionExpression, Set<str
|
|
|
69
98
|
return arrowParamScopes;
|
|
70
99
|
}
|
|
71
100
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
function getFunctionParamScopes(ast: Program): Map<FunctionDeclaration, Set<string>> {
|
|
102
|
+
const functionParamScopes = new Map<FunctionDeclaration, Set<string>>();
|
|
103
|
+
traverse(ast, {
|
|
104
|
+
FunctionDeclaration(node) {
|
|
105
|
+
const paramNames = new Set<string>();
|
|
106
|
+
for (const param of node.params) {
|
|
107
|
+
collectParamNames(param, paramNames);
|
|
108
|
+
}
|
|
109
|
+
functionParamScopes.set(node as FunctionDeclaration, paramNames);
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return functionParamScopes;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getFunctionLocalConsts(ast: Program): Map<FunctionDeclaration, Set<string>> {
|
|
116
|
+
const map = new Map<FunctionDeclaration, Set<string>>();
|
|
117
|
+
traverse(ast, {
|
|
118
|
+
FunctionDeclaration(node) {
|
|
119
|
+
const names = new Set<string>();
|
|
120
|
+
const body = (node as FunctionDeclaration).body;
|
|
121
|
+
if (body?.type === "BlockStatement") {
|
|
122
|
+
for (const stmt of body.body) {
|
|
123
|
+
if (stmt.type === "VariableDeclaration" && stmt.kind === "const") {
|
|
124
|
+
for (const decl of stmt.declarations) {
|
|
125
|
+
if (decl.id.type === "Identifier") {
|
|
126
|
+
names.add(decl.id.name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
map.set(node as FunctionDeclaration, names);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
return map;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function collectParamNames(param: AnyNode, names: Set<string>): void {
|
|
139
|
+
switch (param.type) {
|
|
140
|
+
case "Identifier":
|
|
141
|
+
names.add((param as Identifier).name);
|
|
142
|
+
break;
|
|
143
|
+
case "ObjectPattern":
|
|
144
|
+
for (const prop of (param as any).properties) {
|
|
145
|
+
if (prop.type === "RestElement") {
|
|
146
|
+
collectParamNames(prop.argument, names);
|
|
147
|
+
} else {
|
|
148
|
+
collectParamNames(prop.value, names);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case "ArrayPattern":
|
|
153
|
+
for (const el of (param as any).elements) {
|
|
154
|
+
if (el) collectParamNames(el, names);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
case "AssignmentPattern":
|
|
158
|
+
collectParamNames((param as any).left, names);
|
|
159
|
+
break;
|
|
160
|
+
case "RestElement":
|
|
161
|
+
collectParamNames((param as any).argument, names);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getTopLevelConstNames(ast: Program): string[] {
|
|
167
|
+
const names: string[] = [];
|
|
168
|
+
for (const stmt of ast.body) {
|
|
169
|
+
if (stmt.type === "VariableDeclaration" && stmt.kind === "const") {
|
|
170
|
+
for (const decl of stmt.declarations) {
|
|
171
|
+
if (decl.id.type === "Identifier") {
|
|
172
|
+
names.push(decl.id.name);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return names;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getTopLevelFunctionNames(ast: Program): string[] {
|
|
181
|
+
const names: string[] = [];
|
|
182
|
+
for (const stmt of ast.body) {
|
|
183
|
+
if (stmt.type === "FunctionDeclaration" && stmt.id) {
|
|
184
|
+
names.push(stmt.id.name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return names;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function findScopeParent(targetNode: AnyNode, rootNode: AnyNode): ArrowFunctionExpression | FunctionDeclaration | null {
|
|
191
|
+
let found: ArrowFunctionExpression | FunctionDeclaration | null = null;
|
|
76
192
|
|
|
77
193
|
function walk(node: AnyNode): boolean {
|
|
78
194
|
if (node === targetNode) {
|
|
79
195
|
return true;
|
|
80
196
|
}
|
|
81
197
|
|
|
82
|
-
const
|
|
198
|
+
const isScope = node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration";
|
|
83
199
|
|
|
84
|
-
if (
|
|
85
|
-
found = node as ArrowFunctionExpression;
|
|
200
|
+
if (isScope) {
|
|
201
|
+
found = node as ArrowFunctionExpression | FunctionDeclaration;
|
|
86
202
|
}
|
|
87
203
|
|
|
88
204
|
for (const key of Object.keys(node)) {
|
|
@@ -101,7 +217,7 @@ function findArrowParent(targetNode: AnyNode, rootNode: AnyNode): ArrowFunctionE
|
|
|
101
217
|
}
|
|
102
218
|
}
|
|
103
219
|
|
|
104
|
-
if (
|
|
220
|
+
if (isScope) {
|
|
105
221
|
found = null;
|
|
106
222
|
}
|
|
107
223
|
|
|
@@ -111,3 +227,31 @@ function findArrowParent(targetNode: AnyNode, rootNode: AnyNode): ArrowFunctionE
|
|
|
111
227
|
walk(rootNode);
|
|
112
228
|
return found;
|
|
113
229
|
}
|
|
230
|
+
|
|
231
|
+
function buildParentMap(ast: Program): Map<any, any> {
|
|
232
|
+
const parentMap = new Map<any, any>();
|
|
233
|
+
|
|
234
|
+
function visit(node: any, parent: any): void {
|
|
235
|
+
if (!node || typeof node !== "object") return;
|
|
236
|
+
if (node.type) {
|
|
237
|
+
parentMap.set(node, parent);
|
|
238
|
+
}
|
|
239
|
+
for (const key of Object.keys(node)) {
|
|
240
|
+
const value = node[key];
|
|
241
|
+
if (value && typeof value === "object") {
|
|
242
|
+
if (Array.isArray(value)) {
|
|
243
|
+
for (const child of value) {
|
|
244
|
+
if (child && typeof child === "object" && child.type) {
|
|
245
|
+
visit(child, node);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} else if (value.type) {
|
|
249
|
+
visit(value, node);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
visit(ast, null);
|
|
256
|
+
return parentMap;
|
|
257
|
+
}
|
package/src/builtins.ts
CHANGED
|
@@ -49,6 +49,7 @@ export interface BuiltinSchema {
|
|
|
49
49
|
const MATH = "Math";
|
|
50
50
|
const NUMBER = "Number";
|
|
51
51
|
const NUMBER_PROTOTYPE = "Number.prototype";
|
|
52
|
+
const STRING = "String";
|
|
52
53
|
const ARRAY = "Array";
|
|
53
54
|
const ARRAY_PROTOTYPE = "Array.prototype";
|
|
54
55
|
const STRING_PROTOTYPE = "String.prototype";
|
|
@@ -739,6 +740,11 @@ const BUILTINS: Record<string, BuiltinSchema> = {
|
|
|
739
740
|
},
|
|
740
741
|
},
|
|
741
742
|
|
|
743
|
+
[STRING]: {
|
|
744
|
+
global: true,
|
|
745
|
+
intrinsic: false,
|
|
746
|
+
},
|
|
747
|
+
|
|
742
748
|
[STRING_PROTOTYPE]: {
|
|
743
749
|
global: false,
|
|
744
750
|
intrinsic: true,
|
|
@@ -265,8 +265,7 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
265
265
|
|
|
266
266
|
const result = generateTypeScriptDefinitions(schema);
|
|
267
267
|
|
|
268
|
-
expect(result).toContain("const
|
|
269
|
-
expect(result).toContain("userName: string");
|
|
268
|
+
expect(result).toContain("const userName: string");
|
|
270
269
|
});
|
|
271
270
|
|
|
272
271
|
it("should generate data with multiple property types", () => {
|
|
@@ -281,10 +280,9 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
281
280
|
|
|
282
281
|
const result = generateTypeScriptDefinitions(schema);
|
|
283
282
|
|
|
284
|
-
expect(result).toContain("const
|
|
285
|
-
expect(result).toContain("
|
|
286
|
-
expect(result).toContain("
|
|
287
|
-
expect(result).toContain("active: boolean");
|
|
283
|
+
expect(result).toContain("const name: string");
|
|
284
|
+
expect(result).toContain("const age: number");
|
|
285
|
+
expect(result).toContain("const active: boolean");
|
|
288
286
|
});
|
|
289
287
|
|
|
290
288
|
it("should generate data with object property", () => {
|
|
@@ -303,8 +301,7 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
303
301
|
|
|
304
302
|
const result = generateTypeScriptDefinitions(schema);
|
|
305
303
|
|
|
306
|
-
expect(result).toContain("const
|
|
307
|
-
expect(result).toContain("user:");
|
|
304
|
+
expect(result).toContain("const user:");
|
|
308
305
|
expect(result).toContain("name: string");
|
|
309
306
|
expect(result).toContain("email: string");
|
|
310
307
|
});
|
|
@@ -331,8 +328,7 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
331
328
|
|
|
332
329
|
const result = generateTypeScriptDefinitions(schema);
|
|
333
330
|
|
|
334
|
-
expect(result).toContain("const
|
|
335
|
-
expect(result).toContain("user:");
|
|
331
|
+
expect(result).toContain("const user:");
|
|
336
332
|
expect(result).toContain("profile:");
|
|
337
333
|
expect(result).toContain("avatar: string");
|
|
338
334
|
expect(result).toContain("bio: string");
|
|
@@ -355,9 +351,8 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
355
351
|
|
|
356
352
|
const result = generateTypeScriptDefinitions(schema);
|
|
357
353
|
|
|
358
|
-
expect(result).toContain("const
|
|
359
|
-
expect(result).toContain("
|
|
360
|
-
expect(result).toContain("scores: number[]");
|
|
354
|
+
expect(result).toContain("const tags: string[]");
|
|
355
|
+
expect(result).toContain("const scores: number[]");
|
|
361
356
|
});
|
|
362
357
|
|
|
363
358
|
it("should generate data with array of objects", () => {
|
|
@@ -379,8 +374,7 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
379
374
|
|
|
380
375
|
const result = generateTypeScriptDefinitions(schema);
|
|
381
376
|
|
|
382
|
-
expect(result).toContain("const
|
|
383
|
-
expect(result).toContain("users:");
|
|
377
|
+
expect(result).toContain("const users:");
|
|
384
378
|
expect(result).toContain("name: string");
|
|
385
379
|
expect(result).toContain("age: number");
|
|
386
380
|
expect(result).toContain("[]");
|
|
@@ -419,9 +413,10 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
419
413
|
|
|
420
414
|
const result = generateTypeScriptDefinitions(schema);
|
|
421
415
|
|
|
422
|
-
expect(result).toContain("const
|
|
423
|
-
expect(result).toContain('
|
|
424
|
-
expect(result).toContain("priority:
|
|
416
|
+
expect(result).toContain("const status:");
|
|
417
|
+
expect(result).toContain('"pending" | "active" | "completed"');
|
|
418
|
+
expect(result).toContain("const priority:");
|
|
419
|
+
expect(result).toContain("1 | 2 | 3");
|
|
425
420
|
});
|
|
426
421
|
});
|
|
427
422
|
|
|
@@ -448,9 +443,8 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
448
443
|
|
|
449
444
|
const result = generateTypeScriptDefinitions(schema);
|
|
450
445
|
|
|
451
|
-
expect(result).toContain("const
|
|
452
|
-
expect(result).toContain("
|
|
453
|
-
expect(result).toContain("count: number");
|
|
446
|
+
expect(result).toContain("const userName: string");
|
|
447
|
+
expect(result).toContain("const count: number");
|
|
454
448
|
|
|
455
449
|
expect(result).toContain("interface DisplayProps");
|
|
456
450
|
expect(result).toContain("interface CounterProps");
|
|
@@ -589,8 +583,7 @@ describe("generateTypeScriptDefinitions", () => {
|
|
|
589
583
|
expect(result).toContain("declare global");
|
|
590
584
|
expect(result).toContain("export {};");
|
|
591
585
|
|
|
592
|
-
expect(result).toContain("const
|
|
593
|
-
expect(result).toContain("user:");
|
|
586
|
+
expect(result).toContain("const user:");
|
|
594
587
|
expect(result).toContain("name: string");
|
|
595
588
|
expect(result).toContain("email: string");
|
|
596
589
|
expect(result).toContain("settings:");
|