@astryxdesign/cli 0.6.4-canary.ed2e54e → 0.6.4

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.
Files changed (145) hide show
  1. package/README.md +66 -64
  2. package/api/component/_adapter.d.mts +0 -25
  3. package/api/component/_adapter.mjs +5 -59
  4. package/api/component/component.d.mts +3 -6
  5. package/api/component/component.doc.mjs +10 -23
  6. package/api/component/component.mjs +9 -249
  7. package/api/component/component.type.d.mts +0 -25
  8. package/api/component/component.type.mjs +0 -44
  9. package/api/discover/_adapter.d.mts +6 -114
  10. package/api/discover/_adapter.mjs +17 -372
  11. package/api/discover/detail/detail.d.mts +6 -18
  12. package/api/discover/detail/detail.mjs +13 -67
  13. package/api/discover/detail/detail.test.mjs +0 -85
  14. package/api/discover/discover.d.mts +9 -3
  15. package/api/discover/discover.doc.mjs +18 -61
  16. package/api/discover/discover.mjs +36 -220
  17. package/api/discover/discover.test.mjs +2 -11
  18. package/api/discover/discover.type.d.mts +8 -147
  19. package/api/discover/discover.type.mjs +12 -102
  20. package/api/discover/list/list.d.mts +6 -20
  21. package/api/discover/list/list.mjs +12 -45
  22. package/api/discover/list/list.test.mjs +0 -46
  23. package/api/discover/search/search.d.mts +16 -18
  24. package/api/discover/search/search.mjs +56 -102
  25. package/api/discover/search/search.test.mjs +10 -144
  26. package/api/docs/docs.test.mjs +0 -2
  27. package/api/doctor/doctor.d.mts +3 -8
  28. package/api/doctor/doctor.mjs +9 -90
  29. package/api/doctor/doctor.test.mjs +10 -122
  30. package/api/index.d.mts +2 -1
  31. package/api/index.mjs +4 -4
  32. package/api/integration/add-helpers.d.mts +2 -5
  33. package/api/integration/add-helpers.mjs +9 -36
  34. package/api/integration/pack-check.mjs +3 -28
  35. package/api/json/index.ts +1 -0
  36. package/api/layout/_adapter.d.mts +34 -0
  37. package/api/layout/_adapter.mjs +148 -0
  38. package/api/layout/check/check.d.mts +16 -0
  39. package/api/layout/check/check.mjs +40 -0
  40. package/api/layout/expand/expand.d.mts +22 -0
  41. package/api/layout/expand/expand.mjs +155 -0
  42. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  43. package/api/layout/grammar/grammar.d.mts +13 -0
  44. package/api/layout/grammar/grammar.mjs +87 -0
  45. package/api/layout/layout.d.mts +6 -0
  46. package/api/layout/layout.mjs +17 -0
  47. package/api/layout/layout.test.mjs +297 -0
  48. package/api/layout/layout.type.d.mts +89 -0
  49. package/api/layout/layout.type.mjs +103 -0
  50. package/api/layout/layoutCheck.doc.d.mts +11 -0
  51. package/api/layout/layoutCheck.doc.mjs +85 -0
  52. package/api/layout/layoutExpand.doc.d.mts +11 -0
  53. package/api/layout/layoutExpand.doc.mjs +107 -0
  54. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  55. package/api/layout/layoutGrammar.doc.mjs +57 -0
  56. package/api/search/search.test.mjs +0 -18
  57. package/api/template/template-integration.test.mjs +65 -1
  58. package/api/template/template.mjs +1 -1
  59. package/api/theme/add/add.mjs +25 -17
  60. package/api/theme/add/add.staging.test.mjs +23 -40
  61. package/api/theme/build/build.family.test.mjs +12 -7
  62. package/api/theme/build/build.mjs +18 -8
  63. package/api/upgrade/run/run.mjs +4 -6
  64. package/api/upgrade/upgrade.type.mjs +2 -2
  65. package/assets/codemods/__tests__/runner.test.mjs +1 -3
  66. package/assets/codemods/integration-runner.mjs +3 -3
  67. package/assets/codemods/runner.mjs +4 -5
  68. package/assets/docs/internationalization.doc.mjs +5 -7
  69. package/assets/docs/tree/integrations.doc.mjs +1 -20
  70. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  71. package/authoring/config/config.doc.mjs +1 -9
  72. package/authoring/config/parse.d.mts +0 -2
  73. package/authoring/config/parse.mjs +0 -19
  74. package/authoring/config/parse.test.mjs +0 -8
  75. package/authoring/config/type.ts +2 -13
  76. package/authoring/doctypes/command/command.doc.mjs +1 -1
  77. package/authoring/doctypes/command/type.ts +1 -1
  78. package/authoring/index.d.mts +0 -1
  79. package/authoring/index.d.ts +0 -10
  80. package/authoring/index.mjs +0 -1
  81. package/clients/cli/command-result-coverage.test.mjs +7 -7
  82. package/clients/cli/commands/component/index.mjs +55 -152
  83. package/clients/cli/commands/component-ownership.test.mjs +0 -89
  84. package/clients/cli/commands/component.doc.mjs +6 -23
  85. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  86. package/clients/cli/commands/discover.doc.mjs +9 -53
  87. package/clients/cli/commands/discover.mjs +118 -393
  88. package/clients/cli/commands/docs.test.mjs +0 -29
  89. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  90. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  91. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  92. package/clients/cli/commands/layout.doc.mjs +34 -0
  93. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  94. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  95. package/clients/cli/commands/layout.mjs +275 -0
  96. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  97. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  98. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  99. package/clients/cli/commands/text-json-parity.test.mjs +17 -0
  100. package/clients/cli/index.mjs +4 -0
  101. package/clients/cli/lib/exit-codes.test.mjs +8 -1
  102. package/clients/cli/lib/json-shim.mjs +14 -24
  103. package/clients/cli/lib/json-shim.test.mjs +20 -6
  104. package/clients/cli/lib/manifest.mjs +8 -3
  105. package/clients/cli/lib/manifest.test.mjs +2 -5
  106. package/foundation/discovery/authoring-self-docs.mjs +0 -1
  107. package/foundation/discovery/template-adapter.mjs +1 -1
  108. package/foundation/doc-compiler/doc-loads.test.mjs +12 -0
  109. package/foundation/doc-compiler/tree.test.mjs +1 -9
  110. package/foundation/integrations/integrations.d.mts +1 -14
  111. package/foundation/integrations/integrations.mjs +1 -41
  112. package/foundation/integrations/integrations.test.mjs +0 -31
  113. package/foundation/response/response-types.doc.mjs +21 -15
  114. package/foundation/response/response-types.doc.test.mjs +0 -23
  115. package/foundation/xle/browser.d.mts +3 -3
  116. package/foundation/xle/browser.mjs +3 -3
  117. package/foundation/xle/expand.mjs +2 -2
  118. package/foundation/xle/parse.mjs +1 -1
  119. package/foundation/xle/print.mjs +2 -2
  120. package/foundation/xle/splice.mjs +1 -1
  121. package/package.json +9 -9
  122. package/api/discover/_adapter.test.mjs +0 -215
  123. package/api/discover/_catalog-view.d.mts +0 -115
  124. package/api/discover/_catalog-view.mjs +0 -203
  125. package/api/discover/_catalog-view.test.mjs +0 -128
  126. package/api/discover/detail/item/item.d.mts +0 -26
  127. package/api/discover/detail/item/item.mjs +0 -78
  128. package/api/discover/detail/item/item.test.mjs +0 -73
  129. package/api/integration/pack-check.lifecycle-output.test.mjs +0 -105
  130. package/api/theme/add/add.rollback.test.mjs +0 -158
  131. package/api/theme/build/build.rollback.test.mjs +0 -148
  132. package/api/upgrade/run/files-changed.test.mjs +0 -111
  133. package/assets/codemods/file-count.test.mjs +0 -163
  134. package/assets/docs/tree/component-lookups.doc.mjs +0 -149
  135. package/authoring/discover/discover.doc.d.mts +0 -13
  136. package/authoring/discover/discover.doc.mjs +0 -138
  137. package/authoring/discover/parse.d.mts +0 -24
  138. package/authoring/discover/parse.mjs +0 -128
  139. package/authoring/discover/parse.test.mjs +0 -124
  140. package/authoring/discover/type.ts +0 -87
  141. package/clients/cli/commands/component-batch.test.mjs +0 -341
  142. package/clients/cli/commands/discover.sources.test.mjs +0 -267
  143. package/clients/cli/lib/parse-error-format.test.mjs +0 -81
  144. package/foundation/response/batch.type.d.mts +0 -33
  145. package/foundation/response/batch.type.mjs +0 -34
@@ -1,148 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * `theme build` writes its CSS, JS, and declarations as one transaction: when
5
- * a later output fails to publish, every output it already replaced gets its
6
- * previous bytes back and every output it created is removed. Publishing is
7
- * forced to fail by wrapping the two calls that publish a staged file:
8
- * `linkSync` for a new file and `renameSync` for a replacement.
9
- *
10
- * Separate file because vi.mock is hoisted and affects the whole module.
11
- * `themeBuild` needs a built core; the `node` project's globalSetup builds it.
12
- */
13
-
14
- import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
15
- import * as os from 'node:os';
16
- import * as path from 'node:path';
17
-
18
- const failures = vi.hoisted(() => ({
19
- /** Fail the Nth staged-file publish (1-based); 0 disables. */
20
- publish: 0,
21
- count: 0,
22
- }));
23
-
24
- vi.mock('node:fs', async importOriginal => {
25
- const actual = /** @type {typeof import('node:fs')} */ (
26
- await importOriginal()
27
- );
28
- /** @param {string} source @param {string} op */
29
- const maybeFail = (source, op) => {
30
- if (!path.basename(String(source)).includes('.tmp')) return;
31
- if (failures.publish === 0) return;
32
- failures.count++;
33
- if (failures.count === failures.publish) {
34
- throw Object.assign(new Error(`EIO: forced ${op} failure`), {
35
- code: 'EIO',
36
- });
37
- }
38
- };
39
- return {
40
- ...actual,
41
- linkSync: vi.fn((source, destination) => {
42
- maybeFail(source, 'link');
43
- return actual.linkSync(source, destination);
44
- }),
45
- renameSync: vi.fn((source, destination) => {
46
- maybeFail(source, 'rename');
47
- return actual.renameSync(source, destination);
48
- }),
49
- };
50
- });
51
-
52
- const fs = await import('node:fs');
53
- const {themeBuild} = await import('./build.mjs');
54
-
55
- vi.setConfig({testTimeout: 30000});
56
-
57
- let tmpDir;
58
-
59
- beforeEach(() => {
60
- tmpDir = fs.mkdtempSync(
61
- path.join(os.tmpdir(), 'astryx-theme-build-rollback-'),
62
- );
63
- failures.publish = 0;
64
- failures.count = 0;
65
- });
66
-
67
- afterEach(() => {
68
- failures.publish = 0;
69
- fs.rmSync(tmpDir, {recursive: true, force: true});
70
- });
71
-
72
- /**
73
- * Write a source for the theme named `rollbacktheme`. Each call uses a new
74
- * file so the loader cannot serve an earlier version from its cache.
75
- * @param {string} file @param {string} background
76
- */
77
- function writeTheme(file, background) {
78
- fs.writeFileSync(
79
- path.join(tmpDir, file),
80
- `export default { name: 'rollbacktheme', tokens: { '--color-bg': '${background}' } };\n`,
81
- );
82
- return file;
83
- }
84
-
85
- const OUTPUTS = ['rollbacktheme.css', 'rollbacktheme.js', 'rollbacktheme.d.ts'];
86
-
87
- /** @returns {Map<string, Buffer | null>} */
88
- function readOutputs() {
89
- return new Map(
90
- OUTPUTS.map(name => {
91
- const file = path.join(tmpDir, name);
92
- return [name, fs.existsSync(file) ? fs.readFileSync(file) : null];
93
- }),
94
- );
95
- }
96
-
97
- function strays() {
98
- return fs
99
- .readdirSync(tmpDir)
100
- .filter(name => name.includes('.tmp') || name.includes('.restore-'));
101
- }
102
-
103
- describe('themeBuild rolls back a partial write', () => {
104
- it('removes every created output when the second publish fails', async () => {
105
- const source = writeTheme('first.mjs', '#0a0a0a');
106
- failures.publish = 2;
107
-
108
- await expect(themeBuild(source, {}, {cwd: tmpDir})).rejects.toMatchObject({
109
- code: 'ERR_WRITE_FAILED',
110
- });
111
-
112
- for (const bytes of readOutputs().values()) expect(bytes).toBeNull();
113
- expect(strays()).toEqual([]);
114
- });
115
-
116
- it('restores every replaced output when the second publish fails', async () => {
117
- const first = await themeBuild(
118
- writeTheme('first.mjs', '#0a0a0a'),
119
- {},
120
- {cwd: tmpDir},
121
- );
122
- expect(first?.type).toBe('theme.build');
123
- const before = readOutputs();
124
- for (const bytes of before.values()) expect(bytes).not.toBeNull();
125
-
126
- const next = writeTheme('next.mjs', '#fafafa');
127
- failures.publish = 2;
128
- await expect(themeBuild(next, {}, {cwd: tmpDir})).rejects.toMatchObject({
129
- code: 'ERR_WRITE_FAILED',
130
- });
131
-
132
- const after = readOutputs();
133
- for (const name of OUTPUTS) {
134
- expect(
135
- after.get(name)?.equals(/** @type {Buffer} */ (before.get(name))),
136
- ).toBe(true);
137
- }
138
-
139
- // The failed build really had different bytes to write.
140
- failures.publish = 0;
141
- await themeBuild(next, {}, {cwd: tmpDir});
142
- const css = readOutputs().get('rollbacktheme.css');
143
- expect(
144
- css?.equals(/** @type {Buffer} */ (before.get('rollbacktheme.css'))),
145
- ).toBe(false);
146
- expect(strays()).toEqual([]);
147
- });
148
- });
@@ -1,111 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file The upgrade receipt counts a file once when a core codemod AND an
5
- * integration codemod both change it: `filesChanged` is the union of the two
6
- * runners' files, while `transformsApplied` adds their changes.
7
- *
8
- * Uses a real consumer under a repo-local temp dir (Vite blocks dynamic import
9
- * of config and integration modules from /tmp), the real core registry, and an
10
- * installed integration whose code codemod stamps the same file.
11
- */
12
-
13
- import {describe, it, expect, afterEach} from 'vitest';
14
- import * as fs from 'node:fs';
15
- import * as path from 'node:path';
16
- import {upgrade} from '../upgrade.mjs';
17
- import {
18
- latestVersion,
19
- versions,
20
- getTransformsBetween,
21
- } from '../../../assets/codemods/registry.mjs';
22
-
23
- const SLOW = 60_000;
24
-
25
- /** The registry version whose manifest ships the authoring migration. */
26
- async function authoringTier() {
27
- const all = await getTransformsBetween('0.0.0', latestVersion);
28
- const tier = all.find(({transforms}) =>
29
- transforms.some(t => t.name === 'migrate-authoring-imports'),
30
- );
31
- if (!tier) throw new Error('no registry version ships the authoring migration');
32
- return tier.version;
33
- }
34
-
35
- /**
36
- * A consumer with installed core at the registry's latest version and one
37
- * old-surface file that the core authoring codemods rewrite.
38
- * @param {string} dir
39
- * @param {{integrationCodemod: boolean}} options
40
- */
41
- function seed(dir, {integrationCodemod}) {
42
- fs.writeFileSync(
43
- path.join(dir, 'package.json'),
44
- JSON.stringify({name: 'consumer', version: '1.0.0'}),
45
- );
46
- const core = path.join(dir, 'node_modules', '@astryxdesign', 'core');
47
- fs.mkdirSync(core, {recursive: true});
48
- fs.writeFileSync(
49
- path.join(core, 'package.json'),
50
- JSON.stringify({name: '@astryxdesign/core', version: latestVersion}),
51
- );
52
- fs.mkdirSync(path.join(dir, 'src'), {recursive: true});
53
- fs.writeFileSync(
54
- path.join(dir, 'src', 'Button.doc.mjs'),
55
- [
56
- "import {createComponentDoc} from '@astryxdesign/core/authoring';",
57
- "export default createComponentDoc({name: 'Button', props: []});",
58
- '',
59
- ].join('\n'),
60
- );
61
- if (!integrationCodemod) return;
62
- fs.writeFileSync(
63
- path.join(dir, 'astryx.config.mjs'),
64
- "export default {integrations: ['@acme/widgets']};\n",
65
- );
66
- const pkg = path.join(dir, 'node_modules', '@acme', 'widgets');
67
- fs.mkdirSync(path.join(pkg, 'codemods', latestVersion), {recursive: true});
68
- fs.writeFileSync(
69
- path.join(pkg, 'package.json'),
70
- JSON.stringify({name: '@acme/widgets', version: '1.0.0'}),
71
- );
72
- fs.writeFileSync(
73
- path.join(pkg, 'astryx.integration.mjs'),
74
- "export default {codemods: './codemods'};\n",
75
- );
76
- fs.writeFileSync(
77
- path.join(pkg, 'codemods', latestVersion, 'acme-stamp.mjs'),
78
- "export default {type: 'code', title: 'Stamp', transform: file => (file.source.includes('// acme') ? null : `${file.source}// acme\\n`)};\n",
79
- );
80
- }
81
-
82
- describe('upgrade receipt — filesChanged across core and integration codemods', () => {
83
- /** @type {string[]} */
84
- const dirs = [];
85
- afterEach(() => {
86
- for (const dir of dirs.splice(0)) fs.rmSync(dir, {recursive: true, force: true});
87
- });
88
-
89
- /** @param {{integrationCodemod: boolean}} options */
90
- async function run(options) {
91
- const dir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-files-changed-'));
92
- dirs.push(dir);
93
- seed(dir, options);
94
- const tier = await authoringTier();
95
- const from = versions[versions.indexOf(tier) - 1];
96
- const res = await upgrade({from, path: 'src'}, {cwd: dir});
97
- expect(res.type).toBe('upgrade.run');
98
- return res.data;
99
- }
100
-
101
- it('counts a file changed by both a core and an integration codemod once', async () => {
102
- const coreOnly = await run({integrationCodemod: false});
103
- expect(coreOnly.filesChanged).toBe(1);
104
- expect(coreOnly.transformsApplied).toBeGreaterThan(0);
105
-
106
- const both = await run({integrationCodemod: true});
107
- expect(both.integrations).toEqual(['@acme/widgets']);
108
- expect(both.filesChanged).toBe(1);
109
- expect(both.transformsApplied).toBe(coreOnly.transformsApplied + 1);
110
- }, SLOW);
111
- });
@@ -1,163 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file `filesChanged` counts FILES, not (codemod, file) pairs.
5
- *
6
- * One source file that four codemods each changed was reported as four files
7
- * changed — the total was incremented once per transform per file, so it
8
- * equalled `transformsApplied` in every run and the documented meaning of the
9
- * field ("Total files changed") was never true. The two numbers answer
10
- * different questions and both are in the receipt.
11
- */
12
-
13
- import {describe, it, expect, beforeEach, afterEach} from 'vitest';
14
- import * as fs from 'node:fs';
15
- import * as os from 'node:os';
16
- import * as path from 'node:path';
17
- import jscodeshift from 'jscodeshift';
18
- import {runCodemods} from './runner.mjs';
19
- import {runIntegrationCodemods} from './integration-runner.mjs';
20
-
21
- let dir;
22
-
23
- beforeEach(() => {
24
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-file-count-'));
25
- });
26
- afterEach(() => fs.rmSync(dir, {recursive: true, force: true}));
27
-
28
- /** A transform that rewrites one distinctive token, so several can stack. */
29
- const renaming = (from, to) => (file) =>
30
- file.source.includes(from) ? file.source.split(from).join(to) : null;
31
-
32
- /** @param {string} name @param {string[]} contents */
33
- function writeSources(...contents) {
34
- return contents.map((content, i) => {
35
- const file = path.join(dir, `file${i}.ts`);
36
- fs.writeFileSync(file, content);
37
- return file;
38
- });
39
- }
40
-
41
- describe('core codemod runner — filesChanged counts files', () => {
42
- it('reports 1 file for one file changed by four codemods', async () => {
43
- writeSources('const a = ONE + TWO + THREE + FOUR;\n');
44
-
45
- const result = await runCodemods(
46
- [
47
- {
48
- version: '0.0.2',
49
- transforms: [
50
- {name: 'one', transform: renaming('ONE', '1'), meta: {title: 'one'}},
51
- {name: 'two', transform: renaming('TWO', '2'), meta: {title: 'two'}},
52
- {name: 'three', transform: renaming('THREE', '3'), meta: {title: 'three'}},
53
- {name: 'four', transform: renaming('FOUR', '4'), meta: {title: 'four'}},
54
- ],
55
- },
56
- ],
57
- {apply: true, path: dir, root: dir, codemod: undefined, skipCodemods: new Set(), silent: true},
58
- );
59
-
60
- expect(result.totalTransformsApplied).toBe(4);
61
- expect(result.totalFilesChanged).toBe(1);
62
- expect(new Set(result.changedFiles).size).toBe(1);
63
- });
64
-
65
- it('still counts two files as two', async () => {
66
- writeSources('const a = ONE;\n', 'const b = ONE;\n');
67
-
68
- const result = await runCodemods(
69
- [
70
- {
71
- version: '0.0.2',
72
- transforms: [
73
- {name: 'one', transform: renaming('ONE', '1'), meta: {title: 'one'}},
74
- ],
75
- },
76
- ],
77
- {apply: true, path: dir, root: dir, codemod: undefined, skipCodemods: new Set(), silent: true},
78
- );
79
-
80
- expect(result.totalTransformsApplied).toBe(2);
81
- expect(result.totalFilesChanged).toBe(2);
82
- });
83
-
84
- it('reports 0 when nothing matched', async () => {
85
- writeSources('const a = 1;\n');
86
-
87
- const result = await runCodemods(
88
- [
89
- {
90
- version: '0.0.2',
91
- transforms: [
92
- {name: 'one', transform: renaming('ONE', '1'), meta: {title: 'one'}},
93
- ],
94
- },
95
- ],
96
- {apply: true, path: dir, root: dir, codemod: undefined, skipCodemods: new Set(), silent: true},
97
- );
98
-
99
- expect(result.totalFilesChanged).toBe(0);
100
- expect(result.totalTransformsApplied).toBe(0);
101
- });
102
- });
103
-
104
- describe('integration codemod runner — filesChanged counts files', () => {
105
- it('reports 1 file for one file changed by three integration codemods', () => {
106
- writeSources('const a = ONE + TWO + THREE;\n');
107
-
108
- const entry = (id, from, to) => ({
109
- id,
110
- package: '@acme/widgets',
111
- type: 'code',
112
- codemod: {title: id, transform: renaming(from, to)},
113
- });
114
-
115
- const result = runIntegrationCodemods(
116
- [
117
- {
118
- version: '1.0.0',
119
- codemods: [
120
- entry('one', 'ONE', '1'),
121
- entry('two', 'TWO', '2'),
122
- entry('three', 'THREE', '3'),
123
- ],
124
- },
125
- ],
126
- {apply: true, path: dir, root: dir, skipCodemods: new Set(), jscodeshift, silent: true},
127
- );
128
-
129
- expect(result.totalTransformsApplied).toBe(3);
130
- expect(result.totalFilesChanged).toBe(1);
131
- expect(new Set(result.changedFiles).size).toBe(1);
132
- });
133
- });
134
-
135
- describe('core codemod runner — a project codemod', () => {
136
- it('counts every file it writes, and one change', async () => {
137
- const result = await runCodemods(
138
- [
139
- {
140
- version: '0.0.2',
141
- transforms: [
142
- {
143
- name: 'project-plan',
144
- meta: {title: 'project plan', codemodType: 'project'},
145
- transform: async root => ({
146
- writes: [
147
- {path: path.join(root, 'a.ts'), contents: 'a\n'},
148
- {path: path.join(root, 'b.ts'), contents: 'b\n'},
149
- ],
150
- deletes: [],
151
- problems: [],
152
- }),
153
- },
154
- ],
155
- },
156
- ],
157
- {apply: true, path: dir, root: dir, codemod: undefined, skipCodemods: new Set(), silent: true},
158
- );
159
-
160
- expect(result.totalFilesChanged).toBe(2);
161
- expect(result.totalTransformsApplied).toBe(1);
162
- });
163
- });
@@ -1,149 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file `astryx docs cli/component-lookups`: exact single and batch component
5
- * lookup through the CLI and programmatic API.
6
- */
7
-
8
- /** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
9
- export const docs = {
10
- type: 'generic',
11
- name: 'component-lookups',
12
- placement: {parent: 'namespace:cli', slot: 'guides', order: 5},
13
- title: 'Looking up components',
14
- category: 'guide',
15
- description:
16
- 'Look up one or several exact component identities, choose a focused projection, and handle complete batch receipts.',
17
- sections: [
18
- {
19
- id: 'several',
20
- title: 'Look up several components',
21
- category: 'guide',
22
- content: [
23
- {
24
- type: 'prose',
25
- text: '`astryx component` accepts exact selectors as a variadic positional argument. With no selector it browses the catalog. With one selector it keeps the normal single-component response. With two or more it prints one complete ordered batch receipt.',
26
- },
27
- {
28
- type: 'code',
29
- lang: 'bash',
30
- label: 'Several component docs',
31
- code: 'astryx component Button Badge Text\nastryx --json component Button Badge Text',
32
- },
33
- {
34
- type: 'prose',
35
- text: 'Every selector gets one row in input order. Duplicate selectors stay duplicate rows. A missing or ambiguous component does not hide successful neighbors or stop later selectors from resolving.',
36
- },
37
- {
38
- type: 'prose',
39
- text: 'A batch accepts at most 100 selectors, including duplicates, in every projection mode. A larger request returns a top-level `ERR_INVALID_ARGUMENT` before any component resolves. It emits no `component.batch` receipt and no partial results.',
40
- },
41
- {
42
- type: 'prose',
43
- text: 'Focused component controls apply to every found row. Use the same control you use for one component:',
44
- },
45
- {
46
- type: 'code',
47
- lang: 'bash',
48
- label: 'Focused batch lookups',
49
- code: 'astryx --json component Button Card --props\nastryx component Button Card --source\nastryx component Button Card --showcase\nastryx component Button Card --blocks\nastryx component Button Card --detail compact\nastryx component Button Card --lang dense\nastryx component Button Card --package @astryxdesign/core',
50
- },
51
- ],
52
- },
53
- {
54
- id: 'selectors',
55
- title: 'Selector forms',
56
- category: 'reference',
57
- content: [
58
- {
59
- type: 'prose',
60
- text: 'A selector is an exact component identity, not free-text search. Use one of these forms:',
61
- },
62
- {
63
- type: 'list',
64
- style: 'unordered',
65
- items: [
66
- '`Button` for an unqualified component name.',
67
- '`widgets/Button` for a component in an unscoped package.',
68
- '`@acme/widgets/Button` for a component in a scoped package.',
69
- '`@acme/widgets@1.2.3/Button` to require that exact installed package version.',
70
- ],
71
- },
72
- {
73
- type: 'prose',
74
- text: 'A version qualifies the package, never the component. The lookup does not fall through to another installed version. An unqualified name owned by several installed packages is `ambiguous` and lists every candidate. Use a package-qualified selector or `--package` to choose one.',
75
- },
76
- {
77
- type: 'prose',
78
- text: '`astryx discover` remains free-text package discovery. Its words form one query; they are not component batch selectors.',
79
- },
80
- ],
81
- },
82
- {
83
- id: 'output',
84
- title: 'Batch output and exit status',
85
- category: 'reference',
86
- content: [
87
- {
88
- type: 'prose',
89
- text: 'JSON uses `component.batch` with `{count, results}`. Each row echoes `selector` and has one status: `found`, `not_found`, `ambiguous`, or `error`. A found row carries the normal single-component `{type, data}` under `result`. Failed rows carry `code` and `error`, plus `suggestions` or `candidates` when available.',
90
- },
91
- {
92
- type: 'code',
93
- lang: 'bash',
94
- label: 'Outcome and duplicate examples',
95
- code: 'astryx --json component Button Badge # all found, exit 0\nastryx --json component Button MissingWidget # mixed, exit 1\nastryx --json component MissingWidget MissingPanel # all failed, exit 1\nastryx --json component Button Button # two ordered rows, exit 0',
96
- },
97
- {
98
- type: 'code',
99
- lang: 'bash',
100
- label: 'A complete failed JSON receipt',
101
- code: 'astryx --json component MissingWidget MissingPanel',
102
- },
103
- {
104
- type: 'code',
105
- lang: 'json',
106
- code: '{\n "apiVersion": 1,\n "type": "component.batch",\n "data": {\n "count": 2,\n "results": [\n {\n "selector": "MissingWidget",\n "status": "not_found",\n "code": "ERR_UNKNOWN_COMPONENT",\n "error": "No component named \\"MissingWidget\\""\n },\n {\n "selector": "MissingPanel",\n "status": "not_found",\n "code": "ERR_UNKNOWN_COMPONENT",\n "error": "No component named \\"MissingPanel\\""\n }\n ]\n }\n}',
107
- },
108
- {
109
- type: 'code',
110
- lang: 'text',
111
- label: 'The same receipt in text mode',
112
- code: 'Component batch\n\ncount: 2\n\nResults\n\nMissingWidget\n\nselector: MissingWidget\nstatus: not_found\ncode: ERR_UNKNOWN_COMPONENT\nerror: No component named "MissingWidget"\n\nMissingPanel\n\nselector: MissingPanel\nstatus: not_found\ncode: ERR_UNKNOWN_COMPONENT\nerror: No component named "MissingPanel"',
113
- },
114
- {
115
- type: 'prose',
116
- text: 'The CLI emits every row first, then exits 1 when any row is not `found`. This includes mixed receipts and receipts where every row failed. JSON and text use the same exit status. A batch where every row is `found` exits 0.',
117
- },
118
- ],
119
- },
120
- {
121
- id: 'api',
122
- title: 'Programmatic API',
123
- category: 'reference',
124
- content: [
125
- {
126
- type: 'prose',
127
- text: 'The argument shape chooses the response shape. Omit the argument for the catalog, pass a string for the existing single-component response, and pass an array for `component.batch`. An array always means batch, including empty and one-item arrays, so filtering a selector list cannot silently change the response type. The published `ComponentBatchResponse` specializes the shared `BatchResponse` and `BatchRow` types.',
128
- },
129
- {
130
- type: 'code',
131
- lang: 'javascript',
132
- code: "import {component} from '@astryxdesign/cli/api';\n\nconst catalog = await component(); // component.list\nconst button = await component('Button'); // component.detail\nconst empty = await component([]); // component.batch, count 0\nconst oneRow = await component(['Button']); // component.batch, count 1\nconst batch = await component(['Button', 'Badge']); // component.batch, count 2\nawait component(Array(101).fill('Button')); // ERR_INVALID_ARGUMENT before lookup",
133
- },
134
- {
135
- type: 'code',
136
- lang: 'json',
137
- label: 'Exact empty-array response',
138
- code: '{\n "type": "component.batch",\n "data": {\n "count": 0,\n "results": []\n }\n}',
139
- },
140
- {
141
- type: 'code',
142
- lang: 'javascript',
143
- label: 'Handle every row without losing partial results',
144
- code: "const receipt = await component(['Button', 'MissingWidget']);\n\nfor (const row of receipt.data.results) {\n if (row.status === 'found') {\n useComponentDoc(row.selector, row.result);\n } else if (row.status === 'ambiguous') {\n choosePackage(row.selector, row.candidates);\n } else {\n reportLookupFailure(row.selector, row.code, row.error);\n }\n}",
145
- },
146
- ],
147
- },
148
- ],
149
- };
@@ -1,13 +0,0 @@
1
- // @generated by scripts/sync-api-types.mjs from the JSDoc in authoring/**/*.mjs.
2
- // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
-
4
- /**
5
- * @file SchemaDoc for DiscoverSource, the function `astryx discover` calls to
6
- * learn which integrations a project could add.
7
- * @input The DiscoverSource type and the catalog types beside it (`type.ts`),
8
- * which `parse.mjs` validates.
9
- * @output The `discover-source` section of `astryx docs authoring`.
10
- * @position packages/cli/authoring/discover — schema documentation
11
- */
12
- /** @type {import('@astryxdesign/cli/authoring').SchemaDoc} */
13
- export const doc: import("@astryxdesign/cli/authoring").SchemaDoc;