@schalkneethling/miyagi-core 4.4.4 → 4.6.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/api/index.js +53 -0
- package/dist/css/iframe.css +1 -1
- package/dist/css/main.css +1 -1
- package/dist/js/iframe.js +1 -1
- package/dist/js/main.build.js +1 -1
- package/dist/js/main.js +1 -1
- package/frontend/assets/css/main/controls.css +91 -0
- package/frontend/assets/css/main.css +5 -1
- package/frontend/assets/css/tokens.css +7 -0
- package/frontend/assets/js/_controls.js +206 -0
- package/frontend/assets/js/_main.js +2 -0
- package/frontend/assets/js/_socket.js +23 -0
- package/frontend/assets/js/iframe.js +12 -0
- package/frontend/views/component_variation.twig.miyagi +3 -0
- package/frontend/views/main.twig.miyagi +9 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/run.js +7 -0
- package/lib/cli/validate-html.js +110 -0
- package/lib/default-config.js +12 -0
- package/lib/i18n/en.js +23 -0
- package/lib/init/args.js +22 -0
- package/lib/init/router.js +56 -1
- package/lib/render/helpers/apply-overrides.js +36 -0
- package/lib/render/views/iframe/variation.js +19 -1
- package/lib/render/views/iframe/variation.standalone.js +9 -0
- package/lib/validator/html-report.js +85 -0
- package/lib/validator/html.js +389 -0
- package/package.json +3 -2
|
@@ -2,6 +2,7 @@ import path from "path";
|
|
|
2
2
|
import config from "../../../default-config.js";
|
|
3
3
|
import { getUserUiConfig } from "../../helpers.js";
|
|
4
4
|
import resolveAssets from "../../helpers/resolve-assets.js";
|
|
5
|
+
import applyOverrides from "../../helpers/apply-overrides.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {object} object - parameter object
|
|
@@ -20,7 +21,14 @@ export default async function renderIframeVariationStandalone({
|
|
|
20
21
|
componentDeclaredAssets = null,
|
|
21
22
|
cb,
|
|
22
23
|
cookies,
|
|
24
|
+
overrides,
|
|
23
25
|
}) {
|
|
26
|
+
if (overrides) {
|
|
27
|
+
const schema =
|
|
28
|
+
global.state.fileContents[component.paths?.schema?.full] ?? null;
|
|
29
|
+
componentData = applyOverrides(componentData ?? {}, overrides, schema);
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
const directoryPath = component.paths.dir.short;
|
|
25
33
|
|
|
26
34
|
return new Promise((resolve, reject) => {
|
|
@@ -49,6 +57,7 @@ export default async function renderIframeVariationStandalone({
|
|
|
49
57
|
"component_variation.twig.miyagi",
|
|
50
58
|
{
|
|
51
59
|
html: result,
|
|
60
|
+
mockDataResolved: JSON.stringify(componentData ?? {}),
|
|
52
61
|
cssFiles,
|
|
53
62
|
jsFilesHead,
|
|
54
63
|
jsFilesBody,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a Markdown report from HTML validation results.
|
|
3
|
+
* @param {object} results - output from validateAllHtml, validateComponentHtml, or validateHtmlFiles
|
|
4
|
+
* @param {Array<object>} results.components
|
|
5
|
+
* @param {object} results.summary
|
|
6
|
+
* @returns {string} Markdown-formatted report
|
|
7
|
+
*/
|
|
8
|
+
export function generateMarkdownReport(results) {
|
|
9
|
+
const { components, summary } = results;
|
|
10
|
+
const lines = [];
|
|
11
|
+
|
|
12
|
+
lines.push("# HTML Validation Report");
|
|
13
|
+
lines.push("");
|
|
14
|
+
lines.push(`**Date:** ${new Date().toISOString().split("T")[0]}`);
|
|
15
|
+
lines.push(
|
|
16
|
+
`**Total components:** ${summary.total} | **Passed:** ${summary.passed} | **Failed:** ${summary.failed}`,
|
|
17
|
+
);
|
|
18
|
+
lines.push(
|
|
19
|
+
`**Errors:** ${summary.errors} | **Warnings:** ${summary.warnings}`,
|
|
20
|
+
);
|
|
21
|
+
lines.push("");
|
|
22
|
+
|
|
23
|
+
// Summary table
|
|
24
|
+
lines.push("## Summary");
|
|
25
|
+
lines.push("");
|
|
26
|
+
lines.push("| Component | Status | Errors | Warnings |");
|
|
27
|
+
lines.push("|-----------|--------|--------|----------|");
|
|
28
|
+
|
|
29
|
+
for (const comp of components) {
|
|
30
|
+
const hasErrors = comp.variations.some((v) => !v.valid);
|
|
31
|
+
const status = hasErrors ? "FAIL" : "PASS";
|
|
32
|
+
let errors = 0;
|
|
33
|
+
let warnings = 0;
|
|
34
|
+
|
|
35
|
+
for (const variation of comp.variations) {
|
|
36
|
+
for (const msg of variation.messages) {
|
|
37
|
+
if (msg.severity === 2) {
|
|
38
|
+
errors++;
|
|
39
|
+
} else {
|
|
40
|
+
warnings++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
lines.push(`| ${comp.component} | ${status} | ${errors} | ${warnings} |`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Failed component details
|
|
49
|
+
const failedComponents = components.filter((comp) =>
|
|
50
|
+
comp.variations.some((v) => !v.valid),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (failedComponents.length > 0) {
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push("## Failed Components");
|
|
56
|
+
|
|
57
|
+
for (const comp of failedComponents) {
|
|
58
|
+
lines.push("");
|
|
59
|
+
lines.push(`### ${comp.component}`);
|
|
60
|
+
|
|
61
|
+
const failedVariations = comp.variations.filter((v) => !v.valid);
|
|
62
|
+
|
|
63
|
+
for (const variation of failedVariations) {
|
|
64
|
+
lines.push("");
|
|
65
|
+
lines.push(`#### ${variation.name}`);
|
|
66
|
+
lines.push("");
|
|
67
|
+
lines.push("| Line | Col | Severity | Rule | Message |");
|
|
68
|
+
lines.push("|------|-----|----------|------|---------|");
|
|
69
|
+
|
|
70
|
+
for (const msg of variation.messages) {
|
|
71
|
+
const severity = msg.severity === 2 ? "error" : "warning";
|
|
72
|
+
const escapedMessage = msg.message
|
|
73
|
+
.replace(/\\/g, "\\\\")
|
|
74
|
+
.replace(/\|/g, "\\|");
|
|
75
|
+
lines.push(
|
|
76
|
+
`| ${msg.line} | ${msg.column} | ${severity} | ${msg.ruleId} | ${escapedMessage} |`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lines.push("");
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { globSync } from "node:fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { HtmlValidate } from "html-validate";
|
|
5
|
+
import { getComponentData } from "../mocks/index.js";
|
|
6
|
+
import { t } from "../i18n/index.js";
|
|
7
|
+
import log from "../logger.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} templatePath
|
|
11
|
+
* @param {object} data
|
|
12
|
+
* @returns {Promise<string>}
|
|
13
|
+
*/
|
|
14
|
+
function renderTemplate(templatePath, data) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
global.app.render(templatePath, data, (error, result) => {
|
|
17
|
+
if (error) {
|
|
18
|
+
reject(error);
|
|
19
|
+
} else {
|
|
20
|
+
resolve(result);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {object} htmlValidateConfig
|
|
28
|
+
* @returns {HtmlValidate}
|
|
29
|
+
*/
|
|
30
|
+
function createValidator(htmlValidateConfig) {
|
|
31
|
+
return new HtmlValidate(htmlValidateConfig);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Array<object>} componentResults
|
|
36
|
+
* @returns {object}
|
|
37
|
+
*/
|
|
38
|
+
function buildSummary(componentResults) {
|
|
39
|
+
let errors = 0;
|
|
40
|
+
let warnings = 0;
|
|
41
|
+
let passed = 0;
|
|
42
|
+
let failed = 0;
|
|
43
|
+
|
|
44
|
+
for (const comp of componentResults) {
|
|
45
|
+
const hasErrors = comp.variations.some((v) => !v.valid);
|
|
46
|
+
if (hasErrors) {
|
|
47
|
+
failed++;
|
|
48
|
+
} else {
|
|
49
|
+
passed++;
|
|
50
|
+
}
|
|
51
|
+
for (const variation of comp.variations) {
|
|
52
|
+
for (const msg of variation.messages) {
|
|
53
|
+
if (msg.severity === 2) {
|
|
54
|
+
errors++;
|
|
55
|
+
} else {
|
|
56
|
+
warnings++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
total: componentResults.length,
|
|
64
|
+
passed,
|
|
65
|
+
failed,
|
|
66
|
+
errors,
|
|
67
|
+
warnings,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate rendered HTML for all components.
|
|
73
|
+
* @param {object} [options]
|
|
74
|
+
* @param {object} [options.htmlValidateConfig]
|
|
75
|
+
* @returns {Promise<{components: Array<object>, summary: object}>}
|
|
76
|
+
*/
|
|
77
|
+
export async function validateAllHtml(options = {}) {
|
|
78
|
+
const config =
|
|
79
|
+
options.htmlValidateConfig ?? global.config.htmlValidation?.htmlValidateConfig;
|
|
80
|
+
const validator = createValidator(config);
|
|
81
|
+
const components = global.state.routes.filter(
|
|
82
|
+
(route) => route.type === "components" && route.paths.tpl,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
log(
|
|
86
|
+
"info",
|
|
87
|
+
t("htmlValidation.all.start"),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const componentResults = await Promise.all(
|
|
91
|
+
components.map((component) =>
|
|
92
|
+
validateSingleComponent(component, validator),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const summary = buildSummary(componentResults);
|
|
97
|
+
|
|
98
|
+
if (summary.failed === 0) {
|
|
99
|
+
log("success", t("htmlValidation.all.valid"));
|
|
100
|
+
} else {
|
|
101
|
+
const msg =
|
|
102
|
+
summary.failed === 1
|
|
103
|
+
? t("htmlValidation.all.invalid.one")
|
|
104
|
+
: t("htmlValidation.all.invalid.other").replace(
|
|
105
|
+
"{{amount}}",
|
|
106
|
+
summary.failed,
|
|
107
|
+
);
|
|
108
|
+
log("error", msg);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { components: componentResults, summary };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Validate rendered HTML for a single component (all variations).
|
|
116
|
+
* @param {object} component - route object from global.state.routes
|
|
117
|
+
* @param {object} [options]
|
|
118
|
+
* @param {object} [options.htmlValidateConfig]
|
|
119
|
+
* @returns {Promise<{component: string, variations: Array<object>}>}
|
|
120
|
+
*/
|
|
121
|
+
export async function validateComponentHtml(component, options = {}) {
|
|
122
|
+
const config =
|
|
123
|
+
options.htmlValidateConfig ?? global.config.htmlValidation?.htmlValidateConfig;
|
|
124
|
+
const validator = createValidator(config);
|
|
125
|
+
|
|
126
|
+
log(
|
|
127
|
+
"info",
|
|
128
|
+
t("htmlValidation.component.start").replace(
|
|
129
|
+
"{{component}}",
|
|
130
|
+
component.paths.dir.short,
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const result = await validateSingleComponent(component, validator);
|
|
135
|
+
|
|
136
|
+
const allValid = result.variations.every((v) => v.valid);
|
|
137
|
+
if (allValid) {
|
|
138
|
+
log("success", t("htmlValidation.component.valid"));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {object} component
|
|
146
|
+
* @param {HtmlValidate} validator
|
|
147
|
+
* @returns {Promise<{component: string, variations: Array<object>}>}
|
|
148
|
+
*/
|
|
149
|
+
async function validateSingleComponent(component, validator) {
|
|
150
|
+
const data = await getComponentData(component);
|
|
151
|
+
const variations = [];
|
|
152
|
+
|
|
153
|
+
if (data && data.length > 0) {
|
|
154
|
+
for (const entry of data) {
|
|
155
|
+
try {
|
|
156
|
+
const html = await renderTemplate(
|
|
157
|
+
component.paths.tpl.full,
|
|
158
|
+
entry.resolved ?? {},
|
|
159
|
+
);
|
|
160
|
+
const report = await validator.validateString(html);
|
|
161
|
+
const messages = report.results.flatMap((r) =>
|
|
162
|
+
r.messages.map((msg) => ({
|
|
163
|
+
severity: msg.severity,
|
|
164
|
+
message: msg.message,
|
|
165
|
+
ruleId: msg.ruleId,
|
|
166
|
+
line: msg.line,
|
|
167
|
+
column: msg.column,
|
|
168
|
+
})),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
variations.push({
|
|
172
|
+
name: entry.name,
|
|
173
|
+
valid: report.valid,
|
|
174
|
+
messages,
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
log(
|
|
178
|
+
"warn",
|
|
179
|
+
t("htmlValidation.component.renderFailed")
|
|
180
|
+
.replace("{{component}}", component.paths.dir.short)
|
|
181
|
+
.replace("{{variation}}", entry.name),
|
|
182
|
+
);
|
|
183
|
+
variations.push({
|
|
184
|
+
name: entry.name,
|
|
185
|
+
valid: false,
|
|
186
|
+
messages: [
|
|
187
|
+
{
|
|
188
|
+
severity: 2,
|
|
189
|
+
message: `Render error: ${error.message || error}`,
|
|
190
|
+
ruleId: "render-error",
|
|
191
|
+
line: 0,
|
|
192
|
+
column: 0,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
// No mock data — render with empty object for default variation
|
|
200
|
+
try {
|
|
201
|
+
const html = await renderTemplate(component.paths.tpl.full, {});
|
|
202
|
+
const report = await validator.validateString(html);
|
|
203
|
+
const messages = report.results.flatMap((r) =>
|
|
204
|
+
r.messages.map((msg) => ({
|
|
205
|
+
severity: msg.severity,
|
|
206
|
+
message: msg.message,
|
|
207
|
+
ruleId: msg.ruleId,
|
|
208
|
+
line: msg.line,
|
|
209
|
+
column: msg.column,
|
|
210
|
+
})),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
variations.push({
|
|
214
|
+
name: "default",
|
|
215
|
+
valid: report.valid,
|
|
216
|
+
messages,
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
variations.push({
|
|
220
|
+
name: "default",
|
|
221
|
+
valid: false,
|
|
222
|
+
messages: [
|
|
223
|
+
{
|
|
224
|
+
severity: 2,
|
|
225
|
+
message: `Render error: ${error.message || error}`,
|
|
226
|
+
ruleId: "render-error",
|
|
227
|
+
line: 0,
|
|
228
|
+
column: 0,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
component: component.paths.dir.short,
|
|
237
|
+
variations,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parse component and variation name from a build output filename.
|
|
243
|
+
* Expected pattern: component-<path>-variation-<name>.html
|
|
244
|
+
* @param {string} filePath
|
|
245
|
+
* @returns {{ component: string, variation: string }}
|
|
246
|
+
*/
|
|
247
|
+
function parseFileName(filePath) {
|
|
248
|
+
const basename = path.basename(filePath, ".html");
|
|
249
|
+
|
|
250
|
+
const variationMatch = basename.match(/^component-(.+)-variation-(.+)$/);
|
|
251
|
+
if (variationMatch) {
|
|
252
|
+
const componentPart = variationMatch[1].replace(/-/g, "/");
|
|
253
|
+
return {
|
|
254
|
+
component: componentPart,
|
|
255
|
+
variation: variationMatch[2],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Fallback: use full basename as component name
|
|
260
|
+
return {
|
|
261
|
+
component: basename,
|
|
262
|
+
variation: "default",
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Validate pre-existing HTML files matching a glob pattern.
|
|
268
|
+
* Files are validated as full HTML documents.
|
|
269
|
+
* @param {string} globPattern
|
|
270
|
+
* @param {object} [options]
|
|
271
|
+
* @param {object} [options.htmlValidateConfig]
|
|
272
|
+
* @returns {Promise<{components: Array<object>, summary: object}>}
|
|
273
|
+
*/
|
|
274
|
+
export async function validateHtmlFiles(globPattern, options = {}) {
|
|
275
|
+
const baseConfig =
|
|
276
|
+
options.htmlValidateConfig ?? global.config?.htmlValidation?.htmlValidateConfig;
|
|
277
|
+
|
|
278
|
+
// For file mode, use full-document validation (don't disable doctype rules)
|
|
279
|
+
const fileConfig = {
|
|
280
|
+
...baseConfig,
|
|
281
|
+
rules: {
|
|
282
|
+
...(baseConfig?.rules ?? {}),
|
|
283
|
+
"doctype-style": undefined,
|
|
284
|
+
"missing-doctype": undefined,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Remove undefined keys so they fall back to the preset defaults
|
|
289
|
+
for (const [key, value] of Object.entries(fileConfig.rules)) {
|
|
290
|
+
if (value === undefined) {
|
|
291
|
+
delete fileConfig.rules[key];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const validator = createValidator(fileConfig);
|
|
296
|
+
|
|
297
|
+
log(
|
|
298
|
+
"info",
|
|
299
|
+
t("htmlValidation.files.start").replace("{{pattern}}", globPattern),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const files = globSync(globPattern);
|
|
303
|
+
|
|
304
|
+
if (files.length === 0) {
|
|
305
|
+
log(
|
|
306
|
+
"warn",
|
|
307
|
+
t("htmlValidation.files.noFilesFound").replace(
|
|
308
|
+
"{{pattern}}",
|
|
309
|
+
globPattern,
|
|
310
|
+
),
|
|
311
|
+
);
|
|
312
|
+
return { components: [], summary: buildSummary([]) };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Group files by component
|
|
316
|
+
const componentMap = new Map();
|
|
317
|
+
|
|
318
|
+
for (const filePath of files) {
|
|
319
|
+
const { component, variation } = parseFileName(filePath);
|
|
320
|
+
if (!componentMap.has(component)) {
|
|
321
|
+
componentMap.set(component, []);
|
|
322
|
+
}
|
|
323
|
+
componentMap.get(component).push({ filePath, variation });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const componentResults = [];
|
|
327
|
+
|
|
328
|
+
for (const [componentName, fileEntries] of componentMap) {
|
|
329
|
+
const variations = [];
|
|
330
|
+
|
|
331
|
+
for (const { filePath, variation } of fileEntries) {
|
|
332
|
+
try {
|
|
333
|
+
const html = await readFile(filePath, "utf-8");
|
|
334
|
+
const report = await validator.validateString(html);
|
|
335
|
+
const messages = report.results.flatMap((r) =>
|
|
336
|
+
r.messages.map((msg) => ({
|
|
337
|
+
severity: msg.severity,
|
|
338
|
+
message: msg.message,
|
|
339
|
+
ruleId: msg.ruleId,
|
|
340
|
+
line: msg.line,
|
|
341
|
+
column: msg.column,
|
|
342
|
+
})),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
variations.push({
|
|
346
|
+
name: variation,
|
|
347
|
+
valid: report.valid,
|
|
348
|
+
messages,
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
variations.push({
|
|
352
|
+
name: variation,
|
|
353
|
+
valid: false,
|
|
354
|
+
messages: [
|
|
355
|
+
{
|
|
356
|
+
severity: 2,
|
|
357
|
+
message: `File read error: ${error.message || error}`,
|
|
358
|
+
ruleId: "file-error",
|
|
359
|
+
line: 0,
|
|
360
|
+
column: 0,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
componentResults.push({
|
|
368
|
+
component: componentName,
|
|
369
|
+
variations,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const summary = buildSummary(componentResults);
|
|
374
|
+
|
|
375
|
+
if (summary.failed === 0) {
|
|
376
|
+
log("success", t("htmlValidation.all.valid"));
|
|
377
|
+
} else {
|
|
378
|
+
const msg =
|
|
379
|
+
summary.failed === 1
|
|
380
|
+
? t("htmlValidation.all.invalid.one")
|
|
381
|
+
: t("htmlValidation.all.invalid.other").replace(
|
|
382
|
+
"{{amount}}",
|
|
383
|
+
summary.failed,
|
|
384
|
+
);
|
|
385
|
+
log("error", msg);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return { components: componentResults, summary };
|
|
389
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schalkneethling/miyagi-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "miyagi is a component development tool for JavaScript template engines.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Schalk Neethling <schalkneethling@duck.com>, Michael Großklaus <mail@mgrossklaus.de> (https://www.mgrossklaus.de)",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"deepmerge": "^4.3.1",
|
|
44
44
|
"directory-tree": "^3.5.2",
|
|
45
45
|
"express": "^5.1.0",
|
|
46
|
+
"html-validate": "^10.11.2",
|
|
46
47
|
"js-yaml": "^4.1.0",
|
|
47
48
|
"marked": "^17.0.2",
|
|
48
49
|
"twing": "7.3.1",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"@types/yargs": "^17.0.35",
|
|
60
61
|
"@vitest/coverage-v8": "^4.0.6",
|
|
61
62
|
"cssnano": "^7.1.2",
|
|
62
|
-
"eslint": "^
|
|
63
|
+
"eslint": "^10.2.0",
|
|
63
64
|
"eslint-plugin-jsdoc": "^62.5.4",
|
|
64
65
|
"globals": "^17.4.0",
|
|
65
66
|
"gulp": "^5.0.1",
|