@schalkneethling/miyagi-core 4.0.2 → 4.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/api/index.js +98 -10
- package/lib/build/index.js +16 -0
- package/lib/cli/drupal-assets.js +159 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/lint.js +142 -94
- package/lib/constants/lint-log-levels.js +11 -0
- package/lib/default-config.js +10 -0
- package/lib/drupal/load-assets-config.js +94 -0
- package/lib/drupal/resolve-library-assets.js +189 -0
- package/lib/i18n/en.js +4 -0
- package/lib/index.js +27 -4
- package/lib/init/args.js +38 -0
- package/lib/init/config.js +26 -0
- package/lib/init/router.js +1 -0
- package/lib/init/static.js +30 -0
- package/lib/init/watcher.js +21 -0
- package/lib/logger.js +37 -2
- package/lib/mocks/get.js +4 -0
- package/lib/render/helpers/resolve-assets.js +58 -0
- package/lib/render/views/iframe/component.js +9 -7
- package/lib/render/views/iframe/variation.standalone.js +9 -7
- package/lib/state/file-contents.js +46 -3
- package/lib/state/index.js +1 -0
- package/lib/validator/mocks.js +31 -26
- package/lib/validator/schemas.js +234 -0
- package/package.json +10 -8
package/api/index.js
CHANGED
|
@@ -7,6 +7,11 @@ import build from "../lib/build/index.js";
|
|
|
7
7
|
import generateMockData from "../lib/generator/mocks.js";
|
|
8
8
|
import generateComponent from "../lib/generator/component.js";
|
|
9
9
|
import validateMockData from "../lib/validator/mocks.js";
|
|
10
|
+
import {
|
|
11
|
+
getSchemaValidationMode,
|
|
12
|
+
toSchemaValidationResult,
|
|
13
|
+
validateSchemas,
|
|
14
|
+
} from "../lib/validator/schemas.js";
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* @param {object} obj
|
|
@@ -172,14 +177,44 @@ export const createComponent = async ({ component, only = [], skip = [] }) => {
|
|
|
172
177
|
|
|
173
178
|
export const lintComponents = async () => {
|
|
174
179
|
global.app = await init("api");
|
|
175
|
-
const
|
|
180
|
+
const mode = getSchemaValidationMode();
|
|
181
|
+
const components = global.state.routes.filter((route) => route.paths.tpl);
|
|
182
|
+
const schemaValidation = validateSchemas({
|
|
183
|
+
components,
|
|
184
|
+
});
|
|
185
|
+
const schemaErrorsByComponent = new Map();
|
|
186
|
+
|
|
187
|
+
schemaValidation.errors.forEach((entry) => {
|
|
188
|
+
if (!schemaErrorsByComponent.has(entry.component)) {
|
|
189
|
+
schemaErrorsByComponent.set(entry.component, []);
|
|
190
|
+
}
|
|
191
|
+
schemaErrorsByComponent
|
|
192
|
+
.get(entry.component)
|
|
193
|
+
.push(toSchemaValidationResult(entry));
|
|
194
|
+
});
|
|
176
195
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
196
|
+
if (mode === "fail-fast" && schemaValidation.errors.length > 0) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
data: getLintComponentErrorsInRouteOrder({
|
|
200
|
+
components,
|
|
201
|
+
errorMap: schemaErrorsByComponent,
|
|
202
|
+
}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const promises = components
|
|
207
|
+
.filter((route) => !schemaErrorsByComponent.has(route.paths.dir.short))
|
|
208
|
+
.map(
|
|
209
|
+
(route) =>
|
|
180
210
|
new Promise((resolve) => {
|
|
181
211
|
getComponentData(route).then((data) => {
|
|
182
|
-
const validation = validateMockData(
|
|
212
|
+
const validation = validateMockData(
|
|
213
|
+
route,
|
|
214
|
+
data || [],
|
|
215
|
+
true,
|
|
216
|
+
schemaValidation.validSchemas,
|
|
217
|
+
);
|
|
183
218
|
|
|
184
219
|
resolve({
|
|
185
220
|
component: route.alias,
|
|
@@ -187,13 +222,25 @@ export const lintComponents = async () => {
|
|
|
187
222
|
});
|
|
188
223
|
});
|
|
189
224
|
}),
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
});
|
|
225
|
+
);
|
|
193
226
|
|
|
194
227
|
return await Promise.all(promises)
|
|
195
228
|
.then((res) => {
|
|
196
|
-
|
|
229
|
+
res.forEach((result) => {
|
|
230
|
+
if (!result?.errors?.length) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const componentErrors = schemaErrorsByComponent.get(result.component) || [];
|
|
234
|
+
schemaErrorsByComponent.set(result.component, [
|
|
235
|
+
...componentErrors,
|
|
236
|
+
...result.errors,
|
|
237
|
+
]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const errors = getLintComponentErrorsInRouteOrder({
|
|
241
|
+
components,
|
|
242
|
+
errorMap: schemaErrorsByComponent,
|
|
243
|
+
});
|
|
197
244
|
|
|
198
245
|
return {
|
|
199
246
|
success: errors.length === 0,
|
|
@@ -216,8 +263,26 @@ export const lintComponent = async ({ component }) => {
|
|
|
216
263
|
message: `The component ${component} does not seem to exist.`,
|
|
217
264
|
};
|
|
218
265
|
|
|
266
|
+
const allSchemaValidation = validateSchemas({
|
|
267
|
+
components: [componentObject],
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (allSchemaValidation.errors.length > 0) {
|
|
271
|
+
return {
|
|
272
|
+
success: false,
|
|
273
|
+
data: allSchemaValidation.errors.map((entry) =>
|
|
274
|
+
toSchemaValidationResult(entry),
|
|
275
|
+
),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
219
279
|
const data = await getComponentData(componentObject);
|
|
220
|
-
const errors = validateMockData(
|
|
280
|
+
const errors = validateMockData(
|
|
281
|
+
componentObject,
|
|
282
|
+
data || [],
|
|
283
|
+
true,
|
|
284
|
+
allSchemaValidation.validSchemas,
|
|
285
|
+
);
|
|
221
286
|
|
|
222
287
|
return {
|
|
223
288
|
success: errors === null || errors?.length === 0,
|
|
@@ -234,3 +299,26 @@ function getComponentsObject(component) {
|
|
|
234
299
|
(route) => route.paths.dir.short === component,
|
|
235
300
|
);
|
|
236
301
|
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @param {object} params
|
|
305
|
+
* @param {Array<object>} params.components
|
|
306
|
+
* @param {Map<string, Array<object>>} params.errorMap
|
|
307
|
+
* @returns {Array<object>}
|
|
308
|
+
*/
|
|
309
|
+
function getLintComponentErrorsInRouteOrder({ components, errorMap }) {
|
|
310
|
+
return components
|
|
311
|
+
.map((route) => {
|
|
312
|
+
const componentErrors = errorMap.get(route.alias) || [];
|
|
313
|
+
|
|
314
|
+
if (componentErrors.length === 0) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
component: route.alias,
|
|
320
|
+
errors: componentErrors,
|
|
321
|
+
};
|
|
322
|
+
})
|
|
323
|
+
.filter(Boolean);
|
|
324
|
+
}
|
package/lib/build/index.js
CHANGED
|
@@ -380,6 +380,19 @@ export default () => {
|
|
|
380
380
|
});
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
const sharedCss = (assetsConfig.shared?.css || []).map((file) =>
|
|
384
|
+
path.join(
|
|
385
|
+
assetsConfig.root,
|
|
386
|
+
typeof file === "string" ? file : file.src,
|
|
387
|
+
),
|
|
388
|
+
);
|
|
389
|
+
const sharedJs = (assetsConfig.shared?.js || []).map((file) =>
|
|
390
|
+
path.join(
|
|
391
|
+
assetsConfig.root,
|
|
392
|
+
typeof file === "string" ? file : file.src,
|
|
393
|
+
),
|
|
394
|
+
);
|
|
395
|
+
|
|
383
396
|
const cssJsFiles = [
|
|
384
397
|
...new Set([
|
|
385
398
|
...assetsConfig.css.map((file) =>
|
|
@@ -394,6 +407,8 @@ export default () => {
|
|
|
394
407
|
typeof file === "string" ? file : file.src,
|
|
395
408
|
),
|
|
396
409
|
),
|
|
410
|
+
...sharedCss,
|
|
411
|
+
...sharedJs,
|
|
397
412
|
...assetsConfig.customProperties.files.map((file) =>
|
|
398
413
|
path.join(assetsConfig.root, file),
|
|
399
414
|
),
|
|
@@ -655,6 +670,7 @@ export default () => {
|
|
|
655
670
|
res: global.app,
|
|
656
671
|
component,
|
|
657
672
|
componentData: data.resolved,
|
|
673
|
+
componentDeclaredAssets: data.$assets || null,
|
|
658
674
|
cb: async (err, response) => {
|
|
659
675
|
if (err) {
|
|
660
676
|
if (typeof err === "string") {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
import log from "../logger.js";
|
|
7
|
+
import { loadAssetsConfig } from "../drupal/load-assets-config.js";
|
|
8
|
+
import {
|
|
9
|
+
parseLibrariesYaml,
|
|
10
|
+
resolveComponentAssets,
|
|
11
|
+
mapLibraryToComponent,
|
|
12
|
+
} from "../drupal/resolve-library-assets.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {{src: string, type?: string}} JsEntry
|
|
16
|
+
* @typedef {{css: string[], js: JsEntry[]}} ComponentAssets
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} args - CLI arguments from yargs
|
|
21
|
+
*/
|
|
22
|
+
export default async function drupalAssets(args) {
|
|
23
|
+
let config;
|
|
24
|
+
try {
|
|
25
|
+
config = await loadAssetsConfig(args);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
log("error", /** @type {Error} */ (err).message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!config.libraries) {
|
|
32
|
+
log("error", "No libraries file specified. Use --libraries or configure it in .miyagi-assets.js.");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let yamlContent;
|
|
37
|
+
try {
|
|
38
|
+
yamlContent = await readFile(config.libraries, "utf8");
|
|
39
|
+
} catch {
|
|
40
|
+
log("error", `Could not read libraries file: ${config.libraries}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const librariesMap = parseLibrariesYaml(yamlContent);
|
|
45
|
+
const targetLibraries = config.components || Object.keys(librariesMap);
|
|
46
|
+
const componentsFolder = global.config?.components?.folder || "src";
|
|
47
|
+
|
|
48
|
+
let updatedCount = 0;
|
|
49
|
+
|
|
50
|
+
for (const libraryName of targetLibraries) {
|
|
51
|
+
if (!librariesMap[libraryName]) {
|
|
52
|
+
log("warn", `Library "${libraryName}" not found in ${config.libraries} — skipping.`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const componentPath = mapLibraryToComponent(
|
|
57
|
+
libraryName,
|
|
58
|
+
config.mapping,
|
|
59
|
+
componentsFolder,
|
|
60
|
+
config.autoDiscoveryPrefixes ?? undefined,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (!componentPath) {
|
|
64
|
+
log("warn", `Could not map library "${libraryName}" to a component folder — skipping.`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const assets = resolveComponentAssets(
|
|
69
|
+
libraryName,
|
|
70
|
+
librariesMap,
|
|
71
|
+
config.ignorePrefixes,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (config.dryRun) {
|
|
75
|
+
log("info", `[dry-run] ${libraryName} → ${componentPath}`);
|
|
76
|
+
console.log(JSON.stringify({ $assets: assets }, null, "\t"));
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const updated = await updateMockFile(
|
|
81
|
+
path.join(componentsFolder, componentPath),
|
|
82
|
+
assets,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (updated) {
|
|
86
|
+
updatedCount++;
|
|
87
|
+
log("info", `Updated $assets in ${componentPath}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!config.dryRun) {
|
|
92
|
+
log("success", `Done. Updated ${updatedCount} component(s).`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reads a component's mock file, injects/replaces $assets, writes back.
|
|
98
|
+
* @param {string} componentDir - absolute or relative path to component folder
|
|
99
|
+
* @param {ComponentAssets} assets
|
|
100
|
+
* @returns {Promise<boolean>} true if file was updated
|
|
101
|
+
*/
|
|
102
|
+
async function updateMockFile(componentDir, assets) {
|
|
103
|
+
const mocksConfig = global.config?.files?.mocks || {
|
|
104
|
+
name: "mocks",
|
|
105
|
+
extension: ["yaml", "yml", "json", "js"],
|
|
106
|
+
};
|
|
107
|
+
const extensions = Array.isArray(mocksConfig.extension)
|
|
108
|
+
? mocksConfig.extension
|
|
109
|
+
: [mocksConfig.extension];
|
|
110
|
+
|
|
111
|
+
for (const ext of extensions) {
|
|
112
|
+
const filePath = path.join(componentDir, `${mocksConfig.name}.${ext}`);
|
|
113
|
+
|
|
114
|
+
let content;
|
|
115
|
+
try {
|
|
116
|
+
content = await readFile(filePath, "utf8");
|
|
117
|
+
} catch {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (["yaml", "yml"].includes(ext)) {
|
|
122
|
+
/** @type {Record<string, unknown>} */
|
|
123
|
+
const data = /** @type {Record<string, unknown>} */ (
|
|
124
|
+
yaml.load(content) || {}
|
|
125
|
+
);
|
|
126
|
+
data.$assets = cleanAssets(assets);
|
|
127
|
+
await writeFile(filePath, yaml.dump(data, { indent: 2 }));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (ext === "json") {
|
|
132
|
+
/** @type {Record<string, unknown>} */
|
|
133
|
+
const data = JSON.parse(content);
|
|
134
|
+
data.$assets = cleanAssets(assets);
|
|
135
|
+
await writeFile(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Strips empty arrays from assets to keep mock files clean.
|
|
145
|
+
* @param {ComponentAssets} assets
|
|
146
|
+
* @returns {Partial<ComponentAssets>}
|
|
147
|
+
*/
|
|
148
|
+
function cleanAssets(assets) {
|
|
149
|
+
/** @type {Partial<ComponentAssets>} */
|
|
150
|
+
const result = {};
|
|
151
|
+
if (assets.css?.length > 0) {
|
|
152
|
+
result.css = assets.css;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (assets.js?.length > 0) {
|
|
156
|
+
result.js = assets.js;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
package/lib/cli/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import lintImport from "./lint.js";
|
|
2
2
|
import componentImport from "./component.js";
|
|
3
|
+
import drupalAssetsImport from "./drupal-assets.js";
|
|
3
4
|
|
|
4
5
|
export const lint = lintImport;
|
|
5
6
|
export const component = componentImport;
|
|
7
|
+
export const drupalAssets = drupalAssetsImport;
|
package/lib/cli/lint.js
CHANGED
|
@@ -4,6 +4,11 @@ import getConfig from "../config.js";
|
|
|
4
4
|
import log from "../logger.js";
|
|
5
5
|
import { getComponentData } from "../mocks/index.js";
|
|
6
6
|
import validateMockData from "../validator/mocks.js";
|
|
7
|
+
import {
|
|
8
|
+
getSchemaValidationMode,
|
|
9
|
+
toSchemaValidationResult,
|
|
10
|
+
validateSchemas,
|
|
11
|
+
} from "../validator/schemas.js";
|
|
7
12
|
import { t } from "../i18n/index.js";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -14,6 +19,8 @@ export default async function lint(args) {
|
|
|
14
19
|
|
|
15
20
|
const componentArg = args._.slice(1)[0];
|
|
16
21
|
const config = await getConfig(args);
|
|
22
|
+
process.env.MIYAGI_LOG_CONTEXT = "lint";
|
|
23
|
+
process.env.MIYAGI_LOG_LEVEL = config.lint?.logLevel || "error";
|
|
17
24
|
global.app = await init(config);
|
|
18
25
|
|
|
19
26
|
if (componentArg) {
|
|
@@ -23,8 +30,20 @@ export default async function lint(args) {
|
|
|
23
30
|
);
|
|
24
31
|
|
|
25
32
|
if (component) {
|
|
33
|
+
const schemaValidation = validateSchemas({
|
|
34
|
+
components: [component],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (schemaValidation.errors.length > 0) {
|
|
38
|
+
reportSchemaErrors(schemaValidation.errors);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
log("success", "All schemas valid.");
|
|
43
|
+
|
|
26
44
|
await validateComponentMockData({
|
|
27
45
|
component,
|
|
46
|
+
validSchemas: schemaValidation.validSchemas,
|
|
28
47
|
});
|
|
29
48
|
} else {
|
|
30
49
|
log("error", `The component ${componentArg} does not seem to exist.`);
|
|
@@ -40,81 +59,90 @@ export default async function lint(args) {
|
|
|
40
59
|
*/
|
|
41
60
|
async function validateAllMockData(exitProcess = true) {
|
|
42
61
|
log("info", t("linter.all.start"));
|
|
62
|
+
const mode = getSchemaValidationMode();
|
|
63
|
+
const components = global.state.routes.filter(
|
|
64
|
+
(route) => route.type === "components" && route.paths.tpl,
|
|
65
|
+
);
|
|
66
|
+
const schemaValidation = validateSchemas({
|
|
67
|
+
components,
|
|
68
|
+
});
|
|
69
|
+
const invalidSchemaComponents = new Set(
|
|
70
|
+
schemaValidation.errors.map((entry) => entry.component),
|
|
71
|
+
);
|
|
43
72
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
);
|
|
73
|
+
if (schemaValidation.errors.length === 0) {
|
|
74
|
+
log("success", "All schemas valid.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (schemaValidation.errors.length > 0 && mode === "fail-fast") {
|
|
78
|
+
reportSchemaErrors(schemaValidation.errors);
|
|
79
|
+
log(
|
|
80
|
+
"error",
|
|
81
|
+
schemaValidation.errors.length === 1
|
|
82
|
+
? t("linter.all.schema.invalid.one")
|
|
83
|
+
: t("linter.all.schema.invalid.other").replace(
|
|
84
|
+
"{{amount}}",
|
|
85
|
+
schemaValidation.errors.length,
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
if (exitProcess) {
|
|
89
|
+
process.exit(1);
|
|
62
90
|
}
|
|
63
|
-
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
64
93
|
|
|
65
|
-
Promise.all(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const results = await Promise.all(
|
|
95
|
+
components
|
|
96
|
+
.filter((route) => !invalidSchemaComponents.has(route.paths.dir.short))
|
|
97
|
+
.map((component) =>
|
|
98
|
+
validateComponentMockData({
|
|
99
|
+
component,
|
|
100
|
+
silent: true,
|
|
101
|
+
exitProcess: false,
|
|
102
|
+
validSchemas: schemaValidation.validSchemas,
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
const mockInvalidResults = results.filter(
|
|
107
|
+
(result) => result?.valid === false && result.type === "mocks",
|
|
108
|
+
);
|
|
73
109
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
110
|
+
if (mode === "collect-all" && schemaValidation.errors.length > 0) {
|
|
111
|
+
reportSchemaErrors(schemaValidation.errors);
|
|
112
|
+
log(
|
|
113
|
+
"error",
|
|
114
|
+
schemaValidation.errors.length === 1
|
|
115
|
+
? t("linter.all.schema.invalid.one")
|
|
116
|
+
: t("linter.all.schema.invalid.other").replace(
|
|
117
|
+
"{{amount}}",
|
|
118
|
+
schemaValidation.errors.length,
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
83
122
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
123
|
+
if (mockInvalidResults.length > 0) {
|
|
124
|
+
log(
|
|
125
|
+
"error",
|
|
126
|
+
mockInvalidResults.length === 1
|
|
127
|
+
? t("linter.all.mocks.invalid.one")
|
|
128
|
+
: t("linter.all.mocks.invalid.other").replace(
|
|
129
|
+
"{{amount}}",
|
|
130
|
+
mockInvalidResults.length,
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
95
134
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
mockInvalidResults.length,
|
|
104
|
-
),
|
|
105
|
-
);
|
|
106
|
-
}
|
|
135
|
+
if (mockInvalidResults.length === 0 && schemaValidation.errors.length === 0) {
|
|
136
|
+
log("success", t("linter.all.valid"));
|
|
137
|
+
if (exitProcess) {
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
107
142
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
})
|
|
112
|
-
.catch((err) => {
|
|
113
|
-
console.error(err);
|
|
114
|
-
if (exitProcess) {
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
143
|
+
if (exitProcess) {
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
118
146
|
}
|
|
119
147
|
|
|
120
148
|
/**
|
|
@@ -122,12 +150,14 @@ async function validateAllMockData(exitProcess = true) {
|
|
|
122
150
|
* @param {object} obj.component
|
|
123
151
|
* @param {boolean} [obj.silent]
|
|
124
152
|
* @param {boolean} [obj.exitProcess]
|
|
153
|
+
* @param {Array<object>} [obj.validSchemas]
|
|
125
154
|
* @returns {Promise<object|null>}
|
|
126
155
|
*/
|
|
127
156
|
async function validateComponentMockData({
|
|
128
157
|
component,
|
|
129
158
|
silent,
|
|
130
159
|
exitProcess = true,
|
|
160
|
+
validSchemas = [],
|
|
131
161
|
}) {
|
|
132
162
|
if (!silent) {
|
|
133
163
|
log(
|
|
@@ -139,42 +169,60 @@ async function validateComponentMockData({
|
|
|
139
169
|
);
|
|
140
170
|
}
|
|
141
171
|
|
|
142
|
-
const data = await getComponentData(component);
|
|
172
|
+
const data = (await getComponentData(component)) || [];
|
|
143
173
|
|
|
144
|
-
if (data) {
|
|
145
|
-
for (const { messages } of data) {
|
|
174
|
+
if (data.length > 0) {
|
|
175
|
+
for (const { messages = [] } of data) {
|
|
146
176
|
for (const { type, text, verbose } of messages) {
|
|
147
177
|
log(type, text, verbose);
|
|
148
178
|
}
|
|
149
179
|
}
|
|
180
|
+
}
|
|
150
181
|
|
|
151
|
-
|
|
182
|
+
const results = validateMockData(component, data, false, validSchemas);
|
|
152
183
|
|
|
153
|
-
|
|
184
|
+
if (!results) return null;
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
186
|
+
if (results.length === 0) {
|
|
187
|
+
if (!silent) {
|
|
188
|
+
log("success", t("linter.component.valid"));
|
|
189
|
+
}
|
|
159
190
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
} else {
|
|
163
|
-
return {
|
|
164
|
-
valid: true,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
if (exitProcess) {
|
|
169
|
-
process.exit(0);
|
|
170
|
-
} else {
|
|
171
|
-
return {
|
|
172
|
-
valid: false,
|
|
173
|
-
type: results[0].type,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
191
|
+
if (exitProcess) {
|
|
192
|
+
process.exit(0);
|
|
176
193
|
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
valid: true,
|
|
197
|
+
};
|
|
177
198
|
}
|
|
178
199
|
|
|
179
|
-
|
|
200
|
+
if (exitProcess) {
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
valid: false,
|
|
206
|
+
type: results[0].type,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param {Array<object>} schemaErrors
|
|
212
|
+
*/
|
|
213
|
+
function reportSchemaErrors(schemaErrors) {
|
|
214
|
+
schemaErrors.forEach((entry) => {
|
|
215
|
+
const result = toSchemaValidationResult(entry);
|
|
216
|
+
log("error", `${entry.component}:\n${result.data[0].message}`);
|
|
217
|
+
log("error", `schema: ${entry.schemaFile}`);
|
|
218
|
+
if (entry.schemaPath || entry.instancePath) {
|
|
219
|
+
log(
|
|
220
|
+
"error",
|
|
221
|
+
`schemaPath: ${entry.schemaPath || "-"} | instancePath: ${entry.instancePath || "-"}`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (entry.hint) {
|
|
225
|
+
log("warn", entry.hint);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
180
228
|
}
|
package/lib/default-config.js
CHANGED
|
@@ -5,6 +5,11 @@ export default {
|
|
|
5
5
|
assets: {
|
|
6
6
|
root: "",
|
|
7
7
|
css: [],
|
|
8
|
+
shared: {
|
|
9
|
+
css: [],
|
|
10
|
+
js: [],
|
|
11
|
+
},
|
|
12
|
+
isolateComponents: false,
|
|
8
13
|
customProperties: {
|
|
9
14
|
files: [],
|
|
10
15
|
prefixes: {
|
|
@@ -41,6 +46,9 @@ export default {
|
|
|
41
46
|
render: null,
|
|
42
47
|
options: {},
|
|
43
48
|
},
|
|
49
|
+
lint: {
|
|
50
|
+
logLevel: "error",
|
|
51
|
+
},
|
|
44
52
|
extensions: [],
|
|
45
53
|
files: {
|
|
46
54
|
css: {
|
|
@@ -91,7 +99,9 @@ export default {
|
|
|
91
99
|
},
|
|
92
100
|
schema: {
|
|
93
101
|
ajv: AJV,
|
|
102
|
+
verbose: false,
|
|
94
103
|
},
|
|
104
|
+
schemaValidationMode: "collect-all",
|
|
95
105
|
},
|
|
96
106
|
projectName: "miyagi",
|
|
97
107
|
defaultPort: 5000,
|