@object-ui/cli 17.6.0 → 17.7.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/CHANGELOG.md +2107 -0
- package/README.md +9 -24
- package/dist/{chunk-AP2INOE7.js → chunk-VMMIGYU3.js} +79 -80
- package/dist/chunk-VMMIGYU3.js.map +1 -0
- package/dist/cli.js +497 -506
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +7 -6
- package/dist/chunk-AP2INOE7.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
reportProjectSource,
|
|
9
9
|
resolveProjectSource,
|
|
10
10
|
serve
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VMMIGYU3.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { Command } from "commander";
|
|
15
|
-
import
|
|
15
|
+
import chalk9 from "chalk";
|
|
16
16
|
|
|
17
17
|
// src/commands/dev.ts
|
|
18
18
|
import { createServer } from "vite";
|
|
@@ -212,108 +212,10 @@ Make sure you have a valid production build.`
|
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
// src/commands/lint.ts
|
|
216
|
-
import { execSync as execSync3 } from "child_process";
|
|
217
|
-
import { existsSync as existsSync3 } from "fs";
|
|
218
|
-
import { join as join4 } from "path";
|
|
219
|
-
import chalk4 from "chalk";
|
|
220
|
-
async function lint(options) {
|
|
221
|
-
const cwd = process.cwd();
|
|
222
|
-
console.log(chalk4.blue("\u{1F50D} Running linter...\n"));
|
|
223
|
-
const tmpDir = join4(cwd, ".objectui-tmp");
|
|
224
|
-
const hasTempApp = existsSync3(tmpDir);
|
|
225
|
-
if (!hasTempApp) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
"No Object UI application found. Run 'objectui dev' first to generate the application."
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
const packageJsonPath = join4(tmpDir, "package.json");
|
|
231
|
-
const nodeModulesPath = join4(tmpDir, "node_modules");
|
|
232
|
-
if (!existsSync3(packageJsonPath) || !existsSync3(nodeModulesPath)) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
"Dependencies not installed. Run 'objectui dev' first to set up the application."
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
const fixFlag = options.fix ? "--fix" : "";
|
|
239
|
-
const command = `npx eslint "src/**/*.{js,jsx,ts,tsx}" ${fixFlag}`.trim();
|
|
240
|
-
console.log(chalk4.dim(` Running: ${command}
|
|
241
|
-
`));
|
|
242
|
-
execSync3(command, {
|
|
243
|
-
cwd: tmpDir,
|
|
244
|
-
stdio: "inherit"
|
|
245
|
-
});
|
|
246
|
-
console.log();
|
|
247
|
-
console.log(chalk4.green("\u2713 Linting completed successfully!"));
|
|
248
|
-
console.log();
|
|
249
|
-
} catch {
|
|
250
|
-
console.log();
|
|
251
|
-
console.log(chalk4.yellow("\u26A0 Linting found issues."));
|
|
252
|
-
if (!options.fix) {
|
|
253
|
-
console.log(chalk4.dim(" Run 'objectui lint --fix' to automatically fix some issues."));
|
|
254
|
-
}
|
|
255
|
-
console.log();
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// src/commands/test.ts
|
|
261
|
-
import { execSync as execSync4 } from "child_process";
|
|
262
|
-
import { existsSync as existsSync4 } from "fs";
|
|
263
|
-
import { join as join5 } from "path";
|
|
264
|
-
import chalk5 from "chalk";
|
|
265
|
-
async function test(options) {
|
|
266
|
-
const cwd = process.cwd();
|
|
267
|
-
console.log(chalk5.blue("\u{1F9EA} Running tests...\n"));
|
|
268
|
-
const tmpDir = join5(cwd, ".objectui-tmp");
|
|
269
|
-
const hasTempApp = existsSync4(tmpDir);
|
|
270
|
-
if (!hasTempApp) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
"No Object UI application found. Run 'objectui dev' first to generate the application."
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
const packageJsonPath = join5(tmpDir, "package.json");
|
|
276
|
-
const nodeModulesPath = join5(tmpDir, "node_modules");
|
|
277
|
-
if (!existsSync4(packageJsonPath) || !existsSync4(nodeModulesPath)) {
|
|
278
|
-
throw new Error(
|
|
279
|
-
"Dependencies not installed. Run 'objectui dev' first to set up the application."
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
try {
|
|
283
|
-
let command = "npx vitest";
|
|
284
|
-
if (options.watch) {
|
|
285
|
-
command += " --watch";
|
|
286
|
-
} else if (options.ui) {
|
|
287
|
-
command += " --ui";
|
|
288
|
-
} else {
|
|
289
|
-
command += " run";
|
|
290
|
-
}
|
|
291
|
-
if (options.coverage) {
|
|
292
|
-
command += " --coverage";
|
|
293
|
-
}
|
|
294
|
-
console.log(chalk5.dim(` Running: ${command}
|
|
295
|
-
`));
|
|
296
|
-
execSync4(command, {
|
|
297
|
-
cwd: tmpDir,
|
|
298
|
-
stdio: "inherit"
|
|
299
|
-
});
|
|
300
|
-
if (!options.watch && !options.ui) {
|
|
301
|
-
console.log();
|
|
302
|
-
console.log(chalk5.green("\u2713 Tests completed successfully!"));
|
|
303
|
-
console.log();
|
|
304
|
-
}
|
|
305
|
-
} catch {
|
|
306
|
-
console.log();
|
|
307
|
-
console.log(chalk5.red("\u2717 Some tests failed."));
|
|
308
|
-
console.log();
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
215
|
// src/commands/generate.ts
|
|
314
|
-
import
|
|
315
|
-
import { existsSync as
|
|
316
|
-
import { join as
|
|
216
|
+
import chalk4 from "chalk";
|
|
217
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
|
|
218
|
+
import { join as join4 } from "path";
|
|
317
219
|
async function generate(type, name) {
|
|
318
220
|
const cwd = process.cwd();
|
|
319
221
|
switch (type) {
|
|
@@ -328,18 +230,18 @@ async function generate(type, name) {
|
|
|
328
230
|
generatePlugin(cwd, name);
|
|
329
231
|
break;
|
|
330
232
|
default:
|
|
331
|
-
console.log(
|
|
233
|
+
console.log(chalk4.red(`Unknown type: ${type}`));
|
|
332
234
|
console.log(`Available types: resource, page, plugin`);
|
|
333
235
|
process.exit(1);
|
|
334
236
|
}
|
|
335
237
|
}
|
|
336
238
|
function generateResource(cwd, name) {
|
|
337
|
-
const dir =
|
|
338
|
-
if (!
|
|
239
|
+
const dir = join4(cwd, "objects");
|
|
240
|
+
if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
|
|
339
241
|
const fileName = `${name.toLowerCase()}.json`;
|
|
340
|
-
const filePath =
|
|
341
|
-
if (
|
|
342
|
-
console.log(
|
|
242
|
+
const filePath = join4(dir, fileName);
|
|
243
|
+
if (existsSync3(filePath)) {
|
|
244
|
+
console.log(chalk4.yellow(`Object ${name} already exists at ${filePath}`));
|
|
343
245
|
return;
|
|
344
246
|
}
|
|
345
247
|
const content = {
|
|
@@ -349,34 +251,35 @@ function generateResource(cwd, name) {
|
|
|
349
251
|
}
|
|
350
252
|
};
|
|
351
253
|
writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
352
|
-
console.log(
|
|
254
|
+
console.log(chalk4.green(`\u2713 Generated resource schema: ${filePath}`));
|
|
353
255
|
}
|
|
354
256
|
function generatePage(cwd, name) {
|
|
355
|
-
const dir =
|
|
356
|
-
if (!
|
|
257
|
+
const dir = join4(cwd, "pages");
|
|
258
|
+
if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
|
|
357
259
|
const fileName = `${name.toLowerCase()}.json`;
|
|
358
|
-
const filePath =
|
|
359
|
-
if (
|
|
360
|
-
console.log(
|
|
260
|
+
const filePath = join4(dir, fileName);
|
|
261
|
+
if (existsSync3(filePath)) {
|
|
262
|
+
console.log(chalk4.yellow(`Page ${name} already exists at ${filePath}`));
|
|
361
263
|
return;
|
|
362
264
|
}
|
|
363
265
|
const content = {
|
|
364
266
|
type: "page",
|
|
267
|
+
pageType: "app",
|
|
365
268
|
title: name,
|
|
366
|
-
|
|
269
|
+
children: [
|
|
367
270
|
{
|
|
368
271
|
type: "markdown",
|
|
369
|
-
content:
|
|
272
|
+
content: `Welcome to ${name}`
|
|
370
273
|
}
|
|
371
274
|
]
|
|
372
275
|
};
|
|
373
276
|
writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
374
|
-
console.log(
|
|
277
|
+
console.log(chalk4.green(`\u2713 Generated page schema: ${filePath}`));
|
|
375
278
|
}
|
|
376
279
|
function generatePlugin(cwd, name) {
|
|
377
|
-
const dir =
|
|
378
|
-
if (
|
|
379
|
-
console.log(
|
|
280
|
+
const dir = join4(cwd, "plugins", name);
|
|
281
|
+
if (existsSync3(dir)) {
|
|
282
|
+
console.log(chalk4.yellow(`Plugin ${name} already exists`));
|
|
380
283
|
return;
|
|
381
284
|
}
|
|
382
285
|
mkdirSync3(dir, { recursive: true });
|
|
@@ -388,14 +291,14 @@ export const ${name}Plugin = {
|
|
|
388
291
|
}
|
|
389
292
|
};
|
|
390
293
|
`;
|
|
391
|
-
writeFileSync(
|
|
392
|
-
console.log(
|
|
294
|
+
writeFileSync(join4(dir, "index.ts"), content.trim());
|
|
295
|
+
console.log(chalk4.green(`\u2713 Generated plugin: ${dir}`));
|
|
393
296
|
}
|
|
394
297
|
|
|
395
298
|
// src/commands/doctor.ts
|
|
396
|
-
import
|
|
397
|
-
import { existsSync as
|
|
398
|
-
import { dirname, join as
|
|
299
|
+
import chalk5 from "chalk";
|
|
300
|
+
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
301
|
+
import { dirname, join as join5, resolve as resolvePath } from "path";
|
|
399
302
|
var CSS_ENTRY_CANDIDATES = [
|
|
400
303
|
"src/index.css",
|
|
401
304
|
"src/main.css",
|
|
@@ -430,15 +333,15 @@ var V4_POSTCSS_PLUGIN = /@tailwindcss\/postcss/;
|
|
|
430
333
|
var V3_POSTCSS_PLUGIN = /["']tailwindcss["']|(?:^|[{,\s])tailwindcss\s*:/m;
|
|
431
334
|
function readIfExists(path) {
|
|
432
335
|
try {
|
|
433
|
-
return
|
|
336
|
+
return existsSync4(path) ? readFileSync(path, "utf-8") : null;
|
|
434
337
|
} catch {
|
|
435
338
|
return null;
|
|
436
339
|
}
|
|
437
340
|
}
|
|
438
341
|
function findFile(cwd, candidates) {
|
|
439
342
|
for (const rel of candidates) {
|
|
440
|
-
const abs =
|
|
441
|
-
if (
|
|
343
|
+
const abs = join5(cwd, rel);
|
|
344
|
+
if (existsSync4(abs)) return abs;
|
|
442
345
|
}
|
|
443
346
|
return null;
|
|
444
347
|
}
|
|
@@ -451,7 +354,7 @@ function majorOf(range) {
|
|
|
451
354
|
function isInstalledFrom(cwd, specifier) {
|
|
452
355
|
let dir = resolvePath(cwd);
|
|
453
356
|
for (; ; ) {
|
|
454
|
-
if (
|
|
357
|
+
if (existsSync4(join5(dir, "node_modules", ...specifier.split("/")))) return true;
|
|
455
358
|
const parent = dirname(dir);
|
|
456
359
|
if (parent === dir) return false;
|
|
457
360
|
dir = parent;
|
|
@@ -468,7 +371,7 @@ function runDiagnostics(cwd) {
|
|
|
468
371
|
const error = (id, message) => {
|
|
469
372
|
results.push({ id, level: "error", message });
|
|
470
373
|
};
|
|
471
|
-
const pkgPath =
|
|
374
|
+
const pkgPath = join5(cwd, "package.json");
|
|
472
375
|
const pkgRaw = readIfExists(pkgPath);
|
|
473
376
|
let pkg = null;
|
|
474
377
|
if (pkgRaw === null) {
|
|
@@ -536,7 +439,7 @@ function runDiagnostics(cwd) {
|
|
|
536
439
|
}
|
|
537
440
|
const cssEntries = CSS_ENTRY_CANDIDATES.map((rel) => ({
|
|
538
441
|
rel,
|
|
539
|
-
content: readIfExists(
|
|
442
|
+
content: readIfExists(join5(cwd, rel))
|
|
540
443
|
})).filter((entry) => entry.content !== null);
|
|
541
444
|
const importsTailwind = cssEntries.some((entry) => V4_CSS_IMPORT.test(entry.content));
|
|
542
445
|
const usesV3Directives = cssEntries.some((entry) => V3_CSS_DIRECTIVE.test(entry.content));
|
|
@@ -570,7 +473,7 @@ function runDiagnostics(cwd) {
|
|
|
570
473
|
ok("tailwind-config-active", "JS Tailwind config is loaded via `@config`");
|
|
571
474
|
}
|
|
572
475
|
}
|
|
573
|
-
if (
|
|
476
|
+
if (existsSync4(join5(cwd, "tsconfig.json"))) {
|
|
574
477
|
ok("tsconfig-found", "tsconfig.json found");
|
|
575
478
|
const tsRange = deps.typescript;
|
|
576
479
|
if (tsRange) {
|
|
@@ -598,86 +501,40 @@ function countIssues(results) {
|
|
|
598
501
|
return results.filter((result) => result.level !== "ok").length;
|
|
599
502
|
}
|
|
600
503
|
async function doctor() {
|
|
601
|
-
console.log(
|
|
504
|
+
console.log(chalk5.bold("Object UI Doctor"));
|
|
602
505
|
console.log("Diagnosis in progress...\n");
|
|
603
506
|
const results = runDiagnostics(process.cwd());
|
|
604
507
|
for (const result of results) {
|
|
605
508
|
if (result.level === "ok") {
|
|
606
|
-
console.log(
|
|
509
|
+
console.log(chalk5.green(`\u2713 ${result.message}`));
|
|
607
510
|
} else if (result.level === "warn") {
|
|
608
|
-
console.log(
|
|
511
|
+
console.log(chalk5.yellow(`\u26A0\uFE0F ${result.message}`));
|
|
609
512
|
} else {
|
|
610
|
-
console.log(
|
|
513
|
+
console.log(chalk5.red(`x ${result.message}`));
|
|
611
514
|
}
|
|
612
515
|
}
|
|
613
516
|
const issues = countIssues(results);
|
|
614
517
|
console.log("\nResult:");
|
|
615
518
|
if (issues === 0) {
|
|
616
|
-
console.log(
|
|
519
|
+
console.log(chalk5.green("Everything looks good! \u2728"));
|
|
617
520
|
} else {
|
|
618
|
-
console.log(
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// src/commands/add.ts
|
|
623
|
-
import chalk8 from "chalk";
|
|
624
|
-
async function add(component) {
|
|
625
|
-
console.log(chalk8.bold(`Object UI Add: ${component}`));
|
|
626
|
-
console.log(chalk8.yellow("Feature not implemented yet."));
|
|
627
|
-
console.log(`This command will download the source code for ${component}Renderer to your project.`);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// src/commands/studio.ts
|
|
631
|
-
import chalk9 from "chalk";
|
|
632
|
-
import { spawn } from "child_process";
|
|
633
|
-
import { existsSync as existsSync7 } from "fs";
|
|
634
|
-
import { join as join8, resolve as resolve3, dirname as dirname2 } from "path";
|
|
635
|
-
import { fileURLToPath } from "url";
|
|
636
|
-
async function studio() {
|
|
637
|
-
console.log(chalk9.bold("\n\u{1F3A8} Starting ObjectUI Studio...\n"));
|
|
638
|
-
const cwd = process.cwd();
|
|
639
|
-
const __filename3 = fileURLToPath(import.meta.url);
|
|
640
|
-
const __dirname3 = dirname2(__filename3);
|
|
641
|
-
const candidates = [
|
|
642
|
-
join8(cwd, "apps/console"),
|
|
643
|
-
resolve3(__dirname3, "../../apps/console"),
|
|
644
|
-
resolve3(__dirname3, "../../../apps/console")
|
|
645
|
-
];
|
|
646
|
-
let consolePath = null;
|
|
647
|
-
for (const candidate of candidates) {
|
|
648
|
-
if (existsSync7(join8(candidate, "package.json"))) {
|
|
649
|
-
consolePath = candidate;
|
|
650
|
-
break;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (!consolePath) {
|
|
654
|
-
console.log(chalk9.yellow("\u26A0 Console app not found in workspace."));
|
|
655
|
-
console.log(chalk9.dim(" Hint: Run this command from the monorepo root."));
|
|
656
|
-
console.log(chalk9.dim(" Expected: apps/console/package.json\n"));
|
|
657
|
-
process.exit(1);
|
|
521
|
+
console.log(chalk5.yellow(`Found ${issues} issue(s).`));
|
|
658
522
|
}
|
|
659
|
-
console.log(chalk9.dim(` Console: ${consolePath}`));
|
|
660
|
-
console.log(chalk9.dim(" Mode: MSW (in-browser mock server)\n"));
|
|
661
|
-
const child = spawn("pnpm", ["run", "dev"], {
|
|
662
|
-
cwd: consolePath,
|
|
663
|
-
stdio: "inherit",
|
|
664
|
-
env: { ...process.env, NODE_ENV: "development" },
|
|
665
|
-
shell: true
|
|
666
|
-
});
|
|
667
|
-
child.on("error", (err) => {
|
|
668
|
-
console.error(chalk9.red(`
|
|
669
|
-
\u2717 Failed to start studio: ${err.message}`));
|
|
670
|
-
process.exit(1);
|
|
671
|
-
});
|
|
672
|
-
child.on("exit", (code) => process.exit(code ?? 0));
|
|
673
523
|
}
|
|
674
524
|
|
|
675
525
|
// src/commands/check.ts
|
|
676
|
-
import
|
|
526
|
+
import chalk6 from "chalk";
|
|
677
527
|
import { globSync } from "glob";
|
|
678
528
|
import { readFileSync as readFileSync2 } from "fs";
|
|
679
|
-
import { join as
|
|
529
|
+
import { join as join6 } from "path";
|
|
680
530
|
import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
|
|
531
|
+
import { safeValidateSchema } from "@object-ui/types/zod";
|
|
532
|
+
|
|
533
|
+
// src/utils/issue-path.ts
|
|
534
|
+
function formatIssuePath(path) {
|
|
535
|
+
const segments = path ?? [];
|
|
536
|
+
return segments.length > 0 ? segments.join(" \u2192 ") : "(root)";
|
|
537
|
+
}
|
|
681
538
|
|
|
682
539
|
// src/utils/known-schema-types.ts
|
|
683
540
|
var KNOWN_SCHEMA_TYPES = [
|
|
@@ -690,7 +547,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
690
547
|
"action:icon",
|
|
691
548
|
"action:menu",
|
|
692
549
|
"address",
|
|
693
|
-
"advanced-chart",
|
|
694
550
|
"ai-form-assist",
|
|
695
551
|
"ai-recommendations",
|
|
696
552
|
"ai:feedback",
|
|
@@ -699,7 +555,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
699
555
|
"alert",
|
|
700
556
|
"alert-dialog",
|
|
701
557
|
"app",
|
|
702
|
-
"app-creation-wizard",
|
|
703
558
|
"app-schema-renderer",
|
|
704
559
|
"app-shell:cloud-connection:panel",
|
|
705
560
|
"app-shell:cloud:ai-model-status",
|
|
@@ -707,19 +562,16 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
707
562
|
"app-shell:marketplace:installed-list",
|
|
708
563
|
"app-shell:mcp:connect-agent",
|
|
709
564
|
"app:launcher",
|
|
710
|
-
"area-chart",
|
|
711
565
|
"article",
|
|
712
566
|
"aside",
|
|
713
567
|
"aspect-ratio",
|
|
714
|
-
"auto_number",
|
|
715
568
|
"avatar",
|
|
716
569
|
"b",
|
|
717
570
|
"badge",
|
|
718
571
|
"bar-chart",
|
|
719
572
|
"blockquote",
|
|
720
|
-
"
|
|
573
|
+
"box",
|
|
721
574
|
"br",
|
|
722
|
-
"branding-editor",
|
|
723
575
|
"breadcrumb",
|
|
724
576
|
"button",
|
|
725
577
|
"button-group",
|
|
@@ -733,28 +585,22 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
733
585
|
"chatbot-enhanced",
|
|
734
586
|
"chatbot-floating",
|
|
735
587
|
"checkbox",
|
|
736
|
-
"checkboxes",
|
|
737
588
|
"cite",
|
|
738
589
|
"cloud-connection:panel",
|
|
739
590
|
"cloud:ai-model-status",
|
|
740
591
|
"cloud:onboarding-next",
|
|
592
|
+
"cloud:plan-status",
|
|
741
593
|
"code",
|
|
742
594
|
"code-editor",
|
|
743
595
|
"collapsible",
|
|
744
|
-
"color",
|
|
745
596
|
"combobox",
|
|
746
597
|
"command",
|
|
747
598
|
"container",
|
|
748
599
|
"context-menu",
|
|
749
|
-
"currency",
|
|
750
600
|
"dashboard",
|
|
751
|
-
"dashboard-editor",
|
|
752
|
-
"dashboard-grid",
|
|
753
601
|
"data-model-designer",
|
|
754
602
|
"data-table",
|
|
755
|
-
"date",
|
|
756
603
|
"date-picker",
|
|
757
|
-
"datetime",
|
|
758
604
|
"dd",
|
|
759
605
|
"del",
|
|
760
606
|
"detail",
|
|
@@ -763,7 +609,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
763
609
|
"dialog",
|
|
764
610
|
"div",
|
|
765
611
|
"dl",
|
|
766
|
-
"donut-chart",
|
|
767
612
|
"drawer",
|
|
768
613
|
"dropdown-menu",
|
|
769
614
|
"dt",
|
|
@@ -836,17 +681,12 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
836
681
|
"field:video",
|
|
837
682
|
"figcaption",
|
|
838
683
|
"figure",
|
|
839
|
-
"file",
|
|
840
684
|
"file-upload",
|
|
841
685
|
"filter-builder",
|
|
842
686
|
"filter-ui",
|
|
843
687
|
"flex",
|
|
844
688
|
"footer",
|
|
845
689
|
"form",
|
|
846
|
-
"form-analytics",
|
|
847
|
-
"formula",
|
|
848
|
-
"gantt",
|
|
849
|
-
"geolocation",
|
|
850
690
|
"global:notifications",
|
|
851
691
|
"global:search",
|
|
852
692
|
"grid",
|
|
@@ -866,47 +706,28 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
866
706
|
"icon",
|
|
867
707
|
"image",
|
|
868
708
|
"img",
|
|
869
|
-
"import-wizard",
|
|
870
709
|
"input",
|
|
871
710
|
"input-otp",
|
|
872
711
|
"ins",
|
|
873
|
-
"kanban",
|
|
874
|
-
"kanban-enhanced",
|
|
875
|
-
"kanban-ui",
|
|
876
712
|
"kbd",
|
|
877
713
|
"label",
|
|
878
714
|
"layout:app-schema-renderer",
|
|
879
|
-
"layout:navigation-renderer",
|
|
880
|
-
"layout:page-header",
|
|
881
715
|
"layout:page:card",
|
|
882
|
-
"layout:responsive-grid",
|
|
883
716
|
"li",
|
|
884
|
-
"line-chart",
|
|
885
717
|
"list",
|
|
886
718
|
"list-view",
|
|
887
719
|
"loading",
|
|
888
|
-
"location",
|
|
889
|
-
"lookup",
|
|
890
720
|
"main",
|
|
891
|
-
"map",
|
|
892
721
|
"mark",
|
|
893
722
|
"markdown",
|
|
894
723
|
"marketplace:installed-list",
|
|
895
|
-
"master_detail",
|
|
896
724
|
"mcp:connect-agent",
|
|
897
725
|
"menubar",
|
|
898
|
-
"metric",
|
|
899
|
-
"metric-card",
|
|
900
|
-
"multiselect",
|
|
901
726
|
"nav",
|
|
902
727
|
"nav:breadcrumb",
|
|
903
728
|
"nav:menu",
|
|
904
|
-
"navigation-designer",
|
|
905
729
|
"navigation-menu",
|
|
906
|
-
"navigation-renderer",
|
|
907
730
|
"nl-query",
|
|
908
|
-
"number",
|
|
909
|
-
"object",
|
|
910
731
|
"object-calendar",
|
|
911
732
|
"object-chart",
|
|
912
733
|
"object-data-table",
|
|
@@ -927,7 +748,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
927
748
|
"p",
|
|
928
749
|
"page",
|
|
929
750
|
"page-designer",
|
|
930
|
-
"page-header",
|
|
931
751
|
"page:accordion",
|
|
932
752
|
"page:card",
|
|
933
753
|
"page:footer",
|
|
@@ -937,57 +757,37 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
937
757
|
"page:tabs",
|
|
938
758
|
"pagination",
|
|
939
759
|
"password",
|
|
940
|
-
"percent",
|
|
941
|
-
"phone",
|
|
942
|
-
"pie-chart",
|
|
943
760
|
"pivot",
|
|
944
761
|
"plugin-calendar:calendar-view",
|
|
945
762
|
"plugin-calendar:object-calendar",
|
|
946
|
-
"plugin-charts:advanced-chart",
|
|
947
|
-
"plugin-charts:area-chart",
|
|
948
763
|
"plugin-charts:bar-chart",
|
|
949
764
|
"plugin-charts:chart",
|
|
950
765
|
"plugin-charts:chart:bar",
|
|
951
|
-
"plugin-charts:donut-chart",
|
|
952
|
-
"plugin-charts:line-chart",
|
|
953
766
|
"plugin-charts:object-chart",
|
|
954
|
-
"plugin-charts:pie-chart",
|
|
955
|
-
"plugin-charts:radar-chart",
|
|
956
|
-
"plugin-charts:scatter-chart",
|
|
957
767
|
"plugin-chatbot:chatbot",
|
|
958
768
|
"plugin-chatbot:chatbot-enhanced",
|
|
959
769
|
"plugin-chatbot:chatbot-floating",
|
|
960
770
|
"plugin-dashboard:dashboard",
|
|
961
|
-
"plugin-dashboard:dashboard-grid",
|
|
962
771
|
"plugin-dashboard:metric",
|
|
963
772
|
"plugin-dashboard:metric-card",
|
|
964
773
|
"plugin-dashboard:object-data-table",
|
|
965
774
|
"plugin-dashboard:object-metric",
|
|
966
775
|
"plugin-dashboard:object-pivot",
|
|
967
776
|
"plugin-dashboard:pivot",
|
|
968
|
-
"plugin-designer:app-creation-wizard",
|
|
969
|
-
"plugin-designer:branding-editor",
|
|
970
|
-
"plugin-designer:dashboard-editor",
|
|
971
777
|
"plugin-designer:data-model-designer",
|
|
972
778
|
"plugin-designer:field-designer",
|
|
973
|
-
"plugin-designer:navigation-designer",
|
|
974
779
|
"plugin-designer:object-manager",
|
|
975
780
|
"plugin-designer:page-designer",
|
|
976
781
|
"plugin-designer:process-designer",
|
|
977
782
|
"plugin-designer:report-designer",
|
|
978
783
|
"plugin-detail:detail-section",
|
|
979
784
|
"plugin-detail:detail-view",
|
|
980
|
-
"plugin-detail:related-list",
|
|
981
785
|
"plugin-editor:code-editor",
|
|
982
786
|
"plugin-form:embeddable-form",
|
|
983
|
-
"plugin-form:form-analytics",
|
|
984
787
|
"plugin-form:object-form",
|
|
985
788
|
"plugin-form:object-master-detail-form",
|
|
986
789
|
"plugin-gantt:object-gantt",
|
|
987
|
-
"plugin-grid:import-wizard",
|
|
988
790
|
"plugin-grid:object-grid",
|
|
989
|
-
"plugin-kanban:kanban-enhanced",
|
|
990
|
-
"plugin-kanban:kanban-ui",
|
|
991
791
|
"plugin-kanban:object-kanban",
|
|
992
792
|
"plugin-list:list-view",
|
|
993
793
|
"plugin-list:object-gallery",
|
|
@@ -995,12 +795,10 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
995
795
|
"plugin-markdown:markdown",
|
|
996
796
|
"plugin-report:report",
|
|
997
797
|
"plugin-report:report-viewer",
|
|
998
|
-
"plugin-report:spec-report",
|
|
999
798
|
"plugin-timeline:object-timeline",
|
|
1000
799
|
"plugin-timeline:timeline",
|
|
1001
800
|
"plugin-tree:object-tree",
|
|
1002
801
|
"plugin-view:object-view",
|
|
1003
|
-
"plugin-view:view",
|
|
1004
802
|
"plugin-view:view:simple",
|
|
1005
803
|
"popover",
|
|
1006
804
|
"pre",
|
|
@@ -1052,7 +850,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1052
850
|
"protocol-placeholder:global:search",
|
|
1053
851
|
"protocol-placeholder:nav:breadcrumb",
|
|
1054
852
|
"protocol-placeholder:nav:menu",
|
|
1055
|
-
"protocol-placeholder:page-header",
|
|
1056
853
|
"protocol-placeholder:page:accordion",
|
|
1057
854
|
"protocol-placeholder:page:card",
|
|
1058
855
|
"protocol-placeholder:page:footer",
|
|
@@ -1065,7 +862,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1065
862
|
"protocol-placeholder:record:highlights",
|
|
1066
863
|
"protocol-placeholder:record:path",
|
|
1067
864
|
"protocol-placeholder:record:related_list",
|
|
1068
|
-
"protocol-placeholder:user:profile",
|
|
1069
865
|
"protocol-placeholder:view:calendar",
|
|
1070
866
|
"protocol-placeholder:view:drawer",
|
|
1071
867
|
"protocol-placeholder:view:gallery",
|
|
@@ -1092,11 +888,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1092
888
|
"protocol-placeholder:widget:table",
|
|
1093
889
|
"protocol-placeholder:widget:text",
|
|
1094
890
|
"q",
|
|
1095
|
-
"qrcode",
|
|
1096
|
-
"radar-chart",
|
|
1097
|
-
"radio",
|
|
1098
891
|
"radio-group",
|
|
1099
|
-
"rating",
|
|
1100
892
|
"record",
|
|
1101
893
|
"record:activity",
|
|
1102
894
|
"record:alert",
|
|
@@ -1112,53 +904,31 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1112
904
|
"record:quick_actions",
|
|
1113
905
|
"record:reference_rail",
|
|
1114
906
|
"record:related_list",
|
|
1115
|
-
"related-list",
|
|
1116
907
|
"report",
|
|
1117
908
|
"report-designer",
|
|
1118
909
|
"report-viewer",
|
|
1119
|
-
"report:report",
|
|
1120
|
-
"report:report-viewer",
|
|
1121
|
-
"report:spec-report",
|
|
1122
910
|
"resizable",
|
|
1123
|
-
"responsive-grid",
|
|
1124
|
-
"richtext",
|
|
1125
|
-
"scatter-chart",
|
|
1126
911
|
"scroll-area",
|
|
1127
912
|
"section",
|
|
1128
913
|
"select",
|
|
1129
914
|
"separator",
|
|
1130
|
-
"shared-view-link",
|
|
1131
915
|
"sheet",
|
|
1132
916
|
"sidebar",
|
|
1133
|
-
"sidebar-content",
|
|
1134
|
-
"sidebar-footer",
|
|
1135
|
-
"sidebar-group",
|
|
1136
|
-
"sidebar-header",
|
|
1137
|
-
"sidebar-inset",
|
|
1138
|
-
"sidebar-menu",
|
|
1139
|
-
"sidebar-menu-button",
|
|
1140
|
-
"sidebar-menu-item",
|
|
1141
|
-
"sidebar-provider",
|
|
1142
|
-
"sidebar-trigger",
|
|
1143
|
-
"signature",
|
|
1144
917
|
"skeleton",
|
|
1145
918
|
"slider",
|
|
1146
919
|
"small",
|
|
1147
920
|
"sonner",
|
|
1148
921
|
"sort-ui",
|
|
1149
922
|
"span",
|
|
1150
|
-
"spec-report",
|
|
1151
923
|
"spinner",
|
|
1152
924
|
"stack",
|
|
1153
925
|
"statistic",
|
|
1154
926
|
"strong",
|
|
1155
927
|
"sub",
|
|
1156
|
-
"summary",
|
|
1157
928
|
"sup",
|
|
1158
929
|
"switch",
|
|
1159
930
|
"table",
|
|
1160
931
|
"tabs",
|
|
1161
|
-
"tags",
|
|
1162
932
|
"text",
|
|
1163
933
|
"textarea",
|
|
1164
934
|
"time",
|
|
@@ -1168,7 +938,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1168
938
|
"toggle",
|
|
1169
939
|
"toggle-group",
|
|
1170
940
|
"tooltip",
|
|
1171
|
-
"tree",
|
|
1172
941
|
"tree-view",
|
|
1173
942
|
"u",
|
|
1174
943
|
"ui:a",
|
|
@@ -1177,6 +946,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1177
946
|
"ui:address",
|
|
1178
947
|
"ui:alert",
|
|
1179
948
|
"ui:alert-dialog",
|
|
949
|
+
"ui:app",
|
|
1180
950
|
"ui:article",
|
|
1181
951
|
"ui:aside",
|
|
1182
952
|
"ui:aspect-ratio",
|
|
@@ -1184,6 +954,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1184
954
|
"ui:b",
|
|
1185
955
|
"ui:badge",
|
|
1186
956
|
"ui:blockquote",
|
|
957
|
+
"ui:box",
|
|
1187
958
|
"ui:br",
|
|
1188
959
|
"ui:breadcrumb",
|
|
1189
960
|
"ui:button",
|
|
@@ -1193,6 +964,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1193
964
|
"ui:carousel",
|
|
1194
965
|
"ui:checkbox",
|
|
1195
966
|
"ui:cite",
|
|
967
|
+
"ui:code",
|
|
1196
968
|
"ui:collapsible",
|
|
1197
969
|
"ui:combobox",
|
|
1198
970
|
"ui:command",
|
|
@@ -1226,6 +998,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1226
998
|
"ui:h6",
|
|
1227
999
|
"ui:header",
|
|
1228
1000
|
"ui:header-bar",
|
|
1001
|
+
"ui:home",
|
|
1229
1002
|
"ui:hover-card",
|
|
1230
1003
|
"ui:hr",
|
|
1231
1004
|
"ui:html",
|
|
@@ -1248,6 +1021,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1248
1021
|
"ui:navigation-menu",
|
|
1249
1022
|
"ui:ol",
|
|
1250
1023
|
"ui:p",
|
|
1024
|
+
"ui:page",
|
|
1251
1025
|
"ui:pagination",
|
|
1252
1026
|
"ui:password",
|
|
1253
1027
|
"ui:popover",
|
|
@@ -1255,6 +1029,7 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1255
1029
|
"ui:progress",
|
|
1256
1030
|
"ui:q",
|
|
1257
1031
|
"ui:radio-group",
|
|
1032
|
+
"ui:record",
|
|
1258
1033
|
"ui:resizable",
|
|
1259
1034
|
"ui:scroll-area",
|
|
1260
1035
|
"ui:section",
|
|
@@ -1262,16 +1037,6 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1262
1037
|
"ui:separator",
|
|
1263
1038
|
"ui:sheet",
|
|
1264
1039
|
"ui:sidebar",
|
|
1265
|
-
"ui:sidebar-content",
|
|
1266
|
-
"ui:sidebar-footer",
|
|
1267
|
-
"ui:sidebar-group",
|
|
1268
|
-
"ui:sidebar-header",
|
|
1269
|
-
"ui:sidebar-inset",
|
|
1270
|
-
"ui:sidebar-menu",
|
|
1271
|
-
"ui:sidebar-menu-button",
|
|
1272
|
-
"ui:sidebar-menu-item",
|
|
1273
|
-
"ui:sidebar-provider",
|
|
1274
|
-
"ui:sidebar-trigger",
|
|
1275
1040
|
"ui:skeleton",
|
|
1276
1041
|
"ui:slider",
|
|
1277
1042
|
"ui:small",
|
|
@@ -1297,17 +1062,12 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1297
1062
|
"ui:tree-view",
|
|
1298
1063
|
"ui:u",
|
|
1299
1064
|
"ui:ul",
|
|
1065
|
+
"ui:utility",
|
|
1300
1066
|
"ul",
|
|
1301
|
-
"url",
|
|
1302
|
-
"user",
|
|
1303
|
-
"user:profile",
|
|
1304
1067
|
"utility",
|
|
1305
|
-
"vector",
|
|
1306
|
-
"view",
|
|
1307
1068
|
"view-switcher",
|
|
1308
1069
|
"view:calendar",
|
|
1309
1070
|
"view:chart",
|
|
1310
|
-
"view:dashboard",
|
|
1311
1071
|
"view:detail",
|
|
1312
1072
|
"view:drawer",
|
|
1313
1073
|
"view:filter-ui",
|
|
@@ -1319,13 +1079,11 @@ var KNOWN_SCHEMA_TYPES = [
|
|
|
1319
1079
|
"view:list",
|
|
1320
1080
|
"view:map",
|
|
1321
1081
|
"view:modal",
|
|
1322
|
-
"view:shared-view-link",
|
|
1323
1082
|
"view:simple",
|
|
1324
1083
|
"view:sort-ui",
|
|
1325
1084
|
"view:split",
|
|
1326
1085
|
"view:tabbed",
|
|
1327
1086
|
"view:timeline",
|
|
1328
|
-
"view:tree",
|
|
1329
1087
|
"view:view-switcher",
|
|
1330
1088
|
"view:wizard",
|
|
1331
1089
|
"widget:bar",
|
|
@@ -1346,9 +1104,87 @@ function isKnownSchemaType(type) {
|
|
|
1346
1104
|
return KNOWN_SCHEMA_TYPE_SET.has(type);
|
|
1347
1105
|
}
|
|
1348
1106
|
|
|
1107
|
+
// src/utils/known-type-case-suggestion.ts
|
|
1108
|
+
function suggestKnownTypeByCase(type) {
|
|
1109
|
+
if (typeof type !== "string" || type === "") return void 0;
|
|
1110
|
+
const wanted = type.toLowerCase();
|
|
1111
|
+
for (const candidate of KNOWN_SCHEMA_TYPES) {
|
|
1112
|
+
if (candidate !== type && candidate.toLowerCase() === wanted) return candidate;
|
|
1113
|
+
}
|
|
1114
|
+
return void 0;
|
|
1115
|
+
}
|
|
1116
|
+
function didYouMeanClause(type) {
|
|
1117
|
+
const match = suggestKnownTypeByCase(type);
|
|
1118
|
+
return match === void 0 ? "" : ` \u2014 did you mean "${match}"?`;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/utils/unbindable-text-expressions.ts
|
|
1122
|
+
import {
|
|
1123
|
+
EXPRESSION_BINDABLE_TEXT_KEYS,
|
|
1124
|
+
expressionBindableTextKeysFor
|
|
1125
|
+
} from "@objectstack/spec/ui";
|
|
1126
|
+
var EXPRESSION_PATTERN = /\$\{[^}]+\}/;
|
|
1127
|
+
var COMPOSITION_KEY = "children";
|
|
1128
|
+
var PAGE_DOCUMENT_ROOT_TYPE = "page";
|
|
1129
|
+
function isComponentNode(value) {
|
|
1130
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
|
|
1131
|
+
}
|
|
1132
|
+
function judge(node, path, into) {
|
|
1133
|
+
const evaluatedKeys = expressionBindableTextKeysFor(node.type);
|
|
1134
|
+
for (const key of EXPRESSION_BINDABLE_TEXT_KEYS) {
|
|
1135
|
+
const value = node[key];
|
|
1136
|
+
if (typeof value !== "string" || !EXPRESSION_PATTERN.test(value)) continue;
|
|
1137
|
+
if (evaluatedKeys.includes(key)) continue;
|
|
1138
|
+
into.push({
|
|
1139
|
+
severity: isKnownSchemaType(node.type) ? "refusal" : "warning",
|
|
1140
|
+
path: [...path, key],
|
|
1141
|
+
type: node.type,
|
|
1142
|
+
key,
|
|
1143
|
+
evaluatedKeys
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function visit(node, path, isDocumentRoot, into) {
|
|
1148
|
+
if (!(isDocumentRoot && node.type === PAGE_DOCUMENT_ROOT_TYPE)) {
|
|
1149
|
+
judge(node, path, into);
|
|
1150
|
+
}
|
|
1151
|
+
const children = node[COMPOSITION_KEY];
|
|
1152
|
+
if (Array.isArray(children)) {
|
|
1153
|
+
children.forEach((child, index) => {
|
|
1154
|
+
if (isComponentNode(child)) visit(child, [...path, COMPOSITION_KEY, index], false, into);
|
|
1155
|
+
});
|
|
1156
|
+
} else if (isComponentNode(children)) {
|
|
1157
|
+
visit(children, [...path, COMPOSITION_KEY], false, into);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function findUnbindableTextExpressions(document) {
|
|
1161
|
+
const found = [];
|
|
1162
|
+
if (isComponentNode(document)) visit(document, [], true, found);
|
|
1163
|
+
return found;
|
|
1164
|
+
}
|
|
1165
|
+
var inCode = (key) => "`" + key + "`";
|
|
1166
|
+
function describeUnbindableTextExpression(finding) {
|
|
1167
|
+
const at = formatIssuePath(finding.path);
|
|
1168
|
+
if (finding.severity === "warning") {
|
|
1169
|
+
return `at ${at}: "${finding.type}" is not a registered component, so the expression on ${inCode(finding.key)} is not refused; a custom renderer may evaluate it itself.`;
|
|
1170
|
+
}
|
|
1171
|
+
const evaluates = finding.evaluatedKeys.length > 0 ? `it evaluates only ${finding.evaluatedKeys.map(inCode).join(", ")}` : `it evaluates none of ${EXPRESSION_BINDABLE_TEXT_KEYS.map(inCode).join(", ")}`;
|
|
1172
|
+
return `at ${at}: "${finding.type}" never evaluates ${inCode(finding.key)} (${evaluates}), so the expression is never resolved: the user sees its literal text, or nothing.`;
|
|
1173
|
+
}
|
|
1174
|
+
function workingChannels(finding) {
|
|
1175
|
+
const channels = [
|
|
1176
|
+
...finding.evaluatedKeys.map(inCode),
|
|
1177
|
+
inCode("content"),
|
|
1178
|
+
`${inCode(`properties.${finding.key}`)} (evaluated, then hoisted onto the node)`
|
|
1179
|
+
];
|
|
1180
|
+
return `Channels SchemaRenderer evaluates: ${channels.join(", ")}; or resolve the value in the host before the schema reaches SchemaRenderer.`;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1349
1183
|
// src/commands/check.ts
|
|
1350
1184
|
var OBJECTUI_STRUCTURAL_KEYS = [
|
|
1351
1185
|
// Composition — the `children`/`body`-class keys the ruling names.
|
|
1186
|
+
// `body` is the RETIRED spelling, kept so old files are still identified and
|
|
1187
|
+
// then refused — see the docblock above (objectui#6771).
|
|
1352
1188
|
"body",
|
|
1353
1189
|
"children",
|
|
1354
1190
|
// Presentation.
|
|
@@ -1367,8 +1203,24 @@ var OBJECTUI_STRUCTURAL_KEYS = [
|
|
|
1367
1203
|
"testId",
|
|
1368
1204
|
"ariaLabel"
|
|
1369
1205
|
];
|
|
1370
|
-
function
|
|
1371
|
-
|
|
1206
|
+
function recogniseObjectUiSchemaFile(content) {
|
|
1207
|
+
if (OBJECTUI_STRUCTURAL_KEYS.some((key) => key in content)) {
|
|
1208
|
+
return { recognised: true, validated: false };
|
|
1209
|
+
}
|
|
1210
|
+
const result = safeValidateSchema(content);
|
|
1211
|
+
return result.success ? { recognised: true, validated: true } : { recognised: false, issues: result.error.issues };
|
|
1212
|
+
}
|
|
1213
|
+
function describeFirstIssue(issues) {
|
|
1214
|
+
const [first] = issues;
|
|
1215
|
+
if (first === void 0) return void 0;
|
|
1216
|
+
const lead = issues.length === 1 ? "Issue" : `First of ${issues.length} issues`;
|
|
1217
|
+
return `${lead} at ${formatIssuePath(first.path)}: ${first.message}`;
|
|
1218
|
+
}
|
|
1219
|
+
function closingLine(tally) {
|
|
1220
|
+
return `ObjectUI schema files: ${tally.validated} validated, ${tally.notValidated} recognised but not validated, ${tally.didNotValidate} did not validate. This sweep is advisory; run \`objectui validate <file>\` for a verdict.`;
|
|
1221
|
+
}
|
|
1222
|
+
function isUnvalidatedCandidate(type) {
|
|
1223
|
+
return isKnownSchemaType(type);
|
|
1372
1224
|
}
|
|
1373
1225
|
function describeParseError(text, error) {
|
|
1374
1226
|
const upTo = text.slice(0, error.offset);
|
|
@@ -1377,69 +1229,151 @@ function describeParseError(text, error) {
|
|
|
1377
1229
|
return `${printParseErrorCode(error.error)} at line ${line} column ${column}`;
|
|
1378
1230
|
}
|
|
1379
1231
|
async function check(cwd = process.cwd()) {
|
|
1380
|
-
console.log(
|
|
1232
|
+
console.log(chalk6.bold("Object UI Schema Check"));
|
|
1381
1233
|
const files = globSync("**/*.{json,yaml,yml}", {
|
|
1382
1234
|
cwd,
|
|
1383
|
-
ignore
|
|
1235
|
+
// `glob` matches `ignore` patterns against the path relative to `cwd`, so
|
|
1236
|
+
// an unanchored `dist/**`/`node_modules/**` excludes only a directory of
|
|
1237
|
+
// that name at the scan root — every nested `packages/*/dist/`,
|
|
1238
|
+
// `examples/*/dist/`, `apps/*/dist/` (and their `node_modules/`) is still
|
|
1239
|
+
// scanned. `**/` anchors the match at any depth (objectui#6320): measured
|
|
1240
|
+
// on this repository, a built tree without it reports roughly double the
|
|
1241
|
+
// files of a clean checkout, almost all of it re-reading the author's own
|
|
1242
|
+
// schemas from build output.
|
|
1243
|
+
ignore: ["**/node_modules/**", "**/dist/**", ".git/**"]
|
|
1384
1244
|
});
|
|
1385
1245
|
console.log(`Analyzing ${files.length} files...`);
|
|
1386
1246
|
let errors = 0;
|
|
1387
1247
|
let skipped = 0;
|
|
1248
|
+
const unvalidatedCandidates = [];
|
|
1249
|
+
let validated = 0;
|
|
1250
|
+
let notValidated = 0;
|
|
1388
1251
|
for (const file of files) {
|
|
1389
1252
|
try {
|
|
1390
1253
|
if (file.endsWith(".json")) {
|
|
1391
|
-
const text = readFileSync2(
|
|
1254
|
+
const text = readFileSync2(join6(cwd, file), "utf-8");
|
|
1392
1255
|
const parseErrors = [];
|
|
1393
1256
|
const content = parseJsonc(text, parseErrors, { allowTrailingComma: true });
|
|
1394
1257
|
if (parseErrors.length > 0) {
|
|
1395
1258
|
const [first] = parseErrors;
|
|
1396
1259
|
console.log(
|
|
1397
|
-
|
|
1260
|
+
chalk6.red(`x Invalid JSON in ${file}: ${describeParseError(text, first)}`)
|
|
1398
1261
|
);
|
|
1399
1262
|
errors++;
|
|
1400
1263
|
continue;
|
|
1401
1264
|
}
|
|
1402
1265
|
if (content && typeof content === "object" && content.type) {
|
|
1403
1266
|
if (typeof content.type === "string") {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1267
|
+
const recognition = recogniseObjectUiSchemaFile(
|
|
1268
|
+
content
|
|
1269
|
+
);
|
|
1270
|
+
if (!recognition.recognised) {
|
|
1271
|
+
if (isUnvalidatedCandidate(content.type)) {
|
|
1272
|
+
unvalidatedCandidates.push({
|
|
1273
|
+
file,
|
|
1274
|
+
type: content.type,
|
|
1275
|
+
issues: recognition.issues
|
|
1276
|
+
});
|
|
1277
|
+
} else {
|
|
1278
|
+
skipped++;
|
|
1279
|
+
}
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
if (recognition.validated) {
|
|
1283
|
+
validated++;
|
|
1284
|
+
} else {
|
|
1285
|
+
notValidated++;
|
|
1286
|
+
}
|
|
1287
|
+
if (!isKnownSchemaType(content.type)) {
|
|
1288
|
+
console.log(
|
|
1289
|
+
chalk6.yellow(
|
|
1290
|
+
`\u26A0\uFE0F Unknown schema type "${content.type}" in ${file}` + didYouMeanClause(content.type)
|
|
1291
|
+
)
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
for (const finding of findUnbindableTextExpressions(content)) {
|
|
1295
|
+
const line = `${file} ${describeUnbindableTextExpression(finding)}`;
|
|
1296
|
+
if (finding.severity === "refusal") {
|
|
1297
|
+
console.log(chalk6.red(`x Unevaluated expression in ${line}`));
|
|
1298
|
+
console.log(chalk6.dim(` ${workingChannels(finding)}`));
|
|
1299
|
+
errors++;
|
|
1300
|
+
} else {
|
|
1301
|
+
console.log(chalk6.yellow(`\u26A0\uFE0F Expression not judged in ${line}`));
|
|
1302
|
+
}
|
|
1408
1303
|
}
|
|
1409
1304
|
}
|
|
1410
1305
|
}
|
|
1411
1306
|
}
|
|
1412
1307
|
} catch (e) {
|
|
1413
|
-
console.log(
|
|
1308
|
+
console.log(chalk6.red(`x Invalid JSON in ${file}: ${e.message}`));
|
|
1414
1309
|
errors++;
|
|
1415
1310
|
}
|
|
1416
1311
|
}
|
|
1312
|
+
if (unvalidatedCandidates.length > 0) {
|
|
1313
|
+
const n = unvalidatedCandidates.length;
|
|
1314
|
+
console.log(
|
|
1315
|
+
chalk6.yellow(
|
|
1316
|
+
`\u26A0\uFE0F ${n} file${n === 1 ? "" : "s"} carr${n === 1 ? "ies" : "y"} a registered ObjectUI component type but did not validate as an ObjectUI schema:`
|
|
1317
|
+
)
|
|
1318
|
+
);
|
|
1319
|
+
for (const { file, type, issues } of unvalidatedCandidates) {
|
|
1320
|
+
console.log(chalk6.yellow(` ${file} (type "${type}")`));
|
|
1321
|
+
const firstIssue = describeFirstIssue(issues);
|
|
1322
|
+
if (firstIssue !== void 0) {
|
|
1323
|
+
console.log(chalk6.yellow(` ${firstIssue}`));
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
console.log(
|
|
1327
|
+
chalk6.dim(" Run `objectui validate <file>` on any of them for the full diagnosis.")
|
|
1328
|
+
);
|
|
1329
|
+
console.log(
|
|
1330
|
+
chalk6.dim(
|
|
1331
|
+
" Either the document is off-spec, or its component type is not modelled by @object-ui/types."
|
|
1332
|
+
)
|
|
1333
|
+
);
|
|
1334
|
+
console.log(
|
|
1335
|
+
chalk6.dim(
|
|
1336
|
+
" These are NOT counted as skipped: a schema that fails validation is a broken schema, not a foreign file."
|
|
1337
|
+
)
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1417
1340
|
if (skipped > 0) {
|
|
1418
1341
|
console.log(
|
|
1419
|
-
|
|
1420
|
-
`Skipped ${skipped} file${skipped === 1 ? "" : "s"} with a root "type"
|
|
1342
|
+
chalk6.dim(
|
|
1343
|
+
`Skipped ${skipped} file${skipped === 1 ? "" : "s"} with a root "type" that no ObjectUI recogniser admitted.`
|
|
1421
1344
|
)
|
|
1422
1345
|
);
|
|
1423
1346
|
console.log(
|
|
1424
|
-
|
|
1347
|
+
chalk6.dim(
|
|
1425
1348
|
` A file is checked when its root carries an ObjectUI structural key: ${OBJECTUI_STRUCTURAL_KEYS.join(", ")}.`
|
|
1426
1349
|
)
|
|
1427
1350
|
);
|
|
1351
|
+
console.log(
|
|
1352
|
+
chalk6.dim(
|
|
1353
|
+
" It is also checked when the whole document validates as an ObjectUI component schema."
|
|
1354
|
+
)
|
|
1355
|
+
);
|
|
1428
1356
|
}
|
|
1429
1357
|
if (errors === 0) {
|
|
1430
|
-
console.log(
|
|
1358
|
+
console.log(
|
|
1359
|
+
closingLine({
|
|
1360
|
+
validated,
|
|
1361
|
+
notValidated,
|
|
1362
|
+
didNotValidate: unvalidatedCandidates.length
|
|
1363
|
+
})
|
|
1364
|
+
);
|
|
1431
1365
|
} else {
|
|
1432
|
-
console.log(
|
|
1366
|
+
console.log(chalk6.red(`Found ${errors} errors`));
|
|
1433
1367
|
process.exit(1);
|
|
1434
1368
|
}
|
|
1435
1369
|
}
|
|
1436
1370
|
|
|
1437
1371
|
// src/commands/validate.ts
|
|
1438
|
-
import
|
|
1439
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
1440
|
-
import { resolve as
|
|
1372
|
+
import chalk7 from "chalk";
|
|
1373
|
+
import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
|
|
1374
|
+
import { resolve as resolve3 } from "path";
|
|
1441
1375
|
import { load as loadYaml } from "js-yaml";
|
|
1442
|
-
import { safeValidateSchema } from "@object-ui/types/zod";
|
|
1376
|
+
import { safeValidateSchema as safeValidateSchema2 } from "@object-ui/types/zod";
|
|
1443
1377
|
|
|
1444
1378
|
// src/utils/spec-vocabulary-hint.ts
|
|
1445
1379
|
function findSpecVocabularyFormFields(node, path = "") {
|
|
@@ -1470,53 +1404,185 @@ function findSpecVocabularyFormFields(node, path = "") {
|
|
|
1470
1404
|
return findings;
|
|
1471
1405
|
}
|
|
1472
1406
|
|
|
1407
|
+
// src/utils/union-arm-diagnostics.ts
|
|
1408
|
+
var MAX_UNION_ARMS_REPORTED = 5;
|
|
1409
|
+
function editDistance(a, b) {
|
|
1410
|
+
if (a === b) return 0;
|
|
1411
|
+
if (a.length === 0) return b.length;
|
|
1412
|
+
if (b.length === 0) return a.length;
|
|
1413
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
1414
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
1415
|
+
const current = [i];
|
|
1416
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
1417
|
+
current[j] = Math.min(
|
|
1418
|
+
previous[j] + 1,
|
|
1419
|
+
current[j - 1] + 1,
|
|
1420
|
+
previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
previous = current;
|
|
1424
|
+
}
|
|
1425
|
+
return previous[b.length];
|
|
1426
|
+
}
|
|
1427
|
+
function nearestArmNames(authoredType, armNames) {
|
|
1428
|
+
const unique = [...new Set(armNames)].sort();
|
|
1429
|
+
if (typeof authoredType !== "string" || authoredType === "") {
|
|
1430
|
+
return { candidates: [], total: unique.length };
|
|
1431
|
+
}
|
|
1432
|
+
const ordered = [...unique].sort((x, y) => {
|
|
1433
|
+
const d = editDistance(authoredType, x) - editDistance(authoredType, y);
|
|
1434
|
+
return d !== 0 ? d : x.localeCompare(y);
|
|
1435
|
+
});
|
|
1436
|
+
return { candidates: ordered.slice(0, MAX_UNION_ARMS_REPORTED), total: unique.length };
|
|
1437
|
+
}
|
|
1438
|
+
function authoredTypeAt(document, path) {
|
|
1439
|
+
let node = document;
|
|
1440
|
+
for (const key of path) {
|
|
1441
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
1442
|
+
node = node[key];
|
|
1443
|
+
}
|
|
1444
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
1445
|
+
const type = node.type;
|
|
1446
|
+
return typeof type === "string" ? type : void 0;
|
|
1447
|
+
}
|
|
1448
|
+
function isUnion(issue) {
|
|
1449
|
+
return issue.code === "invalid_union" && Array.isArray(issue.errors);
|
|
1450
|
+
}
|
|
1451
|
+
var NO_MATCHING_DISCRIMINATOR = "No matching discriminator";
|
|
1452
|
+
function noArmAccepts(node, literals, document) {
|
|
1453
|
+
const authoredType = authoredTypeAt(document, node);
|
|
1454
|
+
const { candidates, total } = nearestArmNames(authoredType, literals);
|
|
1455
|
+
return {
|
|
1456
|
+
kind: "note",
|
|
1457
|
+
path: [...node],
|
|
1458
|
+
...authoredType === void 0 ? {} : { authoredType },
|
|
1459
|
+
candidates,
|
|
1460
|
+
totalArmNames: total
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function noMatchingDiscriminator(issue, document) {
|
|
1464
|
+
if (issue.code !== "invalid_union") return void 0;
|
|
1465
|
+
if (issue.note !== NO_MATCHING_DISCRIMINATOR) return void 0;
|
|
1466
|
+
if (!Array.isArray(issue.options)) return void 0;
|
|
1467
|
+
if ((issue.errors ?? []).length > 0) return void 0;
|
|
1468
|
+
const path = issue.path ?? [];
|
|
1469
|
+
if (path[path.length - 1] !== "type") return void 0;
|
|
1470
|
+
if (issue.discriminator !== void 0 && issue.discriminator !== "type") return void 0;
|
|
1471
|
+
const literals = issue.options.filter((v) => typeof v === "string");
|
|
1472
|
+
if (literals.length === 0) return void 0;
|
|
1473
|
+
return noArmAccepts(path.slice(0, -1), literals, document);
|
|
1474
|
+
}
|
|
1475
|
+
function isAtTypeKey(issue) {
|
|
1476
|
+
const path = issue.path ?? [];
|
|
1477
|
+
return path.length === 1 && path[0] === "type";
|
|
1478
|
+
}
|
|
1479
|
+
function declaredTypeLiterals(armIssues) {
|
|
1480
|
+
const literals = [];
|
|
1481
|
+
let declares = false;
|
|
1482
|
+
for (const issue of armIssues) {
|
|
1483
|
+
if (isAtTypeKey(issue) && issue.code === "invalid_value" && Array.isArray(issue.values)) {
|
|
1484
|
+
declares = true;
|
|
1485
|
+
literals.push(...issue.values.filter((v) => typeof v === "string"));
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (isAtTypeKey(issue) && isUnion(issue) && Array.isArray(issue.options)) {
|
|
1489
|
+
declares = true;
|
|
1490
|
+
literals.push(...issue.options.filter((v) => typeof v === "string"));
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
if ((issue.path ?? []).length === 0 && isUnion(issue)) {
|
|
1494
|
+
const nested = (issue.errors ?? []).map((sub) => declaredTypeLiterals(sub));
|
|
1495
|
+
if (nested.length > 0 && nested.every((n) => n !== void 0)) {
|
|
1496
|
+
declares = true;
|
|
1497
|
+
for (const n of nested) literals.push(...n);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return declares ? literals : void 0;
|
|
1502
|
+
}
|
|
1503
|
+
function explain(unionIssue, prefix, document) {
|
|
1504
|
+
const arms = unionIssue.errors ?? [];
|
|
1505
|
+
const declared = arms.map((arm) => declaredTypeLiterals(arm));
|
|
1506
|
+
const declaringArms = declared.filter((d) => d !== void 0);
|
|
1507
|
+
const acceptingIndexes = declared.map((d, i) => d === void 0 ? i : -1).filter((i) => i >= 0);
|
|
1508
|
+
const expand = (armIssues, arm) => armIssues.flatMap((issue) => {
|
|
1509
|
+
const absolute = [...prefix, ...issue.path ?? []];
|
|
1510
|
+
const discriminated = noMatchingDiscriminator({ ...issue, path: absolute }, document);
|
|
1511
|
+
if (discriminated) return [discriminated];
|
|
1512
|
+
if (isUnion(issue)) return explain(issue, absolute, document);
|
|
1513
|
+
return [
|
|
1514
|
+
{
|
|
1515
|
+
kind: "issue",
|
|
1516
|
+
path: absolute,
|
|
1517
|
+
message: issue.message ?? "",
|
|
1518
|
+
code: issue.code,
|
|
1519
|
+
...arm === void 0 ? {} : { arm }
|
|
1520
|
+
}
|
|
1521
|
+
];
|
|
1522
|
+
});
|
|
1523
|
+
if (declaringArms.length > 0 && acceptingIndexes.length === 1) {
|
|
1524
|
+
return expand(arms[acceptingIndexes[0]]);
|
|
1525
|
+
}
|
|
1526
|
+
if (declaringArms.length > 0 && acceptingIndexes.length === 0) {
|
|
1527
|
+
return [noArmAccepts(prefix, declaringArms.flat(), document)];
|
|
1528
|
+
}
|
|
1529
|
+
return arms.slice(0, MAX_UNION_ARMS_REPORTED).flatMap((arm, i) => expand(arm, `${i + 1}/${arms.length}`));
|
|
1530
|
+
}
|
|
1531
|
+
function explainUnionIssue(issue, document) {
|
|
1532
|
+
const discriminated = noMatchingDiscriminator(issue, document);
|
|
1533
|
+
if (discriminated) return [discriminated];
|
|
1534
|
+
if (!isUnion(issue)) return [];
|
|
1535
|
+
return explain(issue, issue.path ?? [], document);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1473
1538
|
// src/commands/validate.ts
|
|
1474
1539
|
async function validate(schemaPath) {
|
|
1475
|
-
console.log(
|
|
1476
|
-
const resolvedPath =
|
|
1477
|
-
if (!
|
|
1478
|
-
console.error(
|
|
1540
|
+
console.log(chalk7.blue("\u{1F50D} ObjectUI Schema Validator\n"));
|
|
1541
|
+
const resolvedPath = resolve3(process.cwd(), schemaPath);
|
|
1542
|
+
if (!existsSync5(resolvedPath)) {
|
|
1543
|
+
console.error(chalk7.red(`\u2717 Error: Schema file not found: ${schemaPath}`));
|
|
1479
1544
|
process.exit(1);
|
|
1480
1545
|
}
|
|
1481
1546
|
try {
|
|
1482
1547
|
const fileContent = readFileSync3(resolvedPath, "utf-8");
|
|
1483
1548
|
let schema;
|
|
1484
1549
|
if (schemaPath.endsWith(".yaml") || schemaPath.endsWith(".yml")) {
|
|
1485
|
-
console.log(
|
|
1550
|
+
console.log(chalk7.gray(`Reading YAML schema from: ${schemaPath}`));
|
|
1486
1551
|
schema = loadYaml(fileContent);
|
|
1487
1552
|
} else if (schemaPath.endsWith(".json")) {
|
|
1488
|
-
console.log(
|
|
1553
|
+
console.log(chalk7.gray(`Reading JSON schema from: ${schemaPath}`));
|
|
1489
1554
|
schema = JSON.parse(fileContent);
|
|
1490
1555
|
} else {
|
|
1491
1556
|
try {
|
|
1492
1557
|
schema = JSON.parse(fileContent);
|
|
1493
|
-
console.log(
|
|
1558
|
+
console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as JSON)`));
|
|
1494
1559
|
} catch {
|
|
1495
1560
|
schema = loadYaml(fileContent);
|
|
1496
|
-
console.log(
|
|
1561
|
+
console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as YAML)`));
|
|
1497
1562
|
}
|
|
1498
1563
|
}
|
|
1499
|
-
console.log(
|
|
1500
|
-
const result =
|
|
1564
|
+
console.log(chalk7.gray("Validating schema...\n"));
|
|
1565
|
+
const result = safeValidateSchema2(schema);
|
|
1501
1566
|
if (result.success) {
|
|
1502
|
-
console.log(
|
|
1567
|
+
console.log(chalk7.green("\u2713 Schema is valid!\n"));
|
|
1503
1568
|
const data = result.data;
|
|
1504
|
-
console.log(
|
|
1505
|
-
console.log(
|
|
1569
|
+
console.log(chalk7.bold("Schema Information:"));
|
|
1570
|
+
console.log(chalk7.gray(" Type:"), data.type || "unknown");
|
|
1506
1571
|
if (data.id) {
|
|
1507
|
-
console.log(
|
|
1572
|
+
console.log(chalk7.gray(" ID:"), data.id);
|
|
1508
1573
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1574
|
+
const title = "title" in data ? data.title : void 0;
|
|
1575
|
+
if (data.label || title) {
|
|
1576
|
+
console.log(chalk7.gray(" Label:"), data.label || title);
|
|
1511
1577
|
}
|
|
1512
1578
|
if (data.children && Array.isArray(data.children)) {
|
|
1513
|
-
console.log(
|
|
1579
|
+
console.log(chalk7.gray(" Children:"), data.children.length);
|
|
1514
1580
|
}
|
|
1515
1581
|
const mixed = findSpecVocabularyFormFields(schema).filter((f) => f.mixedName);
|
|
1516
1582
|
if (mixed.length > 0) {
|
|
1517
|
-
console.log(
|
|
1583
|
+
console.log(chalk7.yellow("\nWarnings:"));
|
|
1518
1584
|
for (const m of mixed) {
|
|
1519
|
-
console.log(
|
|
1585
|
+
console.log(chalk7.yellow(
|
|
1520
1586
|
` ${m.path}: '${m.mixedName}' also carries { field: '${m.field}' } \u2014 mixed form-field vocabularies. The renderer ignores \`field\` here; drop one of the two.`
|
|
1521
1587
|
));
|
|
1522
1588
|
}
|
|
@@ -1524,27 +1590,56 @@ async function validate(schemaPath) {
|
|
|
1524
1590
|
console.log("");
|
|
1525
1591
|
process.exit(0);
|
|
1526
1592
|
} else {
|
|
1527
|
-
console.error(
|
|
1528
|
-
console.error(
|
|
1593
|
+
console.error(chalk7.red("\u2717 Schema validation failed!\n"));
|
|
1594
|
+
console.error(chalk7.bold("Validation Errors:"));
|
|
1529
1595
|
result.error.issues.forEach((issue, index) => {
|
|
1530
|
-
console.error(
|
|
1596
|
+
console.error(chalk7.red(`
|
|
1531
1597
|
${index + 1}. ${issue.message}`));
|
|
1532
|
-
|
|
1533
|
-
console.error(chalk11.gray(` Path: ${issue.path.join(" \u2192 ")}`));
|
|
1534
|
-
}
|
|
1598
|
+
console.error(chalk7.gray(` Path: ${formatIssuePath(issue.path)}`));
|
|
1535
1599
|
if (issue.code) {
|
|
1536
|
-
console.error(
|
|
1600
|
+
console.error(chalk7.gray(` Code: ${issue.code}`));
|
|
1537
1601
|
}
|
|
1602
|
+
explainUnionIssue(issue, schema).forEach((line, sub) => {
|
|
1603
|
+
const where = formatIssuePath(line.path);
|
|
1604
|
+
if (line.kind === "note") {
|
|
1605
|
+
const at = line.path.length > 0 ? ` at ${where}` : "";
|
|
1606
|
+
if (line.authoredType === void 0) {
|
|
1607
|
+
console.error(
|
|
1608
|
+
chalk7.yellow(
|
|
1609
|
+
` No \`type\` is declared${at}, so none of the ${line.totalArmNames} accepted component types can be selected.`
|
|
1610
|
+
)
|
|
1611
|
+
);
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
console.error(
|
|
1615
|
+
chalk7.yellow(` No arm accepts type "${line.authoredType}"${at}.`)
|
|
1616
|
+
);
|
|
1617
|
+
if (line.candidates.length > 0) {
|
|
1618
|
+
console.error(
|
|
1619
|
+
chalk7.gray(
|
|
1620
|
+
` Nearest of the ${line.totalArmNames} accepted types: ` + line.candidates.join(", ")
|
|
1621
|
+
)
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const arm = line.arm === void 0 ? "" : `[arm ${line.arm}] `;
|
|
1627
|
+
console.error(chalk7.red(` ${index + 1}.${sub + 1} ${arm}${line.message}`));
|
|
1628
|
+
console.error(chalk7.gray(` Path: ${where}`));
|
|
1629
|
+
if (line.code) {
|
|
1630
|
+
console.error(chalk7.gray(` Code: ${line.code}`));
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1538
1633
|
});
|
|
1539
1634
|
const specShaped = findSpecVocabularyFormFields(schema).filter((f) => !f.mixedName);
|
|
1540
1635
|
if (specShaped.length > 0) {
|
|
1541
|
-
console.error(
|
|
1636
|
+
console.error(chalk7.bold("\nLikely cause \u2014 spec form-view vocabulary in a standalone form:"));
|
|
1542
1637
|
for (const s of specShaped) {
|
|
1543
|
-
console.error(
|
|
1638
|
+
console.error(chalk7.yellow(
|
|
1544
1639
|
` ${s.path}: { field: '${s.field}' } is the spec form-VIEW shape (an object-field reference).`
|
|
1545
1640
|
));
|
|
1546
1641
|
}
|
|
1547
|
-
console.error(
|
|
1642
|
+
console.error(chalk7.yellow(
|
|
1548
1643
|
` A standalone \`type: 'form'\` component uses { name: \u2026 } (the form data path).
|
|
1549
1644
|
Fix: author the field with \`name\` and its own type/label \u2014 or, to reference object fields
|
|
1550
1645
|
with the spec shape, use an object-bound form (\`type: 'object-form'\` with objectName +
|
|
@@ -1555,61 +1650,28 @@ ${index + 1}. ${issue.message}`));
|
|
|
1555
1650
|
process.exit(1);
|
|
1556
1651
|
}
|
|
1557
1652
|
} catch (error) {
|
|
1558
|
-
console.error(
|
|
1559
|
-
console.error(
|
|
1653
|
+
console.error(chalk7.red("\n\u2717 Error reading or parsing schema file:"));
|
|
1654
|
+
console.error(chalk7.red(error.message));
|
|
1560
1655
|
console.error("");
|
|
1561
1656
|
process.exit(1);
|
|
1562
1657
|
}
|
|
1563
1658
|
}
|
|
1564
1659
|
|
|
1565
|
-
// src/commands/create-plugin.ts
|
|
1566
|
-
import chalk12 from "chalk";
|
|
1567
|
-
import { spawn as spawn2 } from "child_process";
|
|
1568
|
-
import { resolve as resolve5, dirname as dirname3 } from "path";
|
|
1569
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1570
|
-
var __filename = fileURLToPath2(import.meta.url);
|
|
1571
|
-
var __dirname = dirname3(__filename);
|
|
1572
|
-
async function createPlugin(pluginName) {
|
|
1573
|
-
console.log(chalk12.blue("\u{1F680} Creating ObjectUI plugin...\n"));
|
|
1574
|
-
const createPluginScript = resolve5(
|
|
1575
|
-
__dirname,
|
|
1576
|
-
"../../../create-plugin/dist/index.js"
|
|
1577
|
-
);
|
|
1578
|
-
return new Promise((resolve7, reject) => {
|
|
1579
|
-
const child = spawn2("node", [createPluginScript, pluginName], {
|
|
1580
|
-
stdio: "inherit",
|
|
1581
|
-
shell: true
|
|
1582
|
-
});
|
|
1583
|
-
child.on("error", (error) => {
|
|
1584
|
-
console.error(chalk12.red("Failed to create plugin:"), error);
|
|
1585
|
-
reject(error);
|
|
1586
|
-
});
|
|
1587
|
-
child.on("exit", (code) => {
|
|
1588
|
-
if (code === 0) {
|
|
1589
|
-
resolve7();
|
|
1590
|
-
} else {
|
|
1591
|
-
reject(new Error(`create-plugin exited with code ${code}`));
|
|
1592
|
-
}
|
|
1593
|
-
});
|
|
1594
|
-
});
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
1660
|
// src/commands/analyze.ts
|
|
1598
|
-
import
|
|
1599
|
-
import { existsSync as
|
|
1600
|
-
import { resolve as
|
|
1661
|
+
import chalk8 from "chalk";
|
|
1662
|
+
import { existsSync as existsSync6, statSync, readdirSync } from "fs";
|
|
1663
|
+
import { resolve as resolve4, join as join7, extname } from "path";
|
|
1601
1664
|
async function analyzeBundleSize() {
|
|
1602
|
-
console.log(
|
|
1603
|
-
const distDir =
|
|
1604
|
-
if (!
|
|
1605
|
-
|
|
1606
|
-
return;
|
|
1665
|
+
console.log(chalk8.bold("\n\u{1F4E6} Bundle Size Analysis\n"));
|
|
1666
|
+
const distDir = resolve4(process.cwd(), "dist");
|
|
1667
|
+
if (!existsSync6(distDir)) {
|
|
1668
|
+
throw new Error("No dist/ directory to analyze. Run `objectui build` first.");
|
|
1607
1669
|
}
|
|
1608
1670
|
const files = [];
|
|
1609
1671
|
function scanDirectory(dir) {
|
|
1610
1672
|
const items = readdirSync(dir);
|
|
1611
1673
|
for (const item of items) {
|
|
1612
|
-
const fullPath =
|
|
1674
|
+
const fullPath = join7(dir, item);
|
|
1613
1675
|
const stat = statSync(fullPath);
|
|
1614
1676
|
if (stat.isDirectory()) {
|
|
1615
1677
|
scanDirectory(fullPath);
|
|
@@ -1628,48 +1690,34 @@ async function analyzeBundleSize() {
|
|
|
1628
1690
|
const cssFiles = files.filter((f) => extname(f.path) === ".css");
|
|
1629
1691
|
const jsSize = jsFiles.reduce((sum, file) => sum + file.size, 0);
|
|
1630
1692
|
const cssSize = cssFiles.reduce((sum, file) => sum + file.size, 0);
|
|
1631
|
-
console.log(
|
|
1632
|
-
console.log(
|
|
1633
|
-
console.log(
|
|
1634
|
-
console.log(
|
|
1635
|
-
console.log(
|
|
1636
|
-
console.log(
|
|
1693
|
+
console.log(chalk8.bold("Summary:"));
|
|
1694
|
+
console.log(chalk8.gray(" Total Size:"), formatBytes(totalSize));
|
|
1695
|
+
console.log(chalk8.gray(" JavaScript:"), formatBytes(jsSize), chalk8.dim(`(${jsFiles.length} files)`));
|
|
1696
|
+
console.log(chalk8.gray(" CSS: "), formatBytes(cssSize), chalk8.dim(`(${cssFiles.length} files)`));
|
|
1697
|
+
console.log(chalk8.gray(" Other: "), formatBytes(totalSize - jsSize - cssSize));
|
|
1698
|
+
console.log(chalk8.bold("\nLargest Files:"));
|
|
1637
1699
|
files.slice(0, 10).forEach((file) => {
|
|
1638
1700
|
const sizeStr = formatBytes(file.size).padStart(10);
|
|
1639
|
-
console.log(
|
|
1701
|
+
console.log(chalk8.gray(` ${sizeStr}`), file.path);
|
|
1640
1702
|
});
|
|
1641
|
-
console.log(
|
|
1703
|
+
console.log(chalk8.bold("\n\u{1F4A1} Recommendations:"));
|
|
1642
1704
|
if (totalSize > 1024 * 1024) {
|
|
1643
|
-
console.log(
|
|
1644
|
-
console.log(
|
|
1705
|
+
console.log(chalk8.yellow(" \u26A0 Total bundle size is large (> 1MB)"));
|
|
1706
|
+
console.log(chalk8.gray(" Consider code splitting or lazy loading"));
|
|
1645
1707
|
}
|
|
1646
1708
|
if (jsSize > 500 * 1024) {
|
|
1647
|
-
console.log(
|
|
1648
|
-
console.log(
|
|
1649
|
-
console.log(
|
|
1650
|
-
console.log(
|
|
1651
|
-
console.log(
|
|
1709
|
+
console.log(chalk8.yellow(" \u26A0 JavaScript bundle is large (> 500KB)"));
|
|
1710
|
+
console.log(chalk8.gray(" Consider:"));
|
|
1711
|
+
console.log(chalk8.gray(" - Tree shaking unused code"));
|
|
1712
|
+
console.log(chalk8.gray(" - Lazy loading components"));
|
|
1713
|
+
console.log(chalk8.gray(" - Using dynamic imports"));
|
|
1652
1714
|
}
|
|
1653
1715
|
if (files.length > 100) {
|
|
1654
|
-
console.log(
|
|
1655
|
-
console.log(
|
|
1716
|
+
console.log(chalk8.yellow(` \u26A0 Large number of files (${files.length})`));
|
|
1717
|
+
console.log(chalk8.gray(" Consider bundling or combining files"));
|
|
1656
1718
|
}
|
|
1657
1719
|
console.log("");
|
|
1658
1720
|
}
|
|
1659
|
-
async function analyzeRenderPerformance() {
|
|
1660
|
-
console.log(chalk13.bold("\n\u26A1 Render Performance Analysis\n"));
|
|
1661
|
-
console.log(chalk13.gray("Performance analysis features:"));
|
|
1662
|
-
console.log(chalk13.gray(" \u2713 Expression caching enabled"));
|
|
1663
|
-
console.log(chalk13.gray(" \u2713 Component memoization available"));
|
|
1664
|
-
console.log(chalk13.gray(" \u2713 Virtual scrolling support for large lists"));
|
|
1665
|
-
console.log(chalk13.bold("\n\u{1F4A1} Performance Tips:"));
|
|
1666
|
-
console.log(chalk13.gray(" \u2022 Use virtual scrolling for lists > 100 items"));
|
|
1667
|
-
console.log(chalk13.gray(" \u2022 Cache frequently evaluated expressions"));
|
|
1668
|
-
console.log(chalk13.gray(" \u2022 Use React.memo for expensive components"));
|
|
1669
|
-
console.log(chalk13.gray(" \u2022 Implement pagination for large datasets"));
|
|
1670
|
-
console.log(chalk13.gray(" \u2022 Use code splitting for large apps"));
|
|
1671
|
-
console.log("");
|
|
1672
|
-
}
|
|
1673
1721
|
function formatBytes(bytes) {
|
|
1674
1722
|
if (bytes === 0) return "0 B";
|
|
1675
1723
|
const k = 1024;
|
|
@@ -1677,26 +1725,20 @@ function formatBytes(bytes) {
|
|
|
1677
1725
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1678
1726
|
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
|
|
1679
1727
|
}
|
|
1680
|
-
async function analyze(
|
|
1681
|
-
console.log(
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
await analyzeBundleSize();
|
|
1685
|
-
}
|
|
1686
|
-
if (options.renderPerformance || runAll) {
|
|
1687
|
-
await analyzeRenderPerformance();
|
|
1688
|
-
}
|
|
1689
|
-
console.log(chalk13.green("\u2713 Analysis complete!\n"));
|
|
1728
|
+
async function analyze() {
|
|
1729
|
+
console.log(chalk8.blue("\u{1F50D} ObjectUI Performance Analyzer\n"));
|
|
1730
|
+
await analyzeBundleSize();
|
|
1731
|
+
console.log(chalk8.green("\u2713 Analysis complete!\n"));
|
|
1690
1732
|
}
|
|
1691
1733
|
|
|
1692
1734
|
// src/cli.ts
|
|
1693
1735
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1694
|
-
import { fileURLToPath
|
|
1695
|
-
import { dirname as
|
|
1696
|
-
var
|
|
1697
|
-
var
|
|
1736
|
+
import { fileURLToPath } from "url";
|
|
1737
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
1738
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
1739
|
+
var __dirname = dirname2(__filename);
|
|
1698
1740
|
var packageJson = JSON.parse(
|
|
1699
|
-
readFileSync4(
|
|
1741
|
+
readFileSync4(join8(__dirname, "../package.json"), "utf-8")
|
|
1700
1742
|
);
|
|
1701
1743
|
var program = new Command();
|
|
1702
1744
|
program.name("objectui").description("CLI tool for Object UI - Build applications from JSON schemas").version(packageJson.version);
|
|
@@ -1704,7 +1746,7 @@ program.command("serve").description("Start a development server with your JSON/
|
|
|
1704
1746
|
try {
|
|
1705
1747
|
await serve(schema, options);
|
|
1706
1748
|
} catch (error) {
|
|
1707
|
-
console.error(
|
|
1749
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1708
1750
|
process.exit(1);
|
|
1709
1751
|
}
|
|
1710
1752
|
});
|
|
@@ -1712,7 +1754,7 @@ program.command("dev").description("Start development server (alias for serve)")
|
|
|
1712
1754
|
try {
|
|
1713
1755
|
await dev(schema, options);
|
|
1714
1756
|
} catch (error) {
|
|
1715
|
-
console.error(
|
|
1757
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1716
1758
|
process.exit(1);
|
|
1717
1759
|
}
|
|
1718
1760
|
});
|
|
@@ -1720,7 +1762,7 @@ program.command("build").description("Build application for production").argumen
|
|
|
1720
1762
|
try {
|
|
1721
1763
|
await buildApp(schema, options);
|
|
1722
1764
|
} catch (error) {
|
|
1723
|
-
console.error(
|
|
1765
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1724
1766
|
process.exit(1);
|
|
1725
1767
|
}
|
|
1726
1768
|
});
|
|
@@ -1728,7 +1770,7 @@ program.command("start").description("Start production server").option("-p, --po
|
|
|
1728
1770
|
try {
|
|
1729
1771
|
await start(options);
|
|
1730
1772
|
} catch (error) {
|
|
1731
|
-
console.error(
|
|
1773
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1732
1774
|
process.exit(1);
|
|
1733
1775
|
}
|
|
1734
1776
|
});
|
|
@@ -1736,68 +1778,31 @@ program.command("init").description("Initialize a new Object UI application with
|
|
|
1736
1778
|
try {
|
|
1737
1779
|
await init(name, options);
|
|
1738
1780
|
} catch (error) {
|
|
1739
|
-
console.error(
|
|
1740
|
-
process.exit(1);
|
|
1741
|
-
}
|
|
1742
|
-
});
|
|
1743
|
-
program.command("lint").description("Lint the generated application code").option("--fix", "Automatically fix linting issues").action(async (options) => {
|
|
1744
|
-
try {
|
|
1745
|
-
await lint(options);
|
|
1746
|
-
} catch (error) {
|
|
1747
|
-
console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
|
|
1748
|
-
process.exit(1);
|
|
1749
|
-
}
|
|
1750
|
-
});
|
|
1751
|
-
program.command("test").description("Run tests for the application").option("-w, --watch", "Run tests in watch mode").option("-c, --coverage", "Generate test coverage report").option("--ui", "Run tests with Vitest UI").action(async (options) => {
|
|
1752
|
-
try {
|
|
1753
|
-
await test(options);
|
|
1754
|
-
} catch (error) {
|
|
1755
|
-
console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
|
|
1781
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1756
1782
|
process.exit(1);
|
|
1757
1783
|
}
|
|
1758
1784
|
});
|
|
1759
|
-
program.command("generate").alias("g").description("Generate new resources (objects, pages, plugins)").argument("<type>", "Type of resource to generate (resource/object, page, plugin)").argument("<name>", "Name of the resource").
|
|
1785
|
+
program.command("generate").alias("g").description("Generate new resources (objects, pages, plugins)").argument("<type>", "Type of resource to generate (resource/object, page, plugin)").argument("<name>", "Name of the resource").action(async (type, name) => {
|
|
1760
1786
|
try {
|
|
1761
|
-
if (options.from) {
|
|
1762
|
-
console.log(chalk14.yellow("\n\u26A0 Schema generation from external sources (OpenAPI/Prisma) is not yet implemented."));
|
|
1763
|
-
console.log(chalk14.gray("This feature will be available in a future release.\n"));
|
|
1764
|
-
process.exit(0);
|
|
1765
|
-
}
|
|
1766
1787
|
await generate(type, name);
|
|
1767
1788
|
} catch (error) {
|
|
1768
|
-
console.error(
|
|
1789
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1769
1790
|
process.exit(1);
|
|
1770
1791
|
}
|
|
1771
1792
|
});
|
|
1772
|
-
program.command("doctor").description("Diagnose
|
|
1793
|
+
program.command("doctor").description("Diagnose common project setup issues (reports only, changes nothing)").action(async () => {
|
|
1773
1794
|
try {
|
|
1774
1795
|
await doctor();
|
|
1775
1796
|
} catch (error) {
|
|
1776
|
-
console.error(
|
|
1777
|
-
process.exit(1);
|
|
1778
|
-
}
|
|
1779
|
-
});
|
|
1780
|
-
program.command("add").description("Add a new component renderer to your project").argument("<component>", "Component name (e.g. Input, Grid)").action(async (component) => {
|
|
1781
|
-
try {
|
|
1782
|
-
await add(component);
|
|
1783
|
-
} catch (error) {
|
|
1784
|
-
console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
|
|
1785
|
-
process.exit(1);
|
|
1786
|
-
}
|
|
1787
|
-
});
|
|
1788
|
-
program.command("studio").description("Start the visual designer").action(async () => {
|
|
1789
|
-
try {
|
|
1790
|
-
await studio();
|
|
1791
|
-
} catch (error) {
|
|
1792
|
-
console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
|
|
1797
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1793
1798
|
process.exit(1);
|
|
1794
1799
|
}
|
|
1795
1800
|
});
|
|
1796
|
-
program.command("check").description("
|
|
1801
|
+
program.command("check").description("Advisory JSON file sweep; run objectui validate for a verdict").action(async () => {
|
|
1797
1802
|
try {
|
|
1798
1803
|
await check();
|
|
1799
1804
|
} catch (error) {
|
|
1800
|
-
console.error(
|
|
1805
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1801
1806
|
process.exit(1);
|
|
1802
1807
|
}
|
|
1803
1808
|
});
|
|
@@ -1805,29 +1810,15 @@ program.command("validate").description("Validate a schema file against ObjectUI
|
|
|
1805
1810
|
try {
|
|
1806
1811
|
await validate(schema);
|
|
1807
1812
|
} catch (error) {
|
|
1808
|
-
console.error(
|
|
1809
|
-
process.exit(1);
|
|
1810
|
-
}
|
|
1811
|
-
});
|
|
1812
|
-
program.command("create").description("Create new resources").argument("<type>", "Type of resource to create (plugin)").argument("<name>", "Name of the resource").action(async (type, name) => {
|
|
1813
|
-
try {
|
|
1814
|
-
if (type === "plugin") {
|
|
1815
|
-
await createPlugin(name);
|
|
1816
|
-
} else {
|
|
1817
|
-
console.error(chalk14.red(`Unknown resource type: ${type}`));
|
|
1818
|
-
console.log(chalk14.gray("Available types: plugin"));
|
|
1819
|
-
process.exit(1);
|
|
1820
|
-
}
|
|
1821
|
-
} catch (error) {
|
|
1822
|
-
console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
|
|
1813
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1823
1814
|
process.exit(1);
|
|
1824
1815
|
}
|
|
1825
1816
|
});
|
|
1826
|
-
program.command("analyze").description("Analyze application performance").
|
|
1817
|
+
program.command("analyze").description("Analyze application performance").action(async () => {
|
|
1827
1818
|
try {
|
|
1828
|
-
await analyze(
|
|
1819
|
+
await analyze();
|
|
1829
1820
|
} catch (error) {
|
|
1830
|
-
console.error(
|
|
1821
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
1831
1822
|
process.exit(1);
|
|
1832
1823
|
}
|
|
1833
1824
|
});
|