@carlonicora/nextjs-jsonapi 1.2.0 → 1.3.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/dist/scripts/generate-web-module/generator.d.ts +15 -0
- package/dist/scripts/generate-web-module/generator.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/generator.js +320 -0
- package/dist/scripts/generate-web-module/generator.js.map +1 -0
- package/dist/scripts/generate-web-module/index.d.ts +16 -0
- package/dist/scripts/generate-web-module/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/index.js +80 -0
- package/dist/scripts/generate-web-module/index.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/container.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/container.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/container.template.js +124 -0
- package/dist/scripts/generate-web-module/templates/components/container.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/content.template.d.ts +15 -0
- package/dist/scripts/generate-web-module/templates/components/content.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/content.template.js +39 -0
- package/dist/scripts/generate-web-module/templates/components/content.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/deleter.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/deleter.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/deleter.template.js +51 -0
- package/dist/scripts/generate-web-module/templates/components/deleter.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/details.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/details.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/details.template.js +109 -0
- package/dist/scripts/generate-web-module/templates/components/details.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/editor.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/editor.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/editor.template.js +459 -0
- package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/index.d.ts +15 -0
- package/dist/scripts/generate-web-module/templates/components/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/index.js +29 -0
- package/dist/scripts/generate-web-module/templates/components/index.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/list-container.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/list-container.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/list-container.template.js +30 -0
- package/dist/scripts/generate-web-module/templates/components/list-container.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/list.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/list.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/list.template.js +84 -0
- package/dist/scripts/generate-web-module/templates/components/list.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js +184 -0
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/components/selector.template.js +199 -0
- package/dist/scripts/generate-web-module/templates/components/selector.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/context.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/context.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/context.template.js +121 -0
- package/dist/scripts/generate-web-module/templates/context.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/fields.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/data/fields.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/fields.template.js +55 -0
- package/dist/scripts/generate-web-module/templates/data/fields.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/index.d.ts +10 -0
- package/dist/scripts/generate-web-module/templates/data/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/index.js +26 -0
- package/dist/scripts/generate-web-module/templates/data/index.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/interface.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/data/interface.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/interface.template.js +116 -0
- package/dist/scripts/generate-web-module/templates/data/interface.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/model.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/data/model.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/model.template.js +274 -0
- package/dist/scripts/generate-web-module/templates/data/model.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/service.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/data/service.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/data/service.template.js +168 -0
- package/dist/scripts/generate-web-module/templates/data/service.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/index.d.ts +12 -0
- package/dist/scripts/generate-web-module/templates/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/index.js +37 -0
- package/dist/scripts/generate-web-module/templates/index.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/module.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/module.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/module.template.js +64 -0
- package/dist/scripts/generate-web-module/templates/module.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/detail-page.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/pages/detail-page.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js +65 -0
- package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/index.d.ts +8 -0
- package/dist/scripts/generate-web-module/templates/pages/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/index.js +13 -0
- package/dist/scripts/generate-web-module/templates/pages/index.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/list-page.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/pages/list-page.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/pages/list-page.template.js +37 -0
- package/dist/scripts/generate-web-module/templates/pages/list-page.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/table-hook.template.d.ts +14 -0
- package/dist/scripts/generate-web-module/templates/table-hook.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/table-hook.template.js +174 -0
- package/dist/scripts/generate-web-module/templates/table-hook.template.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/field-mapper.d.ts +55 -0
- package/dist/scripts/generate-web-module/transformers/field-mapper.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/field-mapper.js +179 -0
- package/dist/scripts/generate-web-module/transformers/field-mapper.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/i18n-generator.d.ts +78 -0
- package/dist/scripts/generate-web-module/transformers/i18n-generator.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js +182 -0
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts +106 -0
- package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/import-resolver.js +193 -0
- package/dist/scripts/generate-web-module/transformers/import-resolver.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/index.d.ts +12 -0
- package/dist/scripts/generate-web-module/transformers/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/index.js +28 -0
- package/dist/scripts/generate-web-module/transformers/index.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/name-transformer.d.ts +60 -0
- package/dist/scripts/generate-web-module/transformers/name-transformer.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/name-transformer.js +115 -0
- package/dist/scripts/generate-web-module/transformers/name-transformer.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/parent-detector.d.ts +57 -0
- package/dist/scripts/generate-web-module/transformers/parent-detector.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/parent-detector.js +88 -0
- package/dist/scripts/generate-web-module/transformers/parent-detector.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts +68 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js +219 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js.map +1 -0
- package/dist/scripts/generate-web-module/types/field-mapping.types.d.ts +68 -0
- package/dist/scripts/generate-web-module/types/field-mapping.types.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/types/field-mapping.types.js +129 -0
- package/dist/scripts/generate-web-module/types/field-mapping.types.js.map +1 -0
- package/dist/scripts/generate-web-module/types/index.d.ts +9 -0
- package/dist/scripts/generate-web-module/types/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/types/index.js +25 -0
- package/dist/scripts/generate-web-module/types/index.js.map +1 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts +40 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.js +10 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.js.map +1 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +128 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.js +9 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.js.map +1 -0
- package/dist/scripts/generate-web-module/utils/bootstrapper-updater.d.ts +29 -0
- package/dist/scripts/generate-web-module/utils/bootstrapper-updater.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/utils/bootstrapper-updater.js +153 -0
- package/dist/scripts/generate-web-module/utils/bootstrapper-updater.js.map +1 -0
- package/dist/scripts/generate-web-module/utils/file-writer.d.ts +38 -0
- package/dist/scripts/generate-web-module/utils/file-writer.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/utils/file-writer.js +126 -0
- package/dist/scripts/generate-web-module/utils/file-writer.js.map +1 -0
- package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts +28 -0
- package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/utils/i18n-updater.js +122 -0
- package/dist/scripts/generate-web-module/utils/i18n-updater.js.map +1 -0
- package/dist/scripts/generate-web-module/utils/index.d.ts +9 -0
- package/dist/scripts/generate-web-module/utils/index.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/utils/index.js +20 -0
- package/dist/scripts/generate-web-module/utils/index.js.map +1 -0
- package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts +46 -0
- package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/validators/json-schema-validator.js +265 -0
- package/dist/scripts/generate-web-module/validators/json-schema-validator.js.map +1 -0
- package/package.json +27 -21
- package/scripts/generate-web-module/generator.ts +363 -0
- package/scripts/generate-web-module/index.ts +49 -0
- package/scripts/generate-web-module/templates/components/container.template.ts +129 -0
- package/scripts/generate-web-module/templates/components/content.template.ts +40 -0
- package/scripts/generate-web-module/templates/components/deleter.template.ts +51 -0
- package/scripts/generate-web-module/templates/components/details.template.ts +115 -0
- package/scripts/generate-web-module/templates/components/editor.template.ts +495 -0
- package/scripts/generate-web-module/templates/components/index.ts +18 -0
- package/scripts/generate-web-module/templates/components/list-container.template.ts +30 -0
- package/scripts/generate-web-module/templates/components/list.template.ts +91 -0
- package/scripts/generate-web-module/templates/components/multi-selector.template.ts +184 -0
- package/scripts/generate-web-module/templates/components/selector.template.ts +199 -0
- package/scripts/generate-web-module/templates/context.template.ts +121 -0
- package/scripts/generate-web-module/templates/data/fields.template.ts +64 -0
- package/scripts/generate-web-module/templates/data/index.ts +10 -0
- package/scripts/generate-web-module/templates/data/interface.template.ts +136 -0
- package/scripts/generate-web-module/templates/data/model.template.ts +327 -0
- package/scripts/generate-web-module/templates/data/service.template.ts +185 -0
- package/scripts/generate-web-module/templates/index.ts +34 -0
- package/scripts/generate-web-module/templates/module.template.ts +69 -0
- package/scripts/generate-web-module/templates/pages/detail-page.template.ts +65 -0
- package/scripts/generate-web-module/templates/pages/index.ts +8 -0
- package/scripts/generate-web-module/templates/pages/list-page.template.ts +37 -0
- package/scripts/generate-web-module/templates/table-hook.template.ts +182 -0
- package/scripts/generate-web-module/transformers/field-mapper.ts +201 -0
- package/scripts/generate-web-module/transformers/i18n-generator.ts +199 -0
- package/scripts/generate-web-module/transformers/import-resolver.ts +250 -0
- package/scripts/generate-web-module/transformers/index.ts +12 -0
- package/scripts/generate-web-module/transformers/name-transformer.ts +115 -0
- package/scripts/generate-web-module/transformers/parent-detector.ts +87 -0
- package/scripts/generate-web-module/transformers/relationship-resolver.ts +221 -0
- package/scripts/generate-web-module/tsconfig.json +24 -0
- package/scripts/generate-web-module/types/field-mapping.types.ts +141 -0
- package/scripts/generate-web-module/types/index.ts +9 -0
- package/scripts/generate-web-module/types/json-schema.interface.ts +42 -0
- package/scripts/generate-web-module/types/template-data.interface.ts +164 -0
- package/scripts/generate-web-module/utils/bootstrapper-updater.ts +145 -0
- package/scripts/generate-web-module/utils/file-writer.ts +115 -0
- package/scripts/generate-web-module/utils/i18n-updater.ts +108 -0
- package/scripts/generate-web-module/utils/index.ts +9 -0
- package/scripts/generate-web-module/validators/json-schema-validator.ts +306 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrapper Updater
|
|
3
|
+
*
|
|
4
|
+
* Updates the Bootstrapper.ts file to register new modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import { FrontendTemplateData } from "../types/template-data.interface";
|
|
9
|
+
|
|
10
|
+
const BOOTSTRAPPER_PATH = "apps/web/src/config/Bootstrapper.ts";
|
|
11
|
+
|
|
12
|
+
export interface BootstrapperUpdateResult {
|
|
13
|
+
success: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
alreadyRegistered?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Update Bootstrapper.ts to add a new module
|
|
20
|
+
*
|
|
21
|
+
* @param data - Frontend template data
|
|
22
|
+
* @param webBasePath - Base path to web app
|
|
23
|
+
* @param dryRun - Whether to perform a dry run
|
|
24
|
+
* @returns Update result
|
|
25
|
+
*/
|
|
26
|
+
export function updateBootstrapper(
|
|
27
|
+
data: FrontendTemplateData,
|
|
28
|
+
webBasePath: string,
|
|
29
|
+
dryRun: boolean = false
|
|
30
|
+
): BootstrapperUpdateResult {
|
|
31
|
+
const { names, targetDir } = data;
|
|
32
|
+
const bootstrapperPath = `${webBasePath}/${BOOTSTRAPPER_PATH}`;
|
|
33
|
+
|
|
34
|
+
// Check if file exists
|
|
35
|
+
if (!fs.existsSync(bootstrapperPath)) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
message: `Bootstrapper.ts not found at ${bootstrapperPath}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let content = fs.readFileSync(bootstrapperPath, "utf-8");
|
|
43
|
+
|
|
44
|
+
// Check if module is already registered
|
|
45
|
+
const modulePattern = new RegExp(`${names.pascalCase}Module`, "g");
|
|
46
|
+
if (modulePattern.test(content)) {
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
message: `Module ${names.pascalCase} already registered in Bootstrapper.ts`,
|
|
50
|
+
alreadyRegistered: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add import statement
|
|
55
|
+
const importStatement = `import { ${names.pascalCase}Module } from "@/features/${targetDir}/${names.kebabCase}/${names.pascalCase}Module";`;
|
|
56
|
+
|
|
57
|
+
// Find the right place to insert import (after "// Feature module imports" or at end of imports)
|
|
58
|
+
const featureImportsMarker = "// Feature module imports";
|
|
59
|
+
const lastImportMatch = content.match(/^import .* from ".*";$/gm);
|
|
60
|
+
|
|
61
|
+
if (content.includes(featureImportsMarker)) {
|
|
62
|
+
// Find the last import after the marker
|
|
63
|
+
const markerIndex = content.indexOf(featureImportsMarker);
|
|
64
|
+
const afterMarker = content.substring(markerIndex);
|
|
65
|
+
const nextNewlineIndex = afterMarker.indexOf("\n");
|
|
66
|
+
const insertPosition = markerIndex + nextNewlineIndex;
|
|
67
|
+
|
|
68
|
+
// Find next empty line or start of const
|
|
69
|
+
const afterMarkerContent = content.substring(insertPosition);
|
|
70
|
+
const constIndex = afterMarkerContent.indexOf("\nconst ");
|
|
71
|
+
|
|
72
|
+
if (constIndex > 0) {
|
|
73
|
+
const insertAt = insertPosition + constIndex;
|
|
74
|
+
content = content.slice(0, insertAt) + `\n${importStatement}` + content.slice(insertAt);
|
|
75
|
+
}
|
|
76
|
+
} else if (lastImportMatch) {
|
|
77
|
+
// Insert after last import
|
|
78
|
+
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
79
|
+
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
80
|
+
const insertAt = lastImportIndex + lastImport.length;
|
|
81
|
+
content = content.slice(0, insertAt) + `\n${importStatement}` + content.slice(insertAt);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Add module to allModules object
|
|
85
|
+
const allModulesPattern = /const allModules = \{[^}]+\}/s;
|
|
86
|
+
const allModulesMatch = content.match(allModulesPattern);
|
|
87
|
+
|
|
88
|
+
if (allModulesMatch) {
|
|
89
|
+
const allModulesBlock = allModulesMatch[0];
|
|
90
|
+
// Find the last line before closing brace
|
|
91
|
+
const lines = allModulesBlock.split("\n");
|
|
92
|
+
const lastEntryIndex = lines.length - 1;
|
|
93
|
+
|
|
94
|
+
// Add new module entry before the closing brace
|
|
95
|
+
const newEntry = ` ${names.pascalCase}: ${names.pascalCase}Module(moduleFactory),`;
|
|
96
|
+
|
|
97
|
+
// Find if there's a trailing comma on the last entry
|
|
98
|
+
for (let i = lastEntryIndex - 1; i >= 0; i--) {
|
|
99
|
+
const line = lines[i].trim();
|
|
100
|
+
if (line && !line.startsWith("//")) {
|
|
101
|
+
// Add comma if needed
|
|
102
|
+
if (!line.endsWith(",")) {
|
|
103
|
+
lines[i] = lines[i].replace(/(\s*)$/, ",$1");
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Insert new entry
|
|
110
|
+
lines.splice(lastEntryIndex, 0, newEntry);
|
|
111
|
+
const updatedBlock = lines.join("\n");
|
|
112
|
+
content = content.replace(allModulesBlock, updatedBlock);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (dryRun) {
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
message: `[DRY RUN] Would update Bootstrapper.ts with ${names.pascalCase}Module`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Write updated content
|
|
123
|
+
fs.writeFileSync(bootstrapperPath, content, "utf-8");
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
message: `Updated Bootstrapper.ts with ${names.pascalCase}Module`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate the import statement for a module
|
|
133
|
+
*/
|
|
134
|
+
export function generateImportStatement(data: FrontendTemplateData): string {
|
|
135
|
+
const { names, targetDir } = data;
|
|
136
|
+
return `import { ${names.pascalCase}Module } from "@/features/${targetDir}/${names.kebabCase}/${names.pascalCase}Module";`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate the module registration entry
|
|
141
|
+
*/
|
|
142
|
+
export function generateModuleEntry(data: FrontendTemplateData): string {
|
|
143
|
+
const { names } = data;
|
|
144
|
+
return ` ${names.pascalCase}: ${names.pascalCase}Module(moduleFactory),`;
|
|
145
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Writer Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles file writing with conflict detection and directory creation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { GeneratedFile } from "../types/template-data.interface";
|
|
10
|
+
|
|
11
|
+
export interface WriteOptions {
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WriteResult {
|
|
17
|
+
path: string;
|
|
18
|
+
status: "created" | "updated" | "skipped" | "dry-run";
|
|
19
|
+
existed: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Write a single file to disk
|
|
24
|
+
*
|
|
25
|
+
* @param file - Generated file data
|
|
26
|
+
* @param options - Write options
|
|
27
|
+
* @returns Write result
|
|
28
|
+
*/
|
|
29
|
+
export function writeFile(file: GeneratedFile, options: WriteOptions = {}): WriteResult {
|
|
30
|
+
const { dryRun = false, force = false } = options;
|
|
31
|
+
const existed = fs.existsSync(file.path);
|
|
32
|
+
|
|
33
|
+
if (dryRun) {
|
|
34
|
+
return {
|
|
35
|
+
path: file.path,
|
|
36
|
+
status: "dry-run",
|
|
37
|
+
existed,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if file exists and we're not forcing
|
|
42
|
+
if (existed && !force) {
|
|
43
|
+
return {
|
|
44
|
+
path: file.path,
|
|
45
|
+
status: "skipped",
|
|
46
|
+
existed: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create directory if needed
|
|
51
|
+
const dir = path.dirname(file.path);
|
|
52
|
+
if (!fs.existsSync(dir)) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Write the file
|
|
57
|
+
fs.writeFileSync(file.path, file.content, "utf-8");
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
path: file.path,
|
|
61
|
+
status: existed ? "updated" : "created",
|
|
62
|
+
existed,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Write multiple files to disk
|
|
68
|
+
*
|
|
69
|
+
* @param files - Array of generated files
|
|
70
|
+
* @param options - Write options
|
|
71
|
+
* @returns Array of write results
|
|
72
|
+
*/
|
|
73
|
+
export function writeFiles(files: GeneratedFile[], options: WriteOptions = {}): WriteResult[] {
|
|
74
|
+
return files.map((file) => writeFile(file, options));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Print write results to console
|
|
79
|
+
*
|
|
80
|
+
* @param results - Array of write results
|
|
81
|
+
*/
|
|
82
|
+
export function printResults(results: WriteResult[]): void {
|
|
83
|
+
const grouped = {
|
|
84
|
+
created: results.filter((r) => r.status === "created"),
|
|
85
|
+
updated: results.filter((r) => r.status === "updated"),
|
|
86
|
+
skipped: results.filter((r) => r.status === "skipped"),
|
|
87
|
+
dryRun: results.filter((r) => r.status === "dry-run"),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (grouped.dryRun.length > 0) {
|
|
91
|
+
console.log("\n📋 DRY RUN - Files that would be created:");
|
|
92
|
+
grouped.dryRun.forEach((r) => {
|
|
93
|
+
console.log(` ${r.existed ? "🔄" : "✨"} ${r.path}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (grouped.created.length > 0) {
|
|
98
|
+
console.log("\n✨ Created files:");
|
|
99
|
+
grouped.created.forEach((r) => console.log(` ${r.path}`));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (grouped.updated.length > 0) {
|
|
103
|
+
console.log("\n🔄 Updated files:");
|
|
104
|
+
grouped.updated.forEach((r) => console.log(` ${r.path}`));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (grouped.skipped.length > 0) {
|
|
108
|
+
console.log("\n⏭️ Skipped (already exist, use --force to overwrite):");
|
|
109
|
+
grouped.skipped.forEach((r) => console.log(` ${r.path}`));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(
|
|
113
|
+
`\nTotal: ${results.length} files (${grouped.created.length} created, ${grouped.updated.length} updated, ${grouped.skipped.length} skipped)`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Updater
|
|
3
|
+
*
|
|
4
|
+
* Updates the messages/en.json file with new module translations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import { FrontendTemplateData } from "../types/template-data.interface";
|
|
9
|
+
import { buildI18nMessages } from "../transformers/i18n-generator";
|
|
10
|
+
|
|
11
|
+
const MESSAGES_PATH = "apps/web/messages/en.json";
|
|
12
|
+
|
|
13
|
+
export interface I18nUpdateResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
message: string;
|
|
16
|
+
alreadyExists?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Update messages/en.json with new module translations
|
|
21
|
+
*
|
|
22
|
+
* @param data - Frontend template data
|
|
23
|
+
* @param webBasePath - Base path to web app
|
|
24
|
+
* @param dryRun - Whether to perform a dry run
|
|
25
|
+
* @returns Update result
|
|
26
|
+
*/
|
|
27
|
+
export function updateI18n(
|
|
28
|
+
data: FrontendTemplateData,
|
|
29
|
+
webBasePath: string,
|
|
30
|
+
dryRun: boolean = false
|
|
31
|
+
): I18nUpdateResult {
|
|
32
|
+
const { names, i18nKeys } = data;
|
|
33
|
+
const messagesPath = `${webBasePath}/${MESSAGES_PATH}`;
|
|
34
|
+
|
|
35
|
+
// Check if file exists
|
|
36
|
+
if (!fs.existsSync(messagesPath)) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
message: `Messages file not found at ${messagesPath}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const content = fs.readFileSync(messagesPath, "utf-8");
|
|
44
|
+
let messages: Record<string, any>;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
messages = JSON.parse(content);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
message: `Failed to parse messages/en.json: ${e}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if module already exists in features
|
|
56
|
+
if (messages.features && messages.features[names.camelCase]) {
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
message: `Module ${names.camelCase} already exists in messages/en.json`,
|
|
60
|
+
alreadyExists: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Build the i18n messages for this module
|
|
65
|
+
const moduleMessages = buildI18nMessages(i18nKeys);
|
|
66
|
+
|
|
67
|
+
// Add to features section
|
|
68
|
+
if (!messages.features) {
|
|
69
|
+
messages.features = {};
|
|
70
|
+
}
|
|
71
|
+
messages.features[names.camelCase] = moduleMessages.features[i18nKeys.moduleName];
|
|
72
|
+
|
|
73
|
+
// Add to types section (if not exists)
|
|
74
|
+
if (!messages.types) {
|
|
75
|
+
messages.types = {};
|
|
76
|
+
}
|
|
77
|
+
const typesKey = Object.keys(moduleMessages.types)[0];
|
|
78
|
+
if (typesKey && !messages.types[names.pluralCamel]) {
|
|
79
|
+
messages.types[names.pluralCamel] = moduleMessages.types[typesKey];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (dryRun) {
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
message: `[DRY RUN] Would update messages/en.json with ${names.camelCase} translations`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Write updated content with proper formatting
|
|
90
|
+
const updatedContent = JSON.stringify(messages, null, 2);
|
|
91
|
+
fs.writeFileSync(messagesPath, updatedContent, "utf-8");
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
message: `Updated messages/en.json with ${names.camelCase} translations`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Preview the i18n messages that would be added
|
|
101
|
+
*
|
|
102
|
+
* @param data - Frontend template data
|
|
103
|
+
* @returns Preview of messages
|
|
104
|
+
*/
|
|
105
|
+
export function previewI18nMessages(data: FrontendTemplateData): string {
|
|
106
|
+
const moduleMessages = buildI18nMessages(data.i18nKeys);
|
|
107
|
+
return JSON.stringify(moduleMessages, null, 2);
|
|
108
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utils Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all utility functions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { writeFile, writeFiles, printResults, type WriteOptions, type WriteResult } from "./file-writer";
|
|
8
|
+
export { updateBootstrapper, generateImportStatement, generateModuleEntry, type BootstrapperUpdateResult } from "./bootstrapper-updater";
|
|
9
|
+
export { updateI18n, previewI18nMessages, type I18nUpdateResult } from "./i18n-updater";
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates JSON module definition files for the frontend generator.
|
|
5
|
+
* Note: Backend-specific fields (toNode, relationshipName) are warnings, not errors.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { JsonModuleDefinition } from "../types/json-schema.interface";
|
|
9
|
+
|
|
10
|
+
export interface ValidationError {
|
|
11
|
+
field: string;
|
|
12
|
+
message: string;
|
|
13
|
+
severity: "error" | "warning";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate JSON module definition for frontend generation
|
|
18
|
+
*
|
|
19
|
+
* @param schema - JSON module definition
|
|
20
|
+
* @returns Array of validation errors (empty if valid)
|
|
21
|
+
*/
|
|
22
|
+
export function validateJsonSchema(schema: any): ValidationError[] {
|
|
23
|
+
const errors: ValidationError[] = [];
|
|
24
|
+
|
|
25
|
+
// Required fields
|
|
26
|
+
const required = ["moduleName", "endpointName", "targetDir"];
|
|
27
|
+
for (const field of required) {
|
|
28
|
+
if (!schema[field]) {
|
|
29
|
+
errors.push({
|
|
30
|
+
field,
|
|
31
|
+
message: `${field} is required`,
|
|
32
|
+
severity: "error",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Module name format (PascalCase)
|
|
38
|
+
if (schema.moduleName && !/^[A-Z][a-zA-Z0-9]*$/.test(schema.moduleName)) {
|
|
39
|
+
errors.push({
|
|
40
|
+
field: "moduleName",
|
|
41
|
+
message: 'Must be PascalCase (e.g., "Comment", "Discussion")',
|
|
42
|
+
severity: "error",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Endpoint format (kebab-case plural)
|
|
47
|
+
if (schema.endpointName && !/^[a-z][a-z0-9-]*$/.test(schema.endpointName)) {
|
|
48
|
+
errors.push({
|
|
49
|
+
field: "endpointName",
|
|
50
|
+
message: 'Must be kebab-case (e.g., "comments", "discussions")',
|
|
51
|
+
severity: "error",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Target directory validation
|
|
56
|
+
if (schema.targetDir && !["features", "foundations"].includes(schema.targetDir)) {
|
|
57
|
+
errors.push({
|
|
58
|
+
field: "targetDir",
|
|
59
|
+
message: 'Must be either "features" or "foundations"',
|
|
60
|
+
severity: "error",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Module ID validation (required for frontend)
|
|
65
|
+
if (!schema.moduleId) {
|
|
66
|
+
errors.push({
|
|
67
|
+
field: "moduleId",
|
|
68
|
+
message: "moduleId is required for frontend module registration",
|
|
69
|
+
severity: "error",
|
|
70
|
+
});
|
|
71
|
+
} else if (!/^[a-f0-9-]{36}$/i.test(schema.moduleId)) {
|
|
72
|
+
errors.push({
|
|
73
|
+
field: "moduleId",
|
|
74
|
+
message: "moduleId must be a valid UUID",
|
|
75
|
+
severity: "warning",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fields validation
|
|
80
|
+
if (schema.fields && Array.isArray(schema.fields)) {
|
|
81
|
+
schema.fields.forEach((field: any, index: number) => {
|
|
82
|
+
if (!field.name) {
|
|
83
|
+
errors.push({
|
|
84
|
+
field: `fields[${index}].name`,
|
|
85
|
+
message: "Field name is required",
|
|
86
|
+
severity: "error",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!field.type) {
|
|
91
|
+
errors.push({
|
|
92
|
+
field: `fields[${index}].type`,
|
|
93
|
+
message: "Field type is required",
|
|
94
|
+
severity: "error",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (field.nullable === undefined) {
|
|
99
|
+
errors.push({
|
|
100
|
+
field: `fields[${index}].nullable`,
|
|
101
|
+
message: "Field nullable flag is required",
|
|
102
|
+
severity: "error",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Validate known types
|
|
107
|
+
const validTypes = ["string", "number", "boolean", "date", "any"];
|
|
108
|
+
if (field.type && !validTypes.includes(field.type)) {
|
|
109
|
+
errors.push({
|
|
110
|
+
field: `fields[${index}].type`,
|
|
111
|
+
message: `Unknown type "${field.type}". Valid types: ${validTypes.join(", ")}`,
|
|
112
|
+
severity: "warning",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Relationships validation
|
|
119
|
+
if (schema.relationships && Array.isArray(schema.relationships)) {
|
|
120
|
+
schema.relationships.forEach((rel: any, index: number) => {
|
|
121
|
+
if (!rel.name) {
|
|
122
|
+
errors.push({
|
|
123
|
+
field: `relationships[${index}].name`,
|
|
124
|
+
message: "Relationship name is required",
|
|
125
|
+
severity: "error",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!rel.directory) {
|
|
130
|
+
errors.push({
|
|
131
|
+
field: `relationships[${index}].directory`,
|
|
132
|
+
message: "Relationship directory is required",
|
|
133
|
+
severity: "error",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Directory should be features or foundations
|
|
138
|
+
if (rel.directory && !["features", "foundations"].includes(rel.directory)) {
|
|
139
|
+
errors.push({
|
|
140
|
+
field: `relationships[${index}].directory`,
|
|
141
|
+
message: 'Relationship directory should be "features" or "foundations"',
|
|
142
|
+
severity: "warning",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (rel.single === undefined) {
|
|
147
|
+
errors.push({
|
|
148
|
+
field: `relationships[${index}].single`,
|
|
149
|
+
message: "Relationship single flag is required",
|
|
150
|
+
severity: "error",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (rel.nullable === undefined) {
|
|
155
|
+
errors.push({
|
|
156
|
+
field: `relationships[${index}].nullable`,
|
|
157
|
+
message: "Relationship nullable flag is required",
|
|
158
|
+
severity: "error",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Backend-specific fields are warnings only (not used in frontend)
|
|
163
|
+
if (!rel.relationshipName) {
|
|
164
|
+
errors.push({
|
|
165
|
+
field: `relationships[${index}].relationshipName`,
|
|
166
|
+
message: "relationshipName missing (backend-specific, not used in frontend)",
|
|
167
|
+
severity: "warning",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (rel.toNode === undefined) {
|
|
172
|
+
errors.push({
|
|
173
|
+
field: `relationships[${index}].toNode`,
|
|
174
|
+
message: "toNode missing (backend-specific, not used in frontend)",
|
|
175
|
+
severity: "warning",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return errors;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Format validation errors for display
|
|
186
|
+
*
|
|
187
|
+
* @param errors - Validation errors
|
|
188
|
+
* @returns Formatted error message
|
|
189
|
+
*/
|
|
190
|
+
export function formatValidationErrors(errors: ValidationError[]): string {
|
|
191
|
+
if (errors.length === 0) {
|
|
192
|
+
return "No validation errors";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const errorCount = errors.filter((e) => e.severity === "error").length;
|
|
196
|
+
const warningCount = errors.filter((e) => e.severity === "warning").length;
|
|
197
|
+
|
|
198
|
+
const lines: string[] = [];
|
|
199
|
+
|
|
200
|
+
if (errorCount > 0) {
|
|
201
|
+
lines.push(`\n❌ ${errorCount} error(s):`);
|
|
202
|
+
errors
|
|
203
|
+
.filter((e) => e.severity === "error")
|
|
204
|
+
.forEach((error) => {
|
|
205
|
+
lines.push(` • ${error.field}: ${error.message}`);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (warningCount > 0) {
|
|
210
|
+
lines.push(`\n⚠️ ${warningCount} warning(s):`);
|
|
211
|
+
errors
|
|
212
|
+
.filter((e) => e.severity === "warning")
|
|
213
|
+
.forEach((error) => {
|
|
214
|
+
lines.push(` • ${error.field}: ${error.message}`);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return lines.join("\n");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validation result interface
|
|
223
|
+
*/
|
|
224
|
+
export interface ValidationResult {
|
|
225
|
+
data: JsonModuleDefinition | null;
|
|
226
|
+
errors: string[];
|
|
227
|
+
warnings: string[];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Validate and parse JSON file from path
|
|
232
|
+
*
|
|
233
|
+
* @param filePath - Path to JSON file
|
|
234
|
+
* @returns Validation result with data, errors, and warnings
|
|
235
|
+
*/
|
|
236
|
+
export function parseAndValidate(filePath: string): ValidationResult {
|
|
237
|
+
const result: ValidationResult = {
|
|
238
|
+
data: null,
|
|
239
|
+
errors: [],
|
|
240
|
+
warnings: [],
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Read file
|
|
244
|
+
let content: string;
|
|
245
|
+
try {
|
|
246
|
+
const fs = require("fs");
|
|
247
|
+
if (!fs.existsSync(filePath)) {
|
|
248
|
+
result.errors.push(`File not found: ${filePath}`);
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
252
|
+
} catch (e) {
|
|
253
|
+
result.errors.push(`Failed to read file: ${(e as Error).message}`);
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Parse JSON
|
|
258
|
+
let schema: any;
|
|
259
|
+
try {
|
|
260
|
+
schema = JSON.parse(content);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
result.errors.push(`Invalid JSON: ${(e as Error).message}`);
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Handle array format (for bulk import compatibility)
|
|
267
|
+
if (Array.isArray(schema)) {
|
|
268
|
+
if (schema.length === 0) {
|
|
269
|
+
result.errors.push("JSON array is empty");
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
if (schema.length > 1) {
|
|
273
|
+
result.warnings.push(
|
|
274
|
+
`JSON file contains ${schema.length} definitions. Only processing the first one.`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
schema = schema[0];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Validate
|
|
281
|
+
const validationErrors = validateJsonSchema(schema);
|
|
282
|
+
|
|
283
|
+
// Separate errors and warnings
|
|
284
|
+
validationErrors.forEach((ve) => {
|
|
285
|
+
const msg = `${ve.field}: ${ve.message}`;
|
|
286
|
+
if (ve.severity === "error") {
|
|
287
|
+
result.errors.push(msg);
|
|
288
|
+
} else {
|
|
289
|
+
result.warnings.push(msg);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Only set data if validation passed
|
|
294
|
+
if (result.errors.length === 0) {
|
|
295
|
+
result.data = schema as JsonModuleDefinition;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Check if validation result passed (no errors)
|
|
303
|
+
*/
|
|
304
|
+
export function validationPassed(result: ValidationResult): boolean {
|
|
305
|
+
return result.errors.length === 0 && result.data !== null;
|
|
306
|
+
}
|