@astryxdesign/cli 0.7.2-canary.ebcc404 → 0.7.2-canary.f8f7cf1
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 +9 -8
- package/api/build/help/help.mjs +1 -1
- package/api/component/_adapter.d.mts +14 -0
- package/api/component/_adapter.mjs +48 -0
- package/api/component/component.mjs +10 -2
- package/api/component/integration-export-name.test.mjs +233 -8
- package/api/docs/_adapter.d.mts +11 -0
- package/api/docs/_adapter.mjs +244 -1
- package/api/docs/integrationDocs.test.mjs +124 -0
- package/api/doctor/theme-checks.mjs +1 -1
- package/api/gap-report/gap-report.mjs +2 -1
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/init/run/run.mjs +2 -2
- package/api/integration/_adapter.d.mts +12 -0
- package/api/integration/_adapter.mjs +54 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +11 -2
- package/api/integration/authoring-checks.test.mjs +39 -0
- package/api/integration/authoring-checks.type.d.mts +9 -0
- package/api/integration/authoring-checks.type.mjs +14 -1
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
- package/api/integration/pack-check.mjs +48 -3
- package/api/integration/pack-check.test.mjs +247 -0
- package/api/integration/validate-integration.mjs +8 -0
- package/api/integration/validate-integration.test.mjs +11 -0
- package/api/integration/validate-integration.type.d.mts +8 -0
- package/api/integration/validate-integration.type.mjs +12 -1
- package/api/integration/validateIntegration.doc.mjs +17 -3
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +20 -8
- package/api/theme/build/build.family.test.mjs +54 -6
- package/api/theme/build/build.mjs +114 -17
- package/api/theme/themeBuild.doc.mjs +4 -4
- package/assets/docs/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +3 -3
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +10 -0
- package/assets/docs/tree/block-template.doc.mjs +2 -2
- package/assets/docs/tree/check-the-props.doc.mjs +196 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +44 -3
- package/assets/docs/tree/checks.doc.mjs +21 -13
- package/assets/docs/tree/component-family.doc.mjs +5 -1
- package/assets/docs/tree/component-imports.doc.mjs +1 -1
- package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
- package/assets/docs/tree/links.doc.mjs +2 -2
- package/assets/docs/tree/page-template.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
- package/assets/docs/tree/single-component.doc.mjs +3 -2
- package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/versioning.doc.mjs +1 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +2 -1
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- package/assets/theme.template.ts +1 -1
- package/authoring/doctypes/component/component.doc.mjs +1 -1
- package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +1 -1
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/integration.doc.mjs +8 -1
- package/authoring/integration/parse.test.mjs +10 -0
- package/authoring/integration/schema.d.mts +2 -0
- package/authoring/integration/schema.mjs +1 -0
- package/authoring/integration/type.ts +4 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
- package/clients/cli/commands/build-theme.mjs +1 -1
- package/clients/cli/commands/build-theme.path-safety.test.mjs +155 -21
- package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
- package/clients/cli/commands/doctor-integration.test.mjs +837 -0
- package/clients/cli/commands/doctor.mjs +190 -0
- package/clients/cli/commands/integration-check.doc.mjs +85 -0
- package/clients/cli/commands/integration-check.test.mjs +267 -0
- package/clients/cli/commands/integration.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +107 -1
- package/clients/cli/commands/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-build.doc.mjs +2 -2
- package/clients/cli/index.mjs +1 -0
- package/foundation/config/project.mjs +82 -0
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- package/foundation/discovery/component-discovery.d.mts +108 -17
- package/foundation/discovery/component-discovery.export-names.test.mjs +469 -2
- package/foundation/discovery/component-discovery.mjs +380 -47
- package/foundation/discovery/component-replacement.test.mjs +20 -0
- package/foundation/discovery/doc-types.d.mts +157 -0
- package/foundation/discovery/doc-types.mjs +1224 -0
- package/foundation/discovery/doc-types.test.mjs +652 -0
- package/foundation/discovery/docs-discovery.d.mts +81 -0
- package/foundation/discovery/docs-discovery.mjs +57 -0
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -0
- package/foundation/discovery/module-exports.d.mts +6 -0
- package/foundation/discovery/module-exports.mjs +15 -2
- package/foundation/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +23 -0
- package/foundation/discovery/project-typescript.mjs +61 -0
- package/foundation/discovery/prop-drift.d.mts +278 -0
- package/foundation/discovery/prop-drift.mjs +2263 -0
- package/foundation/discovery/prop-drift.test.mjs +2219 -0
- package/foundation/discovery/type-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +6 -0
- package/foundation/doc-compiler/links.d.mts +9 -5
- package/foundation/doc-compiler/links.mjs +4 -2
- package/foundation/integrations/contribution-inventory.mjs +6 -1
- package/foundation/response/response-types.doc.mjs +7 -2
- package/package.json +9 -9
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @file Integration components
|
|
5
|
-
* every doc that works today reads
|
|
4
|
+
* @file Integration components, and the members a family doc lists, are found
|
|
5
|
+
* by the names their docs document; every doc that works today reads as before.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
@@ -291,3 +291,470 @@ describe('integration components whose doc names another export of its file', ()
|
|
|
291
291
|
expect(names()).toEqual([]);
|
|
292
292
|
});
|
|
293
293
|
});
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Write a family doc and its source.
|
|
297
|
+
* @param {string} file path under the root, without extension
|
|
298
|
+
* @param {{name: string, members: string, source: string, extra?: string}} options
|
|
299
|
+
* `members` is the `components` array, written as JavaScript
|
|
300
|
+
*/
|
|
301
|
+
function writeFamily(file, {name, members, source, extra = ''}) {
|
|
302
|
+
const target = path.join(root, file);
|
|
303
|
+
fs.mkdirSync(path.dirname(target), {recursive: true});
|
|
304
|
+
fs.writeFileSync(
|
|
305
|
+
`${target}.doc.mjs`,
|
|
306
|
+
`export default {\n type: 'component',\n name: '${name}',\n usage: {description: 'A family.'},${extra}\n components: ${members},\n};\n`,
|
|
307
|
+
);
|
|
308
|
+
fs.writeFileSync(`${target}.tsx`, source);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
describe('integration components a family doc lists in components', () => {
|
|
312
|
+
it('records each exported full entry under its own name, with the file that declares it', async () => {
|
|
313
|
+
writeFamily('carousel/AcmeCarousel', {
|
|
314
|
+
name: 'AcmeCarousel',
|
|
315
|
+
members:
|
|
316
|
+
"[{name: 'AcmeCarousel', description: 'The carousel.'}, {name: 'AcmeSlide', description: 'One slide.'}, {name: 'AcmeDots', description: 'The dots.'}]",
|
|
317
|
+
source:
|
|
318
|
+
"export function AcmeCarousel() { return null; }\nexport function AcmeSlide() { return null; }\nexport {AcmeDots} from './dots';\n",
|
|
319
|
+
});
|
|
320
|
+
fs.writeFileSync(
|
|
321
|
+
path.join(root, 'carousel', 'dots.tsx'),
|
|
322
|
+
'export const AcmeDots = () => null;\n',
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const records = discoverIntegrationComponents(integration());
|
|
326
|
+
const docPath = path.join(root, 'carousel', 'AcmeCarousel.doc.mjs');
|
|
327
|
+
expect(records.map(record => record.name)).toEqual([
|
|
328
|
+
'AcmeCarousel',
|
|
329
|
+
'AcmeSlide',
|
|
330
|
+
'AcmeDots',
|
|
331
|
+
]);
|
|
332
|
+
expect(records.find(record => record.name === 'AcmeSlide')).toMatchObject({
|
|
333
|
+
docPath,
|
|
334
|
+
sourcePath: path.join(root, 'carousel', 'AcmeCarousel.tsx'),
|
|
335
|
+
});
|
|
336
|
+
expect(findIntegrationComponentDoc(integration(), 'AcmeDots')).toBe(
|
|
337
|
+
docPath,
|
|
338
|
+
);
|
|
339
|
+
expect(findIntegrationComponentSource(integration(), 'AcmeDots')).toBe(
|
|
340
|
+
path.join(root, 'carousel', 'dots.tsx'),
|
|
341
|
+
);
|
|
342
|
+
const {components, errors} =
|
|
343
|
+
await discoverValidIntegrationComponents(integration());
|
|
344
|
+
expect(errors).toEqual([]);
|
|
345
|
+
expect(components.map(record => record.name)).toEqual([
|
|
346
|
+
'AcmeCarousel',
|
|
347
|
+
'AcmeSlide',
|
|
348
|
+
'AcmeDots',
|
|
349
|
+
]);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('reads members from the module the doc imports, through the package exports', () => {
|
|
353
|
+
// The components root inside a package whose exports publish the module.
|
|
354
|
+
const componentsRoot = path.join(root, 'src');
|
|
355
|
+
fs.mkdirSync(path.join(componentsRoot, 'carousel'), {recursive: true});
|
|
356
|
+
fs.writeFileSync(
|
|
357
|
+
path.join(root, 'package.json'),
|
|
358
|
+
JSON.stringify({
|
|
359
|
+
name: '@acme/widgets',
|
|
360
|
+
exports: {'./carousel': './src/carousel/index.ts'},
|
|
361
|
+
}),
|
|
362
|
+
);
|
|
363
|
+
fs.writeFileSync(
|
|
364
|
+
path.join(componentsRoot, 'carousel', 'AcmeCarousel.doc.mjs'),
|
|
365
|
+
"export default {type: 'component', name: 'AcmeCarousel', import: '@acme/widgets/carousel', usage: {description: 'x'}, components: [{name: 'AcmeCarousel', description: 'x'}, {name: 'AcmeSlide', description: 'x'}]};\n",
|
|
366
|
+
);
|
|
367
|
+
fs.writeFileSync(
|
|
368
|
+
path.join(componentsRoot, 'carousel', 'AcmeCarousel.tsx'),
|
|
369
|
+
'export function AcmeCarousel() { return null; }\n',
|
|
370
|
+
);
|
|
371
|
+
fs.writeFileSync(
|
|
372
|
+
path.join(componentsRoot, 'carousel', 'index.ts'),
|
|
373
|
+
"export * from './AcmeCarousel';\nexport {AcmeSlide} from './slide';\n",
|
|
374
|
+
);
|
|
375
|
+
fs.writeFileSync(
|
|
376
|
+
path.join(componentsRoot, 'carousel', 'slide.tsx'),
|
|
377
|
+
'export function AcmeSlide() { return null; }\n',
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const loaded = {
|
|
381
|
+
name: '@acme/widgets',
|
|
382
|
+
components: componentsRoot,
|
|
383
|
+
__packageDir: root,
|
|
384
|
+
};
|
|
385
|
+
expect(
|
|
386
|
+
discoverIntegrationComponents(loaded).find(
|
|
387
|
+
record => record.name === 'AcmeSlide',
|
|
388
|
+
),
|
|
389
|
+
).toMatchObject({
|
|
390
|
+
sourcePath: path.join(componentsRoot, 'carousel', 'slide.tsx'),
|
|
391
|
+
});
|
|
392
|
+
// Without the package, only the doc's own source is read, and it lacks
|
|
393
|
+
// the member.
|
|
394
|
+
expect(
|
|
395
|
+
discoverIntegrationComponents({...loaded, __packageDir: undefined}).map(
|
|
396
|
+
record => record.name,
|
|
397
|
+
),
|
|
398
|
+
).toEqual(['AcmeCarousel']);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('leaves every other entry as before', () => {
|
|
402
|
+
writeFamily('Gallery', {
|
|
403
|
+
name: 'AcmeGallery',
|
|
404
|
+
extra: "\n hiddenComponents: ['AcmeHidden'],",
|
|
405
|
+
members: [
|
|
406
|
+
'[',
|
|
407
|
+
" {name: 'AcmeGallery', description: 'x'},",
|
|
408
|
+
// Not exported by the source.
|
|
409
|
+
" {name: 'AcmeMissing', description: 'x'},",
|
|
410
|
+
// Hidden by the doc.
|
|
411
|
+
" {name: 'AcmeHidden', description: 'x'},",
|
|
412
|
+
// A hook.
|
|
413
|
+
" {name: 'useGallery', description: 'x'},",
|
|
414
|
+
// A name-only entry points at the member's own doc.
|
|
415
|
+
" {name: 'AcmeThumb'},",
|
|
416
|
+
// Another file already has the name.
|
|
417
|
+
" {name: 'AcmeBadge', description: 'x'},",
|
|
418
|
+
// A computed name.
|
|
419
|
+
" {name: `${'Acme'}Tile`, description: 'x'},",
|
|
420
|
+
']',
|
|
421
|
+
].join('\n'),
|
|
422
|
+
source: [
|
|
423
|
+
'export function AcmeGallery() { return null; }',
|
|
424
|
+
'export function AcmeHidden() { return null; }',
|
|
425
|
+
'export function useGallery() { return []; }',
|
|
426
|
+
'export function AcmeThumb() { return null; }',
|
|
427
|
+
'export function AcmeBadge() { return null; }',
|
|
428
|
+
'export function AcmeTile() { return null; }',
|
|
429
|
+
'',
|
|
430
|
+
].join('\n'),
|
|
431
|
+
});
|
|
432
|
+
writeComponent('AcmeBadge', {
|
|
433
|
+
name: "'AcmeBadge'",
|
|
434
|
+
source: 'export function AcmeBadge() { return null; }\n',
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// Gallery.tsx exports no `Gallery`, so the doc's own name is its export.
|
|
438
|
+
expect(names()).toEqual(['AcmeBadge', 'AcmeGallery']);
|
|
439
|
+
for (const name of [
|
|
440
|
+
'AcmeMissing',
|
|
441
|
+
'AcmeHidden',
|
|
442
|
+
'useGallery',
|
|
443
|
+
'AcmeThumb',
|
|
444
|
+
'AcmeTile',
|
|
445
|
+
]) {
|
|
446
|
+
expect(findIntegrationComponentDoc(integration(), name)).toBeNull();
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("does not hand a family's replaces to its members", async () => {
|
|
451
|
+
writeFamily('AcmeCarousel', {
|
|
452
|
+
name: 'AcmeCarousel',
|
|
453
|
+
extra: "\n replaces: 'Carousel',",
|
|
454
|
+
members:
|
|
455
|
+
"[{name: 'AcmeCarousel', description: 'x'}, {name: 'AcmeSlide', description: 'x'}]",
|
|
456
|
+
source:
|
|
457
|
+
'export function AcmeCarousel() { return null; }\nexport function AcmeSlide() { return null; }\n',
|
|
458
|
+
});
|
|
459
|
+
const {components} =
|
|
460
|
+
await discoverValidIntegrationComponents(integration());
|
|
461
|
+
expect(components.map(record => [record.name, record.replaces])).toEqual([
|
|
462
|
+
['AcmeCarousel', 'Carousel'],
|
|
463
|
+
['AcmeSlide', undefined],
|
|
464
|
+
]);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('drops a member once the file its source re-exports stops exporting it', () => {
|
|
468
|
+
writeFamily('AcmeTabs', {
|
|
469
|
+
name: 'AcmeTabs',
|
|
470
|
+
members: "[{name: 'AcmeTab', description: 'x'}]",
|
|
471
|
+
source:
|
|
472
|
+
"export function AcmeTabs() { return null; }\nexport * from './tab';\n",
|
|
473
|
+
});
|
|
474
|
+
const tab = path.join(root, 'tab.tsx');
|
|
475
|
+
fs.writeFileSync(tab, 'export function AcmeTab() { return null; }\n');
|
|
476
|
+
expect(names()).toEqual(['AcmeTab', 'AcmeTabs']);
|
|
477
|
+
fs.writeFileSync(tab, 'export function AcmeTabLegacy() { return null; }\n');
|
|
478
|
+
expect(names()).toEqual(['AcmeTabs']);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('reads members a doc lists through a shorthand property', () => {
|
|
482
|
+
fs.writeFileSync(
|
|
483
|
+
path.join(root, 'AcmeTabs.tsx'),
|
|
484
|
+
'export function AcmeTabs() { return null; }\nexport function AcmeTab() { return null; }\n',
|
|
485
|
+
);
|
|
486
|
+
fs.writeFileSync(
|
|
487
|
+
path.join(root, 'AcmeTabs.doc.mjs'),
|
|
488
|
+
"const components = [{name: 'AcmeTab', description: 'x'}];\nexport default {type: 'component', name: 'AcmeTabs', usage: {description: 'x'}, components};\n",
|
|
489
|
+
);
|
|
490
|
+
expect(names()).toEqual(['AcmeTab', 'AcmeTabs']);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('reports a family doc the loader rejects once, not once per member', async () => {
|
|
494
|
+
writeFamily('AcmeStrip', {
|
|
495
|
+
name: 'AcmeStrip',
|
|
496
|
+
extra: '\n displayName: 7,',
|
|
497
|
+
members:
|
|
498
|
+
"[{name: 'AcmeStrip', description: 'x'}, {name: 'AcmeStripItem', description: 'x'}]",
|
|
499
|
+
source:
|
|
500
|
+
'export function AcmeStrip() { return null; }\nexport function AcmeStripItem() { return null; }\n',
|
|
501
|
+
});
|
|
502
|
+
const {components, errors} =
|
|
503
|
+
await discoverValidIntegrationComponents(integration());
|
|
504
|
+
expect(components).toEqual([]);
|
|
505
|
+
expect(errors.map(error => error.name)).toEqual(['AcmeStrip']);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* A package around the components root `src/`, with these files.
|
|
510
|
+
* @param {Record<string, unknown>} manifest package.json fields
|
|
511
|
+
* @param {Record<string, string>} files paths under the package
|
|
512
|
+
*/
|
|
513
|
+
function writePackage(manifest, files) {
|
|
514
|
+
fs.writeFileSync(
|
|
515
|
+
path.join(root, 'package.json'),
|
|
516
|
+
JSON.stringify({name: '@acme/widgets', ...manifest}),
|
|
517
|
+
);
|
|
518
|
+
for (const [file, text] of Object.entries(files)) {
|
|
519
|
+
fs.mkdirSync(path.dirname(path.join(root, file)), {recursive: true});
|
|
520
|
+
fs.writeFileSync(path.join(root, file), text);
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
name: '@acme/widgets',
|
|
524
|
+
components: path.join(root, 'src'),
|
|
525
|
+
__packageDir: root,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** @param {object} loaded */
|
|
530
|
+
const namesIn = loaded =>
|
|
531
|
+
discoverIntegrationComponents(/** @type {any} */ (loaded))
|
|
532
|
+
.map(record => record.name)
|
|
533
|
+
.sort();
|
|
534
|
+
|
|
535
|
+
const family = (/** @type {string} */ name, /** @type {string} */ entries) =>
|
|
536
|
+
`export default {type: 'component', name: '${name}', usage: {description: 'x'}, components: [${entries}]};\n`;
|
|
537
|
+
|
|
538
|
+
it('reads members from the main file of a package without exports, and none it cannot resolve', () => {
|
|
539
|
+
const loaded = writePackage(
|
|
540
|
+
{main: './src/index.ts'},
|
|
541
|
+
{
|
|
542
|
+
'src/list/AcmeList.doc.mjs': family(
|
|
543
|
+
'AcmeList',
|
|
544
|
+
"{name: 'AcmeListRow', description: 'x'}, {name: 'AcmeListCell', description: 'x'}",
|
|
545
|
+
).replace('usage:', "import: '@acme/widgets', usage:"),
|
|
546
|
+
'src/list/AcmeList.tsx':
|
|
547
|
+
'export function AcmeList() { return null; }\nexport function AcmeListRow() { return null; }\n',
|
|
548
|
+
'src/list/cell.tsx':
|
|
549
|
+
'export function AcmeListCell() { return null; }\n',
|
|
550
|
+
'src/index.ts':
|
|
551
|
+
"export {AcmeList} from './list/AcmeList';\nexport {AcmeListCell} from './list/cell';\n",
|
|
552
|
+
},
|
|
553
|
+
);
|
|
554
|
+
expect(namesIn(loaded)).toEqual(['AcmeList', 'AcmeListCell']);
|
|
555
|
+
// An import the package cannot resolve lists no members, not the
|
|
556
|
+
// same-stem file's.
|
|
557
|
+
fs.rmSync(path.join(root, 'src', 'index.ts'));
|
|
558
|
+
expect(namesIn(loaded)).toEqual(['AcmeList']);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("takes a member's source from the family's folder, never a bundle or the package root", () => {
|
|
562
|
+
const loaded = writePackage(
|
|
563
|
+
{exports: {'.': {import: './dist/index.mjs'}}},
|
|
564
|
+
{
|
|
565
|
+
'src/tabs/AcmeTabs.doc.mjs': family(
|
|
566
|
+
'AcmeTabs',
|
|
567
|
+
"{name: 'AcmeTab', description: 'x'}",
|
|
568
|
+
),
|
|
569
|
+
'src/tabs/AcmeTabs.tsx':
|
|
570
|
+
'export function AcmeTabs() { return null; }\nexport function AcmeTab() { return null; }\n',
|
|
571
|
+
'dist/index.mjs':
|
|
572
|
+
'function AcmeTabs() { return null; }\nfunction AcmeTab() { return null; }\nexport {AcmeTabs, AcmeTab};\n',
|
|
573
|
+
// A member only the package's root file and the bundle declare is
|
|
574
|
+
// listed, with no source.
|
|
575
|
+
'src/cards/AcmeCards.doc.mjs': family(
|
|
576
|
+
'AcmeCards',
|
|
577
|
+
"{name: 'AcmeCard', description: 'x'}",
|
|
578
|
+
),
|
|
579
|
+
'src/cards/AcmeCards.tsx':
|
|
580
|
+
'export function AcmeCards() { return null; }\n',
|
|
581
|
+
'src/index.tsx':
|
|
582
|
+
"export {AcmeCards} from './cards/AcmeCards';\nexport function AcmeCard() { return null; }\n",
|
|
583
|
+
},
|
|
584
|
+
);
|
|
585
|
+
fs.writeFileSync(
|
|
586
|
+
path.join(root, 'dist', 'index.mjs'),
|
|
587
|
+
`${fs.readFileSync(path.join(root, 'dist', 'index.mjs'), 'utf-8')}export function AcmeCard() { return null; }\n`,
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
const records = discoverIntegrationComponents(loaded);
|
|
591
|
+
expect(records.map(record => record.name).sort()).toEqual([
|
|
592
|
+
'AcmeCard',
|
|
593
|
+
'AcmeCards',
|
|
594
|
+
'AcmeTab',
|
|
595
|
+
'AcmeTabs',
|
|
596
|
+
]);
|
|
597
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeTab')).toBe(
|
|
598
|
+
path.join(root, 'src', 'tabs', 'AcmeTabs.tsx'),
|
|
599
|
+
);
|
|
600
|
+
expect(findIntegrationComponentDoc(loaded, 'AcmeCard')).toBe(
|
|
601
|
+
path.join(root, 'src', 'cards', 'AcmeCards.doc.mjs'),
|
|
602
|
+
);
|
|
603
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeCard')).toBeNull();
|
|
604
|
+
expect(
|
|
605
|
+
records.find(record => record.name === 'AcmeCard')?.sourcePath,
|
|
606
|
+
).toBeNull();
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('lists a member only a bundle declares, with no source, when the family doc sits at the package root', () => {
|
|
610
|
+
const loaded = {
|
|
611
|
+
...writePackage(
|
|
612
|
+
{exports: {'.': './dist/index.mjs'}},
|
|
613
|
+
{
|
|
614
|
+
'AcmeStrip.doc.mjs': family(
|
|
615
|
+
'AcmeStrip',
|
|
616
|
+
"{name: 'AcmeStripItem', description: 'x'}",
|
|
617
|
+
).replace('usage:', "import: '@acme/widgets', usage:"),
|
|
618
|
+
'AcmeStrip.tsx': 'export function AcmeStrip() { return null; }\n',
|
|
619
|
+
'dist/index.mjs':
|
|
620
|
+
'export function AcmeStrip() { return null; }\nexport function AcmeStripItem() { return null; }\n',
|
|
621
|
+
},
|
|
622
|
+
),
|
|
623
|
+
components: root,
|
|
624
|
+
};
|
|
625
|
+
expect(namesIn(loaded)).toEqual(['AcmeStrip', 'AcmeStripItem']);
|
|
626
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeStripItem')).toBeNull();
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('lists the members a family doc without its own source imports', () => {
|
|
630
|
+
const loaded = writePackage(
|
|
631
|
+
{exports: {'./group': './src/group/index.ts'}},
|
|
632
|
+
{
|
|
633
|
+
'src/group/AcmeGroup.doc.mjs': family(
|
|
634
|
+
'AcmeGroup',
|
|
635
|
+
"{name: 'AcmeGroupItem', description: 'x'}",
|
|
636
|
+
).replace('usage:', "import: '@acme/widgets/group', usage:"),
|
|
637
|
+
'src/group/index.ts':
|
|
638
|
+
"export {AcmeGroupItem} from './AcmeGroupItem';\n",
|
|
639
|
+
'src/group/AcmeGroupItem.tsx':
|
|
640
|
+
'export function AcmeGroupItem() { return null; }\n',
|
|
641
|
+
},
|
|
642
|
+
);
|
|
643
|
+
expect(namesIn(loaded)).toEqual(['AcmeGroup', 'AcmeGroupItem']);
|
|
644
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeGroupItem')).toBe(
|
|
645
|
+
path.join(root, 'src', 'group', 'AcmeGroupItem.tsx'),
|
|
646
|
+
);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it("finds a member's source in the family's folder when the import names built output", () => {
|
|
650
|
+
const loaded = writePackage(
|
|
651
|
+
{exports: {'./list': './dist/list/index.js'}},
|
|
652
|
+
{
|
|
653
|
+
'src/list/AcmeList.doc.mjs': family(
|
|
654
|
+
'AcmeList',
|
|
655
|
+
"{name: 'AcmeListRow', description: 'x'}",
|
|
656
|
+
).replace('usage:', "import: '@acme/widgets/list', usage:"),
|
|
657
|
+
'src/list/AcmeList.tsx':
|
|
658
|
+
'export function AcmeList() { return null; }\n',
|
|
659
|
+
'src/list/row.tsx': 'export function AcmeListRow() { return null; }\n',
|
|
660
|
+
'src/list/row.test.tsx':
|
|
661
|
+
'export function AcmeListRow() { return null; }\n',
|
|
662
|
+
'dist/list/index.js':
|
|
663
|
+
"export {AcmeList} from './list.js';\nexport * from './row.js';\n",
|
|
664
|
+
'dist/list/list.js': 'export function AcmeList() { return null; }\n',
|
|
665
|
+
'dist/list/row.js': 'export function AcmeListRow() { return null; }\n',
|
|
666
|
+
},
|
|
667
|
+
);
|
|
668
|
+
expect(namesIn(loaded)).toEqual(['AcmeList', 'AcmeListRow']);
|
|
669
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeListRow')).toBe(
|
|
670
|
+
path.join(root, 'src', 'list', 'row.tsx'),
|
|
671
|
+
);
|
|
672
|
+
// Until the output exists, the import exports nothing.
|
|
673
|
+
fs.rmSync(path.join(root, 'dist'), {recursive: true});
|
|
674
|
+
expect(namesIn(loaded)).toEqual(['AcmeList']);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('lists a member another folder declares, with no source', async () => {
|
|
678
|
+
const loaded = writePackage(
|
|
679
|
+
{exports: {'./menu': './src/menu/index.ts'}},
|
|
680
|
+
{
|
|
681
|
+
'src/menu/AcmeMenu.doc.mjs': family(
|
|
682
|
+
'AcmeMenu',
|
|
683
|
+
"{name: 'AcmeMenuItem', description: 'x'}",
|
|
684
|
+
).replace('usage:', "import: '@acme/widgets/menu', usage:"),
|
|
685
|
+
'src/menu/AcmeMenu.tsx':
|
|
686
|
+
'export function AcmeMenu() { return null; }\n',
|
|
687
|
+
'src/menu/index.ts':
|
|
688
|
+
"export {AcmeMenu} from './AcmeMenu';\nexport {AcmeMenuItem} from '../shared/AcmeMenuItem';\n",
|
|
689
|
+
'src/shared/AcmeMenuItem.tsx':
|
|
690
|
+
'export function AcmeMenuItem() { return null; }\n',
|
|
691
|
+
},
|
|
692
|
+
);
|
|
693
|
+
expect(namesIn(loaded)).toEqual(['AcmeMenu', 'AcmeMenuItem']);
|
|
694
|
+
expect(findIntegrationComponentDoc(loaded, 'AcmeMenuItem')).toBe(
|
|
695
|
+
path.join(root, 'src', 'menu', 'AcmeMenu.doc.mjs'),
|
|
696
|
+
);
|
|
697
|
+
expect(findIntegrationComponentSource(loaded, 'AcmeMenuItem')).toBeNull();
|
|
698
|
+
// A member without a source is valid: only a doc's own component needs
|
|
699
|
+
// its same-stem file.
|
|
700
|
+
const {components, errors} = await discoverValidIntegrationComponents(
|
|
701
|
+
/** @type {any} */ (loaded),
|
|
702
|
+
);
|
|
703
|
+
expect(errors).toEqual([]);
|
|
704
|
+
expect(components.map(component => component.name).sort()).toEqual([
|
|
705
|
+
'AcmeMenu',
|
|
706
|
+
'AcmeMenuItem',
|
|
707
|
+
]);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it('reads members a binding holds and entries with computed fields', () => {
|
|
711
|
+
fs.writeFileSync(
|
|
712
|
+
path.join(root, 'AcmeRows.doc.mjs'),
|
|
713
|
+
"const describe = text => text;\nconst MEMBERS = [{name: 'AcmeRow', description: describe('A row.')}];\nexport default {type: 'component', name: 'AcmeRows', usage: {description: 'x'}, components: MEMBERS};\n",
|
|
714
|
+
);
|
|
715
|
+
fs.writeFileSync(
|
|
716
|
+
path.join(root, 'AcmeRows.tsx'),
|
|
717
|
+
'export function AcmeRows() { return null; }\nexport function AcmeRow() { return null; }\n',
|
|
718
|
+
);
|
|
719
|
+
expect(names()).toEqual(['AcmeRow', 'AcmeRows']);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it("lists the doc's own name when its file keeps its own, and declares replaces once", async () => {
|
|
723
|
+
writeFamily('Chip', {
|
|
724
|
+
name: 'AcmeChip',
|
|
725
|
+
extra: "\n replaces: 'Badge',",
|
|
726
|
+
members:
|
|
727
|
+
"[{name: 'AcmeChip', description: 'x'}, {name: 'AcmeChipIcon', description: 'x'}]",
|
|
728
|
+
source:
|
|
729
|
+
'export function Chip() { return null; }\nexport function AcmeChip() { return null; }\nexport function AcmeChipIcon() { return null; }\n',
|
|
730
|
+
});
|
|
731
|
+
const {components} =
|
|
732
|
+
await discoverValidIntegrationComponents(integration());
|
|
733
|
+
expect(components.map(record => [record.name, record.replaces])).toEqual([
|
|
734
|
+
['Chip', 'Badge'],
|
|
735
|
+
['AcmeChip', undefined],
|
|
736
|
+
['AcmeChipIcon', undefined],
|
|
737
|
+
]);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
it('takes no member name a hidden doc or a replacement in the package claims', () => {
|
|
741
|
+
writeFamily('menu/AcmeMenu', {
|
|
742
|
+
name: 'AcmeMenu',
|
|
743
|
+
members:
|
|
744
|
+
"[{name: 'AcmeMenuDivider', description: 'x'}, {name: 'Carousel', description: 'x'}, {name: 'AcmeMenuItem', description: 'x'}]",
|
|
745
|
+
source:
|
|
746
|
+
'export function AcmeMenu() { return null; }\nexport function AcmeMenuDivider() { return null; }\nexport function Carousel() { return null; }\nexport function AcmeMenuItem() { return null; }\n',
|
|
747
|
+
});
|
|
748
|
+
writeComponent('menu/AcmeMenuDivider', {
|
|
749
|
+
name: "'AcmeMenuDivider'",
|
|
750
|
+
extra: '\n hidden: true,',
|
|
751
|
+
});
|
|
752
|
+
writeComponent('Slides', {
|
|
753
|
+
name: "'AcmeCarousel'",
|
|
754
|
+
extra: "\n replaces: 'Carousel',",
|
|
755
|
+
source: 'export function AcmeCarousel() { return null; }\n',
|
|
756
|
+
});
|
|
757
|
+
expect(names()).toEqual(['AcmeCarousel', 'AcmeMenu', 'AcmeMenuItem']);
|
|
758
|
+
expect(findIntegrationComponentDoc(integration(), 'Carousel')).toBeNull();
|
|
759
|
+
});
|
|
760
|
+
});
|