jsxpression 0.1.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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +368 -0
  3. package/lib/analyze/analysis-error.d.ts.map +1 -0
  4. package/lib/analyze/analysis-report.d.ts +33 -0
  5. package/lib/analyze/analysis-report.d.ts.map +1 -0
  6. package/lib/analyze/analysis-report.js +194 -0
  7. package/lib/analyze/analysis-report.js.map +1 -0
  8. package/lib/analyze/analyze.d.ts.map +1 -0
  9. package/lib/analyze/analyze.test.d.ts.map +1 -0
  10. package/lib/analyze/analyze.test.js +765 -0
  11. package/lib/analyze/analyze.test.js.map +1 -0
  12. package/lib/analyze/data/analyze.d.ts.map +1 -0
  13. package/lib/analyze/data/data-access.d.ts.map +1 -0
  14. package/lib/analyze/data/index.d.ts.map +1 -0
  15. package/lib/analyze/data/method-calls.d.ts.map +1 -0
  16. package/lib/analyze/data/utils.d.ts.map +1 -0
  17. package/lib/analyze/index.d.ts.map +1 -0
  18. package/lib/analyze/jsx/analyze.d.ts.map +1 -0
  19. package/lib/analyze/jsx/element-attributes.d.ts.map +1 -0
  20. package/lib/analyze/jsx/element-attributes.js +89 -0
  21. package/lib/analyze/jsx/element-attributes.js.map +1 -0
  22. package/lib/analyze/jsx/element-children.d.ts.map +1 -0
  23. package/lib/analyze/jsx/element-tags.d.ts.map +1 -0
  24. package/lib/analyze/jsx/index.d.ts.map +1 -0
  25. package/lib/analyze/security/analyze.d.ts.map +1 -0
  26. package/lib/analyze/security/call-expressions.d.ts.map +1 -0
  27. package/lib/analyze/security/declarations.d.ts.map +1 -0
  28. package/lib/analyze/security/expressions.d.ts.map +1 -0
  29. package/lib/analyze/security/identifiers.d.ts.map +1 -0
  30. package/lib/analyze/security/index.d.ts.map +1 -0
  31. package/lib/analyze/security/member-access.d.ts.map +1 -0
  32. package/lib/builtins.d.ts +50 -0
  33. package/lib/builtins.d.ts.map +1 -0
  34. package/lib/builtins.js +1065 -0
  35. package/lib/builtins.js.map +1 -0
  36. package/lib/codegen/builtin-types.d.ts +11 -0
  37. package/lib/codegen/builtin-types.d.ts.map +1 -0
  38. package/lib/codegen/builtin-types.js +151 -0
  39. package/lib/codegen/builtin-types.js.map +1 -0
  40. package/lib/codegen/builtins/builtin-types.d.ts +2 -0
  41. package/lib/codegen/builtins/builtin-types.d.ts.map +1 -0
  42. package/lib/codegen/builtins/builtin-types.js +143 -0
  43. package/lib/codegen/builtins/builtin-types.js.map +1 -0
  44. package/lib/codegen/builtins/index.d.ts +2 -0
  45. package/lib/codegen/builtins/index.d.ts.map +1 -0
  46. package/lib/codegen/builtins/index.js +6 -0
  47. package/lib/codegen/builtins/index.js.map +1 -0
  48. package/lib/codegen/declare-types.d.ts +20 -0
  49. package/lib/codegen/declare-types.d.ts.map +1 -0
  50. package/lib/codegen/declare-types.js +299 -0
  51. package/lib/codegen/declare-types.js.map +1 -0
  52. package/lib/codegen/generate-typescript-definitions.d.ts +31 -0
  53. package/lib/codegen/generate-typescript-definitions.d.ts.map +1 -0
  54. package/lib/codegen/generate-typescript-definitions.js +37 -0
  55. package/lib/codegen/generate-typescript-definitions.js.map +1 -0
  56. package/lib/codegen/generate-typescript-definitions.test.d.ts +2 -0
  57. package/lib/codegen/generate-typescript-definitions.test.d.ts.map +1 -0
  58. package/lib/codegen/generate-typescript-definitions.test.js +587 -0
  59. package/lib/codegen/generate-typescript-definitions.test.js.map +1 -0
  60. package/lib/codegen/index.d.ts +2 -0
  61. package/lib/codegen/index.d.ts.map +1 -0
  62. package/lib/codegen/index.js +6 -0
  63. package/lib/codegen/index.js.map +1 -0
  64. package/lib/codegen/schema/component-types.d.ts +3 -0
  65. package/lib/codegen/schema/component-types.d.ts.map +1 -0
  66. package/lib/codegen/schema/component-types.js +118 -0
  67. package/lib/codegen/schema/component-types.js.map +1 -0
  68. package/lib/codegen/schema/data-types.d.ts +3 -0
  69. package/lib/codegen/schema/data-types.d.ts.map +1 -0
  70. package/lib/codegen/schema/data-types.js +98 -0
  71. package/lib/codegen/schema/data-types.js.map +1 -0
  72. package/lib/codegen/schema/index.d.ts +3 -0
  73. package/lib/codegen/schema/index.d.ts.map +1 -0
  74. package/lib/codegen/schema/index.js +8 -0
  75. package/lib/codegen/schema/index.js.map +1 -0
  76. package/lib/codegen/schema/utils.d.ts +4 -0
  77. package/lib/codegen/schema/utils.d.ts.map +1 -0
  78. package/lib/codegen/schema/utils.js +103 -0
  79. package/lib/codegen/schema/utils.js.map +1 -0
  80. package/lib/codegen/schema-to-types.d.ts +14 -0
  81. package/lib/codegen/schema-to-types.d.ts.map +1 -0
  82. package/lib/codegen/schema-to-types.js +203 -0
  83. package/lib/codegen/schema-to-types.js.map +1 -0
  84. package/lib/compile/compile.d.ts.map +1 -0
  85. package/lib/compile/compile.js +245 -0
  86. package/lib/compile/compile.js.map +1 -0
  87. package/lib/compile/compile.test.d.ts.map +1 -0
  88. package/lib/compile/compile.test.js +148 -0
  89. package/lib/compile/compile.test.js.map +1 -0
  90. package/lib/evaluate/evaluate.d.ts +26 -0
  91. package/lib/evaluate/evaluate.d.ts.map +1 -0
  92. package/lib/evaluate/evaluate.js +40 -0
  93. package/lib/evaluate/evaluation-error.d.ts +4 -0
  94. package/lib/evaluate/evaluation-error.d.ts.map +1 -0
  95. package/lib/evaluate/evaluation-error.js +6 -0
  96. package/lib/index.d.ts +8 -0
  97. package/lib/index.d.ts.map +1 -0
  98. package/lib/index.js +16 -0
  99. package/lib/index.js.map +1 -0
  100. package/lib/render.d.ts +58 -0
  101. package/lib/render.d.ts.map +1 -0
  102. package/lib/render.js +52 -0
  103. package/lib/render.test.d.ts.map +1 -0
  104. package/lib/schema.d.ts +68 -0
  105. package/lib/schema.d.ts.map +1 -0
  106. package/lib/schema.js +106 -0
  107. package/lib/validate.d.ts +41 -0
  108. package/lib/validate.d.ts.map +1 -0
  109. package/lib/validate.js +42 -0
  110. package/package.json +27 -0
  111. package/src/analyze/analysis-error.ts +26 -0
  112. package/src/analyze/analysis-report.ts +228 -0
  113. package/src/analyze/analyze.test.ts +829 -0
  114. package/src/analyze/analyze.ts +10 -0
  115. package/src/analyze/data/analyze.ts +15 -0
  116. package/src/analyze/data/data-access.ts +349 -0
  117. package/src/analyze/data/index.ts +1 -0
  118. package/src/analyze/data/method-calls.ts +96 -0
  119. package/src/analyze/data/utils.ts +329 -0
  120. package/src/analyze/index.ts +3 -0
  121. package/src/analyze/jsx/analyze.ts +17 -0
  122. package/src/analyze/jsx/element-attributes.ts +143 -0
  123. package/src/analyze/jsx/element-children.ts +97 -0
  124. package/src/analyze/jsx/element-tags.ts +55 -0
  125. package/src/analyze/jsx/index.ts +1 -0
  126. package/src/analyze/security/analyze.ts +21 -0
  127. package/src/analyze/security/call-expressions.ts +41 -0
  128. package/src/analyze/security/declarations.ts +67 -0
  129. package/src/analyze/security/expressions.ts +100 -0
  130. package/src/analyze/security/identifiers.ts +113 -0
  131. package/src/analyze/security/index.ts +1 -0
  132. package/src/analyze/security/member-access.ts +35 -0
  133. package/src/analyze/utils.ts +118 -0
  134. package/src/analyze/validation-context.ts +32 -0
  135. package/src/builtins.ts +1149 -0
  136. package/src/codegen/builtins/builtin-types.ts +174 -0
  137. package/src/codegen/builtins/index.ts +1 -0
  138. package/src/codegen/generate-typescript-definitions.test.ts +676 -0
  139. package/src/codegen/generate-typescript-definitions.ts +35 -0
  140. package/src/codegen/index.ts +1 -0
  141. package/src/codegen/schema/component-types.ts +140 -0
  142. package/src/codegen/schema/data-types.ts +109 -0
  143. package/src/codegen/schema/index.ts +2 -0
  144. package/src/codegen/schema/utils.ts +111 -0
  145. package/src/compile/compilation-error.ts +37 -0
  146. package/src/compile/compile.test.ts +180 -0
  147. package/src/compile/compile.ts +282 -0
  148. package/src/compile/index.ts +2 -0
  149. package/src/evaluate/evaluate.test.ts +316 -0
  150. package/src/evaluate/evaluate.ts +81 -0
  151. package/src/evaluate/evaluation-error.ts +6 -0
  152. package/src/evaluate/index.ts +2 -0
  153. package/src/index.ts +10 -0
  154. package/src/jsx.ts +161 -0
  155. package/src/parse/index.ts +2 -0
  156. package/src/parse/parse-error.ts +14 -0
  157. package/src/parse/parse.test.ts +99 -0
  158. package/src/parse/parse.ts +40 -0
  159. package/src/render.test.ts +425 -0
  160. package/src/render.ts +75 -0
  161. package/src/schema.ts +195 -0
  162. package/src/traverse.test.ts +104 -0
  163. package/src/traverse.ts +78 -0
  164. package/src/types.ts +17 -0
  165. package/src/validate.ts +59 -0
@@ -0,0 +1,10 @@
1
+ import type { Program } from "acorn";
2
+ import type { Schema } from "../schema";
3
+ import { AnalysisReport } from "./analysis-report";
4
+ import { analyzeSecurity } from "./security";
5
+ import { analyzeJsx } from "./jsx";
6
+ import { analyzeData } from "./data";
7
+
8
+ export function analyze(ast: Program, schema: Schema): AnalysisReport {
9
+ return new AnalysisReport().merge(analyzeSecurity(ast, schema), analyzeJsx(ast, schema), analyzeData(ast, schema));
10
+ }
@@ -0,0 +1,15 @@
1
+ import { Program } from "acorn";
2
+ import { Schema } from "../../schema";
3
+ import { AnalysisReport } from "../analysis-report";
4
+ import { ValidationContext } from "../validation-context";
5
+ import { analyzeDataAccess } from "./data-access";
6
+ import { analyzeMethodCalls } from "./method-calls";
7
+
8
+ export function analyzeData(ast: Program, schema: Schema): AnalysisReport {
9
+ const validationContext = new ValidationContext();
10
+
11
+ return new AnalysisReport().merge(
12
+ analyzeMethodCalls(ast, schema, validationContext),
13
+ analyzeDataAccess(ast, schema, validationContext)
14
+ );
15
+ }
@@ -0,0 +1,349 @@
1
+ import type { Program } from "acorn";
2
+ import { traverse } from "../../traverse";
3
+ import type { ArrayPropertySchema, PropertySchema, Schema } from "../../schema";
4
+ import { ValidationContext } from "../validation-context";
5
+ import { AnalysisReport } from "../analysis-report";
6
+ import { isSimpleDataAccess, extractPath, getParentNode, validateSchemaPath } from "./utils";
7
+ import { getNodeRange } from "../utils";
8
+
9
+ export function analyzeDataAccess(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
10
+ const analysisReport = new AnalysisReport();
11
+
12
+ const dataPaths = new Set<string>();
13
+ const arrowFunctionContexts = new Map<any, Map<string, any>>();
14
+
15
+ // First pass: collect all data paths and arrow function contexts (including nested ones)
16
+ function collectArrowFunctionContexts(node: any): void {
17
+ if (node.type === "CallExpression") {
18
+ const { callee, arguments: args } = node;
19
+
20
+ if (
21
+ callee.type === "MemberExpression" &&
22
+ !callee.computed &&
23
+ callee.property.type === "Identifier" &&
24
+ args.length > 0 &&
25
+ args[0].type === "ArrowFunctionExpression"
26
+ ) {
27
+ const method = callee.property.name;
28
+
29
+ // TODO: Move these into builtins so it stays in sync with the global method definitions
30
+ const arrayCallbackMethods = ["map", "filter", "find", "forEach", "some", "every", "reduce"];
31
+
32
+ if (arrayCallbackMethods.includes(method)) {
33
+ const arrayElementType = getArrayElementTypeFromCall(node, schema, arrowFunctionContexts);
34
+ const arrowFunction = args[0];
35
+
36
+ if (arrayElementType && arrowFunction.params.length > 0) {
37
+ const parameterTypes = new Map();
38
+
39
+ // First parameter is always the array element
40
+ if (arrowFunction.params[0].type === "Identifier") {
41
+ parameterTypes.set(arrowFunction.params[0].name, arrayElementType);
42
+ }
43
+
44
+ // For methods like reduce, second parameter might be index (number)
45
+ if (arrowFunction.params.length > 1 && arrowFunction.params[1].type === "Identifier") {
46
+ parameterTypes.set(arrowFunction.params[1].name, { type: "number" });
47
+ }
48
+
49
+ arrowFunctionContexts.set(arrowFunction, parameterTypes);
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ // Recursively traverse all child nodes
56
+ for (const key of Object.keys(node)) {
57
+ const value = node[key];
58
+ if (value && typeof value === "object") {
59
+ if (Array.isArray(value)) {
60
+ for (const child of value) {
61
+ if (child && typeof child === "object" && child.type) {
62
+ collectArrowFunctionContexts(child);
63
+ }
64
+ }
65
+ } else if (value.type) {
66
+ collectArrowFunctionContexts(value);
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ traverse(ast, {
73
+ MemberExpression(node) {
74
+ if (isSimpleDataAccess(node)) {
75
+ const path = extractPath(node);
76
+ if (path[0] === "data") {
77
+ dataPaths.add(path.join("."));
78
+ }
79
+ }
80
+ },
81
+ });
82
+
83
+ // Collect all arrow function contexts including nested ones
84
+ collectArrowFunctionContexts(ast);
85
+
86
+ const validatablePaths = new Set<string>();
87
+
88
+ for (const path of dataPaths) {
89
+ let hasLongerPath = false;
90
+ for (const otherPath of dataPaths) {
91
+ if (otherPath !== path && otherPath.startsWith(path + ".")) {
92
+ hasLongerPath = true;
93
+ break;
94
+ }
95
+ }
96
+ if (!hasLongerPath) {
97
+ validatablePaths.add(path);
98
+ }
99
+ }
100
+
101
+ // Second pass: validate with context awareness
102
+ traverse(ast, {
103
+ MemberExpression(node) {
104
+ const parent = getParentNode(node, ast);
105
+ if (parent && parent.type === "CallExpression" && parent.callee === node) {
106
+ return;
107
+ }
108
+
109
+ // Check if this is inside an arrow function with parameter context
110
+ const arrowFunctionContext = findArrowFunctionContext(node, ast, arrowFunctionContexts);
111
+
112
+ if (arrowFunctionContext && node.object.type === "Identifier") {
113
+ const paramType = arrowFunctionContext.get(node.object.name);
114
+ if (paramType) {
115
+ validateParameterAccess(node, paramType, analysisReport, validationContext);
116
+ return;
117
+ }
118
+ }
119
+
120
+ if (isSimpleDataAccess(node)) {
121
+ const path = extractPath(node);
122
+
123
+ if (path[0] === "data") {
124
+ const pathString = path.join(".");
125
+
126
+ if (validatablePaths.has(pathString)) {
127
+ const schemaPath = path.slice(1);
128
+ validateSchemaPath(schemaPath, schema, analysisReport, node, validationContext);
129
+ }
130
+ }
131
+ }
132
+ },
133
+ });
134
+
135
+ return analysisReport;
136
+ }
137
+
138
+ function findArrowFunctionContext(
139
+ targetNode: any,
140
+ ast: Program,
141
+ arrowFunctionContexts: Map<any, Map<string, any>>
142
+ ): Map<string, any> | null {
143
+ function findInNode(node: any, depth: number = 0): any {
144
+ if (node === targetNode) {
145
+ return { found: true, depth };
146
+ }
147
+
148
+ if (node.type === "ArrowFunctionExpression" && arrowFunctionContexts.has(node)) {
149
+ const result = findInNode(node.body, depth + 1);
150
+ if (result?.found) {
151
+ return { arrowFunction: node, depth: result.depth };
152
+ }
153
+ }
154
+
155
+ for (const key of Object.keys(node)) {
156
+ const value = node[key];
157
+ if (value && typeof value === "object") {
158
+ if (Array.isArray(value)) {
159
+ for (const child of value) {
160
+ if (child && typeof child === "object" && child.type) {
161
+ const result = findInNode(child, depth);
162
+ if (result?.found || result?.arrowFunction) {
163
+ return result;
164
+ }
165
+ }
166
+ }
167
+ } else if (value.type) {
168
+ const result = findInNode(value, depth);
169
+ if (result?.found || result?.arrowFunction) {
170
+ return result;
171
+ }
172
+ }
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+
178
+ const result = findInNode(ast);
179
+ return result?.arrowFunction ? arrowFunctionContexts.get(result.arrowFunction) || null : null;
180
+ }
181
+
182
+ function getArrayElementTypeFromCall(
183
+ node: any,
184
+ schema: Schema,
185
+ arrowFunctionContexts: Map<any, Map<string, any>>
186
+ ): any {
187
+ const { callee } = node;
188
+
189
+ if (callee.type !== "MemberExpression") {
190
+ return null;
191
+ }
192
+
193
+ if (isSimpleDataAccess(callee)) {
194
+ const path = extractPath(callee);
195
+ if (path[0] === "data") {
196
+ const schemaPath = path.slice(1, -1); // Remove "data" prefix and method name
197
+ const arraySchema = getSchemaAtPath(schemaPath, schema);
198
+
199
+ if (arraySchema?.type === "array") {
200
+ return getArrayElementType(arraySchema);
201
+ }
202
+ }
203
+ } else if (callee.object.type === "MemberExpression") {
204
+ // Handle nested access like group.items.map where group is a parameter
205
+ const objectPath = extractPath(callee.object);
206
+
207
+ // Check if this is a parameter access (not data access)
208
+ if (objectPath.length === 2) {
209
+ // e.g., ["group", "items"] or ["dept", "employees"]
210
+ const paramName = objectPath[0];
211
+ const propertyName = objectPath[1];
212
+
213
+ // Find the arrow function context that defines this parameter
214
+ const parameterType = resolveParameterType(paramName, node, arrowFunctionContexts);
215
+
216
+ if (parameterType && parameterType.type === "object" && parameterType.shape) {
217
+ const propertySchema = parameterType.shape[propertyName];
218
+ if (propertySchema && propertySchema.type === "array") {
219
+ return getArrayElementType(propertySchema);
220
+ }
221
+ }
222
+
223
+ // Fallback: try to find any matching property in schema
224
+ if (propertyName === "items" || propertyName === "employees") {
225
+ const itemsArraySchema = findArrayPropertyInSchema(schema, propertyName);
226
+ if (itemsArraySchema) {
227
+ return getArrayElementType(itemsArraySchema);
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ return null;
234
+ }
235
+
236
+ function resolveParameterType(
237
+ paramName: string,
238
+ currentNode: any,
239
+ arrowFunctionContexts: Map<any, Map<string, any>>
240
+ ): any {
241
+ // Find the arrow function that contains this node and has the parameter
242
+ for (const [arrowFunction, parameterTypes] of arrowFunctionContexts.entries()) {
243
+ if (parameterTypes.has(paramName)) {
244
+ // Check if currentNode is inside this arrow function
245
+ if (isNodeInsideArrowFunction(currentNode, arrowFunction)) {
246
+ return parameterTypes.get(paramName);
247
+ }
248
+ }
249
+ }
250
+ return null;
251
+ }
252
+
253
+ function isNodeInsideArrowFunction(targetNode: any, arrowFunction: any): boolean {
254
+ // Simple check: if the target node's position is within the arrow function's range
255
+ if (targetNode.start >= arrowFunction.start && targetNode.end <= arrowFunction.end) {
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+
261
+ function findArrayPropertyInSchema(schema: Schema, propertyName: string): any {
262
+ function searchInObject(obj: any): any {
263
+ if (!obj || typeof obj !== "object") return null;
264
+
265
+ if (obj.type === "object" && obj.shape) {
266
+ // Check if this object has the property we're looking for
267
+ if (obj.shape[propertyName] && obj.shape[propertyName].type === "array") {
268
+ return obj.shape[propertyName];
269
+ }
270
+
271
+ // Recursively search in nested objects
272
+ for (const key of Object.keys(obj.shape)) {
273
+ const result = searchInObject(obj.shape[key]);
274
+ if (result) return result;
275
+ }
276
+ }
277
+
278
+ if (obj.type === "array" && obj.shape) {
279
+ return searchInObject(obj.shape);
280
+ }
281
+
282
+ return null;
283
+ }
284
+
285
+ if (!schema.data) return null;
286
+
287
+ for (const key of Object.keys(schema.data)) {
288
+ const result = searchInObject(schema.data[key]);
289
+ if (result) return result;
290
+ }
291
+
292
+ return null;
293
+ }
294
+
295
+ function getSchemaAtPath(path: string[], schema: Schema): any {
296
+ if (!schema.data || path.length === 0) {
297
+ return schema.data;
298
+ }
299
+
300
+ let current = schema.data[path[0]];
301
+
302
+ for (let i = 1; i < path.length && current; i++) {
303
+ if (current.type === "object" && current.shape) {
304
+ current = current.shape[path[i]];
305
+ } else {
306
+ return null;
307
+ }
308
+ }
309
+
310
+ return current;
311
+ }
312
+
313
+ function validateParameterAccess(
314
+ node: any,
315
+ paramType: any,
316
+ analysisReport: AnalysisReport,
317
+ validationContext: ValidationContext
318
+ ): void {
319
+ if (node.property.type !== "Identifier") {
320
+ return;
321
+ }
322
+
323
+ const propertyName = node.property.name;
324
+
325
+ if (paramType.type === "object" && paramType.shape) {
326
+ if (!paramType.shape[propertyName]) {
327
+ analysisReport.addIssue(
328
+ "INVALID_PARAMETER_ACCESS",
329
+ `Property '${propertyName}' does not exist on parameter '${node.object.name}'`,
330
+ getNodeRange(node),
331
+ validationContext.getSnapshot(),
332
+ Object.keys(paramType.shape)
333
+ );
334
+ }
335
+ } else {
336
+ analysisReport.addIssue(
337
+ "INVALID_PARAMETER_ACCESS",
338
+ `Cannot access property '${propertyName}' on ${paramType.type || "undefined"} type parameter '${
339
+ node.object.name
340
+ }'`,
341
+ getNodeRange(node),
342
+ validationContext.getSnapshot()
343
+ );
344
+ }
345
+ }
346
+
347
+ export function getArrayElementType(schema: ArrayPropertySchema): PropertySchema | null {
348
+ return schema.shape || null;
349
+ }
@@ -0,0 +1 @@
1
+ export { analyzeData } from "./analyze";
@@ -0,0 +1,96 @@
1
+ import type { Program } from "acorn";
2
+ import { traverse } from "../../traverse";
3
+ import type { Schema } from "../../schema";
4
+ import { isMethodAllowed, getMethodDefinition, getMethodDisplayName } from "../../builtins";
5
+ import { ValidationContext } from "../validation-context";
6
+ import { AnalysisReport } from "../analysis-report";
7
+ import { isSimpleDataAccess, extractPath, validateSchemaPath } from "./utils";
8
+ import { getNodeRange } from "../utils";
9
+
10
+ export function analyzeMethodCalls(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
11
+ const analysisReport = new AnalysisReport();
12
+
13
+ traverse(ast, {
14
+ CallExpression(node) {
15
+ const { callee, arguments: args } = node;
16
+
17
+ if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
18
+ const method = callee.property.name;
19
+
20
+ if (isSimpleDataAccess(callee)) {
21
+ const path = extractPath(callee);
22
+ if (path[0] === "data") {
23
+ const schemaPath = path.slice(1, -1);
24
+ if (schemaPath.length > 0) {
25
+ validateSchemaPath(schemaPath, schema, analysisReport, callee, validationContext);
26
+ }
27
+ }
28
+ }
29
+
30
+ if (!isMethodAllowed(method)) {
31
+ const methodContext = callee.object.type === "Identifier" ? `${callee.object.name}.${method}` : `.${method}`;
32
+
33
+ analysisReport.addIssue(
34
+ "METHOD_NOT_ALLOWED",
35
+ `method ${methodContext} not allowed`,
36
+ getNodeRange(callee),
37
+ validationContext.getSnapshot()
38
+ );
39
+ return;
40
+ }
41
+
42
+ const methodDef = getMethodDefinition(method);
43
+
44
+ if (!methodDef || !methodDef.params) {
45
+ return;
46
+ }
47
+
48
+ const { params } = methodDef;
49
+
50
+ const requiredParams = params.filter((params) => params.required !== false);
51
+ const hasVariadic = params.some((params) => params.variadic);
52
+
53
+ if (hasVariadic) {
54
+ const nonVariadicRequired = requiredParams.filter((params) => !params.variadic);
55
+ if (args.length < nonVariadicRequired.length) {
56
+ analysisReport.addIssue(
57
+ "INSUFFICIENT_PARAMETERS",
58
+ `${getMethodDisplayName(method)} expects at least ${nonVariadicRequired.length} parameter${
59
+ nonVariadicRequired.length !== 1 ? "s" : ""
60
+ }, got ${args.length}`,
61
+ getNodeRange(node),
62
+ validationContext.getSnapshot()
63
+ );
64
+ }
65
+ } else {
66
+ const minParams = requiredParams.length;
67
+ const maxParams = params.length;
68
+
69
+ if (args.length < minParams) {
70
+ analysisReport.addIssue(
71
+ "INSUFFICIENT_PARAMETERS",
72
+ `${getMethodDisplayName(method)} expects at least ${minParams} parameter${
73
+ minParams !== 1 ? "s" : ""
74
+ }, got ${args.length}`,
75
+ getNodeRange(node),
76
+ validationContext.getSnapshot()
77
+ );
78
+ }
79
+
80
+ if (args.length > maxParams) {
81
+ analysisReport.addIssue(
82
+ "TOO_MANY_PARAMETERS",
83
+ `${getMethodDisplayName(method)} expects at most ${maxParams} parameter${
84
+ maxParams !== 1 ? "s" : ""
85
+ }, got ${args.length}`,
86
+ getNodeRange(node),
87
+ validationContext.getSnapshot()
88
+ );
89
+ }
90
+ }
91
+ }
92
+ },
93
+ });
94
+
95
+ return analysisReport;
96
+ }