@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2
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 +119 -0
- package/README.md +169 -149
- package/api/build/_adapter.d.mts +8 -0
- package/api/build/_adapter.mjs +12 -1
- package/api/build/build.doc.mjs +4 -0
- package/api/build/kit/kit.mjs +3 -11
- package/api/component/_adapter.d.mts +31 -0
- package/api/component/_adapter.mjs +119 -11
- package/api/component/component.doc.mjs +6 -1
- package/api/component/component.mjs +60 -10
- package/api/component/doc-links.test.mjs +386 -0
- package/api/discover/_adapter.d.mts +132 -12
- package/api/discover/_adapter.mjs +488 -27
- package/api/discover/_adapter.test.mjs +599 -17
- package/api/discover/_catalog-view.d.mts +16 -5
- package/api/discover/_catalog-view.mjs +32 -8
- package/api/discover/detail/doc/doc.mjs +9 -1
- package/api/discover/discover.d.mts +11 -0
- package/api/discover/discover.doc.mjs +9 -7
- package/api/discover/discover.mjs +121 -19
- package/api/discover/discover.type.d.mts +20 -7
- package/api/discover/discover.type.mjs +17 -7
- package/api/discover/list/list.d.mts +4 -3
- package/api/discover/list/list.mjs +17 -10
- package/api/docs/_adapter.d.mts +63 -0
- package/api/docs/_adapter.mjs +312 -18
- package/api/docs/docs.doc.mjs +2 -1
- package/api/docs/docs.mjs +2 -1
- package/api/docs/docs.test.mjs +5 -2
- package/api/docs/integration-tree.test.mjs +22 -0
- package/api/docs/list/list.mjs +12 -2
- package/api/docs/reference-blocks.test.mjs +1 -3
- package/api/doctor/_adapter.d.mts +108 -0
- package/api/doctor/_adapter.mjs +223 -4
- package/api/doctor/doctor.d.mts +199 -0
- package/api/doctor/doctor.doc.mjs +3 -2
- package/api/doctor/doctor.mjs +530 -34
- package/api/doctor/doctor.test.mjs +180 -14
- package/api/doctor/release-checks.test.mjs +156 -0
- package/api/doctor/theme-checks.mjs +16 -3
- package/api/doctor/theme-checks.test.mjs +223 -7
- package/api/error.d.mts +10 -0
- package/api/error.mjs +25 -0
- package/api/gap-report/gap-report.d.mts +3 -0
- package/api/gap-report/gap-report.doc.mjs +11 -3
- package/api/gap-report/gap-report.mjs +152 -48
- package/api/gap-report/gap-report.test.mjs +233 -0
- package/api/gap-report/gap-report.type.d.mts +41 -0
- package/api/gap-report/gap-report.type.mjs +18 -0
- package/api/hook/_adapter.mjs +3 -11
- package/api/hook/hook.doc.mjs +4 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/init/_adapter.d.mts +22 -0
- package/api/init/_adapter.mjs +59 -0
- package/api/init/init.d.mts +7 -5
- package/api/init/init.doc.mjs +28 -7
- package/api/init/init.mjs +28 -4
- package/api/init/init.type.d.mts +48 -3
- package/api/init/init.type.mjs +37 -3
- package/api/init/run/run.d.mts +1 -1
- package/api/init/run/run.mjs +15 -4
- package/api/init/user/user.d.mts +14 -0
- package/api/init/user/user.mjs +87 -0
- package/api/init/user/user.test.mjs +232 -0
- package/api/integration/_adapter.d.mts +33 -0
- package/api/integration/_adapter.mjs +58 -0
- package/api/integration/authoring-checks.mjs +100 -5
- package/api/integration/authoring-checks.test.mjs +769 -0
- package/api/integration/authoring-checks.type.d.mts +134 -0
- package/api/integration/authoring-checks.type.mjs +60 -1
- package/api/integration/integrationCheck.doc.mjs +1 -1
- package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
- package/api/integration/integrationDocConflicts.doc.mjs +22 -5
- package/api/integration/pack-check.mjs +303 -13
- package/api/integration/pack-check.test.mjs +371 -10
- package/api/integration/validate-integration-fixes.test.mjs +67 -0
- package/api/integration/validate-integration.d.mts +8 -0
- package/api/integration/validate-integration.mjs +55 -9
- package/api/integration/validate-integration.test.mjs +77 -8
- package/api/integration/validate-integration.type.d.mts +8 -0
- package/api/integration/validate-integration.type.mjs +3 -1
- package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
- package/api/integration/validateIntegration.doc.mjs +4 -2
- package/api/search/_adapter.d.mts +11 -0
- package/api/search/_adapter.mjs +25 -0
- package/api/search/search.d.mts +73 -13
- package/api/search/search.doc.mjs +8 -3
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.mjs +541 -52
- package/api/search/search.test.mjs +143 -1
- package/api/search/search.type.d.mts +14 -1
- package/api/search/search.type.mjs +6 -1
- package/api/swizzle/_adapter.mjs +2 -2
- package/api/swizzle/copy/copy.mjs +1 -1
- package/api/template/template-integration.test.mjs +1 -1
- package/api/theme/_adapter.d.mts +16 -0
- package/api/theme/_adapter.mjs +32 -5
- package/api/theme/build/build.d.mts +2 -1
- package/api/theme/build/build.mjs +70 -14
- package/api/theme/build/build.packed-old-core.test.mjs +20 -3
- package/api/theme/build/font-warning.mjs +5 -3
- package/api/theme/build/font-warning.test.mjs +2 -1
- package/api/theme/remove/remove.test.mjs +2 -2
- package/api/theme/targets/targets.mjs +3 -10
- package/api/theme/themeTargets.doc.mjs +4 -0
- package/api/theme/use/use.test.mjs +1 -1
- package/api/upgrade/_adapter.d.mts +40 -2
- package/api/upgrade/_adapter.mjs +118 -5
- package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
- package/api/upgrade/run/run.mjs +35 -2
- package/api/upgrade/status/status.d.mts +21 -5
- package/api/upgrade/status/status.mjs +45 -8
- package/api/upgrade/upgrade.doc.mjs +6 -3
- package/api/upgrade/upgrade.mjs +5 -1
- package/api/upgrade/upgrade.type.d.mts +23 -0
- package/api/upgrade/upgrade.type.mjs +16 -1
- package/assets/codemods/__tests__/registry.test.mjs +1 -0
- package/assets/codemods/registry.mjs +1 -0
- package/assets/codemods/runner.mjs +7 -1
- package/assets/codemods/transforms/next/index.mjs +1 -11
- package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
- package/assets/discover/official-packages.json +367 -0
- package/assets/docs/author-a-theme.doc.mjs +2 -2
- package/assets/docs/browser-support.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +23 -3
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +12 -2
- package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
- package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
- package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
- package/assets/docs/tree/block-template.doc.mjs +6 -18
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +4 -0
- package/assets/docs/tree/check-the-props.doc.mjs +8 -3
- package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
- package/assets/docs/tree/checks.doc.mjs +17 -17
- package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
- package/assets/docs/tree/cli-command.doc.mjs +142 -0
- package/assets/docs/tree/cli.doc.mjs +6 -0
- package/assets/docs/tree/codemods.doc.mjs +17 -7
- package/assets/docs/tree/commands.doc.mjs +7 -1
- package/assets/docs/tree/component-family.doc.mjs +3 -5
- package/assets/docs/tree/component-imports.doc.mjs +13 -3
- package/assets/docs/tree/components.doc.mjs +7 -1
- package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
- package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
- package/assets/docs/tree/configuration.doc.mjs +7 -3
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
- package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
- package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
- package/assets/docs/tree/docs.doc.mjs +9 -3
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
- package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
- package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
- package/assets/docs/tree/global-integrations.doc.mjs +124 -0
- package/assets/docs/tree/global.doc.mjs +131 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
- package/assets/docs/tree/install-once.doc.mjs +105 -0
- package/assets/docs/tree/integrations.doc.mjs +13 -1
- package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
- package/assets/docs/tree/layout.doc.mjs +6 -6
- package/assets/docs/tree/links.doc.mjs +6 -5
- package/assets/docs/tree/migration-setup.doc.mjs +3 -2
- package/assets/docs/tree/migration-verification.doc.mjs +0 -1
- package/assets/docs/tree/migration.doc.mjs +0 -1
- package/assets/docs/tree/monorepos.doc.mjs +84 -0
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +3 -7
- package/assets/docs/tree/problems.doc.mjs +205 -0
- package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
- package/assets/docs/tree/publishing.doc.mjs +6 -6
- package/assets/docs/tree/quick-start.doc.mjs +52 -26
- package/assets/docs/tree/reference.doc.mjs +192 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
- package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
- package/assets/docs/tree/scaffold.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
- package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
- package/assets/docs/tree/single-component.doc.mjs +14 -6
- package/assets/docs/tree/start-a-template.doc.mjs +18 -8
- package/assets/docs/tree/subcomponent.doc.mjs +6 -6
- package/assets/docs/tree/template-assets.doc.mjs +2 -2
- package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +2 -2
- package/assets/docs/tree/templates.doc.mjs +7 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
- package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
- package/assets/docs/tree/themes.doc.mjs +4 -0
- package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
- package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
- package/assets/docs/tree/uninstall.doc.mjs +109 -0
- package/assets/docs/tree/updates.doc.mjs +92 -0
- package/assets/docs/tree/upgrading.doc.mjs +6 -6
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
- package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
- package/assets/docs/tree/versioning.doc.mjs +9 -9
- package/assets/docs/tree/who-answers.doc.mjs +119 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
- package/assets/docs/working-with-ai.doc.mjs +23 -9
- package/authoring/codemod/codemod.doc.mjs +7 -3
- package/authoring/config/config.doc.mjs +9 -6
- package/authoring/config/type.ts +4 -2
- package/authoring/debug/parse.d.mts +5 -5
- package/authoring/discover/discover.doc.mjs +7 -1
- package/authoring/doctypes/_schema.d.mts +8 -3
- package/authoring/doctypes/_schema.mjs +1 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
- package/authoring/doctypes/command/command.doc.mjs +20 -12
- package/authoring/doctypes/command/type.ts +14 -0
- package/authoring/doctypes/component/component.doc.mjs +29 -26
- package/authoring/doctypes/component/type.ts +15 -5
- package/authoring/doctypes/enum/enum.doc.mjs +7 -4
- package/authoring/doctypes/function/function.doc.mjs +9 -8
- package/authoring/doctypes/hook/hook.doc.mjs +5 -4
- package/authoring/doctypes/hook/type.ts +4 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
- package/authoring/doctypes/reference/reference.doc.mjs +12 -11
- package/authoring/doctypes/schema/schema.doc.mjs +9 -9
- package/authoring/doctypes/template/template.doc.mjs +2 -2
- package/authoring/doctypes/theme/theme.doc.mjs +2 -2
- package/authoring/gap-report/gap-report.doc.mjs +2 -2
- package/authoring/identity/identity.doc.mjs +3 -3
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/integration.doc.mjs +22 -5
- package/authoring/integration/parse.test.mjs +11 -0
- package/authoring/integration/schema.d.mts +14 -3
- package/authoring/integration/schema.mjs +33 -2
- package/authoring/integration/type.ts +8 -0
- package/clients/cli/bin/astryx.mjs +20 -3
- package/clients/cli/cli-command.test.mjs +166 -0
- package/clients/cli/commands/blog.doc.mjs +1 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
- package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
- package/clients/cli/commands/build-theme.mjs +25 -12
- package/clients/cli/commands/build.doc.mjs +1 -0
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
- package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
- package/clients/cli/commands/component.doc.mjs +1 -0
- package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
- package/clients/cli/commands/discover.doc.mjs +9 -1
- package/clients/cli/commands/discover.mjs +144 -93
- package/clients/cli/commands/discover.no-source.test.mjs +448 -33
- package/clients/cli/commands/discover.sources.test.mjs +20 -1
- package/clients/cli/commands/docs.doc.mjs +1 -0
- package/clients/cli/commands/docs.mjs +49 -19
- package/clients/cli/commands/docs.test.mjs +2 -2
- package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
- package/clients/cli/commands/doctor-integration.test.mjs +52 -4
- package/clients/cli/commands/doctor.doc.mjs +12 -2
- package/clients/cli/commands/doctor.mjs +102 -7
- package/clients/cli/commands/doctor.test.mjs +10 -4
- package/clients/cli/commands/gap-report.doc.mjs +15 -2
- package/clients/cli/commands/gap-report.mjs +24 -2
- package/clients/cli/commands/gap-report.test.mjs +79 -0
- package/clients/cli/commands/hook.doc.mjs +1 -0
- package/clients/cli/commands/init.behavior.test.mjs +5 -0
- package/clients/cli/commands/init.doc.mjs +29 -3
- package/clients/cli/commands/integration-add.doc.mjs +7 -0
- package/clients/cli/commands/integration-authoring.test.mjs +49 -3
- package/clients/cli/commands/integration-check.doc.mjs +10 -2
- package/clients/cli/commands/integration-check.test.mjs +175 -1
- package/clients/cli/commands/integration-pack.doc.mjs +8 -1
- package/clients/cli/commands/integration-verify.doc.mjs +8 -1
- package/clients/cli/commands/integration.mjs +15 -9
- package/clients/cli/commands/json-contract.test.mjs +11 -2
- package/clients/cli/commands/manifest.doc.mjs +1 -0
- package/clients/cli/commands/outside-project.test.mjs +774 -0
- package/clients/cli/commands/search.doc.mjs +1 -0
- package/clients/cli/commands/search.mjs +16 -11
- package/clients/cli/commands/search.test.mjs +429 -296
- package/clients/cli/commands/setup-nudge.test.mjs +102 -2
- package/clients/cli/commands/swizzle.doc.mjs +7 -0
- package/clients/cli/commands/swizzle.mjs +5 -3
- package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
- package/clients/cli/commands/template.doc.mjs +1 -0
- package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
- package/clients/cli/commands/theme-add.doc.mjs +8 -1
- package/clients/cli/commands/theme-build.doc.mjs +1 -0
- package/clients/cli/commands/theme-eject.doc.mjs +1 -0
- package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
- package/clients/cli/commands/theme-list.doc.mjs +1 -0
- package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
- package/clients/cli/commands/theme-remove.doc.mjs +7 -0
- package/clients/cli/commands/theme-targets.doc.mjs +1 -0
- package/clients/cli/commands/theme-template.doc.mjs +1 -0
- package/clients/cli/commands/theme-use.doc.mjs +7 -0
- package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
- package/clients/cli/commands/upgrade.doc.mjs +22 -1
- package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
- package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
- package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
- package/clients/cli/index.mjs +111 -16
- package/clients/cli/lib/cli-error.mjs +4 -1
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +2 -2
- package/clients/cli/lib/json-shim.mjs +3 -1
- package/clients/cli/lib/manifest.d.ts +9 -0
- package/clients/cli/lib/manifest.mjs +17 -1
- package/clients/cli/lib/manifest.test.mjs +25 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
- package/foundation/agent-docs/agent-doc-state.mjs +66 -6
- package/foundation/agent-docs/agent-docs.d.mts +4 -3
- package/foundation/agent-docs/agent-docs.mjs +11 -9
- package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
- package/foundation/agent-docs/computer-agent-text.mjs +623 -0
- package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
- package/foundation/config/project.d.mts +35 -3
- package/foundation/config/project.mjs +145 -8
- package/foundation/config/project.test.mjs +66 -0
- package/foundation/config/theme-state.mjs +5 -4
- package/foundation/discovery/authoring-self-docs.mjs +1 -1
- package/foundation/discovery/cli-self-docs.mjs +20 -3
- package/foundation/discovery/cli-self-docs.test.mjs +3 -2
- package/foundation/discovery/component-replacement.mjs +2 -1
- package/foundation/discovery/copied-passages.d.mts +216 -0
- package/foundation/discovery/copied-passages.mjs +720 -0
- package/foundation/discovery/copied-passages.test.mjs +124 -0
- package/foundation/discovery/doc-consistency.d.mts +34 -0
- package/foundation/discovery/doc-consistency.mjs +513 -0
- package/foundation/discovery/doc-consistency.test.mjs +347 -0
- package/foundation/discovery/doc-fluff.d.mts +112 -0
- package/foundation/discovery/doc-fluff.mjs +328 -0
- package/foundation/discovery/doc-fluff.test.mjs +142 -0
- package/foundation/discovery/doc-types.d.mts +1 -1
- package/foundation/discovery/doc-types.mjs +15 -8
- package/foundation/discovery/doc-types.test.mjs +94 -2
- package/foundation/discovery/docs-discovery.d.mts +7 -0
- package/foundation/discovery/docs-discovery.mjs +26 -2
- package/foundation/discovery/project-typescript.d.mts +11 -2
- package/foundation/discovery/project-typescript.mjs +32 -4
- package/foundation/discovery/prop-drift.d.mts +26 -7
- package/foundation/discovery/prop-drift.mjs +173 -8
- package/foundation/discovery/prop-drift.test.mjs +120 -2
- package/foundation/discovery/runner-allowlist.d.mts +40 -0
- package/foundation/discovery/runner-allowlist.mjs +53 -0
- package/foundation/discovery/stray-pointers.d.mts +52 -0
- package/foundation/discovery/stray-pointers.mjs +293 -0
- package/foundation/discovery/stray-pointers.test.mjs +86 -0
- package/foundation/discovery/template-needs.mjs +5 -2
- package/foundation/discovery/theme-imports.mjs +8 -4
- package/foundation/discovery/theme-imports.test.mjs +1 -1
- package/foundation/discovery/type-text.mjs +1 -1
- package/foundation/discovery/type-text.test.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
- package/foundation/doc-compiler/inputs.mjs +4 -1
- package/foundation/doc-compiler/links.d.mts +32 -0
- package/foundation/doc-compiler/links.mjs +163 -26
- package/foundation/doc-compiler/links.test.mjs +97 -4
- package/foundation/doc-compiler/read.d.mts +1 -1
- package/foundation/doc-compiler/tree.mjs +30 -1
- package/foundation/doc-compiler/tree.test.mjs +8 -1
- package/foundation/env/cli-command.d.mts +83 -0
- package/foundation/env/cli-command.mjs +152 -0
- package/foundation/env/cli-command.test.mjs +129 -0
- package/foundation/env/node-version.test.mjs +15 -3
- package/foundation/env/package-manager.d.mts +93 -9
- package/foundation/env/package-manager.global.test.mjs +66 -0
- package/foundation/env/package-manager.mjs +184 -13
- package/foundation/env/package-manager.test.mjs +341 -15
- package/foundation/env/project-scope.d.mts +9 -0
- package/foundation/env/project-scope.mjs +33 -0
- package/foundation/env/search-path.d.mts +42 -0
- package/foundation/env/search-path.mjs +205 -0
- package/foundation/env/search-path.test.mjs +122 -0
- package/foundation/env/user-dirs.d.mts +39 -0
- package/foundation/env/user-dirs.mjs +93 -0
- package/foundation/env/user-dirs.test.mjs +72 -0
- package/foundation/env/user-settings.d.mts +46 -0
- package/foundation/env/user-settings.mjs +106 -0
- package/foundation/env/user-settings.test.mjs +64 -0
- package/foundation/fs/file-protection.mjs +15 -1
- package/foundation/fs/module-loader.d.mts +2 -1
- package/foundation/fs/module-loader.mjs +60 -4
- package/foundation/fs/module-loader.stdout.test.mjs +3 -2
- package/foundation/fs/module-loader.test.mjs +194 -0
- package/foundation/fs/path-compare.d.mts +33 -0
- package/foundation/fs/path-compare.mjs +73 -0
- package/foundation/fs/paths.d.mts +57 -1
- package/foundation/fs/paths.mjs +131 -3
- package/foundation/fs/paths.test.mjs +10 -1
- package/foundation/integrations/computer-integrations.d.mts +78 -0
- package/foundation/integrations/computer-integrations.mjs +316 -0
- package/foundation/integrations/computer-integrations.test.mjs +292 -0
- package/foundation/integrations/contribution-inventory.d.mts +11 -0
- package/foundation/integrations/contribution-inventory.mjs +50 -2
- package/foundation/integrations/integration-warnings.mjs +3 -2
- package/foundation/integrations/integration-warnings.test.mjs +8 -9
- package/foundation/integrations/integrations.d.mts +25 -1
- package/foundation/integrations/integrations.mjs +46 -3
- package/foundation/integrations/issue.ts +6 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +41 -1
- package/foundation/release/agent-text-worker.d.mts +4 -0
- package/foundation/release/agent-text-worker.mjs +52 -0
- package/foundation/release/cache.d.mts +310 -0
- package/foundation/release/cache.mjs +1216 -0
- package/foundation/release/cache.test.mjs +920 -0
- package/foundation/release/choose.d.mts +207 -0
- package/foundation/release/choose.mjs +677 -0
- package/foundation/release/choose.test.mjs +427 -0
- package/foundation/release/core-worker.d.mts +4 -0
- package/foundation/release/core-worker.mjs +38 -0
- package/foundation/release/global-state.d.mts +259 -0
- package/foundation/release/global-state.mjs +472 -0
- package/foundation/release/global-state.test.mjs +381 -0
- package/foundation/release/handoff.d.mts +60 -0
- package/foundation/release/handoff.e2e.test.mjs +278 -0
- package/foundation/release/handoff.mjs +357 -0
- package/foundation/release/lock-lease.d.mts +23 -0
- package/foundation/release/lock-lease.mjs +80 -0
- package/foundation/release/notices.d.mts +253 -0
- package/foundation/release/notices.mjs +596 -0
- package/foundation/release/notices.test.mjs +565 -0
- package/foundation/release/npm-guard.d.mts +4 -0
- package/foundation/release/npm-guard.mjs +126 -0
- package/foundation/release/project.d.mts +141 -0
- package/foundation/release/project.mjs +466 -0
- package/foundation/release/project.test.mjs +288 -0
- package/foundation/release/refresh-worker.d.mts +4 -0
- package/foundation/release/refresh-worker.mjs +27 -0
- package/foundation/release/run-context.d.mts +47 -0
- package/foundation/release/run-context.mjs +149 -0
- package/foundation/release/update-notice-env.d.mts +28 -0
- package/foundation/release/update-notice-env.mjs +39 -0
- package/foundation/response/base.d.ts +26 -3
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.doc.mjs +12 -0
- package/foundation/response/error-codes.mjs +17 -0
- package/foundation/response/error-codes.test.mjs +2 -0
- package/foundation/response/json-contract.test.mjs +7 -2
- package/foundation/response/json.mjs +4 -1
- package/foundation/response/response-types.doc.mjs +17 -11
- package/foundation/response/response-types.doc.test.mjs +46 -0
- package/foundation/response/response.doc.mjs +3 -3
- package/foundation/xle/browser-imports.test.mjs +74 -0
- package/package.json +11 -11
- package/scripts/postinstall.mjs +24 -5
- /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
- /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
|
@@ -8,11 +8,17 @@
|
|
|
8
8
|
* the runner that launched us. Returns the correct command prefix for running
|
|
9
9
|
* package binaries (e.g. 'npx astryx', 'yarn astryx', 'pnpm exec astryx'), and
|
|
10
10
|
* the scoped one-off form ('npx @astryxdesign/cli') when the `astryx` bin does
|
|
11
|
-
* not resolve from the project.
|
|
11
|
+
* not resolve from the project. An integration's declared `cliCommand`, then
|
|
12
|
+
* a host's door name, come first (see cli-command.mjs).
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import * as fs from 'node:fs';
|
|
15
16
|
import * as path from 'node:path';
|
|
17
|
+
import {cliDoor, declaredCliCommand} from './cli-command.mjs';
|
|
18
|
+
import {CLI_ROOT} from '../fs/paths.mjs';
|
|
19
|
+
import {cliVersion, runChoice} from '../release/run-context.mjs';
|
|
20
|
+
import {isGlobalCopy} from '../release/choose.mjs';
|
|
21
|
+
import {isInReleaseCache} from '../release/cache.mjs';
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
* A package manager we can install with and run binaries through.
|
|
@@ -473,25 +479,73 @@ export function cliBinResolves(targetDir = process.cwd()) {
|
|
|
473
479
|
}
|
|
474
480
|
|
|
475
481
|
/**
|
|
476
|
-
* The
|
|
482
|
+
* The CLI invocation stem every printed command starts with. The one picker:
|
|
483
|
+
* every hint, error, Doctor fix and doc line that names a command goes through
|
|
484
|
+
* it (directly, or through {@link formatCliCommand} and
|
|
485
|
+
* {@link formatCliCommandsIn}). JSON fields that carry a command stay bare.
|
|
477
486
|
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
* -
|
|
481
|
-
* `
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
487
|
+
* In order:
|
|
488
|
+
*
|
|
489
|
+
* - The `cliCommand` an integration loaded in this project declares, e.g.
|
|
490
|
+
* `acme astryx`. When two integrations declare different commands,
|
|
491
|
+
* neither is used, the guess below is (even when a host set a door), and
|
|
492
|
+
* `astryx doctor` warns.
|
|
493
|
+
* - The door a host ran this call through (see cli-command.mjs). Empty in a
|
|
494
|
+
* plain run.
|
|
495
|
+
* - Otherwise the guess below.
|
|
496
|
+
*
|
|
497
|
+
* The guess names a command that, run from `targetDir`, reaches the release
|
|
498
|
+
* that answered (spec AST-081 FR5):
|
|
499
|
+
*
|
|
500
|
+
* - The project's own CLI answered and its `astryx` bin resolves from
|
|
501
|
+
* `targetDir`: `<run-prefix> astryx` (e.g. `pnpm exec astryx`).
|
|
502
|
+
* - Outside a project, the CLI installed for the computer (or the release it
|
|
503
|
+
* keeps) answered: `astryx`.
|
|
504
|
+
* - Otherwise: `<dlx-prefix> @astryxdesign/cli@<answering version>`, pinned so
|
|
505
|
+
* the package runner starts the same release. The bare `astryx` name would
|
|
506
|
+
* make npm fetch an unrelated registry package, and the unpinned scoped
|
|
507
|
+
* package the newest release.
|
|
508
|
+
*
|
|
509
|
+
* With `installed`, the line follows a step that installs the project's own
|
|
510
|
+
* CLI (an update notice, the hint that matches a project's CLI to its Core),
|
|
511
|
+
* so the guess is that CLI: `<run-prefix> astryx`. A declared `cliCommand`
|
|
512
|
+
* and a host's door still come first.
|
|
485
513
|
*
|
|
486
514
|
* @param {string} [targetDir] the project the printed command runs in;
|
|
487
515
|
* defaults to the working directory
|
|
516
|
+
* @param {{installed?: boolean}} [options]
|
|
517
|
+
* @returns {string}
|
|
518
|
+
*/
|
|
519
|
+
export function getCliInvocation(targetDir, {installed = false} = {}) {
|
|
520
|
+
const declared = declaredCliCommand(targetDir);
|
|
521
|
+
if (declared.command) return declared.command;
|
|
522
|
+
const guess = () =>
|
|
523
|
+
installed
|
|
524
|
+
? `${getRunPrefix(targetDir)} ${CLI_BIN}`
|
|
525
|
+
: guessCliInvocation(targetDir);
|
|
526
|
+
if (declared.conflict) return guess();
|
|
527
|
+
return cliDoor() ?? guess();
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* The package-manager guess alone, without any integration's `cliCommand` or
|
|
532
|
+
* a host's door. The picker falls back to it when declared commands conflict,
|
|
533
|
+
* and Doctor names it then.
|
|
534
|
+
*
|
|
535
|
+
* @param {string} [targetDir]
|
|
488
536
|
* @returns {string}
|
|
489
537
|
*/
|
|
490
|
-
export function
|
|
491
|
-
|
|
492
|
-
|
|
538
|
+
export function guessCliInvocation(targetDir) {
|
|
539
|
+
// Who answered this run decides the stem, so every command one run prints
|
|
540
|
+
// reaches the same release, whichever folder a line names.
|
|
541
|
+
const choice = runChoice(process.cwd());
|
|
542
|
+
if (choice.copy === 'project' && !isCliOneOff() && cliBinResolves(targetDir)) {
|
|
543
|
+
return `${getRunPrefix(targetDir)} ${CLI_BIN}`;
|
|
493
544
|
}
|
|
494
|
-
|
|
545
|
+
if (!choice.inProject && (isGlobalCopy(CLI_ROOT) || isInReleaseCache(CLI_ROOT))) {
|
|
546
|
+
return CLI_BIN;
|
|
547
|
+
}
|
|
548
|
+
return `${getDlxPrefix(targetDir)} ${CLI_PACKAGE}@${cliVersion()}`;
|
|
495
549
|
}
|
|
496
550
|
|
|
497
551
|
/**
|
|
@@ -512,3 +566,120 @@ export function formatCliCommand(command, targetDir) {
|
|
|
512
566
|
const stem = getCliInvocation(targetDir);
|
|
513
567
|
return sub ? `${stem} ${sub}` : stem;
|
|
514
568
|
}
|
|
569
|
+
|
|
570
|
+
/** Code-fence languages whose lines are shell commands. */
|
|
571
|
+
const SHELL_FENCES = new Set(['', 'bash', 'sh', 'shell', 'zsh', 'console']);
|
|
572
|
+
|
|
573
|
+
/** Code-fence languages that hold prose, such as a prompt to paste. */
|
|
574
|
+
const PROSE_FENCES = new Set(['text', 'txt', 'markdown', 'md']);
|
|
575
|
+
|
|
576
|
+
/** `astryx` where a shell line starts a command: line start, `$ `, `&&`, `||`, `;`, `|`. */
|
|
577
|
+
const SHELL_COMMAND = /(^\s*(?:\$\s+)?|(?:&&|\|\||;|\|)\s*)astryx(?=\s|$)/g;
|
|
578
|
+
|
|
579
|
+
/** `astryx` opening an inline code span: `` `astryx docs` `` or `` `astryx` ``. */
|
|
580
|
+
const INLINE_COMMAND = /`astryx(?=[\s`])/g;
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Prefix the CLI commands in one line.
|
|
584
|
+
* @param {string} line
|
|
585
|
+
* @param {string} stem
|
|
586
|
+
* @param {string | null} lang the fence the line sits in; null for prose
|
|
587
|
+
* @returns {string}
|
|
588
|
+
*/
|
|
589
|
+
function prefixLine(line, stem, lang) {
|
|
590
|
+
if (lang == null) return line.replace(INLINE_COMMAND, `\`${stem}`);
|
|
591
|
+
if (SHELL_FENCES.has(lang)) return line.replace(SHELL_COMMAND, `$1${stem}`);
|
|
592
|
+
if (PROSE_FENCES.has(lang)) {
|
|
593
|
+
return line
|
|
594
|
+
.replace(INLINE_COMMAND, `\`${stem}`)
|
|
595
|
+
.replace(SHELL_COMMAND, `$1${stem}`);
|
|
596
|
+
}
|
|
597
|
+
return line;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Prefix every CLI command in doc text with `stem`, the invocation from
|
|
602
|
+
* {@link getCliInvocation}.
|
|
603
|
+
*
|
|
604
|
+
* Docs, including an integration's, are written with bare `astryx ...`
|
|
605
|
+
* commands; this rewrites them when they are shown. In prose, an inline code
|
|
606
|
+
* span that starts with `astryx`. In a shell code fence (no language, `bash`,
|
|
607
|
+
* `sh`, `shell`, `zsh`, `console`), each `astryx` that starts a command. In a
|
|
608
|
+
* `text` or `markdown` fence, both. Other fences, and plain words, are left
|
|
609
|
+
* alone. JSON output keeps the authored text.
|
|
610
|
+
*
|
|
611
|
+
* @param {string} text Markdown-style doc text, or the body of one code block.
|
|
612
|
+
* @param {string} stem e.g. `pnpm exec astryx`
|
|
613
|
+
* @param {string | null} [lang] the code block's language when `text` is one
|
|
614
|
+
* block's body; omit for Markdown, whose fences are followed
|
|
615
|
+
* @returns {string}
|
|
616
|
+
*/
|
|
617
|
+
export function prefixCliCommands(text, stem, lang = null) {
|
|
618
|
+
if (stem === CLI_BIN) return String(text);
|
|
619
|
+
const blockLang = lang == null ? null : lang.toLowerCase();
|
|
620
|
+
/** @type {{marker: string, lang: string} | null} */
|
|
621
|
+
let fence = null;
|
|
622
|
+
return String(text)
|
|
623
|
+
.split('\n')
|
|
624
|
+
.map(line => {
|
|
625
|
+
if (blockLang != null) return prefixLine(line, stem, blockLang);
|
|
626
|
+
const marker = /^\s*(`{3,}|~{3,})\s*([\w-]*)/.exec(line);
|
|
627
|
+
if (marker) {
|
|
628
|
+
if (fence == null) {
|
|
629
|
+
fence = {marker: marker[1], lang: marker[2].toLowerCase()};
|
|
630
|
+
} else if (marker[1].startsWith(fence.marker)) {
|
|
631
|
+
fence = null;
|
|
632
|
+
}
|
|
633
|
+
return line;
|
|
634
|
+
}
|
|
635
|
+
return prefixLine(line, stem, fence?.lang ?? null);
|
|
636
|
+
})
|
|
637
|
+
.join('\n');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Quote one argument of a printed command so the shell it is pasted into
|
|
642
|
+
* passes it on unchanged. Printed commands target a POSIX shell (sh, bash,
|
|
643
|
+
* zsh), and PowerShell on Windows; cmd.exe is not a target. A value made only
|
|
644
|
+
* of letters, digits and `@ / . _ -` (and, on Windows, a drive path's `:` and
|
|
645
|
+
* `\`) stays bare. Any other value gets single quotes, inside which both
|
|
646
|
+
* shells read `$`, backticks, `%`, `!`, spaces and double quotes literally. An
|
|
647
|
+
* embedded single quote is written `'\''` for a POSIX shell and doubled for
|
|
648
|
+
* PowerShell, which also ends a single-quoted string at the typographic
|
|
649
|
+
* quotes `‘ ’ ‚ ‛`, so those are doubled too (`Macy’s` → `'Macy’’s'`).
|
|
650
|
+
*
|
|
651
|
+
* @param {string} value
|
|
652
|
+
* @param {string} [platform] the `process.platform` the command is printed for
|
|
653
|
+
* @returns {string}
|
|
654
|
+
*/
|
|
655
|
+
export function quoteCommandArgument(value, platform = process.platform) {
|
|
656
|
+
const text = String(value);
|
|
657
|
+
if (platform === 'win32') {
|
|
658
|
+
if (/^[A-Za-z0-9@/._:\\-]+$/u.test(text)) return text;
|
|
659
|
+
return `'${text.replace(/['\u2018\u2019\u201A\u201B]/gu, quote => quote + quote)}'`;
|
|
660
|
+
}
|
|
661
|
+
if (/^[A-Za-z0-9@/._-]+$/u.test(text)) return text;
|
|
662
|
+
return `'${text.replace(/'/gu, `'\\''`)}'`;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* A printed command that changes to `dir`, then runs `command` there, so it
|
|
667
|
+
* does the same work pasted from any folder. It runs `command` only when the
|
|
668
|
+
* change worked. For a POSIX shell: `cd <dir> && <command>`. For PowerShell
|
|
669
|
+
* on Windows: `cd -LiteralPath <dir> -ErrorAction Stop; <command>`, because
|
|
670
|
+
* `-LiteralPath` reads `[` and `]` in a folder name literally, and a failed
|
|
671
|
+
* change stops the line in Windows PowerShell 5.1, which has no `&&`, as in
|
|
672
|
+
* PowerShell 7.
|
|
673
|
+
*
|
|
674
|
+
* @param {string} dir an absolute folder
|
|
675
|
+
* @param {string} command the command to run there, its arguments already
|
|
676
|
+
* quoted with {@link quoteCommandArgument}
|
|
677
|
+
* @param {string} [platform] the `process.platform` the command is printed for
|
|
678
|
+
* @returns {string}
|
|
679
|
+
*/
|
|
680
|
+
export function commandInFolder(dir, command, platform = process.platform) {
|
|
681
|
+
const folder = quoteCommandArgument(dir, platform);
|
|
682
|
+
return platform === 'win32'
|
|
683
|
+
? `cd -LiteralPath ${folder} -ErrorAction Stop; ${command}`
|
|
684
|
+
: `cd ${folder} && ${command}`;
|
|
685
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
import {describe, it, expect, afterEach, vi} from 'vitest';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
4
5
|
import * as fs from 'node:fs';
|
|
5
6
|
import * as path from 'node:path';
|
|
6
7
|
import * as os from 'node:os';
|
|
@@ -11,12 +12,37 @@ import {
|
|
|
11
12
|
isCliOneOff,
|
|
12
13
|
cliBinResolves,
|
|
13
14
|
getCliInvocation,
|
|
15
|
+
guessCliInvocation,
|
|
14
16
|
formatCliCommand,
|
|
17
|
+
prefixCliCommands,
|
|
18
|
+
quoteCommandArgument,
|
|
19
|
+
commandInFolder,
|
|
15
20
|
} from './package-manager.mjs';
|
|
21
|
+
import {recordDeclaredCliCommands, runWithCliDoor} from './cli-command.mjs';
|
|
22
|
+
import {CLI_VERSION} from '../../test-utils/release-fixtures.mjs';
|
|
16
23
|
|
|
17
24
|
let tmpDir;
|
|
18
25
|
const ORIGINAL_ARGV1 = process.argv[1];
|
|
19
26
|
|
|
27
|
+
/** Prints, as JSON, the folder a command ran in and the arguments it got. */
|
|
28
|
+
const PRINT_RUN =
|
|
29
|
+
'process.stdout.write(JSON.stringify({cwd: process.cwd(), argv: process.argv.slice(1)}))';
|
|
30
|
+
|
|
31
|
+
/** PowerShell, when it is on the PATH: printed commands target it on Windows. */
|
|
32
|
+
const PWSH = (process.env.PATH ?? '')
|
|
33
|
+
.split(path.delimiter)
|
|
34
|
+
.flatMap(dir => ['pwsh', 'pwsh.exe'].map(name => path.join(dir, name)))
|
|
35
|
+
.find(file => isFile(file));
|
|
36
|
+
|
|
37
|
+
/** @param {string} file */
|
|
38
|
+
function isFile(file) {
|
|
39
|
+
try {
|
|
40
|
+
return fs.statSync(file).isFile();
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
20
46
|
afterEach(() => {
|
|
21
47
|
if (tmpDir) {
|
|
22
48
|
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
@@ -37,8 +63,16 @@ function linkCliBin(dir, name = 'astryx') {
|
|
|
37
63
|
const binDir = path.join(dir, 'node_modules', '.bin');
|
|
38
64
|
fs.mkdirSync(binDir, {recursive: true});
|
|
39
65
|
fs.writeFileSync(path.join(binDir, name), '');
|
|
66
|
+
// The installed package the bin belongs to: this copy, so the project's own
|
|
67
|
+
// CLI is the one answering (spec AST-081 FR5's run-prefix case).
|
|
68
|
+
const pkg = path.join(dir, 'node_modules', '@astryxdesign', 'cli');
|
|
69
|
+
fs.mkdirSync(path.dirname(pkg), {recursive: true});
|
|
70
|
+
if (!fs.existsSync(pkg)) fs.symlinkSync(THIS_CLI, pkg, 'dir');
|
|
40
71
|
}
|
|
41
72
|
|
|
73
|
+
/** This CLI package's folder. */
|
|
74
|
+
const THIS_CLI = path.resolve(import.meta.dirname, '..', '..');
|
|
75
|
+
|
|
42
76
|
/** The header a classic Yarn (1.x) lockfile starts with. */
|
|
43
77
|
const YARN_V1_LOCK =
|
|
44
78
|
'# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n# yarn lockfile v1\n\n';
|
|
@@ -48,7 +82,8 @@ const YARN_BERRY_LOCK =
|
|
|
48
82
|
'# This file is generated by running "yarn install" inside your project.\n# Manual changes might be lost - proceed with caution!\n\n__metadata:\n version: 8\n';
|
|
49
83
|
|
|
50
84
|
/** An installed CLI's entry, outside any runner cache (a global or workspace install). */
|
|
51
|
-
const INSTALLED_ENTRY =
|
|
85
|
+
const INSTALLED_ENTRY =
|
|
86
|
+
'/usr/local/lib/node_modules/@astryxdesign/cli/clients/cli/bin/astryx.mjs';
|
|
52
87
|
|
|
53
88
|
describe('detectPackageManager', () => {
|
|
54
89
|
it('detects yarn from yarn.lock', () => {
|
|
@@ -132,7 +167,10 @@ describe('detectPackageManager', () => {
|
|
|
132
167
|
const dir = makeTmpDir();
|
|
133
168
|
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
134
169
|
fs.writeFileSync(path.join(dir, 'yarn.lock'), '');
|
|
135
|
-
fs.writeFileSync(
|
|
170
|
+
fs.writeFileSync(
|
|
171
|
+
path.join(dir, '.yarnrc.yml'),
|
|
172
|
+
'nodeLinker: node-modules\n',
|
|
173
|
+
);
|
|
136
174
|
expect(detectPackageManager(dir)).toBe('yarn');
|
|
137
175
|
});
|
|
138
176
|
|
|
@@ -425,12 +463,14 @@ describe('isCliOneOff', () => {
|
|
|
425
463
|
});
|
|
426
464
|
|
|
427
465
|
it('detects a pnpm dlx cache entry', () => {
|
|
428
|
-
process.argv[1] =
|
|
466
|
+
process.argv[1] =
|
|
467
|
+
'/home/u/.cache/pnpm/dlx/9f/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
429
468
|
expect(isCliOneOff()).toBe(true);
|
|
430
469
|
});
|
|
431
470
|
|
|
432
471
|
it('detects a bunx cache entry', () => {
|
|
433
|
-
process.argv[1] =
|
|
472
|
+
process.argv[1] =
|
|
473
|
+
'/home/u/.bun/install/cache/@astryxdesign/cli/bin/astryx.mjs';
|
|
434
474
|
expect(isCliOneOff()).toBe(true);
|
|
435
475
|
});
|
|
436
476
|
|
|
@@ -506,11 +546,11 @@ describe('getCliInvocation', () => {
|
|
|
506
546
|
// fresh package), the CLI used to print `npx astryx` for a project where that
|
|
507
547
|
// bin does not resolve, so following the hint fetched someone else's package.
|
|
508
548
|
it.each([
|
|
509
|
-
['package-lock.json', '{}',
|
|
510
|
-
['pnpm-lock.yaml', '',
|
|
511
|
-
['yarn.lock', YARN_BERRY_LOCK,
|
|
512
|
-
['yarn.lock', YARN_V1_LOCK,
|
|
513
|
-
['bun.lockb', '',
|
|
549
|
+
['package-lock.json', '{}', `npx @astryxdesign/cli@${CLI_VERSION}`],
|
|
550
|
+
['pnpm-lock.yaml', '', `pnpm dlx @astryxdesign/cli@${CLI_VERSION}`],
|
|
551
|
+
['yarn.lock', YARN_BERRY_LOCK, `yarn dlx @astryxdesign/cli@${CLI_VERSION}`],
|
|
552
|
+
['yarn.lock', YARN_V1_LOCK, `npx @astryxdesign/cli@${CLI_VERSION}`],
|
|
553
|
+
['bun.lockb', '', `bunx @astryxdesign/cli@${CLI_VERSION}`],
|
|
514
554
|
])(
|
|
515
555
|
'prints the scoped package for a project with no local CLI (%s), launched from an installed path',
|
|
516
556
|
(lockfile, contents, expected) => {
|
|
@@ -527,21 +567,24 @@ describe('getCliInvocation', () => {
|
|
|
527
567
|
const dir = makeTmpDir();
|
|
528
568
|
delete process.env.npm_config_user_agent;
|
|
529
569
|
fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"fresh"}');
|
|
530
|
-
expect(getCliInvocation(dir)).toBe(
|
|
570
|
+
expect(getCliInvocation(dir)).toBe(`npx @astryxdesign/cli@${CLI_VERSION}`);
|
|
531
571
|
});
|
|
532
572
|
|
|
533
573
|
it('uses the dlx runner + scoped package when run one-off', () => {
|
|
534
574
|
process.argv[1] = '/home/u/.npm/_npx/a1b2/node_modules/.bin/astryx';
|
|
535
575
|
const dir = makeTmpDir();
|
|
536
576
|
delete process.env.npm_config_user_agent;
|
|
537
|
-
expect(getCliInvocation(dir)).toBe(
|
|
577
|
+
expect(getCliInvocation(dir)).toBe(`npx @astryxdesign/cli@${CLI_VERSION}`);
|
|
538
578
|
});
|
|
539
579
|
|
|
540
580
|
it('pairs the dlx runner with the scoped package for pnpm one-off', () => {
|
|
541
|
-
process.argv[1] =
|
|
581
|
+
process.argv[1] =
|
|
582
|
+
'/home/u/.cache/pnpm/dlx/9f/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
542
583
|
const dir = makeTmpDir();
|
|
543
584
|
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
544
|
-
expect(getCliInvocation(dir)).toBe(
|
|
585
|
+
expect(getCliInvocation(dir)).toBe(
|
|
586
|
+
`pnpm dlx @astryxdesign/cli@${CLI_VERSION}`,
|
|
587
|
+
);
|
|
545
588
|
});
|
|
546
589
|
});
|
|
547
590
|
|
|
@@ -551,7 +594,9 @@ describe('formatCliCommand', () => {
|
|
|
551
594
|
const dir = makeTmpDir();
|
|
552
595
|
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
553
596
|
linkCliBin(dir);
|
|
554
|
-
expect(formatCliCommand('astryx component Button', dir)).toBe(
|
|
597
|
+
expect(formatCliCommand('astryx component Button', dir)).toBe(
|
|
598
|
+
'pnpm exec astryx component Button',
|
|
599
|
+
);
|
|
555
600
|
});
|
|
556
601
|
|
|
557
602
|
it('accepts a bare subcommand (no leading astryx)', () => {
|
|
@@ -566,6 +611,287 @@ describe('formatCliCommand', () => {
|
|
|
566
611
|
process.argv[1] = '/home/u/.npm/_npx/a1b2/node_modules/.bin/astryx';
|
|
567
612
|
const dir = makeTmpDir();
|
|
568
613
|
fs.writeFileSync(path.join(dir, 'package-lock.json'), '{}');
|
|
569
|
-
expect(formatCliCommand('astryx component Button', dir)).toBe(
|
|
614
|
+
expect(formatCliCommand('astryx component Button', dir)).toBe(
|
|
615
|
+
`npx @astryxdesign/cli@${CLI_VERSION} component Button`,
|
|
616
|
+
);
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
describe('the picker: declared cliCommand, then the door, then the guess', () => {
|
|
621
|
+
/** A pnpm project with the CLI installed: the guess is `pnpm exec astryx`. */
|
|
622
|
+
function pnpmProject() {
|
|
623
|
+
process.argv[1] = INSTALLED_ENTRY;
|
|
624
|
+
const dir = makeTmpDir();
|
|
625
|
+
fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"app"}');
|
|
626
|
+
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
627
|
+
linkCliBin(dir);
|
|
628
|
+
return dir;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
it('guesses when nothing is declared', () => {
|
|
632
|
+
const dir = pnpmProject();
|
|
633
|
+
recordDeclaredCliCommands(dir, [{name: '@acme/a'}]);
|
|
634
|
+
expect(getCliInvocation(dir)).toBe('pnpm exec astryx');
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("uses an integration's cliCommand over the guess", () => {
|
|
638
|
+
const dir = pnpmProject();
|
|
639
|
+
recordDeclaredCliCommands(dir, [
|
|
640
|
+
{name: '@acme/a', cliCommand: 'acme astryx'},
|
|
641
|
+
{name: '@acme/b', cliCommand: 'acme astryx'},
|
|
642
|
+
]);
|
|
643
|
+
expect(getCliInvocation(dir)).toBe('acme astryx');
|
|
644
|
+
expect(formatCliCommand('astryx docs tokens', dir)).toBe(
|
|
645
|
+
'acme astryx docs tokens',
|
|
646
|
+
);
|
|
647
|
+
expect(guessCliInvocation(dir)).toBe('pnpm exec astryx');
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
it('falls back to the guess when integrations declare different commands', () => {
|
|
651
|
+
const dir = pnpmProject();
|
|
652
|
+
recordDeclaredCliCommands(dir, [
|
|
653
|
+
{name: '@acme/a', cliCommand: 'acme astryx'},
|
|
654
|
+
{name: '@acme/b', cliCommand: 'other astryx'},
|
|
655
|
+
]);
|
|
656
|
+
expect(getCliInvocation(dir)).toBe('pnpm exec astryx');
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("puts a declared command ahead of a host's door", () => {
|
|
660
|
+
const dir = pnpmProject();
|
|
661
|
+
recordDeclaredCliCommands(dir, [
|
|
662
|
+
{name: '@acme/a', cliCommand: 'acme astryx'},
|
|
663
|
+
]);
|
|
664
|
+
expect(runWithCliDoor('astryx', () => getCliInvocation(dir))).toBe(
|
|
665
|
+
'acme astryx',
|
|
666
|
+
);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it("uses a host's door when nothing is declared", () => {
|
|
670
|
+
const dir = pnpmProject();
|
|
671
|
+
recordDeclaredCliCommands(dir, []);
|
|
672
|
+
expect(runWithCliDoor('astryx', () => getCliInvocation(dir))).toBe(
|
|
673
|
+
'astryx',
|
|
674
|
+
);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('guesses on a conflict even when a host set a door', () => {
|
|
678
|
+
const dir = pnpmProject();
|
|
679
|
+
recordDeclaredCliCommands(dir, [
|
|
680
|
+
{name: '@acme/a', cliCommand: 'acme astryx'},
|
|
681
|
+
{name: '@acme/b', cliCommand: 'other astryx'},
|
|
682
|
+
]);
|
|
683
|
+
expect(runWithCliDoor('astryx', () => getCliInvocation(dir))).toBe(
|
|
684
|
+
'pnpm exec astryx',
|
|
685
|
+
);
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
describe('prefixCliCommands', () => {
|
|
690
|
+
/** @param {string} text @param {string | null} [lang] */
|
|
691
|
+
const shown = (text, lang) => prefixCliCommands(text, 'acme astryx', lang);
|
|
692
|
+
|
|
693
|
+
it('prefixes inline code spans that run the CLI', () => {
|
|
694
|
+
expect(
|
|
695
|
+
shown('Run `astryx docs theme`, then `astryx`. Not `astryx.config.ts`.'),
|
|
696
|
+
).toBe(
|
|
697
|
+
'Run `acme astryx docs theme`, then `acme astryx`. Not `astryx.config.ts`.',
|
|
698
|
+
);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it('prefixes commands in shell fences and leaves code fences alone', () => {
|
|
702
|
+
const text = [
|
|
703
|
+
'```bash',
|
|
704
|
+
'# astryx in a comment stays',
|
|
705
|
+
'astryx doctor && astryx integration verify',
|
|
706
|
+
'$ astryx docs',
|
|
707
|
+
'npm install @astryxdesign/cli',
|
|
708
|
+
'```',
|
|
709
|
+
'```tsx',
|
|
710
|
+
'astryx docs',
|
|
711
|
+
'```',
|
|
712
|
+
].join('\n');
|
|
713
|
+
expect(shown(text).split('\n')).toEqual([
|
|
714
|
+
'```bash',
|
|
715
|
+
'# astryx in a comment stays',
|
|
716
|
+
'acme astryx doctor && acme astryx integration verify',
|
|
717
|
+
'$ acme astryx docs',
|
|
718
|
+
'npm install @astryxdesign/cli',
|
|
719
|
+
'```',
|
|
720
|
+
'```tsx',
|
|
721
|
+
'astryx docs',
|
|
722
|
+
'```',
|
|
723
|
+
]);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it("follows one code block's language", () => {
|
|
727
|
+
expect(shown('astryx docs', 'bash')).toBe('acme astryx docs');
|
|
728
|
+
expect(shown('1. Read `astryx docs cli`.', 'text')).toBe(
|
|
729
|
+
'1. Read `acme astryx docs cli`.',
|
|
730
|
+
);
|
|
731
|
+
expect(shown("const run = 'astryx docs';", 'ts')).toBe(
|
|
732
|
+
"const run = 'astryx docs';",
|
|
733
|
+
);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it('leaves plain prose alone', () => {
|
|
737
|
+
expect(shown('Astryx and astryx themes')).toBe('Astryx and astryx themes');
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
it('changes nothing when the stem is the bare bin', () => {
|
|
741
|
+
expect(prefixCliCommands('`astryx docs`', 'astryx')).toBe('`astryx docs`');
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
describe('quoteCommandArgument', () => {
|
|
746
|
+
it('leaves a plain value bare', () => {
|
|
747
|
+
for (const platform of ['linux', 'darwin', 'win32']) {
|
|
748
|
+
expect(quoteCommandArgument('0.7.1', platform)).toBe('0.7.1');
|
|
749
|
+
expect(quoteCommandArgument('@acme/widgets', platform)).toBe(
|
|
750
|
+
'@acme/widgets',
|
|
751
|
+
);
|
|
752
|
+
expect(quoteCommandArgument('/home/me/app/src', platform)).toBe(
|
|
753
|
+
'/home/me/app/src',
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it('single-quotes for a POSIX shell, so $, backticks and double quotes stay literal', () => {
|
|
759
|
+
expect(quoteCommandArgument('/tmp/my app/src', 'linux')).toBe(
|
|
760
|
+
"'/tmp/my app/src'",
|
|
761
|
+
);
|
|
762
|
+
expect(quoteCommandArgument('/tmp/cost $5/src', 'darwin')).toBe(
|
|
763
|
+
"'/tmp/cost $5/src'",
|
|
764
|
+
);
|
|
765
|
+
expect(quoteCommandArgument('a`id`"b"!', 'linux')).toBe('\'a`id`"b"!\'');
|
|
766
|
+
expect(quoteCommandArgument("it's", 'linux')).toBe("'it'\\''s'");
|
|
767
|
+
expect(quoteCommandArgument('C:\\app', 'linux')).toBe("'C:\\app'");
|
|
768
|
+
expect(quoteCommandArgument('', 'linux')).toBe("''");
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it('keeps a Windows drive path bare', () => {
|
|
772
|
+
expect(quoteCommandArgument('C:\\Users\\me\\app\\src', 'win32')).toBe(
|
|
773
|
+
'C:\\Users\\me\\app\\src',
|
|
774
|
+
);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
// Windows output targets PowerShell, not cmd.exe: PowerShell reads a
|
|
778
|
+
// single-quoted value literally, `$`, backticks, `%` and `!` included.
|
|
779
|
+
it('single-quotes for PowerShell on Windows, doubling an embedded quote', () => {
|
|
780
|
+
expect(quoteCommandArgument('C:\\Users\\Jo Smith\\app\\src', 'win32')).toBe(
|
|
781
|
+
"'C:\\Users\\Jo Smith\\app\\src'",
|
|
782
|
+
);
|
|
783
|
+
expect(quoteCommandArgument('C:\\cost $5 %x% !y\\src', 'win32')).toBe(
|
|
784
|
+
"'C:\\cost $5 %x% !y\\src'",
|
|
785
|
+
);
|
|
786
|
+
expect(quoteCommandArgument('C:\\it\'s `x` "q"', 'win32')).toBe(
|
|
787
|
+
"'C:\\it''s `x` \"q\"'",
|
|
788
|
+
);
|
|
789
|
+
// PowerShell also ends a single-quoted string at ‘ ’ ‚ and ‛.
|
|
790
|
+
expect(
|
|
791
|
+
quoteCommandArgument('C:\\Users\\jo\\OneDrive - Macy’s\\app', 'win32'),
|
|
792
|
+
).toBe("'C:\\Users\\jo\\OneDrive - Macy’’s\\app'");
|
|
793
|
+
expect(quoteCommandArgument('‘a’ ‚b‛', 'win32')).toBe("'‘‘a’’ ‚‚b‛‛'");
|
|
794
|
+
// A POSIX shell reads them literally inside single quotes.
|
|
795
|
+
expect(quoteCommandArgument('/home/jo/Macy’s app', 'linux')).toBe(
|
|
796
|
+
"'/home/jo/Macy’s app'",
|
|
797
|
+
);
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
describe('commandInFolder', () => {
|
|
802
|
+
it('enters the folder, then runs the command only if that worked, in a POSIX shell', () => {
|
|
803
|
+
expect(commandInFolder('/tmp/my app', 'npx x', 'linux')).toBe(
|
|
804
|
+
"cd '/tmp/my app' && npx x",
|
|
805
|
+
);
|
|
806
|
+
expect(commandInFolder('/srv/app', 'npx x', 'darwin')).toBe(
|
|
807
|
+
'cd /srv/app && npx x',
|
|
808
|
+
);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it('enters the folder literally and stops if it cannot, in PowerShell', () => {
|
|
812
|
+
expect(commandInFolder('C:\\a[1] b', 'npx x', 'win32')).toBe(
|
|
813
|
+
"cd -LiteralPath 'C:\\a[1] b' -ErrorAction Stop; npx x",
|
|
814
|
+
);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it.skipIf(process.platform === 'win32')(
|
|
818
|
+
'runs in the folder with every argument unchanged, pasted into /bin/sh elsewhere',
|
|
819
|
+
() => {
|
|
820
|
+
const root = makeTmpDir();
|
|
821
|
+
const project = path.join(root, 'it\'s Macy’s ‘q’ $5 %x% !y `z` "q" [1]');
|
|
822
|
+
fs.mkdirSync(project);
|
|
823
|
+
const args = [path.join(project, 'src'), 'a$b c', "it's", '@scope/pkg@1'];
|
|
824
|
+
const command = commandInFolder(
|
|
825
|
+
project,
|
|
826
|
+
['npx', ...args.map(arg => quoteCommandArgument(arg, 'linux'))].join(
|
|
827
|
+
' ',
|
|
828
|
+
),
|
|
829
|
+
'linux',
|
|
830
|
+
);
|
|
831
|
+
const ran = spawnSync(
|
|
832
|
+
'/bin/sh',
|
|
833
|
+
[
|
|
834
|
+
'-c',
|
|
835
|
+
`npx() { "$0" -e '${PRINT_RUN}' -- "$@"; }; ${command}`,
|
|
836
|
+
process.execPath,
|
|
837
|
+
],
|
|
838
|
+
{cwd: root, encoding: 'utf8'},
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
expect(ran.status, ran.stderr).toBe(0);
|
|
842
|
+
expect(JSON.parse(ran.stdout)).toEqual({
|
|
843
|
+
cwd: fs.realpathSync(project),
|
|
844
|
+
argv: args,
|
|
845
|
+
});
|
|
846
|
+
},
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
describe.runIf(PWSH)('in PowerShell, where it is installed', () => {
|
|
850
|
+
it('runs in the folder with every argument unchanged', () => {
|
|
851
|
+
const root = makeTmpDir();
|
|
852
|
+
const project = path.join(root, 'it\'s Macy’s ‘q’ $5 %x% !y `z` "q" [1]');
|
|
853
|
+
fs.mkdirSync(project);
|
|
854
|
+
const args = [path.join(project, 'src'), 'a$b c', "it's", '@scope/pkg@1'];
|
|
855
|
+
const command = commandInFolder(
|
|
856
|
+
project,
|
|
857
|
+
['npx', ...args.map(arg => quoteCommandArgument(arg, 'win32'))].join(
|
|
858
|
+
' ',
|
|
859
|
+
),
|
|
860
|
+
'win32',
|
|
861
|
+
);
|
|
862
|
+
const npx = `function npx { & ${quoteCommandArgument(process.execPath, 'win32')} -e '${PRINT_RUN}' -- @args }`;
|
|
863
|
+
const ran = spawnSync(
|
|
864
|
+
/** @type {string} */ (PWSH),
|
|
865
|
+
['-NoProfile', '-NonInteractive', '-Command', `${npx}; ${command}`],
|
|
866
|
+
{cwd: root, encoding: 'utf8'},
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
expect(ran.status, ran.stderr).toBe(0);
|
|
870
|
+
expect(JSON.parse(ran.stdout)).toEqual({
|
|
871
|
+
cwd: fs.realpathSync(project),
|
|
872
|
+
argv: args,
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('stops when the folder cannot be entered', () => {
|
|
877
|
+
const root = makeTmpDir();
|
|
878
|
+
const command = commandInFolder(
|
|
879
|
+
path.join(root, 'missing'),
|
|
880
|
+
'npx',
|
|
881
|
+
'win32',
|
|
882
|
+
);
|
|
883
|
+
const ran = spawnSync(
|
|
884
|
+
/** @type {string} */ (PWSH),
|
|
885
|
+
[
|
|
886
|
+
'-NoProfile',
|
|
887
|
+
'-NonInteractive',
|
|
888
|
+
'-Command',
|
|
889
|
+
`function npx { 'RAN' }; ${command}`,
|
|
890
|
+
],
|
|
891
|
+
{cwd: root, encoding: 'utf8'},
|
|
892
|
+
);
|
|
893
|
+
|
|
894
|
+
expect(ran.stdout).not.toContain('RAN');
|
|
895
|
+
});
|
|
570
896
|
});
|
|
571
897
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* True when `cwd` is outside any project.
|
|
6
|
+
* @param {string} [cwd]
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function isOutsideProject(cwd?: string): boolean;
|