@astryxdesign/cli 0.7.2-canary.fe2c655 → 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 +175 -154
- 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/help/help.mjs +1 -1
- package/api/build/kit/kit.mjs +3 -11
- package/api/component/_adapter.d.mts +72 -1
- package/api/component/_adapter.mjs +212 -18
- package/api/component/component.doc.mjs +6 -1
- package/api/component/component.mjs +82 -15
- package/api/component/component.test.mjs +80 -0
- package/api/component/doc-links.test.mjs +386 -0
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- 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 +74 -0
- package/api/docs/_adapter.mjs +553 -16
- 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/integrationDocs.test.mjs +124 -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 +17 -4
- 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 +219 -52
- 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/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- 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 +17 -6
- 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 +45 -0
- package/api/integration/_adapter.mjs +112 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +110 -6
- package/api/integration/authoring-checks.test.mjs +808 -0
- package/api/integration/authoring-checks.type.d.mts +143 -0
- package/api/integration/authoring-checks.type.mjs +74 -2
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
- package/api/integration/integrationDocConflicts.doc.mjs +22 -5
- package/api/integration/pack-check.mjs +364 -120
- package/api/integration/pack-check.test.mjs +730 -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 +63 -9
- package/api/integration/validate-integration.test.mjs +88 -8
- package/api/integration/validate-integration.type.d.mts +16 -0
- package/api/integration/validate-integration.type.mjs +14 -1
- package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
- package/api/integration/validateIntegration.doc.mjs +19 -3
- package/api/search/_adapter.d.mts +11 -0
- package/api/search/_adapter.mjs +25 -0
- package/api/search/search.d.mts +82 -13
- package/api/search/search.doc.mjs +8 -3
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +645 -64
- package/api/search/search.test.mjs +179 -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 +29 -15
- package/api/template/template-integration.test.mjs +1 -1
- package/api/template/template.test.mjs +120 -3
- 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.family.test.mjs +64 -0
- package/api/theme/build/build.mjs +250 -38
- 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/themeBuild.doc.mjs +4 -4
- 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/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
- package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
- package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
- package/assets/discover/official-packages.json +367 -0
- package/assets/docs/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +3 -3
- package/assets/docs/browser-support.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +26 -6
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +2 -2
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +22 -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 +8 -20
- 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 +201 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
- package/assets/docs/tree/checks.doc.mjs +32 -24
- 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 +8 -6
- package/assets/docs/tree/component-imports.doc.mjs +21 -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 +3 -3
- 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 +7 -6
- 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 +4 -8
- 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 +53 -27
- 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 +10 -12
- 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 +15 -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 +29 -13
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- 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 +10 -9
- package/assets/docs/tree/who-answers.doc.mjs +119 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +66 -13
- package/assets/theme.template.ts +1 -1
- 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 +30 -27
- package/authoring/doctypes/component/type.ts +21 -8
- 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/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/template/type.ts +22 -0
- 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 +2 -0
- package/authoring/integration/integration.doc.mjs +30 -6
- package/authoring/integration/parse.test.mjs +21 -0
- package/authoring/integration/schema.d.mts +16 -3
- package/authoring/integration/schema.mjs +34 -2
- package/authoring/integration/type.ts +12 -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 +17 -3
- package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
- package/clients/cli/commands/build-theme.mjs +26 -13
- package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- 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 +19 -4
- 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 +22 -2
- package/clients/cli/commands/doctor-integration.test.mjs +888 -3
- package/clients/cli/commands/doctor.doc.mjs +12 -2
- package/clients/cli/commands/doctor.mjs +292 -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 +93 -0
- package/clients/cli/commands/integration-check.test.mjs +441 -0
- 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.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +119 -7
- 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/text-json-parity.test.mjs +5 -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 +3 -2
- 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 +112 -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 +226 -7
- package/foundation/config/project.test.mjs +66 -0
- package/foundation/config/theme-state.mjs +5 -4
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/cli-self-docs.mjs +20 -3
- package/foundation/discovery/cli-self-docs.test.mjs +3 -2
- package/foundation/discovery/component-discovery.d.mts +190 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +864 -56
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +20 -1
- package/foundation/discovery/component-replacement.test.mjs +40 -0
- 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 +157 -0
- package/foundation/discovery/doc-types.mjs +1231 -0
- package/foundation/discovery/doc-types.test.mjs +744 -0
- package/foundation/discovery/docs-discovery.d.mts +88 -0
- package/foundation/discovery/docs-discovery.mjs +83 -2
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -0
- package/foundation/discovery/module-exports.d.mts +27 -0
- package/foundation/discovery/module-exports.mjs +356 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +32 -0
- package/foundation/discovery/project-typescript.mjs +89 -0
- package/foundation/discovery/prop-drift.d.mts +297 -0
- package/foundation/discovery/prop-drift.mjs +2428 -0
- package/foundation/discovery/prop-drift.test.mjs +2337 -0
- 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-adapter.mjs +74 -5
- 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-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
- package/foundation/doc-compiler/inputs.mjs +27 -1
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/links.d.mts +41 -5
- package/foundation/doc-compiler/links.mjs +167 -28
- 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 +56 -3
- 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 +22 -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/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +11 -11
- package/scripts/postinstall.mjs +24 -5
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
PASSAGE_WORDS,
|
|
7
|
+
contentWords,
|
|
8
|
+
passageWords,
|
|
9
|
+
wordSpans,
|
|
10
|
+
proseWords,
|
|
11
|
+
sharedPassages,
|
|
12
|
+
} from './copied-passages.mjs';
|
|
13
|
+
|
|
14
|
+
const words = (/** @type {string} */ text) =>
|
|
15
|
+
passageWords(text).map(word => ({word, section: null, block: 1}));
|
|
16
|
+
|
|
17
|
+
describe('passageWords', () => {
|
|
18
|
+
it('sets aside case, links, and Markdown emphasis', () => {
|
|
19
|
+
expect(
|
|
20
|
+
passageWords('Use **`Button`** for {@link component:Button} actions.'),
|
|
21
|
+
).toEqual(['use', 'button', 'for', 'actions.']);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('proseWords', () => {
|
|
26
|
+
it('reads prose with its section, and skips code, references, and names', () => {
|
|
27
|
+
const doc = {
|
|
28
|
+
type: 'generic',
|
|
29
|
+
name: 'acme-guide',
|
|
30
|
+
title: 'Acme guide',
|
|
31
|
+
description: 'Intro words.',
|
|
32
|
+
sections: [
|
|
33
|
+
{
|
|
34
|
+
title: 'Setup',
|
|
35
|
+
content: [
|
|
36
|
+
{type: 'prose', text: 'Install it.'},
|
|
37
|
+
{type: 'code', lang: 'sh', code: 'npm install acme'},
|
|
38
|
+
{type: 'reference', ref: 'generic:other'},
|
|
39
|
+
{type: 'list', items: ['First step.']},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
expect(proseWords(doc)).toEqual([
|
|
45
|
+
{word: 'intro', section: null, block: 1},
|
|
46
|
+
{word: 'words.', section: null, block: 1},
|
|
47
|
+
{word: 'install', section: 'Setup', block: 2},
|
|
48
|
+
{word: 'it.', section: 'Setup', block: 2},
|
|
49
|
+
{word: 'first', section: 'Setup', block: 3},
|
|
50
|
+
{word: 'step.', section: 'Setup', block: 3},
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('sharedPassages', () => {
|
|
56
|
+
const passage = Array.from(
|
|
57
|
+
{length: PASSAGE_WORDS + 5},
|
|
58
|
+
(_, i) => `w${i}`,
|
|
59
|
+
).join(' ');
|
|
60
|
+
|
|
61
|
+
it('finds the longest shared run', () => {
|
|
62
|
+
expect(
|
|
63
|
+
sharedPassages(words(`a b ${passage} c`), words(`x ${passage} y`)),
|
|
64
|
+
).toEqual([{start: 2, length: PASSAGE_WORDS + 5}]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('never runs a passage from one string into the next', () => {
|
|
68
|
+
const half = Math.ceil(PASSAGE_WORDS / 2);
|
|
69
|
+
const first = passage.split(' ').slice(0, half).join(' ');
|
|
70
|
+
const second = passage.split(' ').slice(half, PASSAGE_WORDS).join(' ');
|
|
71
|
+
const split = [
|
|
72
|
+
...passageWords(first).map(word => ({word, section: null, block: 1})),
|
|
73
|
+
...passageWords(second).map(word => ({word, section: null, block: 2})),
|
|
74
|
+
];
|
|
75
|
+
expect(sharedPassages(split, words(`${first} ${second}`))).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('ignores a run shorter than the passage size', () => {
|
|
79
|
+
const short = passage
|
|
80
|
+
.split(' ')
|
|
81
|
+
.slice(0, PASSAGE_WORDS - 1)
|
|
82
|
+
.join(' ');
|
|
83
|
+
expect(
|
|
84
|
+
sharedPassages(words(`a ${short} b`), words(`c ${short} d`)),
|
|
85
|
+
).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('contentWords', () => {
|
|
90
|
+
it('counts distinct words that are not function words', () => {
|
|
91
|
+
const run = words('and it is in the way that you can do it for them');
|
|
92
|
+
expect(contentWords(run, 0, run.length)).toBe(1);
|
|
93
|
+
const fact = words('Name each codemod folder after the Core version.');
|
|
94
|
+
expect(contentWords(fact, 0, fact.length)).toBe(5);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('wordSpans', () => {
|
|
99
|
+
it('reads the words passageWords reads, with where each sits as written', () => {
|
|
100
|
+
const text = 'Run `astryx docs`, then {@link generic:theme} *now*.';
|
|
101
|
+
const spans = wordSpans(text);
|
|
102
|
+
expect(spans.map(span => span.word)).toEqual(passageWords(text));
|
|
103
|
+
expect(text.slice(spans[1].start, spans[2].end)).toBe('`astryx docs`');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('passageWords and pointers', () => {
|
|
108
|
+
it('reads a link and a written docs command as no words', () => {
|
|
109
|
+
expect(
|
|
110
|
+
passageWords(
|
|
111
|
+
'See {@link generic:theme} or `astryx docs theme` (Share it).',
|
|
112
|
+
),
|
|
113
|
+
).toEqual(['see', 'or', 'share', 'it']);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('counts no punctuation-only token as a word', () => {
|
|
117
|
+
expect(
|
|
118
|
+
passageWords('Set the theme first, see {@link generic:theme}.'),
|
|
119
|
+
).toEqual(['set', 'the', 'theme', 'first', 'see']);
|
|
120
|
+
expect(
|
|
121
|
+
wordSpans('Read it - then {@link generic:theme}.').map(s => s.word),
|
|
122
|
+
).toEqual(['read', 'it', 'then']);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
* Problems in one component doc module.
|
|
6
|
+
* @param {Record<string, any>} mod
|
|
7
|
+
* @param {string} file relative to the package
|
|
8
|
+
* @param {string} [text] the file's source, to locate each finding's line
|
|
9
|
+
* @returns {Issue[]}
|
|
10
|
+
*/
|
|
11
|
+
export function componentDocProblems(mod: Record<string, any>, file: string, text?: string): Issue[];
|
|
12
|
+
/**
|
|
13
|
+
* Problems in one topic doc.
|
|
14
|
+
* @param {any} doc
|
|
15
|
+
* @param {string} file relative to the package
|
|
16
|
+
* @param {string} [text] the file's source, to locate each finding's line
|
|
17
|
+
* @returns {Issue[]}
|
|
18
|
+
*/
|
|
19
|
+
export function topicDocProblems(doc: any, file: string, text?: string): Issue[];
|
|
20
|
+
/**
|
|
21
|
+
* Check an integration's component docs and topics.
|
|
22
|
+
* @param {{components?: string, docs?: string}} integration absolute roots
|
|
23
|
+
* @param {string} packageDir
|
|
24
|
+
* @returns {Promise<Issue[]>}
|
|
25
|
+
*/
|
|
26
|
+
export function integrationDocConsistency(integration: {
|
|
27
|
+
components?: string;
|
|
28
|
+
docs?: string;
|
|
29
|
+
}, packageDir: string): Promise<Issue[]>;
|
|
30
|
+
export type Issue = import("../integrations/issue").AstryxIntegrationIssue;
|
|
31
|
+
/**
|
|
32
|
+
* The codes this check raises.
|
|
33
|
+
*/
|
|
34
|
+
export type DocConsistencyProblem = "duplicate_prop" | "unknown_translated_prop" | "empty_section" | "duplicate_section_key" | "orphan_doc_overlay";
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file What an integration's docs say that no reader can serve, read from
|
|
5
|
+
* the docs alone, with no compiler.
|
|
6
|
+
*
|
|
7
|
+
* Component docs:
|
|
8
|
+
* - `duplicate_prop`: a prop table lists one prop twice, so a reader sees it
|
|
9
|
+
* twice and the two entries can disagree;
|
|
10
|
+
* - `unknown_translated_prop`: a `docsZh`/`docsDense` overlay's
|
|
11
|
+
* `propDescriptions` key matches no prop entry's `name` as written (an
|
|
12
|
+
* overlay applies by the entry's whole name, `value / onChange` included),
|
|
13
|
+
* so its text is dropped when the overlay is laid over the doc.
|
|
14
|
+
*
|
|
15
|
+
* Topics (the docs root):
|
|
16
|
+
* - `empty_section`: a section with no content, which reads as a bare title;
|
|
17
|
+
* - `duplicate_section_key`: two sections of one topic whose keys (their
|
|
18
|
+
* `id`, else the key their title derives) collide, so the later one is
|
|
19
|
+
* served only at a suffixed key nobody wrote; the fix names that key;
|
|
20
|
+
* - `orphan_doc_overlay`: a `{topic}.doc.zh.mjs` or `{topic}.doc.dense.mjs`
|
|
21
|
+
* file beside no `{topic}.doc.mjs`, which ships and is never read.
|
|
22
|
+
*
|
|
23
|
+
* Every finding is a warning with a fix ready to paste.
|
|
24
|
+
*
|
|
25
|
+
* @input A loaded integration (its roots) and the package directory.
|
|
26
|
+
* @output AstryxIntegrationIssue warnings, each with a `fix` and a `file`.
|
|
27
|
+
* @position Run by `astryx doctor integration validate`, so
|
|
28
|
+
* `astryx integration check --strict` fails on them.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import {sectionKeyProblems, withSectionKeys} from './docs-section-key.mjs';
|
|
34
|
+
import {importDocModule} from '../doc-compiler/import.mjs';
|
|
35
|
+
import {OVERLAY_LANGUAGES} from '../doc-compiler/read.mjs';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {import('../integrations/issue').AstryxIntegrationIssue} Issue
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The codes this check raises.
|
|
43
|
+
* @typedef {'duplicate_prop' | 'unknown_translated_prop' | 'empty_section' | 'duplicate_section_key' | 'orphan_doc_overlay'} DocConsistencyProblem
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const TOPIC_SUFFIXES = ['.doc.ts', '.doc.mjs', '.doc.js'];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {DocConsistencyProblem} code
|
|
50
|
+
* @param {string} message
|
|
51
|
+
* @param {string} fix
|
|
52
|
+
* @param {string} file relative to the package
|
|
53
|
+
* @param {number} [line] 1-based, where the check can locate it
|
|
54
|
+
* @returns {Issue}
|
|
55
|
+
*/
|
|
56
|
+
function finding(code, message, fix, file, line) {
|
|
57
|
+
return {
|
|
58
|
+
code,
|
|
59
|
+
severity: 'warning',
|
|
60
|
+
message,
|
|
61
|
+
fix,
|
|
62
|
+
file,
|
|
63
|
+
...(line != null ? {line} : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The 1-based line of the `nth` match of `pattern` in `text` at or after
|
|
69
|
+
* `from`, or undefined.
|
|
70
|
+
* @param {string} text
|
|
71
|
+
* @param {RegExp} pattern a global pattern
|
|
72
|
+
* @param {number} [nth]
|
|
73
|
+
* @param {number} [from]
|
|
74
|
+
* @returns {number | undefined}
|
|
75
|
+
*/
|
|
76
|
+
function lineOf(text, pattern, nth = 0, from = 0) {
|
|
77
|
+
if (!text) return undefined;
|
|
78
|
+
const matches = [...text.slice(from).matchAll(pattern)];
|
|
79
|
+
const match = matches[Math.min(nth, matches.length - 1)];
|
|
80
|
+
if (match?.index == null) return undefined;
|
|
81
|
+
return text.slice(0, from + match.index).split('\n').length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @param {string} file */
|
|
85
|
+
function readText(file) {
|
|
86
|
+
try {
|
|
87
|
+
return fs.readFileSync(file, 'utf8');
|
|
88
|
+
} catch {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @param {string} value */
|
|
94
|
+
function escapeRe(value) {
|
|
95
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* A pattern for `name: '<value>'` in any quote style.
|
|
100
|
+
* @param {string} value
|
|
101
|
+
*/
|
|
102
|
+
function namePattern(value) {
|
|
103
|
+
return new RegExp(`\\bname['"]?\\s*:\\s*(['"\`])${escapeRe(value)}\\1`, 'g');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The names one prop entry documents: `a / b` names two.
|
|
108
|
+
* @param {any} prop
|
|
109
|
+
* @returns {string[]}
|
|
110
|
+
*/
|
|
111
|
+
function namesOf(prop) {
|
|
112
|
+
if (!prop || typeof prop.name !== 'string') return [];
|
|
113
|
+
return prop.name
|
|
114
|
+
.split(/\s+\/\s+/)
|
|
115
|
+
.map((/** @type {string} */ name) => name.trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Each prop table a component doc holds: its own `props`, and each
|
|
121
|
+
* `components` entry's, with where it sits.
|
|
122
|
+
* @param {any} doc
|
|
123
|
+
* @returns {Array<{at: string, owner: string, props: any[]}>}
|
|
124
|
+
*/
|
|
125
|
+
function propTablesOf(doc) {
|
|
126
|
+
/** @type {Array<{at: string, owner: string, props: any[]}>} */
|
|
127
|
+
const tables = [];
|
|
128
|
+
if (!doc || typeof doc !== 'object') return tables;
|
|
129
|
+
if (Array.isArray(doc.props)) {
|
|
130
|
+
tables.push({at: 'props', owner: String(doc.name ?? ''), props: doc.props});
|
|
131
|
+
}
|
|
132
|
+
if (Array.isArray(doc.components)) {
|
|
133
|
+
doc.components.forEach(
|
|
134
|
+
(/** @type {any} */ entry, /** @type {number} */ i) => {
|
|
135
|
+
if (entry && Array.isArray(entry.props)) {
|
|
136
|
+
tables.push({
|
|
137
|
+
at: `components[${i}].props`,
|
|
138
|
+
owner: String(entry.name ?? ''),
|
|
139
|
+
props: entry.props,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return tables;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The closest name to `name` among `names`, when one is near enough to be a
|
|
150
|
+
* typo of it.
|
|
151
|
+
* @param {string} name
|
|
152
|
+
* @param {Iterable<string>} names
|
|
153
|
+
* @returns {string | undefined}
|
|
154
|
+
*/
|
|
155
|
+
function closest(name, names) {
|
|
156
|
+
let best;
|
|
157
|
+
let bestDistance = Infinity;
|
|
158
|
+
for (const candidate of names) {
|
|
159
|
+
const distance = editDistance(name.toLowerCase(), candidate.toLowerCase());
|
|
160
|
+
if (distance < bestDistance) {
|
|
161
|
+
best = candidate;
|
|
162
|
+
bestDistance = distance;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return best != null &&
|
|
166
|
+
bestDistance <= Math.max(2, Math.floor(name.length / 3))
|
|
167
|
+
? best
|
|
168
|
+
: undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @param {string} a @param {string} b */
|
|
172
|
+
function editDistance(a, b) {
|
|
173
|
+
const row = Array.from({length: b.length + 1}, (_, j) => j);
|
|
174
|
+
for (let i = 1; i <= a.length; i++) {
|
|
175
|
+
let previous = row[0];
|
|
176
|
+
row[0] = i;
|
|
177
|
+
for (let j = 1; j <= b.length; j++) {
|
|
178
|
+
const kept = row[j];
|
|
179
|
+
row[j] = Math.min(
|
|
180
|
+
row[j] + 1,
|
|
181
|
+
row[j - 1] + 1,
|
|
182
|
+
previous + (a[i - 1] === b[j - 1] ? 0 : 1),
|
|
183
|
+
);
|
|
184
|
+
previous = kept;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return row[b.length];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Problems in one component doc module.
|
|
192
|
+
* @param {Record<string, any>} mod
|
|
193
|
+
* @param {string} file relative to the package
|
|
194
|
+
* @param {string} [text] the file's source, to locate each finding's line
|
|
195
|
+
* @returns {Issue[]}
|
|
196
|
+
*/
|
|
197
|
+
export function componentDocProblems(mod, file, text = '') {
|
|
198
|
+
/** @type {Issue[]} */
|
|
199
|
+
const issues = [];
|
|
200
|
+
const doc = mod?.default ?? mod?.docs;
|
|
201
|
+
if (!doc || typeof doc !== 'object') return issues;
|
|
202
|
+
const tables = propTablesOf(doc);
|
|
203
|
+
|
|
204
|
+
// A prop documented twice: doc structure the reader silently drops
|
|
205
|
+
// (architecture:cli-surface DEC-8).
|
|
206
|
+
/** @type {Map<string, number>} entries of each name seen so far, file-wide */
|
|
207
|
+
const seenNames = new Map();
|
|
208
|
+
for (const {at, owner, props} of tables) {
|
|
209
|
+
/** @type {Map<string, number>} */
|
|
210
|
+
const first = new Map();
|
|
211
|
+
props.forEach((/** @type {any} */ prop, /** @type {number} */ i) => {
|
|
212
|
+
for (const name of namesOf(prop)) {
|
|
213
|
+
const whole = typeof prop.name === 'string' ? prop.name : name;
|
|
214
|
+
const nth = seenNames.get(whole) ?? 0;
|
|
215
|
+
seenNames.set(whole, nth + 1);
|
|
216
|
+
const earlier = first.get(name);
|
|
217
|
+
if (earlier == null) {
|
|
218
|
+
first.set(name, i);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
issues.push(
|
|
222
|
+
finding(
|
|
223
|
+
'duplicate_prop',
|
|
224
|
+
`${owner ? `${owner}'s ` : ''}prop "${name}" is documented twice: ${at}[${earlier}] and ${at}[${i}].`,
|
|
225
|
+
`In ${file}, delete ${at}[${i}] (the second "${name}" entry), keeping what it adds in ${at}[${earlier}].`,
|
|
226
|
+
file,
|
|
227
|
+
lineOf(text, namePattern(whole), nth),
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// An overlay key that names no prop entry: doc structure the reader
|
|
235
|
+
// silently drops (architecture:cli-surface DEC-8).
|
|
236
|
+
for (const exportName of ['docsZh', 'docsDense']) {
|
|
237
|
+
const overlay = mod?.[exportName];
|
|
238
|
+
if (!overlay || typeof overlay !== 'object') continue;
|
|
239
|
+
/** @type {Array<{at: string, descriptions: unknown, props: any[] | undefined, owner: string}>} */
|
|
240
|
+
const checks = [
|
|
241
|
+
{
|
|
242
|
+
at: `${exportName}.propDescriptions`,
|
|
243
|
+
descriptions: overlay.propDescriptions,
|
|
244
|
+
props: Array.isArray(doc.props) ? doc.props : undefined,
|
|
245
|
+
owner: String(doc.name ?? ''),
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
if (Array.isArray(overlay.components) && Array.isArray(doc.components)) {
|
|
249
|
+
overlay.components.forEach(
|
|
250
|
+
(/** @type {any} */ entry, /** @type {number} */ i) => {
|
|
251
|
+
if (!entry || typeof entry !== 'object') return;
|
|
252
|
+
const base =
|
|
253
|
+
doc.components.find(
|
|
254
|
+
(/** @type {any} */ c) => c?.name === entry.name,
|
|
255
|
+
) ?? doc.components[i];
|
|
256
|
+
checks.push({
|
|
257
|
+
at: `${exportName}.components[${i}].propDescriptions`,
|
|
258
|
+
descriptions: entry.propDescriptions,
|
|
259
|
+
props: Array.isArray(base?.props) ? base.props : undefined,
|
|
260
|
+
owner: String(base?.name ?? entry.name ?? ''),
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
const exportAt = Math.max(
|
|
266
|
+
0,
|
|
267
|
+
text.search(new RegExp(`\\b${exportName}\\b\\s*=`)),
|
|
268
|
+
);
|
|
269
|
+
for (const {at, descriptions, props, owner} of checks) {
|
|
270
|
+
if (!props || !descriptions || typeof descriptions !== 'object') continue;
|
|
271
|
+
// An overlay applies by the entry's whole name, as written.
|
|
272
|
+
const known = new Set(
|
|
273
|
+
props
|
|
274
|
+
.map(prop => (typeof prop?.name === 'string' ? prop.name : null))
|
|
275
|
+
.filter(name => name != null),
|
|
276
|
+
);
|
|
277
|
+
/** @type {Map<string, string>} each name an entry documents, to its whole name */
|
|
278
|
+
const wholeOf = new Map();
|
|
279
|
+
for (const prop of props) {
|
|
280
|
+
for (const each of namesOf(prop)) wholeOf.set(each, prop.name);
|
|
281
|
+
}
|
|
282
|
+
for (const key of Object.keys(descriptions)) {
|
|
283
|
+
if (known.has(key)) continue;
|
|
284
|
+
const part = closest(key, [...wholeOf.keys(), ...known]);
|
|
285
|
+
const near = part == null ? undefined : (wholeOf.get(part) ?? part);
|
|
286
|
+
issues.push(
|
|
287
|
+
finding(
|
|
288
|
+
'unknown_translated_prop',
|
|
289
|
+
`${at} names "${key}", but no prop entry of ${owner || 'the doc'} is named "${key}" (an overlay matches an entry's whole name), so that text is never shown.`,
|
|
290
|
+
near
|
|
291
|
+
? `In ${file}, rename the key "${key}" in ${at} to "${near}".`
|
|
292
|
+
: `In ${file}, remove "${key}" from ${at}, or document the prop "${key}" first.`,
|
|
293
|
+
file,
|
|
294
|
+
lineOf(
|
|
295
|
+
text,
|
|
296
|
+
new RegExp(`(['"\`]?)${escapeRe(key)}\\1\\s*:`, 'g'),
|
|
297
|
+
0,
|
|
298
|
+
exportAt,
|
|
299
|
+
),
|
|
300
|
+
),
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return issues;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Every file under a folder whose name passes `test`, skipping
|
|
310
|
+
* `node_modules`, `.git` and `__tests__`.
|
|
311
|
+
* @param {string} dir
|
|
312
|
+
* @param {(name: string) => boolean} test
|
|
313
|
+
* @returns {string[]}
|
|
314
|
+
*/
|
|
315
|
+
function filesUnder(dir, test) {
|
|
316
|
+
/** @type {string[]} */
|
|
317
|
+
const found = [];
|
|
318
|
+
/** @param {string} folder */
|
|
319
|
+
const walk = folder => {
|
|
320
|
+
/** @type {fs.Dirent[]} */
|
|
321
|
+
let entries;
|
|
322
|
+
try {
|
|
323
|
+
entries = fs.readdirSync(folder, {withFileTypes: true});
|
|
324
|
+
} catch {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
for (const entry of entries) {
|
|
328
|
+
if (['node_modules', '.git', '__tests__'].includes(entry.name)) continue;
|
|
329
|
+
const full = path.join(folder, entry.name);
|
|
330
|
+
if (entry.isDirectory()) walk(full);
|
|
331
|
+
else if (test(entry.name)) found.push(full);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
walk(dir);
|
|
335
|
+
return found.sort();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Problems in one topic doc.
|
|
340
|
+
* @param {any} doc
|
|
341
|
+
* @param {string} file relative to the package
|
|
342
|
+
* @param {string} [text] the file's source, to locate each finding's line
|
|
343
|
+
* @returns {Issue[]}
|
|
344
|
+
*/
|
|
345
|
+
export function topicDocProblems(doc, file, text = '') {
|
|
346
|
+
/** @type {Issue[]} */
|
|
347
|
+
const issues = [];
|
|
348
|
+
if (!doc || typeof doc !== 'object' || doc.type === 'namespace') {
|
|
349
|
+
return issues;
|
|
350
|
+
}
|
|
351
|
+
const name = String(doc.name ?? '');
|
|
352
|
+
const sections = Array.isArray(doc.sections) ? doc.sections : [];
|
|
353
|
+
// A section with no content is fluff, the limiting case of the density
|
|
354
|
+
// floor (architecture:cli-surface DEC-8).
|
|
355
|
+
let empties = 0;
|
|
356
|
+
sections.forEach((/** @type {any} */ section, /** @type {number} */ i) => {
|
|
357
|
+
if (Array.isArray(section?.content) && section.content.length === 0) {
|
|
358
|
+
const nth = empties++;
|
|
359
|
+
const title =
|
|
360
|
+
typeof section.title === 'string' ? ` "${section.title}"` : '';
|
|
361
|
+
issues.push(
|
|
362
|
+
finding(
|
|
363
|
+
'empty_section',
|
|
364
|
+
`Topic "${name}" section${title} (sections[${i}]) has no content, so it reads as a bare title.`,
|
|
365
|
+
`In ${file}, give sections[${i}] content, such as content: [{type: 'prose', text: '…'}], or delete the section.`,
|
|
366
|
+
file,
|
|
367
|
+
lineOf(text, /\bcontent['"]?\s*:\s*\[\s*\]/g, nth),
|
|
368
|
+
),
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// Two sections with one key: doc structure the reader silently drops
|
|
373
|
+
// (architecture:cli-surface DEC-8).
|
|
374
|
+
for (const problem of sectionKeyProblems(sections)) {
|
|
375
|
+
const collision =
|
|
376
|
+
/^sections\[(\d+)\]: the key "([^"]+)" is already used by sections\[(\d+)\]/.exec(
|
|
377
|
+
problem,
|
|
378
|
+
);
|
|
379
|
+
if (!collision) continue;
|
|
380
|
+
const [, later, key, earlier] = collision;
|
|
381
|
+
// A later section that writes its own `id` is an invalid doc, which docs
|
|
382
|
+
// discovery reports (sectionKeyErrors); only a derived key is served at a
|
|
383
|
+
// suffix.
|
|
384
|
+
if (sections[Number(later)]?.id != null) continue;
|
|
385
|
+
let served = `${key}-2`;
|
|
386
|
+
try {
|
|
387
|
+
served = withSectionKeys({sections}).sections[Number(later)].id ?? served;
|
|
388
|
+
} catch {
|
|
389
|
+
// Keep the first suffix withSectionKeys tries.
|
|
390
|
+
}
|
|
391
|
+
const laterTitle = sections[Number(later)]?.title;
|
|
392
|
+
const laterLine =
|
|
393
|
+
typeof laterTitle === 'string'
|
|
394
|
+
? lineOf(
|
|
395
|
+
text,
|
|
396
|
+
new RegExp(
|
|
397
|
+
`\\btitle['"]?\\s*:\\s*(['"\`])${escapeRe(laterTitle)}\\1`,
|
|
398
|
+
'g',
|
|
399
|
+
),
|
|
400
|
+
sections
|
|
401
|
+
.slice(0, Number(later))
|
|
402
|
+
.filter((/** @type {any} */ each) => each?.title === laterTitle)
|
|
403
|
+
.length,
|
|
404
|
+
)
|
|
405
|
+
: undefined;
|
|
406
|
+
const titleOf = (/** @type {string} */ i) =>
|
|
407
|
+
typeof sections[Number(i)]?.title === 'string'
|
|
408
|
+
? ` "${sections[Number(i)].title}"`
|
|
409
|
+
: '';
|
|
410
|
+
issues.push(
|
|
411
|
+
finding(
|
|
412
|
+
'duplicate_section_key',
|
|
413
|
+
`Topic "${name}": sections[${earlier}]${titleOf(earlier)} and sections[${later}]${titleOf(later)} both have the key "${key}", so \`astryx docs ${name} ${key}\` opens only sections[${earlier}]; sections[${later}] is served at "${served}", a key no one wrote.`,
|
|
414
|
+
`In ${file}, give sections[${later}] its own title, or write the key it is already served at: id: '${served}'.`,
|
|
415
|
+
file,
|
|
416
|
+
laterLine,
|
|
417
|
+
),
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
return issues;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Check an integration's component docs and topics.
|
|
425
|
+
* @param {{components?: string, docs?: string}} integration absolute roots
|
|
426
|
+
* @param {string} packageDir
|
|
427
|
+
* @returns {Promise<Issue[]>}
|
|
428
|
+
*/
|
|
429
|
+
export async function integrationDocConsistency(integration, packageDir) {
|
|
430
|
+
/** @type {Issue[]} */
|
|
431
|
+
const issues = [];
|
|
432
|
+
const rel = (/** @type {string} */ file) =>
|
|
433
|
+
path.relative(packageDir, file).split(path.sep).join('/');
|
|
434
|
+
|
|
435
|
+
/** @type {string[]} */
|
|
436
|
+
const componentDocs = [];
|
|
437
|
+
if (integration.components) {
|
|
438
|
+
componentDocs.push(
|
|
439
|
+
...filesUnder(integration.components, name =>
|
|
440
|
+
TOPIC_SUFFIXES.some(suffix => name.endsWith(suffix)),
|
|
441
|
+
),
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
for (const docPath of componentDocs.sort()) {
|
|
445
|
+
let mod;
|
|
446
|
+
try {
|
|
447
|
+
mod = await importDocModule(docPath);
|
|
448
|
+
} catch {
|
|
449
|
+
// A doc that does not load is reported by the checks that load it.
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
issues.push(...componentDocProblems(mod, rel(docPath), readText(docPath)));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const docsDir = integration.docs;
|
|
456
|
+
if (docsDir && fs.existsSync(docsDir)) {
|
|
457
|
+
const topicFiles = filesUnder(docsDir, name =>
|
|
458
|
+
TOPIC_SUFFIXES.some(suffix => name.endsWith(suffix)),
|
|
459
|
+
);
|
|
460
|
+
/** @type {Array<{file: string, doc: any}>} */
|
|
461
|
+
const topics = [];
|
|
462
|
+
for (const file of topicFiles) {
|
|
463
|
+
try {
|
|
464
|
+
const mod = await importDocModule(file);
|
|
465
|
+
topics.push({file, doc: mod?.docs ?? mod?.default});
|
|
466
|
+
} catch {
|
|
467
|
+
// Reported by docs discovery.
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const {file, doc} of topics) {
|
|
471
|
+
issues.push(...topicDocProblems(doc, rel(file), readText(file)));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// An overlay beside no topic file: doc structure the reader silently drops
|
|
475
|
+
// (architecture:cli-surface DEC-8).
|
|
476
|
+
// An overlay is read beside the topic file of its stem.
|
|
477
|
+
for (const overlay of filesUnder(docsDir, name =>
|
|
478
|
+
OVERLAY_LANGUAGES.some(lang => name.endsWith(`.doc.${lang}.mjs`)),
|
|
479
|
+
)) {
|
|
480
|
+
const base = path.basename(overlay);
|
|
481
|
+
const lang = /** @type {string} */ (
|
|
482
|
+
OVERLAY_LANGUAGES.find(each => base.endsWith(`.doc.${each}.mjs`))
|
|
483
|
+
);
|
|
484
|
+
const stem = base.slice(0, -`.doc.${lang}.mjs`.length);
|
|
485
|
+
const dir = path.dirname(overlay);
|
|
486
|
+
if (
|
|
487
|
+
TOPIC_SUFFIXES.some(suffix =>
|
|
488
|
+
fs.existsSync(path.join(dir, `${stem}${suffix}`)),
|
|
489
|
+
)
|
|
490
|
+
) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const topicNames = topics
|
|
494
|
+
.filter(({file}) => path.dirname(file) === dir)
|
|
495
|
+
.map(({file}) =>
|
|
496
|
+
path.basename(file).replace(/\.doc\.(ts|mjs|js)$/, ''),
|
|
497
|
+
);
|
|
498
|
+
const near = closest(stem, topicNames);
|
|
499
|
+
issues.push(
|
|
500
|
+
finding(
|
|
501
|
+
'orphan_doc_overlay',
|
|
502
|
+
`${rel(overlay)} overlays a topic file ${stem}.doc.mjs that does not exist, so it ships and is never read.`,
|
|
503
|
+
near
|
|
504
|
+
? `Rename ${rel(overlay)} to ${rel(path.join(dir, `${near}.doc.${lang}.mjs`))}, the overlay of ${near}.doc.mjs.`
|
|
505
|
+
: `Delete ${rel(overlay)}, or add the topic it translates as ${rel(path.join(dir, `${stem}.doc.mjs`))}.`,
|
|
506
|
+
rel(overlay),
|
|
507
|
+
1,
|
|
508
|
+
),
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return issues;
|
|
513
|
+
}
|