@spwig/theme-validator 1.0.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/README.md +367 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/component_manifest_schema.json +221 -0
- package/dist/schemas/theme_manifest_schema.json +267 -0
- package/dist/types/manifest.d.ts +183 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/manifest.js +5 -0
- package/dist/types/manifest.js.map +1 -0
- package/dist/types/validation-result.d.ts +48 -0
- package/dist/types/validation-result.d.ts.map +1 -0
- package/dist/types/validation-result.js +26 -0
- package/dist/types/validation-result.js.map +1 -0
- package/dist/validators/component-validator.d.ts +50 -0
- package/dist/validators/component-validator.d.ts.map +1 -0
- package/dist/validators/component-validator.js +235 -0
- package/dist/validators/component-validator.js.map +1 -0
- package/dist/validators/design-tokens-validator.d.ts +46 -0
- package/dist/validators/design-tokens-validator.d.ts.map +1 -0
- package/dist/validators/design-tokens-validator.js +202 -0
- package/dist/validators/design-tokens-validator.js.map +1 -0
- package/dist/validators/manifest-validator.d.ts +69 -0
- package/dist/validators/manifest-validator.d.ts.map +1 -0
- package/dist/validators/manifest-validator.js +206 -0
- package/dist/validators/manifest-validator.js.map +1 -0
- package/dist/validators/template-validator.d.ts +34 -0
- package/dist/validators/template-validator.d.ts.map +1 -0
- package/dist/validators/template-validator.js +170 -0
- package/dist/validators/template-validator.js.map +1 -0
- package/dist/validators/theme-validator.d.ts +44 -0
- package/dist/validators/theme-validator.d.ts.map +1 -0
- package/dist/validators/theme-validator.js +237 -0
- package/dist/validators/theme-validator.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest Validator
|
|
3
|
+
* Validates JSON manifests against schemas using Ajv
|
|
4
|
+
*/
|
|
5
|
+
import Ajv from 'ajv';
|
|
6
|
+
import addErrors from 'ajv-errors';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { createError, } from '../types/validation-result.js';
|
|
10
|
+
export class ManifestValidator {
|
|
11
|
+
ajv;
|
|
12
|
+
errors = [];
|
|
13
|
+
warnings = [];
|
|
14
|
+
constructor() {
|
|
15
|
+
this.ajv = new Ajv({
|
|
16
|
+
allErrors: true,
|
|
17
|
+
verbose: true,
|
|
18
|
+
strict: false,
|
|
19
|
+
});
|
|
20
|
+
addErrors(this.ajv);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load a JSON schema from file
|
|
24
|
+
*/
|
|
25
|
+
async loadSchema(schemaPath) {
|
|
26
|
+
try {
|
|
27
|
+
const schemaContent = await fs.readFile(schemaPath, 'utf-8');
|
|
28
|
+
return JSON.parse(schemaContent);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error instanceof Error) {
|
|
32
|
+
throw new Error(`Failed to load schema from ${schemaPath}: ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load a JSON file
|
|
39
|
+
*/
|
|
40
|
+
async loadJSON(filePath) {
|
|
41
|
+
try {
|
|
42
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
43
|
+
return JSON.parse(content);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof SyntaxError) {
|
|
47
|
+
this.errors.push(createError('json_parse_error', `Invalid JSON in ${path.basename(filePath)}: ${error.message}`, {
|
|
48
|
+
path: filePath,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
else if (error instanceof Error) {
|
|
52
|
+
this.errors.push(createError('file_error', `Failed to read ${path.basename(filePath)}: ${error.message}`, {
|
|
53
|
+
path: filePath,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validate manifest against schema
|
|
61
|
+
*/
|
|
62
|
+
validateAgainstSchema(manifest, schema, manifestType = 'manifest') {
|
|
63
|
+
const validate = this.ajv.compile(schema);
|
|
64
|
+
const valid = validate(manifest);
|
|
65
|
+
if (!valid && validate.errors) {
|
|
66
|
+
for (const error of validate.errors) {
|
|
67
|
+
this.errors.push(this.convertAjvError(error, manifestType));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return valid;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Convert Ajv error to ValidationError
|
|
74
|
+
*/
|
|
75
|
+
convertAjvError(error, manifestType) {
|
|
76
|
+
const dataPath = error.instancePath || error.schemaPath;
|
|
77
|
+
let message = error.message || 'Validation error';
|
|
78
|
+
// Make error message more readable
|
|
79
|
+
if (error.keyword === 'required') {
|
|
80
|
+
const missingProp = error.params.missingProperty;
|
|
81
|
+
message = `Missing required property: ${missingProp}`;
|
|
82
|
+
}
|
|
83
|
+
else if (error.keyword === 'pattern') {
|
|
84
|
+
message = `${dataPath} ${message}`;
|
|
85
|
+
}
|
|
86
|
+
else if (error.keyword === 'enum') {
|
|
87
|
+
const allowedValues = error.params.allowedValues;
|
|
88
|
+
message = `${dataPath} must be one of: ${allowedValues.join(', ')}`;
|
|
89
|
+
}
|
|
90
|
+
else if (dataPath) {
|
|
91
|
+
message = `${dataPath}: ${message}`;
|
|
92
|
+
}
|
|
93
|
+
return createError('schema_validation', message, {
|
|
94
|
+
path: dataPath,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Reset errors and warnings
|
|
99
|
+
*/
|
|
100
|
+
reset() {
|
|
101
|
+
this.errors = [];
|
|
102
|
+
this.warnings = [];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get current errors
|
|
106
|
+
*/
|
|
107
|
+
getErrors() {
|
|
108
|
+
return this.errors;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get current warnings
|
|
112
|
+
*/
|
|
113
|
+
getWarnings() {
|
|
114
|
+
return this.warnings;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Add an error
|
|
118
|
+
*/
|
|
119
|
+
addError(error) {
|
|
120
|
+
this.errors.push(error);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Add a warning
|
|
124
|
+
*/
|
|
125
|
+
addWarning(warning) {
|
|
126
|
+
this.warnings.push(warning);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if file exists
|
|
130
|
+
*/
|
|
131
|
+
async fileExists(filePath) {
|
|
132
|
+
try {
|
|
133
|
+
const stats = await fs.stat(filePath);
|
|
134
|
+
return stats.isFile();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if directory exists
|
|
142
|
+
*/
|
|
143
|
+
async directoryExists(dirPath) {
|
|
144
|
+
try {
|
|
145
|
+
const stats = await fs.stat(dirPath);
|
|
146
|
+
return stats.isDirectory();
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Compare semantic versions
|
|
154
|
+
* @returns -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
155
|
+
*/
|
|
156
|
+
compareVersions(v1, v2) {
|
|
157
|
+
const parts1 = v1.split('.').map(Number);
|
|
158
|
+
const parts2 = v2.split('.').map(Number);
|
|
159
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
160
|
+
const p1 = parts1[i] || 0;
|
|
161
|
+
const p2 = parts2[i] || 0;
|
|
162
|
+
if (p1 < p2)
|
|
163
|
+
return -1;
|
|
164
|
+
if (p1 > p2)
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Validate file is not empty
|
|
171
|
+
*/
|
|
172
|
+
async validateFileNotEmpty(filePath, fileType) {
|
|
173
|
+
try {
|
|
174
|
+
const stats = await fs.stat(filePath);
|
|
175
|
+
if (stats.size === 0) {
|
|
176
|
+
this.addError(createError('empty_file', `${fileType} file is empty`, {
|
|
177
|
+
path: filePath,
|
|
178
|
+
}));
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
this.addError(createError('file_error', `Failed to check ${fileType} file size`, {
|
|
185
|
+
path: filePath,
|
|
186
|
+
}));
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Validate file is valid UTF-8
|
|
192
|
+
*/
|
|
193
|
+
async validateUTF8(filePath, fileType) {
|
|
194
|
+
try {
|
|
195
|
+
await fs.readFile(filePath, 'utf-8');
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
this.addError(createError('encoding_error', `${fileType} file is not valid UTF-8`, {
|
|
200
|
+
path: filePath,
|
|
201
|
+
}));
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=manifest-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-validator.js","sourceRoot":"","sources":["../../src/validators/manifest-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Validator
|
|
3
|
+
* Validates Django/Jinja2 template files
|
|
4
|
+
*/
|
|
5
|
+
import { ValidationResult } from '../types/validation-result.js';
|
|
6
|
+
export declare class TemplateValidator {
|
|
7
|
+
private errors;
|
|
8
|
+
private warnings;
|
|
9
|
+
/**
|
|
10
|
+
* Validate a template file
|
|
11
|
+
*/
|
|
12
|
+
validate(templatePath: string): Promise<ValidationResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Check file exists
|
|
15
|
+
*/
|
|
16
|
+
private fileExists;
|
|
17
|
+
/**
|
|
18
|
+
* Read template content
|
|
19
|
+
*/
|
|
20
|
+
private readTemplate;
|
|
21
|
+
/**
|
|
22
|
+
* Check basic template syntax
|
|
23
|
+
*/
|
|
24
|
+
private checkTemplateSyntax;
|
|
25
|
+
/**
|
|
26
|
+
* Check for common template issues
|
|
27
|
+
*/
|
|
28
|
+
private checkCommonIssues;
|
|
29
|
+
/**
|
|
30
|
+
* Build validation result
|
|
31
|
+
*/
|
|
32
|
+
private buildResult;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=template-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-validator.d.ts","sourceRoot":"","sources":["../../src/validators/template-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,gBAAgB,EAKjB,MAAM,+BAA+B,CAAC;AAEvC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,QAAQ,CAA2B;IAE3C;;OAEG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2B/D;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,YAAY;IA0B1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwE3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6CzB;;OAEG;IACH,OAAO,CAAC,WAAW;CAOpB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Validator
|
|
3
|
+
* Validates Django/Jinja2 template files
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import { createError, createWarning, } from '../types/validation-result.js';
|
|
7
|
+
export class TemplateValidator {
|
|
8
|
+
errors = [];
|
|
9
|
+
warnings = [];
|
|
10
|
+
/**
|
|
11
|
+
* Validate a template file
|
|
12
|
+
*/
|
|
13
|
+
async validate(templatePath) {
|
|
14
|
+
this.errors = [];
|
|
15
|
+
this.warnings = [];
|
|
16
|
+
// Check file exists
|
|
17
|
+
if (!(await this.fileExists(templatePath))) {
|
|
18
|
+
this.errors.push(createError('file_not_found', `Template file does not exist: ${templatePath}`));
|
|
19
|
+
return this.buildResult();
|
|
20
|
+
}
|
|
21
|
+
// Check file is not empty
|
|
22
|
+
const content = await this.readTemplate(templatePath);
|
|
23
|
+
if (!content) {
|
|
24
|
+
return this.buildResult();
|
|
25
|
+
}
|
|
26
|
+
// Basic template syntax checks
|
|
27
|
+
this.checkTemplateSyntax(content, templatePath);
|
|
28
|
+
// Check for common issues
|
|
29
|
+
this.checkCommonIssues(content, templatePath);
|
|
30
|
+
return this.buildResult();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check file exists
|
|
34
|
+
*/
|
|
35
|
+
async fileExists(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
const stats = await fs.stat(filePath);
|
|
38
|
+
return stats.isFile();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read template content
|
|
46
|
+
*/
|
|
47
|
+
async readTemplate(templatePath) {
|
|
48
|
+
try {
|
|
49
|
+
const content = await fs.readFile(templatePath, 'utf-8');
|
|
50
|
+
if (content.trim().length === 0) {
|
|
51
|
+
this.errors.push(createError('empty_template', 'Template file is empty', {
|
|
52
|
+
path: templatePath,
|
|
53
|
+
}));
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return content;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
this.errors.push(createError('read_error', `Failed to read template: ${error.message}`, {
|
|
61
|
+
path: templatePath,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check basic template syntax
|
|
69
|
+
*/
|
|
70
|
+
checkTemplateSyntax(content, templatePath) {
|
|
71
|
+
const lines = content.split('\n');
|
|
72
|
+
// Check for unclosed tags
|
|
73
|
+
let blockStack = [];
|
|
74
|
+
const blockTags = ['if', 'for', 'block', 'with', 'autoescape', 'spaceless', 'verbatim'];
|
|
75
|
+
lines.forEach((line, index) => {
|
|
76
|
+
const lineNumber = index + 1;
|
|
77
|
+
// Find opening tags
|
|
78
|
+
const openMatches = line.matchAll(/{%\s*(\w+)/g);
|
|
79
|
+
for (const match of openMatches) {
|
|
80
|
+
const tag = match[1];
|
|
81
|
+
if (blockTags.includes(tag)) {
|
|
82
|
+
blockStack.push({ tag, line: lineNumber });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Find closing tags
|
|
86
|
+
const closeMatches = line.matchAll(/{%\s*end(\w+)/g);
|
|
87
|
+
for (const match of closeMatches) {
|
|
88
|
+
const tag = match[1];
|
|
89
|
+
if (blockStack.length === 0) {
|
|
90
|
+
this.errors.push(createError('template_syntax', `Unexpected closing tag: {% end${tag} %}`, {
|
|
91
|
+
path: templatePath,
|
|
92
|
+
line: lineNumber,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const lastBlock = blockStack.pop();
|
|
97
|
+
if (lastBlock && lastBlock.tag !== tag) {
|
|
98
|
+
this.errors.push(createError('template_syntax', `Mismatched closing tag: expected {% end${lastBlock.tag} %}, found {% end${tag} %}`, {
|
|
99
|
+
path: templatePath,
|
|
100
|
+
line: lineNumber,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Check for unclosed blocks
|
|
107
|
+
if (blockStack.length > 0) {
|
|
108
|
+
for (const block of blockStack) {
|
|
109
|
+
this.errors.push(createError('template_syntax', `Unclosed block: {% ${block.tag} %}`, {
|
|
110
|
+
path: templatePath,
|
|
111
|
+
line: block.line,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Check for basic variable syntax errors
|
|
116
|
+
const variablePattern = /{{\s*[\w._[\]]+\s*}}/g;
|
|
117
|
+
const invalidVariables = content.match(/{{[^}]*$/gm);
|
|
118
|
+
if (invalidVariables) {
|
|
119
|
+
this.errors.push(createError('template_syntax', 'Unclosed variable tag: {{', {
|
|
120
|
+
path: templatePath,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Check for common template issues
|
|
126
|
+
*/
|
|
127
|
+
checkCommonIssues(content, templatePath) {
|
|
128
|
+
// Warn about hardcoded URLs
|
|
129
|
+
if (content.includes('http://') || content.includes('https://')) {
|
|
130
|
+
const httpMatches = content.match(/https?:\/\/[^\s"'<>]+/g);
|
|
131
|
+
if (httpMatches && httpMatches.length > 0) {
|
|
132
|
+
this.warnings.push(createWarning('hardcoded_url', `Found ${httpMatches.length} hardcoded URL(s) in template`, {
|
|
133
|
+
path: templatePath,
|
|
134
|
+
suggestion: 'Use relative URLs or Django template tags like {% url %}',
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Warn about inline styles (should use design tokens)
|
|
139
|
+
const inlineStyleCount = (content.match(/style\s*=/gi) || []).length;
|
|
140
|
+
if (inlineStyleCount > 3) {
|
|
141
|
+
this.warnings.push(createWarning('inline_styles', `Found ${inlineStyleCount} inline style attributes`, {
|
|
142
|
+
path: templatePath,
|
|
143
|
+
suggestion: 'Consider using CSS classes and design tokens instead',
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
// Check for missing alt attributes on images
|
|
147
|
+
const imgTags = content.match(/<img[^>]+>/gi);
|
|
148
|
+
if (imgTags) {
|
|
149
|
+
for (const imgTag of imgTags) {
|
|
150
|
+
if (!imgTag.includes('alt=')) {
|
|
151
|
+
this.warnings.push(createWarning('accessibility', 'Image tag missing alt attribute', {
|
|
152
|
+
path: templatePath,
|
|
153
|
+
suggestion: 'Add alt attribute for accessibility',
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Build validation result
|
|
161
|
+
*/
|
|
162
|
+
buildResult() {
|
|
163
|
+
return {
|
|
164
|
+
isValid: this.errors.length === 0,
|
|
165
|
+
errors: this.errors,
|
|
166
|
+
warnings: this.warnings,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=template-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-validator.js","sourceRoot":"","sources":["../../src/validators/template-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Validator
|
|
3
|
+
* Validates theme packages with bundled components
|
|
4
|
+
*/
|
|
5
|
+
import { ManifestValidator } from './manifest-validator.js';
|
|
6
|
+
import { ValidationResult } from '../types/validation-result.js';
|
|
7
|
+
export declare class ThemeValidator extends ManifestValidator {
|
|
8
|
+
private themeDir;
|
|
9
|
+
private manifest;
|
|
10
|
+
constructor(themeDir: string);
|
|
11
|
+
/**
|
|
12
|
+
* Validate complete theme package
|
|
13
|
+
*/
|
|
14
|
+
validate(): Promise<ValidationResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Validate all bundled components
|
|
17
|
+
*/
|
|
18
|
+
private validateBundledComponents;
|
|
19
|
+
/**
|
|
20
|
+
* Validate page schema files exist
|
|
21
|
+
*/
|
|
22
|
+
private validatePageSchemas;
|
|
23
|
+
/**
|
|
24
|
+
* Validate design tokens file exists and is valid JSON
|
|
25
|
+
*/
|
|
26
|
+
private validateDesignTokens;
|
|
27
|
+
/**
|
|
28
|
+
* Validate preview image exists
|
|
29
|
+
*/
|
|
30
|
+
private validatePreviewImage;
|
|
31
|
+
/**
|
|
32
|
+
* Validate screenshot files exist
|
|
33
|
+
*/
|
|
34
|
+
private validateScreenshots;
|
|
35
|
+
/**
|
|
36
|
+
* Build validation result
|
|
37
|
+
*/
|
|
38
|
+
private buildResult;
|
|
39
|
+
/**
|
|
40
|
+
* Generate human-readable validation report
|
|
41
|
+
*/
|
|
42
|
+
getValidationReport(): string;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=theme-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-validator.d.ts","sourceRoot":"","sources":["../../src/validators/theme-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EACL,gBAAgB,EAGjB,MAAM,+BAA+B,CAAC;AAGvC,qBAAa,cAAe,SAAQ,iBAAiB;IACnD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA8B;gBAElC,QAAQ,EAAE,MAAM;IAK5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC;IA6D3C;;OAEG;YACW,yBAAyB;IAyCvC;;OAEG;YACW,mBAAmB;IAwBjC;;OAEG;YACW,oBAAoB;IAiClC;;OAEG;YACW,oBAAoB;IA0BlC;;OAEG;YACW,mBAAmB;IAyBjC;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB;;OAEG;IACH,mBAAmB,IAAI,MAAM;CAqC9B"}
|