@wix/web50-cli 0.1.0 → 0.1.2
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/bin/web5.js +1 -2
- package/dist/cjs/auth/deviceFlow.js +175 -16
- package/dist/cjs/auth/deviceFlow.js.map +1 -1
- package/dist/cjs/auth/index.js +93 -10
- package/dist/cjs/auth/index.js.map +1 -1
- package/dist/cjs/auth/secretStore.js.map +1 -1
- package/dist/cjs/cli.js +29 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/bundle.js +103 -0
- package/dist/cjs/commands/bundle.js.map +1 -0
- package/dist/cjs/commands/conversation.js +50 -0
- package/dist/cjs/commands/conversation.js.map +1 -0
- package/dist/cjs/commands/conversationWizard.js +528 -0
- package/dist/cjs/commands/conversationWizard.js.map +1 -0
- package/dist/cjs/commands/deploy.js +237 -0
- package/dist/cjs/commands/deploy.js.map +1 -0
- package/dist/cjs/commands/ecom.js +239 -0
- package/dist/cjs/commands/ecom.js.map +1 -0
- package/dist/cjs/commands/embed.js +118 -0
- package/dist/cjs/commands/embed.js.map +1 -0
- package/dist/cjs/commands/init.js +65 -29
- package/dist/cjs/commands/init.js.map +1 -1
- package/dist/cjs/commands/instructions.js +456 -0
- package/dist/cjs/commands/instructions.js.map +1 -0
- package/dist/cjs/commands/login.js +63 -4
- package/dist/cjs/commands/login.js.map +1 -1
- package/dist/cjs/commands/logout.js +16 -0
- package/dist/cjs/commands/logout.js.map +1 -0
- package/dist/cjs/commands/serve.js +122 -0
- package/dist/cjs/commands/serve.js.map +1 -0
- package/dist/cjs/commands/storybook.js +102 -0
- package/dist/cjs/commands/storybook.js.map +1 -0
- package/dist/cjs/commands/validate.js +617 -17
- package/dist/cjs/commands/validate.js.map +1 -1
- package/dist/cjs/commands/whoami.js +48 -0
- package/dist/cjs/commands/whoami.js.map +1 -0
- package/dist/cjs/templates/aiInstructionsSchema.js +5 -1
- package/dist/cjs/templates/aiInstructionsSchema.js.map +1 -1
- package/dist/cjs/templates/cmsMappingSchema.js +132 -0
- package/dist/cjs/templates/cmsMappingSchema.js.map +1 -0
- package/dist/cjs/utils/print.js +12 -0
- package/dist/cjs/utils/print.js.map +1 -1
- package/dist/cjs/utils/project.js +24 -0
- package/dist/cjs/utils/project.js.map +1 -1
- package/dist/cjs/utils/wixApi.js +57 -0
- package/dist/cjs/utils/wixApi.js.map +1 -0
- package/dist/esm/auth/deviceFlow.js +182 -17
- package/dist/esm/auth/deviceFlow.js.map +1 -1
- package/dist/esm/auth/index.js +98 -11
- package/dist/esm/auth/index.js.map +1 -1
- package/dist/esm/auth/secretStore.js.map +1 -1
- package/dist/esm/cli.js +29 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/bundle.js +100 -0
- package/dist/esm/commands/bundle.js.map +1 -0
- package/dist/esm/commands/conversation.js +44 -0
- package/dist/esm/commands/conversation.js.map +1 -0
- package/dist/esm/commands/conversationWizard.js +529 -0
- package/dist/esm/commands/conversationWizard.js.map +1 -0
- package/dist/esm/commands/deploy.js +239 -0
- package/dist/esm/commands/deploy.js.map +1 -0
- package/dist/esm/commands/ecom.js +234 -0
- package/dist/esm/commands/ecom.js.map +1 -0
- package/dist/esm/commands/embed.js +112 -0
- package/dist/esm/commands/embed.js.map +1 -0
- package/dist/esm/commands/init.js +66 -30
- package/dist/esm/commands/init.js.map +1 -1
- package/dist/esm/commands/instructions.js +459 -0
- package/dist/esm/commands/instructions.js.map +1 -0
- package/dist/esm/commands/login.js +66 -6
- package/dist/esm/commands/login.js.map +1 -1
- package/dist/esm/commands/logout.js +12 -0
- package/dist/esm/commands/logout.js.map +1 -0
- package/dist/esm/commands/serve.js +117 -0
- package/dist/esm/commands/serve.js.map +1 -0
- package/dist/esm/commands/storybook.js +97 -0
- package/dist/esm/commands/storybook.js.map +1 -0
- package/dist/esm/commands/validate.js +623 -19
- package/dist/esm/commands/validate.js.map +1 -1
- package/dist/esm/commands/whoami.js +44 -0
- package/dist/esm/commands/whoami.js.map +1 -0
- package/dist/esm/templates/aiInstructionsSchema.js +5 -1
- package/dist/esm/templates/aiInstructionsSchema.js.map +1 -1
- package/dist/esm/templates/cmsMappingSchema.js +128 -0
- package/dist/esm/templates/cmsMappingSchema.js.map +1 -0
- package/dist/esm/utils/print.js +10 -0
- package/dist/esm/utils/print.js.map +1 -1
- package/dist/esm/utils/project.js +23 -0
- package/dist/esm/utils/project.js.map +1 -1
- package/dist/esm/utils/wixApi.js +53 -0
- package/dist/esm/utils/wixApi.js.map +1 -0
- package/dist/types/auth/deviceFlow.d.ts +3 -1
- package/dist/types/auth/deviceFlow.d.ts.map +1 -1
- package/dist/types/auth/index.d.ts +6 -1
- package/dist/types/auth/index.d.ts.map +1 -1
- package/dist/types/auth/secretStore.d.ts +2 -0
- package/dist/types/auth/secretStore.d.ts.map +1 -1
- package/dist/types/commands/bundle.d.ts +10 -0
- package/dist/types/commands/bundle.d.ts.map +1 -0
- package/dist/types/commands/conversation.d.ts +3 -0
- package/dist/types/commands/conversation.d.ts.map +1 -0
- package/dist/types/commands/conversationWizard.d.ts +3 -0
- package/dist/types/commands/conversationWizard.d.ts.map +1 -0
- package/dist/types/commands/deploy.d.ts +3 -0
- package/dist/types/commands/deploy.d.ts.map +1 -0
- package/dist/types/commands/ecom.d.ts +3 -0
- package/dist/types/commands/ecom.d.ts.map +1 -0
- package/dist/types/commands/embed.d.ts +3 -0
- package/dist/types/commands/embed.d.ts.map +1 -0
- package/dist/types/commands/init.d.ts.map +1 -1
- package/dist/types/commands/instructions.d.ts +3 -0
- package/dist/types/commands/instructions.d.ts.map +1 -0
- package/dist/types/commands/login.d.ts.map +1 -1
- package/dist/types/commands/logout.d.ts +3 -0
- package/dist/types/commands/logout.d.ts.map +1 -0
- package/dist/types/commands/serve.d.ts +3 -0
- package/dist/types/commands/serve.d.ts.map +1 -0
- package/dist/types/commands/storybook.d.ts +3 -0
- package/dist/types/commands/storybook.d.ts.map +1 -0
- package/dist/types/commands/validate.d.ts +7 -0
- package/dist/types/commands/validate.d.ts.map +1 -1
- package/dist/types/commands/whoami.d.ts +3 -0
- package/dist/types/commands/whoami.d.ts.map +1 -0
- package/dist/types/templates/aiInstructionsSchema.d.ts.map +1 -1
- package/dist/types/templates/cmsMappingSchema.d.ts +2 -0
- package/dist/types/templates/cmsMappingSchema.d.ts.map +1 -0
- package/dist/types/utils/print.d.ts +3 -0
- package/dist/types/utils/print.d.ts.map +1 -1
- package/dist/types/utils/project.d.ts +12 -0
- package/dist/types/utils/project.d.ts.map +1 -1
- package/dist/types/utils/wixApi.d.ts +9 -0
- package/dist/types/utils/wixApi.d.ts.map +1 -0
- package/package.json +5 -5
- package/defaults/package.json +0 -42
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
exports.__esModule = true;
|
|
5
|
+
exports.runValidation = runValidation;
|
|
5
6
|
exports.validateCommand = void 0;
|
|
6
7
|
var _commander = require("commander");
|
|
7
8
|
var _2 = _interopRequireDefault(require("ajv/dist/2020"));
|
|
@@ -11,6 +12,7 @@ var path = _interopRequireWildcard(require("path"));
|
|
|
11
12
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
12
13
|
var _print = require("../utils/print");
|
|
13
14
|
var _cmsSchemaSchema = require("../templates/cmsSchemaSchema");
|
|
15
|
+
var _cmsMappingSchema = require("../templates/cmsMappingSchema");
|
|
14
16
|
var _aiInstructionsSchema = require("../templates/aiInstructionsSchema");
|
|
15
17
|
var _actionYamlSchema = require("../templates/actionYamlSchema");
|
|
16
18
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -18,6 +20,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
18
20
|
|
|
19
21
|
const SCHEMAS = {
|
|
20
22
|
'cms-schema.yaml': (0, _jsYaml.load)((0, _cmsSchemaSchema.cmsSchemaSchemaTemplate)()),
|
|
23
|
+
'cms-mapping.yaml': (0, _jsYaml.load)((0, _cmsMappingSchema.cmsMappingSchemaTemplate)()),
|
|
21
24
|
'prompt-instructions.yaml': (0, _jsYaml.load)((0, _aiInstructionsSchema.aiInstructionsSchemaTemplate)()),
|
|
22
25
|
'action.yaml': (0, _jsYaml.load)((0, _actionYamlSchema.actionYamlSchemaTemplate)())
|
|
23
26
|
};
|
|
@@ -295,6 +298,91 @@ function buildCmsSchemaDomain(root) {
|
|
|
295
298
|
}]
|
|
296
299
|
};
|
|
297
300
|
}
|
|
301
|
+
function buildCmsMappingDomain(root) {
|
|
302
|
+
const filePath = path.join(root, 'src', 'configuration', 'cms', 'cms-mapping.yaml');
|
|
303
|
+
const fileName = 'cms-mapping.yaml';
|
|
304
|
+
const ajv = new _2.default({
|
|
305
|
+
allErrors: true,
|
|
306
|
+
strict: false
|
|
307
|
+
});
|
|
308
|
+
const schema = SCHEMAS[fileName];
|
|
309
|
+
let parsed;
|
|
310
|
+
let fileExists = false;
|
|
311
|
+
let yamlOk = false;
|
|
312
|
+
return {
|
|
313
|
+
name: 'CMS Mapping',
|
|
314
|
+
checks: [{
|
|
315
|
+
label: `${fileName} — file exists`,
|
|
316
|
+
run() {
|
|
317
|
+
fileExists = (0, _fs.existsSync)(filePath);
|
|
318
|
+
if (fileExists) {
|
|
319
|
+
return {
|
|
320
|
+
ok: true,
|
|
321
|
+
errors: []
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
ok: false,
|
|
326
|
+
errors: [{
|
|
327
|
+
message: `File not found: ${filePath}`,
|
|
328
|
+
fix: `Create 'src/configuration/cms/cms-mapping.yaml' in your project.`
|
|
329
|
+
}]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}, {
|
|
333
|
+
label: `${fileName} — valid YAML`,
|
|
334
|
+
run() {
|
|
335
|
+
if (!fileExists) {
|
|
336
|
+
return {
|
|
337
|
+
ok: false,
|
|
338
|
+
errors: [{
|
|
339
|
+
message: 'skipped — file does not exist'
|
|
340
|
+
}]
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const result = tryParseYaml(filePath);
|
|
344
|
+
if (result.err !== undefined) {
|
|
345
|
+
return {
|
|
346
|
+
ok: false,
|
|
347
|
+
errors: [{
|
|
348
|
+
message: result.err,
|
|
349
|
+
fix: 'Fix the YAML syntax error above.'
|
|
350
|
+
}]
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
parsed = result.data;
|
|
354
|
+
yamlOk = true;
|
|
355
|
+
return {
|
|
356
|
+
ok: true,
|
|
357
|
+
errors: []
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}, {
|
|
361
|
+
label: `${fileName} — schema valid`,
|
|
362
|
+
run() {
|
|
363
|
+
if (!yamlOk) {
|
|
364
|
+
return {
|
|
365
|
+
ok: false,
|
|
366
|
+
errors: [{
|
|
367
|
+
message: 'skipped — YAML parse failed'
|
|
368
|
+
}]
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const validate = ajv.compile(schema);
|
|
372
|
+
if (validate(parsed)) {
|
|
373
|
+
return {
|
|
374
|
+
ok: true,
|
|
375
|
+
errors: []
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
ok: false,
|
|
380
|
+
errors: (validate.errors ?? []).map(e => formatAjvError(e))
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}]
|
|
384
|
+
};
|
|
385
|
+
}
|
|
298
386
|
function buildActionsDomain(root) {
|
|
299
387
|
const actionsDir = path.join(root, 'src', 'actions');
|
|
300
388
|
const registryPath = path.join(root, 'src', 'createRegistry.ts');
|
|
@@ -668,27 +756,275 @@ function buildActionsDomain(root) {
|
|
|
668
756
|
};
|
|
669
757
|
}
|
|
670
758
|
|
|
671
|
-
// ──
|
|
759
|
+
// ── Sections helpers ──────────────────────────────────────────────────────────
|
|
672
760
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
761
|
+
/**
|
|
762
|
+
* Extracts the text of every `.register(...)` call that has a component array.
|
|
763
|
+
* Uses bracket-counting to handle nesting correctly.
|
|
764
|
+
* Skips system registrations that have no `[` (e.g. SkipNodesSectionDefinition).
|
|
765
|
+
*/
|
|
766
|
+
function extractRegisterBlocks(content) {
|
|
767
|
+
const blocks = [];
|
|
768
|
+
const marker = '.register(';
|
|
769
|
+
let searchFrom = 0;
|
|
770
|
+
let start = content.indexOf(marker, searchFrom);
|
|
771
|
+
while (start !== -1) {
|
|
772
|
+
let depth = 0;
|
|
773
|
+
let i = start + marker.length - 1; // position of the opening '('
|
|
774
|
+
while (i < content.length) {
|
|
775
|
+
const ch = content[i];
|
|
776
|
+
if (ch === '(') {
|
|
777
|
+
depth++;
|
|
778
|
+
} else if (ch === ')') {
|
|
779
|
+
depth--;
|
|
780
|
+
if (depth === 0) {
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
i++;
|
|
785
|
+
}
|
|
786
|
+
const block = content.slice(start, i + 1);
|
|
787
|
+
if (block.includes('[')) {
|
|
788
|
+
blocks.push(block);
|
|
789
|
+
}
|
|
790
|
+
searchFrom = i + 1;
|
|
791
|
+
start = content.indexOf(marker, searchFrom);
|
|
678
792
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
793
|
+
return blocks;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Scans all import lines that reference a `sections/` path and returns a
|
|
798
|
+
* Map<localName, stem>. Handles aliases and multi-name imports:
|
|
799
|
+
* import { KpiSection as KpiSectionComponent } from '...sections/KpiSection'
|
|
800
|
+
* → "KpiSectionComponent" → "KpiSection"
|
|
801
|
+
*/
|
|
802
|
+
function buildSectionImportMap(content) {
|
|
803
|
+
const map = new Map();
|
|
804
|
+
const re = /import\s*\{([^}]+)\}\s*from\s*['"][^'"]*sections\/(\w+)['"]/g;
|
|
805
|
+
let m = re.exec(content);
|
|
806
|
+
while (m !== null) {
|
|
807
|
+
const stem = m[2];
|
|
808
|
+
const names = m[1].split(',');
|
|
809
|
+
for (const raw of names) {
|
|
810
|
+
const token = raw.trim();
|
|
811
|
+
if (!token) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const asParts = token.split(/\s+as\s+/);
|
|
815
|
+
const localName = (asParts[1] ?? asParts[0]).trim();
|
|
816
|
+
if (localName) {
|
|
817
|
+
map.set(localName, stem);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
m = re.exec(content);
|
|
821
|
+
}
|
|
822
|
+
return map;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// ── Sections domain ────────────────────────────────────────────────────────────
|
|
826
|
+
|
|
827
|
+
function buildSectionsDomain(root) {
|
|
828
|
+
const registryPath = path.join(root, 'src', 'createRegistry.ts');
|
|
829
|
+
const sectionsDir = path.join(root, 'src', 'components', 'sections');
|
|
830
|
+
let registryExists = false;
|
|
831
|
+
let registryContent = '';
|
|
832
|
+
let tsxFiles = [];
|
|
833
|
+
return {
|
|
834
|
+
name: 'Sections',
|
|
835
|
+
checks: [
|
|
836
|
+
// ── Check 1: createRegistry.ts exists ────────────────────────────────
|
|
837
|
+
{
|
|
838
|
+
label: 'createRegistry.ts — file exists',
|
|
839
|
+
run() {
|
|
840
|
+
registryExists = (0, _fs.existsSync)(registryPath);
|
|
841
|
+
if (!registryExists) {
|
|
842
|
+
return {
|
|
843
|
+
ok: false,
|
|
844
|
+
errors: [{
|
|
845
|
+
message: 'src/createRegistry.ts not found',
|
|
846
|
+
fix: 'Create src/createRegistry.ts and export a createRegistry() function that registers your section components.'
|
|
847
|
+
}]
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
registryContent = readText(registryPath);
|
|
851
|
+
tsxFiles = (0, _fs.existsSync)(sectionsDir) ? (0, _fs.readdirSync)(sectionsDir).filter(f => f.endsWith('.tsx')) : [];
|
|
852
|
+
return {
|
|
853
|
+
ok: true,
|
|
854
|
+
errors: []
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
// ── Check 2: all .tsx files imported in createRegistry.ts ─────────────
|
|
859
|
+
{
|
|
860
|
+
label: 'src/components/sections/ — all .tsx files imported in createRegistry.ts',
|
|
861
|
+
run() {
|
|
862
|
+
if (!registryExists) {
|
|
863
|
+
return {
|
|
864
|
+
ok: false,
|
|
865
|
+
errors: [{
|
|
866
|
+
message: 'skipped — createRegistry.ts not found'
|
|
867
|
+
}]
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
const errors = [];
|
|
871
|
+
for (const file of tsxFiles) {
|
|
872
|
+
const stem = path.basename(file, '.tsx');
|
|
873
|
+
if (!registryContent.includes(`sections/${stem}`)) {
|
|
874
|
+
errors.push({
|
|
875
|
+
message: `'${stem}.tsx' is not imported in src/createRegistry.ts`,
|
|
876
|
+
fix: `Add: import { ${stem} } from './components/sections/${stem}';`
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return errors.length === 0 ? {
|
|
881
|
+
ok: true,
|
|
882
|
+
errors: []
|
|
883
|
+
} : {
|
|
884
|
+
ok: false,
|
|
885
|
+
errors
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
// ── Check 3: all .tsx files wired into a .register() call ─────────────
|
|
890
|
+
{
|
|
891
|
+
label: 'src/components/sections/ — all .tsx files wired into .register()',
|
|
892
|
+
run() {
|
|
893
|
+
if (!registryExists) {
|
|
894
|
+
return {
|
|
895
|
+
ok: false,
|
|
896
|
+
errors: [{
|
|
897
|
+
message: 'skipped — createRegistry.ts not found'
|
|
898
|
+
}]
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const importMap = buildSectionImportMap(registryContent);
|
|
902
|
+
const blocks = extractRegisterBlocks(registryContent);
|
|
903
|
+
const blockText = blocks.join('\n');
|
|
904
|
+
const errors = [];
|
|
905
|
+
for (const file of tsxFiles) {
|
|
906
|
+
var _find;
|
|
907
|
+
const stem = path.basename(file, '.tsx');
|
|
908
|
+
// Find the local name for this stem (may have been aliased on import)
|
|
909
|
+
const localName = ((_find = [...importMap.entries()].find(([, s]) => s === stem)) == null ? void 0 : _find[0]) ?? stem;
|
|
910
|
+
if (!blockText.includes(localName)) {
|
|
911
|
+
errors.push({
|
|
912
|
+
message: `'${localName}' (${stem}.tsx) is imported but not passed to any .register() call`,
|
|
913
|
+
fix: `Add { component: ${localName} } inside a .register(new <Definition>(), [...]) call in src/createRegistry.ts.`
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return errors.length === 0 ? {
|
|
918
|
+
ok: true,
|
|
919
|
+
errors: []
|
|
920
|
+
} : {
|
|
921
|
+
ok: false,
|
|
922
|
+
errors
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
// ── Check 4: every .register() block has a catch-all entry ────────────
|
|
927
|
+
{
|
|
928
|
+
label: 'createRegistry.ts — all .register() blocks have a catch-all component',
|
|
929
|
+
run() {
|
|
930
|
+
if (!registryExists) {
|
|
931
|
+
return {
|
|
932
|
+
ok: false,
|
|
933
|
+
errors: [{
|
|
934
|
+
message: 'skipped — createRegistry.ts not found'
|
|
935
|
+
}]
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
const blocks = extractRegisterBlocks(registryContent);
|
|
939
|
+
const errors = [];
|
|
940
|
+
for (const block of blocks) {
|
|
941
|
+
const defMatch = block.match(/\.register\(\s*new\s+(\w+)/);
|
|
942
|
+
const defName = defMatch ? defMatch[1] : '(unknown)';
|
|
943
|
+
|
|
944
|
+
// Split on "{ component:" to isolate each entry
|
|
945
|
+
const entries = block.split('{ component:').slice(1);
|
|
946
|
+
if (entries.length === 0) {
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const allHaveIntent = entries.every(entry => entry.includes('intent:'));
|
|
950
|
+
if (allHaveIntent) {
|
|
951
|
+
errors.push({
|
|
952
|
+
message: `'${defName}' has no catch-all component — every entry specifies an intent`,
|
|
953
|
+
fix: `Add a catch-all entry to the .register(new ${defName}(), [...]) call:\n { component: YourDefaultComponent }`
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return errors.length === 0 ? {
|
|
958
|
+
ok: true,
|
|
959
|
+
errors: []
|
|
960
|
+
} : {
|
|
961
|
+
ok: false,
|
|
962
|
+
errors
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
// ── Check 5: all component refs in .register() resolve to a .tsx file ─
|
|
967
|
+
{
|
|
968
|
+
label: 'createRegistry.ts — all .register() component references resolve to a .tsx file',
|
|
969
|
+
run() {
|
|
970
|
+
if (!registryExists) {
|
|
971
|
+
return {
|
|
972
|
+
ok: false,
|
|
973
|
+
errors: [{
|
|
974
|
+
message: 'skipped — createRegistry.ts not found'
|
|
975
|
+
}]
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
const importMap = buildSectionImportMap(registryContent);
|
|
979
|
+
const blocks = extractRegisterBlocks(registryContent);
|
|
980
|
+
const errors = [];
|
|
981
|
+
for (const block of blocks) {
|
|
982
|
+
const refs = [...block.matchAll(/\{\s*component:\s*(\w+)/g)].map(m => m[1]);
|
|
983
|
+
for (const ref of refs) {
|
|
984
|
+
const stem = importMap.get(ref);
|
|
985
|
+
if (stem === undefined) {
|
|
986
|
+
errors.push({
|
|
987
|
+
message: `Component '${ref}' used in .register() has no matching import from sections/`,
|
|
988
|
+
fix: `Add: import { ${ref} } from './components/sections/${ref}'; or check the import alias.`
|
|
989
|
+
});
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
const filePath = path.join(sectionsDir, `${stem}.tsx`);
|
|
993
|
+
if (!(0, _fs.existsSync)(filePath)) {
|
|
994
|
+
errors.push({
|
|
995
|
+
message: `Component '${ref}' maps to '${stem}.tsx' which does not exist in src/components/sections/`,
|
|
996
|
+
fix: `Create src/components/sections/${stem}.tsx, or remove the stale .register() entry from src/createRegistry.ts.`
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return errors.length === 0 ? {
|
|
1002
|
+
ok: true,
|
|
1003
|
+
errors: []
|
|
1004
|
+
} : {
|
|
1005
|
+
ok: false,
|
|
1006
|
+
errors
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
}]
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// ── Rendering ─────────────────────────────────────────────────────────────────
|
|
1014
|
+
|
|
1015
|
+
function printDomain(domain, results, verbose) {
|
|
1016
|
+
const failed = results.filter(r => !r.result.ok);
|
|
1017
|
+
const domainOk = failed.length === 0;
|
|
1018
|
+
if (verbose) {
|
|
684
1019
|
console.log(domain.name);
|
|
685
|
-
for (const
|
|
686
|
-
|
|
1020
|
+
for (const {
|
|
1021
|
+
label,
|
|
1022
|
+
result
|
|
1023
|
+
} of results) {
|
|
687
1024
|
if (result.ok) {
|
|
688
|
-
console.log(_chalk.default.green(` \u2714 ${
|
|
1025
|
+
console.log(_chalk.default.green(` \u2714 ${label}`));
|
|
689
1026
|
} else {
|
|
690
|
-
|
|
691
|
-
process.stderr.write(_chalk.default.red(` \u2716 ${check.label}\n`));
|
|
1027
|
+
process.stderr.write(_chalk.default.red(` \u2716 ${label}\n`));
|
|
692
1028
|
for (const e_ of result.errors) {
|
|
693
1029
|
process.stderr.write(` ${e_.message}\n`);
|
|
694
1030
|
if (e_.fix) {
|
|
@@ -698,9 +1034,273 @@ const validateCommand = exports.validateCommand = new _commander.Command('valida
|
|
|
698
1034
|
}
|
|
699
1035
|
}
|
|
700
1036
|
console.log('');
|
|
1037
|
+
return domainOk;
|
|
701
1038
|
}
|
|
702
|
-
|
|
1039
|
+
|
|
1040
|
+
// Slim mode
|
|
1041
|
+
if (domainOk) {
|
|
1042
|
+
console.log(_chalk.default.green(`\u2714 ${domain.name} — valid`));
|
|
1043
|
+
return true;
|
|
1044
|
+
}
|
|
1045
|
+
console.log(domain.name);
|
|
1046
|
+
for (const {
|
|
1047
|
+
label,
|
|
1048
|
+
result
|
|
1049
|
+
} of failed) {
|
|
1050
|
+
process.stderr.write(_chalk.default.red(` \u2716 ${label}\n`));
|
|
1051
|
+
for (const e_ of result.errors) {
|
|
1052
|
+
process.stderr.write(` ${e_.message}\n`);
|
|
1053
|
+
if (e_.fix) {
|
|
1054
|
+
process.stderr.write(` Fix: ${e_.fix}\n`);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
console.log('');
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// ── JSON output ───────────────────────────────────────────────────────────────
|
|
1063
|
+
|
|
1064
|
+
function collectDomainJson(domain, results) {
|
|
1065
|
+
return {
|
|
1066
|
+
name: domain.name,
|
|
1067
|
+
ok: results.every(r => r.result.ok),
|
|
1068
|
+
checks: results.map(({
|
|
1069
|
+
label,
|
|
1070
|
+
result
|
|
1071
|
+
}) => ({
|
|
1072
|
+
label,
|
|
1073
|
+
ok: result.ok,
|
|
1074
|
+
errors: result.errors
|
|
1075
|
+
}))
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// ── Build scripts domain ──────────────────────────────────────────────────────
|
|
1080
|
+
|
|
1081
|
+
function buildScriptsDomain(root) {
|
|
1082
|
+
const pkgPath = path.join(root, 'package.json');
|
|
1083
|
+
let pkg = {};
|
|
1084
|
+
let fileExists = false;
|
|
1085
|
+
return {
|
|
1086
|
+
name: 'Build Scripts',
|
|
1087
|
+
checks: [{
|
|
1088
|
+
label: 'package.json — file exists',
|
|
1089
|
+
run() {
|
|
1090
|
+
fileExists = (0, _fs.existsSync)(pkgPath);
|
|
1091
|
+
if (!fileExists) {
|
|
1092
|
+
return {
|
|
1093
|
+
ok: false,
|
|
1094
|
+
errors: [{
|
|
1095
|
+
message: `File not found: ${pkgPath}`,
|
|
1096
|
+
fix: `Create a 'package.json' at the root of your project.`
|
|
1097
|
+
}]
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
pkg = JSON.parse((0, _fs.readFileSync)(pkgPath, 'utf8'));
|
|
1102
|
+
} catch (e) {
|
|
1103
|
+
fileExists = false;
|
|
1104
|
+
return {
|
|
1105
|
+
ok: false,
|
|
1106
|
+
errors: [{
|
|
1107
|
+
message: `Failed to parse package.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
1108
|
+
fix: `Fix the JSON syntax in your package.json.`
|
|
1109
|
+
}]
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
return {
|
|
1113
|
+
ok: true,
|
|
1114
|
+
errors: []
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
}, {
|
|
1118
|
+
label: 'package.json — bundle script defined',
|
|
1119
|
+
run() {
|
|
1120
|
+
var _pkg$scripts;
|
|
1121
|
+
if (!fileExists) {
|
|
1122
|
+
return {
|
|
1123
|
+
ok: false,
|
|
1124
|
+
errors: [{
|
|
1125
|
+
message: 'skipped — package.json not readable'
|
|
1126
|
+
}]
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
if (typeof ((_pkg$scripts = pkg.scripts) == null ? void 0 : _pkg$scripts.bundle) === 'string' && pkg.scripts.bundle.length > 0) {
|
|
1130
|
+
return {
|
|
1131
|
+
ok: true,
|
|
1132
|
+
errors: []
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
return {
|
|
1136
|
+
ok: false,
|
|
1137
|
+
errors: [{
|
|
1138
|
+
message: `Missing 'bundle' script in package.json`,
|
|
1139
|
+
fix: `Add a 'bundle' script to your package.json scripts, e.g.:\n "bundle": "vite build --config src/vite.cdn.config.ts"`
|
|
1140
|
+
}]
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
}, {
|
|
1144
|
+
label: 'package.json — build script defined',
|
|
1145
|
+
run() {
|
|
1146
|
+
var _pkg$scripts2;
|
|
1147
|
+
if (!fileExists) {
|
|
1148
|
+
return {
|
|
1149
|
+
ok: false,
|
|
1150
|
+
errors: [{
|
|
1151
|
+
message: 'skipped — package.json not readable'
|
|
1152
|
+
}]
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
if (typeof ((_pkg$scripts2 = pkg.scripts) == null ? void 0 : _pkg$scripts2.build) === 'string' && pkg.scripts.build.length > 0) {
|
|
1156
|
+
return {
|
|
1157
|
+
ok: true,
|
|
1158
|
+
errors: []
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
ok: false,
|
|
1163
|
+
errors: [{
|
|
1164
|
+
message: `Missing 'build' script in package.json`,
|
|
1165
|
+
fix: `Add a 'build' script to your package.json scripts, e.g.:\n "build": "tsc"`
|
|
1166
|
+
}]
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
}]
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// ── Domain selection ──────────────────────────────────────────────────────────
|
|
1174
|
+
|
|
1175
|
+
const DOMAIN_NAME_MAP = {
|
|
1176
|
+
ai: 'AI Instructions',
|
|
1177
|
+
cms: 'CMS Schema',
|
|
1178
|
+
'cms-mapping': 'CMS Mapping',
|
|
1179
|
+
scripts: 'Build Scripts',
|
|
1180
|
+
actions: 'Actions',
|
|
1181
|
+
sections: 'Sections'
|
|
1182
|
+
};
|
|
1183
|
+
function selectDomains(root, filter) {
|
|
1184
|
+
const all = [buildAiInstructionsDomain(root), buildCmsSchemaDomain(root), buildCmsMappingDomain(root), buildActionsDomain(root), buildScriptsDomain(root), buildSectionsDomain(root)];
|
|
1185
|
+
if (!filter) {
|
|
1186
|
+
return all;
|
|
1187
|
+
}
|
|
1188
|
+
const filters = Array.isArray(filter) ? filter : [filter];
|
|
1189
|
+
const names = new Set(filters.map(f => DOMAIN_NAME_MAP[f]));
|
|
1190
|
+
return all.filter(d => names.has(d.name));
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// ── Core runner ───────────────────────────────────────────────────────────────
|
|
1194
|
+
|
|
1195
|
+
function runDomains(domains, verbose, format) {
|
|
1196
|
+
let hasErrors = false;
|
|
1197
|
+
const jsonResults = [];
|
|
1198
|
+
for (const domain of domains) {
|
|
1199
|
+
const results = domain.checks.map(check => ({
|
|
1200
|
+
label: check.label,
|
|
1201
|
+
result: check.run()
|
|
1202
|
+
}));
|
|
1203
|
+
let domainOk;
|
|
1204
|
+
if (format === 'json') {
|
|
1205
|
+
const jr = collectDomainJson(domain, results);
|
|
1206
|
+
jsonResults.push(jr);
|
|
1207
|
+
domainOk = jr.ok;
|
|
1208
|
+
} else {
|
|
1209
|
+
domainOk = printDomain(domain, results, verbose);
|
|
1210
|
+
}
|
|
1211
|
+
if (!domainOk) {
|
|
1212
|
+
hasErrors = true;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return {
|
|
1216
|
+
ok: !hasErrors,
|
|
1217
|
+
jsonResults
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// ── Programmatic API ─────────────────────────────────────────────────────────
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Runs validation domains against the given project root.
|
|
1225
|
+
* Pass `filters` to validate only specific domains; omit to validate all.
|
|
1226
|
+
* Prints results in slim mode and returns true if everything passes.
|
|
1227
|
+
*/
|
|
1228
|
+
function runValidation(root, filters) {
|
|
1229
|
+
const domains = selectDomains(root, filters);
|
|
1230
|
+
const {
|
|
1231
|
+
ok
|
|
1232
|
+
} = runDomains(domains, false, 'text');
|
|
1233
|
+
return ok;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// ── Watch mode ────────────────────────────────────────────────────────────────
|
|
1237
|
+
|
|
1238
|
+
function startWatch(root, run) {
|
|
1239
|
+
const watchDirs = [path.join(root, 'src', 'configuration'), path.join(root, 'src', 'actions'), path.join(root, 'src', 'components', 'sections'), path.join(root, 'src', 'createRegistry.ts')].filter(d => (0, _fs.existsSync)(d));
|
|
1240
|
+
if (watchDirs.length === 0) {
|
|
1241
|
+
(0, _print.error)('No directories to watch found.');
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
let debounce = null;
|
|
1245
|
+
const trigger = () => {
|
|
1246
|
+
if (debounce) {
|
|
1247
|
+
clearTimeout(debounce);
|
|
1248
|
+
}
|
|
1249
|
+
debounce = setTimeout(() => {
|
|
1250
|
+
console.clear();
|
|
1251
|
+
(0, _print.info)(`[${new Date().toLocaleTimeString()}] Re-validating...`);
|
|
1252
|
+
console.log('');
|
|
1253
|
+
run();
|
|
1254
|
+
}, 200);
|
|
1255
|
+
};
|
|
1256
|
+
for (const dir of watchDirs) {
|
|
1257
|
+
(0, _fs.watch)(dir, {
|
|
1258
|
+
recursive: true
|
|
1259
|
+
}, trigger);
|
|
1260
|
+
}
|
|
1261
|
+
(0, _print.info)(`Watching for changes in ${watchDirs.map(d => path.relative(root, d)).join(', ')}...`);
|
|
1262
|
+
(0, _print.info)('Press Ctrl+C to stop.');
|
|
1263
|
+
console.log('');
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// ── Command ───────────────────────────────────────────────────────────────────
|
|
1267
|
+
|
|
1268
|
+
const validateCommand = exports.validateCommand = new _commander.Command('validate').description('Validate project config files against their schemas').option('--verbose', 'Show all checks, including passing ones').option('--domain <domain>', 'Only validate one domain: ai, cms, actions, scripts, or sections').option('--format <format>', 'Output format: text (default) or json', 'text').option('--watch', 'Re-validate on file changes (TTY only)').option('--strict', 'Exit with error even on warnings').option('--project <path>', 'Explicit project root (overrides cwd auto-detection)').action(opts => {
|
|
1269
|
+
const verbose = Boolean(opts.verbose);
|
|
1270
|
+
const format = opts.format ?? 'text';
|
|
1271
|
+
const domainFilter = opts.domain;
|
|
1272
|
+
const root = opts.project ? path.resolve(opts.project) : findProjectRoot(process.cwd());
|
|
1273
|
+
if (!root) {
|
|
1274
|
+
(0, _print.error)('No web5.config.json found — run this command from inside a Web5 project');
|
|
703
1275
|
process.exit(1);
|
|
704
1276
|
}
|
|
1277
|
+
if (format === 'text') {
|
|
1278
|
+
(0, _print.info)(`Project root: ${root}`);
|
|
1279
|
+
console.log('');
|
|
1280
|
+
}
|
|
1281
|
+
const run = () => {
|
|
1282
|
+
const domains = selectDomains(root, domainFilter);
|
|
1283
|
+
const {
|
|
1284
|
+
ok,
|
|
1285
|
+
jsonResults
|
|
1286
|
+
} = runDomains(domains, verbose, format);
|
|
1287
|
+
if (format === 'json') {
|
|
1288
|
+
console.log(JSON.stringify({
|
|
1289
|
+
ok,
|
|
1290
|
+
domains: jsonResults
|
|
1291
|
+
}, null, 2));
|
|
1292
|
+
}
|
|
1293
|
+
if (!ok && !opts.watch) {
|
|
1294
|
+
process.exit(1);
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
run();
|
|
1298
|
+
if (opts.watch) {
|
|
1299
|
+
if (!process.stdout.isTTY) {
|
|
1300
|
+
(0, _print.error)('--watch requires a TTY (interactive terminal).');
|
|
1301
|
+
process.exit(1);
|
|
1302
|
+
}
|
|
1303
|
+
startWatch(root, run);
|
|
1304
|
+
}
|
|
705
1305
|
});
|
|
706
1306
|
//# sourceMappingURL=validate.js.map
|