@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ebaebc4
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 +2 -1
- package/api/build/build.type.d.mts +2 -2
- package/api/build/build.type.mjs +2 -2
- package/api/component/component.type.d.mts +6 -6
- package/api/component/component.type.mjs +19 -19
- package/api/discover/discover.type.d.mts +4 -4
- package/api/discover/discover.type.mjs +10 -10
- package/api/docs/_adapter.d.mts +37 -24
- package/api/docs/_adapter.mjs +169 -83
- package/api/docs/compiled-topics.test.mjs +78 -0
- package/api/docs/detail/detail.mjs +14 -63
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +44 -20
- package/api/docs/detail/section/section.test.mjs +41 -0
- package/api/docs/docs.d.mts +7 -2
- package/api/docs/docs.doc.mjs +27 -10
- package/api/docs/docs.mjs +16 -9
- package/api/docs/docs.test.mjs +6 -0
- package/api/docs/docs.type.d.mts +40 -3
- package/api/docs/docs.type.mjs +36 -8
- package/api/docs/index/index.d.mts +18 -0
- package/api/docs/index/index.mjs +32 -0
- package/api/docs/index/index.test.mjs +62 -0
- package/api/docs/integrationDocs.test.mjs +106 -0
- package/api/doctor/doctor.d.mts +48 -0
- package/api/doctor/doctor.mjs +232 -0
- package/api/doctor/doctor.test.mjs +196 -0
- package/api/hook/hook.type.d.mts +3 -3
- package/api/hook/hook.type.mjs +11 -11
- package/api/hook/list/list.d.mts +1 -1
- package/api/integration/add-contribution.mjs +5 -3
- package/api/integration/add-contribution.test.mjs +4 -4
- package/api/integration/integration-authoring.type.d.mts +1 -1
- package/api/integration/pack-check.mjs +49 -7
- package/api/integration/pack-check.test.mjs +249 -0
- package/api/search/search.d.mts +1 -1
- package/api/search/search.mjs +5 -5
- package/api/search/search.type.d.mts +2 -2
- package/api/search/search.type.mjs +1 -1
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/template.d.mts +1 -1
- package/api/template/template.type.d.mts +6 -6
- package/api/template/template.type.mjs +12 -12
- package/api/theme/build/build.mjs +20 -6
- package/api/theme/build/build.test.mjs +127 -0
- package/api/theme/palette/generate/generate.mjs +1 -1
- package/api/theme/palette/generate/generator.d.mts +10 -13
- package/api/theme/palette/generate/generator.mjs +7 -3
- package/api/theme/theme.type.d.mts +170 -11
- package/api/theme/theme.type.mjs +94 -27
- package/api/upgrade/_adapter.mjs +71 -5
- package/api/upgrade/project-context.test.mjs +272 -0
- package/api/upgrade/upgrade.doc.mjs +4 -3
- package/api/upgrade/upgrade.type.d.mts +5 -5
- package/api/upgrade/upgrade.type.mjs +11 -11
- package/assets/codemods/integration-discovery.mjs +40 -2
- package/assets/codemods/integration-discovery.test.mjs +58 -0
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
- package/assets/docs/README.md +9 -0
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/cli-integrations.doc.mjs +86 -15
- package/assets/docs/styling-libraries.doc.mjs +1 -1
- package/assets/docs/working-with-ai.doc.mjs +1 -1
- package/authoring/_shared/contract.ts +22 -0
- package/authoring/codemod/codemod.doc.mjs +6 -1
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +8 -6
- package/authoring/config/parse.d.mts +13 -13
- package/authoring/config/parse.mjs +8 -8
- package/authoring/config/type.ts +3 -3
- package/authoring/debug/parse.d.mts +5 -5
- package/authoring/debug/parse.mjs +3 -3
- package/authoring/doctypes/_schema.d.mts +788 -23
- package/authoring/doctypes/_schema.mjs +492 -39
- package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +62 -0
- package/authoring/doctypes/base/type.ts +40 -0
- package/authoring/doctypes/command/command.doc.mjs +3 -2
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +3 -2
- package/authoring/doctypes/component/component.doc.mjs +6 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +4 -3
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +3 -1
- package/authoring/doctypes/function/function.doc.mjs +4 -0
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +6 -2
- package/authoring/doctypes/hook/hook.doc.mjs +4 -0
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +3 -2
- package/authoring/doctypes/legacy.d.mts +8 -6
- package/authoring/doctypes/legacy.mjs +5 -4
- package/authoring/doctypes/load-contract.test.mjs +207 -0
- package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +132 -0
- package/authoring/doctypes/namespace/parse.d.mts +12 -0
- package/authoring/doctypes/namespace/parse.mjs +25 -0
- package/authoring/doctypes/namespace/parse.test.mjs +165 -0
- package/authoring/doctypes/namespace/type.ts +71 -0
- package/authoring/doctypes/parse.d.mts +20 -18
- package/authoring/doctypes/parse.mjs +16 -10
- package/authoring/doctypes/parse.test.mjs +77 -3
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +8 -5
- package/authoring/doctypes/reference/reference.doc.mjs +17 -4
- package/authoring/doctypes/reference/type.ts +51 -5
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +3 -2
- package/authoring/doctypes/template/parse.d.mts +92 -1
- package/authoring/doctypes/template/parse.mjs +36 -2
- package/authoring/doctypes/template/parse.test.mjs +8 -2
- package/authoring/doctypes/template/template.doc.mjs +4 -0
- package/authoring/doctypes/template/type.ts +5 -2
- package/authoring/doctypes/types.ts +10 -9
- package/authoring/gap-report/parse.d.mts +10 -10
- package/authoring/gap-report/parse.mjs +6 -6
- package/authoring/gap-report/type.ts +1 -1
- package/authoring/identity/identity.doc.d.mts +9 -0
- package/authoring/identity/identity.doc.mjs +61 -0
- package/authoring/identity/type.ts +132 -0
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +49 -17
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +13 -6
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +10 -1
- package/authoring/integration/schema.d.mts +6 -4
- package/authoring/integration/schema.mjs +9 -3
- package/authoring/integration/type.ts +23 -6
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/commands/docs.doc.mjs +13 -3
- package/clients/cli/commands/docs.mjs +121 -21
- package/clients/cli/commands/docs.test.mjs +88 -0
- package/clients/cli/commands/integration-authoring.test.mjs +13 -9
- package/clients/cli/commands/theme-palette-generate.doc.mjs +8 -4
- package/clients/cli/commands/upgrade.doc.mjs +2 -2
- package/clients/cli/formatters/index.mjs +162 -1
- package/clients/cli/formatters/index.test.mjs +91 -0
- package/clients/cli/lib/manifest.mjs +7 -2
- package/foundation/config/project.mjs +21 -6
- package/foundation/discovery/authoring-self-docs.d.mts +69 -0
- package/foundation/discovery/authoring-self-docs.mjs +214 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +154 -0
- package/foundation/discovery/component-discovery.d.mts +1 -1
- package/foundation/discovery/component-discovery.mjs +2 -1
- package/foundation/discovery/docs-discovery.d.mts +11 -4
- package/foundation/discovery/docs-discovery.mjs +208 -88
- package/foundation/discovery/docs-discovery.test.mjs +279 -13
- package/foundation/discovery/docs-output-budget.d.mts +28 -0
- package/foundation/discovery/docs-output-budget.mjs +50 -0
- package/foundation/discovery/docs-section-key.d.mts +98 -0
- package/foundation/discovery/docs-section-key.mjs +221 -0
- package/foundation/discovery/docs-section-key.test.mjs +224 -0
- package/foundation/discovery/template-adapter.mjs +2 -1
- package/foundation/discovery/theming-targets.test.mjs +4 -0
- package/foundation/doc-compiler/compile.d.mts +162 -0
- package/foundation/doc-compiler/compile.mjs +262 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +687 -0
- package/foundation/doc-compiler/ir.d.mts +9 -0
- package/foundation/doc-compiler/ir.mjs +287 -0
- package/foundation/doc-compiler/lenses.d.mts +33 -0
- package/foundation/doc-compiler/lenses.mjs +127 -0
- package/foundation/identity/provider-identity.d.mts +90 -0
- package/foundation/identity/provider-identity.mjs +320 -0
- package/foundation/identity/provider-identity.test.mjs +254 -0
- package/foundation/identity/providers.d.mts +7 -0
- package/foundation/identity/providers.mjs +16 -0
- package/foundation/integrations/autolink.mjs +12 -5
- package/foundation/integrations/integration-warnings.mjs +6 -0
- package/foundation/integrations/integrations.d.mts +46 -2
- package/foundation/integrations/integrations.mjs +167 -8
- package/foundation/integrations/integrations.test.mjs +384 -1
- package/foundation/integrations/provider-conflicts.test.mjs +125 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +10 -0
- package/foundation/response/json-contract.test.mjs +46 -17
- package/foundation/response/response-types.doc.mjs +6 -1
- package/package.json +9 -11
|
@@ -388,6 +388,14 @@ async function validatePackedComponentExports(integration, scratchBase) {
|
|
|
388
388
|
components.map(component => component.specifier),
|
|
389
389
|
scratchBase,
|
|
390
390
|
);
|
|
391
|
+
issues.push(
|
|
392
|
+
...validateSpecifierExtensions(
|
|
393
|
+
components.map(({name, specifier}) => ({
|
|
394
|
+
label: `Component "${name}"`,
|
|
395
|
+
specifier,
|
|
396
|
+
})),
|
|
397
|
+
),
|
|
398
|
+
);
|
|
391
399
|
for (const {name, specifier} of components) {
|
|
392
400
|
const result = resolved.get(specifier);
|
|
393
401
|
if (!result?.url || result.error) {
|
|
@@ -427,19 +435,27 @@ async function validatePackedComponentExports(integration, scratchBase) {
|
|
|
427
435
|
*/
|
|
428
436
|
async function validatePackedTemplateExports(integration, scratchBase) {
|
|
429
437
|
const {templates} = await discoverIntegrationTemplatesForOne(integration);
|
|
430
|
-
const entries = templates.map(template =>
|
|
431
|
-
|
|
432
|
-
specifier: `${integration.name}/${path
|
|
438
|
+
const entries = templates.map(template => {
|
|
439
|
+
const relPath = path
|
|
433
440
|
.relative(integration.__packageDir, template.filePath)
|
|
434
441
|
.split(path.sep)
|
|
435
|
-
.join('/')
|
|
436
|
-
|
|
442
|
+
.join('/');
|
|
443
|
+
const extensionless = relPath.replace(/\.tsx?$/u, '');
|
|
444
|
+
return {
|
|
445
|
+
id: template.dirName,
|
|
446
|
+
specifier: `${integration.name}/${extensionless}`,
|
|
447
|
+
};
|
|
448
|
+
});
|
|
437
449
|
const resolved = resolveConsumerSpecifiers(
|
|
438
450
|
entries.map(entry => entry.specifier),
|
|
439
451
|
scratchBase,
|
|
440
452
|
);
|
|
441
|
-
|
|
442
|
-
|
|
453
|
+
const issues = validateSpecifierExtensions(
|
|
454
|
+
entries.map(({id, specifier}) => ({
|
|
455
|
+
label: `Template "${id}"`,
|
|
456
|
+
specifier,
|
|
457
|
+
})),
|
|
458
|
+
);
|
|
443
459
|
for (const {id, specifier} of entries) {
|
|
444
460
|
const result = resolved.get(specifier);
|
|
445
461
|
if (!result?.url || result.error) {
|
|
@@ -469,6 +485,32 @@ async function validatePackedTemplateExports(integration, scratchBase) {
|
|
|
469
485
|
return issues;
|
|
470
486
|
}
|
|
471
487
|
|
|
488
|
+
const TS_EXTENSION_RE = /\.tsx?$/u;
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Reject specifiers that end in a TypeScript extension — they fail under
|
|
492
|
+
* default `moduleResolution` with TS5097/TS2307 unless the consumer enables
|
|
493
|
+
* `allowImportingTsExtensions`.
|
|
494
|
+
*
|
|
495
|
+
* @param {Array<{label: string, specifier: string}>} entries
|
|
496
|
+
* @returns {Issue[]}
|
|
497
|
+
*/
|
|
498
|
+
function validateSpecifierExtensions(entries) {
|
|
499
|
+
/** @type {Issue[]} */
|
|
500
|
+
const issues = [];
|
|
501
|
+
for (const {label, specifier} of entries) {
|
|
502
|
+
if (TS_EXTENSION_RE.test(specifier)) {
|
|
503
|
+
issues.push(
|
|
504
|
+
error(
|
|
505
|
+
'typescript_extension_in_specifier',
|
|
506
|
+
`${label} advertises import "${specifier}", which ends in a TypeScript extension. A consumer with default moduleResolution will reject it (TS5097). Use an extensionless specifier mapped through the package exports.`,
|
|
507
|
+
),
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return issues;
|
|
512
|
+
}
|
|
513
|
+
|
|
472
514
|
/**
|
|
473
515
|
* Verify an integration package is ready to publish.
|
|
474
516
|
*
|
|
@@ -567,3 +567,252 @@ describe('pack-check mutation tests', () => {
|
|
|
567
567
|
}
|
|
568
568
|
});
|
|
569
569
|
});
|
|
570
|
+
|
|
571
|
+
describe('extensionless subpath resolution', () => {
|
|
572
|
+
it('rejects .tsx public specifiers before publishing', async () => {
|
|
573
|
+
// Manually construct a package with OLD-style .tsx exports keys
|
|
574
|
+
writePackage({
|
|
575
|
+
manifest: "export default {components: './components'};\n",
|
|
576
|
+
files: ['astryx.integration.mjs', 'components', 'index.mjs'],
|
|
577
|
+
themes: false,
|
|
578
|
+
});
|
|
579
|
+
const pkgFile = path.join(tmpDir, 'package.json');
|
|
580
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
581
|
+
pkg.exports = {
|
|
582
|
+
'.': './index.mjs',
|
|
583
|
+
'./components/AcmeWidget.tsx': './components/AcmeWidget.tsx',
|
|
584
|
+
};
|
|
585
|
+
fs.writeFileSync(pkgFile, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
586
|
+
fs.writeFileSync(
|
|
587
|
+
path.join(tmpDir, 'index.mjs'),
|
|
588
|
+
'export const existing = 1;\n',
|
|
589
|
+
);
|
|
590
|
+
fs.mkdirSync(path.join(tmpDir, 'components'));
|
|
591
|
+
fs.writeFileSync(
|
|
592
|
+
path.join(tmpDir, 'components', 'AcmeWidget.doc.mjs'),
|
|
593
|
+
`export default {type: 'component', name: 'AcmeWidget', import: '@acme/widgets/components/AcmeWidget.tsx', description: 'Widget.', props: []};\n`,
|
|
594
|
+
);
|
|
595
|
+
fs.writeFileSync(
|
|
596
|
+
path.join(tmpDir, 'components', 'AcmeWidget.tsx'),
|
|
597
|
+
'export function AcmeWidget() { return null; }\n',
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
const result = await integrationPackCheck({cwd: tmpDir});
|
|
601
|
+
|
|
602
|
+
// The .tsx extension in the specifier must be caught
|
|
603
|
+
expect(result.data.packable).toBe(false);
|
|
604
|
+
expect(result.data.issues).toContainEqual(
|
|
605
|
+
expect.objectContaining({
|
|
606
|
+
code: 'typescript_extension_in_specifier',
|
|
607
|
+
message: expect.stringContaining('.tsx'),
|
|
608
|
+
}),
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('generated extensionless component specifier resolves through exports map', async () => {
|
|
613
|
+
fs.writeFileSync(
|
|
614
|
+
path.join(tmpDir, 'package.json'),
|
|
615
|
+
`${JSON.stringify({
|
|
616
|
+
name: '@acme/widgets',
|
|
617
|
+
version: '1.0.0',
|
|
618
|
+
files: ['index.mjs'],
|
|
619
|
+
exports: {'.': './index.mjs'},
|
|
620
|
+
})}\n`,
|
|
621
|
+
);
|
|
622
|
+
fs.writeFileSync(
|
|
623
|
+
path.join(tmpDir, 'index.mjs'),
|
|
624
|
+
'export const existing = 1;\n',
|
|
625
|
+
);
|
|
626
|
+
await integrationAddComponent('AcmeWidget', {cwd: tmpDir});
|
|
627
|
+
|
|
628
|
+
// Verify the exports map has extensionless key → .tsx target
|
|
629
|
+
const pkg = JSON.parse(
|
|
630
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
631
|
+
);
|
|
632
|
+
expect(pkg.exports['./components/AcmeWidget']).toBe(
|
|
633
|
+
'./components/AcmeWidget.tsx',
|
|
634
|
+
);
|
|
635
|
+
expect(pkg.exports['./components/AcmeWidget.tsx']).toBeUndefined();
|
|
636
|
+
|
|
637
|
+
const result = await integrationPackCheck({cwd: tmpDir});
|
|
638
|
+
|
|
639
|
+
expect(result.data.packable).toBe(true);
|
|
640
|
+
expect(result.data.issues).not.toContainEqual(
|
|
641
|
+
expect.objectContaining({code: 'typescript_extension_in_specifier'}),
|
|
642
|
+
);
|
|
643
|
+
expect(result.data.issues).not.toContainEqual(
|
|
644
|
+
expect.objectContaining({code: 'component_import_unresolvable'}),
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('validates generated imports without project-local TypeScript', async () => {
|
|
649
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
650
|
+
tmpDir = fs.mkdtempSync(
|
|
651
|
+
path.join(os.tmpdir(), 'astryx-pack-check-no-typescript-'),
|
|
652
|
+
);
|
|
653
|
+
fs.writeFileSync(
|
|
654
|
+
path.join(tmpDir, 'package.json'),
|
|
655
|
+
`${JSON.stringify({
|
|
656
|
+
name: '@acme/widgets',
|
|
657
|
+
version: '1.0.0',
|
|
658
|
+
files: ['index.mjs'],
|
|
659
|
+
exports: {'.': './index.mjs'},
|
|
660
|
+
})}\n`,
|
|
661
|
+
);
|
|
662
|
+
fs.writeFileSync(
|
|
663
|
+
path.join(tmpDir, 'index.mjs'),
|
|
664
|
+
'export const existing = 1;\n',
|
|
665
|
+
);
|
|
666
|
+
await integrationAddComponent('AcmeWidget', {cwd: tmpDir});
|
|
667
|
+
|
|
668
|
+
const result = await integrationPackCheck({cwd: tmpDir});
|
|
669
|
+
|
|
670
|
+
expect(result.data.packable).toBe(true);
|
|
671
|
+
expect(result.data.issues).not.toContainEqual(
|
|
672
|
+
expect.objectContaining({code: 'component_import_unresolvable'}),
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('generated extensionless template specifiers resolve for page and block types', async () => {
|
|
677
|
+
fs.writeFileSync(
|
|
678
|
+
path.join(tmpDir, 'package.json'),
|
|
679
|
+
`${JSON.stringify({
|
|
680
|
+
name: '@acme/widgets',
|
|
681
|
+
version: '1.0.0',
|
|
682
|
+
files: ['index.mjs'],
|
|
683
|
+
exports: {'.': './index.mjs'},
|
|
684
|
+
})}\n`,
|
|
685
|
+
);
|
|
686
|
+
fs.writeFileSync(
|
|
687
|
+
path.join(tmpDir, 'index.mjs'),
|
|
688
|
+
'export const existing = 1;\n',
|
|
689
|
+
);
|
|
690
|
+
await integrationAddTemplate('account-page', {cwd: tmpDir, type: 'page'});
|
|
691
|
+
await integrationAddTemplate('info-card', {cwd: tmpDir, type: 'block'});
|
|
692
|
+
|
|
693
|
+
const pkg = JSON.parse(
|
|
694
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
695
|
+
);
|
|
696
|
+
expect(pkg.exports['./templates/account-page']).toBe(
|
|
697
|
+
'./templates/account-page.tsx',
|
|
698
|
+
);
|
|
699
|
+
expect(pkg.exports['./templates/info-card']).toBe(
|
|
700
|
+
'./templates/info-card.tsx',
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const result = await integrationPackCheck({cwd: tmpDir});
|
|
704
|
+
|
|
705
|
+
expect(result.data.packable).toBe(true);
|
|
706
|
+
expect(result.data.issues).not.toContainEqual(
|
|
707
|
+
expect.objectContaining({code: 'template_import_unresolvable'}),
|
|
708
|
+
);
|
|
709
|
+
expect(result.data.issues).not.toContainEqual(
|
|
710
|
+
expect.objectContaining({code: 'typescript_extension_in_specifier'}),
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('preserves condition-style exports sugar without creating a map', async () => {
|
|
715
|
+
// Package with condition-only exports (sugar, no subpaths)
|
|
716
|
+
fs.writeFileSync(
|
|
717
|
+
path.join(tmpDir, 'package.json'),
|
|
718
|
+
`${JSON.stringify({
|
|
719
|
+
name: '@acme/widgets',
|
|
720
|
+
version: '1.0.0',
|
|
721
|
+
files: ['index.mjs'],
|
|
722
|
+
exports: {import: './index.mjs', require: './index.cjs'},
|
|
723
|
+
})}\n`,
|
|
724
|
+
);
|
|
725
|
+
fs.writeFileSync(
|
|
726
|
+
path.join(tmpDir, 'index.mjs'),
|
|
727
|
+
'export const existing = 1;\n',
|
|
728
|
+
);
|
|
729
|
+
fs.writeFileSync(path.join(tmpDir, 'index.cjs'), 'exports.existing = 1;\n');
|
|
730
|
+
fs.writeFileSync(
|
|
731
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
732
|
+
'export default {};\n',
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
await integrationAddComponent('AcmeWidget', {cwd: tmpDir});
|
|
736
|
+
|
|
737
|
+
const pkg = JSON.parse(
|
|
738
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
739
|
+
);
|
|
740
|
+
// Condition sugar wrapped into subpath map with the component added
|
|
741
|
+
expect(pkg.exports['.']).toEqual({
|
|
742
|
+
import: './index.mjs',
|
|
743
|
+
require: './index.cjs',
|
|
744
|
+
});
|
|
745
|
+
expect(pkg.exports['./components/AcmeWidget']).toBe(
|
|
746
|
+
'./components/AcmeWidget.tsx',
|
|
747
|
+
);
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('no-map: pack-check fails when generated component has no exports map', async () => {
|
|
751
|
+
fs.writeFileSync(
|
|
752
|
+
path.join(tmpDir, 'package.json'),
|
|
753
|
+
`${JSON.stringify({
|
|
754
|
+
name: '@acme/widgets',
|
|
755
|
+
version: '1.0.0',
|
|
756
|
+
files: ['dist'],
|
|
757
|
+
})}\n`,
|
|
758
|
+
);
|
|
759
|
+
fs.writeFileSync(
|
|
760
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
761
|
+
'export default {};\n',
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
await integrationAddComponent('AcmeWidget', {cwd: tmpDir});
|
|
765
|
+
await integrationAddTemplate('hero-page', {cwd: tmpDir, type: 'page'});
|
|
766
|
+
|
|
767
|
+
const pkg = JSON.parse(
|
|
768
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
769
|
+
);
|
|
770
|
+
// No exports map created
|
|
771
|
+
expect(pkg.exports).toBeUndefined();
|
|
772
|
+
|
|
773
|
+
const result = await integrationPackCheck({cwd: tmpDir});
|
|
774
|
+
|
|
775
|
+
// The extensionless import cannot resolve without an exports map —
|
|
776
|
+
// pack-check must fail closed, not false-green.
|
|
777
|
+
expect(result.data.packable).toBe(false);
|
|
778
|
+
expect(result.data.issues).toContainEqual(
|
|
779
|
+
expect.objectContaining({
|
|
780
|
+
severity: 'error',
|
|
781
|
+
message: expect.stringContaining('AcmeWidget'),
|
|
782
|
+
}),
|
|
783
|
+
);
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('idempotent when extensionless export already exists', async () => {
|
|
787
|
+
fs.writeFileSync(
|
|
788
|
+
path.join(tmpDir, 'package.json'),
|
|
789
|
+
`${JSON.stringify({
|
|
790
|
+
name: '@acme/widgets',
|
|
791
|
+
version: '1.0.0',
|
|
792
|
+
files: ['index.mjs'],
|
|
793
|
+
exports: {
|
|
794
|
+
'.': './index.mjs',
|
|
795
|
+
'./components/AcmeWidget': './components/AcmeWidget.tsx',
|
|
796
|
+
},
|
|
797
|
+
})}\n`,
|
|
798
|
+
);
|
|
799
|
+
fs.writeFileSync(
|
|
800
|
+
path.join(tmpDir, 'index.mjs'),
|
|
801
|
+
'export const existing = 1;\n',
|
|
802
|
+
);
|
|
803
|
+
fs.writeFileSync(
|
|
804
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
805
|
+
'export default {};\n',
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
await integrationAddComponent('AcmeWidget', {cwd: tmpDir});
|
|
809
|
+
|
|
810
|
+
const pkg = JSON.parse(
|
|
811
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
812
|
+
);
|
|
813
|
+
// Export preserved, no duplicate
|
|
814
|
+
expect(
|
|
815
|
+
Object.keys(pkg.exports).filter(k => k.includes('AcmeWidget')),
|
|
816
|
+
).toEqual(['./components/AcmeWidget']);
|
|
817
|
+
});
|
|
818
|
+
});
|
package/api/search/search.d.mts
CHANGED
package/api/search/search.mjs
CHANGED
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
import {loadIntegrationsSafely} from '../component/_adapter.mjs';
|
|
67
67
|
import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
68
68
|
import {discoverTemplates, extractComponents} from '../template/template.mjs';
|
|
69
|
-
import {loadDocsCatalog,
|
|
69
|
+
import {loadDocsCatalog, lowerTopic} from '../docs/_adapter.mjs';
|
|
70
70
|
import {AstryxError} from '../error.mjs';
|
|
71
71
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
72
72
|
import {setResultCoverage} from './coverage.mjs';
|
|
@@ -732,16 +732,16 @@ async function gatherHooks(coreDir) {
|
|
|
732
732
|
async function gatherDocs(cwd) {
|
|
733
733
|
/** @type {Candidate[]} */
|
|
734
734
|
const candidates = [];
|
|
735
|
-
let
|
|
735
|
+
let catalog;
|
|
736
736
|
try {
|
|
737
|
-
|
|
737
|
+
catalog = await loadDocsCatalog(cwd);
|
|
738
738
|
} catch {
|
|
739
739
|
return candidates;
|
|
740
740
|
}
|
|
741
|
-
for (const entry of entries) {
|
|
741
|
+
for (const entry of catalog.entries()) {
|
|
742
742
|
let doc = null;
|
|
743
743
|
try {
|
|
744
|
-
doc = await
|
|
744
|
+
doc = (await lowerTopic(catalog, entry)).doc;
|
|
745
745
|
} catch {
|
|
746
746
|
// A topic that cannot be loaded is reported by the commands that own
|
|
747
747
|
// integration issues; search just cannot index it.
|
|
@@ -48,10 +48,10 @@ export type SearchResultEntry = {
|
|
|
48
48
|
/**
|
|
49
49
|
* - Template kind (`page` | `block`) — present for template results.
|
|
50
50
|
*/
|
|
51
|
-
kind?: "
|
|
51
|
+
kind?: "page" | "block" | undefined;
|
|
52
52
|
};
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
* astryx --json search <query>
|
|
55
55
|
*/
|
|
56
56
|
export type SearchResponse = {
|
|
57
57
|
type: "search";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* astryx --json swizzle [--list]
|
|
6
6
|
*/
|
|
7
7
|
export type SwizzleListResponse = {
|
|
8
8
|
type: "swizzle.list";
|
|
@@ -22,7 +22,7 @@ export type SwizzleFeedback = {
|
|
|
22
22
|
ghCommand?: string | undefined;
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* astryx --json swizzle <component>
|
|
26
26
|
*/
|
|
27
27
|
export type SwizzleCopyResponse = {
|
|
28
28
|
type: "swizzle.copy";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* astryx --json swizzle [--list]
|
|
11
11
|
*
|
|
12
12
|
* @typedef {object} SwizzleListResponse
|
|
13
13
|
* @property {'swizzle.list'} type
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* astryx --json swizzle <component>
|
|
27
27
|
*
|
|
28
28
|
* @typedef {object} SwizzleCopyResponse
|
|
29
29
|
* @property {'swizzle.copy'} type
|
|
@@ -31,7 +31,7 @@ export function template(name?: string, options?: {
|
|
|
31
31
|
skeleton?: boolean | undefined;
|
|
32
32
|
cdn?: string | boolean | undefined;
|
|
33
33
|
show?: boolean | undefined;
|
|
34
|
-
type?: "
|
|
34
|
+
type?: "page" | "block" | undefined;
|
|
35
35
|
package?: string | undefined;
|
|
36
36
|
cwd?: string | undefined;
|
|
37
37
|
}): Promise<{
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* astryx --json template [--list]
|
|
6
6
|
*/
|
|
7
7
|
export type TemplateListResponse = {
|
|
8
8
|
type: "template.list";
|
|
@@ -53,7 +53,7 @@ export type TemplateListEntry = {
|
|
|
53
53
|
scaffold?: boolean | undefined;
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* astryx --json template <name>
|
|
57
57
|
*/
|
|
58
58
|
export type TemplateShowResponse = {
|
|
59
59
|
type: "template.show";
|
|
@@ -66,7 +66,7 @@ export type TemplateShowResponse = {
|
|
|
66
66
|
};
|
|
67
67
|
};
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* astryx --json template <name> --skeleton
|
|
70
70
|
*/
|
|
71
71
|
export type TemplateSkeletonResponse = {
|
|
72
72
|
type: "template.skeleton";
|
|
@@ -78,7 +78,7 @@ export type TemplateSkeletonResponse = {
|
|
|
78
78
|
};
|
|
79
79
|
};
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* astryx --json template <name> [path]
|
|
82
82
|
*/
|
|
83
83
|
export type TemplateCopyResponse = {
|
|
84
84
|
type: "template.copy";
|
|
@@ -90,7 +90,7 @@ export type TemplateCopyResponse = {
|
|
|
90
90
|
};
|
|
91
91
|
};
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* astryx --json template --cdn [path]
|
|
94
94
|
* `written: false` with `reason: 'exists'` is a success: the command is safe to
|
|
95
95
|
* re-run, and an edited page is the consumer's file to keep. `version` is the
|
|
96
96
|
* Astryx version every CDN URL in the file was pinned to.
|
|
@@ -118,7 +118,7 @@ export type TemplateOptions = {
|
|
|
118
118
|
/**
|
|
119
119
|
* Filter templates by kind: 'page' or 'block'. Only applies to list views.
|
|
120
120
|
*/
|
|
121
|
-
type?: "
|
|
121
|
+
type?: "page" | "block" | undefined;
|
|
122
122
|
/**
|
|
123
123
|
* Narrow to templates from a specific package (id-only lookups across packages are ambiguous).
|
|
124
124
|
*/
|
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Each template is exactly two files: page.tsx (code) + template.doc.mjs (metadata).
|
|
8
8
|
*
|
|
9
|
-
* Invocation
|
|
9
|
+
* Invocation -> type discriminator
|
|
10
10
|
* ------------------------------------------------------------------
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* (unknown template)
|
|
11
|
+
* astryx --json template [--list] -> template.list
|
|
12
|
+
* astryx --json template <name> -> template.show
|
|
13
|
+
* astryx --json template <name> --skeleton -> template.skeleton
|
|
14
|
+
* astryx --json template <name> [path] -> template.copy
|
|
15
|
+
* astryx --json template --cdn [path] -> template.cdn
|
|
16
|
+
* (unknown template) -> CLIError
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* astryx --json template [--list]
|
|
21
21
|
* @typedef {object} TemplateListResponse
|
|
22
22
|
* @property {'template.list'} type
|
|
23
23
|
* @property {TemplateListEntry[]} data
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* astryx --json template <name>
|
|
47
47
|
* @typedef {object} TemplateShowResponse
|
|
48
48
|
* @property {'template.show'} type
|
|
49
49
|
* @property {object} data
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* astryx --json template <name> --skeleton
|
|
59
59
|
* @typedef {object} TemplateSkeletonResponse
|
|
60
60
|
* @property {'template.skeleton'} type
|
|
61
61
|
* @property {object} data
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
*/
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* astryx --json template <name> [path]
|
|
70
70
|
* @typedef {object} TemplateCopyResponse
|
|
71
71
|
* @property {'template.copy'} type
|
|
72
72
|
* @property {object} data
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
*/
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* astryx --json template --cdn [path]
|
|
81
81
|
* `written: false` with `reason: 'exists'` is a success: the command is safe to
|
|
82
82
|
* re-run, and an edited page is the consumer's file to keep. `version` is the
|
|
83
83
|
* Astryx version every CDN URL in the file was pinned to.
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* - A .d.ts (plus an optional .variants.d.ts for custom prop values)
|
|
13
13
|
*
|
|
14
14
|
* It performs the writes and returns a `theme.build` receipt — its `warnings`
|
|
15
|
-
* carry override problems
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* carry override problems, every declaration core's generator dropped because
|
|
16
|
+
* its value could not stay one CSS declaration, and any fonts the theme names
|
|
17
|
+
* but does not load (font-warning.mjs) — or `null` when the theme produced no
|
|
18
|
+
* CSS (nothing to build). Errors throw AstryxError (with
|
|
18
19
|
* a stable code). Human progress is emitted through the shared `logger`
|
|
19
20
|
* (silent by default), so the CLI keeps its exact output while a programmatic
|
|
20
21
|
* caller stays quiet.
|
|
@@ -2303,7 +2304,15 @@ async function themeBuildInternal(
|
|
|
2303
2304
|
const scopeSelector = themeScopeStart(themeDef.name);
|
|
2304
2305
|
const scopeTo = THEME_SCOPE_TO;
|
|
2305
2306
|
|
|
2306
|
-
|
|
2307
|
+
// Older cores ignore this optional collector. Current core writes the same
|
|
2308
|
+
// warning text here that runtime callers receive on the console.
|
|
2309
|
+
/** @type {string[]} */
|
|
2310
|
+
const droppedDeclarations = [];
|
|
2311
|
+
|
|
2312
|
+
const {component, prose} = _generateThemeRulesSplit(
|
|
2313
|
+
resolvedTheme,
|
|
2314
|
+
droppedDeclarations,
|
|
2315
|
+
);
|
|
2307
2316
|
const cssParts = [];
|
|
2308
2317
|
// Prose element defaults always ship — the `<Theme>` runtime
|
|
2309
2318
|
// (generateThemeCSS) always emits them, so the build must too, or the
|
|
@@ -2322,7 +2331,7 @@ async function themeBuildInternal(
|
|
|
2322
2331
|
let adaptationCss;
|
|
2323
2332
|
try {
|
|
2324
2333
|
adaptationCss = _generateAdaptationCSS
|
|
2325
|
-
? _generateAdaptationCSS(resolvedTheme)
|
|
2334
|
+
? _generateAdaptationCSS(resolvedTheme, droppedDeclarations)
|
|
2326
2335
|
: {component: '', prose: ''};
|
|
2327
2336
|
} catch (error) {
|
|
2328
2337
|
const message =
|
|
@@ -2368,11 +2377,16 @@ async function themeBuildInternal(
|
|
|
2368
2377
|
// adaptations on the same resolved leaf.
|
|
2369
2378
|
let onMediaCss = '';
|
|
2370
2379
|
if (_generateOnMediaCSS) {
|
|
2371
|
-
onMediaCss = _generateOnMediaCSS(resolvedTheme);
|
|
2380
|
+
onMediaCss = _generateOnMediaCSS(resolvedTheme, droppedDeclarations);
|
|
2372
2381
|
if (onMediaCss) {
|
|
2373
2382
|
cssParts.push(`@layer astryx-theme {\n${onMediaCss}\n}`);
|
|
2374
2383
|
}
|
|
2375
2384
|
}
|
|
2385
|
+
for (const message of droppedDeclarations) {
|
|
2386
|
+
const w = `Declaration ${message}. The generated CSS omits it; fix the value in the theme source.`;
|
|
2387
|
+
warningMessages.push(w);
|
|
2388
|
+
logger.warn(` ⚠ ${w}`);
|
|
2389
|
+
}
|
|
2376
2390
|
if (cssParts.length === 0) {
|
|
2377
2391
|
logger.log('No overrides found — nothing to build.');
|
|
2378
2392
|
return null;
|