@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
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Matching a project to a host's package sets (architecture:cli-surface
|
|
5
|
+
* INV30): one root per run, the first that qualifies in the host's order, the
|
|
6
|
+
* versions a project would install, nothing filled that ships an integration
|
|
7
|
+
* manifest, and the integrations a host approves for use outside a project.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {describe, it, expect, afterEach} from 'vitest';
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import {
|
|
14
|
+
cleanupFixtures,
|
|
15
|
+
project,
|
|
16
|
+
tempDir,
|
|
17
|
+
writeJson,
|
|
18
|
+
} from '../../test-utils/release-fixtures.mjs';
|
|
19
|
+
import {projectAstryxPins} from './locked-versions.mjs';
|
|
20
|
+
import {qualifyingRoot, rootPackage, satisfiesInstall} from './package-set.mjs';
|
|
21
|
+
import {scanComputerIntegrations} from '../integrations/computer-integrations.mjs';
|
|
22
|
+
import {findCoreDir} from '../fs/paths.mjs';
|
|
23
|
+
import {
|
|
24
|
+
_resetHostedRun,
|
|
25
|
+
activateHostedRun,
|
|
26
|
+
recordResolved,
|
|
27
|
+
recordResolvedIntegrations,
|
|
28
|
+
resolvedPackages,
|
|
29
|
+
} from './package-root.mjs';
|
|
30
|
+
|
|
31
|
+
afterEach(cleanupFixtures);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Write a package into `dir`'s node_modules.
|
|
35
|
+
* @param {string} dir
|
|
36
|
+
* @param {string} name
|
|
37
|
+
* @param {Record<string, unknown>} pkg
|
|
38
|
+
* @param {{manifest?: boolean, bin?: boolean, hostEntry?: boolean}} [options]
|
|
39
|
+
* @returns {string} the package folder
|
|
40
|
+
*/
|
|
41
|
+
function install(dir, name, pkg, options = {}) {
|
|
42
|
+
const pkgDir = path.join(dir, 'node_modules', ...name.split('/'));
|
|
43
|
+
writeJson(path.join(pkgDir, 'package.json'), {
|
|
44
|
+
name,
|
|
45
|
+
...pkg,
|
|
46
|
+
...(options.bin ? {bin: {astryx: './bin.cjs'}} : {}),
|
|
47
|
+
});
|
|
48
|
+
if (options.bin) fs.writeFileSync(path.join(pkgDir, 'bin.cjs'), '');
|
|
49
|
+
if (options.hostEntry) {
|
|
50
|
+
fs.mkdirSync(path.join(pkgDir, 'host'), {recursive: true});
|
|
51
|
+
fs.writeFileSync(path.join(pkgDir, 'host', 'index.mjs'), '');
|
|
52
|
+
}
|
|
53
|
+
if (options.manifest) {
|
|
54
|
+
fs.writeFileSync(
|
|
55
|
+
path.join(pkgDir, 'astryx.integration.mjs'),
|
|
56
|
+
'export default {};\n',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return pkgDir;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A package set: a root holding a CLI and Core of `version`.
|
|
64
|
+
* @param {string} version
|
|
65
|
+
* @param {{hostEntry?: boolean, bin?: boolean}} [options]
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function packageSet(version, options = {}) {
|
|
69
|
+
const root = tempDir('set');
|
|
70
|
+
install(
|
|
71
|
+
root,
|
|
72
|
+
'@astryxdesign/cli',
|
|
73
|
+
{version},
|
|
74
|
+
{bin: options.bin ?? true, hostEntry: options.hostEntry},
|
|
75
|
+
);
|
|
76
|
+
install(root, '@astryxdesign/core', {version});
|
|
77
|
+
return root;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe('the versions a project would install', () => {
|
|
81
|
+
it('reads each declared @astryxdesign/* package with its lockfile and installed versions', () => {
|
|
82
|
+
const root = project(tempDir(), {
|
|
83
|
+
'@astryxdesign/core': '^0.7.0',
|
|
84
|
+
'@astryxdesign/cli': '0.7.0',
|
|
85
|
+
react: '^19.0.0',
|
|
86
|
+
});
|
|
87
|
+
install(root, '@astryxdesign/cli', {version: '0.7.0'});
|
|
88
|
+
writeJson(path.join(root, 'package-lock.json'), {
|
|
89
|
+
lockfileVersion: 3,
|
|
90
|
+
packages: {
|
|
91
|
+
'': {},
|
|
92
|
+
'node_modules/@astryxdesign/core': {version: '0.7.1'},
|
|
93
|
+
'node_modules/@astryxdesign/cli': {version: '0.7.0'},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
expect(projectAstryxPins(root)).toEqual([
|
|
97
|
+
{
|
|
98
|
+
name: '@astryxdesign/core',
|
|
99
|
+
declared: '^0.7.0',
|
|
100
|
+
locked: '0.7.1',
|
|
101
|
+
installed: null,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: '@astryxdesign/cli',
|
|
105
|
+
declared: '0.7.0',
|
|
106
|
+
locked: '0.7.0',
|
|
107
|
+
installed: '0.7.0',
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('a version the project would install for a range', () => {
|
|
114
|
+
it.each([
|
|
115
|
+
['0.7.1', '^0.7.0', true],
|
|
116
|
+
['0.8.0', '^0.7.0', false],
|
|
117
|
+
['0.7.1-canary.1', '^0.7.0', false],
|
|
118
|
+
['0.7.1-canary.1', '^0.7.1-canary.0', true],
|
|
119
|
+
['0.7.1', 'file:../core', false],
|
|
120
|
+
['0.7.1', 'workspace:*', false],
|
|
121
|
+
[null, '^0.7.0', false],
|
|
122
|
+
])('%s for %s: %s', (version, range, expected) => {
|
|
123
|
+
expect(satisfiesInstall(version, range)).toBe(expected);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('the package set that serves a run another release answers', () => {
|
|
128
|
+
it('serves from the first set whose CLI and Core are that release', () => {
|
|
129
|
+
const older = packageSet('0.6.8');
|
|
130
|
+
const first = packageSet('0.7.0');
|
|
131
|
+
const second = packageSet('0.7.0');
|
|
132
|
+
const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
|
|
133
|
+
const served = qualifyingRoot({
|
|
134
|
+
release: '0.7.0',
|
|
135
|
+
projectDir: root,
|
|
136
|
+
roots: [older, first, second],
|
|
137
|
+
});
|
|
138
|
+
expect(served).toMatchObject({root: first, hostEntry: null});
|
|
139
|
+
expect(/** @type {any} */ (served).bin).toBe(
|
|
140
|
+
path.join(first, 'node_modules', '@astryxdesign', 'cli', 'bin.cjs'),
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('skips a set whose Core is another release, or missing', () => {
|
|
145
|
+
const mixed = tempDir('set');
|
|
146
|
+
install(mixed, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
|
|
147
|
+
install(mixed, '@astryxdesign/core', {version: '0.7.1'});
|
|
148
|
+
const bare = tempDir('set');
|
|
149
|
+
install(bare, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
|
|
150
|
+
const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
|
|
151
|
+
expect(
|
|
152
|
+
qualifyingRoot({
|
|
153
|
+
release: '0.7.0',
|
|
154
|
+
projectDir: root,
|
|
155
|
+
roots: [mixed, bare],
|
|
156
|
+
}),
|
|
157
|
+
).toEqual({
|
|
158
|
+
reason:
|
|
159
|
+
"no package set the host passed holds CLI 0.7.0 with this project's packages",
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("holds the lockfile's version of each package it fills, else one in the declared range", () => {
|
|
164
|
+
const locked = project(tempDir(), {
|
|
165
|
+
'@astryxdesign/core': '^0.7.0',
|
|
166
|
+
'@astryxdesign/charts': '^0.7.0',
|
|
167
|
+
});
|
|
168
|
+
writeJson(path.join(locked, 'package-lock.json'), {
|
|
169
|
+
lockfileVersion: 3,
|
|
170
|
+
packages: {
|
|
171
|
+
'': {},
|
|
172
|
+
'node_modules/@astryxdesign/core': {version: '0.7.0'},
|
|
173
|
+
'node_modules/@astryxdesign/charts': {version: '0.7.0'},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const newer = packageSet('0.7.0');
|
|
177
|
+
install(newer, '@astryxdesign/charts', {version: '0.7.2'});
|
|
178
|
+
const exact = packageSet('0.7.0');
|
|
179
|
+
install(exact, '@astryxdesign/charts', {version: '0.7.0'});
|
|
180
|
+
expect(
|
|
181
|
+
qualifyingRoot({
|
|
182
|
+
release: '0.7.0',
|
|
183
|
+
projectDir: locked,
|
|
184
|
+
roots: [newer, exact],
|
|
185
|
+
}),
|
|
186
|
+
).toMatchObject({root: exact});
|
|
187
|
+
|
|
188
|
+
const ranged = project(tempDir(), {
|
|
189
|
+
'@astryxdesign/core': '^0.7.0',
|
|
190
|
+
'@astryxdesign/charts': '^0.7.0',
|
|
191
|
+
});
|
|
192
|
+
expect(
|
|
193
|
+
qualifyingRoot({release: '0.7.0', projectDir: ranged, roots: [newer]}),
|
|
194
|
+
).toMatchObject({root: newer});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('keeps out a set whose copy the project declares as a path and has not locked', () => {
|
|
198
|
+
const root = project(tempDir(), {
|
|
199
|
+
'@astryxdesign/core': '0.7.0',
|
|
200
|
+
'@astryxdesign/charts': 'file:../charts',
|
|
201
|
+
});
|
|
202
|
+
const set = packageSet('0.7.0');
|
|
203
|
+
install(set, '@astryxdesign/charts', {version: '0.7.0'});
|
|
204
|
+
expect(
|
|
205
|
+
qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
|
|
206
|
+
).toHaveProperty('reason');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('never fills an @astryxdesign/* integration, so its version never keeps a set out', () => {
|
|
210
|
+
const root = project(tempDir(), {
|
|
211
|
+
'@astryxdesign/core': '0.7.0',
|
|
212
|
+
'@astryxdesign/richtext': '^0.9.0',
|
|
213
|
+
});
|
|
214
|
+
const set = packageSet('0.7.0');
|
|
215
|
+
install(
|
|
216
|
+
set,
|
|
217
|
+
'@astryxdesign/richtext',
|
|
218
|
+
{version: '0.7.0'},
|
|
219
|
+
{manifest: true},
|
|
220
|
+
);
|
|
221
|
+
expect(
|
|
222
|
+
qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
|
|
223
|
+
).toMatchObject({root: set});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('fills nothing the project installed', () => {
|
|
227
|
+
const root = project(tempDir(), {
|
|
228
|
+
'@astryxdesign/core': '0.7.0',
|
|
229
|
+
'@astryxdesign/charts': '^0.6.0',
|
|
230
|
+
});
|
|
231
|
+
install(root, '@astryxdesign/charts', {version: '0.6.3'});
|
|
232
|
+
const set = packageSet('0.7.0');
|
|
233
|
+
install(set, '@astryxdesign/charts', {version: '0.7.0'});
|
|
234
|
+
expect(
|
|
235
|
+
qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
|
|
236
|
+
).toMatchObject({root: set});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('needs a host entry when asked to', () => {
|
|
240
|
+
const plain = packageSet('0.7.0');
|
|
241
|
+
const hosted = packageSet('0.7.0', {hostEntry: true});
|
|
242
|
+
const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
|
|
243
|
+
expect(
|
|
244
|
+
qualifyingRoot({
|
|
245
|
+
release: '0.7.0',
|
|
246
|
+
projectDir: root,
|
|
247
|
+
roots: [plain, hosted],
|
|
248
|
+
needsHostEntry: true,
|
|
249
|
+
}),
|
|
250
|
+
).toMatchObject({
|
|
251
|
+
root: hosted,
|
|
252
|
+
hostEntry: path.join(
|
|
253
|
+
hosted,
|
|
254
|
+
'node_modules',
|
|
255
|
+
'@astryxdesign',
|
|
256
|
+
'cli',
|
|
257
|
+
'host',
|
|
258
|
+
'index.mjs',
|
|
259
|
+
),
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("reads a root's own node_modules only", () => {
|
|
264
|
+
const outer = tempDir('outer');
|
|
265
|
+
install(outer, '@astryxdesign/core', {version: '0.7.0'});
|
|
266
|
+
const set = path.join(outer, 'set');
|
|
267
|
+
install(set, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
|
|
268
|
+
expect(rootPackage(set, '@astryxdesign/core')).toBeNull();
|
|
269
|
+
expect(
|
|
270
|
+
qualifyingRoot({release: '0.7.0', projectDir: null, roots: [set]}),
|
|
271
|
+
).toHaveProperty('reason');
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('the integrations a host approves', () => {
|
|
276
|
+
it('load by name from the package set that holds the CLI, with the same checks', () => {
|
|
277
|
+
const set = tempDir('set');
|
|
278
|
+
const cliDir = install(set, '@astryxdesign/cli', {version: '0.7.2'});
|
|
279
|
+
// The set declares only the CLI and Core, so FR12's scan finds nothing.
|
|
280
|
+
writeJson(path.join(set, 'package.json'), {
|
|
281
|
+
dependencies: {'@astryxdesign/cli': '0.7.2'},
|
|
282
|
+
});
|
|
283
|
+
install(set, 'acme-widgets', {version: '1.0.0'}, {manifest: true});
|
|
284
|
+
install(
|
|
285
|
+
set,
|
|
286
|
+
'acme-old',
|
|
287
|
+
{version: '1.0.0', peerDependencies: {'@astryxdesign/core': '^0.6.0'}},
|
|
288
|
+
{manifest: true},
|
|
289
|
+
);
|
|
290
|
+
install(set, 'acme-plain', {version: '1.0.0'});
|
|
291
|
+
expect(
|
|
292
|
+
scanComputerIntegrations({cliRoot: cliDir, release: '0.7.2'}),
|
|
293
|
+
).toEqual([]);
|
|
294
|
+
const found = scanComputerIntegrations({
|
|
295
|
+
cliRoot: cliDir,
|
|
296
|
+
release: '0.7.2',
|
|
297
|
+
approved: ['acme-widgets', 'acme-old', 'acme-plain', 'acme-missing'],
|
|
298
|
+
});
|
|
299
|
+
expect(found.map(c => [c.name, c.incompatible])).toEqual([
|
|
300
|
+
['acme-widgets', null],
|
|
301
|
+
[
|
|
302
|
+
'acme-old',
|
|
303
|
+
"it needs @astryxdesign/core ^0.6.0, and this computer's CLI is 0.7.2",
|
|
304
|
+
],
|
|
305
|
+
]);
|
|
306
|
+
expect(found[0].packageDir).toBe(
|
|
307
|
+
path.join(set, 'node_modules', 'acme-widgets'),
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('never takes an approved name from a folder above the package set', () => {
|
|
312
|
+
const outer = tempDir('outer');
|
|
313
|
+
install(outer, 'acme-outer', {version: '1.0.0'}, {manifest: true});
|
|
314
|
+
const set = path.join(outer, 'set');
|
|
315
|
+
const cliDir = install(set, '@astryxdesign/cli', {version: '0.7.2'});
|
|
316
|
+
writeJson(path.join(set, 'package.json'), {
|
|
317
|
+
dependencies: {'@astryxdesign/cli': '0.7.2'},
|
|
318
|
+
});
|
|
319
|
+
expect(
|
|
320
|
+
scanComputerIntegrations({
|
|
321
|
+
cliRoot: cliDir,
|
|
322
|
+
release: '0.7.2',
|
|
323
|
+
approved: ['acme-outer'],
|
|
324
|
+
}),
|
|
325
|
+
).toEqual([]);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('where each package a hosted run resolved came from', () => {
|
|
330
|
+
afterEach(() => _resetHostedRun());
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @param {string} root
|
|
334
|
+
* @param {string} name
|
|
335
|
+
*/
|
|
336
|
+
const at = (root, name) =>
|
|
337
|
+
path.join(root, 'node_modules', ...name.split('/'));
|
|
338
|
+
|
|
339
|
+
it("names the project's packages as the project's when the host's CLI is installed in the project", () => {
|
|
340
|
+
const app = tempDir('app');
|
|
341
|
+
activateHostedRun({startingRoot: app, projectRoot: app});
|
|
342
|
+
recordResolved('@astryxdesign/cli', '0.7.2', at(app, '@astryxdesign/cli'));
|
|
343
|
+
recordResolved(
|
|
344
|
+
'@astryxdesign/core',
|
|
345
|
+
'0.7.2',
|
|
346
|
+
at(app, '@astryxdesign/core'),
|
|
347
|
+
'project',
|
|
348
|
+
);
|
|
349
|
+
recordResolvedIntegrations([
|
|
350
|
+
{
|
|
351
|
+
name: 'acme-widgets',
|
|
352
|
+
version: '1.0.0',
|
|
353
|
+
__packageDir: at(app, 'acme-widgets'),
|
|
354
|
+
},
|
|
355
|
+
]);
|
|
356
|
+
expect(resolvedPackages()).toEqual([
|
|
357
|
+
{name: '@astryxdesign/cli', version: '0.7.2', source: 'project'},
|
|
358
|
+
{name: '@astryxdesign/core', version: '0.7.2', source: 'project'},
|
|
359
|
+
{name: 'acme-widgets', version: '1.0.0', source: 'project'},
|
|
360
|
+
]);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("names what a package set holds as the package set's, and the project's integrations as the project's", () => {
|
|
364
|
+
const set = tempDir('set');
|
|
365
|
+
const app = tempDir('app');
|
|
366
|
+
activateHostedRun({startingRoot: set, projectRoot: app});
|
|
367
|
+
recordResolved('@astryxdesign/cli', '0.7.2', at(set, '@astryxdesign/cli'));
|
|
368
|
+
recordResolved(
|
|
369
|
+
'@astryxdesign/core',
|
|
370
|
+
'0.7.2',
|
|
371
|
+
at(set, '@astryxdesign/core'),
|
|
372
|
+
);
|
|
373
|
+
recordResolvedIntegrations([
|
|
374
|
+
{
|
|
375
|
+
name: 'acme-tools',
|
|
376
|
+
version: '2.0.0',
|
|
377
|
+
__packageDir: at(set, 'acme-tools'),
|
|
378
|
+
__computer: true,
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: 'acme-widgets',
|
|
382
|
+
version: '1.0.0',
|
|
383
|
+
__packageDir: at(app, 'acme-widgets'),
|
|
384
|
+
},
|
|
385
|
+
]);
|
|
386
|
+
expect(resolvedPackages()).toEqual([
|
|
387
|
+
{name: '@astryxdesign/cli', version: '0.7.2', source: 'package-set'},
|
|
388
|
+
{name: '@astryxdesign/core', version: '0.7.2', source: 'package-set'},
|
|
389
|
+
{name: 'acme-tools', version: '2.0.0', source: 'package-set'},
|
|
390
|
+
{name: 'acme-widgets', version: '1.0.0', source: 'project'},
|
|
391
|
+
]);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("names what the project loaded or found above itself, as a workspace hoists it, as the project's", () => {
|
|
395
|
+
// The host's CLI sits in the workspace root, which is its package set,
|
|
396
|
+
// and the project is a workspace package whose packages are hoisted there.
|
|
397
|
+
const mono = tempDir('mono');
|
|
398
|
+
writeJson(path.join(mono, 'package.json'), {
|
|
399
|
+
private: true,
|
|
400
|
+
workspaces: ['packages/*'],
|
|
401
|
+
});
|
|
402
|
+
const app = path.join(mono, 'packages', 'app');
|
|
403
|
+
writeJson(path.join(app, 'package.json'), {
|
|
404
|
+
name: 'app',
|
|
405
|
+
dependencies: {'@astryxdesign/core': '0.7.2', 'acme-widgets': '1.0.0'},
|
|
406
|
+
});
|
|
407
|
+
install(mono, '@astryxdesign/core', {version: '0.7.2'});
|
|
408
|
+
activateHostedRun({startingRoot: mono, projectRoot: app});
|
|
409
|
+
expect(findCoreDir(app)).toBe(at(mono, '@astryxdesign/core'));
|
|
410
|
+
recordResolvedIntegrations([
|
|
411
|
+
{
|
|
412
|
+
name: 'acme-widgets',
|
|
413
|
+
version: '1.0.0',
|
|
414
|
+
__packageDir: at(mono, 'acme-widgets'),
|
|
415
|
+
},
|
|
416
|
+
]);
|
|
417
|
+
expect(resolvedPackages()).toEqual([
|
|
418
|
+
{name: '@astryxdesign/core', version: '0.7.2', source: 'project'},
|
|
419
|
+
{name: 'acme-widgets', version: '1.0.0', source: 'project'},
|
|
420
|
+
]);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('names a package set inside the project folder as a package set', () => {
|
|
424
|
+
const app = tempDir('app');
|
|
425
|
+
const set = path.join(app, '.tools', 'astryx');
|
|
426
|
+
activateHostedRun({startingRoot: set, projectRoot: app});
|
|
427
|
+
recordResolved('@astryxdesign/cli', '0.7.2', at(set, '@astryxdesign/cli'));
|
|
428
|
+
expect(resolvedPackages()).toEqual([
|
|
429
|
+
{name: '@astryxdesign/cli', version: '0.7.2', source: 'package-set'},
|
|
430
|
+
]);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
@@ -28,7 +28,8 @@ export function findClosestComponents(name: string, components: Record<string, s
|
|
|
28
28
|
* 30 keyword Levenshtein distance 2
|
|
29
29
|
* 20 name Levenshtein distance 3
|
|
30
30
|
*
|
|
31
|
-
* Each result: { name, score, reason }
|
|
31
|
+
* Each result: { name, score, reason }, plus `keywordAt` when a keyword set
|
|
32
|
+
* the component's score: the position of the keyword that set it.
|
|
32
33
|
*
|
|
33
34
|
* @param {string} needle
|
|
34
35
|
* @param {string} coreDir
|
|
@@ -48,7 +48,8 @@ export function findClosestComponents(name, components, maxDistance = 3) {
|
|
|
48
48
|
* 30 keyword Levenshtein distance 2
|
|
49
49
|
* 20 name Levenshtein distance 3
|
|
50
50
|
*
|
|
51
|
-
* Each result: { name, score, reason }
|
|
51
|
+
* Each result: { name, score, reason }, plus `keywordAt` when a keyword set
|
|
52
|
+
* the component's score: the position of the keyword that set it.
|
|
52
53
|
*
|
|
53
54
|
* @param {string} needle
|
|
54
55
|
* @param {string} coreDir
|
|
@@ -64,6 +65,9 @@ export async function searchComponents(needle, coreDir, components) {
|
|
|
64
65
|
const allNames = Object.values(components).flat();
|
|
65
66
|
/** @type {Map<string, {name: string, score: number, reason: string}>} */
|
|
66
67
|
const scored = new Map();
|
|
68
|
+
/** Position of the keyword that set each component's score. */
|
|
69
|
+
/** @type {Map<string, number>} */
|
|
70
|
+
const keywordAt = new Map();
|
|
67
71
|
|
|
68
72
|
/**
|
|
69
73
|
* @param {string} name
|
|
@@ -146,12 +150,14 @@ export async function searchComponents(needle, coreDir, components) {
|
|
|
146
150
|
|
|
147
151
|
// Keyword matching
|
|
148
152
|
if (docs.keywords && Array.isArray(docs.keywords)) {
|
|
149
|
-
for (const kw of docs.keywords) {
|
|
153
|
+
for (const [at, kw] of docs.keywords.entries()) {
|
|
150
154
|
const kwLower = kw.toLowerCase();
|
|
155
|
+
const before = scored.get(comp)?.score ?? 0;
|
|
151
156
|
|
|
152
157
|
// Exact keyword
|
|
153
158
|
if (kwLower === term) {
|
|
154
159
|
addMatch(comp, 90, 'keyword "' + kw + '"');
|
|
160
|
+
if (!keywordAt.has(comp) || before < 90) keywordAt.set(comp, at);
|
|
155
161
|
break;
|
|
156
162
|
}
|
|
157
163
|
|
|
@@ -170,6 +176,7 @@ export async function searchComponents(needle, coreDir, components) {
|
|
|
170
176
|
addMatch(comp, 70, 'keyword "' + kw + '" (distance ' + dist + ')');
|
|
171
177
|
else if (dist === 2)
|
|
172
178
|
addMatch(comp, 30, 'keyword "' + kw + '" (distance ' + dist + ')');
|
|
179
|
+
if ((scored.get(comp)?.score ?? 0) > before) keywordAt.set(comp, at);
|
|
173
180
|
}
|
|
174
181
|
}
|
|
175
182
|
|
|
@@ -202,9 +209,13 @@ export async function searchComponents(needle, coreDir, components) {
|
|
|
202
209
|
}
|
|
203
210
|
}
|
|
204
211
|
|
|
205
|
-
const results = Array.from(scored.values())
|
|
206
|
-
(
|
|
207
|
-
|
|
212
|
+
const results = Array.from(scored.values())
|
|
213
|
+
.map(r =>
|
|
214
|
+
keywordAt.has(r.name)
|
|
215
|
+
? {...r, keywordAt: /** @type {number} */ (keywordAt.get(r.name))}
|
|
216
|
+
: r,
|
|
217
|
+
)
|
|
218
|
+
.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
208
219
|
|
|
209
220
|
return results;
|
|
210
221
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
* A name's words, lowercase and singular: camelCase, PascalCase, kebab-case,
|
|
6
|
+
* snake_case, slashes and spaces all split.
|
|
7
|
+
* @param {string} text
|
|
8
|
+
* @returns {string[]}
|
|
9
|
+
*/
|
|
10
|
+
export function nameWords(text: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* The same items, closest to the typed name first; each item is as close as
|
|
13
|
+
* its closest name. Equal matches keep their original order.
|
|
14
|
+
* @template T
|
|
15
|
+
* @param {string} typedName
|
|
16
|
+
* @param {T[]} items
|
|
17
|
+
* @param {(item: T) => Array<string | undefined>} namesOf
|
|
18
|
+
* @returns {T[]}
|
|
19
|
+
*/
|
|
20
|
+
export function orderByAffinity<T>(typedName: string, items: T[], namesOf: (item: T) => Array<string | undefined>): T[];
|
|
21
|
+
/**
|
|
22
|
+
* Docs route suggestions, closest to the typed route first. When every
|
|
23
|
+
* suggestion sits under one namespace the typed route also starts with, that
|
|
24
|
+
* shared part is left out of the comparison: it says nothing about which page
|
|
25
|
+
* was meant. Equal matches keep their order.
|
|
26
|
+
* @template {{name: string}} T
|
|
27
|
+
* @param {string} topic - the typed route
|
|
28
|
+
* @param {T[]} suggestions
|
|
29
|
+
* @returns {T[]}
|
|
30
|
+
*/
|
|
31
|
+
export function orderRouteSuggestions<T extends {
|
|
32
|
+
name: string;
|
|
33
|
+
}>(topic: string, suggestions: T[]): T[];
|
|
34
|
+
/**
|
|
35
|
+
* The components whose synonyms name what was typed. A synonym matches when
|
|
36
|
+
* its words, with or without a leading library name, are the typed words
|
|
37
|
+
* (`MUI TextField` matches `MUI TextField`, `TextField` and `text-field`).
|
|
38
|
+
* @param {string} needle
|
|
39
|
+
* @param {Record<string, {synonyms?: string[]}>} cards - component name → its search card
|
|
40
|
+
* @returns {Set<string>}
|
|
41
|
+
*/
|
|
42
|
+
export function synonymMatches(needle: string, cards: Record<string, {
|
|
43
|
+
synonyms?: string[];
|
|
44
|
+
}>): Set<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Rank component suggestions for a name that resolved to nothing.
|
|
47
|
+
*
|
|
48
|
+
* Every component `searchComponents` scored keeps its score. A component whose
|
|
49
|
+
* name has exactly the typed words (`chat-messages` for `ChatMessage`) scores
|
|
50
|
+
* like an exact name; one of whose synonyms is the typed name (see
|
|
51
|
+
* {@link synonymMatches}) like an exact keyword; one whose name shares the
|
|
52
|
+
* typed compound name's words (see {@link compoundMatch}) just below a
|
|
53
|
+
* one-letter typo of a name. Equal scores put synonym matches first, then order by how many of the typed words the
|
|
54
|
+
* name carries, then by how early the keyword that matched sits in the
|
|
55
|
+
* component's keywords, then by edit distance, then by shared leading letters,
|
|
56
|
+
* then by name. The caller decides how many to print.
|
|
57
|
+
* @param {string} needle - the typed component name (no `XDS` prefix)
|
|
58
|
+
* @param {Array<{name: string, score: number, reason: string, keywordAt?: number}>} results - `searchComponents` output
|
|
59
|
+
* @param {string[]} allNames - every component name
|
|
60
|
+
* @param {Set<string>} [synonyms] - {@link synonymMatches} for the typed name
|
|
61
|
+
* @returns {Array<{name: string, score: number, reason: string}>}
|
|
62
|
+
*/
|
|
63
|
+
export function rankComponentSuggestions(needle: string, results: Array<{
|
|
64
|
+
name: string;
|
|
65
|
+
score: number;
|
|
66
|
+
reason: string;
|
|
67
|
+
keywordAt?: number;
|
|
68
|
+
}>, allNames: string[], synonyms?: Set<string>): Array<{
|
|
69
|
+
name: string;
|
|
70
|
+
score: number;
|
|
71
|
+
reason: string;
|
|
72
|
+
}>;
|