@10up/block-renderer-validator 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +352 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/pattern-validator.d.ts +19 -0
- package/dist/pattern-validator.d.ts.map +1 -0
- package/dist/pattern-validator.js +47 -0
- package/dist/pattern-validator.js.map +1 -0
- package/dist/schema-validator.d.ts +18 -0
- package/dist/schema-validator.d.ts.map +1 -0
- package/dist/schema-validator.js +76 -0
- package/dist/schema-validator.js.map +1 -0
- package/dist/template-validator.d.ts +61 -0
- package/dist/template-validator.d.ts.map +1 -0
- package/dist/template-validator.js +158 -0
- package/dist/template-validator.js.map +1 -0
- package/dist/token-validator.d.ts +23 -0
- package/dist/token-validator.d.ts.map +1 -0
- package/dist/token-validator.js +192 -0
- package/dist/token-validator.js.map +1 -0
- package/dist/tree-validator.d.ts +26 -0
- package/dist/tree-validator.d.ts.map +1 -0
- package/dist/tree-validator.js +160 -0
- package/dist/tree-validator.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { BlockTemplate, BlockCatalog, ValidationResult } from '@10up/block-renderer-core';
|
|
2
|
+
/**
|
|
3
|
+
* Result of template validation with path information.
|
|
4
|
+
*/
|
|
5
|
+
export interface TemplateValidationResult extends ValidationResult {
|
|
6
|
+
/** Paths to elements with errors (e.g., "[0][2][1]") */
|
|
7
|
+
paths?: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validates the structural format of a block template using Zod.
|
|
11
|
+
*
|
|
12
|
+
* @param template - The template to validate
|
|
13
|
+
* @returns Validation result with structural errors
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateTemplateStructure(template: unknown): TemplateValidationResult;
|
|
16
|
+
/**
|
|
17
|
+
* Validates that all block types in the template exist in the catalog.
|
|
18
|
+
*
|
|
19
|
+
* @param template - The template to validate
|
|
20
|
+
* @param catalog - The block catalog to check against
|
|
21
|
+
* @returns Validation result with unknown block warnings
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateTemplateBlockTypes(template: BlockTemplate, catalog: BlockCatalog): TemplateValidationResult;
|
|
24
|
+
/**
|
|
25
|
+
* Validates nesting constraints for a block template.
|
|
26
|
+
*
|
|
27
|
+
* Checks:
|
|
28
|
+
* - `parent` constraints: block must be direct child of specified parent types
|
|
29
|
+
* - `ancestor` constraints: block must have specified ancestor somewhere above
|
|
30
|
+
* - `allowedBlocks` constraints: parent can only contain specified child types
|
|
31
|
+
*
|
|
32
|
+
* @param template - The template to validate
|
|
33
|
+
* @param catalog - The block catalog with nesting rules
|
|
34
|
+
* @returns Validation result with nesting errors
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateTemplateNesting(template: BlockTemplate, catalog: BlockCatalog): TemplateValidationResult;
|
|
37
|
+
/**
|
|
38
|
+
* Options for template validation.
|
|
39
|
+
*/
|
|
40
|
+
export interface ValidateTemplateOptions {
|
|
41
|
+
/** Block catalog for block type and nesting validation */
|
|
42
|
+
catalog?: BlockCatalog;
|
|
43
|
+
/** Whether to validate block types exist (default: true if catalog provided) */
|
|
44
|
+
validateBlockTypes?: boolean;
|
|
45
|
+
/** Whether to validate nesting rules (default: true if catalog provided) */
|
|
46
|
+
validateNesting?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Full validation of a block template.
|
|
50
|
+
*
|
|
51
|
+
* Performs:
|
|
52
|
+
* 1. Structural validation (always)
|
|
53
|
+
* 2. Block type validation (if catalog provided)
|
|
54
|
+
* 3. Nesting constraint validation (if catalog provided)
|
|
55
|
+
*
|
|
56
|
+
* @param template - The template to validate
|
|
57
|
+
* @param options - Validation options
|
|
58
|
+
* @returns Combined validation result
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateTemplate(template: unknown, options?: ValidateTemplateOptions): TemplateValidationResult;
|
|
61
|
+
//# sourceMappingURL=template-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-validator.d.ts","sourceRoot":"","sources":["../src/template-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,YAAY,EACZ,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AAGnC;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAChB,wBAAwB,CAa1B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,YAAY,GACpB,wBAAwB,CA2B1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,YAAY,GACpB,wBAAwB,CAiF1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,uBAA4B,GACpC,wBAAwB,CA8B1B"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { blockTemplateSchema } from '@10up/block-renderer-core';
|
|
2
|
+
/**
|
|
3
|
+
* Validates the structural format of a block template using Zod.
|
|
4
|
+
*
|
|
5
|
+
* @param template - The template to validate
|
|
6
|
+
* @returns Validation result with structural errors
|
|
7
|
+
*/
|
|
8
|
+
export function validateTemplateStructure(template) {
|
|
9
|
+
const result = blockTemplateSchema.safeParse(template);
|
|
10
|
+
if (result.success) {
|
|
11
|
+
return { valid: true, errors: [] };
|
|
12
|
+
}
|
|
13
|
+
const errors = result.error.issues.map((issue) => {
|
|
14
|
+
const path = issue.path.join('.');
|
|
15
|
+
return `Invalid template structure at ${path || 'root'}: ${issue.message}`;
|
|
16
|
+
});
|
|
17
|
+
return { valid: false, errors };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validates that all block types in the template exist in the catalog.
|
|
21
|
+
*
|
|
22
|
+
* @param template - The template to validate
|
|
23
|
+
* @param catalog - The block catalog to check against
|
|
24
|
+
* @returns Validation result with unknown block warnings
|
|
25
|
+
*/
|
|
26
|
+
export function validateTemplateBlockTypes(template, catalog) {
|
|
27
|
+
const errors = [];
|
|
28
|
+
const warnings = [];
|
|
29
|
+
function validateEntry(entry, path) {
|
|
30
|
+
const [blockType, , innerBlocks] = entry;
|
|
31
|
+
if (!catalog.has(blockType)) {
|
|
32
|
+
warnings.push(`${path}: Unknown block type "${blockType}"`);
|
|
33
|
+
}
|
|
34
|
+
if (innerBlocks) {
|
|
35
|
+
innerBlocks.forEach((child, i) => {
|
|
36
|
+
validateEntry(child, `${path}[2][${i}]`);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
template.forEach((entry, i) => {
|
|
41
|
+
validateEntry(entry, `[${i}]`);
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
valid: errors.length === 0,
|
|
45
|
+
errors,
|
|
46
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validates nesting constraints for a block template.
|
|
51
|
+
*
|
|
52
|
+
* Checks:
|
|
53
|
+
* - `parent` constraints: block must be direct child of specified parent types
|
|
54
|
+
* - `ancestor` constraints: block must have specified ancestor somewhere above
|
|
55
|
+
* - `allowedBlocks` constraints: parent can only contain specified child types
|
|
56
|
+
*
|
|
57
|
+
* @param template - The template to validate
|
|
58
|
+
* @param catalog - The block catalog with nesting rules
|
|
59
|
+
* @returns Validation result with nesting errors
|
|
60
|
+
*/
|
|
61
|
+
export function validateTemplateNesting(template, catalog) {
|
|
62
|
+
const errors = [];
|
|
63
|
+
function validateEntry(entry, path, parentType, ancestorTypes) {
|
|
64
|
+
const [blockType, , innerBlocks] = entry;
|
|
65
|
+
const definition = catalog.get(blockType);
|
|
66
|
+
if (!definition) {
|
|
67
|
+
// Skip validation for unknown blocks but still recurse into children
|
|
68
|
+
if (innerBlocks) {
|
|
69
|
+
const newAncestors = [...ancestorTypes, blockType];
|
|
70
|
+
innerBlocks.forEach((child, i) => {
|
|
71
|
+
validateEntry(child, `${path}[2][${i}]`, blockType, newAncestors);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Validate parent constraint
|
|
77
|
+
if (definition.parent && definition.parent.length > 0) {
|
|
78
|
+
if (!parentType) {
|
|
79
|
+
errors.push(`${path}: Block "${blockType}" must be inside one of: ${definition.parent.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
else if (!definition.parent.includes(parentType)) {
|
|
82
|
+
errors.push(`${path}: Block "${blockType}" cannot be a direct child of "${parentType}". ` +
|
|
83
|
+
`Allowed parents: ${definition.parent.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Validate ancestor constraint
|
|
87
|
+
if (definition.ancestor && definition.ancestor.length > 0) {
|
|
88
|
+
const hasRequiredAncestor = definition.ancestor.some((required) => ancestorTypes.includes(required));
|
|
89
|
+
if (!hasRequiredAncestor && parentType) {
|
|
90
|
+
errors.push(`${path}: Block "${blockType}" must be nested inside one of: ${definition.ancestor.join(', ')}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Validate children constraints (allowedBlocks)
|
|
94
|
+
if (innerBlocks && innerBlocks.length > 0) {
|
|
95
|
+
if (definition.allowedBlocks !== true &&
|
|
96
|
+
Array.isArray(definition.allowedBlocks)) {
|
|
97
|
+
for (let i = 0; i < innerBlocks.length; i++) {
|
|
98
|
+
const childType = innerBlocks[i][0];
|
|
99
|
+
if (!definition.allowedBlocks.includes(childType)) {
|
|
100
|
+
errors.push(`${path}[2][${i}]: Block "${blockType}" cannot contain "${childType}". ` +
|
|
101
|
+
`Allowed children: ${definition.allowedBlocks.join(', ')}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Recurse into inner blocks
|
|
107
|
+
if (innerBlocks) {
|
|
108
|
+
const newAncestors = [...ancestorTypes, blockType];
|
|
109
|
+
innerBlocks.forEach((child, i) => {
|
|
110
|
+
validateEntry(child, `${path}[2][${i}]`, blockType, newAncestors);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
template.forEach((entry, i) => {
|
|
115
|
+
validateEntry(entry, `[${i}]`, null, []);
|
|
116
|
+
});
|
|
117
|
+
return { valid: errors.length === 0, errors };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Full validation of a block template.
|
|
121
|
+
*
|
|
122
|
+
* Performs:
|
|
123
|
+
* 1. Structural validation (always)
|
|
124
|
+
* 2. Block type validation (if catalog provided)
|
|
125
|
+
* 3. Nesting constraint validation (if catalog provided)
|
|
126
|
+
*
|
|
127
|
+
* @param template - The template to validate
|
|
128
|
+
* @param options - Validation options
|
|
129
|
+
* @returns Combined validation result
|
|
130
|
+
*/
|
|
131
|
+
export function validateTemplate(template, options = {}) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
const warnings = [];
|
|
134
|
+
// 1. Structural validation
|
|
135
|
+
const structureResult = validateTemplateStructure(template);
|
|
136
|
+
if (!structureResult.valid) {
|
|
137
|
+
return structureResult;
|
|
138
|
+
}
|
|
139
|
+
const validTemplate = template;
|
|
140
|
+
// 2. Block type validation (if catalog provided)
|
|
141
|
+
if (options.catalog && options.validateBlockTypes !== false) {
|
|
142
|
+
const typeResult = validateTemplateBlockTypes(validTemplate, options.catalog);
|
|
143
|
+
errors.push(...typeResult.errors);
|
|
144
|
+
if (typeResult.warnings)
|
|
145
|
+
warnings.push(...typeResult.warnings);
|
|
146
|
+
}
|
|
147
|
+
// 3. Nesting validation (if catalog provided)
|
|
148
|
+
if (options.catalog && options.validateNesting !== false) {
|
|
149
|
+
const nestingResult = validateTemplateNesting(validTemplate, options.catalog);
|
|
150
|
+
errors.push(...nestingResult.errors);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
valid: errors.length === 0,
|
|
154
|
+
errors,
|
|
155
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=template-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-validator.js","sourceRoot":"","sources":["../src/template-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BlockTree, BlockElement, ValidationResult } from '@10up/block-renderer-core';
|
|
2
|
+
import type { ThemeTokens } from '@10up/block-renderer-theme-json';
|
|
3
|
+
/**
|
|
4
|
+
* Validates color tokens used in a block element.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateColorTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
|
|
7
|
+
/**
|
|
8
|
+
* Validates typography tokens used in a block element.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateTypographyTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
|
|
11
|
+
/**
|
|
12
|
+
* Validates spacing tokens used in a block element.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateSpacingTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
|
|
15
|
+
/**
|
|
16
|
+
* Validates all theme tokens used in a block element.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateElementTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
|
|
19
|
+
/**
|
|
20
|
+
* Validates all theme tokens used in a block tree.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateTreeTokens(tree: BlockTree, tokens: ThemeTokens): ValidationResult;
|
|
23
|
+
//# sourceMappingURL=token-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-validator.d.ts","sourceRoot":"","sources":["../src/token-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AA0EnE;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAiDlB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CA0BlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAwBlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAmBlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAqBlB"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { validateSlug, parseCssVarReference } from '@10up/block-renderer-theme-json';
|
|
2
|
+
/**
|
|
3
|
+
* Color-related attribute names that should use theme tokens.
|
|
4
|
+
*/
|
|
5
|
+
const COLOR_ATTRIBUTES = [
|
|
6
|
+
'backgroundColor',
|
|
7
|
+
'textColor',
|
|
8
|
+
'borderColor',
|
|
9
|
+
'overlayColor',
|
|
10
|
+
'customBackgroundColor',
|
|
11
|
+
'customTextColor',
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Spacing-related style paths that should use theme tokens.
|
|
15
|
+
*/
|
|
16
|
+
const SPACING_STYLE_PATHS = [
|
|
17
|
+
'style.spacing.margin',
|
|
18
|
+
'style.spacing.padding',
|
|
19
|
+
'style.spacing.blockGap',
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a value is a direct token slug (not a CSS var reference).
|
|
23
|
+
*/
|
|
24
|
+
function isDirectSlug(value) {
|
|
25
|
+
if (typeof value !== 'string')
|
|
26
|
+
return false;
|
|
27
|
+
// Direct slugs don't start with var: or have CSS variable syntax
|
|
28
|
+
return !value.startsWith('var:') && !value.startsWith('var(') && !value.startsWith('#');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Gets a nested value from an object using a dot-separated path.
|
|
32
|
+
*/
|
|
33
|
+
function getNestedValue(obj, path) {
|
|
34
|
+
const parts = path.split('.');
|
|
35
|
+
let current = obj;
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
current = current[part];
|
|
41
|
+
}
|
|
42
|
+
return current;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extracts spacing values from a sides object or string.
|
|
46
|
+
*/
|
|
47
|
+
function extractSpacingValues(value) {
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
return [value];
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === 'object' && value !== null) {
|
|
52
|
+
const sides = value;
|
|
53
|
+
const values = [];
|
|
54
|
+
for (const side of ['top', 'right', 'bottom', 'left', 'horizontal', 'vertical']) {
|
|
55
|
+
if (typeof sides[side] === 'string') {
|
|
56
|
+
values.push(sides[side]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return values;
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validates color tokens used in a block element.
|
|
65
|
+
*/
|
|
66
|
+
export function validateColorTokens(element, tokens) {
|
|
67
|
+
const errors = [];
|
|
68
|
+
const warnings = [];
|
|
69
|
+
// Check direct color attributes
|
|
70
|
+
for (const attr of COLOR_ATTRIBUTES) {
|
|
71
|
+
const value = element.props[attr];
|
|
72
|
+
if (isDirectSlug(value)) {
|
|
73
|
+
if (!validateSlug(tokens, 'color', value)) {
|
|
74
|
+
errors.push(`[${element.key}] Unknown color token "${value}" in ${attr}. ` +
|
|
75
|
+
`Available: ${tokens.colors.palette.map(c => c.slug).join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Check gradient attribute
|
|
80
|
+
const gradient = element.props.gradient;
|
|
81
|
+
if (isDirectSlug(gradient)) {
|
|
82
|
+
if (!validateSlug(tokens, 'gradient', gradient)) {
|
|
83
|
+
errors.push(`[${element.key}] Unknown gradient token "${gradient}". ` +
|
|
84
|
+
`Available: ${tokens.colors.gradients.map(g => g.slug).join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Check style.color values
|
|
88
|
+
const styleColor = getNestedValue(element.props, 'style.color');
|
|
89
|
+
if (styleColor) {
|
|
90
|
+
for (const [key, value] of Object.entries(styleColor)) {
|
|
91
|
+
if (typeof value === 'string' && value.startsWith('var:preset|color|')) {
|
|
92
|
+
const parsed = parseCssVarReference(value);
|
|
93
|
+
if (parsed && !validateSlug(tokens, parsed.type, parsed.slug)) {
|
|
94
|
+
errors.push(`[${element.key}] Unknown color token "${parsed.slug}" in style.color.${key}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
valid: errors.length === 0,
|
|
101
|
+
errors,
|
|
102
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validates typography tokens used in a block element.
|
|
107
|
+
*/
|
|
108
|
+
export function validateTypographyTokens(element, tokens) {
|
|
109
|
+
const errors = [];
|
|
110
|
+
// Check fontSize
|
|
111
|
+
const fontSize = element.props.fontSize;
|
|
112
|
+
if (isDirectSlug(fontSize)) {
|
|
113
|
+
if (!validateSlug(tokens, 'font-size', fontSize)) {
|
|
114
|
+
errors.push(`[${element.key}] Unknown font size token "${fontSize}". ` +
|
|
115
|
+
`Available: ${tokens.typography.fontSizes.map(f => f.slug).join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Check fontFamily
|
|
119
|
+
const fontFamily = element.props.fontFamily;
|
|
120
|
+
if (isDirectSlug(fontFamily)) {
|
|
121
|
+
if (!validateSlug(tokens, 'font-family', fontFamily)) {
|
|
122
|
+
errors.push(`[${element.key}] Unknown font family token "${fontFamily}". ` +
|
|
123
|
+
`Available: ${tokens.typography.fontFamilies.map(f => f.slug).join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { valid: errors.length === 0, errors };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Validates spacing tokens used in a block element.
|
|
130
|
+
*/
|
|
131
|
+
export function validateSpacingTokens(element, tokens) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
for (const path of SPACING_STYLE_PATHS) {
|
|
134
|
+
const value = getNestedValue(element.props, path);
|
|
135
|
+
if (value === undefined)
|
|
136
|
+
continue;
|
|
137
|
+
const spacingValues = extractSpacingValues(value);
|
|
138
|
+
for (const spacing of spacingValues) {
|
|
139
|
+
// Check if it's a CSS var reference
|
|
140
|
+
if (spacing.startsWith('var:preset|spacing|')) {
|
|
141
|
+
const parsed = parseCssVarReference(spacing);
|
|
142
|
+
if (parsed && !validateSlug(tokens, 'spacing', parsed.slug)) {
|
|
143
|
+
errors.push(`[${element.key}] Unknown spacing token "${parsed.slug}" in ${path}. ` +
|
|
144
|
+
`Available: ${tokens.spacing.spacingSizes.map(s => s.slug).join(', ')}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { valid: errors.length === 0, errors };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Validates all theme tokens used in a block element.
|
|
153
|
+
*/
|
|
154
|
+
export function validateElementTokens(element, tokens) {
|
|
155
|
+
const errors = [];
|
|
156
|
+
const warnings = [];
|
|
157
|
+
const colorResult = validateColorTokens(element, tokens);
|
|
158
|
+
errors.push(...colorResult.errors);
|
|
159
|
+
if (colorResult.warnings)
|
|
160
|
+
warnings.push(...colorResult.warnings);
|
|
161
|
+
const typographyResult = validateTypographyTokens(element, tokens);
|
|
162
|
+
errors.push(...typographyResult.errors);
|
|
163
|
+
const spacingResult = validateSpacingTokens(element, tokens);
|
|
164
|
+
errors.push(...spacingResult.errors);
|
|
165
|
+
return {
|
|
166
|
+
valid: errors.length === 0,
|
|
167
|
+
errors,
|
|
168
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Validates all theme tokens used in a block tree.
|
|
173
|
+
*/
|
|
174
|
+
export function validateTreeTokens(tree, tokens) {
|
|
175
|
+
const errors = [];
|
|
176
|
+
const warnings = [];
|
|
177
|
+
for (const element of Object.values(tree.elements)) {
|
|
178
|
+
const result = validateElementTokens(element, tokens);
|
|
179
|
+
if (!result.valid) {
|
|
180
|
+
errors.push(...result.errors);
|
|
181
|
+
}
|
|
182
|
+
if (result.warnings) {
|
|
183
|
+
warnings.push(...result.warnings);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: errors.length === 0,
|
|
188
|
+
errors,
|
|
189
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=token-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-validator.js","sourceRoot":"","sources":["../src/token-validator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAErF;;GAEG;AACH,MAAM,gBAAgB,GAAG;IACvB,iBAAiB;IACjB,WAAW;IACX,aAAa;IACb,cAAc;IACd,uBAAuB;IACvB,iBAAiB;CAClB,CAAC;AAEF;;GAEG;AACH,MAAM,mBAAmB,GAAG;IAC1B,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;CACzB,CAAC;AAEF;;GAEG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,iEAAiE;IACjE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAA4B,EAAE,IAAY;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,OAAO,GAAY,GAAG,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,KAAgC,CAAC;QAC/C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,CAAC;YAChF,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAW,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAqB,EACrB,MAAmB;IAEnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,gCAAgC;IAChC,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,0BAA0B,KAAK,QAAQ,IAAI,IAAI;oBAC5D,cAAc,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;IACxC,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,6BAA6B,QAAQ,KAAK;gBACvD,cAAc,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,CAAwC,CAAC;IACvG,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACvE,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;gBAC3C,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC9D,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,0BAA0B,MAAM,CAAC,IAAI,oBAAoB,GAAG,EAAE,CAC9E,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAqB,EACrB,MAAmB;IAEnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,iBAAiB;IACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;IACxC,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,8BAA8B,QAAQ,KAAK;gBACxD,cAAc,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;IAC5C,IAAI,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,gCAAgC,UAAU,KAAK;gBAC5D,cAAc,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAqB,EACrB,MAAmB;IAEnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAElC,MAAM,aAAa,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAElD,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;YACpC,oCAAoC;YACpC,IAAI,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5D,MAAM,CAAC,IAAI,CACT,IAAI,OAAO,CAAC,GAAG,4BAA4B,MAAM,CAAC,IAAI,QAAQ,IAAI,IAAI;wBACpE,cAAc,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1E,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAqB,EACrB,MAAmB;IAEnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,MAAM,WAAW,GAAG,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,WAAW,CAAC,QAAQ;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEjE,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAErC,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAe,EACf,MAAmB;IAEnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEtD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BlockTree, BlockElement, BlockDefinition, BlockCatalog, ValidationResult } from '@10up/block-renderer-core';
|
|
2
|
+
/**
|
|
3
|
+
* Validates parent constraints for a block.
|
|
4
|
+
* Checks if the block's direct parent is in its allowed `parent` list.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateParent(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
|
|
7
|
+
/**
|
|
8
|
+
* Validates ancestor constraints for a block.
|
|
9
|
+
* Checks if any required ancestors exist somewhere in the tree path.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateAncestor(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
|
|
12
|
+
/**
|
|
13
|
+
* Validates children constraints for a block.
|
|
14
|
+
* Checks if all children are in the block's `allowedBlocks` list.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateChildren(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
|
|
17
|
+
/**
|
|
18
|
+
* Validates the `multiple` constraint.
|
|
19
|
+
* Checks if blocks with `multiple: false` appear only once.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateMultiple(tree: BlockTree, catalog: BlockCatalog): ValidationResult;
|
|
22
|
+
/**
|
|
23
|
+
* Validates all nesting rules for a tree.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateTreeNesting(tree: BlockTree, catalog: BlockCatalog): ValidationResult;
|
|
26
|
+
//# sourceMappingURL=tree-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-validator.d.ts","sourceRoot":"","sources":["../src/tree-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AA6CnC;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CA4BlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CAwBlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CAyBlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,GACpB,gBAAgB,CAuBlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,GACpB,gBAAgB,CA2ClB"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets the parent element of a block in the tree.
|
|
3
|
+
*/
|
|
4
|
+
function getParentElement(element, tree) {
|
|
5
|
+
if (!element.parentKey)
|
|
6
|
+
return null;
|
|
7
|
+
return tree.elements[element.parentKey] || null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Gets all ancestor elements of a block (from immediate parent to root).
|
|
11
|
+
*/
|
|
12
|
+
function getAncestors(element, tree) {
|
|
13
|
+
const ancestors = [];
|
|
14
|
+
let current = getParentElement(element, tree);
|
|
15
|
+
while (current) {
|
|
16
|
+
ancestors.push(current);
|
|
17
|
+
current = getParentElement(current, tree);
|
|
18
|
+
}
|
|
19
|
+
return ancestors;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets the children elements of a block.
|
|
23
|
+
*/
|
|
24
|
+
function getChildren(element, tree) {
|
|
25
|
+
if (!element.children)
|
|
26
|
+
return [];
|
|
27
|
+
return element.children
|
|
28
|
+
.map(key => tree.elements[key])
|
|
29
|
+
.filter((child) => child !== undefined);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validates parent constraints for a block.
|
|
33
|
+
* Checks if the block's direct parent is in its allowed `parent` list.
|
|
34
|
+
*/
|
|
35
|
+
export function validateParent(element, tree, definition) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
// If no parent constraint, all parents are valid
|
|
38
|
+
if (!definition.parent || definition.parent.length === 0) {
|
|
39
|
+
return { valid: true, errors: [] };
|
|
40
|
+
}
|
|
41
|
+
const parent = getParentElement(element, tree);
|
|
42
|
+
// If block requires a parent but has none
|
|
43
|
+
if (!parent) {
|
|
44
|
+
errors.push(`[${element.key}] Block "${element.type}" must be inside one of: ${definition.parent.join(', ')}`);
|
|
45
|
+
return { valid: false, errors };
|
|
46
|
+
}
|
|
47
|
+
// Check if parent type is in allowed list
|
|
48
|
+
if (!definition.parent.includes(parent.type)) {
|
|
49
|
+
errors.push(`[${element.key}] Block "${element.type}" cannot be a direct child of "${parent.type}". ` +
|
|
50
|
+
`Allowed parents: ${definition.parent.join(', ')}`);
|
|
51
|
+
return { valid: false, errors };
|
|
52
|
+
}
|
|
53
|
+
return { valid: true, errors: [] };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validates ancestor constraints for a block.
|
|
57
|
+
* Checks if any required ancestors exist somewhere in the tree path.
|
|
58
|
+
*/
|
|
59
|
+
export function validateAncestor(element, tree, definition) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
// If no ancestor constraint, all ancestors are valid
|
|
62
|
+
if (!definition.ancestor || definition.ancestor.length === 0) {
|
|
63
|
+
return { valid: true, errors: [] };
|
|
64
|
+
}
|
|
65
|
+
const ancestors = getAncestors(element, tree);
|
|
66
|
+
const ancestorTypes = ancestors.map(a => a.type);
|
|
67
|
+
// Check if at least one required ancestor exists
|
|
68
|
+
const hasRequiredAncestor = definition.ancestor.some(requiredType => ancestorTypes.includes(requiredType));
|
|
69
|
+
if (!hasRequiredAncestor) {
|
|
70
|
+
errors.push(`[${element.key}] Block "${element.type}" must be nested inside one of: ${definition.ancestor.join(', ')}`);
|
|
71
|
+
return { valid: false, errors };
|
|
72
|
+
}
|
|
73
|
+
return { valid: true, errors: [] };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validates children constraints for a block.
|
|
77
|
+
* Checks if all children are in the block's `allowedBlocks` list.
|
|
78
|
+
*/
|
|
79
|
+
export function validateChildren(element, tree, definition) {
|
|
80
|
+
const errors = [];
|
|
81
|
+
// If allowedBlocks is true, any block is allowed
|
|
82
|
+
if (definition.allowedBlocks === true) {
|
|
83
|
+
return { valid: true, errors: [] };
|
|
84
|
+
}
|
|
85
|
+
// If no allowedBlocks constraint, all children are valid
|
|
86
|
+
if (!definition.allowedBlocks || !Array.isArray(definition.allowedBlocks)) {
|
|
87
|
+
return { valid: true, errors: [] };
|
|
88
|
+
}
|
|
89
|
+
const children = getChildren(element, tree);
|
|
90
|
+
for (const child of children) {
|
|
91
|
+
if (!definition.allowedBlocks.includes(child.type)) {
|
|
92
|
+
errors.push(`[${element.key}] Block "${element.type}" cannot contain "${child.type}". ` +
|
|
93
|
+
`Allowed children: ${definition.allowedBlocks.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { valid: errors.length === 0, errors };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validates the `multiple` constraint.
|
|
100
|
+
* Checks if blocks with `multiple: false` appear only once.
|
|
101
|
+
*/
|
|
102
|
+
export function validateMultiple(tree, catalog) {
|
|
103
|
+
const errors = [];
|
|
104
|
+
const blockCounts = new Map();
|
|
105
|
+
// Count occurrences of each block type
|
|
106
|
+
for (const element of Object.values(tree.elements)) {
|
|
107
|
+
const existing = blockCounts.get(element.type) || [];
|
|
108
|
+
existing.push(element.key);
|
|
109
|
+
blockCounts.set(element.type, existing);
|
|
110
|
+
}
|
|
111
|
+
// Check blocks with multiple: false
|
|
112
|
+
for (const [blockType, keys] of blockCounts.entries()) {
|
|
113
|
+
const definition = catalog.get(blockType);
|
|
114
|
+
if (definition?.multiple === false && keys.length > 1) {
|
|
115
|
+
errors.push(`Block "${blockType}" can only appear once, but found ${keys.length} instances: ${keys.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { valid: errors.length === 0, errors };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validates all nesting rules for a tree.
|
|
122
|
+
*/
|
|
123
|
+
export function validateTreeNesting(tree, catalog) {
|
|
124
|
+
const errors = [];
|
|
125
|
+
const warnings = [];
|
|
126
|
+
// Validate each element's nesting
|
|
127
|
+
for (const element of Object.values(tree.elements)) {
|
|
128
|
+
const definition = catalog.get(element.type);
|
|
129
|
+
if (!definition) {
|
|
130
|
+
// Unknown block type - skip nesting validation
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
// Validate parent constraint
|
|
134
|
+
const parentResult = validateParent(element, tree, definition);
|
|
135
|
+
if (!parentResult.valid) {
|
|
136
|
+
errors.push(...parentResult.errors);
|
|
137
|
+
}
|
|
138
|
+
// Validate ancestor constraint
|
|
139
|
+
const ancestorResult = validateAncestor(element, tree, definition);
|
|
140
|
+
if (!ancestorResult.valid) {
|
|
141
|
+
errors.push(...ancestorResult.errors);
|
|
142
|
+
}
|
|
143
|
+
// Validate children constraint
|
|
144
|
+
const childrenResult = validateChildren(element, tree, definition);
|
|
145
|
+
if (!childrenResult.valid) {
|
|
146
|
+
errors.push(...childrenResult.errors);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Validate multiple constraints
|
|
150
|
+
const multipleResult = validateMultiple(tree, catalog);
|
|
151
|
+
if (!multipleResult.valid) {
|
|
152
|
+
errors.push(...multipleResult.errors);
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
valid: errors.length === 0,
|
|
156
|
+
errors,
|
|
157
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=tree-validator.js.map
|