@astryxdesign/cli 0.6.4 → 0.6.5-canary.031021b
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/CHANGELOG.md +56 -0
- package/README.md +97 -90
- package/api/build/build.doc.mjs +6 -1
- package/api/build/build.test.mjs +22 -0
- package/api/build/kit/kit.mjs +44 -5
- package/api/component/_adapter.d.mts +25 -0
- package/api/component/_adapter.mjs +59 -5
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +37 -17
- package/api/component/component.mjs +249 -9
- package/api/component/component.type.d.mts +25 -0
- package/api/component/component.type.mjs +44 -0
- package/api/discover/_adapter.d.mts +114 -6
- package/api/discover/_adapter.mjs +372 -17
- package/api/discover/_adapter.test.mjs +215 -0
- package/api/discover/_catalog-view.d.mts +115 -0
- package/api/discover/_catalog-view.mjs +203 -0
- package/api/discover/_catalog-view.test.mjs +128 -0
- package/api/discover/detail/detail.d.mts +18 -6
- package/api/discover/detail/detail.mjs +67 -13
- package/api/discover/detail/detail.test.mjs +85 -0
- package/api/discover/detail/item/item.d.mts +26 -0
- package/api/discover/detail/item/item.mjs +78 -0
- package/api/discover/detail/item/item.test.mjs +73 -0
- package/api/discover/discover.d.mts +3 -9
- package/api/discover/discover.doc.mjs +61 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +147 -8
- package/api/discover/discover.type.mjs +102 -12
- package/api/discover/list/list.d.mts +20 -6
- package/api/discover/list/list.mjs +45 -12
- package/api/discover/list/list.test.mjs +46 -0
- package/api/discover/search/search.d.mts +18 -16
- package/api/discover/search/search.mjs +102 -56
- package/api/discover/search/search.test.mjs +144 -10
- package/api/docs/_adapter.d.mts +8 -3
- package/api/docs/_adapter.mjs +14 -6
- package/api/docs/docOverlays.test.mjs +27 -1
- package/api/docs/docs.doc.mjs +2 -2
- package/api/doctor/doctor.d.mts +8 -3
- package/api/doctor/doctor.doc.mjs +17 -8
- package/api/doctor/doctor.mjs +90 -9
- package/api/doctor/doctor.test.mjs +122 -10
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/gap-report/gap-report.doc.mjs +19 -10
- package/api/hook/hook.doc.mjs +6 -3
- package/api/index.d.mts +1 -0
- package/api/index.mjs +5 -3
- package/api/init/init.doc.mjs +17 -12
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +22 -1
- package/api/integration/add-theme.test.mjs +34 -0
- package/api/integration/authoring-checks.mjs +2 -2
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
- package/api/integration/pack-check.mjs +82 -9
- package/api/integration/pack-check.test.mjs +90 -0
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/isError.doc.mjs +1 -1
- package/api/search/search.d.mts +27 -1
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +228 -16
- package/api/swizzle/swizzle.doc.mjs +7 -5
- package/api/template/copy/copy.mjs +1 -1
- package/api/template/copy/copy.test.mjs +9 -0
- package/api/template/template.doc.mjs +2 -1
- package/api/theme/add/add.mjs +17 -25
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +40 -23
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +8 -18
- package/api/theme/build/build.rollback.test.mjs +148 -0
- package/api/theme/generateTonalPalette.doc.mjs +1 -2
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +9 -10
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +2 -1
- package/api/theme/themePaletteGenerate.doc.mjs +15 -8
- package/api/theme/themeTargets.doc.mjs +3 -2
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/run/files-changed.test.mjs +111 -0
- package/api/upgrade/run/run.mjs +5 -3
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +3 -1
- package/assets/codemods/file-count.test.mjs +163 -0
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +5 -4
- package/assets/docs/README.md +4 -2
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +8 -2
- package/assets/docs/elevation.doc.mjs +6 -4
- package/assets/docs/getting-started.doc.mjs +5 -16
- package/assets/docs/icons.doc.mjs +2 -21
- package/assets/docs/illustrations.doc.mjs +7 -15
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +130 -82
- package/assets/docs/layout.doc.mjs +133 -77
- package/assets/docs/migration.doc.mjs +19 -21
- package/assets/docs/motion.doc.mjs +16 -3
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +8 -0
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +8 -3
- package/assets/docs/spacing.doc.mjs +7 -2
- package/assets/docs/styling-libraries.doc.mjs +6 -2
- package/assets/docs/styling.doc.mjs +19 -23
- package/assets/docs/theme.doc.dense.mjs +58 -18
- package/assets/docs/theme.doc.mjs +57 -47
- package/assets/docs/theme.doc.zh.mjs +9 -8
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +389 -8
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/add-a-component.doc.mjs +75 -0
- package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
- package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
- package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
- package/assets/docs/tree/block-template.doc.mjs +130 -0
- package/assets/docs/tree/build-the-template.doc.mjs +28 -0
- package/assets/docs/tree/building-blocks.doc.mjs +46 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
- package/assets/docs/tree/checks.doc.mjs +119 -0
- package/assets/docs/tree/codemods.doc.mjs +147 -0
- package/assets/docs/tree/component-family.doc.mjs +113 -0
- package/assets/docs/tree/component-imports.doc.mjs +69 -0
- package/assets/docs/tree/component-lookups.doc.mjs +149 -0
- package/assets/docs/tree/components.doc.mjs +23 -0
- package/assets/docs/tree/configuration.doc.mjs +23 -0
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
- package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
- package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
- package/assets/docs/tree/docs.doc.mjs +21 -0
- package/assets/docs/tree/document-the-template.doc.mjs +28 -0
- package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
- package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
- package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
- package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
- package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
- package/assets/docs/tree/help.doc.mjs +16 -0
- package/assets/docs/tree/integrations.doc.mjs +25 -451
- package/assets/docs/tree/links.doc.mjs +98 -0
- package/assets/docs/tree/package-and-test.doc.mjs +32 -0
- package/assets/docs/tree/page-template.doc.mjs +71 -0
- package/assets/docs/tree/publishing.doc.mjs +111 -0
- package/assets/docs/tree/quick-start.doc.mjs +272 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
- package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
- package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
- package/assets/docs/tree/ship.doc.mjs +16 -0
- package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
- package/assets/docs/tree/single-component.doc.mjs +165 -0
- package/assets/docs/tree/start-a-template.doc.mjs +143 -0
- package/assets/docs/tree/subcomponent.doc.mjs +115 -0
- package/assets/docs/tree/template-assets.doc.mjs +64 -0
- package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
- package/assets/docs/tree/template-fonts.doc.mjs +102 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
- package/assets/docs/tree/template-icons.doc.mjs +97 -0
- package/assets/docs/tree/template-images-media.doc.mjs +127 -0
- package/assets/docs/tree/template-styles.doc.mjs +93 -0
- package/assets/docs/tree/templates.doc.mjs +34 -0
- package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
- package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
- package/assets/docs/tree/themes.doc.mjs +39 -0
- package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
- package/assets/docs/tree/upgrading.doc.mjs +103 -0
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
- package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
- package/assets/docs/tree/versioning.doc.mjs +161 -0
- package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
- package/assets/docs/typography.doc.mjs +24 -4
- package/assets/docs/working-with-ai.doc.mjs +30 -22
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +9 -1
- package/authoring/config/parse.d.mts +2 -0
- package/authoring/config/parse.mjs +19 -0
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +11 -0
- package/authoring/discover/discover.doc.d.mts +13 -0
- package/authoring/discover/discover.doc.mjs +138 -0
- package/authoring/discover/parse.d.mts +24 -0
- package/authoring/discover/parse.mjs +128 -0
- package/authoring/discover/parse.test.mjs +124 -0
- package/authoring/discover/type.ts +87 -0
- package/authoring/doctypes/_schema.d.mts +3 -2
- package/authoring/doctypes/_schema.mjs +6 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +4 -2
- package/authoring/doctypes/component/component.doc.mjs +6 -0
- package/authoring/doctypes/component/type.ts +8 -0
- package/authoring/doctypes/reference/reference.doc.mjs +7 -0
- package/authoring/doctypes/reference/type.ts +5 -0
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/template/template.doc.mjs +1 -1
- package/authoring/doctypes/template/type.ts +2 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +10 -0
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +12 -10
- package/clients/cli/commands/component/index.mjs +152 -55
- package/clients/cli/commands/component-batch.test.mjs +341 -0
- package/clients/cli/commands/component-ownership.test.mjs +89 -0
- package/clients/cli/commands/component.doc.mjs +27 -9
- package/clients/cli/commands/discover.doc.mjs +53 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +60 -17
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
- package/clients/cli/commands/doctor-integration.test.mjs +53 -0
- package/clients/cli/commands/doctor.doc.mjs +3 -1
- package/clients/cli/commands/doctor.mjs +49 -5
- package/clients/cli/commands/gap-report.doc.mjs +10 -9
- package/clients/cli/commands/init.doc.mjs +9 -6
- package/clients/cli/commands/integration-add.doc.mjs +9 -9
- package/clients/cli/commands/integration-authoring.test.mjs +61 -10
- package/clients/cli/commands/integration-pack.doc.mjs +5 -9
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration-verify.doc.mjs +22 -0
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +74 -43
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +10 -3
- package/clients/cli/commands/search.mjs +21 -2
- package/clients/cli/commands/search.test.mjs +21 -4
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/text-json-parity.test.mjs +7 -1
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
- package/clients/cli/commands/theme-palette.doc.mjs +1 -2
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +2 -1
- package/clients/cli/commands/upgrade.doc.mjs +62 -3
- package/clients/cli/index.mjs +28 -6
- package/clients/cli/lib/define-command.mjs +28 -4
- package/clients/cli/lib/define-command.test.mjs +54 -0
- package/clients/cli/lib/exit-codes.test.mjs +17 -1
- package/clients/cli/lib/json-shim.mjs +24 -14
- package/clients/cli/lib/manifest.mjs +18 -5
- package/clients/cli/lib/manifest.test.mjs +5 -2
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
- package/foundation/discovery/cli-self-docs.mjs +16 -2
- package/foundation/discovery/cli-self-docs.test.mjs +20 -0
- package/foundation/discovery/docs-discovery.mjs +5 -1
- package/foundation/discovery/docs-discovery.test.mjs +21 -0
- package/foundation/discovery/docs-section-key.d.mts +1 -1
- package/foundation/discovery/docs-section-key.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +3 -2
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/tree.d.mts +4 -0
- package/foundation/doc-compiler/tree.mjs +6 -1
- package/foundation/integrations/cli-requirement.d.mts +26 -6
- package/foundation/integrations/cli-requirement.mjs +46 -11
- package/foundation/integrations/cli-requirement.test.mjs +7 -2
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/integrations/integrations.d.mts +14 -1
- package/foundation/integrations/integrations.mjs +41 -1
- package/foundation/integrations/integrations.test.mjs +31 -0
- package/foundation/response/batch.type.d.mts +33 -0
- package/foundation/response/batch.type.mjs +34 -0
- package/foundation/response/error-codes.doc.mjs +6 -8
- package/foundation/response/error-codes.test.mjs +30 -5
- package/foundation/response/response-types.doc.d.mts +4 -3
- package/foundation/response/response-types.doc.mjs +40 -10
- package/foundation/response/response-types.doc.test.mjs +23 -0
- package/foundation/response/response.doc.mjs +11 -10
- package/package.json +9 -9
- package/api/docs/docs.test.mjs +0 -243
- package/api/docs/integration-tree.test.mjs +0 -555
- package/api/docs/integrationDocs.test.mjs +0 -314
- package/api/search/search.test.mjs +0 -512
- package/assets/docs/tree/integrations.test.mjs +0 -62
- package/assets/docs/tree/writing-docs.doc.mjs +0 -286
- package/clients/cli/commands/docs.test.mjs +0 -294
- package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
- package/foundation/doc-compiler/tree.test.mjs +0 -598
|
@@ -27,30 +27,34 @@ export const doc = {
|
|
|
27
27
|
{
|
|
28
28
|
name: 'configPath',
|
|
29
29
|
type: 'string',
|
|
30
|
-
description:
|
|
30
|
+
description:
|
|
31
|
+
'Path to a JSON file holding a TonalPaletteGenerationInput (the object generateTonalPalette() takes), resolved within cwd.',
|
|
31
32
|
required: true,
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
35
|
name: 'options.out',
|
|
35
36
|
type: 'string',
|
|
36
37
|
description:
|
|
37
|
-
'
|
|
38
|
+
'Where to write the candidate: a path ending in .ts (a TypeScript module) or .json. A sibling <name>.receipt.json is written next to it.',
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
name: 'options.preview',
|
|
41
42
|
type: 'string',
|
|
42
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
'Optional path, ending in .html, for a self-contained HTML review page.',
|
|
43
45
|
},
|
|
44
46
|
{
|
|
45
47
|
name: 'options.overwrite',
|
|
46
48
|
type: 'boolean',
|
|
47
|
-
description:
|
|
49
|
+
description:
|
|
50
|
+
"Replace existing candidate, receipt and preview files. Without it, if any target exists, nothing is written and the result has written: false, reason: 'exists'.",
|
|
48
51
|
default: 'false',
|
|
49
52
|
},
|
|
50
53
|
{
|
|
51
54
|
name: 'ctx.cwd',
|
|
52
55
|
type: 'string',
|
|
53
56
|
description: 'Directory used to resolve the input and output paths.',
|
|
57
|
+
default: 'process.cwd()',
|
|
54
58
|
},
|
|
55
59
|
],
|
|
56
60
|
returns: [
|
|
@@ -64,17 +68,20 @@ export const doc = {
|
|
|
64
68
|
{code: 'ERR_FILE_NOT_FOUND', when: 'the config file does not exist'},
|
|
65
69
|
{
|
|
66
70
|
code: 'ERR_PALETTE_GENERATION',
|
|
67
|
-
when: 'the request, seed, stop layout, mode
|
|
71
|
+
when: 'the config is not valid JSON; the request, seed, stop layout, mode or anchor is invalid; out does not end in .ts or .json; or preview does not end in .html',
|
|
68
72
|
},
|
|
69
73
|
{
|
|
70
74
|
code: 'ERR_PATH_TRAVERSAL',
|
|
71
75
|
when: 'an input or output path escapes cwd, or output would replace input',
|
|
72
76
|
},
|
|
73
|
-
{
|
|
77
|
+
{
|
|
78
|
+
code: 'ERR_WRITE_FAILED',
|
|
79
|
+
when: 'the candidate, receipt or preview file cannot be written',
|
|
80
|
+
},
|
|
74
81
|
],
|
|
75
82
|
examples: [
|
|
76
83
|
{
|
|
77
|
-
label: '
|
|
84
|
+
label: 'Generate a candidate without writing files',
|
|
78
85
|
code: "themePaletteGenerate('palette.config.json');",
|
|
79
86
|
},
|
|
80
87
|
{
|
|
@@ -83,5 +90,5 @@ export const doc = {
|
|
|
83
90
|
},
|
|
84
91
|
],
|
|
85
92
|
command: 'theme palette generate',
|
|
86
|
-
related: ['themeBuild', 'themeTemplate'],
|
|
93
|
+
related: ['generateTonalPalette', 'themeBuild', 'themeTemplate'],
|
|
87
94
|
};
|
|
@@ -22,7 +22,7 @@ export const doc = {
|
|
|
22
22
|
'Same source as the Theming table `astryx component <Name>` prints ' +
|
|
23
23
|
'(the component docs), so the list cannot drift from the components, and `theme build` ' +
|
|
24
24
|
'validates overrides against this exact set. A filter naming a component gives that ' +
|
|
25
|
-
"component's set; anything else is a substring search over
|
|
25
|
+
"component's set; anything else is a case-insensitive substring search over each target's key, class and component.",
|
|
26
26
|
importPath: '@astryxdesign/cli/api',
|
|
27
27
|
signature:
|
|
28
28
|
'themeTargets(filter?: string, ctx?: {cwd?: string}): Promise<ThemeTargetsResponse>',
|
|
@@ -41,13 +41,14 @@ export const doc = {
|
|
|
41
41
|
name: 'filter',
|
|
42
42
|
type: 'string',
|
|
43
43
|
description:
|
|
44
|
-
'A component name (exact, case-insensitive) or a substring of a target key. Omit for the whole surface.',
|
|
44
|
+
'A component name (exact, case-insensitive), or a substring of a target key, class or component. Omit for the whole surface.',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: 'ctx.cwd',
|
|
48
48
|
type: 'string',
|
|
49
49
|
description:
|
|
50
50
|
"Directory the project's @astryxdesign/core is resolved from.",
|
|
51
|
+
default: 'process.cwd()',
|
|
51
52
|
},
|
|
52
53
|
],
|
|
53
54
|
returns: [
|
|
@@ -43,6 +43,7 @@ export const doc = {
|
|
|
43
43
|
name: 'options.cwd',
|
|
44
44
|
type: 'string',
|
|
45
45
|
description: 'Directory the target path resolves against.',
|
|
46
|
+
default: 'process.cwd()',
|
|
46
47
|
},
|
|
47
48
|
],
|
|
48
49
|
returns: [
|
|
@@ -64,5 +65,5 @@ export const doc = {
|
|
|
64
65
|
},
|
|
65
66
|
],
|
|
66
67
|
command: 'theme template',
|
|
67
|
-
related: ['themeAdd', 'themeBuild', '
|
|
68
|
+
related: ['themeAdd', 'themeBuild', 'themeListAvailable', 'themeTargets'],
|
|
68
69
|
};
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
});
|
package/api/upgrade/run/run.mjs
CHANGED
|
@@ -437,9 +437,11 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
|
|
|
437
437
|
|
|
438
438
|
const registryResult = await reconcileCompositions();
|
|
439
439
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
(
|
|
440
|
+
// A file a core codemod AND an integration codemod both changed is one file.
|
|
441
|
+
const mergedFilesChanged = new Set([
|
|
442
|
+
...(coreResult?.changedFiles ?? []),
|
|
443
|
+
...(integrationResult?.changedFiles ?? []),
|
|
444
|
+
]).size;
|
|
443
445
|
const mergedTransformsApplied =
|
|
444
446
|
(coreResult?.totalTransformsApplied ?? 0) +
|
|
445
447
|
(integrationResult?.totalTransformsApplied ?? 0);
|
|
@@ -13,19 +13,16 @@ export const doc = {
|
|
|
13
13
|
name: 'upgrade',
|
|
14
14
|
namespace: 'cli/api',
|
|
15
15
|
displayName: 'upgrade()',
|
|
16
|
-
summary:
|
|
16
|
+
summary:
|
|
17
|
+
'After bumping @astryxdesign/core, migrate project source with codemods and update copied compositions.',
|
|
17
18
|
description:
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'the config is loaded so a config codemod can repair an otherwise-invalid ' +
|
|
26
|
-
'astryx.config. Copied compositions carry adjacent receipts with exact canonical and format-specific install bases; upgrade ' +
|
|
27
|
-
'compares those installed bases with the matching registry release, updates pristine ' +
|
|
28
|
-
'files, merges non-overlapping edits, and leaves conflicting originals untouched.',
|
|
19
|
+
'Runs the codemods between `from` and the installed Core version, then refreshes the ' +
|
|
20
|
+
'managed agent-docs block. Dry-run by default; `apply` writes changes only after the ' +
|
|
21
|
+
'selected codemods and hooks succeed. Config codemods run before astryx.config is ' +
|
|
22
|
+
'loaded, so they can repair an invalid config. Also updates copied compositions from ' +
|
|
23
|
+
'their install receipts: unchanged files are updated, non-overlapping edits are merged, ' +
|
|
24
|
+
'and conflicts are left untouched. `list` only lists codemods; `registry` only updates ' +
|
|
25
|
+
'copied compositions.',
|
|
29
26
|
importPath: '@astryxdesign/cli/api',
|
|
30
27
|
signature:
|
|
31
28
|
'upgrade(options?: UpgradeOptions, ctx?: {cwd?: string}): Promise<UpgradeListResponse | UpgradeRegistryResponse | UpgradeStatusResponse | UpgradeRunResponse>',
|
|
@@ -42,7 +39,7 @@ export const doc = {
|
|
|
42
39
|
name: 'options.from',
|
|
43
40
|
type: 'string',
|
|
44
41
|
description:
|
|
45
|
-
'Version before the dependency bump. Required unless `list` or `registry` is set.',
|
|
42
|
+
'Version before the dependency bump; the target is the installed @astryxdesign/core (or legacy @xds/core). Required unless `list` or `registry` is set.',
|
|
46
43
|
},
|
|
47
44
|
{
|
|
48
45
|
name: 'options.apply',
|
|
@@ -55,11 +52,13 @@ export const doc = {
|
|
|
55
52
|
type: 'boolean',
|
|
56
53
|
description:
|
|
57
54
|
'Run codemods even when `from` is at/after the installed version.',
|
|
55
|
+
default: 'false',
|
|
58
56
|
},
|
|
59
57
|
{
|
|
60
58
|
name: 'options.codemod',
|
|
61
59
|
type: 'string',
|
|
62
|
-
description:
|
|
60
|
+
description:
|
|
61
|
+
'Run only this codemod. Optional codemods run only when named here. Setting it also skips copied-composition reconciliation.',
|
|
63
62
|
},
|
|
64
63
|
{
|
|
65
64
|
name: 'options.skipCodemod',
|
|
@@ -83,23 +82,26 @@ export const doc = {
|
|
|
83
82
|
type: 'boolean',
|
|
84
83
|
description:
|
|
85
84
|
'Install jscodeshift when it is missing; otherwise a missing jscodeshift throws ERR_DEP_MISSING.',
|
|
85
|
+
default: 'false',
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
name: 'options.registry',
|
|
89
89
|
type: 'boolean',
|
|
90
90
|
description:
|
|
91
|
-
'
|
|
91
|
+
'Only reconcile copied compositions from their install receipts; `from` is not required. Cannot be combined with `list`, `from`, `force`, `codemod`, `skipCodemod`, `integration` or `installDeps`.',
|
|
92
92
|
default: 'false',
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
name: 'options.list',
|
|
96
96
|
type: 'boolean',
|
|
97
97
|
description: 'Return the available codemods instead of running any.',
|
|
98
|
+
default: 'false',
|
|
98
99
|
},
|
|
99
100
|
{
|
|
100
101
|
name: 'ctx.cwd',
|
|
101
102
|
type: 'string',
|
|
102
103
|
description: 'Directory to run the upgrade in.',
|
|
104
|
+
default: 'process.cwd()',
|
|
103
105
|
},
|
|
104
106
|
],
|
|
105
107
|
returns: [
|
|
@@ -116,36 +118,36 @@ export const doc = {
|
|
|
116
118
|
{
|
|
117
119
|
type: 'upgrade.status',
|
|
118
120
|
description:
|
|
119
|
-
'A short-circuit outcome (no codemods executed): `up_to_date` (`from` is at/after the installed target and no `force`), `no_codemods` (none apply to the range), or `config_fixable` (dry-run preview that a pending config codemod would repair an invalid astryx.config).
|
|
121
|
+
'A short-circuit outcome (no codemods executed): `up_to_date` (`from` is at/after the installed target and no `force`), `no_codemods` (none apply to the range), or `config_fixable` (dry-run preview that a pending config codemod would repair an invalid astryx.config). up_to_date and no_codemods carry the agent-docs summary and, when receipts are found, the copied-composition summary; config_fixable carries configError, configCodemods, suggestedCommand, message, note, and the agent-docs summary.',
|
|
120
122
|
},
|
|
121
123
|
{
|
|
122
124
|
type: 'upgrade.run',
|
|
123
125
|
description:
|
|
124
|
-
'The terminal run receipt: from
|
|
126
|
+
'The terminal run receipt: from, to, codemods (count), integrations, agentDocs, agentDocsRefreshed, registryCompositions (when receipts are found), filesChanged, transformsApplied, modifiedFiles, protectedFiles, declinedCandidates, errors, and complete. When a protected file still requires a change, complete is false and errorCode is ERR_CODEMOD_PROTECTED; the CLI exits nonzero while preserving the structured receipt.',
|
|
125
127
|
},
|
|
126
128
|
],
|
|
127
129
|
throws: [
|
|
128
130
|
{
|
|
129
131
|
code: 'ERR_INVALID_ARGUMENT',
|
|
130
|
-
when: '`from` is missing (and neither `list` nor `registry` is set)
|
|
132
|
+
when: '`from` is missing (and neither `list` nor `registry` is set); `list` and `registry` are both set; `registry` is combined with `from`, `force`, `codemod`, `skipCodemod`, `integration` or `installDeps`; an `integration` specifier is invalid or not installed; or astryx.config fails to load or validate and no pending config codemod repairs it',
|
|
131
133
|
},
|
|
132
134
|
{code: 'ERR_INVALID_VERSION', when: '`from` is not a valid semver string'},
|
|
133
135
|
{code: 'ERR_PATH_TRAVERSAL', when: '`path` resolves outside cwd'},
|
|
134
136
|
{
|
|
135
137
|
code: 'ERR_VERSION_DETECT',
|
|
136
|
-
when: '
|
|
138
|
+
when: 'neither @astryxdesign/core nor legacy @xds/core is installed in cwd; with `registry`, only when copied-composition receipts exist and @astryxdesign/core is not installed',
|
|
137
139
|
},
|
|
138
140
|
{
|
|
139
141
|
code: 'ERR_DEP_MISSING',
|
|
140
|
-
when: 'jscodeshift is
|
|
142
|
+
when: 'jscodeshift is missing and `installDeps` is not set, or installing it failed',
|
|
141
143
|
},
|
|
142
144
|
{
|
|
143
145
|
code: 'ERR_UNKNOWN_CODEMOD',
|
|
144
|
-
when: '
|
|
146
|
+
when: 'the version range has codemods but none remain selected: `codemod` names no codemod in the range, or `skipCodemod` excludes all of them',
|
|
145
147
|
},
|
|
146
148
|
{
|
|
147
149
|
code: 'ERR_CODEMOD_FAILED',
|
|
148
|
-
when: 'one or more codemods failed, or a post-codemod hook failed',
|
|
150
|
+
when: 'one or more codemods failed, or a post-codemod hook failed, and no protected file still needs a change (otherwise an upgrade.run receipt with complete: false is returned)',
|
|
149
151
|
},
|
|
150
152
|
{
|
|
151
153
|
code: 'ERR_CODEMOD_PROTECTION_SOURCE',
|
|
@@ -119,11 +119,11 @@
|
|
|
119
119
|
* @property {RegistryCompositionSummary} [data.registryCompositions]
|
|
120
120
|
* @property {boolean} [data.complete] False when protected required changes remain.
|
|
121
121
|
* @property {'ERR_CODEMOD_PROTECTED'} [data.errorCode] Stable incomplete-result code when complete is false.
|
|
122
|
-
* @property {number} [data.filesChanged]
|
|
122
|
+
* @property {number} [data.filesChanged] Distinct files changed across core + integration codemods (apply mode). One file that four codemods each changed counts once.
|
|
123
123
|
* @property {string[]} [data.modifiedFiles] Project-relative files changed or previewed.
|
|
124
124
|
* @property {ProtectedCodemodFile[]} [data.protectedFiles] Protected files that still require a codemod change after regeneration.
|
|
125
125
|
* @property {Array<{file: string, location?: string, reason: string}>} [data.declinedCandidates] Candidates left unchanged because proof was insufficient.
|
|
126
|
-
* @property {number} [data.transformsApplied] Total
|
|
126
|
+
* @property {number} [data.transformsApplied] Total codemod changes. A code or config codemod counts once for each file it changed, so one file changed by four of them counts four times; a project codemod counts once, however many files it writes.
|
|
127
127
|
* @property {Array<{file: string, codemod: string, error: string}>} [data.errors] Per-codemod errors, when any codemod failed.
|
|
128
128
|
*/
|
|
129
129
|
|
|
@@ -46,7 +46,9 @@ describe('runCodemods — ordered dry-run state', () => {
|
|
|
46
46
|
silent: true,
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
// One file that two transforms changed is one file and two changes.
|
|
50
|
+
expect(preview.totalFilesChanged).toBe(1);
|
|
51
|
+
expect(preview.totalTransformsApplied).toBe(2);
|
|
50
52
|
expect(preview.changedFiles).toEqual([
|
|
51
53
|
path.join(srcDir, 'a.ts'),
|
|
52
54
|
path.join(srcDir, 'a.ts'),
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
});
|
|
@@ -67,7 +67,6 @@ export function runIntegrationCodemods(
|
|
|
67
67
|
/** In-memory pipeline state keeps ordered dry-runs equivalent to apply. */
|
|
68
68
|
const virtualContents = new Map(providedContents ?? []);
|
|
69
69
|
|
|
70
|
-
let totalFilesChanged = 0;
|
|
71
70
|
let totalTransformsApplied = 0;
|
|
72
71
|
/** @type {string[]} */
|
|
73
72
|
const changedFiles = [];
|
|
@@ -115,7 +114,6 @@ export function runIntegrationCodemods(
|
|
|
115
114
|
protection,
|
|
116
115
|
contents: virtualContents,
|
|
117
116
|
});
|
|
118
|
-
totalFilesChanged += r.filesChanged;
|
|
119
117
|
totalTransformsApplied += r.filesChanged;
|
|
120
118
|
changedFiles.push(...r.changedFiles);
|
|
121
119
|
writtenFiles.push(...r.writtenFiles);
|
|
@@ -145,7 +143,6 @@ export function runIntegrationCodemods(
|
|
|
145
143
|
protection,
|
|
146
144
|
contents: virtualContents,
|
|
147
145
|
});
|
|
148
|
-
totalFilesChanged += r.filesChanged;
|
|
149
146
|
totalTransformsApplied += r.filesChanged;
|
|
150
147
|
changedFiles.push(...r.changedFiles);
|
|
151
148
|
writtenFiles.push(...r.writtenFiles);
|
|
@@ -154,6 +151,9 @@ export function runIntegrationCodemods(
|
|
|
154
151
|
}
|
|
155
152
|
}
|
|
156
153
|
|
|
154
|
+
// A file several codemods changed is one file; transforms count each change.
|
|
155
|
+
const totalFilesChanged = new Set(changedFiles).size;
|
|
156
|
+
|
|
157
157
|
return {
|
|
158
158
|
totalFilesChanged,
|
|
159
159
|
totalTransformsApplied,
|
|
@@ -486,7 +486,6 @@ export async function runCodemods(
|
|
|
486
486
|
(await import('jscodeshift')).default
|
|
487
487
|
);
|
|
488
488
|
|
|
489
|
-
let totalFilesChanged = 0;
|
|
490
489
|
let totalTransformsApplied = 0;
|
|
491
490
|
let totalValidationBlocked = 0;
|
|
492
491
|
/** @type {Array<{file: string, codemod: string, error: string}>} */
|
|
@@ -554,7 +553,6 @@ export async function runCodemods(
|
|
|
554
553
|
protectionWriteCount = writtenFiles.length;
|
|
555
554
|
}
|
|
556
555
|
if (result.filesChanged > 0) {
|
|
557
|
-
totalFilesChanged += result.filesChanged;
|
|
558
556
|
totalTransformsApplied += 1;
|
|
559
557
|
}
|
|
560
558
|
continue;
|
|
@@ -577,7 +575,6 @@ export async function runCodemods(
|
|
|
577
575
|
changedFiles.push(...result.changedFiles);
|
|
578
576
|
writtenFiles.push(...result.writtenFiles);
|
|
579
577
|
if (result.filesChanged > 0) {
|
|
580
|
-
totalFilesChanged += result.filesChanged;
|
|
581
578
|
totalTransformsApplied += result.filesChanged;
|
|
582
579
|
}
|
|
583
580
|
continue;
|
|
@@ -592,7 +589,6 @@ export async function runCodemods(
|
|
|
592
589
|
protectedFiles.push(...result.protectedFiles);
|
|
593
590
|
changedFiles.push(...result.changedFiles);
|
|
594
591
|
writtenFiles.push(...result.writtenFiles);
|
|
595
|
-
totalFilesChanged += result.filesChanged;
|
|
596
592
|
totalTransformsApplied += result.filesChanged;
|
|
597
593
|
totalValidationBlocked += result.errors.filter(
|
|
598
594
|
error =>
|
|
@@ -630,6 +626,11 @@ export async function runCodemods(
|
|
|
630
626
|
);
|
|
631
627
|
}
|
|
632
628
|
|
|
629
|
+
// A file several codemods changed is one file. `totalTransformsApplied` is
|
|
630
|
+
// unchanged: a code or config codemod counts each file it changed, and a
|
|
631
|
+
// project codemod counts once. The two answer different questions.
|
|
632
|
+
const totalFilesChanged = new Set(changedFiles).size;
|
|
633
|
+
|
|
633
634
|
if (protectedFiles.length > 0) {
|
|
634
635
|
const files = [...new Set(protectedFiles.map(item => item.file))];
|
|
635
636
|
log.warn(
|
package/assets/docs/README.md
CHANGED
|
@@ -21,13 +21,15 @@ Someone building a product with Astryx. Their questions:
|
|
|
21
21
|
## Tells that you are writing for us instead
|
|
22
22
|
|
|
23
23
|
- second person aimed at the wrong reader — "reviewers should…", "before promoting a component…", "attach evidence for…"
|
|
24
|
-
-
|
|
24
|
+
- an internal rubric, readiness gate, audit checklist, or sign-off that the reader must satisfy for Astryx maintainers
|
|
25
25
|
- a table of things to verify rather than things to use
|
|
26
26
|
- anything about lab → core, which is our lifecycle, not theirs
|
|
27
27
|
- Storybook, Playwright, CI or the Simulator named as tools the reader runs
|
|
28
28
|
|
|
29
29
|
One subtlety: a statement about the **system's behavior** is caller-facing even when it sounds like process. "A component's theme targets are stable once published" tells a caller what they can rely on; "reviewers must check that theme targets are stable" is ours. Same fact, different reader — **rewrite it rather than move it**.
|
|
30
30
|
|
|
31
|
+
A public authoring-quality rubric is also caller-facing when it helps someone evaluate an artifact they create through Astryx. It must be complete and actionable from public inputs. It must not include Astryx's internal approval, promotion, evidence-publication, or CI process. A current system spec must assign the shipped guide as the rubric's owner.
|
|
32
|
+
|
|
31
33
|
## Where the rest goes
|
|
32
34
|
|
|
33
35
|
The material is usually good; the finding is placement, not quality. It goes in the [wiki](https://github.com/facebook/astryx/wiki) — **as a section on the page that already covers it, not a new page.** The wiki is at nearly 60 pages, several of them overlapping, because every stray section got its own.
|
|
@@ -55,5 +57,5 @@ Worked example: a responsive-and-interaction readiness rubric is grading criteri
|
|
|
55
57
|
one section by its key. A section's key is its `id`, or a key derived from its
|
|
56
58
|
title when it has none. Give a section an `id` when its title may change, since
|
|
57
59
|
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`
|
|
60
|
+
a key. Keep each section small enough to read on its own: `astryx doctor` warns on
|
|
59
61
|
any section over 32 KB.
|