@astryxdesign/cli 0.7.2 → 0.7.3-canary.02f5fe4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -71
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +127 -3
- package/api/build/blocks/blocks.d.mts +92 -0
- package/api/build/blocks/blocks.mjs +528 -0
- package/api/build/blocks/blocks.test.mjs +238 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/build.type.mjs +1 -1
- package/api/build/kit/answer.d.mts +18 -0
- package/api/build/kit/answer.mjs +195 -0
- package/api/build/kit/answer.test.mjs +108 -0
- package/api/build/kit/kind.d.mts +25 -0
- package/api/build/kit/kind.mjs +170 -0
- package/api/build/kit/kit.d.mts +2 -4
- package/api/build/kit/kit.mjs +8 -398
- package/api/build/kit/rank.d.mts +3 -32
- package/api/build/kit/rank.mjs +25 -342
- package/api/build/kit/rank.test.mjs +2 -1
- package/api/build/kit/signals.d.mts +79 -0
- package/api/build/kit/signals.mjs +295 -0
- package/api/build/kit/start.d.mts +32 -0
- package/api/build/kit/start.mjs +163 -0
- package/api/build/kit/words.d.mts +72 -0
- package/api/build/kit/words.mjs +202 -0
- package/api/build/tree.d.mts +20 -0
- package/api/build/tree.mjs +53 -0
- package/api/build/tree.test.mjs +185 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/_adapter.mjs +39 -5
- package/api/component/component-replacement.test.mjs +1 -0
- package/api/component/component.mjs +17 -3
- package/api/component/component.test.mjs +15 -0
- package/api/component/doc-links.test.mjs +2 -2
- package/api/discover/_adapter.d.mts +1 -2
- package/api/discover/_adapter.mjs +42 -17
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/discover/discover.mjs +0 -1
- package/api/docs/_adapter.d.mts +2 -2
- package/api/docs/_adapter.mjs +82 -23
- package/api/docs/docs-homes.test.mjs +127 -0
- package/api/docs/docs.doc.mjs +3 -3
- package/api/docs/docs.test.mjs +35 -19
- package/api/docs/integration-tree.test.mjs +20 -20
- package/api/docs/integrationDocs.test.mjs +1 -1
- package/api/docs/namespace-section.test.mjs +6 -6
- package/api/docs/reference-blocks.test.mjs +2 -2
- package/api/doctor/_adapter.d.mts +33 -13
- package/api/doctor/_adapter.mjs +55 -25
- package/api/doctor/doctor.d.mts +23 -5
- package/api/doctor/doctor.mjs +183 -40
- package/api/doctor/doctor.test.mjs +95 -12
- package/api/hook/hook.mjs +1 -1
- package/api/init/init.doc.mjs +20 -6
- package/api/init/init.test.mjs +18 -2
- package/api/init/init.type.d.mts +3 -3
- package/api/init/init.type.mjs +3 -3
- package/api/init/remove/remove.mjs +17 -4
- package/api/init/remove/remove.test.mjs +114 -2
- package/api/init/run/run.mjs +69 -6
- package/api/integration/_adapter.d.mts +8 -0
- package/api/integration/_adapter.mjs +21 -1
- package/api/integration/authoring-checks.mjs +83 -9
- package/api/integration/authoring-checks.test.mjs +220 -0
- package/api/integration/pack-check.mjs +123 -4
- package/api/integration/pack-check.test.mjs +87 -0
- package/api/integration/validate-integration-case-disk.test.mjs +111 -0
- package/api/integration/validate-integration-fixes.test.mjs +6 -5
- package/api/integration/validate-integration-gaps.test.mjs +1975 -0
- package/api/integration/validate-integration.d.mts +15 -2
- package/api/integration/validate-integration.mjs +298 -39
- package/api/integration/validate-integration.type.d.mts +90 -1
- package/api/integration/validate-integration.type.mjs +67 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +82 -2
- package/api/search/_adapter.mjs +157 -10
- package/api/search/_adapter.test.mjs +173 -0
- package/api/search/_candidate-score.d.mts +40 -0
- package/api/search/_candidate-score.mjs +238 -0
- package/api/search/_cards.json +6098 -0
- package/api/search/_compound.d.mts +50 -0
- package/api/search/_compound.mjs +170 -0
- package/api/search/_doc-searcher.d.mts +4 -0
- package/api/search/_doc-searcher.mjs +23 -0
- package/api/search/_doc-searcher.test.mjs +36 -0
- package/api/search/_doc-searcher.weights.json +1 -0
- package/api/search/_doc-text.d.mts +30 -0
- package/api/search/_doc-text.mjs +87 -0
- package/api/search/_entries.d.mts +63 -0
- package/api/search/_entries.mjs +232 -0
- package/api/search/_entries.test.mjs +48 -0
- package/api/search/_features.d.mts +35 -0
- package/api/search/_features.mjs +155 -0
- package/api/search/_gather-components.d.mts +26 -0
- package/api/search/_gather-components.mjs +284 -0
- package/api/search/_gather-docs.d.mts +20 -0
- package/api/search/_gather-docs.mjs +254 -0
- package/api/search/_gather-hooks.d.mts +12 -0
- package/api/search/_gather-hooks.mjs +52 -0
- package/api/search/_gather-templates.d.mts +25 -0
- package/api/search/_gather-templates.mjs +107 -0
- package/api/search/_lexical.d.mts +135 -0
- package/api/search/_lexical.mjs +351 -0
- package/api/search/_main-searcher.d.mts +19 -0
- package/api/search/_main-searcher.mjs +115 -0
- package/api/search/_main-searcher.test.mjs +106 -0
- package/api/search/_main-searcher.weights.json +1 -0
- package/api/search/_module-doc.d.mts +32 -0
- package/api/search/_module-doc.mjs +78 -0
- package/api/search/_order.d.mts +32 -0
- package/api/search/_order.mjs +162 -0
- package/api/search/_order.test.mjs +72 -0
- package/api/search/_priority.d.mts +50 -0
- package/api/search/_priority.mjs +139 -0
- package/api/search/_query-words.d.mts +25 -0
- package/api/search/_query-words.mjs +92 -0
- package/api/search/_query.d.mts +65 -0
- package/api/search/_query.mjs +176 -0
- package/api/search/_result.d.mts +97 -0
- package/api/search/_result.mjs +150 -0
- package/api/search/_score.d.mts +60 -0
- package/api/search/_score.mjs +283 -0
- package/api/search/_synonyms.d.mts +21 -0
- package/api/search/_synonyms.mjs +67 -0
- package/api/search/_template-searcher.d.mts +4 -0
- package/api/search/_template-searcher.mjs +23 -0
- package/api/search/_template-searcher.test.mjs +40 -0
- package/api/search/_template-searcher.weights.json +1 -0
- package/api/search/_titles.d.mts +33 -0
- package/api/search/_titles.mjs +65 -0
- package/api/search/_words.d.mts +64 -0
- package/api/search/_words.mjs +157 -0
- package/api/search/search.d.mts +22 -305
- package/api/search/search.doc.mjs +8 -6
- package/api/search/search.mjs +56 -2482
- package/api/search/search.test.mjs +14 -14
- package/api/search/search.type.d.mts +6 -1
- package/api/search/search.type.mjs +6 -1
- package/api/search/tree.d.mts +15 -0
- package/api/search/tree.mjs +40 -0
- package/api/search/tree.test.mjs +66 -0
- package/api/search/tree.weights.json +1 -0
- package/api/template/template.mjs +5 -1
- package/api/theme/build/build.packed-old-core.test.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +57 -9
- package/api/upgrade/_adapter.mjs +198 -28
- package/api/upgrade/registry/registry.d.mts +10 -4
- package/api/upgrade/registry/registry.mjs +33 -16
- package/api/upgrade/registry/registry.test.mjs +85 -0
- package/api/upgrade/run/run.d.mts +4 -2
- package/api/upgrade/run/run.mjs +13 -2
- package/api/upgrade/run/run.test.mjs +417 -3
- package/api/upgrade/upgrade.doc.mjs +10 -5
- package/api/upgrade/upgrade.mjs +13 -2
- package/api/upgrade/upgrade.test.mjs +194 -0
- package/api/upgrade/upgrade.type.d.mts +5 -3
- package/api/upgrade/upgrade.type.mjs +8 -6
- package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
- package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
- package/assets/docs/author-a-theme.doc.mjs +2 -2
- package/assets/docs/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/add-a-component.doc.mjs +1 -1
- package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
- package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
- package/assets/docs/tree/block-template.doc.mjs +1 -1
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +2 -2
- package/assets/docs/tree/check-the-props.doc.mjs +1 -1
- package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
- package/assets/docs/tree/checks.doc.mjs +3 -3
- package/assets/docs/tree/ci-and-containers.doc.mjs +13 -0
- package/assets/docs/tree/cli-command.doc.mjs +1 -1
- package/assets/docs/tree/cli.doc.mjs +1 -1
- package/assets/docs/tree/codemods.doc.mjs +1 -1
- package/assets/docs/tree/component-family.doc.mjs +1 -1
- package/assets/docs/tree/component-imports.doc.mjs +1 -1
- package/assets/docs/tree/components.doc.mjs +1 -1
- package/assets/docs/tree/configuration.doc.mjs +1 -1
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
- package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
- package/assets/docs/tree/docs.doc.mjs +1 -1
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
- package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
- package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
- package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
- package/assets/docs/tree/global-integrations.doc.mjs +2 -2
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
- package/assets/docs/tree/help.doc.mjs +2 -2
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/links.doc.mjs +1 -1
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +1 -1
- package/assets/docs/tree/publishing.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/ship.doc.mjs +2 -2
- package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
- package/assets/docs/tree/single-component.doc.mjs +1 -1
- package/assets/docs/tree/start-a-template.doc.mjs +1 -1
- package/assets/docs/tree/subcomponent.doc.mjs +1 -1
- package/assets/docs/tree/template-assets.doc.mjs +1 -1
- package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
- 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 +1 -1
- package/assets/docs/tree/templates.doc.mjs +1 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
- package/assets/docs/tree/themes.doc.mjs +1 -1
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
- package/assets/docs/tree/upgrading.doc.mjs +1 -1
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
- package/assets/docs/tree/versioning.doc.mjs +5 -1
- package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
- package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
- package/assets/docs/use-integrations.doc.mjs +120 -0
- package/assets/docs/working-with-ai.doc.mjs +6 -2
- package/assets/templates/themes/butter/butterTheme.ts +4 -2
- package/assets/templates/themes/stone/stoneTheme.ts +4 -2
- package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
- package/authoring/debug/parse.d.mts +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/integration/integration.doc.mjs +1 -1
- package/authoring/integration/type.ts +1 -1
- package/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/command-result-coverage.test.mjs +6 -5
- package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
- package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
- package/clients/cli/commands/component.test.mjs +6 -6
- package/clients/cli/commands/docs.doc.mjs +2 -2
- package/clients/cli/commands/docs.mjs +1 -1
- package/clients/cli/commands/docs.test.mjs +14 -14
- package/clients/cli/commands/doctor-integration.test.mjs +49 -1
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +40 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
- package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
- package/clients/cli/commands/init.behavior.test.mjs +230 -1
- package/clients/cli/commands/init.doc.mjs +28 -9
- package/clients/cli/commands/init.flag-help.test.mjs +1 -0
- package/clients/cli/commands/init.mjs +2 -2
- package/clients/cli/commands/integration-check.test.mjs +48 -0
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/manifest.mjs +67 -0
- package/clients/cli/commands/outside-project.test.mjs +101 -1
- package/clients/cli/commands/postinstall.doc.mjs +23 -0
- package/clients/cli/commands/postinstall.mjs +31 -0
- package/clients/cli/commands/setup-nudge.test.mjs +78 -0
- package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +4 -2
- package/clients/cli/commands/upgrade.mjs +9 -2
- package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
- package/clients/cli/index.mjs +54 -94
- package/clients/cli/lib/component-format.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +12 -7
- package/clients/cli/lib/exit-codes.test.mjs +5 -0
- package/clients/cli/lib/manifest.test.mjs +12 -0
- package/clients/cli/lib/welcome-box.mjs +50 -0
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +32 -7
- package/foundation/agent-docs/agent-doc-state.mjs +58 -9
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1017 -105
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +683 -39
- package/foundation/agent-docs/computer-agent-text.d.mts +39 -2
- package/foundation/agent-docs/computer-agent-text.mjs +135 -8
- package/foundation/agent-docs/computer-agent-text.test.mjs +99 -2
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- package/foundation/discovery/authoring-self-docs.d.mts +7 -0
- package/foundation/discovery/authoring-self-docs.mjs +16 -4
- package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
- package/foundation/discovery/authoring-surface.d.mts +1 -1
- package/foundation/discovery/authoring-surface.mjs +2 -2
- package/foundation/discovery/authoring-surface.test.mjs +1 -1
- package/foundation/discovery/cli-self-docs.d.mts +2 -1
- package/foundation/discovery/cli-self-docs.mjs +4 -3
- package/foundation/discovery/cli-self-docs.test.mjs +2 -2
- package/foundation/discovery/component-discovery.d.mts +37 -0
- package/foundation/discovery/component-discovery.mjs +80 -5
- package/foundation/discovery/component-loader.d.mts +1 -1
- package/foundation/discovery/docs-discovery.mjs +14 -1
- package/foundation/discovery/module-exports.d.mts +41 -0
- package/foundation/discovery/module-exports.mjs +253 -0
- package/foundation/discovery/runner-allowlist.d.mts +2 -4
- package/foundation/discovery/runner-allowlist.mjs +2 -4
- package/foundation/discovery/runner-commands.d.mts +77 -0
- package/foundation/discovery/runner-commands.mjs +294 -0
- package/foundation/discovery/runner-commands.test.mjs +128 -0
- package/foundation/discovery/stray-pointers.test.mjs +1 -1
- package/foundation/discovery/template-adapter.mjs +18 -1
- package/foundation/discovery/template-needs.mjs +1 -44
- package/foundation/discovery/theme-discovery.mjs +147 -3
- package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
- package/foundation/doc-compiler/tree.d.mts +32 -6
- package/foundation/doc-compiler/tree.mjs +77 -43
- package/foundation/doc-compiler/tree.test.mjs +7 -7
- package/foundation/fs/paths.d.mts +70 -8
- package/foundation/fs/paths.mjs +556 -38
- package/foundation/fs/paths.test.mjs +430 -54
- package/foundation/fs/read-record.d.mts +18 -0
- package/foundation/fs/read-record.mjs +131 -0
- package/foundation/fs/read-record.test.mjs +77 -0
- package/foundation/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- package/foundation/integrations/cli-peer-version.d.mts +36 -0
- package/foundation/integrations/cli-peer-version.mjs +132 -0
- package/foundation/integrations/cli-peer-version.test.mjs +354 -0
- package/foundation/integrations/computer-integrations.d.mts +6 -3
- package/foundation/integrations/computer-integrations.mjs +38 -9
- package/foundation/integrations/contribution-inventory.d.mts +15 -20
- package/foundation/integrations/contribution-inventory.mjs +41 -5
- package/foundation/integrations/exports-subpaths.d.mts +57 -0
- package/foundation/integrations/exports-subpaths.mjs +113 -0
- package/foundation/integrations/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/public-imports.d.mts +118 -0
- package/foundation/integrations/public-imports.mjs +1769 -0
- package/foundation/integrations/public-imports.test.mjs +1793 -0
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/cache.test.mjs +24 -21
- package/foundation/release/global-state.d.mts +8 -4
- package/foundation/release/global-state.mjs +4 -2
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- package/foundation/release/notices.d.mts +62 -8
- package/foundation/release/notices.mjs +136 -25
- package/foundation/release/notices.test.mjs +21 -0
- package/foundation/release/run-context.mjs +14 -0
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/route/route.d.mts +285 -0
- package/foundation/route/route.mjs +324 -0
- package/foundation/route/route.test.mjs +153 -0
- package/foundation/runtime/hosted-runtime.test.mjs +403 -0
- package/foundation/runtime/locked-versions.d.mts +32 -0
- package/foundation/runtime/locked-versions.mjs +65 -0
- package/foundation/runtime/package-root.d.mts +91 -0
- package/foundation/runtime/package-root.mjs +174 -0
- package/foundation/runtime/package-set.d.mts +62 -0
- package/foundation/runtime/package-set.mjs +130 -0
- package/foundation/runtime/package-set.test.mjs +432 -0
- package/foundation/text/string-utils.d.mts +2 -1
- package/foundation/text/string-utils.mjs +16 -5
- package/foundation/text/suggest-order.d.mts +72 -0
- package/foundation/text/suggest-order.mjs +363 -0
- package/foundation/text/suggest-order.test.mjs +259 -0
- package/host/door-conformance.test.mjs +274 -0
- package/host/index.d.ts +116 -0
- package/host/index.mjs +295 -0
- package/host/index.test.mjs +302 -0
- package/package.json +14 -9
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @input authoring/index.d.ts and the type modules it re-exports, parsed with
|
|
8
8
|
* jscodeshift and never imported or type-checked (consumer installs have no
|
|
9
9
|
* TypeScript); the self-docs on disk and in AUTHORING_SELF_DOCS; and the
|
|
10
|
-
* section keys `astryx docs authoring --index` lists.
|
|
10
|
+
* section keys `astryx docs authoring/field-reference --index` lists.
|
|
11
11
|
* @output {@link tracePublicAuthoringTypes}: each exported type and the module
|
|
12
12
|
* that declares it. {@link auditAuthoringSurface}: each module whose exported
|
|
13
13
|
* types a reader cannot reach and why, each export that cannot be traced,
|
|
@@ -424,7 +424,7 @@ function declaredIn(t, folder, name) {
|
|
|
424
424
|
* document something public: a public type declared beside it, and, when its
|
|
425
425
|
* title is the name of a type declared beside it, that type.
|
|
426
426
|
* @param {{root?: string, sources?: string[], topicKeys?: Set<string> | null}} [options]
|
|
427
|
-
* `topicKeys` are the keys `astryx docs authoring --index` lists; null skips
|
|
427
|
+
* `topicKeys` are the keys `astryx docs authoring/field-reference --index` lists; null skips
|
|
428
428
|
* that comparison.
|
|
429
429
|
* @returns {Promise<{
|
|
430
430
|
* types: number,
|
|
@@ -86,7 +86,7 @@ describe('the public authoring surface of this repo', () => {
|
|
|
86
86
|
it(
|
|
87
87
|
'audits clean: every exported type has a readable doc and every self-doc documents one',
|
|
88
88
|
async () => {
|
|
89
|
-
const index = await docs('authoring', undefined, {index: true});
|
|
89
|
+
const index = await docs('authoring/field-reference', undefined, {index: true});
|
|
90
90
|
const topicKeys = new Set(
|
|
91
91
|
index.data.sections.map((/** @type {any} */ s) => s.id),
|
|
92
92
|
);
|
|
@@ -102,7 +102,8 @@ export const CLI_SELF_DOC_DIRS: string[];
|
|
|
102
102
|
/**
|
|
103
103
|
* Every namespace a CLI doc may declare, and what reads it. The docs tree
|
|
104
104
|
* adopts `cli/commands` (under `cli/commands`) and `cli/api` (under
|
|
105
|
-
* `cli/api/<kind>s`); `astryx docs authoring`
|
|
105
|
+
* `cli/api/<kind>s`); the field reference (`astryx docs authoring/field-reference`)
|
|
106
|
+
* reads `authoring` from its list.
|
|
106
107
|
* @type {Record<string, {reader: 'tree' | 'authoring'}>}
|
|
107
108
|
*/
|
|
108
109
|
export const CLI_DOC_NAMESPACES: Record<string, {
|
|
@@ -41,7 +41,8 @@ const NAMESPACED_KINDS = new Set(['command', 'function', 'schema', 'enum']);
|
|
|
41
41
|
/**
|
|
42
42
|
* Every namespace a CLI doc may declare, and what reads it. The docs tree
|
|
43
43
|
* adopts `cli/commands` (under `cli/commands`) and `cli/api` (under
|
|
44
|
-
* `cli/api/<kind>s`); `astryx docs authoring`
|
|
44
|
+
* `cli/api/<kind>s`); the field reference (`astryx docs authoring/field-reference`)
|
|
45
|
+
* reads `authoring` from its list.
|
|
45
46
|
* @type {Record<string, {reader: 'tree' | 'authoring'}>}
|
|
46
47
|
*/
|
|
47
48
|
export const CLI_DOC_NAMESPACES = {
|
|
@@ -492,13 +493,13 @@ export async function auditCliSelfDocs({
|
|
|
492
493
|
else {
|
|
493
494
|
misfiled.push({
|
|
494
495
|
source,
|
|
495
|
-
message: `${source} has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
|
|
496
|
+
message: `${source} has namespace "authoring", but \`${formatCliCommand('docs authoring/field-reference')}\` does not list it`,
|
|
496
497
|
});
|
|
497
498
|
}
|
|
498
499
|
} else if (inAuthoring) {
|
|
499
500
|
misfiled.push({
|
|
500
501
|
source,
|
|
501
|
-
message: `${source} is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "${namespace}"`,
|
|
502
|
+
message: `${source} is read in \`${formatCliCommand('docs authoring/field-reference')}\`, but its namespace is "${namespace}"`,
|
|
502
503
|
});
|
|
503
504
|
} else {
|
|
504
505
|
treeDocs.push(doc);
|
|
@@ -146,8 +146,8 @@ describe('auditCliSelfDocs', () => {
|
|
|
146
146
|
});
|
|
147
147
|
const result = await audit(root, ['y/y.doc.mjs', 'z/z.doc.mjs']);
|
|
148
148
|
expect(result.misfiled.map(entry => entry.message)).toEqual([
|
|
149
|
-
`authoring/x/x.doc.mjs has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
|
|
150
|
-
`authoring/y/y.doc.mjs is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "cli/api"`,
|
|
149
|
+
`authoring/x/x.doc.mjs has namespace "authoring", but \`${formatCliCommand('docs authoring/field-reference')}\` does not list it`,
|
|
150
|
+
`authoring/y/y.doc.mjs is read in \`${formatCliCommand('docs authoring/field-reference')}\`, but its namespace is "cli/api"`,
|
|
151
151
|
]);
|
|
152
152
|
expect(result.authoring).toBe(1);
|
|
153
153
|
expect(result.tree).toBe(0);
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Whether a component doc is hidden (`hidden: true`), read as discovery
|
|
6
|
+
* reads it: discovery skips a hidden doc.
|
|
7
|
+
* @param {string} docPath
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
export function isHiddenComponentDoc(docPath: string): boolean;
|
|
4
11
|
/**
|
|
5
12
|
* The entry a family doc gives `name` in its `components`, when the doc is not
|
|
6
13
|
* the component's own (Stack's doc for VStack); null otherwise.
|
|
@@ -117,6 +124,36 @@ export function discoverExternalComponentsGrouped(docsDir: string): Record<strin
|
|
|
117
124
|
export function findExternalComponentDoc(docsDir: string, name: string): string | null;
|
|
118
125
|
/** @param {string} docPath */
|
|
119
126
|
export function docStem(docPath: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* The local module a doc's import names: the doc's own `import`, else the
|
|
129
|
+
* subpath its directory publishes, read through the package's `exports` (its
|
|
130
|
+
* `main` when it has none). Undefined when the package directory is unknown;
|
|
131
|
+
* null when the import names another package or resolves to no file.
|
|
132
|
+
*
|
|
133
|
+
* @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
134
|
+
* @param {string} docPath
|
|
135
|
+
* @param {Record<string, any> | null} doc
|
|
136
|
+
* @returns {string | null | undefined}
|
|
137
|
+
*/
|
|
138
|
+
export function importedModule(integration: {
|
|
139
|
+
name?: string;
|
|
140
|
+
__packageDir?: string;
|
|
141
|
+
__packageExports?: Record<string, unknown> | null;
|
|
142
|
+
}, docPath: string, doc: Record<string, any> | null): string | null | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* The file an import of the package's own name resolves to, through its
|
|
145
|
+
* package.json `exports` (or `main` when it has none): `@acme/kit` or
|
|
146
|
+
* `@acme/kit/widget`. Null when the specifier names another package, or the
|
|
147
|
+
* package publishes nothing at it.
|
|
148
|
+
* @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown> | null}} integration
|
|
149
|
+
* @param {string} specifier
|
|
150
|
+
* @returns {string | null}
|
|
151
|
+
*/
|
|
152
|
+
export function ownPackageModule(integration: {
|
|
153
|
+
name?: string;
|
|
154
|
+
__packageDir?: string;
|
|
155
|
+
__packageExports?: Record<string, unknown> | null;
|
|
156
|
+
}, specifier: string): string | null;
|
|
120
157
|
/**
|
|
121
158
|
* The `.tsx` file in `folder`, or below it, that declares `name`, taking files
|
|
122
159
|
* in path order: the source of a member whose import never reaches a `.tsx`
|
|
@@ -118,6 +118,16 @@ function scanDocText(text) {
|
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Whether a component doc is hidden (`hidden: true`), read as discovery
|
|
123
|
+
* reads it: discovery skips a hidden doc.
|
|
124
|
+
* @param {string} docPath
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
export function isHiddenComponentDoc(docPath) {
|
|
128
|
+
return readDocMeta(docPath).hidden;
|
|
129
|
+
}
|
|
130
|
+
|
|
121
131
|
/**
|
|
122
132
|
* Read the `group`, `hiddenComponents`, and `hidden` fields from a
|
|
123
133
|
* component's .doc.mjs file (synchronous), with what {@link scanDocText}
|
|
@@ -994,7 +1004,7 @@ function packageMain(packageDir) {
|
|
|
994
1004
|
* @param {Record<string, any> | null} doc
|
|
995
1005
|
* @returns {string | null | undefined}
|
|
996
1006
|
*/
|
|
997
|
-
function importedModule(integration, docPath, doc) {
|
|
1007
|
+
export function importedModule(integration, docPath, doc) {
|
|
998
1008
|
const packageName = integration?.name;
|
|
999
1009
|
const packageDir = integration?.__packageDir;
|
|
1000
1010
|
if (!packageName || !packageDir) return undefined;
|
|
@@ -1024,6 +1034,42 @@ function importedModule(integration, docPath, doc) {
|
|
|
1024
1034
|
).target;
|
|
1025
1035
|
}
|
|
1026
1036
|
|
|
1037
|
+
/**
|
|
1038
|
+
* The file an import of the package's own name resolves to, through its
|
|
1039
|
+
* package.json `exports` (or `main` when it has none): `@acme/kit` or
|
|
1040
|
+
* `@acme/kit/widget`. Null when the specifier names another package, or the
|
|
1041
|
+
* package publishes nothing at it.
|
|
1042
|
+
* @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown> | null}} integration
|
|
1043
|
+
* @param {string} specifier
|
|
1044
|
+
* @returns {string | null}
|
|
1045
|
+
*/
|
|
1046
|
+
export function ownPackageModule(integration, specifier) {
|
|
1047
|
+
const packageName = integration?.name;
|
|
1048
|
+
const packageDir = integration?.__packageDir;
|
|
1049
|
+
if (!packageName || !packageDir) return null;
|
|
1050
|
+
if (specifier !== packageName && !specifier.startsWith(`${packageName}/`)) {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
const exportsMap =
|
|
1054
|
+
integration.__packageExports === undefined
|
|
1055
|
+
? readPackageExports(packageDir)
|
|
1056
|
+
: integration.__packageExports;
|
|
1057
|
+
if (specifier === packageName && exportsMap == null) {
|
|
1058
|
+
return packageMain(packageDir) ?? null;
|
|
1059
|
+
}
|
|
1060
|
+
const subpath =
|
|
1061
|
+
specifier === packageName
|
|
1062
|
+
? '.'
|
|
1063
|
+
: `./${specifier.slice(packageName.length + 1)}`;
|
|
1064
|
+
return (
|
|
1065
|
+
inspectPackageExport(
|
|
1066
|
+
{name: packageName, exports: exportsMap ?? undefined},
|
|
1067
|
+
packageDir,
|
|
1068
|
+
subpath,
|
|
1069
|
+
).target ?? null
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1027
1073
|
/**
|
|
1028
1074
|
* Whether `file` sits in `folder` or below it.
|
|
1029
1075
|
* @param {string} folder
|
|
@@ -1211,6 +1257,19 @@ export function integrationMemberEntry(doc, record) {
|
|
|
1211
1257
|
);
|
|
1212
1258
|
}
|
|
1213
1259
|
|
|
1260
|
+
/**
|
|
1261
|
+
* Whether `dir` is a folder to list. A declared root that is a file is
|
|
1262
|
+
* reported by the root check; discovery finds nothing in it.
|
|
1263
|
+
* @param {string} dir
|
|
1264
|
+
*/
|
|
1265
|
+
function isFolder(dir) {
|
|
1266
|
+
try {
|
|
1267
|
+
return fs.statSync(dir).isDirectory();
|
|
1268
|
+
} catch {
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1214
1273
|
/**
|
|
1215
1274
|
* Visit every component doc under an integration's components root, hidden
|
|
1216
1275
|
* ones included, in discovery order, with its stem.
|
|
@@ -1218,10 +1277,26 @@ export function integrationMemberEntry(doc, record) {
|
|
|
1218
1277
|
* @param {(docPath: string, stem: string) => void} visit
|
|
1219
1278
|
*/
|
|
1220
1279
|
export function forEachIntegrationDoc(componentsDir, visit) {
|
|
1221
|
-
if (!componentsDir || !
|
|
1280
|
+
if (!componentsDir || !isFolder(componentsDir)) return;
|
|
1281
|
+
/** @param {string} name */
|
|
1282
|
+
const key = name => {
|
|
1283
|
+
const rank = INTEGRATION_DOC_SUFFIXES.findIndex(s => name.endsWith(s));
|
|
1284
|
+
return rank < 0
|
|
1285
|
+
? {stem: name, rank}
|
|
1286
|
+
: {stem: name.slice(0, -INTEGRATION_DOC_SUFFIXES[rank].length), rank};
|
|
1287
|
+
};
|
|
1222
1288
|
/** @param {string} dirPath */
|
|
1223
1289
|
function scanDir(dirPath) {
|
|
1224
|
-
for
|
|
1290
|
+
// Name order, and for two docs of one stem the suffix precedence, so the
|
|
1291
|
+
// doc a stem records is the one a lookup by name finds, on every disk.
|
|
1292
|
+
const entries = listDirEntries(dirPath).sort((a, b) => {
|
|
1293
|
+
const left = key(a.name);
|
|
1294
|
+
const right = key(b.name);
|
|
1295
|
+
return left.stem === right.stem
|
|
1296
|
+
? left.rank - right.rank
|
|
1297
|
+
: byCodeUnit(left.stem, right.stem);
|
|
1298
|
+
});
|
|
1299
|
+
for (const entry of entries) {
|
|
1225
1300
|
if (entry.name === 'node_modules' || entry.name === '__tests__') continue;
|
|
1226
1301
|
const fullPath = path.join(dirPath, entry.name);
|
|
1227
1302
|
if (entry.isDirectory()) {
|
|
@@ -1270,7 +1345,7 @@ export function discoverIntegrationComponents(
|
|
|
1270
1345
|
{documentedNames = true} = {},
|
|
1271
1346
|
) {
|
|
1272
1347
|
const componentsDir = integration?.components;
|
|
1273
|
-
if (!componentsDir || !
|
|
1348
|
+
if (!componentsDir || !isFolder(componentsDir)) return [];
|
|
1274
1349
|
|
|
1275
1350
|
/** @type {Map<string, {name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>} */
|
|
1276
1351
|
const byName = new Map();
|
|
@@ -1485,7 +1560,7 @@ function findDocumentedComponent(integration, componentsDir, name) {
|
|
|
1485
1560
|
*/
|
|
1486
1561
|
export function findIntegrationComponent(integration, name) {
|
|
1487
1562
|
const componentsDir = integration?.components;
|
|
1488
|
-
if (!componentsDir || !
|
|
1563
|
+
if (!componentsDir || !isFolder(componentsDir)) return null;
|
|
1489
1564
|
const sameStem = findSameStemDoc(componentsDir, name);
|
|
1490
1565
|
if (sameStem) {
|
|
1491
1566
|
const exportName = otherExportName({
|
|
@@ -423,6 +423,19 @@ export function problemsInNamespace(doc) {
|
|
|
423
423
|
return problems;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Whether `dir` is a folder to list. A declared root that is a file is
|
|
428
|
+
* reported by the root check; discovery finds nothing in it.
|
|
429
|
+
* @param {string} dir
|
|
430
|
+
*/
|
|
431
|
+
function isFolder(dir) {
|
|
432
|
+
try {
|
|
433
|
+
return fs.statSync(dir).isDirectory();
|
|
434
|
+
} catch {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
426
439
|
/**
|
|
427
440
|
* Discover the topics contributed by a single loaded integration. Mirrors
|
|
428
441
|
* `discoverIntegrationComponents`: walk the resolved root, take every
|
|
@@ -450,7 +463,7 @@ export async function discoverIntegrationDocs(integration) {
|
|
|
450
463
|
const namespaces = [];
|
|
451
464
|
/** @type {import('../doc-compiler/tree.mjs').TreeDocInput[]} */
|
|
452
465
|
const guides = [];
|
|
453
|
-
if (!docsDir || !
|
|
466
|
+
if (!docsDir || !isFolder(docsDir)) {
|
|
454
467
|
return {records, errors, namespaces, guides};
|
|
455
468
|
}
|
|
456
469
|
const providerId = integration.providerId ?? integration.name;
|
|
@@ -18,6 +18,39 @@ export function moduleExportsName(file: string, exportName: string, seen?: Set<s
|
|
|
18
18
|
* @returns {string | null}
|
|
19
19
|
*/
|
|
20
20
|
export function moduleExportSource(file: string, exportName: string, seen?: Set<string>): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Every runtime name a module exports that this read can see (`default` for
|
|
23
|
+
* a default export), or null when it cannot see them all: an `export *`, `export =`, CommonJS exports, or a
|
|
24
|
+
* file that does not parse.
|
|
25
|
+
* @param {string} file
|
|
26
|
+
* @returns {string[] | null}
|
|
27
|
+
*/
|
|
28
|
+
export function moduleExportNames(file: string): string[] | null;
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {{verdict: 'exports'} | {verdict: 'lacks'} | {verdict: 'unknown', reason: string}} ExportVerdict
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Whether a module exports `exportName` at runtime, read without executing
|
|
34
|
+
* it: `exports` when a declaration or a followed re-export binds it, `lacks`
|
|
35
|
+
* only when every export the module writes was read and none binds it, and
|
|
36
|
+
* `unknown`, with the first reason, when some export could not be read: a
|
|
37
|
+
* re-export from another package or from a specifier that resolves to no
|
|
38
|
+
* file, a destructured binding, a namespace, an ambient or const enum
|
|
39
|
+
* declaration, `export =`, CommonJS exports, or a file that does not parse.
|
|
40
|
+
* @param {string} file
|
|
41
|
+
* @param {string} exportName
|
|
42
|
+
* @param {Set<string>} [seen]
|
|
43
|
+
* @returns {ExportVerdict}
|
|
44
|
+
*/
|
|
45
|
+
export function moduleExportVerdict(file: string, exportName: string, seen?: Set<string>): ExportVerdict;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a module provably does not export `exportName` at runtime: see
|
|
48
|
+
* {@link moduleExportVerdict}.
|
|
49
|
+
* @param {string} file
|
|
50
|
+
* @param {string} exportName
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
export function moduleLacksExport(file: string, exportName: string): boolean;
|
|
21
54
|
/**
|
|
22
55
|
* The object a module exports, as plain data, read without running it: the
|
|
23
56
|
* first of `exportNames` it exports, an object literal in place or a top-level
|
|
@@ -35,3 +68,11 @@ export function staticExportObject(file: string, exportNames: readonly string[])
|
|
|
35
68
|
* enumerable, so the object compares and serializes as its literal properties.
|
|
36
69
|
*/
|
|
37
70
|
export const HAS_COMPUTED: unique symbol;
|
|
71
|
+
export type ExportVerdict = {
|
|
72
|
+
verdict: "exports";
|
|
73
|
+
} | {
|
|
74
|
+
verdict: "lacks";
|
|
75
|
+
} | {
|
|
76
|
+
verdict: "unknown";
|
|
77
|
+
reason: string;
|
|
78
|
+
};
|
|
@@ -309,6 +309,259 @@ export function moduleExportSource(file, exportName, seen = new Set()) {
|
|
|
309
309
|
return found;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
/**
|
|
313
|
+
* The module's own top-level statements of one type: an `export` inside a
|
|
314
|
+
* TypeScript namespace names a member of that namespace, not of the module.
|
|
315
|
+
* @param {any} program
|
|
316
|
+
* @param {string} type
|
|
317
|
+
* @returns {any[]}
|
|
318
|
+
*/
|
|
319
|
+
function topLevel(program, type) {
|
|
320
|
+
return program.body.filter((/** @type {any} */ node) => node.type === type);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Every runtime name a module exports that this read can see (`default` for
|
|
325
|
+
* a default export), or null when it cannot see them all: an `export *`, `export =`, CommonJS exports, or a
|
|
326
|
+
* file that does not parse.
|
|
327
|
+
* @param {string} file
|
|
328
|
+
* @returns {string[] | null}
|
|
329
|
+
*/
|
|
330
|
+
export function moduleExportNames(file) {
|
|
331
|
+
let text;
|
|
332
|
+
let ast;
|
|
333
|
+
try {
|
|
334
|
+
if (!fs.statSync(file).isFile()) return null;
|
|
335
|
+
text = moduleText(file);
|
|
336
|
+
ast = parseModule(file);
|
|
337
|
+
} catch {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
if (/\bmodule\.exports\b|\bexports\.[$\w]/u.test(text)) return null;
|
|
341
|
+
if (
|
|
342
|
+
topLevel(ast.program, 'TSExportAssignment').length > 0 ||
|
|
343
|
+
topLevel(ast.program, 'ExportAllDeclaration').length > 0
|
|
344
|
+
) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
/** @type {Set<string>} */
|
|
348
|
+
const names = new Set();
|
|
349
|
+
for (const node of topLevel(ast.program, 'TSImportEqualsDeclaration')) {
|
|
350
|
+
if (!node.isExport) continue;
|
|
351
|
+
// A type-only alias has no runtime value; any other might.
|
|
352
|
+
if (node.importKind === 'type') continue;
|
|
353
|
+
if (typeof node.id?.name !== 'string') return null;
|
|
354
|
+
names.add(node.id.name);
|
|
355
|
+
}
|
|
356
|
+
for (const node of topLevel(ast.program, 'ExportNamedDeclaration')) {
|
|
357
|
+
if (node.exportKind === 'type') continue;
|
|
358
|
+
const declaration = node.declaration;
|
|
359
|
+
if (declaration?.type === 'VariableDeclaration') {
|
|
360
|
+
for (const item of declaration.declarations) {
|
|
361
|
+
if (item.id?.type !== 'Identifier') return null;
|
|
362
|
+
names.add(item.id.name);
|
|
363
|
+
}
|
|
364
|
+
} else if (declaration != null) {
|
|
365
|
+
if (!runtimeDefault(declaration)) continue;
|
|
366
|
+
if (typeof declaration.id?.name !== 'string') return null;
|
|
367
|
+
names.add(declaration.id.name);
|
|
368
|
+
}
|
|
369
|
+
for (const specifier of node.specifiers ?? []) {
|
|
370
|
+
if (specifier.exportKind === 'type') continue;
|
|
371
|
+
const exported = specifier.exported?.name ?? specifier.exported?.value;
|
|
372
|
+
if (typeof exported === 'string') names.add(exported);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (
|
|
376
|
+
topLevel(ast.program, 'ExportDefaultDeclaration').some(
|
|
377
|
+
(/** @type {any} */ node) => runtimeDefault(node.declaration),
|
|
378
|
+
)
|
|
379
|
+
) {
|
|
380
|
+
names.add('default');
|
|
381
|
+
}
|
|
382
|
+
return [...names].sort();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* @typedef {{verdict: 'exports'} | {verdict: 'lacks'} | {verdict: 'unknown', reason: string}} ExportVerdict
|
|
387
|
+
*/
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Whether a module exports `exportName` at runtime, read without executing
|
|
391
|
+
* it: `exports` when a declaration or a followed re-export binds it, `lacks`
|
|
392
|
+
* only when every export the module writes was read and none binds it, and
|
|
393
|
+
* `unknown`, with the first reason, when some export could not be read: a
|
|
394
|
+
* re-export from another package or from a specifier that resolves to no
|
|
395
|
+
* file, a destructured binding, a namespace, an ambient or const enum
|
|
396
|
+
* declaration, `export =`, CommonJS exports, or a file that does not parse.
|
|
397
|
+
* @param {string} file
|
|
398
|
+
* @param {string} exportName
|
|
399
|
+
* @param {Set<string>} [seen]
|
|
400
|
+
* @returns {ExportVerdict}
|
|
401
|
+
*/
|
|
402
|
+
export function moduleExportVerdict(file, exportName, seen = new Set()) {
|
|
403
|
+
const identity = `${file}\0${exportName}`;
|
|
404
|
+
// A cycle adds nothing a module's own exports have not already said.
|
|
405
|
+
if (seen.has(identity)) return {verdict: 'lacks'};
|
|
406
|
+
seen.add(identity);
|
|
407
|
+
const shown = path.basename(file);
|
|
408
|
+
let text;
|
|
409
|
+
let ast;
|
|
410
|
+
try {
|
|
411
|
+
if (!fs.statSync(file).isFile()) {
|
|
412
|
+
return {verdict: 'unknown', reason: `${shown} is not a file.`};
|
|
413
|
+
}
|
|
414
|
+
text = moduleText(file);
|
|
415
|
+
ast = parseModule(file);
|
|
416
|
+
} catch {
|
|
417
|
+
return {
|
|
418
|
+
verdict: 'unknown',
|
|
419
|
+
reason: `${shown} could not be read or parsed.`,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/** @type {string[]} */
|
|
423
|
+
const unknown = [];
|
|
424
|
+
if (/\bmodule\.exports\b|\bexports\.[$\w]/u.test(text)) {
|
|
425
|
+
unknown.push(
|
|
426
|
+
`${shown} uses CommonJS exports, whose names this check does not read.`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (topLevel(ast.program, 'TSExportAssignment').length > 0) {
|
|
430
|
+
unknown.push(`${shown} uses \`export =\`, whose names this check does not read.`);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Follow a re-export of `imported` from `specifier`.
|
|
434
|
+
* @param {string} specifier
|
|
435
|
+
* @param {string} imported
|
|
436
|
+
* @param {string} what how the reason names what is re-exported
|
|
437
|
+
* @returns {boolean} true when it proves the export
|
|
438
|
+
*/
|
|
439
|
+
const follow = (specifier, imported, what) => {
|
|
440
|
+
if (!specifier.startsWith('.')) {
|
|
441
|
+
unknown.push(
|
|
442
|
+
`${shown} re-exports ${what} from "${specifier}", another package this check does not read.`,
|
|
443
|
+
);
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
const target = resolveLocalModule(specifier, file);
|
|
447
|
+
if (!target) {
|
|
448
|
+
unknown.push(
|
|
449
|
+
`${shown} re-exports ${what} from "${specifier}", which resolves to no file this check can read.`,
|
|
450
|
+
);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const inner = moduleExportVerdict(target, imported, seen);
|
|
454
|
+
if (inner.verdict === 'exports') return true;
|
|
455
|
+
if (inner.verdict === 'unknown') unknown.push(inner.reason);
|
|
456
|
+
return false;
|
|
457
|
+
};
|
|
458
|
+
// `export import Name = Some.Path;` binds a runtime value unless it is
|
|
459
|
+
// type-only.
|
|
460
|
+
for (const node of topLevel(ast.program, 'TSImportEqualsDeclaration')) {
|
|
461
|
+
if (!node.isExport || node.id?.name !== exportName) continue;
|
|
462
|
+
if (node.importKind === 'type') {
|
|
463
|
+
unknown.push(
|
|
464
|
+
`${shown} exports ${exportName} with \`export import type\`, which has no runtime value this check can see.`,
|
|
465
|
+
);
|
|
466
|
+
} else {
|
|
467
|
+
return {verdict: 'exports'};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const node of topLevel(ast.program, 'ExportNamedDeclaration')) {
|
|
471
|
+
if (node.exportKind === 'type') continue;
|
|
472
|
+
const declaration = node.declaration;
|
|
473
|
+
if (declaration?.type === 'VariableDeclaration') {
|
|
474
|
+
for (const item of declaration.declarations) {
|
|
475
|
+
if (item.id?.type !== 'Identifier') {
|
|
476
|
+
unknown.push(
|
|
477
|
+
`${shown} exports a destructured binding, whose names this check does not read.`,
|
|
478
|
+
);
|
|
479
|
+
} else if (item.id.name === exportName) {
|
|
480
|
+
if (declaration.declare === true) {
|
|
481
|
+
unknown.push(
|
|
482
|
+
`${shown} declares ${exportName} with \`declare\`, which has no runtime value this check can see.`,
|
|
483
|
+
);
|
|
484
|
+
} else {
|
|
485
|
+
return {verdict: 'exports'};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
} else if (declaration != null && declaration.id?.name === exportName) {
|
|
490
|
+
if (
|
|
491
|
+
['TSInterfaceDeclaration', 'TSTypeAliasDeclaration'].includes(
|
|
492
|
+
declaration.type,
|
|
493
|
+
)
|
|
494
|
+
) {
|
|
495
|
+
// A type only: no runtime export.
|
|
496
|
+
} else if (declaration.type === 'TSModuleDeclaration') {
|
|
497
|
+
unknown.push(
|
|
498
|
+
`${shown} exports ${exportName} as a TypeScript namespace, which has a runtime value only when it holds one.`,
|
|
499
|
+
);
|
|
500
|
+
} else if (declaration.declare === true || declaration.const === true) {
|
|
501
|
+
unknown.push(
|
|
502
|
+
`${shown} declares ${exportName} as an ambient or const declaration, which has no runtime value this check can see.`,
|
|
503
|
+
);
|
|
504
|
+
} else if (runtimeDefault(declaration)) {
|
|
505
|
+
return {verdict: 'exports'};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
for (const specifier of node.specifiers ?? []) {
|
|
509
|
+
if (specifier.exportKind === 'type') continue;
|
|
510
|
+
const exported = specifier.exported?.name ?? specifier.exported?.value;
|
|
511
|
+
if (exported !== exportName) continue;
|
|
512
|
+
if (!node.source || specifier.type === 'ExportNamespaceSpecifier') {
|
|
513
|
+
return {verdict: 'exports'};
|
|
514
|
+
}
|
|
515
|
+
if (specifier.type !== 'ExportSpecifier') {
|
|
516
|
+
unknown.push(`${shown} re-exports ${exportName} in a form this check does not read.`);
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const imported =
|
|
520
|
+
specifier.local?.name ?? specifier.local?.value ?? exportName;
|
|
521
|
+
if (follow(String(node.source.value), imported, exportName)) {
|
|
522
|
+
return {verdict: 'exports'};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (exportName === 'default') {
|
|
527
|
+
const defaults = topLevel(ast.program, 'ExportDefaultDeclaration');
|
|
528
|
+
if (
|
|
529
|
+
defaults.some((/** @type {any} */ node) =>
|
|
530
|
+
runtimeDefault(node.declaration),
|
|
531
|
+
)
|
|
532
|
+
) {
|
|
533
|
+
return {verdict: 'exports'};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const node of topLevel(ast.program, 'ExportAllDeclaration')) {
|
|
537
|
+
if (node.exportKind === 'type') continue;
|
|
538
|
+
const exported = node.exported?.name ?? node.exported?.value;
|
|
539
|
+
if (exported != null) {
|
|
540
|
+
if (exported === exportName) return {verdict: 'exports'};
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
// `export *` never re-exports a default.
|
|
544
|
+
if (exportName === 'default') continue;
|
|
545
|
+
if (follow(String(node.source?.value ?? ''), exportName, 'everything')) {
|
|
546
|
+
return {verdict: 'exports'};
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return unknown.length > 0
|
|
550
|
+
? {verdict: 'unknown', reason: unknown[0]}
|
|
551
|
+
: {verdict: 'lacks'};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Whether a module provably does not export `exportName` at runtime: see
|
|
556
|
+
* {@link moduleExportVerdict}.
|
|
557
|
+
* @param {string} file
|
|
558
|
+
* @param {string} exportName
|
|
559
|
+
* @returns {boolean}
|
|
560
|
+
*/
|
|
561
|
+
export function moduleLacksExport(file, exportName) {
|
|
562
|
+
return moduleExportVerdict(file, exportName).verdict === 'lacks';
|
|
563
|
+
}
|
|
564
|
+
|
|
312
565
|
/**
|
|
313
566
|
* Marks an object {@link staticExportObject} returns (or one inside it) whose
|
|
314
567
|
* source also has properties or spreads it could not read as literals. Not
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
* command, except in `runnerExamples`, where `npx @astryxdesign/cli …` and a
|
|
15
15
|
* project's runner may (still never the unscoped name through npx).
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* repo test runs that check over Core's and the CLI's own docs with this
|
|
20
|
-
* allowlist.
|
|
17
|
+
* The matcher is runnerCommandsIn (foundation/discovery/runner-commands.mjs),
|
|
18
|
+
* the rule `doctor integration validate` holds integrations to.
|
|
21
19
|
*
|
|
22
20
|
* @input nothing
|
|
23
21
|
* @output the allowlist
|
|
@@ -13,10 +13,8 @@
|
|
|
13
13
|
* command, except in `runnerExamples`, where `npx @astryxdesign/cli …` and a
|
|
14
14
|
* project's runner may (still never the unscoped name through npx).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* repo test runs that check over Core's and the CLI's own docs with this
|
|
19
|
-
* allowlist.
|
|
16
|
+
* The matcher is runnerCommandsIn (foundation/discovery/runner-commands.mjs),
|
|
17
|
+
* the rule `doctor integration validate` holds integrations to.
|
|
20
18
|
*
|
|
21
19
|
* @input nothing
|
|
22
20
|
* @output the allowlist
|