jsxpression 0.1.41 → 0.2.1

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 (183) hide show
  1. package/lib/analyze/analysis-error.d.ts.map +1 -1
  2. package/lib/analyze/analysis-error.js +2 -7
  3. package/lib/analyze/analysis-error.js.map +1 -1
  4. package/lib/analyze/analysis-report.d.ts +13 -13
  5. package/lib/analyze/analysis-report.d.ts.map +1 -1
  6. package/lib/analyze/analysis-report.js +48 -45
  7. package/lib/analyze/analysis-report.js.map +1 -1
  8. package/lib/analyze/analyze.d.ts +1 -1
  9. package/lib/analyze/analyze.d.ts.map +1 -1
  10. package/lib/analyze/analyze.js +4 -4
  11. package/lib/analyze/analyze.js.map +1 -1
  12. package/lib/analyze/data/analyze.d.ts +1 -1
  13. package/lib/analyze/data/analyze.d.ts.map +1 -1
  14. package/lib/analyze/data/analyze.js +5 -5
  15. package/lib/analyze/data/analyze.js.map +1 -1
  16. package/lib/analyze/data/data-access.d.ts +1 -1
  17. package/lib/analyze/data/data-access.d.ts.map +1 -1
  18. package/lib/analyze/data/data-access.js +19 -15
  19. package/lib/analyze/data/data-access.js.map +1 -1
  20. package/lib/analyze/data/method-calls.d.ts +1 -1
  21. package/lib/analyze/data/method-calls.d.ts.map +1 -1
  22. package/lib/analyze/data/method-calls.js +5 -5
  23. package/lib/analyze/data/method-calls.js.map +1 -1
  24. package/lib/analyze/data/utils.d.ts +3 -3
  25. package/lib/analyze/data/utils.d.ts.map +1 -1
  26. package/lib/analyze/data/utils.js +6 -5
  27. package/lib/analyze/data/utils.js.map +1 -1
  28. package/lib/analyze/index.d.ts +1 -1
  29. package/lib/analyze/index.d.ts.map +1 -1
  30. package/lib/analyze/index.js +1 -1
  31. package/lib/analyze/index.js.map +1 -1
  32. package/lib/analyze/jsx/analyze.d.ts +1 -1
  33. package/lib/analyze/jsx/analyze.d.ts.map +1 -1
  34. package/lib/analyze/jsx/analyze.js +4 -4
  35. package/lib/analyze/jsx/analyze.js.map +1 -1
  36. package/lib/analyze/jsx/element-attributes.d.ts +1 -1
  37. package/lib/analyze/jsx/element-attributes.d.ts.map +1 -1
  38. package/lib/analyze/jsx/element-attributes.js +3 -3
  39. package/lib/analyze/jsx/element-attributes.js.map +1 -1
  40. package/lib/analyze/jsx/element-children.d.ts +1 -1
  41. package/lib/analyze/jsx/element-children.d.ts.map +1 -1
  42. package/lib/analyze/jsx/element-children.js +3 -3
  43. package/lib/analyze/jsx/element-children.js.map +1 -1
  44. package/lib/analyze/jsx/element-tags.d.ts +1 -1
  45. package/lib/analyze/jsx/element-tags.d.ts.map +1 -1
  46. package/lib/analyze/jsx/element-tags.js +2 -2
  47. package/lib/analyze/jsx/element-tags.js.map +1 -1
  48. package/lib/analyze/security/analyze.d.ts +1 -1
  49. package/lib/analyze/security/analyze.d.ts.map +1 -1
  50. package/lib/analyze/security/analyze.js +4 -4
  51. package/lib/analyze/security/analyze.js.map +1 -1
  52. package/lib/analyze/security/call-expressions.d.ts +1 -1
  53. package/lib/analyze/security/call-expressions.d.ts.map +1 -1
  54. package/lib/analyze/security/call-expressions.js +3 -5
  55. package/lib/analyze/security/call-expressions.js.map +1 -1
  56. package/lib/analyze/security/declarations.d.ts +1 -1
  57. package/lib/analyze/security/declarations.d.ts.map +1 -1
  58. package/lib/analyze/security/declarations.js +6 -6
  59. package/lib/analyze/security/declarations.js.map +1 -1
  60. package/lib/analyze/security/expressions.d.ts +1 -1
  61. package/lib/analyze/security/expressions.d.ts.map +1 -1
  62. package/lib/analyze/security/expressions.js +3 -6
  63. package/lib/analyze/security/expressions.js.map +1 -1
  64. package/lib/analyze/security/identifiers.d.ts +1 -1
  65. package/lib/analyze/security/identifiers.d.ts.map +1 -1
  66. package/lib/analyze/security/identifiers.js +19 -22
  67. package/lib/analyze/security/identifiers.js.map +1 -1
  68. package/lib/analyze/utils.d.ts +4 -4
  69. package/lib/analyze/utils.d.ts.map +1 -1
  70. package/lib/analyze/utils.js +5 -3
  71. package/lib/analyze/utils.js.map +1 -1
  72. package/lib/analyze/validation-context.d.ts +8 -8
  73. package/lib/analyze/validation-context.d.ts.map +1 -1
  74. package/lib/analyze/validation-context.js +23 -21
  75. package/lib/analyze/validation-context.js.map +1 -1
  76. package/lib/builtins.d.ts +6 -6
  77. package/lib/builtins.d.ts.map +1 -1
  78. package/lib/builtins.js +9 -8
  79. package/lib/builtins.js.map +1 -1
  80. package/lib/codegen/builtins/builtin-types.d.ts.map +1 -1
  81. package/lib/codegen/builtins/builtin-types.js +9 -13
  82. package/lib/codegen/builtins/builtin-types.js.map +1 -1
  83. package/lib/codegen/generate-typescript-definitions.d.ts +18 -17
  84. package/lib/codegen/generate-typescript-definitions.d.ts.map +1 -1
  85. package/lib/codegen/generate-typescript-definitions.js +19 -23
  86. package/lib/codegen/generate-typescript-definitions.js.map +1 -1
  87. package/lib/codegen/schema/component-types.d.ts.map +1 -1
  88. package/lib/codegen/schema/component-types.js +7 -4
  89. package/lib/codegen/schema/component-types.js.map +1 -1
  90. package/lib/codegen/schema/data-types.d.ts.map +1 -1
  91. package/lib/codegen/schema/data-types.js +1 -1
  92. package/lib/codegen/schema/data-types.js.map +1 -1
  93. package/lib/codegen/schema/function-types.d.ts.map +1 -1
  94. package/lib/codegen/schema/function-types.js +1 -3
  95. package/lib/codegen/schema/function-types.js.map +1 -1
  96. package/lib/codegen/schema/utils.d.ts +4 -3
  97. package/lib/codegen/schema/utils.d.ts.map +1 -1
  98. package/lib/codegen/schema/utils.js +12 -8
  99. package/lib/codegen/schema/utils.js.map +1 -1
  100. package/lib/compile/compilation-error.d.ts +1 -1
  101. package/lib/compile/compilation-error.d.ts.map +1 -1
  102. package/lib/compile/compilation-error.js +3 -1
  103. package/lib/compile/compilation-error.js.map +1 -1
  104. package/lib/compile/compile.d.ts.map +1 -1
  105. package/lib/compile/compile.js +5 -11
  106. package/lib/compile/compile.js.map +1 -1
  107. package/lib/evaluate/evaluate.d.ts +6 -6
  108. package/lib/evaluate/evaluate.d.ts.map +1 -1
  109. package/lib/evaluate/evaluate.js +1 -1
  110. package/lib/evaluate/evaluate.js.map +1 -1
  111. package/lib/evaluate/evaluation-error.d.ts.map +1 -1
  112. package/lib/evaluate/evaluation-error.js +1 -0
  113. package/lib/evaluate/evaluation-error.js.map +1 -1
  114. package/lib/index.d.ts +1 -1
  115. package/lib/index.d.ts.map +1 -1
  116. package/lib/index.js +1 -1
  117. package/lib/index.js.map +1 -1
  118. package/lib/jsx.d.ts +3 -3
  119. package/lib/jsx.d.ts.map +1 -1
  120. package/lib/jsx.js.map +1 -1
  121. package/lib/parse/parse-error.d.ts.map +1 -1
  122. package/lib/parse/parse-error.js +1 -0
  123. package/lib/parse/parse-error.js.map +1 -1
  124. package/lib/render.d.ts +25 -33
  125. package/lib/render.d.ts.map +1 -1
  126. package/lib/render.js +21 -26
  127. package/lib/render.js.map +1 -1
  128. package/lib/schema.d.ts +9 -9
  129. package/lib/schema.d.ts.map +1 -1
  130. package/lib/schema.js +1 -1
  131. package/lib/schema.js.map +1 -1
  132. package/lib/traverse.d.ts.map +1 -1
  133. package/lib/traverse.js.map +1 -1
  134. package/lib/validate.d.ts +14 -17
  135. package/lib/validate.d.ts.map +1 -1
  136. package/lib/validate.js +13 -14
  137. package/lib/validate.js.map +1 -1
  138. package/package.json +12 -12
  139. package/src/analyze/analysis-error.ts +2 -6
  140. package/src/analyze/analysis-report.ts +73 -62
  141. package/src/analyze/analyze.test.ts +60 -60
  142. package/src/analyze/analyze.ts +4 -4
  143. package/src/analyze/data/analyze.ts +5 -8
  144. package/src/analyze/data/data-access.ts +49 -46
  145. package/src/analyze/data/method-calls.ts +9 -18
  146. package/src/analyze/data/utils.ts +15 -19
  147. package/src/analyze/index.ts +1 -1
  148. package/src/analyze/jsx/analyze.ts +4 -4
  149. package/src/analyze/jsx/element-attributes.ts +5 -18
  150. package/src/analyze/jsx/element-children.ts +4 -8
  151. package/src/analyze/jsx/element-tags.ts +2 -2
  152. package/src/analyze/security/analyze.ts +4 -4
  153. package/src/analyze/security/call-expressions.ts +5 -18
  154. package/src/analyze/security/declarations.ts +14 -43
  155. package/src/analyze/security/expressions.ts +12 -49
  156. package/src/analyze/security/identifiers.ts +37 -39
  157. package/src/analyze/utils.ts +25 -19
  158. package/src/analyze/validation-context.ts +32 -23
  159. package/src/builtins.ts +27 -38
  160. package/src/codegen/builtins/builtin-types.ts +13 -20
  161. package/src/codegen/generate-typescript-definitions.test.ts +6 -6
  162. package/src/codegen/generate-typescript-definitions.ts +19 -23
  163. package/src/codegen/schema/component-types.ts +22 -12
  164. package/src/codegen/schema/data-types.ts +6 -6
  165. package/src/codegen/schema/function-types.ts +1 -3
  166. package/src/codegen/schema/utils.ts +24 -23
  167. package/src/compile/compilation-error.ts +5 -1
  168. package/src/compile/compile.test.ts +7 -21
  169. package/src/compile/compile.ts +18 -30
  170. package/src/evaluate/evaluate.test.ts +78 -94
  171. package/src/evaluate/evaluate.ts +15 -20
  172. package/src/evaluate/evaluation-error.ts +1 -0
  173. package/src/index.ts +1 -7
  174. package/src/jsx.ts +4 -6
  175. package/src/parse/parse-error.ts +1 -0
  176. package/src/parse/parse.test.ts +16 -18
  177. package/src/render.test.ts +4 -4
  178. package/src/render.ts +26 -38
  179. package/src/schema.ts +11 -11
  180. package/src/traverse.test.ts +6 -6
  181. package/src/traverse.ts +3 -3
  182. package/src/validate.ts +14 -17
  183. package/CHANGELOG.md +0 -8
@@ -169,9 +169,7 @@ describe("analyze - consolidated tests", () => {
169
169
  const ast = parse("<Text>{items[variable]}</Text>");
170
170
  const result = analyze(ast, baseSchema);
171
171
  expect(result.hasErrors).toBe(true);
172
- expect(result.errors.some((e) => e.message.includes("variable") || e.message.includes("not allowed"))).toBe(
173
- true
174
- );
172
+ expect(result.errors.some((e) => e.message.includes("variable") || e.message.includes("not allowed"))).toBe(true);
175
173
  });
176
174
 
177
175
  it("should block string-based computed access on objects", () => {
@@ -230,17 +228,13 @@ describe("analyze - consolidated tests", () => {
230
228
  });
231
229
 
232
230
  it("should handle complex nested scenarios", () => {
233
- const ast = parse(
234
- "<Text>{departments.map((dept) => dept.employees.map((employee) => employee.salary))}</Text>"
235
- );
231
+ const ast = parse("<Text>{departments.map((dept) => dept.employees.map((employee) => employee.salary))}</Text>");
236
232
  const result = analyze(ast, baseSchema);
237
233
  expect(result.hasErrors).toBe(false);
238
234
  });
239
235
 
240
236
  it("should detect typos in complex nested scenarios", () => {
241
- const ast = parse(
242
- "<Text>{departments.map((dept) => dept.employees.map((employee) => employee.saladry))}</Text>"
243
- );
237
+ const ast = parse("<Text>{departments.map((dept) => dept.employees.map((employee) => employee.saladry))}</Text>");
244
238
  const result = analyze(ast, baseSchema);
245
239
  expect(result.hasErrors).toBe(true);
246
240
  expect(result.errors[0].message).toContain("Property 'saladry' does not exist on parameter 'employee'");
@@ -256,14 +250,16 @@ describe("analyze - consolidated tests", () => {
256
250
 
257
251
  // Test each builtin object (Math, Array.prototype, String.prototype)
258
252
  Object.entries(builtins).forEach(([objectName, schema]) => {
259
- if (!schema.methods) return;
253
+ if (!schema.methods) {
254
+ return;
255
+ }
260
256
 
261
257
  describe(`${objectName} methods`, () => {
262
258
  Object.entries(schema.methods!).forEach(([methodName, methodDef]) => {
263
259
  const displayName = objectName === "Math" ? `Math.${methodName}` : methodName;
264
260
 
265
261
  it(`should allow ${displayName} with valid parameters`, () => {
266
- let testExpression: string;
262
+ let testExpression: string = "";
267
263
 
268
264
  if (objectName === "Math") {
269
265
  // Math methods
@@ -325,60 +321,62 @@ describe("analyze - consolidated tests", () => {
325
321
  const requiredParams = methodDef.params.filter((p) => p.required);
326
322
  const nonVariadicParams = methodDef.params.filter((p) => !p.variadic);
327
323
 
328
- if (requiredParams.length > 0) {
329
- it(`should validate ${displayName} minimum parameters`, () => {
330
- let testExpression: string;
324
+ it(`should validate ${displayName} minimum parameters`, () => {
325
+ if (requiredParams.length === 0) {
326
+ return;
327
+ }
331
328
 
332
- if (objectName === "Math") {
333
- testExpression = `<Text>{Math.${methodName}()}</Text>`;
334
- } else if (objectName === "Array.prototype") {
335
- testExpression = `<Text>{items.${methodName}()}</Text>`;
336
- } else if (objectName === "String.prototype") {
337
- testExpression = `<Text>{text.${methodName}()}</Text>`;
338
- } else {
339
- testExpression = `<Text>{${objectName}.${methodName}()}</Text>`;
340
- }
329
+ let testExpression: string = "";
330
+
331
+ if (objectName === "Math") {
332
+ testExpression = `<Text>{Math.${methodName}()}</Text>`;
333
+ } else if (objectName === "Array.prototype") {
334
+ testExpression = `<Text>{items.${methodName}()}</Text>`;
335
+ } else if (objectName === "String.prototype") {
336
+ testExpression = `<Text>{text.${methodName}()}</Text>`;
337
+ } else {
338
+ testExpression = `<Text>{${objectName}.${methodName}()}</Text>`;
339
+ }
341
340
 
342
- const ast = parse(testExpression);
343
- const result = analyze(ast, baseSchema);
344
- expect(result.hasWarnings).toBe(true);
345
- expect(result.warnings[0].message).toContain(
346
- `${displayName} expects at least ${requiredParams.length} parameter`
347
- );
348
- });
349
- }
341
+ const ast = parse(testExpression);
342
+ const result = analyze(ast, baseSchema);
343
+ expect(result.hasWarnings).toBe(true);
344
+ expect(result.warnings[0].message).toContain(`${displayName} expects at least ${requiredParams.length} parameter`);
345
+ });
350
346
 
351
347
  // Test maximum parameters for non-variadic methods
352
- if (nonVariadicParams.length === methodDef.params.length && methodDef.params.length > 0) {
353
- it(`should validate ${displayName} maximum parameters`, () => {
354
- // Create a call with too many parameters
355
- const maxParams = methodDef.params.length;
356
- const extraParams = Array(maxParams + 2)
357
- .fill("1")
358
- .join(", ");
359
-
360
- let testExpression: string;
361
-
362
- if (objectName === "Math") {
363
- testExpression = `<Text>{Math.${methodName}(${extraParams})}</Text>`;
364
- } else if (objectName === "Array.prototype") {
365
- if (methodName === "map" || methodName === "filter") {
366
- testExpression = `<Text>{items.${methodName}(x => x, {}, ${extraParams})}</Text>`;
367
- } else {
368
- testExpression = `<Text>{items.${methodName}(${extraParams})}</Text>`;
369
- }
370
- } else if (objectName === "String.prototype") {
371
- testExpression = `<Text>{text.${methodName}(${extraParams})}</Text>`;
348
+ it(`should validate ${displayName} maximum parameters`, () => {
349
+ if (!(nonVariadicParams.length === methodDef.params.length && methodDef.params.length > 0)) {
350
+ return;
351
+ }
352
+
353
+ // Create a call with too many parameters
354
+ const maxParams = methodDef.params.length;
355
+ const extraParams = Array(maxParams + 2)
356
+ .fill("1")
357
+ .join(", ");
358
+
359
+ let testExpression: string = "";
360
+
361
+ if (objectName === "Math") {
362
+ testExpression = `<Text>{Math.${methodName}(${extraParams})}</Text>`;
363
+ } else if (objectName === "Array.prototype") {
364
+ if (methodName === "map" || methodName === "filter") {
365
+ testExpression = `<Text>{items.${methodName}(x => x, {}, ${extraParams})}</Text>`;
372
366
  } else {
373
- testExpression = `<Text>{${objectName}.${methodName}(${extraParams})}</Text>`;
367
+ testExpression = `<Text>{items.${methodName}(${extraParams})}</Text>`;
374
368
  }
369
+ } else if (objectName === "String.prototype") {
370
+ testExpression = `<Text>{text.${methodName}(${extraParams})}</Text>`;
371
+ } else {
372
+ testExpression = `<Text>{${objectName}.${methodName}(${extraParams})}</Text>`;
373
+ }
375
374
 
376
- const ast = parse(testExpression);
377
- const result = analyze(ast, baseSchema);
378
- expect(result.hasWarnings).toBe(true);
379
- expect(result.warnings[0].message).toContain(`${displayName} expects at most ${maxParams} parameter`);
380
- });
381
- }
375
+ const ast = parse(testExpression);
376
+ const result = analyze(ast, baseSchema);
377
+ expect(result.hasWarnings).toBe(true);
378
+ expect(result.warnings[0].message).toContain(`${displayName} expects at most ${maxParams} parameter`);
379
+ });
382
380
  });
383
381
  });
384
382
  });
@@ -388,12 +386,14 @@ describe("analyze - consolidated tests", () => {
388
386
  const builtins = getBuiltins();
389
387
 
390
388
  Object.entries(builtins).forEach(([objectName, schema]) => {
391
- if (!schema.properties) return;
389
+ if (!schema.properties) {
390
+ return;
391
+ }
392
392
 
393
393
  describe(`${objectName} properties`, () => {
394
394
  Object.entries(schema.properties!).forEach(([propName, propDef]) => {
395
395
  it(`should allow ${objectName}.${propName} property access`, () => {
396
- let testExpression: string;
396
+ let testExpression: string = "";
397
397
 
398
398
  if (objectName === "Array.prototype") {
399
399
  testExpression = `<Text>{items.${propName}}</Text>`;
@@ -1,10 +1,10 @@
1
1
  import type { Program } from "acorn";
2
2
  import type { Schema } from "../schema.js";
3
- import { AnalysisReport } from "./analysis-report.js";
4
- import { analyzeSecurity } from "./security/index.js";
5
- import { analyzeJsx } from "./jsx/index.js";
3
+ import { createAnalysisReport, type AnalysisReport } from "./analysis-report.js";
6
4
  import { analyzeData } from "./data/index.js";
5
+ import { analyzeJsx } from "./jsx/index.js";
6
+ import { analyzeSecurity } from "./security/index.js";
7
7
 
8
8
  export function analyze(ast: Program, schema: Schema): AnalysisReport {
9
- return new AnalysisReport().merge(analyzeSecurity(ast, schema), analyzeJsx(ast, schema), analyzeData(ast, schema));
9
+ return createAnalysisReport().merge(analyzeSecurity(ast, schema), analyzeJsx(ast, schema), analyzeData(ast, schema));
10
10
  }
@@ -1,15 +1,12 @@
1
1
  import type { Program } from "acorn";
2
2
  import type { Schema } from "../../schema.js";
3
- import { AnalysisReport } from "../analysis-report.js";
4
- import { ValidationContext } from "../validation-context.js";
5
- import { analyzeMethodCalls } from "./method-calls.js";
3
+ import { createAnalysisReport, type AnalysisReport } from "../analysis-report.js";
4
+ import { createValidationContext } from "../validation-context.js";
6
5
  import { analyzeDataAccess } from "./data-access.js";
6
+ import { analyzeMethodCalls } from "./method-calls.js";
7
7
 
8
8
  export function analyzeData(ast: Program, schema: Schema): AnalysisReport {
9
- const validationContext = new ValidationContext();
9
+ const validationContext = createValidationContext();
10
10
 
11
- return new AnalysisReport().merge(
12
- analyzeMethodCalls(ast, schema, validationContext),
13
- analyzeDataAccess(ast, schema, validationContext)
14
- );
11
+ return createAnalysisReport().merge(analyzeMethodCalls(ast, schema, validationContext), analyzeDataAccess(ast, schema, validationContext));
15
12
  }
@@ -1,20 +1,20 @@
1
- import type { Program } from "acorn";
2
- import { traverse } from "../../traverse.js";
1
+ import type { AnyNode, ArrowFunctionExpression, CallExpression, Identifier, MemberExpression, Program } from "acorn";
3
2
  import type { ArrayPropertySchema, PropertySchema, Schema } from "../../schema.js";
3
+ import { traverse } from "../../traverse.js";
4
+ import { createAnalysisReport, type AnalysisReport } from "../analysis-report.js";
5
+ import { getNodeRange } from "../utils.js";
4
6
  import type { ValidationContext } from "../validation-context.js";
5
- import { AnalysisReport } from "../analysis-report.js";
6
7
  import { isSimpleDataAccess, extractPath, validateSchemaPath } from "./utils.js";
7
- import { getNodeRange } from "../utils.js";
8
8
 
9
9
  export function analyzeDataAccess(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
10
- const analysisReport = new AnalysisReport();
10
+ const analysisReport = createAnalysisReport();
11
11
 
12
12
  const dataKeys = new Set(Object.keys(schema.data ?? {}));
13
13
  const dataPaths = new Set<string>();
14
- const arrowFunctionContexts = new Map<any, Map<string, any>>();
14
+ const arrowFunctionContexts = new Map<ArrowFunctionExpression, Map<string, PropertySchema>>();
15
15
 
16
16
  // First pass: collect all data paths and arrow function contexts (including nested ones)
17
- function collectArrowFunctionContexts(node: any): void {
17
+ function collectArrowFunctionContexts(node: AnyNode): void {
18
18
  if (node.type === "CallExpression") {
19
19
  const { callee, arguments: args } = node;
20
20
 
@@ -35,7 +35,7 @@ export function analyzeDataAccess(ast: Program, schema: Schema, validationContex
35
35
  const arrowFunction = args[0];
36
36
 
37
37
  if (arrayElementType && arrowFunction.params.length > 0) {
38
- const parameterTypes = new Map();
38
+ const parameterTypes = new Map<string, PropertySchema>();
39
39
 
40
40
  // First parameter is always the array element
41
41
  if (arrowFunction.params[0].type === "Identifier") {
@@ -54,17 +54,18 @@ export function analyzeDataAccess(ast: Program, schema: Schema, validationContex
54
54
  }
55
55
 
56
56
  // Recursively traverse all child nodes
57
- for (const key of Object.keys(node)) {
58
- const value = node[key];
57
+ const record = node as unknown as Record<string, unknown>;
58
+ for (const key of Object.keys(record)) {
59
+ const value = record[key];
59
60
  if (value && typeof value === "object") {
60
61
  if (Array.isArray(value)) {
61
62
  for (const child of value) {
62
- if (child && typeof child === "object" && child.type) {
63
- collectArrowFunctionContexts(child);
63
+ if (child && typeof child === "object" && (child as Record<string, unknown>)["type"]) {
64
+ collectArrowFunctionContexts(child as AnyNode);
64
65
  }
65
66
  }
66
- } else if (value.type) {
67
- collectArrowFunctionContexts(value);
67
+ } else if ((value as Record<string, unknown>)["type"]) {
68
+ collectArrowFunctionContexts(value as AnyNode);
68
69
  }
69
70
  }
70
71
  }
@@ -105,7 +106,7 @@ export function analyzeDataAccess(ast: Program, schema: Schema, validationContex
105
106
  traverse(ast, {
106
107
  MemberExpression(node) {
107
108
  const parent = parentMap.get(node);
108
- if (parent && parent.type === "CallExpression" && parent.callee === node) {
109
+ if (parent?.type === "CallExpression" && parent.callee === node) {
109
110
  return;
110
111
  }
111
112
 
@@ -138,10 +139,10 @@ export function analyzeDataAccess(ast: Program, schema: Schema, validationContex
138
139
  }
139
140
 
140
141
  function findEnclosingArrowContext(
141
- targetNode: any,
142
- arrowFunctionContexts: Map<any, Map<string, any>>
143
- ): Map<string, any> | null {
144
- let best: { arrowFn: any; size: number } | null = null;
142
+ targetNode: MemberExpression,
143
+ arrowFunctionContexts: Map<ArrowFunctionExpression, Map<string, PropertySchema>>
144
+ ): Map<string, PropertySchema> | null {
145
+ let best: { arrowFn: ArrowFunctionExpression; size: number } | null = null;
145
146
  for (const arrowFn of arrowFunctionContexts.keys()) {
146
147
  if (targetNode.start >= arrowFn.start && targetNode.end <= arrowFn.end) {
147
148
  const size = arrowFn.end - arrowFn.start;
@@ -150,14 +151,14 @@ function findEnclosingArrowContext(
150
151
  }
151
152
  }
152
153
  }
153
- return best ? arrowFunctionContexts.get(best.arrowFn) || null : null;
154
+ return best ? (arrowFunctionContexts.get(best.arrowFn) ?? null) : null;
154
155
  }
155
156
 
156
157
  function getArrayElementTypeFromCall(
157
- node: any,
158
+ node: CallExpression,
158
159
  schema: Schema,
159
- arrowFunctionContexts: Map<any, Map<string, any>>
160
- ): any {
160
+ arrowFunctionContexts: Map<ArrowFunctionExpression, Map<string, PropertySchema>>
161
+ ): PropertySchema | null {
161
162
  const { callee } = node;
162
163
  const dataKeys = new Set(Object.keys(schema.data ?? {}));
163
164
 
@@ -188,9 +189,9 @@ function getArrayElementTypeFromCall(
188
189
  // Find the arrow function context that defines this parameter
189
190
  const parameterType = resolveParameterType(paramName, node, arrowFunctionContexts);
190
191
 
191
- if (parameterType && parameterType.type === "object" && parameterType.shape) {
192
+ if (parameterType?.type === "object" && parameterType.shape) {
192
193
  const propertySchema = parameterType.shape[propertyName];
193
- if (propertySchema && propertySchema.type === "array") {
194
+ if (propertySchema?.type === "array") {
194
195
  return getArrayElementType(propertySchema);
195
196
  }
196
197
  }
@@ -202,9 +203,9 @@ function getArrayElementTypeFromCall(
202
203
 
203
204
  function resolveParameterType(
204
205
  paramName: string,
205
- currentNode: any,
206
- arrowFunctionContexts: Map<any, Map<string, any>>
207
- ): any {
206
+ currentNode: AnyNode,
207
+ arrowFunctionContexts: Map<ArrowFunctionExpression, Map<string, PropertySchema>>
208
+ ): PropertySchema | undefined | null {
208
209
  // Find the arrow function that contains this node and has the parameter
209
210
  for (const [arrowFunction, parameterTypes] of arrowFunctionContexts.entries()) {
210
211
  if (parameterTypes.has(paramName)) {
@@ -217,7 +218,7 @@ function resolveParameterType(
217
218
  return null;
218
219
  }
219
220
 
220
- function isNodeInsideArrowFunction(targetNode: any, arrowFunction: any): boolean {
221
+ function isNodeInsideArrowFunction(targetNode: AnyNode, arrowFunction: ArrowFunctionExpression): boolean {
221
222
  // Simple check: if the target node's position is within the arrow function's range
222
223
  if (targetNode.start >= arrowFunction.start && targetNode.end <= arrowFunction.end) {
223
224
  return true;
@@ -225,9 +226,10 @@ function isNodeInsideArrowFunction(targetNode: any, arrowFunction: any): boolean
225
226
  return false;
226
227
  }
227
228
 
228
- function getSchemaAtPath(path: string[], schema: Schema): any {
229
+ function getSchemaAtPath(path: Array<string>, schema: Schema): PropertySchema | null | undefined {
229
230
  if (!schema.data || path.length === 0) {
230
- return schema.data;
231
+ // oxlint-disable-next-line typescript/no-explicit-any -- this branch returns the raw schema.data record (not a PropertySchema) when no path segments remain; preserved as-is for behavior parity
232
+ return schema.data as any;
231
233
  }
232
234
 
233
235
  let current = schema.data[path[0]];
@@ -244,8 +246,8 @@ function getSchemaAtPath(path: string[], schema: Schema): any {
244
246
  }
245
247
 
246
248
  function validateParameterAccess(
247
- node: any,
248
- paramType: any,
249
+ node: MemberExpression,
250
+ paramType: PropertySchema,
249
251
  analysisReport: AnalysisReport,
250
252
  validationContext: ValidationContext
251
253
  ): void {
@@ -259,7 +261,7 @@ function validateParameterAccess(
259
261
  if (!paramType.shape[propertyName]) {
260
262
  analysisReport.addIssue(
261
263
  "INVALID_PARAMETER_ACCESS",
262
- `Property '${propertyName}' does not exist on parameter '${node.object.name}'`,
264
+ `Property '${propertyName}' does not exist on parameter '${(node.object as Identifier).name}'`,
263
265
  getNodeRange(node),
264
266
  validationContext.getSnapshot(),
265
267
  Object.keys(paramType.shape)
@@ -268,9 +270,7 @@ function validateParameterAccess(
268
270
  } else {
269
271
  analysisReport.addIssue(
270
272
  "INVALID_PARAMETER_ACCESS",
271
- `Cannot access property '${propertyName}' on ${paramType.type || "undefined"} type parameter '${
272
- node.object.name
273
- }'`,
273
+ `Cannot access property '${propertyName}' on ${paramType.type ?? "undefined"} type parameter '${(node.object as Identifier).name}'`,
274
274
  getNodeRange(node),
275
275
  validationContext.getSnapshot()
276
276
  );
@@ -281,20 +281,23 @@ export function getArrayElementType(schema: ArrayPropertySchema): PropertySchema
281
281
  return schema.shape || null;
282
282
  }
283
283
 
284
- function buildParentMap(ast: Program): Map<any, any> {
285
- const map = new Map<any, any>();
286
- function visit(node: any, parent: any): void {
287
- if (!node || typeof node !== "object" || !node.type) return;
288
- map.set(node, parent);
289
- for (const key of Object.keys(node)) {
290
- const value = node[key];
284
+ function buildParentMap(ast: Program): Map<AnyNode, AnyNode | null> {
285
+ const map = new Map<AnyNode, AnyNode | null>();
286
+ function visit(node: unknown, parent: AnyNode | null): void {
287
+ const record = node as Record<string, unknown> | null;
288
+ if (!record || typeof record !== "object" || !record["type"]) {
289
+ return;
290
+ }
291
+ map.set(node as AnyNode, parent);
292
+ for (const key of Object.keys(record)) {
293
+ const value = record[key];
291
294
  if (value && typeof value === "object") {
292
295
  if (Array.isArray(value)) {
293
296
  for (const child of value) {
294
- visit(child, node);
297
+ visit(child, node as AnyNode);
295
298
  }
296
299
  } else {
297
- visit(value, node);
300
+ visit(value, node as AnyNode);
298
301
  }
299
302
  }
300
303
  }
@@ -1,14 +1,14 @@
1
1
  import type { Program } from "acorn";
2
- import { traverse } from "../../traverse.js";
3
- import type { Schema } from "../../schema.js";
4
2
  import { isMethodAllowed, getMethodDefinition, getMethodDisplayName } from "../../builtins.js";
3
+ import type { Schema } from "../../schema.js";
4
+ import { traverse } from "../../traverse.js";
5
+ import { createAnalysisReport, type AnalysisReport } from "../analysis-report.js";
6
+ import { getNodeRange } from "../utils.js";
5
7
  import type { ValidationContext } from "../validation-context.js";
6
- import { AnalysisReport } from "../analysis-report.js";
7
8
  import { isSimpleDataAccess, extractPath, validateSchemaPath } from "./utils.js";
8
- import { getNodeRange } from "../utils.js";
9
9
 
10
10
  export function analyzeMethodCalls(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
11
- const analysisReport = new AnalysisReport();
11
+ const analysisReport = createAnalysisReport();
12
12
  const dataKeys = new Set(Object.keys(schema.data ?? {}));
13
13
 
14
14
  traverse(ast, {
@@ -31,18 +31,13 @@ export function analyzeMethodCalls(ast: Program, schema: Schema, validationConte
31
31
  if (!isMethodAllowed(method)) {
32
32
  const methodContext = callee.object.type === "Identifier" ? `${callee.object.name}.${method}` : `.${method}`;
33
33
 
34
- analysisReport.addIssue(
35
- "METHOD_NOT_ALLOWED",
36
- `method ${methodContext} not allowed`,
37
- getNodeRange(callee),
38
- validationContext.getSnapshot()
39
- );
34
+ analysisReport.addIssue("METHOD_NOT_ALLOWED", `method ${methodContext} not allowed`, getNodeRange(callee), validationContext.getSnapshot());
40
35
  return;
41
36
  }
42
37
 
43
38
  const methodDef = getMethodDefinition(method);
44
39
 
45
- if (!methodDef || !methodDef.params) {
40
+ if (!methodDef?.params) {
46
41
  return;
47
42
  }
48
43
 
@@ -70,9 +65,7 @@ export function analyzeMethodCalls(ast: Program, schema: Schema, validationConte
70
65
  if (args.length < minParams) {
71
66
  analysisReport.addIssue(
72
67
  "INSUFFICIENT_PARAMETERS",
73
- `${getMethodDisplayName(method)} expects at least ${minParams} parameter${
74
- minParams !== 1 ? "s" : ""
75
- }, got ${args.length}`,
68
+ `${getMethodDisplayName(method)} expects at least ${minParams} parameter${minParams !== 1 ? "s" : ""}, got ${args.length}`,
76
69
  getNodeRange(node),
77
70
  validationContext.getSnapshot()
78
71
  );
@@ -81,9 +74,7 @@ export function analyzeMethodCalls(ast: Program, schema: Schema, validationConte
81
74
  if (args.length > maxParams) {
82
75
  analysisReport.addIssue(
83
76
  "TOO_MANY_PARAMETERS",
84
- `${getMethodDisplayName(method)} expects at most ${maxParams} parameter${
85
- maxParams !== 1 ? "s" : ""
86
- }, got ${args.length}`,
77
+ `${getMethodDisplayName(method)} expects at most ${maxParams} parameter${maxParams !== 1 ? "s" : ""}, got ${args.length}`,
87
78
  getNodeRange(node),
88
79
  validationContext.getSnapshot()
89
80
  );
@@ -1,11 +1,6 @@
1
- import type { MemberExpression } from "acorn";
2
- import {
3
- isAllowedOnArray,
4
- isAllowedOnString,
5
- getAvailableArrayMembers,
6
- getAvailableStringMembers,
7
- } from "../../builtins.js";
8
- import type { Schema } from "../../schema.js";
1
+ import type { Expression, MemberExpression, PrivateIdentifier, Super } from "acorn";
2
+ import { isAllowedOnArray, isAllowedOnString, getAvailableArrayMembers, getAvailableStringMembers } from "../../builtins.js";
3
+ import type { PropertySchema, Schema } from "../../schema.js";
9
4
  import type { AnalysisReport } from "../analysis-report.js";
10
5
  import { getNodeRange } from "../utils.js";
11
6
  import type { ValidationContext } from "../validation-context.js";
@@ -42,8 +37,8 @@ export function isSimpleDataAccess(node: MemberExpression, dataKeys: ReadonlySet
42
37
  return false;
43
38
  }
44
39
 
45
- export function extractPath(node: MemberExpression): string[] {
46
- const path: string[] = [];
40
+ export function extractPath(node: MemberExpression): Array<string> {
41
+ const path: Array<string> = [];
47
42
 
48
43
  if (node.property.type === "Identifier") {
49
44
  path.unshift(node.property.name);
@@ -70,7 +65,8 @@ export function extractPath(node: MemberExpression): string[] {
70
65
  return path;
71
66
  }
72
67
 
73
- export function getAvailablePropsAtPath(path: string[], depth: number, schemaData: any): string[] {
68
+ export function getAvailablePropsAtPath(path: Array<string>, depth: number, schemaDataParam: unknown): Array<string> {
69
+ const schemaData = schemaDataParam as Record<string, PropertySchema> | undefined;
74
70
  if (!schemaData || depth < 0) {
75
71
  return schemaData ? Object.keys(schemaData) : [];
76
72
  }
@@ -79,16 +75,16 @@ export function getAvailablePropsAtPath(path: string[], depth: number, schemaDat
79
75
  return Object.keys(schemaData);
80
76
  }
81
77
 
82
- let current = schemaData[path[0]];
78
+ let current: PropertySchema | undefined = schemaData[path[0]];
83
79
 
84
80
  for (let i = 1; i <= depth; i++) {
85
- if (!current || current.type !== "object" || !current.shape) {
81
+ if (current?.type !== "object" || !current.shape) {
86
82
  return [];
87
83
  }
88
84
  current = current.shape[path[i]];
89
85
  }
90
86
 
91
- if (current && current.type === "object" && current.shape) {
87
+ if (current?.type === "object" && current.shape) {
92
88
  return Object.keys(current.shape);
93
89
  }
94
90
 
@@ -96,7 +92,7 @@ export function getAvailablePropsAtPath(path: string[], depth: number, schemaDat
96
92
  }
97
93
 
98
94
  export function validateSchemaPath(
99
- path: string[],
95
+ path: Array<string>,
100
96
  schema: Schema,
101
97
  analysisReport: AnalysisReport,
102
98
  node: MemberExpression,
@@ -268,12 +264,12 @@ export function validateSchemaPath(
268
264
  }
269
265
  }
270
266
 
271
- function getElementAccessFlags(node: MemberExpression, pathLength: number): boolean[] {
267
+ function getElementAccessFlags(node: MemberExpression, pathLength: number): Array<boolean> {
272
268
  const flags = Array(pathLength).fill(false);
273
- const segments: Array<{ property: any; computed: boolean }> = [];
269
+ const segments: Array<{ property: Expression | PrivateIdentifier; computed: boolean }> = [];
274
270
 
275
- let current: any = node;
276
- while (current && current.type === "MemberExpression") {
271
+ let current: Expression | Super | undefined = node;
272
+ while (current?.type === "MemberExpression") {
277
273
  segments.unshift({ property: current.property, computed: current.computed });
278
274
  current = current.object;
279
275
  }
@@ -1,3 +1,3 @@
1
1
  export { analyze } from "./analyze.js";
2
2
  export { AnalysisError } from "./analysis-error.js";
3
- export { AnalysisReport } from "./analysis-report.js";
3
+ export { type AnalysisReport, createAnalysisReport } from "./analysis-report.js";
@@ -1,15 +1,15 @@
1
1
  import type { Program } from "acorn";
2
2
  import type { Schema } from "../../schema.js";
3
- import { AnalysisReport } from "../analysis-report.js";
4
- import { ValidationContext } from "../validation-context.js";
3
+ import { createAnalysisReport, type AnalysisReport } from "../analysis-report.js";
4
+ import { createValidationContext } from "../validation-context.js";
5
5
  import { analyzeElementAttributes } from "./element-attributes.js";
6
6
  import { analyzeElementChildren } from "./element-children.js";
7
7
  import { analyzeElementTags } from "./element-tags.js";
8
8
 
9
9
  export function analyzeJsx(ast: Program, schema: Schema): AnalysisReport {
10
- const validationContext = new ValidationContext();
10
+ const validationContext = createValidationContext();
11
11
 
12
- return new AnalysisReport().merge(
12
+ return createAnalysisReport().merge(
13
13
  analyzeElementTags(ast, schema, validationContext),
14
14
  analyzeElementAttributes(ast, schema, validationContext),
15
15
  analyzeElementChildren(ast, schema, validationContext)
@@ -1,24 +1,13 @@
1
1
  import type { Program } from "acorn";
2
2
  import { isJsxAttribute, isJsxExpressionContainer, type JSXAttribute } from "../../jsx.js";
3
- import {
4
- getAllowedAttributes,
5
- getAttributeSchema,
6
- getEnumValues,
7
- getRequiredAttributes,
8
- isAttributeAllowed,
9
- type Schema,
10
- } from "../../schema.js";
3
+ import { getAllowedAttributes, getAttributeSchema, getEnumValues, getRequiredAttributes, isAttributeAllowed, type Schema } from "../../schema.js";
11
4
  import { traverse } from "../../traverse.js";
12
- import { AnalysisReport } from "../analysis-report.js";
5
+ import { createAnalysisReport, type AnalysisReport } from "../analysis-report.js";
13
6
  import { getAttributeSimilarityMatchers, getBestSimilarityMatcherSuggestion, getNodeRange } from "../utils.js";
14
7
  import type { ValidationContext } from "../validation-context.js";
15
8
 
16
- export function analyzeElementAttributes(
17
- ast: Program,
18
- schema: Schema,
19
- validationContext: ValidationContext
20
- ): AnalysisReport {
21
- const analysisReport = new AnalysisReport();
9
+ export function analyzeElementAttributes(ast: Program, schema: Schema, validationContext: ValidationContext): AnalysisReport {
10
+ const analysisReport = createAnalysisReport();
22
11
 
23
12
  const localFunctionNames = new Set<string>();
24
13
  for (const stmt of ast.body) {
@@ -87,9 +76,7 @@ export function analyzeElementAttributes(
87
76
  const stringEnumValues = enumValues.map((value) => String(value));
88
77
  const suggestionMatch = getBestSimilarityMatcherSuggestion(String(literalValue), stringEnumValues);
89
78
 
90
- let message = `Value ${JSON.stringify(
91
- literalValue
92
- )} is not allowed for attribute "${attributeName}" on <${tagName}>`;
79
+ let message = `Value ${JSON.stringify(literalValue)} is not allowed for attribute "${attributeName}" on <${tagName}>`;
93
80
 
94
81
  if (suggestionMatch) {
95
82
  message += `. Did you mean "${suggestionMatch}"?`;