jsxpression 0.1.40 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/analyze/analysis-error.d.ts.map +1 -1
- package/lib/analyze/analysis-error.js +2 -7
- package/lib/analyze/analysis-error.js.map +1 -1
- package/lib/analyze/analysis-report.d.ts +13 -13
- package/lib/analyze/analysis-report.d.ts.map +1 -1
- package/lib/analyze/analysis-report.js +48 -45
- package/lib/analyze/analysis-report.js.map +1 -1
- package/lib/analyze/analyze.d.ts +1 -1
- package/lib/analyze/analyze.d.ts.map +1 -1
- package/lib/analyze/analyze.js +4 -4
- package/lib/analyze/analyze.js.map +1 -1
- package/lib/analyze/data/analyze.d.ts +1 -1
- package/lib/analyze/data/analyze.d.ts.map +1 -1
- package/lib/analyze/data/analyze.js +5 -5
- package/lib/analyze/data/analyze.js.map +1 -1
- package/lib/analyze/data/data-access.d.ts +1 -1
- package/lib/analyze/data/data-access.d.ts.map +1 -1
- package/lib/analyze/data/data-access.js +19 -15
- package/lib/analyze/data/data-access.js.map +1 -1
- package/lib/analyze/data/method-calls.d.ts +1 -1
- package/lib/analyze/data/method-calls.d.ts.map +1 -1
- package/lib/analyze/data/method-calls.js +5 -5
- package/lib/analyze/data/method-calls.js.map +1 -1
- package/lib/analyze/data/utils.d.ts +3 -3
- package/lib/analyze/data/utils.d.ts.map +1 -1
- package/lib/analyze/data/utils.js +6 -5
- package/lib/analyze/data/utils.js.map +1 -1
- package/lib/analyze/index.d.ts +1 -1
- package/lib/analyze/index.d.ts.map +1 -1
- package/lib/analyze/index.js +1 -1
- package/lib/analyze/index.js.map +1 -1
- package/lib/analyze/jsx/analyze.d.ts +1 -1
- package/lib/analyze/jsx/analyze.d.ts.map +1 -1
- package/lib/analyze/jsx/analyze.js +4 -4
- package/lib/analyze/jsx/analyze.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 +3 -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 +8 -9
- 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 +2 -2
- package/lib/analyze/jsx/element-tags.js.map +1 -1
- package/lib/analyze/security/analyze.d.ts +1 -1
- package/lib/analyze/security/analyze.d.ts.map +1 -1
- package/lib/analyze/security/analyze.js +4 -4
- package/lib/analyze/security/analyze.js.map +1 -1
- package/lib/analyze/security/call-expressions.d.ts +1 -1
- package/lib/analyze/security/call-expressions.d.ts.map +1 -1
- package/lib/analyze/security/call-expressions.js +3 -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 +6 -6
- package/lib/analyze/security/declarations.js.map +1 -1
- package/lib/analyze/security/expressions.d.ts +1 -1
- package/lib/analyze/security/expressions.d.ts.map +1 -1
- package/lib/analyze/security/expressions.js +3 -6
- package/lib/analyze/security/expressions.js.map +1 -1
- package/lib/analyze/security/identifiers.d.ts +1 -1
- package/lib/analyze/security/identifiers.d.ts.map +1 -1
- package/lib/analyze/security/identifiers.js +19 -22
- package/lib/analyze/security/identifiers.js.map +1 -1
- package/lib/analyze/utils.d.ts +4 -4
- package/lib/analyze/utils.d.ts.map +1 -1
- package/lib/analyze/utils.js +5 -3
- package/lib/analyze/utils.js.map +1 -1
- package/lib/analyze/validation-context.d.ts +8 -8
- package/lib/analyze/validation-context.d.ts.map +1 -1
- package/lib/analyze/validation-context.js +23 -21
- package/lib/analyze/validation-context.js.map +1 -1
- package/lib/builtins.d.ts +6 -6
- package/lib/builtins.d.ts.map +1 -1
- package/lib/builtins.js +9 -8
- package/lib/builtins.js.map +1 -1
- package/lib/codegen/builtins/builtin-types.d.ts.map +1 -1
- package/lib/codegen/builtins/builtin-types.js +9 -13
- package/lib/codegen/builtins/builtin-types.js.map +1 -1
- package/lib/codegen/generate-typescript-definitions.d.ts +18 -17
- package/lib/codegen/generate-typescript-definitions.d.ts.map +1 -1
- package/lib/codegen/generate-typescript-definitions.js +19 -23
- package/lib/codegen/generate-typescript-definitions.js.map +1 -1
- package/lib/codegen/schema/component-types.d.ts.map +1 -1
- package/lib/codegen/schema/component-types.js +7 -4
- package/lib/codegen/schema/component-types.js.map +1 -1
- package/lib/codegen/schema/data-types.d.ts.map +1 -1
- package/lib/codegen/schema/data-types.js +1 -1
- package/lib/codegen/schema/data-types.js.map +1 -1
- package/lib/codegen/schema/function-types.js.map +1 -1
- package/lib/codegen/schema/utils.d.ts +4 -3
- package/lib/codegen/schema/utils.d.ts.map +1 -1
- package/lib/codegen/schema/utils.js +12 -8
- package/lib/codegen/schema/utils.js.map +1 -1
- package/lib/compile/compilation-error.d.ts +1 -1
- package/lib/compile/compilation-error.d.ts.map +1 -1
- package/lib/compile/compilation-error.js +3 -1
- package/lib/compile/compilation-error.js.map +1 -1
- package/lib/compile/compile.d.ts.map +1 -1
- package/lib/compile/compile.js +5 -11
- package/lib/compile/compile.js.map +1 -1
- package/lib/evaluate/evaluate.d.ts +6 -6
- package/lib/evaluate/evaluate.d.ts.map +1 -1
- package/lib/evaluate/evaluate.js +2 -2
- package/lib/evaluate/evaluate.js.map +1 -1
- package/lib/evaluate/evaluation-error.d.ts.map +1 -1
- package/lib/evaluate/evaluation-error.js +1 -0
- package/lib/evaluate/evaluation-error.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/jsx.d.ts +3 -3
- package/lib/jsx.d.ts.map +1 -1
- package/lib/jsx.js.map +1 -1
- package/lib/parse/parse-error.d.ts.map +1 -1
- package/lib/parse/parse-error.js +1 -0
- package/lib/parse/parse-error.js.map +1 -1
- package/lib/render.d.ts +25 -33
- package/lib/render.d.ts.map +1 -1
- package/lib/render.js +21 -26
- package/lib/render.js.map +1 -1
- package/lib/schema.d.ts +9 -9
- package/lib/schema.d.ts.map +1 -1
- package/lib/schema.js +1 -1
- package/lib/schema.js.map +1 -1
- package/lib/traverse.d.ts.map +1 -1
- package/lib/traverse.js +0 -1
- package/lib/traverse.js.map +1 -1
- package/lib/validate.d.ts +14 -17
- package/lib/validate.d.ts.map +1 -1
- package/lib/validate.js +13 -14
- package/lib/validate.js.map +1 -1
- package/package.json +14 -14
- package/src/analyze/analysis-error.ts +2 -6
- package/src/analyze/analysis-report.ts +73 -62
- package/src/analyze/analyze.test.ts +14 -18
- package/src/analyze/analyze.ts +4 -4
- package/src/analyze/data/analyze.ts +5 -8
- package/src/analyze/data/data-access.ts +49 -46
- package/src/analyze/data/method-calls.ts +9 -18
- package/src/analyze/data/utils.ts +15 -19
- package/src/analyze/index.ts +1 -1
- package/src/analyze/jsx/analyze.ts +4 -4
- package/src/analyze/jsx/element-attributes.ts +6 -19
- package/src/analyze/jsx/element-children.ts +9 -13
- package/src/analyze/jsx/element-tags.ts +2 -2
- package/src/analyze/security/analyze.ts +4 -4
- package/src/analyze/security/call-expressions.ts +5 -18
- package/src/analyze/security/declarations.ts +14 -43
- package/src/analyze/security/expressions.ts +12 -49
- package/src/analyze/security/identifiers.ts +39 -41
- package/src/analyze/utils.ts +25 -19
- package/src/analyze/validation-context.ts +32 -23
- package/src/builtins.ts +28 -39
- package/src/codegen/builtins/builtin-types.ts +13 -20
- package/src/codegen/generate-typescript-definitions.test.ts +29 -29
- package/src/codegen/generate-typescript-definitions.ts +19 -23
- package/src/codegen/schema/component-types.ts +22 -12
- package/src/codegen/schema/data-types.ts +6 -6
- package/src/codegen/schema/function-types.ts +1 -1
- package/src/codegen/schema/utils.ts +24 -23
- package/src/compile/compilation-error.ts +5 -1
- package/src/compile/compile.test.ts +7 -21
- package/src/compile/compile.ts +33 -45
- package/src/evaluate/evaluate.test.ts +95 -111
- package/src/evaluate/evaluate.ts +16 -21
- package/src/evaluate/evaluation-error.ts +1 -0
- package/src/index.ts +1 -7
- package/src/jsx.ts +4 -6
- package/src/parse/parse-error.ts +1 -0
- package/src/parse/parse.test.ts +1 -1
- package/src/render.test.ts +4 -4
- package/src/render.ts +27 -39
- package/src/schema.ts +12 -12
- package/src/traverse.test.ts +6 -6
- package/src/traverse.ts +15 -16
- package/src/validate.ts +14 -17
- package/CHANGELOG.md +0 -8
package/src/jsx.ts
CHANGED
|
@@ -19,9 +19,7 @@ export type JSXLiteral = {
|
|
|
19
19
|
|
|
20
20
|
export type JSXAttributeValue = JSXLiteral | JSXExpressionContainer;
|
|
21
21
|
|
|
22
|
-
export type JSXNameNode =
|
|
23
|
-
| { type: "JSXIdentifier"; name: string }
|
|
24
|
-
| { type: "JSXMemberExpression"; object: JSXNameNode; property: JSXNameNode };
|
|
22
|
+
export type JSXNameNode = { type: "JSXIdentifier"; name: string } | { type: "JSXMemberExpression"; object: JSXNameNode; property: JSXNameNode };
|
|
25
23
|
|
|
26
24
|
export type JSXAttribute = {
|
|
27
25
|
type: "JSXAttribute";
|
|
@@ -41,7 +39,7 @@ export type JSXOpeningElement = {
|
|
|
41
39
|
name: {
|
|
42
40
|
name: string;
|
|
43
41
|
};
|
|
44
|
-
attributes:
|
|
42
|
+
attributes: Array<JSXAttribute | JSXSpreadAttribute>;
|
|
45
43
|
};
|
|
46
44
|
|
|
47
45
|
export type JSXClosingElement = {
|
|
@@ -53,12 +51,12 @@ export type JSXElement = {
|
|
|
53
51
|
type: "JSXElement";
|
|
54
52
|
openingElement: JSXOpeningElement;
|
|
55
53
|
closingElement?: JSXClosingElement;
|
|
56
|
-
children: JSXNode
|
|
54
|
+
children: Array<JSXNode>;
|
|
57
55
|
};
|
|
58
56
|
|
|
59
57
|
export type JSXFragment = {
|
|
60
58
|
type: "JSXFragment";
|
|
61
|
-
children: JSXNode
|
|
59
|
+
children: Array<JSXNode>;
|
|
62
60
|
};
|
|
63
61
|
|
|
64
62
|
export type JSXText = {
|
package/src/parse/parse-error.ts
CHANGED
package/src/parse/parse.test.ts
CHANGED
package/src/render.test.ts
CHANGED
|
@@ -7,12 +7,12 @@ import { render } from "./render.js";
|
|
|
7
7
|
type TestNode = {
|
|
8
8
|
type: string;
|
|
9
9
|
props: Record<string, unknown>;
|
|
10
|
-
children: unknown
|
|
10
|
+
children: Array<unknown>;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
describe("render", () => {
|
|
14
14
|
it("should render complete JSX pipeline successfully", () => {
|
|
15
|
-
const TestComponent = ({ children, ...props }: { x: number; y: number; children?:
|
|
15
|
+
const TestComponent = ({ children, ...props }: { x: number; y: number; children?: Array<unknown> }): TestNode => ({
|
|
16
16
|
type: "TestComponent",
|
|
17
17
|
props,
|
|
18
18
|
children: children ?? [],
|
|
@@ -76,13 +76,13 @@ describe("render", () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
it("should render nested JSX elements", () => {
|
|
79
|
-
const OuterComponent = ({ children, ...props }: { x: number; children?:
|
|
79
|
+
const OuterComponent = ({ children, ...props }: { x: number; children?: Array<unknown> }): TestNode => ({
|
|
80
80
|
type: "OuterComponent",
|
|
81
81
|
props,
|
|
82
82
|
children: children ?? [],
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
const InnerComponent = ({ children, ...props }: { y: number; children?:
|
|
85
|
+
const InnerComponent = ({ children, ...props }: { y: number; children?: Array<unknown> }): TestNode => ({
|
|
86
86
|
type: "InnerComponent",
|
|
87
87
|
props,
|
|
88
88
|
children: children ?? [],
|
package/src/render.ts
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
+
import { analyze, AnalysisError } from "./analyze/index.js";
|
|
1
2
|
import { compile as compileInternal } from "./compile/index.js";
|
|
2
3
|
import type { EvaluateOptions } from "./evaluate/evaluate.js";
|
|
3
4
|
import { evaluate } from "./evaluate/evaluate.js";
|
|
4
5
|
import { parse } from "./parse/index.js";
|
|
5
6
|
import type { Schema } from "./schema.js";
|
|
6
|
-
import { analyze, AnalysisError } from "./analyze/index.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Configuration options for rendering JSX expressions.
|
|
10
10
|
*
|
|
11
|
-
* Extends EvaluateOptions with render-specific functionality, including control over
|
|
12
|
-
* the minimum severity threshold that stops rendering.
|
|
11
|
+
* Extends EvaluateOptions with render-specific functionality, including control over the minimum severity threshold that stops rendering.
|
|
13
12
|
*
|
|
14
13
|
* @template T - The return type of createElement function
|
|
15
14
|
*/
|
|
16
|
-
export interface RenderOptions<T =
|
|
17
|
-
/**
|
|
18
|
-
* Minimum severity level that counts as a failure: 1=info, 2=warning, 3=error.
|
|
19
|
-
*/
|
|
15
|
+
export interface RenderOptions<T = unknown> extends EvaluateOptions<T> {
|
|
16
|
+
/** Minimum severity level that counts as a failure: 1=info, 2=warning, 3=error. */
|
|
20
17
|
minSeverity?: 1 | 2 | 3;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
/**
|
|
24
21
|
* Renders a JSX expression with data validation and security constraints.
|
|
25
22
|
*
|
|
26
|
-
* Parses JSX source code, validates it against a schema for security and type safety,
|
|
27
|
-
*
|
|
23
|
+
* Parses JSX source code, validates it against a schema for security and type safety, compiles it to executable code, and evaluates it with the
|
|
24
|
+
* provided data and components.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const schema: Schema = {
|
|
29
|
+
* data: {
|
|
30
|
+
* user: { type: "object", shape: { name: { type: "string" } } },
|
|
31
|
+
* },
|
|
32
|
+
* elements: {
|
|
33
|
+
* Text: { props: { children: { type: "string" } } },
|
|
34
|
+
* },
|
|
35
|
+
* };
|
|
36
|
+
*
|
|
37
|
+
* const result = render("<Text>Hello {user.name}!</Text>", schema, {
|
|
38
|
+
* data: { user: { name: "World" } },
|
|
39
|
+
* components: { Text: ({ children }) => children },
|
|
40
|
+
* createElement: (type, props, ...children) => ({ type, props, children }),
|
|
41
|
+
* });
|
|
42
|
+
* ```;
|
|
28
43
|
*
|
|
29
44
|
* @template T - The return type of the rendered JSX (e.g., ReactElement, VNode, string)
|
|
30
45
|
* @param source - JSX expression source code to render (e.g., `"<Text x={user.name}>Hello</Text>"`)
|
|
@@ -35,36 +50,9 @@ export interface RenderOptions<T = any> extends EvaluateOptions<T> {
|
|
|
35
50
|
* @param options.createElement - Function to create elements (e.g., React.createElement)
|
|
36
51
|
* @param options.minSeverity - Minimum severity (1=info, 2=warning, 3=error) required to abort rendering; defaults to `3`
|
|
37
52
|
* @returns The rendered JSX element of type T
|
|
38
|
-
*
|
|
39
53
|
* @throws {AnalysisError} When JSX contains security violations, invalid data access, or schema mismatches
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* const schema: Schema = {
|
|
44
|
-
* data: {
|
|
45
|
-
* user: { type: "object", shape: { name: { type: "string" } } }
|
|
46
|
-
* },
|
|
47
|
-
* elements: {
|
|
48
|
-
* Text: { props: { children: { type: "string" } } }
|
|
49
|
-
* }
|
|
50
|
-
* };
|
|
51
|
-
*
|
|
52
|
-
* const result = render(
|
|
53
|
-
* "<Text>Hello {user.name}!</Text>",
|
|
54
|
-
* schema,
|
|
55
|
-
* {
|
|
56
|
-
* data: { user: { name: "World" } },
|
|
57
|
-
* components: { Text: ({ children }) => children },
|
|
58
|
-
* createElement: (type, props, ...children) => ({ type, props, children })
|
|
59
|
-
* }
|
|
60
|
-
* );
|
|
61
|
-
* ```
|
|
62
54
|
*/
|
|
63
|
-
export function render<T =
|
|
64
|
-
source: string,
|
|
65
|
-
schema: Schema,
|
|
66
|
-
{ minSeverity = 3, ...options }: RenderOptions<T> = {}
|
|
67
|
-
): T {
|
|
55
|
+
export function render<T = unknown>(source: string, schema: Schema, { minSeverity = 3, ...options }: RenderOptions<T> = {}): T {
|
|
68
56
|
const ast = parse(source);
|
|
69
57
|
const report = analyze(ast, schema);
|
|
70
58
|
|
|
@@ -72,10 +60,10 @@ export function render<T = any>(
|
|
|
72
60
|
throw AnalysisError.fromReport(report);
|
|
73
61
|
}
|
|
74
62
|
|
|
75
|
-
return evaluate(compileInternal(ast), schema, options)
|
|
63
|
+
return evaluate(compileInternal(ast), schema, options);
|
|
76
64
|
}
|
|
77
65
|
|
|
78
|
-
export function compile<T =
|
|
66
|
+
export function compile<T = unknown>(source: string, schema: Schema, { minSeverity = 3 }: RenderOptions<T> = {}): string {
|
|
79
67
|
const ast = parse(source);
|
|
80
68
|
const report = analyze(ast, schema);
|
|
81
69
|
if (report.hasIssues(minSeverity)) {
|
package/src/schema.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type StringPropertySchema = {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
required?: boolean;
|
|
8
8
|
default?: string;
|
|
9
|
-
enum?:
|
|
9
|
+
enum?: ReadonlyArray<string>;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export type NumberPropertySchema = {
|
|
@@ -14,7 +14,7 @@ export type NumberPropertySchema = {
|
|
|
14
14
|
description?: string;
|
|
15
15
|
required?: boolean;
|
|
16
16
|
default?: number;
|
|
17
|
-
enum?:
|
|
17
|
+
enum?: ReadonlyArray<number>;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export type BooleanPropertySchema = {
|
|
@@ -22,7 +22,7 @@ export type BooleanPropertySchema = {
|
|
|
22
22
|
description?: string;
|
|
23
23
|
required?: boolean;
|
|
24
24
|
default?: boolean;
|
|
25
|
-
enum?:
|
|
25
|
+
enum?: ReadonlyArray<boolean>;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
export type FunctionPropertySchema = {
|
|
@@ -72,7 +72,7 @@ export type PropertySchema =
|
|
|
72
72
|
export type ElementSchema = {
|
|
73
73
|
description?: string;
|
|
74
74
|
props?: Record<string, PropertySchema>;
|
|
75
|
-
allowedChildren?: string
|
|
75
|
+
allowedChildren?: Array<string>;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export type ArgumentSchema = {
|
|
@@ -161,7 +161,7 @@ export function deserializePropertySchemaFromJson(json: string): PropertySchema
|
|
|
161
161
|
return undefined;
|
|
162
162
|
}
|
|
163
163
|
const validated = zodSchema.safeParse(parsed);
|
|
164
|
-
if(!validated.success) {
|
|
164
|
+
if (!validated.success) {
|
|
165
165
|
return undefined;
|
|
166
166
|
}
|
|
167
167
|
return validated.data;
|
|
@@ -175,7 +175,7 @@ export function getElementSchema(schema: Schema, tagName: string): ElementSchema
|
|
|
175
175
|
return schema.elements?.[tagName];
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
export function getAllowedAttributes(schema: Schema, tagName: string): string
|
|
178
|
+
export function getAllowedAttributes(schema: Schema, tagName: string): Array<string> {
|
|
179
179
|
const elementConfig = getElementSchema(schema, tagName);
|
|
180
180
|
return elementConfig?.props ? Object.keys(elementConfig.props) : [];
|
|
181
181
|
}
|
|
@@ -185,7 +185,7 @@ export function isAttributeAllowed(schema: Schema, tagName: string, attributeNam
|
|
|
185
185
|
return allowedAttributes.includes(attributeName);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
export function getAllowedChildren(schema: Schema, tagName: string): string
|
|
188
|
+
export function getAllowedChildren(schema: Schema, tagName: string): Array<string> | undefined {
|
|
189
189
|
const elementConfig = getElementSchema(schema, tagName);
|
|
190
190
|
return elementConfig?.allowedChildren;
|
|
191
191
|
}
|
|
@@ -197,7 +197,7 @@ export function canHaveChildren(schema: Schema, tagName: string): boolean {
|
|
|
197
197
|
|
|
198
198
|
export function isSelfClosing(schema: Schema, tagName: string): boolean {
|
|
199
199
|
const allowedChildren = getAllowedChildren(schema, tagName);
|
|
200
|
-
return allowedChildren
|
|
200
|
+
return allowedChildren?.length === 0;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
export function isChildAllowed(schema: Schema, parentTag: string, childTag: string): boolean {
|
|
@@ -206,7 +206,7 @@ export function isChildAllowed(schema: Schema, parentTag: string, childTag: stri
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// New schema utilities for enhanced validation
|
|
209
|
-
export function getAllElements(schema: Schema): string
|
|
209
|
+
export function getAllElements(schema: Schema): Array<string> {
|
|
210
210
|
return schema.elements ? Object.keys(schema.elements) : [];
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -215,13 +215,13 @@ export function getAttributeSchema(schema: Schema, tagName: string, attributeNam
|
|
|
215
215
|
return elementSchema?.props?.[attributeName];
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
export function getRequiredAttributes(schema: Schema, tagName: string): string
|
|
218
|
+
export function getRequiredAttributes(schema: Schema, tagName: string): Array<string> {
|
|
219
219
|
const elementSchema = getElementSchema(schema, tagName);
|
|
220
220
|
if (!elementSchema?.props) {
|
|
221
221
|
return [];
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
const required: string
|
|
224
|
+
const required: Array<string> = [];
|
|
225
225
|
for (const [attrName, attrSchema] of Object.entries(elementSchema.props)) {
|
|
226
226
|
if (attrSchema.required === true) {
|
|
227
227
|
required.push(attrName);
|
|
@@ -233,7 +233,7 @@ export function getRequiredAttributes(schema: Schema, tagName: string): string[]
|
|
|
233
233
|
|
|
234
234
|
type PropertyEnumValue = string | number | boolean;
|
|
235
235
|
|
|
236
|
-
export function getEnumValues(property: PropertySchema):
|
|
236
|
+
export function getEnumValues(property: PropertySchema): ReadonlyArray<PropertyEnumValue> | undefined {
|
|
237
237
|
switch (property.type) {
|
|
238
238
|
case "string":
|
|
239
239
|
return property.enum;
|
package/src/traverse.test.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { traverse } from "./traverse.js";
|
|
3
2
|
import { parse } from "./parse/index.js";
|
|
3
|
+
import { traverse } from "./traverse.js";
|
|
4
4
|
|
|
5
5
|
describe("traverse", () => {
|
|
6
6
|
it("should call JSXElement visitor", () => {
|
|
7
7
|
const ast = parse("<Text>Hello</Text>");
|
|
8
|
-
const visits: string
|
|
8
|
+
const visits: Array<string> = [];
|
|
9
9
|
|
|
10
10
|
traverse(ast, {
|
|
11
11
|
JSXElement() {
|
|
@@ -18,7 +18,7 @@ describe("traverse", () => {
|
|
|
18
18
|
|
|
19
19
|
it("should call JSXFragment visitor", () => {
|
|
20
20
|
const ast = parse("<>Fragment</>");
|
|
21
|
-
const visits: string
|
|
21
|
+
const visits: Array<string> = [];
|
|
22
22
|
|
|
23
23
|
traverse(ast, {
|
|
24
24
|
JSXFragment() {
|
|
@@ -31,7 +31,7 @@ describe("traverse", () => {
|
|
|
31
31
|
|
|
32
32
|
it("should call Identifier visitor for expressions", () => {
|
|
33
33
|
const ast = parse("<Text>{props.value}</Text>");
|
|
34
|
-
const identifiers: string
|
|
34
|
+
const identifiers: Array<string> = [];
|
|
35
35
|
|
|
36
36
|
traverse(ast, {
|
|
37
37
|
Identifier(node) {
|
|
@@ -57,7 +57,7 @@ describe("traverse", () => {
|
|
|
57
57
|
|
|
58
58
|
it("should call JSXExpressionContainer visitor", () => {
|
|
59
59
|
const ast = parse("<Text>{props.value}</Text>");
|
|
60
|
-
const visits: string
|
|
60
|
+
const visits: Array<string> = [];
|
|
61
61
|
|
|
62
62
|
traverse(ast, {
|
|
63
63
|
JSXExpressionContainer() {
|
|
@@ -83,7 +83,7 @@ describe("traverse", () => {
|
|
|
83
83
|
|
|
84
84
|
it("should handle multiple visitor types simultaneously", () => {
|
|
85
85
|
const ast = parse("<Text x={props.value}>Hello</Text>");
|
|
86
|
-
const visits: string
|
|
86
|
+
const visits: Array<string> = [];
|
|
87
87
|
|
|
88
88
|
traverse(ast, {
|
|
89
89
|
JSXElement() {
|
package/src/traverse.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Program } from "acorn";
|
|
2
|
-
import { base, type RecursiveVisitors, type SimpleVisitors, simple } from "acorn-walk";
|
|
1
|
+
import type { Node, Program } from "acorn";
|
|
2
|
+
import { base, type RecursiveVisitors, type SimpleVisitors, simple, type WalkerCallback } from "acorn-walk";
|
|
3
3
|
import {
|
|
4
4
|
isJsxEmptyExpression,
|
|
5
5
|
isJsxText,
|
|
@@ -101,57 +101,56 @@ const tsNodeTypes = [
|
|
|
101
101
|
"TSThisType",
|
|
102
102
|
] as const;
|
|
103
103
|
|
|
104
|
-
const tsWalkers: Record<string, (node:
|
|
104
|
+
const tsWalkers: Record<string, (node: Node, state: unknown, callback: WalkerCallback<unknown>) => void> = {};
|
|
105
105
|
for (const nodeType of tsNodeTypes) {
|
|
106
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
107
106
|
tsWalkers[nodeType] = () => {};
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
const jsxWalkers: ExtendedRecursiveVisitors = {
|
|
111
110
|
JSXElement(node, state, visit) {
|
|
112
|
-
const element = node
|
|
111
|
+
const element = node;
|
|
113
112
|
visit(element.openingElement, state);
|
|
114
113
|
for (const child of element.children) {
|
|
115
114
|
if (isJsxText(child) || isJsxEmptyExpression(child)) {
|
|
116
115
|
continue;
|
|
117
116
|
}
|
|
118
|
-
visit(child
|
|
117
|
+
visit(child, state);
|
|
119
118
|
}
|
|
120
119
|
},
|
|
121
120
|
|
|
122
121
|
JSXFragment(node, state, visit) {
|
|
123
|
-
const fragment = node
|
|
122
|
+
const fragment = node;
|
|
124
123
|
for (const child of fragment.children) {
|
|
125
124
|
if (isJsxText(child) || isJsxEmptyExpression(child)) {
|
|
126
125
|
continue;
|
|
127
126
|
}
|
|
128
|
-
visit(child
|
|
127
|
+
visit(child, state);
|
|
129
128
|
}
|
|
130
129
|
},
|
|
131
130
|
|
|
132
131
|
JSXOpeningElement(node, state, visit) {
|
|
133
|
-
const opening = node
|
|
132
|
+
const opening = node;
|
|
134
133
|
for (const attr of opening.attributes) {
|
|
135
|
-
visit(attr
|
|
134
|
+
visit(attr, state);
|
|
136
135
|
}
|
|
137
136
|
},
|
|
138
137
|
|
|
139
138
|
JSXAttribute(node, state, visit) {
|
|
140
|
-
const attr = node
|
|
139
|
+
const attr = node;
|
|
141
140
|
if (attr.value) {
|
|
142
|
-
visit(attr.value
|
|
141
|
+
visit(attr.value, state);
|
|
143
142
|
}
|
|
144
143
|
},
|
|
145
144
|
|
|
146
145
|
JSXSpreadAttribute(node, state, visit) {
|
|
147
|
-
const spread = node
|
|
148
|
-
visit(spread.argument
|
|
146
|
+
const spread = node;
|
|
147
|
+
visit(spread.argument, state);
|
|
149
148
|
},
|
|
150
149
|
|
|
151
150
|
JSXExpressionContainer(node, state, visit) {
|
|
152
|
-
const container = node
|
|
151
|
+
const container = node;
|
|
153
152
|
if (!isJsxEmptyExpression(container.expression)) {
|
|
154
|
-
visit(container.expression
|
|
153
|
+
visit(container.expression, state);
|
|
155
154
|
}
|
|
156
155
|
},
|
|
157
156
|
};
|
package/src/validate.ts
CHANGED
|
@@ -5,34 +5,31 @@ import type { Schema } from "./schema.js";
|
|
|
5
5
|
export type ValidateResult = { ok: true } | { ok: false; error: AnalysisError | ParseError };
|
|
6
6
|
|
|
7
7
|
export interface ValidateOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Minimum severity level that counts as a failure: 1=info, 2=warning, 3=error.
|
|
10
|
-
*/
|
|
8
|
+
/** Minimum severity level that counts as a failure: 1=info, 2=warning, 3=error. */
|
|
11
9
|
minSeverity?: 1 | 2 | 3;
|
|
12
10
|
}
|
|
13
11
|
/**
|
|
14
12
|
* Validates JSX expression syntax and schema compliance without execution.
|
|
15
13
|
*
|
|
16
|
-
* Performs static analysis to check if JSX source code is valid according to the schema.
|
|
17
|
-
*
|
|
18
|
-
* and data access pattern validation - all without needing actual data or components.
|
|
14
|
+
* Performs static analysis to check if JSX source code is valid according to the schema. This includes syntax validation, security constraints, JSX
|
|
15
|
+
* element/attribute validation, and data access pattern validation - all without needing actual data or components.
|
|
19
16
|
*
|
|
20
17
|
* This function parses the source code and returns a simple ok/error result.
|
|
21
18
|
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const result = validate("<Text>{user.name}</Text>", schema);
|
|
22
|
+
* if (result.ok) {
|
|
23
|
+
* console.log("JSX is valid!");
|
|
24
|
+
* } else {
|
|
25
|
+
* console.error("Validation failed with", result.report.errors.length, "errors");
|
|
26
|
+
* result.report.errors.forEach((err) => console.error(err.message));
|
|
27
|
+
* }
|
|
28
|
+
* ```;
|
|
29
|
+
*
|
|
22
30
|
* @param source - JSX expression source code to validate
|
|
23
31
|
* @param schema - Schema defining allowed data properties, JSX elements, and hierarchy rules
|
|
24
32
|
* @returns Validation result indicating success or failure with parse or analysis error
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```typescript
|
|
28
|
-
* const result = validate("<Text>{user.name}</Text>", schema);
|
|
29
|
-
* if (result.ok) {
|
|
30
|
-
* console.log("JSX is valid!");
|
|
31
|
-
* } else {
|
|
32
|
-
* console.error("Validation failed with", result.report.errors.length, "errors");
|
|
33
|
-
* result.report.errors.forEach(err => console.error(err.message));
|
|
34
|
-
* }
|
|
35
|
-
* ```
|
|
36
33
|
*/
|
|
37
34
|
export function validate(
|
|
38
35
|
source: string,
|
package/CHANGELOG.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
|
-
|
|
5
|
-
## [0.1.39] - 2026-09-05
|
|
6
|
-
|
|
7
|
-
- Wired the package's test suite into the shared workspace test runner.
|
|
8
|
-
- Internal: adopt `verbatimModuleSyntax` and stricter compiler/lint settings. No other functional changes.
|