jsxpression 0.1.27 → 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.
Files changed (141) hide show
  1. package/README.md +155 -188
  2. package/lib/analyze/analysis-report.d.ts +2 -2
  3. package/lib/analyze/analysis-report.d.ts.map +1 -1
  4. package/lib/analyze/analysis-report.js +4 -0
  5. package/lib/analyze/analysis-report.js.map +1 -1
  6. package/lib/analyze/data/data-access.d.ts.map +1 -1
  7. package/lib/analyze/data/data-access.js +45 -82
  8. package/lib/analyze/data/data-access.js.map +1 -1
  9. package/lib/analyze/data/method-calls.d.ts.map +1 -1
  10. package/lib/analyze/data/method-calls.js +4 -3
  11. package/lib/analyze/data/method-calls.js.map +1 -1
  12. package/lib/analyze/data/utils.d.ts +2 -3
  13. package/lib/analyze/data/utils.d.ts.map +1 -1
  14. package/lib/analyze/data/utils.js +10 -36
  15. package/lib/analyze/data/utils.js.map +1 -1
  16. package/lib/analyze/jsx/element-attributes.d.ts +1 -1
  17. package/lib/analyze/jsx/element-attributes.d.ts.map +1 -1
  18. package/lib/analyze/jsx/element-attributes.js +13 -3
  19. package/lib/analyze/jsx/element-attributes.js.map +1 -1
  20. package/lib/analyze/jsx/element-children.d.ts +1 -1
  21. package/lib/analyze/jsx/element-children.d.ts.map +1 -1
  22. package/lib/analyze/jsx/element-children.js +16 -5
  23. package/lib/analyze/jsx/element-children.js.map +1 -1
  24. package/lib/analyze/jsx/element-tags.d.ts +1 -1
  25. package/lib/analyze/jsx/element-tags.d.ts.map +1 -1
  26. package/lib/analyze/jsx/element-tags.js +12 -2
  27. package/lib/analyze/jsx/element-tags.js.map +1 -1
  28. package/lib/analyze/security/analyze.d.ts.map +1 -1
  29. package/lib/analyze/security/analyze.js +2 -4
  30. package/lib/analyze/security/analyze.js.map +1 -1
  31. package/lib/analyze/security/call-expressions.d.ts +2 -2
  32. package/lib/analyze/security/call-expressions.d.ts.map +1 -1
  33. package/lib/analyze/security/call-expressions.js +21 -5
  34. package/lib/analyze/security/call-expressions.js.map +1 -1
  35. package/lib/analyze/security/declarations.d.ts +1 -1
  36. package/lib/analyze/security/declarations.d.ts.map +1 -1
  37. package/lib/analyze/security/declarations.js +22 -2
  38. package/lib/analyze/security/declarations.js.map +1 -1
  39. package/lib/analyze/security/expressions.d.ts.map +1 -1
  40. package/lib/analyze/security/expressions.js +10 -2
  41. package/lib/analyze/security/expressions.js.map +1 -1
  42. package/lib/analyze/security/identifiers.d.ts +2 -2
  43. package/lib/analyze/security/identifiers.d.ts.map +1 -1
  44. package/lib/analyze/security/identifiers.js +159 -19
  45. package/lib/analyze/security/identifiers.js.map +1 -1
  46. package/lib/builtins.d.ts.map +1 -1
  47. package/lib/builtins.js +5 -0
  48. package/lib/builtins.js.map +1 -1
  49. package/lib/codegen/schema/utils.d.ts.map +1 -1
  50. package/lib/codegen/schema/utils.js +7 -1
  51. package/lib/codegen/schema/utils.js.map +1 -1
  52. package/lib/compile/compile.d.ts.map +1 -1
  53. package/lib/compile/compile.js +131 -61
  54. package/lib/compile/compile.js.map +1 -1
  55. package/lib/evaluate/evaluate.d.ts +3 -3
  56. package/lib/evaluate/evaluate.d.ts.map +1 -1
  57. package/lib/evaluate/evaluate.js +28 -7
  58. package/lib/evaluate/evaluate.js.map +1 -1
  59. package/lib/jsx.d.ts +2 -19
  60. package/lib/jsx.d.ts.map +1 -1
  61. package/lib/jsx.js +0 -6
  62. package/lib/jsx.js.map +1 -1
  63. package/lib/parse/parse.d.ts.map +1 -1
  64. package/lib/parse/parse.js +4 -3
  65. package/lib/parse/parse.js.map +1 -1
  66. package/lib/render.d.ts +4 -4
  67. package/lib/render.d.ts.map +1 -1
  68. package/lib/render.js +7 -9
  69. package/lib/render.js.map +1 -1
  70. package/lib/schema.d.ts +2 -7
  71. package/lib/schema.d.ts.map +1 -1
  72. package/lib/schema.js +0 -43
  73. package/lib/schema.js.map +1 -1
  74. package/lib/traverse.d.ts +4 -4
  75. package/lib/traverse.d.ts.map +1 -1
  76. package/lib/traverse.js +118 -40
  77. package/lib/traverse.js.map +1 -1
  78. package/lib/validate.d.ts +1 -1
  79. package/lib/validate.js +1 -1
  80. package/package.json +4 -3
  81. package/src/analyze/analysis-report.ts +6 -2
  82. package/src/analyze/analyze.test.ts +108 -69
  83. package/src/analyze/data/data-access.ts +45 -90
  84. package/src/analyze/data/method-calls.ts +4 -3
  85. package/src/analyze/data/utils.ts +11 -40
  86. package/src/analyze/jsx/element-attributes.ts +24 -9
  87. package/src/analyze/jsx/element-children.ts +20 -6
  88. package/src/analyze/jsx/element-tags.ts +15 -3
  89. package/src/analyze/security/analyze.ts +1 -3
  90. package/src/analyze/security/call-expressions.ts +37 -17
  91. package/src/analyze/security/declarations.ts +35 -9
  92. package/src/analyze/security/expressions.ts +17 -3
  93. package/src/analyze/security/identifiers.ts +172 -28
  94. package/src/builtins.ts +6 -0
  95. package/src/codegen/generate-typescript-definitions.test.ts +16 -23
  96. package/src/codegen/schema/utils.ts +9 -1
  97. package/src/compile/compile.test.ts +153 -30
  98. package/src/compile/compile.ts +163 -73
  99. package/src/evaluate/evaluate.test.ts +276 -14
  100. package/src/evaluate/evaluate.ts +34 -10
  101. package/src/jsx.ts +2 -31
  102. package/src/parse/parse.test.ts +4 -4
  103. package/src/parse/parse.ts +4 -3
  104. package/src/render.test.ts +476 -27
  105. package/src/render.ts +7 -14
  106. package/src/schema.ts +1 -55
  107. package/src/traverse.ts +135 -49
  108. package/src/validate.ts +1 -1
  109. package/LICENSE +0 -21
  110. package/lib/analyze/analyze.test.d.ts +0 -2
  111. package/lib/analyze/analyze.test.d.ts.map +0 -1
  112. package/lib/analyze/analyze.test.js +0 -763
  113. package/lib/analyze/analyze.test.js.map +0 -1
  114. package/lib/analyze/security/member-access.d.ts +0 -6
  115. package/lib/codegen/generate-typescript-definitions.test.d.ts +0 -2
  116. package/lib/codegen/generate-typescript-definitions.test.d.ts.map +0 -1
  117. package/lib/codegen/generate-typescript-definitions.test.js +0 -649
  118. package/lib/codegen/generate-typescript-definitions.test.js.map +0 -1
  119. package/lib/compile/compile.test.d.ts +0 -2
  120. package/lib/compile/compile.test.d.ts.map +0 -1
  121. package/lib/compile/compile.test.js +0 -146
  122. package/lib/compile/compile.test.js.map +0 -1
  123. package/lib/evaluate/evaluate.test.d.ts +0 -2
  124. package/lib/evaluate/evaluate.test.d.ts.map +0 -1
  125. package/lib/evaluate/evaluate.test.js +0 -260
  126. package/lib/evaluate/evaluate.test.js.map +0 -1
  127. package/lib/parse/parse.test.d.ts +0 -2
  128. package/lib/parse/parse.test.d.ts.map +0 -1
  129. package/lib/parse/parse.test.js +0 -80
  130. package/lib/parse/parse.test.js.map +0 -1
  131. package/lib/render.test.d.ts +0 -2
  132. package/lib/render.test.d.ts.map +0 -1
  133. package/lib/render.test.js +0 -301
  134. package/lib/render.test.js.map +0 -1
  135. package/lib/traverse.test.d.ts +0 -2
  136. package/lib/traverse.test.d.ts.map +0 -1
  137. package/lib/traverse.test.js +0 -84
  138. package/lib/traverse.test.js.map +0 -1
  139. package/lib/types.d.ts +0 -12
  140. package/src/analyze/security/member-access.ts +0 -35
  141. package/src/types.ts +0 -17
@@ -1,9 +1,10 @@
1
1
  import type { Program } from "acorn";
2
- import { traverse } from "../../traverse.js";
2
+ import { isJsxRoot } from "../../jsx.js";
3
3
  import type { Schema } from "../../schema.js";
4
- import { ValidationContext } from "../validation-context.js";
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
- analysisReport.addIssue(
42
- "FUNCTION_NOT_ALLOWED",
43
- "function declarations not allowed",
44
- getNodeRange(node),
45
- validationContext.getSnapshot()
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
- "variables not allowed",
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; // Allow numeric array indices
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 { ValidationContext } from "../validation-context.js";
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 allowedRoots = ["props", "data", ...getBuiltinGlobals()];
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 arrowParent = findArrowParent(node, ast);
52
+ const scopeParent = findScopeParent(node, ast);
28
53
 
29
- if (arrowParent) {
30
- const params = arrowParamScopes.get(arrowParent);
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
- if (p.type === "Identifier") {
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
- // Walk up the AST tree to find the nearest ArrowFunctionExpression parent.
73
- // This lets us check if an identifier is in scope as an arrow function parameter.
74
- function findArrowParent(targetNode: AnyNode, rootNode: AnyNode): ArrowFunctionExpression | null {
75
- let found: ArrowFunctionExpression | null = null;
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 isArrow = node.type === "ArrowFunctionExpression";
198
+ const isScope = node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration";
83
199
 
84
- if (isArrow) {
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 (isArrow) {
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 data:");
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 data:");
285
- expect(result).toContain("name: string");
286
- expect(result).toContain("age: number");
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 data:");
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 data:");
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 data:");
359
- expect(result).toContain("tags: string[]");
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 data:");
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 data:");
423
- expect(result).toContain('status: "pending" | "active" | "completed"');
424
- expect(result).toContain("priority: 1 | 2 | 3");
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 data:");
452
- expect(result).toContain("userName: string");
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 data:");
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:");
@@ -48,7 +48,8 @@ export function generateObjectTypeWithJSDoc(properties: any, depth: number = 0):
48
48
 
49
49
  const nestedType = mapSchemaTypeToTypeScript(value, depth + 1);
50
50
  const required = value.required !== false ? "" : "?";
51
- result += ` ${key}${required}: ${nestedType};\n`;
51
+
52
+ result += ` ${formatKey(key)}${required}: ${nestedType};\n`;
52
53
  });
53
54
 
54
55
  result += " }";
@@ -112,3 +113,10 @@ export function generatePropertyJSDoc(
112
113
 
113
114
  return output;
114
115
  }
116
+
117
+ function formatKey(key: string): string {
118
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
119
+ return key;
120
+ }
121
+ return `"${key}"`;
122
+ }