@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.2982c61
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/README.md +10 -6
- package/api/doctor/doctor.d.mts +3 -8
- package/api/doctor/doctor.mjs +9 -90
- package/api/doctor/doctor.test.mjs +10 -122
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/integration/pack-check.mjs +3 -28
- package/api/json/index.ts +1 -0
- package/api/layout/_adapter.d.mts +34 -0
- package/api/layout/_adapter.mjs +148 -0
- package/api/layout/check/check.d.mts +16 -0
- package/api/layout/check/check.mjs +40 -0
- package/api/layout/expand/expand.d.mts +22 -0
- package/api/layout/expand/expand.mjs +155 -0
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.d.mts +13 -0
- package/api/layout/grammar/grammar.mjs +87 -0
- package/api/layout/layout.d.mts +6 -0
- package/api/layout/layout.mjs +17 -0
- package/api/layout/layout.test.mjs +297 -0
- package/api/layout/layout.type.d.mts +89 -0
- package/api/layout/layout.type.mjs +103 -0
- package/api/layout/layoutCheck.doc.d.mts +11 -0
- package/api/layout/layoutCheck.doc.mjs +85 -0
- package/api/layout/layoutExpand.doc.d.mts +11 -0
- package/api/layout/layoutExpand.doc.mjs +107 -0
- package/api/layout/layoutGrammar.doc.d.mts +11 -0
- package/api/layout/layoutGrammar.doc.mjs +57 -0
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.mjs +1 -1
- package/api/upgrade/run/run.mjs +4 -6
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +1 -3
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +4 -5
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/config/type.ts +2 -2
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/layout-check.doc.mjs +65 -0
- package/clients/cli/commands/layout-expand.doc.mjs +83 -0
- package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
- package/clients/cli/commands/layout.doc.mjs +34 -0
- package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
- package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
- package/clients/cli/commands/layout.mjs +275 -0
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/text-json-parity.test.mjs +17 -0
- package/clients/cli/index.mjs +4 -0
- package/clients/cli/lib/exit-codes.test.mjs +8 -1
- package/clients/cli/lib/json-shim.mjs +14 -24
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +8 -0
- package/foundation/discovery/template-adapter.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +12 -0
- package/foundation/response/response-types.doc.mjs +17 -0
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.mjs +2 -2
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/package.json +9 -9
- package/api/integration/pack-check.lifecycle-output.test.mjs +0 -105
- package/api/upgrade/run/files-changed.test.mjs +0 -111
- package/assets/codemods/file-count.test.mjs +0 -163
- package/clients/cli/lib/parse-error-format.test.mjs +0 -81
package/README.md
CHANGED
|
@@ -89,6 +89,7 @@ Options:
|
|
|
89
89
|
| `hook` | List hooks or print hook docs |
|
|
90
90
|
| `init` | Initialize the design system in your project |
|
|
91
91
|
| `integration` | Author and verify an Astryx integration package |
|
|
92
|
+
| `layout` | Generate XDS layouts from compressed expressions (XLE/XLO) |
|
|
92
93
|
| `search` | Search components, hooks, docs, and templates in one ranked list |
|
|
93
94
|
| `swizzle` | Copy component source for customization |
|
|
94
95
|
| `template` | Inject a page or block template |
|
|
@@ -474,6 +475,9 @@ Every response has a `type` discriminant. The full set is below (generated from
|
|
|
474
475
|
| `integration.template-conflicts` | The integration identity, structural issues, and non-blocking Core template-id conflicts as {id, severity: warning, integrationPackage, integrationType, integrationName, coreMatches, message, command}. |
|
|
475
476
|
| `integration.component-conflicts` | The integration identity, structural issues, and non-blocking conflicts where an integration component name is also owned by Core; each conflict includes the exact package-qualified command. |
|
|
476
477
|
| `integration.doc-conflicts` | The integration identity, structural issues, and Core doc overlaps. Each finding includes `severity` (`info` \| `error`) and `relationship` (`replaces` \| `extends` \| `accidental`). |
|
|
478
|
+
| `layout.expand` | The expansion: parsed form, generated TSX code, componentsUsed, states (count of useState hooks scaffolded), todos, blocksReferenced (each {name, mode}), warnings, and written (the output path, or null when nothing was written). |
|
|
479
|
+
| `layout.check` | The validation result: a valid flag, the detected form, errors (each with line/col, message, formatted text, and suggestions), warnings, and the expression re-printed in both canonical surfaces (compact and outline). |
|
|
480
|
+
| `layout.grammar` | The XLE/XLO grammar cheatsheet: a text field with the full reference plus an aliases map (short name → canonical component) generated from this install's registry. |
|
|
477
481
|
|
|
478
482
|
<!-- END GENERATED: response-types -->
|
|
479
483
|
<!-- Generated by scripts/generate-cli-readme.mjs from the response-types EnumDoc. Run `pnpm -F @astryxdesign/cli readme`. -->
|
|
@@ -574,12 +578,12 @@ There is no factory: write a plain object. For editor autocomplete and
|
|
|
574
578
|
type-checking, annotate it with the `AstryxConfig` type exported from
|
|
575
579
|
`@astryxdesign/cli/authoring`.
|
|
576
580
|
|
|
577
|
-
| Field | Type | Purpose
|
|
578
|
-
| ----------------------------- | ------------------------------ |
|
|
579
|
-
| `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)).
|
|
580
|
-
| `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker.
|
|
581
|
-
| `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat).
|
|
582
|
-
| `experimental.xle.components` | `Record<string, XleComponent>` |
|
|
581
|
+
| Field | Type | Purpose |
|
|
582
|
+
| ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
583
|
+
| `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)). |
|
|
584
|
+
| `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker. |
|
|
585
|
+
| `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat). |
|
|
586
|
+
| `experimental.xle.components` | `Record<string, XleComponent>` | Register app-local components so layout (XLE) expressions can reference them by name. Unstable. |
|
|
583
587
|
|
|
584
588
|
The config is validated against a strict schema when the CLI loads it, so an
|
|
585
589
|
unknown field is a hard error rather than a silent no-op. `astryx doctor`
|
package/api/doctor/doctor.d.mts
CHANGED
|
@@ -257,20 +257,15 @@ export type DoctorContext = {
|
|
|
257
257
|
* could not be built, when it could not.
|
|
258
258
|
*/
|
|
259
259
|
docsCatalogError?: string | null | undefined;
|
|
260
|
+
/**
|
|
261
|
+
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
262
|
+
*/
|
|
260
263
|
integrationIssues?: {
|
|
261
264
|
package: string;
|
|
262
265
|
code: string;
|
|
263
266
|
severity: "warning" | "error";
|
|
264
267
|
message: string;
|
|
265
268
|
}[] | null | undefined;
|
|
266
|
-
/**
|
|
267
|
-
* installed dependencies whose integration manifest could not be loaded
|
|
268
|
-
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
269
|
-
*/
|
|
270
|
-
autolinkFailures?: {
|
|
271
|
-
spec: string;
|
|
272
|
-
error: string;
|
|
273
|
-
}[] | null | undefined;
|
|
274
269
|
/**
|
|
275
270
|
* - Error thrown while resolving the config
|
|
276
271
|
* path (e.g. multiple config files present), surfaced by checkConfig as a FAIL.
|
package/api/doctor/doctor.mjs
CHANGED
|
@@ -26,11 +26,7 @@ import * as path from 'node:path';
|
|
|
26
26
|
import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
|
|
27
27
|
import {CLI_ROOT, findCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
|
|
28
28
|
import {explainPackageManager, getCliInvocation} from '../../foundation/env/package-manager.mjs';
|
|
29
|
-
import {
|
|
30
|
-
findConfigPath,
|
|
31
|
-
Project,
|
|
32
|
-
providerLedgerOf,
|
|
33
|
-
} from '../../foundation/config/project.mjs';
|
|
29
|
+
import {findConfigPath, Project} from '../../foundation/config/project.mjs';
|
|
34
30
|
import {DocsCatalog} from '../../foundation/discovery/docs-discovery.mjs';
|
|
35
31
|
import {buildDocsIndexData} from '../../foundation/discovery/docs-section-key.mjs';
|
|
36
32
|
import {
|
|
@@ -80,8 +76,6 @@ import {semverCompare, isValidSemver, satisfiesRange} from '../../foundation/env
|
|
|
80
76
|
* @property {string|null} [docsCatalogError] - Why the project's docs catalog
|
|
81
77
|
* could not be built, when it could not.
|
|
82
78
|
* @property {Array<{package: string, code: string, severity: 'warning'|'error', message: string}>|null} [integrationIssues]
|
|
83
|
-
* @property {Array<{spec: string, error: string}>|null} [autolinkFailures]
|
|
84
|
-
* installed dependencies whose integration manifest could not be loaded
|
|
85
79
|
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
86
80
|
* @property {Error|null} [configError] - Error thrown while resolving the config
|
|
87
81
|
* path (e.g. multiple config files present), surfaced by checkConfig as a FAIL.
|
|
@@ -423,9 +417,6 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
423
417
|
const implicit = ctx.integrations.filter(
|
|
424
418
|
integration => integration.__autolinked,
|
|
425
419
|
);
|
|
426
|
-
// A dependency whose manifest cannot be loaded leaves no loaded record, so
|
|
427
|
-
// without this line doctor would say no dependency ships a manifest at all.
|
|
428
|
-
const unreadable = describeUnreadableManifests(ctx.autolinkFailures ?? []);
|
|
429
420
|
|
|
430
421
|
if (implicit.length === 0) {
|
|
431
422
|
return {
|
|
@@ -433,12 +424,9 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
433
424
|
label,
|
|
434
425
|
status: 'info',
|
|
435
426
|
message:
|
|
436
|
-
|
|
427
|
+
ctx.integrations.length > 0
|
|
437
428
|
? 'None — every loaded integration is named in astryx.config.'
|
|
438
|
-
:
|
|
439
|
-
? 'None loaded.'
|
|
440
|
-
: 'None — no installed dependency ships an astryx.integration.* manifest.') +
|
|
441
|
-
unreadable,
|
|
429
|
+
: 'None — no installed dependency ships an astryx.integration.* manifest.',
|
|
442
430
|
};
|
|
443
431
|
}
|
|
444
432
|
|
|
@@ -451,25 +439,11 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
451
439
|
integration.__spec && integration.__spec !== integration.name
|
|
452
440
|
? ` (declared as "${integration.__spec}")`
|
|
453
441
|
: '';
|
|
454
|
-
|
|
455
|
-
// claim, and `integration-issues` reports the ones that are not true.
|
|
456
|
-
const declared = ['components', 'templates', 'themes', 'docs', 'codemods'].filter(
|
|
442
|
+
const roots = ['components', 'templates', 'themes', 'docs', 'codemods'].filter(
|
|
457
443
|
root => integration[/** @type {'components'} */ (root)],
|
|
458
444
|
);
|
|
459
|
-
const missing = declared.filter(root => {
|
|
460
|
-
const dir = integration[/** @type {'components'} */ (root)];
|
|
461
|
-
if (typeof dir !== 'string') return false;
|
|
462
|
-
const base =
|
|
463
|
-
typeof integration.__packageDir === 'string'
|
|
464
|
-
? integration.__packageDir
|
|
465
|
-
: (ctx.cwd ?? process.cwd());
|
|
466
|
-
return !fs.existsSync(path.isAbsolute(dir) ? dir : path.resolve(base, dir));
|
|
467
|
-
});
|
|
468
|
-
const roots = declared.filter(root => !missing.includes(root));
|
|
469
445
|
const contributes = roots.length > 0 ? roots.join(', ') : 'nothing';
|
|
470
|
-
|
|
471
|
-
missing.length > 0 ? ` (declared ${missing.join(', ')} missing on disk)` : '';
|
|
472
|
-
return `${integration.name}${version}${alias} from ${integration.__dependencyField}, contributing ${contributes}${absent}`;
|
|
446
|
+
return `${integration.name}${version}${alias} from ${integration.__dependencyField}, contributing ${contributes}`;
|
|
473
447
|
});
|
|
474
448
|
|
|
475
449
|
const plural = implicit.length === 1 ? '' : 's';
|
|
@@ -479,8 +453,7 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
479
453
|
status: 'info',
|
|
480
454
|
message:
|
|
481
455
|
`${implicit.length} integration${plural} loaded from installed ` +
|
|
482
|
-
`dependencies with no astryx.config entry: ${described.join('; ')}
|
|
483
|
-
unreadable,
|
|
456
|
+
`dependencies with no astryx.config entry: ${described.join('; ')}.`,
|
|
484
457
|
fix:
|
|
485
458
|
'Nothing to fix. Keep these dependencies installed. The CLI links them ' +
|
|
486
459
|
'from package.json, so an unused-dependency check that looks only for ' +
|
|
@@ -489,30 +462,6 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
489
462
|
};
|
|
490
463
|
}
|
|
491
464
|
|
|
492
|
-
/**
|
|
493
|
-
* The sentence `implicit-integrations` adds for dependencies whose manifest
|
|
494
|
-
* could not be loaded, or '' when there are none. Still informational: the
|
|
495
|
-
* package is a dependency's own bug, which `doctor integration validate`
|
|
496
|
-
* diagnoses, but doctor must not report it as absent.
|
|
497
|
-
* @param {Array<{spec: string, error: string}>} failures
|
|
498
|
-
* @returns {string}
|
|
499
|
-
*/
|
|
500
|
-
function describeUnreadableManifests(failures) {
|
|
501
|
-
if (failures.length === 0) return '';
|
|
502
|
-
const one = failures.length === 1;
|
|
503
|
-
const listed = failures
|
|
504
|
-
.map(({spec, error}) => {
|
|
505
|
-
const reason = String(error).split('\n')[0].slice(0, 160);
|
|
506
|
-
return `${spec} (${reason})`;
|
|
507
|
-
})
|
|
508
|
-
.join('; ');
|
|
509
|
-
return (
|
|
510
|
-
` ${failures.length} installed ${one ? 'dependency ships' : 'dependencies ship'} ` +
|
|
511
|
-
`an astryx.integration.* manifest that could not be loaded, so ${one ? 'it contributes' : 'they contribute'} ` +
|
|
512
|
-
`nothing: ${listed}. Run \`astryx doctor integration validate <package>\` for details.`
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
465
|
/**
|
|
517
466
|
* Check 7 — agent docs exist and contain the Astryx section markers.
|
|
518
467
|
* @param {DoctorContext} ctx
|
|
@@ -789,26 +738,12 @@ export function checkProviderIdentity(ctx) {
|
|
|
789
738
|
integration =>
|
|
790
739
|
integration.providerId != null && integration.__loadError == null,
|
|
791
740
|
).length;
|
|
792
|
-
// An integration that could not be read has no provider ID to check, so the
|
|
793
|
-
// count above is not a complete survey. Say so instead of counting silently.
|
|
794
|
-
const unread = ctx.integrations.filter(
|
|
795
|
-
integration => integration.__loadError != null,
|
|
796
|
-
).length;
|
|
797
|
-
const unreadNote =
|
|
798
|
-
unread === 0
|
|
799
|
-
? ''
|
|
800
|
-
: unread === 1
|
|
801
|
-
? ' 1 loaded integration could not be read, so its provider ID is unknown.'
|
|
802
|
-
: ` ${unread} loaded integrations could not be read, so their provider IDs are unknown.`;
|
|
803
741
|
if (count === 0) {
|
|
804
742
|
return {
|
|
805
743
|
id,
|
|
806
744
|
label,
|
|
807
745
|
status: 'info',
|
|
808
|
-
message:
|
|
809
|
-
(unread === 0
|
|
810
|
-
? 'None — no loaded integration has a provider identity.'
|
|
811
|
-
: 'No readable integration has a provider identity.') + unreadNote,
|
|
746
|
+
message: 'None — no loaded integration has a provider identity.',
|
|
812
747
|
};
|
|
813
748
|
}
|
|
814
749
|
return {
|
|
@@ -816,10 +751,9 @@ export function checkProviderIdentity(ctx) {
|
|
|
816
751
|
label,
|
|
817
752
|
status: 'pass',
|
|
818
753
|
message:
|
|
819
|
-
|
|
754
|
+
count === 1
|
|
820
755
|
? '1 loaded integration has its own provider ID.'
|
|
821
|
-
: `${count} loaded integrations each have their own provider ID
|
|
822
|
-
unreadNote,
|
|
756
|
+
: `${count} loaded integrations each have their own provider ID.`,
|
|
823
757
|
};
|
|
824
758
|
}
|
|
825
759
|
|
|
@@ -1264,25 +1198,11 @@ export async function runChecks(options = {}) {
|
|
|
1264
1198
|
let docsCatalogError = null;
|
|
1265
1199
|
/** @type {Array<{package: string, code: string, severity: 'warning'|'error', message: string}>|null} */
|
|
1266
1200
|
let integrationIssues = null;
|
|
1267
|
-
/** @type {Array<{spec: string, error: string}>|null} */
|
|
1268
|
-
let autolinkFailures = null;
|
|
1269
1201
|
try {
|
|
1270
1202
|
const project = await Project.load(cwd);
|
|
1271
1203
|
configTheme =
|
|
1272
1204
|
/** @type {{theme?: string}} */ (project.config ?? {}).theme ?? null;
|
|
1273
1205
|
integrations = project.loadedIntegrations;
|
|
1274
|
-
// An installed dependency whose manifest cannot be loaded is kept out of
|
|
1275
|
-
// loadedIntegrations on purpose. The provider ledger still records it.
|
|
1276
|
-
autolinkFailures = [...providerLedgerOf(project).values()]
|
|
1277
|
-
.filter(
|
|
1278
|
-
entry =>
|
|
1279
|
-
entry.outcome === 'load-failed' &&
|
|
1280
|
-
entry.candidate.source === 'autolinked',
|
|
1281
|
-
)
|
|
1282
|
-
.map(entry => ({
|
|
1283
|
-
spec: entry.candidate.spec ?? entry.label,
|
|
1284
|
-
error: entry.error ?? 'its manifest could not be loaded',
|
|
1285
|
-
}));
|
|
1286
1206
|
try {
|
|
1287
1207
|
docsCatalog = await project.docs();
|
|
1288
1208
|
docsCatalogIssues = (await project.issues()).filter(
|
|
@@ -1309,7 +1229,6 @@ export async function runChecks(options = {}) {
|
|
|
1309
1229
|
docsCatalogIssues,
|
|
1310
1230
|
docsCatalogError,
|
|
1311
1231
|
integrationIssues,
|
|
1312
|
-
autolinkFailures,
|
|
1313
1232
|
configError,
|
|
1314
1233
|
};
|
|
1315
1234
|
|
|
@@ -354,31 +354,16 @@ describe('checkImplicitIntegrations', () => {
|
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
it('names the package, the field, and what it contributes', () => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
components: dir('components'),
|
|
368
|
-
templates: dir('templates'),
|
|
369
|
-
themes: dir('themes'),
|
|
370
|
-
}),
|
|
371
|
-
],
|
|
372
|
-
});
|
|
373
|
-
expect(c.message).toContain('@acme/widgets@1.0.0');
|
|
374
|
-
expect(c.message).toContain('from dependencies');
|
|
375
|
-
expect(c.message).toContain(
|
|
376
|
-
'contributing components, templates, themes',
|
|
377
|
-
);
|
|
378
|
-
expect(c.message).not.toContain('missing on disk');
|
|
379
|
-
} finally {
|
|
380
|
-
fs.rmSync(root, {recursive: true, force: true});
|
|
381
|
-
}
|
|
357
|
+
const c = checkImplicitIntegrations({
|
|
358
|
+
integrations: [
|
|
359
|
+
autolinked({templates: '/abs/templates', themes: '/abs/themes'}),
|
|
360
|
+
],
|
|
361
|
+
});
|
|
362
|
+
expect(c.message).toContain('@acme/widgets@1.0.0');
|
|
363
|
+
expect(c.message).toContain('from dependencies');
|
|
364
|
+
expect(c.message).toContain(
|
|
365
|
+
'contributing components, templates, themes',
|
|
366
|
+
);
|
|
382
367
|
});
|
|
383
368
|
|
|
384
369
|
it('names the declared key too when an npm alias makes them differ', () => {
|
|
@@ -921,100 +906,3 @@ describe('checkDocsProgressiveDisclosure languages', () => {
|
|
|
921
906
|
expect(c.message).not.toMatch(/deploying overview:/);
|
|
922
907
|
});
|
|
923
908
|
});
|
|
924
|
-
|
|
925
|
-
describe('doctor says what it could not check', () => {
|
|
926
|
-
const CORE = {
|
|
927
|
-
'node_modules/@astryxdesign/core/package.json': JSON.stringify({
|
|
928
|
-
name: '@astryxdesign/core',
|
|
929
|
-
version: '0.6.3',
|
|
930
|
-
}),
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
/** Installed dependency whose manifest declares roots that do not exist. */
|
|
934
|
-
const dangling = {
|
|
935
|
-
'node_modules/@acme/dangling/package.json': JSON.stringify({
|
|
936
|
-
name: '@acme/dangling',
|
|
937
|
-
version: '2.0.0',
|
|
938
|
-
}),
|
|
939
|
-
'node_modules/@acme/dangling/astryx.integration.mjs':
|
|
940
|
-
"export default {providerId: 'acme-dangling', components: './components', templates: './templates', docs: './docs'};\n",
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
/** Installed dependency whose manifest cannot be parsed at all. */
|
|
944
|
-
const broken = {
|
|
945
|
-
'node_modules/@acme/broken/package.json': JSON.stringify({
|
|
946
|
-
name: '@acme/broken',
|
|
947
|
-
version: '1.0.0',
|
|
948
|
-
}),
|
|
949
|
-
'node_modules/@acme/broken/astryx.integration.mjs':
|
|
950
|
-
'export default { this is not valid javascript ((\n',
|
|
951
|
-
};
|
|
952
|
-
|
|
953
|
-
/** @param {Record<string, string>} extra @param {string[]} deps */
|
|
954
|
-
const project = (extra, deps) =>
|
|
955
|
-
mkProject({
|
|
956
|
-
'package.json': JSON.stringify({
|
|
957
|
-
name: 'consumer',
|
|
958
|
-
version: '1.0.0',
|
|
959
|
-
dependencies: Object.fromEntries(
|
|
960
|
-
['@astryxdesign/core', ...deps].map(d => [d, '1.0.0']),
|
|
961
|
-
),
|
|
962
|
-
}),
|
|
963
|
-
...CORE,
|
|
964
|
-
...extra,
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
// An unparseable manifest is kept out of the loaded set on purpose, and
|
|
968
|
-
// doctor used to report that no installed dependency ships a manifest.
|
|
969
|
-
it('names an installed dependency whose manifest cannot be loaded', async () => {
|
|
970
|
-
const report = (await doctor({cwd: project(broken, ['@acme/broken'])})).data;
|
|
971
|
-
const check = report.checks.find(c => c.id === 'implicit-integrations');
|
|
972
|
-
|
|
973
|
-
expect(check.status).toBe('info');
|
|
974
|
-
expect(check.message).toContain('@acme/broken');
|
|
975
|
-
expect(check.message).toContain('could not be loaded');
|
|
976
|
-
expect(check.message).not.toContain('no installed dependency ships');
|
|
977
|
-
expect(report.summary.fail).toBe(0);
|
|
978
|
-
}, SLOW);
|
|
979
|
-
|
|
980
|
-
it('does not claim contributions from roots that are missing on disk', async () => {
|
|
981
|
-
const report = (await doctor({cwd: project(dangling, ['@acme/dangling'])}))
|
|
982
|
-
.data;
|
|
983
|
-
const implicit = report.checks.find(c => c.id === 'implicit-integrations');
|
|
984
|
-
const issues = report.checks.find(c => c.id === 'integration-issues');
|
|
985
|
-
|
|
986
|
-
expect(implicit.message).toContain('contributing nothing');
|
|
987
|
-
expect(implicit.message).toContain('missing on disk');
|
|
988
|
-
expect(implicit.message).not.toContain('contributing components');
|
|
989
|
-
expect(issues.status).toBe('warn');
|
|
990
|
-
expect(issues.message).toContain('@acme/dangling');
|
|
991
|
-
}, SLOW);
|
|
992
|
-
|
|
993
|
-
it('still says no dependency ships a manifest when none does', async () => {
|
|
994
|
-
const report = (await doctor({cwd: project({}, [])})).data;
|
|
995
|
-
const check = report.checks.find(c => c.id === 'implicit-integrations');
|
|
996
|
-
|
|
997
|
-
expect(check.message).toBe(
|
|
998
|
-
'None — no installed dependency ships an astryx.integration.* manifest.',
|
|
999
|
-
);
|
|
1000
|
-
}, SLOW);
|
|
1001
|
-
|
|
1002
|
-
it('says how many integrations could not be read, rather than counting silently', () => {
|
|
1003
|
-
/** @type {any} */
|
|
1004
|
-
const ctx = {
|
|
1005
|
-
cwd: '/x',
|
|
1006
|
-
nodeVersion: process.versions.node,
|
|
1007
|
-
coreDir: null,
|
|
1008
|
-
configPath: null,
|
|
1009
|
-
configTheme: null,
|
|
1010
|
-
integrations: [
|
|
1011
|
-
{name: '@acme/ok', __spec: '@acme/ok', providerId: 'ok'},
|
|
1012
|
-
{name: '@acme/bad', __spec: '@acme/bad', __loadError: 'boom'},
|
|
1013
|
-
],
|
|
1014
|
-
};
|
|
1015
|
-
const check = checkProviderIdentity(ctx);
|
|
1016
|
-
|
|
1017
|
-
expect(check.message).toContain('1 loaded integration has its own provider ID.');
|
|
1018
|
-
expect(check.message).toContain('could not be read');
|
|
1019
|
-
});
|
|
1020
|
-
});
|
package/api/index.d.mts
CHANGED
|
@@ -33,12 +33,14 @@ export * from "./gap-report/gap-report.type.mjs";
|
|
|
33
33
|
export * from "./upgrade/upgrade.type.mjs";
|
|
34
34
|
export * from "./init/init.type.mjs";
|
|
35
35
|
export * from "./doctor/doctor.type.mjs";
|
|
36
|
+
export * from "./layout/layout.type.mjs";
|
|
36
37
|
export * from "./integration/integration-authoring.type.mjs";
|
|
37
38
|
export * from "./integration/pack-check.type.mjs";
|
|
38
39
|
export * from "./integration/validate-integration.type.mjs";
|
|
39
40
|
export * from "./integration/authoring-checks.type.mjs";
|
|
40
41
|
export type Logger = import("./logger.mjs").Logger;
|
|
41
42
|
export { themeBuild, themeAdd, themeTemplate, themeList, themeListAvailable, themeTargets, themePaletteGenerate, generateTonalPalette, listThemes } from "./theme/theme.mjs";
|
|
43
|
+
export { layoutExpand, layoutCheck, layoutGrammar } from "./layout/layout.mjs";
|
|
42
44
|
export { integrationAdd, integrationAddAgentDoc, integrationAddCodemod, integrationAddComponent, integrationAddDoc, integrationAddTemplate } from "./integration/add-contribution.mjs";
|
|
43
45
|
export { validateIntegration, summarizeIssues } from "./integration/validate-integration.mjs";
|
|
44
46
|
export { integrationTemplateConflicts, integrationComponentConflicts, integrationDocConflicts } from "./integration/authoring-checks.mjs";
|
package/api/index.mjs
CHANGED
|
@@ -44,6 +44,7 @@ export {gapReport} from './gap-report/gap-report.mjs';
|
|
|
44
44
|
export {upgrade} from './upgrade/upgrade.mjs';
|
|
45
45
|
export {init} from './init/init.mjs';
|
|
46
46
|
export {doctor} from './doctor/doctor.mjs';
|
|
47
|
+
export {layoutExpand, layoutCheck, layoutGrammar} from './layout/layout.mjs';
|
|
47
48
|
export {
|
|
48
49
|
integrationAdd,
|
|
49
50
|
integrationAddAgentDoc,
|
|
@@ -92,6 +93,7 @@ export * from './gap-report/gap-report.type.mjs';
|
|
|
92
93
|
export * from './upgrade/upgrade.type.mjs';
|
|
93
94
|
export * from './init/init.type.mjs';
|
|
94
95
|
export * from './doctor/doctor.type.mjs';
|
|
96
|
+
export * from './layout/layout.type.mjs';
|
|
95
97
|
export * from './integration/integration-authoring.type.mjs';
|
|
96
98
|
export * from './integration/pack-check.type.mjs';
|
|
97
99
|
export * from './integration/validate-integration.type.mjs';
|
|
@@ -162,22 +162,6 @@ export function parseNpmPackOutput(output) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
/**
|
|
166
|
-
* Summarize the JSON error npm prints on stdout when `--json` pack fails.
|
|
167
|
-
* @param {string} output
|
|
168
|
-
* @returns {string}
|
|
169
|
-
*/
|
|
170
|
-
function npmPackErrorDetail(output) {
|
|
171
|
-
try {
|
|
172
|
-
const error = JSON.parse(output)?.error;
|
|
173
|
-
return [error?.summary, error?.detail]
|
|
174
|
-
.filter(part => typeof part === 'string' && part.trim() !== '')
|
|
175
|
-
.join(': ');
|
|
176
|
-
} catch {
|
|
177
|
-
return (output || '').trim();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
165
|
/**
|
|
182
166
|
* Run `npm pack --json` with output directed to `destDir` so no preexisting
|
|
183
167
|
* tgz is overwritten. This intentionally runs the package lifecycle, matching
|
|
@@ -191,25 +175,16 @@ function npmPackErrorDetail(output) {
|
|
|
191
175
|
function runNpmPack(packageDir, destDir) {
|
|
192
176
|
const result = spawnSync(
|
|
193
177
|
'npm',
|
|
194
|
-
[
|
|
195
|
-
'pack',
|
|
196
|
-
'--json',
|
|
197
|
-
'--silent',
|
|
198
|
-
// Lifecycle scripts still run; background mode keeps their output off
|
|
199
|
-
// the stdout that carries the JSON result.
|
|
200
|
-
'--foreground-scripts=false',
|
|
201
|
-
`--pack-destination=${destDir}`,
|
|
202
|
-
],
|
|
178
|
+
['pack', '--json', '--silent', `--pack-destination=${destDir}`],
|
|
203
179
|
{cwd: packageDir, encoding: 'utf-8', timeout: 60_000},
|
|
204
180
|
);
|
|
205
181
|
if (result.error) {
|
|
206
182
|
throw new Error(`Could not start npm pack: ${result.error.message}`);
|
|
207
183
|
}
|
|
208
184
|
if (result.status !== 0) {
|
|
209
|
-
const
|
|
210
|
-
(result.stderr || '').trim() || npmPackErrorDetail(result.stdout);
|
|
185
|
+
const stderr = (result.stderr || '').trim();
|
|
211
186
|
throw new Error(
|
|
212
|
-
`npm pack failed (exit ${result.status})${
|
|
187
|
+
`npm pack failed (exit ${result.status})${stderr ? `: ${stderr}` : ''}.`,
|
|
213
188
|
);
|
|
214
189
|
}
|
|
215
190
|
return parseNpmPackOutput(result.stdout);
|
package/api/json/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type * from '../gap-report/gap-report.type.mjs';
|
|
|
26
26
|
export type * from '../upgrade/upgrade.type.mjs';
|
|
27
27
|
export type * from '../init/init.type.mjs';
|
|
28
28
|
export type * from '../doctor/doctor.type.mjs';
|
|
29
|
+
export type * from '../layout/layout.type.mjs';
|
|
29
30
|
export type * from '../integration/validate-integration.type.mjs';
|
|
30
31
|
export type * from '../integration/authoring-checks.type.mjs';
|
|
31
32
|
export type * from '../integration/pack-check.type.mjs';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/** @param {import('../../foundation/xle/xle-ast').RawIssue} issue */
|
|
5
|
+
export function formatIssue(issue: import("../../foundation/xle/xle-ast").RawIssue): string;
|
|
6
|
+
/**
|
|
7
|
+
* Parse + validate, throwing structured XDSErrors on failure.
|
|
8
|
+
* Returns {doc, registry, blocks, warnings}.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} expression
|
|
11
|
+
* @param {{form?: 'compact'|'outline'|'auto', loose?: boolean, cwd?: string}} [options]
|
|
12
|
+
*/
|
|
13
|
+
export function analyze(expression: string, { form, loose, cwd }?: {
|
|
14
|
+
form?: "compact" | "outline" | "auto";
|
|
15
|
+
loose?: boolean;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
doc: import("../../foundation/xle/xle-ast").XLEDoc;
|
|
19
|
+
registry: import("../../foundation/xle/xle-ast").Registry;
|
|
20
|
+
blocks: LayoutBlock[];
|
|
21
|
+
errors: import("../../foundation/xle/xle-ast").RawIssue[];
|
|
22
|
+
warnings: import("../../foundation/xle/xle-ast").RawIssue[];
|
|
23
|
+
}>;
|
|
24
|
+
export type LayoutBlock = {
|
|
25
|
+
dirName: string;
|
|
26
|
+
name: string;
|
|
27
|
+
kind: "template" | "component";
|
|
28
|
+
type?: string | undefined;
|
|
29
|
+
description?: string | undefined;
|
|
30
|
+
category?: string | undefined;
|
|
31
|
+
importPath?: string | undefined;
|
|
32
|
+
filePath?: string | undefined;
|
|
33
|
+
isDefault?: boolean | undefined;
|
|
34
|
+
};
|