@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
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
* @file Colocated types for the `upgrade` command — source of truth for the
|
|
5
5
|
* upgrade command JSON responses. Re-exported by `types/upgrade.d.ts`.
|
|
6
6
|
*
|
|
7
|
-
* Invocation
|
|
7
|
+
* Invocation -> type discriminator
|
|
8
8
|
* ------------------------------------------------------------------
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* (version detection failure)
|
|
9
|
+
* astryx --json upgrade --list -> upgrade.list
|
|
10
|
+
* astryx --json upgrade [--apply] -> upgrade.run
|
|
11
|
+
* astryx --json upgrade --registry [--apply] -> upgrade.registry
|
|
12
|
+
* astryx --json upgrade (status short-circuit) -> upgrade.status
|
|
13
|
+
* (version detection failure) -> CLIError
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* astryx --json upgrade --list
|
|
18
18
|
* @typedef {object} UpgradeListResponse
|
|
19
19
|
* @property {'upgrade.list'} type
|
|
20
20
|
* @property {UpgradeListEntry[]} data
|
|
@@ -88,14 +88,14 @@
|
|
|
88
88
|
*/
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* astryx --json upgrade --registry [--apply]
|
|
92
92
|
* @typedef {object} UpgradeRegistryResponse
|
|
93
93
|
* @property {'upgrade.registry'} type
|
|
94
94
|
* @property {RegistryCompositionSummary} data
|
|
95
95
|
*/
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* astryx --json upgrade [--apply]
|
|
99
99
|
* @typedef {object} UpgradeRunResponse
|
|
100
100
|
* @property {'upgrade.run'} type
|
|
101
101
|
* @property {object} data
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
*/
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* astryx --json upgrade — short-circuit status results.
|
|
116
116
|
*
|
|
117
117
|
* - `up_to_date`: `--from` is >= installed target and `--force` was not passed.
|
|
118
118
|
* - `no_codemods`: no codemods (core or integration) apply to the range.
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
* @property {boolean} [force] Run codemods even if `from` >= installed.
|
|
136
136
|
* @property {string} [codemod] Run a single named transform.
|
|
137
137
|
* @property {string[]} [skipCodemod] Exclude named codemods (re-run past a failure).
|
|
138
|
-
* @property {string[]} [integration] Explicit integration
|
|
138
|
+
* @property {string[]} [integration] Explicit integration specifiers resolved beneath node_modules; absolute paths and `.` or `..` segments are rejected.
|
|
139
139
|
* @property {string} [path] Source directory to scan (default `./src`).
|
|
140
140
|
* @property {boolean} [installDeps] Auto-install jscodeshift without prompting.
|
|
141
141
|
* @property {boolean} [registry] Reconcile only ShadCN-copied compositions; `from` is not required.
|
|
@@ -32,6 +32,40 @@ import {semverCompare} from '../../foundation/env/semver.mjs';
|
|
|
32
32
|
/** File extensions recognized as codemod modules. */
|
|
33
33
|
const CODEMOD_EXTENSIONS = ['.ts', '.mjs', '.js'];
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Directories never walked for codemods: a test directory beside a transform is
|
|
37
|
+
* the natural place to put its test, and every file found here is loaded and
|
|
38
|
+
* validated as a codemod.
|
|
39
|
+
*/
|
|
40
|
+
const SKIP_DIRS = new Set([
|
|
41
|
+
'node_modules',
|
|
42
|
+
'.git',
|
|
43
|
+
'__tests__',
|
|
44
|
+
'__fixtures__',
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether a file name is a test or fixture rather than a codemod.
|
|
49
|
+
*
|
|
50
|
+
* The trap this closes: EVERY `.ts`/`.mjs`/`.js` under a version folder used to
|
|
51
|
+
* be loaded as a codemod, so a test file colocated with its transform failed
|
|
52
|
+
* validation and — because a definition error is a hard error — took every
|
|
53
|
+
* codemod in that package's version with it. `astryx upgrade` then applied no
|
|
54
|
+
* transforms and reported success, which is the worst shape a failure can take.
|
|
55
|
+
*
|
|
56
|
+
* Core's own codemods never hit this: they are enumerated in a registry, and
|
|
57
|
+
* their colocated tests are simply not in it. Only integrations are discovered
|
|
58
|
+
* by walking a directory, so only integrations carry the landmine — which is why
|
|
59
|
+
* this is a loader fix and not a documentation one. It protects the integrations
|
|
60
|
+
* that already exist, which no authoring tool can reach.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} name a file's base name
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
function isTestFile(name) {
|
|
66
|
+
return /\.(test|spec)\.[^.]+$/.test(name) || /\.fixture\.[^.]+$/.test(name);
|
|
67
|
+
}
|
|
68
|
+
|
|
35
69
|
/**
|
|
36
70
|
* Recursively collect codemod module files under a version folder. Returns
|
|
37
71
|
* entries of {id, file} where id is the extension-less relative path
|
|
@@ -49,14 +83,18 @@ function collectCodemodFiles(versionDir) {
|
|
|
49
83
|
for (const entry of entries) {
|
|
50
84
|
const full = path.join(dir, entry.name);
|
|
51
85
|
if (entry.isDirectory()) {
|
|
52
|
-
if (
|
|
86
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
53
87
|
walk(full);
|
|
54
88
|
continue;
|
|
55
89
|
}
|
|
56
90
|
const ext = path.extname(entry.name);
|
|
57
91
|
if (!CODEMOD_EXTENSIONS.includes(ext)) continue;
|
|
92
|
+
if (isTestFile(entry.name)) continue;
|
|
58
93
|
const rel = path.relative(versionDir, full);
|
|
59
|
-
const id = rel
|
|
94
|
+
const id = rel
|
|
95
|
+
.slice(0, rel.length - ext.length)
|
|
96
|
+
.split(path.sep)
|
|
97
|
+
.join('/');
|
|
60
98
|
out.push({id, file: full});
|
|
61
99
|
}
|
|
62
100
|
}
|
|
@@ -221,3 +221,61 @@ describe('integration codemod discovery', () => {
|
|
|
221
221
|
).rejects.toThrow(/across versions/i);
|
|
222
222
|
});
|
|
223
223
|
});
|
|
224
|
+
|
|
225
|
+
describe('test files beside a codemod', () => {
|
|
226
|
+
// The incident this closes: every .ts/.mjs/.js under a version folder was
|
|
227
|
+
// loaded AND VALIDATED as a codemod, so a test file colocated with its
|
|
228
|
+
// transform failed validation — and because a definition error is a hard
|
|
229
|
+
// error, it took every codemod in that version with it. `astryx upgrade`
|
|
230
|
+
// then applied nothing and reported success. Core is immune because its own
|
|
231
|
+
// codemods are enumerated in a registry rather than discovered by walking a
|
|
232
|
+
// directory, so its colocated tests are simply never visited. Only
|
|
233
|
+
// integrations carry the landmine.
|
|
234
|
+
const TRANSFORM = `
|
|
235
|
+
export default {
|
|
236
|
+
type: 'code',
|
|
237
|
+
title: 'Drop foo',
|
|
238
|
+
transform: (file) => file.source.replace(/foo/g, 'bar'),
|
|
239
|
+
};
|
|
240
|
+
`;
|
|
241
|
+
// Not a codemod: no default export of the right shape. Loading it throws.
|
|
242
|
+
const A_TEST = `
|
|
243
|
+
import {describe, it, expect} from 'vitest';
|
|
244
|
+
describe('drop-foo', () => {
|
|
245
|
+
it('drops foo', () => expect(1).toBe(1));
|
|
246
|
+
});
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
it.each([
|
|
250
|
+
['a .test. sibling', '0.2.0/drop-foo.test.mjs'],
|
|
251
|
+
['a .spec. sibling', '0.2.0/drop-foo.spec.mjs'],
|
|
252
|
+
['a fixture sibling', '0.2.0/drop-foo.fixture.mjs'],
|
|
253
|
+
['a __tests__ directory', '0.2.0/__tests__/drop-foo.mjs'],
|
|
254
|
+
['a __fixtures__ directory', '0.2.0/__fixtures__/input.mjs'],
|
|
255
|
+
])('ignores %s and still discovers the codemod', async (_label, testPath) => {
|
|
256
|
+
scaffold({'0.2.0/drop-foo.mjs': TRANSFORM, [testPath]: A_TEST});
|
|
257
|
+
|
|
258
|
+
const project = await Project.load(tmpDir);
|
|
259
|
+
const byVersion = await discoverIntegrationCodemods(
|
|
260
|
+
project.loadedIntegrations,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect([...byVersion.keys()]).toEqual(['0.2.0']);
|
|
264
|
+
expect(byVersion.get('0.2.0').map(entry => entry.id)).toEqual(['drop-foo']);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('a nested helper directory is still walked', async () => {
|
|
268
|
+
// Only test and fixture names are skipped. A package that organises its
|
|
269
|
+
// transforms into subdirectories keeps working.
|
|
270
|
+
scaffold({'0.2.0/imports/drop-foo.mjs': TRANSFORM});
|
|
271
|
+
|
|
272
|
+
const project = await Project.load(tmpDir);
|
|
273
|
+
const byVersion = await discoverIntegrationCodemods(
|
|
274
|
+
project.loadedIntegrations,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
expect(byVersion.get('0.2.0').map(entry => entry.id)).toEqual([
|
|
278
|
+
'imports/drop-foo',
|
|
279
|
+
]);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import {describe, it, expect} from 'vitest';
|
|
4
4
|
|
|
5
5
|
async function applyTransform(source, path = 'test.ts') {
|
|
6
|
-
const {default: transform} =
|
|
6
|
+
const {default: transform} =
|
|
7
|
+
await import('../unwrap-authoring-factories.mjs');
|
|
7
8
|
const jscodeshift = (await import('jscodeshift')).default;
|
|
8
9
|
const j = jscodeshift.withParser('tsx');
|
|
9
10
|
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
@@ -18,7 +19,7 @@ export default createConfig({integrations: ['@acme/widgets']});
|
|
|
18
19
|
`;
|
|
19
20
|
const output = await applyTransform(input);
|
|
20
21
|
expect(output).not.toContain('createConfig');
|
|
21
|
-
expect(output).toContain(
|
|
22
|
+
expect(output).toContain('export default {');
|
|
22
23
|
expect(output).toContain("integrations: ['@acme/widgets']");
|
|
23
24
|
expect(output).not.toContain('type:');
|
|
24
25
|
});
|
|
@@ -132,13 +133,34 @@ export default createComponentDoc();
|
|
|
132
133
|
expect(output).toContain("type: 'component'");
|
|
133
134
|
});
|
|
134
135
|
|
|
136
|
+
it('leaves same-named factories from unrelated packages unchanged', async () => {
|
|
137
|
+
const input = `import {createConfig} from '@acme/eslint';
|
|
138
|
+
export default createConfig({strict: true});
|
|
139
|
+
`;
|
|
140
|
+
expect(await applyTransform(input)).toBe(input);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('keeps an unrelated same-named import when another factory is migrated', async () => {
|
|
144
|
+
const input = `import {createConfig as createLintConfig} from '@acme/eslint';
|
|
145
|
+
import {createDoc} from '@astryxdesign/cli/doc';
|
|
146
|
+
export const lintConfig = createLintConfig({strict: true});
|
|
147
|
+
export const doc = createDoc({name: 'Theming', description: 'How theming works.'});
|
|
148
|
+
`;
|
|
149
|
+
const output = await applyTransform(input);
|
|
150
|
+
expect(output).toContain(
|
|
151
|
+
"import {createConfig as createLintConfig} from '@acme/eslint'",
|
|
152
|
+
);
|
|
153
|
+
expect(output).toContain('createLintConfig({strict: true})');
|
|
154
|
+
expect(output).not.toContain('createDoc');
|
|
155
|
+
expect(output).toContain("type: 'generic'");
|
|
156
|
+
});
|
|
157
|
+
|
|
135
158
|
it('is a no-op when no authoring factory is imported', async () => {
|
|
136
159
|
const input = `import {Button} from '@astryxdesign/core';
|
|
137
160
|
export default Button;
|
|
138
161
|
`;
|
|
139
|
-
const {default: transform} =
|
|
140
|
-
'../unwrap-authoring-factories.mjs'
|
|
141
|
-
);
|
|
162
|
+
const {default: transform} =
|
|
163
|
+
await import('../unwrap-authoring-factories.mjs');
|
|
142
164
|
const jscodeshift = (await import('jscodeshift')).default;
|
|
143
165
|
const j = jscodeshift.withParser('tsx');
|
|
144
166
|
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
* v0.3.0 removes the authoring factories. Authoring is now types + parsers: an
|
|
7
7
|
* author writes a plain object and stamps its `type` directly. This transform
|
|
8
|
-
* rewrites
|
|
9
|
-
* then drops the now-dead
|
|
8
|
+
* rewrites factory calls imported from the retired Astryx authoring entrypoints
|
|
9
|
+
* to the plain object the factory used to return, then drops the now-dead
|
|
10
|
+
* factory imports:
|
|
10
11
|
*
|
|
11
12
|
* createConfig(o) / createIntegration(o) -> o (no discriminant)
|
|
12
13
|
* createComponentDoc(o) -> { ...o, type: 'component' }
|
|
@@ -25,8 +26,9 @@
|
|
|
25
26
|
*
|
|
26
27
|
* Import aliases are followed (`import {createDoc as mk}` → calls to `mk`), and
|
|
27
28
|
* the factory specifiers are removed afterward (the whole import statement goes
|
|
28
|
-
* if nothing else was imported from it).
|
|
29
|
-
* `migrate-authoring-imports`, which repoints
|
|
29
|
+
* if nothing else was imported from it). Same-named imports from other packages
|
|
30
|
+
* remain untouched. Run this BEFORE `migrate-authoring-imports`, which repoints
|
|
31
|
+
* the surviving type imports.
|
|
30
32
|
*/
|
|
31
33
|
|
|
32
34
|
export const meta = {
|
|
@@ -35,13 +37,24 @@ export const meta = {
|
|
|
35
37
|
'Rewrites createConfig/createIntegration/createComponentDoc/' +
|
|
36
38
|
'createFunctionDoc/createDoc/createPageTemplate/createBlockTemplate/' +
|
|
37
39
|
'createCodemod/createConfigCodemod calls to the plain object they returned ' +
|
|
38
|
-
|
|
40
|
+
'(stamping the doc/template/codemod `type` discriminant), and removes the ' +
|
|
39
41
|
'now-dead factory imports. Authoring is types + parsers in v0.3.0 — there ' +
|
|
40
42
|
'are no factories.',
|
|
41
43
|
pr: '#4612',
|
|
42
44
|
fileExtensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'],
|
|
43
45
|
};
|
|
44
46
|
|
|
47
|
+
/** Legacy Astryx authoring entrypoints that exported the removed factories. */
|
|
48
|
+
const AUTHORING_SOURCES = new Set([
|
|
49
|
+
'@astryxdesign/cli/config',
|
|
50
|
+
'@astryxdesign/cli/doc',
|
|
51
|
+
'@astryxdesign/cli/integration',
|
|
52
|
+
'@astryxdesign/cli/template',
|
|
53
|
+
'@astryxdesign/cli/codemod',
|
|
54
|
+
'@astryxdesign/core/authoring',
|
|
55
|
+
'@astryxdesign/core/config',
|
|
56
|
+
]);
|
|
57
|
+
|
|
45
58
|
/**
|
|
46
59
|
* Factory name → the `type` discriminant it stamped, or `null` for the config /
|
|
47
60
|
* integration factories, which were pure typed-identity (no discriminant).
|
|
@@ -108,6 +121,7 @@ export default function transformer(file, api) {
|
|
|
108
121
|
/** @type {Map<string, string>} */
|
|
109
122
|
const localToFactory = new Map();
|
|
110
123
|
root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => {
|
|
124
|
+
if (!AUTHORING_SOURCES.has(path.node.source.value)) return;
|
|
111
125
|
for (const spec of path.node.specifiers ?? []) {
|
|
112
126
|
if (spec.type !== 'ImportSpecifier') continue;
|
|
113
127
|
const importedName = spec.imported?.name;
|
|
@@ -163,6 +177,7 @@ export default function transformer(file, api) {
|
|
|
163
177
|
// Drop the now-dead factory import specifiers; remove any import statement
|
|
164
178
|
// left empty.
|
|
165
179
|
root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => {
|
|
180
|
+
if (!AUTHORING_SOURCES.has(path.node.source.value)) return;
|
|
166
181
|
const specs = path.node.specifiers ?? [];
|
|
167
182
|
const kept = specs.filter(
|
|
168
183
|
(/** @type {any} */ spec) =>
|
package/assets/docs/README.md
CHANGED
|
@@ -48,3 +48,12 @@ The material is usually good; the finding is placement, not quality. It goes in
|
|
|
48
48
|
**Fits no row?** It is still not caller-facing. Default it to [Contributing](https://github.com/facebook/astryx/wiki/Contributing), or `CONTRIBUTING.md` when it is a step someone follows with the repo cloned. Never default it back to this directory.
|
|
49
49
|
|
|
50
50
|
Worked example: a responsive-and-interaction readiness rubric is grading criteria → **Component-Audit-Rubric**, or **Component-Lifecycle** if it is a promotion gate.
|
|
51
|
+
|
|
52
|
+
## Sections are read one at a time
|
|
53
|
+
|
|
54
|
+
`astryx docs <topic> --index` lists a topic's sections, and readers then open
|
|
55
|
+
one section by its key. A section's key is its `id`, or a key derived from its
|
|
56
|
+
title when it has none. Give a section an `id` when its title may change, since
|
|
57
|
+
readers and extensions link to the key. Two sections in one topic cannot share
|
|
58
|
+
a key. Keep each section small enough to read on its own: `astryx doctor` fails
|
|
59
|
+
any section over 32 KB.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file `astryx docs authoring`: every authoring schema, one section each.
|
|
5
|
+
*
|
|
6
|
+
* Built from the self-docs colocated under packages/cli/authoring, so it cannot
|
|
7
|
+
* drift from the schemas it describes. `astryx doctor` names a self-doc this
|
|
8
|
+
* topic cannot reach.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {buildAuthoringTopic} from '../../foundation/discovery/authoring-self-docs.mjs';
|
|
12
|
+
|
|
13
|
+
/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
|
|
14
|
+
export const docs = await buildAuthoringTopic();
|
|
@@ -20,7 +20,11 @@ export const docs = {
|
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
type: 'prose',
|
|
23
|
-
text: 'The authoring CLI owns the integration file. The first `astryx integration add` creates `astryx.integration.mjs`; each later add declares its root only after writing a valid contribution behind it. Identity (name and version) still comes from package.json. For the consumer side, run `
|
|
23
|
+
text: 'The authoring CLI owns the integration file. The first `astryx integration add` creates `astryx.integration.mjs`; each later add declares its root only after writing a valid contribution behind it. Identity (name and version) still comes from package.json. For the consumer side, run `astryx docs getting-started`.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'prose',
|
|
27
|
+
text: 'Every file an integration author writes is documented field by field in `npx astryx docs authoring`: the manifest, astryx.config, codemods, identity, and each doc type. `npx astryx docs authoring --index` lists them, and `npx astryx docs authoring <key>` reads one.',
|
|
24
28
|
},
|
|
25
29
|
{
|
|
26
30
|
type: 'prose',
|
|
@@ -48,7 +52,7 @@ export const docs = {
|
|
|
48
52
|
content: [
|
|
49
53
|
{
|
|
50
54
|
type: 'prose',
|
|
51
|
-
text: 'Do not start by hand-editing a manifest. Add the contribution you mean to ship; Astryx creates the manifest, writes every required file, preserves an existing custom root, and updates an existing package.json files allowlist without creating one.',
|
|
55
|
+
text: 'Do not start by hand-editing a manifest. Add the contribution you mean to ship; Astryx creates the manifest, writes every required file, preserves an existing custom root, and updates an existing package.json files allowlist without creating one. Always run these commands from the locally installed CLI in the package (e.g. `node node_modules/@astryxdesign/cli/clients/cli/bin/astryx.mjs` or `pnpm astryx`), not `npx @astryxdesign/cli` — npx may resolve a stale registry version whose integration scaffolding does not match the installed one.',
|
|
52
56
|
},
|
|
53
57
|
{
|
|
54
58
|
type: 'code',
|
|
@@ -80,17 +84,17 @@ export const docs = {
|
|
|
80
84
|
content: [
|
|
81
85
|
{
|
|
82
86
|
type: 'prose',
|
|
83
|
-
text: 'A useful theme package usually ships more than colors. Start with the source theme, then add the guides its consumers need.
|
|
87
|
+
text: 'A useful theme package usually ships more than colors. Start with the source theme, author the palette request at `themes/ocean/palette.config.json`, then add the guides its consumers need. The `integration add` commands keep the package manifest in sync; add palette outputs to the theme catalog after generation.',
|
|
84
88
|
},
|
|
85
89
|
{
|
|
86
90
|
type: 'code',
|
|
87
91
|
lang: 'bash',
|
|
88
92
|
label: 'In the provider package',
|
|
89
|
-
code: 'astryx integration add theme ocean\nastryx theme palette generate palette.config.json --out themes/ocean/tokens/ocean.palette.ts\nastryx integration add doc brand-theme\nastryx integration add doc theme-migration\nastryx theme list --package @acme/brand-integration\nastryx docs brand-theme\nastryx integration pack --check\nnpm pack',
|
|
93
|
+
code: 'astryx integration add theme ocean\nastryx theme palette generate themes/ocean/palette.config.json --out themes/ocean/tokens/ocean.palette.ts\nastryx integration add doc brand-theme\nastryx integration add doc theme-migration\nastryx theme list --package @acme/brand-integration\nastryx docs brand-theme\nastryx integration pack --check\nnpm pack',
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
type: 'prose',
|
|
93
|
-
text:
|
|
97
|
+
text: "Edit the generated theme and guide files before publishing. The shown palette command writes `themes/ocean/tokens/ocean.palette.ts` and its sibling `themes/ocean/tokens/ocean.palette.receipt.json`. The TypeScript candidate directly exports `black`, `white`, and `palette`; import what the theme uses from `./tokens/ocean.palette`. Keep the request at `themes/ocean/palette.config.json`, and list the theme source, request, candidate, and receipt in the catalog entry's `files` array. Add any optional wrapper, refs, icon, or preview modules only when you author them, and list each one too. `integration pack --check` runs the real package lifecycle and compares local discovery with the npm tarball, so a missing source file or files allowlist entry fails before a consumer sees it.",
|
|
94
98
|
},
|
|
95
99
|
{
|
|
96
100
|
type: 'code',
|
|
@@ -104,6 +108,25 @@ export const docs = {
|
|
|
104
108
|
},
|
|
105
109
|
],
|
|
106
110
|
},
|
|
111
|
+
{
|
|
112
|
+
title: 'Contribution Kinds at a Glance',
|
|
113
|
+
category: 'guide',
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'prose',
|
|
117
|
+
text: 'Each contribution kind uses a different metadata suffix, type stamp, and discovery rule. The table below prevents the most common first-time authoring mistake — using the wrong file or export convention.',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'code',
|
|
121
|
+
lang: 'text',
|
|
122
|
+
code: "Kind Metadata suffix type stamp Source file\n──────── ───────────────────────── ───────────── ──────────────────────\nComponent Name.doc.{ts,mjs,js} 'component' Name.tsx (same stem)\nTemplate Name.template.{ts,mjs,js} 'page'/'block' Name.tsx (same stem)\nDoc topic topic.doc.{ts,mjs,js} 'generic' (none — docs are prose)\nCodemod <version>/<id>.{ts,mjs,js} 'code'/'config' (the codemod IS the source)\nTheme manifest.json entry — <slug>/<entry>.ts",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
type: 'prose',
|
|
126
|
+
text: 'The `type` stamp is how new docs should be authored — it routes parsing to the correct schema at the load boundary. Legacy docs without a stamp still load via shape-sniffing for backward compatibility, but unstamped docs rely on heuristics (presence of `props`, `params`, etc.) and may parse under the wrong schema if the shape is ambiguous. Always stamp new integration contributions.',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
107
130
|
{
|
|
108
131
|
title: 'The Integration File',
|
|
109
132
|
category: 'guide',
|
|
@@ -129,7 +152,7 @@ export const docs = {
|
|
|
129
152
|
content: [
|
|
130
153
|
{
|
|
131
154
|
type: 'prose',
|
|
132
|
-
text:
|
|
155
|
+
text: "Export your components from your library however you like, and consumers still import them from your package. For each component the CLI should document, ship a `.doc.{ts,mjs,js}` file with the same stem, for example `AcmeCarousel.tsx` alongside `AcmeCarousel.doc.ts`. The doc file must default-export an object with `type: 'component'` — not `'generic'` (that is for reference docs) and not `'page'`/`'block'` (those are for templates).",
|
|
133
156
|
},
|
|
134
157
|
{
|
|
135
158
|
type: 'prose',
|
|
@@ -138,7 +161,7 @@ export const docs = {
|
|
|
138
161
|
{
|
|
139
162
|
type: 'code',
|
|
140
163
|
lang: 'typescript',
|
|
141
|
-
code: "// AcmeCarousel.doc.ts\nexport default {\n type: 'component',\n name: 'AcmeCarousel',\n description: 'A carousel that cycles through slides.',\n // props, usage, examples, ...\n};",
|
|
164
|
+
code: "// AcmeCarousel.doc.ts\nexport default {\n type: 'component',\n name: 'AcmeCarousel',\n description: 'A carousel that cycles through slides.',\n // props, usage, examples, ...\n} satisfies import('@astryxdesign/cli/authoring').ComponentDoc;",
|
|
142
165
|
},
|
|
143
166
|
],
|
|
144
167
|
},
|
|
@@ -148,7 +171,7 @@ export const docs = {
|
|
|
148
171
|
content: [
|
|
149
172
|
{
|
|
150
173
|
type: 'prose',
|
|
151
|
-
text: "Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template as a plain object
|
|
174
|
+
text: "Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template as a plain object with `type: 'page'` (full pages) or `type: 'block'` (smaller chunks) as its default export in a `.template.{ts,mjs,js}` file next to the source, for example `AcmeLandingPage.tsx` and `AcmeLandingPage.template.ts`. Do not use the `.doc.{ts,mjs,js}` suffix — that is for component docs and reference docs.",
|
|
152
175
|
},
|
|
153
176
|
{
|
|
154
177
|
type: 'prose',
|
|
@@ -161,7 +184,7 @@ export const docs = {
|
|
|
161
184
|
},
|
|
162
185
|
{
|
|
163
186
|
type: 'prose',
|
|
164
|
-
text: 'The CLI needs both files at consume time. `integration add` includes the templates root when package.json already has a files allowlist. It never creates an exports map, because doing that can make previously-open deep imports private; when a map already exists, it adds the generated source subpath without replacing author-owned entries. `integration pack --check` proves the source and metadata survive the tarball and verifies every component through the public import its metadata advertises.',
|
|
187
|
+
text: 'The CLI needs both files at consume time. `integration add` includes the templates root when package.json already has a files allowlist. It never creates an exports map, because doing that can make previously-open deep imports private; when a map already exists, it adds the generated source subpath without replacing author-owned entries. Use consumer-safe extensionless subpaths in the exports map (e.g. `"./templates/AcmeDashboard"` instead of `"./templates/AcmeDashboard.tsx"`), so consumers import without knowing the file extension. `integration pack --check` proves the source and metadata survive the tarball and verifies every component through the public import its metadata advertises.',
|
|
165
188
|
},
|
|
166
189
|
],
|
|
167
190
|
},
|
|
@@ -171,7 +194,7 @@ export const docs = {
|
|
|
171
194
|
content: [
|
|
172
195
|
{
|
|
173
196
|
type: 'prose',
|
|
174
|
-
text: "Point the integration file's `docs` field at a directory of reference docs and every `{topic}.doc.{ts,mjs,js}` under it becomes a topic the CLI serves: `astryx docs` lists it, `astryx docs <topic>` prints it, `astryx search` indexes it, and `astryx init` names it in the agent block. A topic is a plain object
|
|
197
|
+
text: "Point the integration file's `docs` field at a directory of reference docs and every `{topic}.doc.{ts,mjs,js}` under it becomes a topic the CLI serves: `astryx docs` lists it, `astryx docs <topic>` prints it, `astryx search` indexes it, and `astryx init` names it in the agent block. A topic is a plain object with `type: 'generic'` as its default export — not `'component'` (that is for component docs with a same-stem source file). This is the same shape core's own topics use.",
|
|
175
198
|
},
|
|
176
199
|
{
|
|
177
200
|
type: 'code',
|
|
@@ -189,7 +212,7 @@ export const docs = {
|
|
|
189
212
|
},
|
|
190
213
|
{
|
|
191
214
|
type: 'prose',
|
|
192
|
-
text: "`extends: 'x'` merges onto a topic instead of owning it: a section
|
|
215
|
+
text: "`extends: 'x'` merges onto a topic instead of owning it: a section with the same key as one in the base (its `id`, or the key its title derives) or the same title replaces that section, and a section the base does not have is appended. Reach for it to correct or add to a topic you do not want to fork: a fork of someone else's guide stops receiving their fixes the day you write it.",
|
|
193
216
|
},
|
|
194
217
|
{
|
|
195
218
|
type: 'list',
|
|
@@ -215,16 +238,33 @@ export const docs = {
|
|
|
215
238
|
{
|
|
216
239
|
type: 'code',
|
|
217
240
|
lang: 'text',
|
|
218
|
-
code: 'themes/\n manifest.json\n ocean/\n oceanTheme.ts',
|
|
241
|
+
code: 'themes/\n manifest.json\n ocean/\n oceanTheme.ts\n palette.config.json\n tokens/\n ocean.palette.ts\n ocean.palette.receipt.json',
|
|
219
242
|
},
|
|
220
243
|
{
|
|
221
244
|
type: 'prose',
|
|
222
|
-
text:
|
|
245
|
+
text: 'The root catalog `manifest.json` must be `{ "version": 1, "themes": [...] }`. Each entry in the `themes` array requires every field shown below — omitting any one is a hard validation error:',
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'list',
|
|
249
|
+
style: 'unordered',
|
|
250
|
+
items: [
|
|
251
|
+
'`slug` — lowercase kebab-case starting with a letter (e.g. `"ocean"`). Must be unique within the catalog.',
|
|
252
|
+
'`displayName` — human-readable label (e.g. `"Ocean"`).',
|
|
253
|
+
'`description` — string description of the theme.',
|
|
254
|
+
'`maintained` — boolean indicating active maintenance.',
|
|
255
|
+
'`entry` — source file relative to `themes/<slug>/` (e.g. `"oceanTheme.ts"`).',
|
|
256
|
+
'`exportName` — a valid JS identifier naming the runtime export in the entry file (e.g. `"oceanTheme"`). Astryx parses the source without executing it and rejects missing or type-only exports.',
|
|
257
|
+
'`files` — non-empty array of filenames relative to `themes/<slug>/`. Must include the entry file and every local static import the entry source uses. Astryx validates that every listed file exists on disk and that every local import in the entry names a file in this list.',
|
|
258
|
+
],
|
|
223
259
|
},
|
|
224
260
|
{
|
|
225
261
|
type: 'code',
|
|
226
262
|
lang: 'json',
|
|
227
|
-
code: '{\n "version": 1,\n "themes": [{\n "slug": "ocean",\n "displayName": "Ocean",\n "description": "Ocean theme.",\n "maintained": true,\n "entry": "oceanTheme.ts",\n "exportName": "oceanTheme",\n "files": ["oceanTheme.ts"]\n }]\n}',
|
|
263
|
+
code: '{\n "version": 1,\n "themes": [{\n "slug": "ocean",\n "displayName": "Ocean",\n "description": "Ocean theme with OKLCH palettes.",\n "maintained": true,\n "entry": "oceanTheme.ts",\n "exportName": "oceanTheme",\n "files": [\n "oceanTheme.ts",\n "palette.config.json",\n "tokens/ocean.palette.ts",\n "tokens/ocean.palette.receipt.json"\n ]\n }]\n}',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
type: 'prose',
|
|
267
|
+
text: 'The generated candidate is already importable: it exports `black`, `white`, `palette`, and a default palette value. Import it directly from `./tokens/ocean.palette`. A wrapper or palette-refs module is optional application code, not generator output; list it only if you create it.',
|
|
228
268
|
},
|
|
229
269
|
{
|
|
230
270
|
type: 'prose',
|
|
@@ -267,10 +307,41 @@ export const docs = {
|
|
|
267
307
|
type: 'prose',
|
|
268
308
|
text: "Ship codemods so `astryx upgrade` can migrate consumers across breaking changes in your package. Point the integration file's `codemods` field at your codemods root, and author each one as a plain object stamped with `type: 'code'` (transforms source files) or `type: 'config'` (rewrites the consumer's `astryx.config`).",
|
|
269
309
|
},
|
|
310
|
+
{
|
|
311
|
+
type: 'prose',
|
|
312
|
+
text: 'The codemods root uses a version-folder-first layout. Each folder name is an exact semver string (no `v` prefix) matching the version the codemod migrates TO. Each module under it is a kebab-case `.ts`, `.mjs`, or `.js` file whose default export is the codemod envelope:',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
type: 'code',
|
|
316
|
+
lang: 'text',
|
|
317
|
+
code: 'codemods/\n 0.2.0/\n rename-widget-prop.ts\n 0.3.0/\n update-theme-import.ts\n config/rename-integration.ts',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
type: 'prose',
|
|
321
|
+
text: 'Codemod ids (the extension-less relative path under the version folder, e.g. `rename-widget-prop`, `config/rename-integration`) must be unique within a package across all versions. A duplicate id across versions is a hard error.',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
type: 'prose',
|
|
325
|
+
text: 'The loader automatically skips test and fixture files so you can colocate tests with transforms. Reserved names: files matching `*.test.*`, `*.spec.*`, or `*.fixture.*`, and any file under a `__tests__/` or `__fixtures__/` directory. These are never loaded as codemods regardless of their extension.',
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
type: 'code',
|
|
329
|
+
lang: 'text',
|
|
330
|
+
code: 'codemods/\n 0.2.0/\n rename-widget-prop.ts # loaded as a codemod\n rename-widget-prop.test.ts # skipped (reserved name)\n __tests__/\n rename-widget-prop.test.ts # skipped (reserved directory)',
|
|
331
|
+
},
|
|
270
332
|
{
|
|
271
333
|
type: 'code',
|
|
272
334
|
lang: 'typescript',
|
|
273
|
-
code: "// codemods/
|
|
335
|
+
code: "// codemods/0.2.0/rename-widget-prop.ts\nexport default {\n type: 'code',\n title: 'Rename AcmeWidget oldProp to newProp',\n description: 'Updates JSX props in consumer source files.',\n transform(file, api) {\n // jscodeshift transform\n return file.source;\n },\n};",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
type: 'prose',
|
|
339
|
+
text: "`astryx upgrade` is dry-run by default — it previews which codemods would run and what files would change, without writing anything. Pass `--apply` to write the changes. There is no `--dry-run` flag; omitting `--apply` is the dry run. The `--integration` flag resolves each value beneath the project's `node_modules` (for example, `--integration @acme/widgets`). Absolute paths and `.` or `..` segments are rejected; other slash-separated values remain beneath `node_modules`.",
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
type: 'code',
|
|
343
|
+
lang: 'bash',
|
|
344
|
+
code: '# Preview what would change (dry-run, the default)\nastryx upgrade --from 0.1.0\n\n# Apply the migration\nastryx upgrade --from 0.1.0 --apply',
|
|
274
345
|
},
|
|
275
346
|
{
|
|
276
347
|
type: 'prose',
|
|
@@ -28,5 +28,27 @@ export type MutuallyAssignable<A, B> = [A] extends [B]
|
|
|
28
28
|
: false
|
|
29
29
|
: false;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* `T` without string or number index signatures, at every depth. A
|
|
33
|
+
* `.passthrough()` schema infers one and a hand-written interface never has
|
|
34
|
+
* one, so a lock drops them before comparing named fields. Recursive fields
|
|
35
|
+
* that a schema casts to their public type compare equal by construction.
|
|
36
|
+
*/
|
|
37
|
+
export type NamedFields<T> = T extends readonly (infer U)[]
|
|
38
|
+
? NamedFields<U>[]
|
|
39
|
+
: T extends (...args: never[]) => unknown
|
|
40
|
+
? T
|
|
41
|
+
: T extends object
|
|
42
|
+
? {
|
|
43
|
+
[
|
|
44
|
+
K in keyof T as string extends K
|
|
45
|
+
? never
|
|
46
|
+
: number extends K
|
|
47
|
+
? never
|
|
48
|
+
: K
|
|
49
|
+
]: NamedFields<T[K]>;
|
|
50
|
+
}
|
|
51
|
+
: T;
|
|
52
|
+
|
|
31
53
|
/** Compiles only when `T` is exactly `true`; otherwise a type error. */
|
|
32
54
|
export type Expect<T extends true> = T;
|
|
@@ -63,11 +63,13 @@ export const doc = {
|
|
|
63
63
|
name: 'file.path',
|
|
64
64
|
type: 'string',
|
|
65
65
|
description: 'Absolute path to the file being transformed.',
|
|
66
|
+
required: true,
|
|
66
67
|
},
|
|
67
68
|
{
|
|
68
69
|
name: 'file.source',
|
|
69
70
|
type: 'string',
|
|
70
71
|
description: 'The current source contents of the file.',
|
|
72
|
+
required: true,
|
|
71
73
|
},
|
|
72
74
|
],
|
|
73
75
|
},
|
|
@@ -81,18 +83,21 @@ export const doc = {
|
|
|
81
83
|
type: 'unknown',
|
|
82
84
|
description:
|
|
83
85
|
'A jscodeshift instance configured with a parser for the file.',
|
|
86
|
+
required: true,
|
|
84
87
|
},
|
|
85
88
|
{
|
|
86
89
|
name: 'api.stats',
|
|
87
90
|
type: '(...args: unknown[]) => void',
|
|
88
91
|
description:
|
|
89
92
|
'Report a statistic (no-op-friendly; provided for jscodeshift parity).',
|
|
93
|
+
required: true,
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
name: 'api.report',
|
|
93
97
|
type: '(...args: unknown[]) => void',
|
|
94
98
|
description:
|
|
95
99
|
'Report progress (no-op-friendly; provided for jscodeshift parity).',
|
|
100
|
+
required: true,
|
|
96
101
|
},
|
|
97
102
|
],
|
|
98
103
|
},
|
|
@@ -100,7 +105,7 @@ export const doc = {
|
|
|
100
105
|
},
|
|
101
106
|
{
|
|
102
107
|
name: 'type',
|
|
103
|
-
type: "'code'",
|
|
108
|
+
type: "'code' | 'config'",
|
|
104
109
|
description:
|
|
105
110
|
"Discriminant for the file-transforming variant. Use 'config' for a " +
|
|
106
111
|
'codemod that rewrites astryx.config.* instead (see notes).',
|