@astryxdesign/cli 0.6.4-canary.ed2e54e → 0.6.4
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 +66 -64
- package/api/component/_adapter.d.mts +0 -25
- package/api/component/_adapter.mjs +5 -59
- package/api/component/component.d.mts +3 -6
- package/api/component/component.doc.mjs +10 -23
- package/api/component/component.mjs +9 -249
- package/api/component/component.type.d.mts +0 -25
- package/api/component/component.type.mjs +0 -44
- package/api/discover/_adapter.d.mts +6 -114
- package/api/discover/_adapter.mjs +17 -372
- package/api/discover/detail/detail.d.mts +6 -18
- package/api/discover/detail/detail.mjs +13 -67
- package/api/discover/detail/detail.test.mjs +0 -85
- package/api/discover/discover.d.mts +9 -3
- package/api/discover/discover.doc.mjs +18 -61
- package/api/discover/discover.mjs +36 -220
- package/api/discover/discover.test.mjs +2 -11
- package/api/discover/discover.type.d.mts +8 -147
- package/api/discover/discover.type.mjs +12 -102
- package/api/discover/list/list.d.mts +6 -20
- package/api/discover/list/list.mjs +12 -45
- package/api/discover/list/list.test.mjs +0 -46
- package/api/discover/search/search.d.mts +16 -18
- package/api/discover/search/search.mjs +56 -102
- package/api/discover/search/search.test.mjs +10 -144
- package/api/docs/docs.test.mjs +0 -2
- package/api/doctor/doctor.d.mts +3 -8
- package/api/doctor/doctor.mjs +9 -90
- package/api/doctor/doctor.test.mjs +10 -122
- package/api/index.d.mts +2 -1
- package/api/index.mjs +4 -4
- package/api/integration/add-helpers.d.mts +2 -5
- package/api/integration/add-helpers.mjs +9 -36
- package/api/integration/pack-check.mjs +3 -28
- package/api/json/index.ts +1 -0
- package/api/layout/_adapter.d.mts +34 -0
- package/api/layout/_adapter.mjs +148 -0
- package/api/layout/check/check.d.mts +16 -0
- package/api/layout/check/check.mjs +40 -0
- package/api/layout/expand/expand.d.mts +22 -0
- package/api/layout/expand/expand.mjs +155 -0
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.d.mts +13 -0
- package/api/layout/grammar/grammar.mjs +87 -0
- package/api/layout/layout.d.mts +6 -0
- package/api/layout/layout.mjs +17 -0
- package/api/layout/layout.test.mjs +297 -0
- package/api/layout/layout.type.d.mts +89 -0
- package/api/layout/layout.type.mjs +103 -0
- package/api/layout/layoutCheck.doc.d.mts +11 -0
- package/api/layout/layoutCheck.doc.mjs +85 -0
- package/api/layout/layoutExpand.doc.d.mts +11 -0
- package/api/layout/layoutExpand.doc.mjs +107 -0
- package/api/layout/layoutGrammar.doc.d.mts +11 -0
- package/api/layout/layoutGrammar.doc.mjs +57 -0
- package/api/search/search.test.mjs +0 -18
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.mjs +1 -1
- package/api/theme/add/add.mjs +25 -17
- package/api/theme/add/add.staging.test.mjs +23 -40
- package/api/theme/build/build.family.test.mjs +12 -7
- package/api/theme/build/build.mjs +18 -8
- package/api/upgrade/run/run.mjs +4 -6
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +1 -3
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +4 -5
- package/assets/docs/internationalization.doc.mjs +5 -7
- package/assets/docs/tree/integrations.doc.mjs +1 -20
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +1 -9
- package/authoring/config/parse.d.mts +0 -2
- package/authoring/config/parse.mjs +0 -19
- package/authoring/config/parse.test.mjs +0 -8
- package/authoring/config/type.ts +2 -13
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +0 -10
- package/authoring/index.mjs +0 -1
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/component/index.mjs +55 -152
- package/clients/cli/commands/component-ownership.test.mjs +0 -89
- package/clients/cli/commands/component.doc.mjs +6 -23
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/discover.doc.mjs +9 -53
- package/clients/cli/commands/discover.mjs +118 -393
- package/clients/cli/commands/docs.test.mjs +0 -29
- package/clients/cli/commands/layout-check.doc.mjs +65 -0
- package/clients/cli/commands/layout-expand.doc.mjs +83 -0
- package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
- package/clients/cli/commands/layout.doc.mjs +34 -0
- package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
- package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
- package/clients/cli/commands/layout.mjs +275 -0
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/text-json-parity.test.mjs +17 -0
- package/clients/cli/index.mjs +4 -0
- package/clients/cli/lib/exit-codes.test.mjs +8 -1
- package/clients/cli/lib/json-shim.mjs +14 -24
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +8 -3
- package/clients/cli/lib/manifest.test.mjs +2 -5
- package/foundation/discovery/authoring-self-docs.mjs +0 -1
- package/foundation/discovery/template-adapter.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +12 -0
- package/foundation/doc-compiler/tree.test.mjs +1 -9
- package/foundation/integrations/integrations.d.mts +1 -14
- package/foundation/integrations/integrations.mjs +1 -41
- package/foundation/integrations/integrations.test.mjs +0 -31
- package/foundation/response/response-types.doc.mjs +21 -15
- package/foundation/response/response-types.doc.test.mjs +0 -23
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.mjs +2 -2
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/package.json +9 -9
- package/api/discover/_adapter.test.mjs +0 -215
- package/api/discover/_catalog-view.d.mts +0 -115
- package/api/discover/_catalog-view.mjs +0 -203
- package/api/discover/_catalog-view.test.mjs +0 -128
- package/api/discover/detail/item/item.d.mts +0 -26
- package/api/discover/detail/item/item.mjs +0 -78
- package/api/discover/detail/item/item.test.mjs +0 -73
- package/api/integration/pack-check.lifecycle-output.test.mjs +0 -105
- package/api/theme/add/add.rollback.test.mjs +0 -158
- package/api/theme/build/build.rollback.test.mjs +0 -148
- package/api/upgrade/run/files-changed.test.mjs +0 -111
- package/assets/codemods/file-count.test.mjs +0 -163
- package/assets/docs/tree/component-lookups.doc.mjs +0 -149
- package/authoring/discover/discover.doc.d.mts +0 -13
- package/authoring/discover/discover.doc.mjs +0 -138
- package/authoring/discover/parse.d.mts +0 -24
- package/authoring/discover/parse.mjs +0 -128
- package/authoring/discover/parse.test.mjs +0 -124
- package/authoring/discover/type.ts +0 -87
- package/clients/cli/commands/component-batch.test.mjs +0 -341
- package/clients/cli/commands/discover.sources.test.mjs +0 -267
- package/clients/cli/lib/parse-error-format.test.mjs +0 -81
- package/foundation/response/batch.type.d.mts +0 -33
- package/foundation/response/batch.type.mjs +0 -34
|
@@ -3,13 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @param {string} [query]
|
|
6
|
-
* @param {
|
|
6
|
+
* @param {object} [options]
|
|
7
|
+
* @param {boolean} [options.components]
|
|
8
|
+
* @param {string} [options.lang]
|
|
9
|
+
* @param {boolean} [options.zh]
|
|
7
10
|
* @returns {Promise<
|
|
8
11
|
* import('./discover.type.mjs').DiscoverListResponse |
|
|
9
12
|
* import('./discover.type.mjs').DiscoverDetailResponse |
|
|
10
13
|
* import('./discover.type.mjs').DiscoverDetailDocResponse |
|
|
11
|
-
* import('./discover.type.mjs').DiscoverItemResponse |
|
|
12
14
|
* import('./discover.type.mjs').DiscoverSearchResponse
|
|
13
15
|
* >}
|
|
14
16
|
*/
|
|
15
|
-
export function discover(query?: string, options?:
|
|
17
|
+
export function discover(query?: string, options?: {
|
|
18
|
+
components?: boolean | undefined;
|
|
19
|
+
lang?: string | undefined;
|
|
20
|
+
zh?: boolean | undefined;
|
|
21
|
+
}): Promise<import("./discover.type.mjs").DiscoverListResponse | import("./discover.type.mjs").DiscoverDetailResponse | import("./discover.type.mjs").DiscoverDetailDocResponse | import("./discover.type.mjs").DiscoverSearchResponse>;
|
|
@@ -13,25 +13,22 @@ export const doc = {
|
|
|
13
13
|
name: 'discover',
|
|
14
14
|
namespace: 'cli/api',
|
|
15
15
|
displayName: 'discover()',
|
|
16
|
-
summary:
|
|
17
|
-
'Browse and search integrations: the ones a project has and, through discover sources, the ones it could add.',
|
|
16
|
+
summary: 'Browse and search components from configured external packages.',
|
|
18
17
|
description:
|
|
19
|
-
'
|
|
20
|
-
'the
|
|
21
|
-
'
|
|
22
|
-
"
|
|
23
|
-
'
|
|
18
|
+
'Explores components contributed by configured external packages and integrations ' +
|
|
19
|
+
'the ones that declare a components root. With no query it lists those packages; ' +
|
|
20
|
+
'an @scope/name query browses one package; @scope/name/Component (or a free-text ' +
|
|
21
|
+
"term that resolves to a single component) returns that component's validated doc; " +
|
|
22
|
+
'a free-text term with several matches returns the candidate list.',
|
|
24
23
|
importPath: '@astryxdesign/cli/api',
|
|
25
24
|
signature:
|
|
26
|
-
'discover(query?: string, options?: DiscoverOptions): Promise<DiscoverListResponse | DiscoverDetailResponse | DiscoverDetailDocResponse |
|
|
25
|
+
'discover(query?: string, options?: DiscoverOptions): Promise<DiscoverListResponse | DiscoverDetailResponse | DiscoverDetailDocResponse | DiscoverSearchResponse>',
|
|
27
26
|
keywords: [
|
|
28
27
|
'discover',
|
|
29
28
|
'packages',
|
|
30
29
|
'integrations',
|
|
31
30
|
'external',
|
|
32
31
|
'components',
|
|
33
|
-
'catalog',
|
|
34
|
-
'versions',
|
|
35
32
|
'search',
|
|
36
33
|
],
|
|
37
34
|
params: [
|
|
@@ -39,39 +36,13 @@ export const doc = {
|
|
|
39
36
|
name: 'query',
|
|
40
37
|
type: 'string',
|
|
41
38
|
description:
|
|
42
|
-
'
|
|
39
|
+
'An @scope/name package, an @scope/name/Component path, or a free-text term. Omit to list all configured packages.',
|
|
43
40
|
},
|
|
44
41
|
{
|
|
45
42
|
name: 'options.components',
|
|
46
43
|
type: 'boolean',
|
|
47
44
|
description:
|
|
48
|
-
'In the CLI package list, print every component
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: 'options.type',
|
|
52
|
-
type: "'component' | 'template' | 'doc' | 'theme' | 'codemod' | 'agent-doc'",
|
|
53
|
-
description:
|
|
54
|
-
'Only one kind: in the list, packages that add it; in a search, items of that kind.',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'options.installed',
|
|
58
|
-
type: 'boolean',
|
|
59
|
-
description:
|
|
60
|
-
'Only what the project has. Cannot be set with options.available.',
|
|
61
|
-
default: 'false',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: 'options.available',
|
|
65
|
-
type: 'boolean',
|
|
66
|
-
description:
|
|
67
|
-
'Only what the project could add. Cannot be set with options.installed.',
|
|
68
|
-
default: 'false',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'options.limit',
|
|
72
|
-
type: 'number',
|
|
73
|
-
description: 'Max number of search results, a positive integer.',
|
|
74
|
-
default: '20',
|
|
45
|
+
'In the CLI package list, print every component of each package instead of the first 10. A display flag for the CLI renderer; the programmatic response is unchanged.',
|
|
75
46
|
},
|
|
76
47
|
{
|
|
77
48
|
name: 'options.lang',
|
|
@@ -89,27 +60,21 @@ export const doc = {
|
|
|
89
60
|
{
|
|
90
61
|
type: 'discover.list',
|
|
91
62
|
description:
|
|
92
|
-
'The
|
|
63
|
+
'The configured external packages (name, category, components, version, description). When empty it carries meta.configured to distinguish "nothing configured" from "configured but nothing discovered".',
|
|
93
64
|
},
|
|
94
65
|
{
|
|
95
66
|
type: 'discover.detail',
|
|
96
|
-
description:
|
|
97
|
-
'One package: what the shown version adds, whether the project has it, its versions and latest release when a source knows them, and the command that adds it when the project does not have it.',
|
|
67
|
+
description: 'A single package entry, for an @scope/name query.',
|
|
98
68
|
},
|
|
99
69
|
{
|
|
100
70
|
type: 'discover.detail.doc',
|
|
101
71
|
description:
|
|
102
|
-
'The validated ComponentDoc for one
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
type: 'discover.item',
|
|
106
|
-
description:
|
|
107
|
-
'One item that is not an installed component: its kind, name, the package and version that add it, and whether the project has the package.',
|
|
72
|
+
'The validated ComponentDoc for one external component: an @scope/name/Component query, or a free-text term that resolves to exactly one component.',
|
|
108
73
|
},
|
|
109
74
|
{
|
|
110
75
|
type: 'discover.search',
|
|
111
76
|
description:
|
|
112
|
-
'The query echoed back plus
|
|
77
|
+
'The query echoed back plus the matching {package, component} pairs, when a free-text term matches several components.',
|
|
113
78
|
},
|
|
114
79
|
],
|
|
115
80
|
throws: [
|
|
@@ -117,21 +82,17 @@ export const doc = {
|
|
|
117
82
|
code: 'ERR_INVALID_ARGUMENT',
|
|
118
83
|
when: 'the query is a non-string value, or a free-text search is run with an empty query',
|
|
119
84
|
},
|
|
120
|
-
{
|
|
121
|
-
code: 'ERR_INVALID_OPTION',
|
|
122
|
-
when: 'options.type is not a known kind, options.limit is not a positive integer, or options.installed and options.available are both set',
|
|
123
|
-
},
|
|
124
85
|
{
|
|
125
86
|
code: 'ERR_UNKNOWN_PACKAGE',
|
|
126
|
-
when: '
|
|
87
|
+
when: 'the @scope/name package is not among the configured packages',
|
|
127
88
|
},
|
|
128
89
|
{
|
|
129
90
|
code: 'ERR_UNKNOWN_COMPONENT',
|
|
130
|
-
when: 'the
|
|
91
|
+
when: 'the component is not found in the named @scope/name package',
|
|
131
92
|
},
|
|
132
93
|
{
|
|
133
94
|
code: 'ERR_NOT_FOUND',
|
|
134
|
-
when: 'a free-text term matches
|
|
95
|
+
when: 'a free-text term matches no component in any package',
|
|
135
96
|
},
|
|
136
97
|
{
|
|
137
98
|
code: 'ERR_INVALID_DOC',
|
|
@@ -139,14 +100,10 @@ export const doc = {
|
|
|
139
100
|
},
|
|
140
101
|
],
|
|
141
102
|
examples: [
|
|
142
|
-
{label: 'List packages', code: 'const {data
|
|
103
|
+
{label: 'List packages', code: 'const {data} = await discover();'},
|
|
143
104
|
{label: 'Browse a package', code: "await discover('@acme/ui');"},
|
|
144
|
-
{label: 'One version', code: "await discover('@acme/ui@2.1.0');"},
|
|
145
105
|
{label: 'Show a component doc', code: "await discover('@acme/ui/Button');"},
|
|
146
|
-
{
|
|
147
|
-
label: 'Search templates to add',
|
|
148
|
-
code: "await discover('dashboard', {type: 'template', available: true});",
|
|
149
|
-
},
|
|
106
|
+
{label: 'Free-text search', code: "await discover('button');"},
|
|
150
107
|
],
|
|
151
108
|
command: 'discover',
|
|
152
109
|
related: ['component', 'search', 'template'],
|
|
@@ -3,193 +3,55 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Programmatic API for the discover command — dispatcher + barrel.
|
|
5
5
|
*
|
|
6
|
-
* `discover()`
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `discover()` keeps the exact same signature and response union it always had,
|
|
7
|
+
* so `api/index.mjs` and the CLI consumer are untouched. Its only job now is to
|
|
8
|
+
* discover the external packages (via ./_adapter) and route to the leaf that
|
|
9
|
+
* owns each response shape:
|
|
10
10
|
*
|
|
11
|
-
* discover
|
|
12
|
-
* discover
|
|
13
|
-
* discover
|
|
14
|
-
* discover
|
|
15
|
-
* discover <words> -> ./search/search.mjs discover.search (always a list)
|
|
11
|
+
* discover.list -> ./list/list.mjs
|
|
12
|
+
* discover.detail -> ./detail/detail.mjs
|
|
13
|
+
* discover.detail.doc -> ./detail/doc/doc.mjs
|
|
14
|
+
* discover.search -> ./search/search.mjs
|
|
16
15
|
*
|
|
17
|
-
*
|
|
18
|
-
* in extra fields, or as extra search matches, so every existing field keeps
|
|
19
|
-
* its meaning.
|
|
20
|
-
*
|
|
21
|
-
* @position api/discover — thin router over ./_adapter, ./_catalog-view, and
|
|
22
|
-
* the discover leaves.
|
|
16
|
+
* @position api/discover — thin router over ./_adapter + the discover leaves.
|
|
23
17
|
*/
|
|
24
18
|
|
|
25
|
-
import {
|
|
26
|
-
addCommand,
|
|
27
|
-
callSources,
|
|
28
|
-
declaredDependencies,
|
|
29
|
-
describeInstalled,
|
|
30
|
-
discoverPackages,
|
|
31
|
-
findComponent,
|
|
32
|
-
hasSources,
|
|
33
|
-
} from './_adapter.mjs';
|
|
34
|
-
import {
|
|
35
|
-
availableEntries,
|
|
36
|
-
catalogState,
|
|
37
|
-
searchItems,
|
|
38
|
-
withLatest,
|
|
39
|
-
} from './_catalog-view.mjs';
|
|
19
|
+
import {discoverPackages} from './_adapter.mjs';
|
|
40
20
|
import {list} from './list/list.mjs';
|
|
41
21
|
import {detail} from './detail/detail.mjs';
|
|
42
|
-
import {doc
|
|
43
|
-
import {item} from './detail/item/item.mjs';
|
|
22
|
+
import {doc} from './detail/doc/doc.mjs';
|
|
44
23
|
import {search} from './search/search.mjs';
|
|
45
24
|
import {AstryxError} from '../error.mjs';
|
|
46
25
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
47
|
-
import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
48
|
-
import {DISCOVER_KINDS} from '../../authoring/discover/parse.mjs';
|
|
49
|
-
|
|
50
|
-
const DEFAULT_LIMIT = 20;
|
|
51
|
-
|
|
52
|
-
/** @type {import('./_adapter.mjs').CatalogResult} */
|
|
53
|
-
const NO_CATALOG = {sources: [], packages: []};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Split a package-shaped query: `name`, `name@version`, or `name/item`, with an
|
|
57
|
-
* optional `@scope/` prefix on the name.
|
|
58
|
-
* @param {string} query
|
|
59
|
-
* @returns {{scoped: boolean, name: string, version?: string, item?: string}}
|
|
60
|
-
*/
|
|
61
|
-
function splitTarget(query) {
|
|
62
|
-
const scoped = query.startsWith('@');
|
|
63
|
-
const firstSlash = query.indexOf('/');
|
|
64
|
-
let head = query;
|
|
65
|
-
/** @type {string | undefined} */
|
|
66
|
-
let rest;
|
|
67
|
-
const split = scoped
|
|
68
|
-
? firstSlash < 0
|
|
69
|
-
? -1
|
|
70
|
-
: query.indexOf('/', firstSlash + 1)
|
|
71
|
-
: firstSlash;
|
|
72
|
-
if (split > 0) {
|
|
73
|
-
head = query.slice(0, split);
|
|
74
|
-
rest = query.slice(split + 1) || undefined;
|
|
75
|
-
}
|
|
76
|
-
const at = head.indexOf('@', 1);
|
|
77
|
-
return {
|
|
78
|
-
scoped,
|
|
79
|
-
name: at > 0 ? head.slice(0, at) : head,
|
|
80
|
-
...(at > 0 && head.slice(at + 1) ? {version: head.slice(at + 1)} : {}),
|
|
81
|
-
...(rest ? {item: rest} : {}),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @param {{type?: unknown, installed?: boolean, available?: boolean, limit?: unknown}} options
|
|
87
|
-
*/
|
|
88
|
-
function checkOptions({type, installed, available, limit}) {
|
|
89
|
-
if (
|
|
90
|
-
type != null &&
|
|
91
|
-
!(/** @type {readonly unknown[]} */ (DISCOVER_KINDS).includes(type))
|
|
92
|
-
) {
|
|
93
|
-
throw new AstryxError(
|
|
94
|
-
`Unknown --type "${String(type)}"`,
|
|
95
|
-
DISCOVER_KINDS.map(kind => ({name: kind, reason: 'item kind'})),
|
|
96
|
-
ERROR_CODES.ERR_INVALID_OPTION,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
if (installed && available) {
|
|
100
|
-
throw new AstryxError(
|
|
101
|
-
'--installed and --available cannot be used together',
|
|
102
|
-
undefined,
|
|
103
|
-
ERROR_CODES.ERR_INVALID_OPTION,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
if (
|
|
107
|
-
limit != null &&
|
|
108
|
-
!(typeof limit === 'number' && Number.isInteger(limit) && limit > 0)
|
|
109
|
-
) {
|
|
110
|
-
throw new AstryxError(
|
|
111
|
-
`--limit must be a positive integer, not "${String(limit)}"`,
|
|
112
|
-
undefined,
|
|
113
|
-
ERROR_CODES.ERR_INVALID_OPTION,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Items a source lists for a package that look like the one asked for: names
|
|
120
|
-
* that contain it, else the closest few by spelling.
|
|
121
|
-
* @param {import('../../authoring/discover/type.js').DiscoverPackage} entry
|
|
122
|
-
* @param {string} wanted
|
|
123
|
-
* @returns {Array<{name: string, reason: string}>}
|
|
124
|
-
*/
|
|
125
|
-
function similarItems(entry, wanted) {
|
|
126
|
-
const lower = wanted.toLowerCase();
|
|
127
|
-
const names = [...new Set(entry.contributions.map(c => c.name))];
|
|
128
|
-
const hits = names.filter(name => name.toLowerCase().includes(lower));
|
|
129
|
-
const picked =
|
|
130
|
-
hits.length > 0
|
|
131
|
-
? hits.slice(0, 5)
|
|
132
|
-
: names
|
|
133
|
-
.map(name => ({
|
|
134
|
-
name,
|
|
135
|
-
distance: levenshteinDistance(lower, name.toLowerCase()),
|
|
136
|
-
}))
|
|
137
|
-
.filter(m => m.distance <= 3)
|
|
138
|
-
.sort((a, b) => a.distance - b.distance)
|
|
139
|
-
.slice(0, 5)
|
|
140
|
-
.map(m => m.name);
|
|
141
|
-
return picked.map(name => ({
|
|
142
|
-
name: `${entry.package}/${name}`,
|
|
143
|
-
reason: 'similar name',
|
|
144
|
-
}));
|
|
145
|
-
}
|
|
146
26
|
|
|
147
27
|
/**
|
|
148
28
|
* @param {string} [query]
|
|
149
|
-
* @param {
|
|
29
|
+
* @param {object} [options]
|
|
30
|
+
* @param {boolean} [options.components]
|
|
31
|
+
* @param {string} [options.lang]
|
|
32
|
+
* @param {boolean} [options.zh]
|
|
150
33
|
* @returns {Promise<
|
|
151
34
|
* import('./discover.type.mjs').DiscoverListResponse |
|
|
152
35
|
* import('./discover.type.mjs').DiscoverDetailResponse |
|
|
153
36
|
* import('./discover.type.mjs').DiscoverDetailDocResponse |
|
|
154
|
-
* import('./discover.type.mjs').DiscoverItemResponse |
|
|
155
37
|
* import('./discover.type.mjs').DiscoverSearchResponse
|
|
156
38
|
* >}
|
|
157
39
|
*/
|
|
158
40
|
export async function discover(query, options = {}) {
|
|
159
|
-
const {lang = null, zh = false
|
|
160
|
-
|
|
161
|
-
const only = options.installed
|
|
162
|
-
? /** @type {const} */ ('installed')
|
|
163
|
-
: options.available
|
|
164
|
-
? /** @type {const} */ ('available')
|
|
165
|
-
: undefined;
|
|
166
|
-
|
|
167
|
-
const {packages: scanned, configured, project} = await discoverPackages();
|
|
168
|
-
const withSources = hasSources(project);
|
|
169
|
-
const packages = await describeInstalled(project, scanned);
|
|
170
|
-
const declared = declaredDependencies(project);
|
|
171
|
-
const add = addCommand(project);
|
|
41
|
+
const {lang = null, zh = false} = options;
|
|
42
|
+
const {packages, configured} = await discoverPackages();
|
|
172
43
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
44
|
+
// No discoverable packages — either nothing configured, or configured but
|
|
45
|
+
// empty. The list leaf owns the empty envelope + `configured` flag. This
|
|
46
|
+
// short-circuits before query parsing, exactly as the flat command did.
|
|
47
|
+
if (packages.length === 0) return list(packages, {configured});
|
|
176
48
|
|
|
177
|
-
// No query
|
|
178
|
-
if (!query
|
|
179
|
-
if (!withSources) return list(packages, {configured, type, only});
|
|
180
|
-
const catalog = await catalogFor();
|
|
181
|
-
return list(withLatest(packages, catalog), {
|
|
182
|
-
configured,
|
|
183
|
-
type,
|
|
184
|
-
only,
|
|
185
|
-
available: availableEntries(catalog, packages, declared),
|
|
186
|
-
sources: catalog.sources,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
49
|
+
// No query: the full package list.
|
|
50
|
+
if (!query) return list(packages, {configured});
|
|
189
51
|
|
|
190
|
-
// A non-string (truthy) query would crash
|
|
191
|
-
// TypeError (no ERR_* code → downgrades to ERR_UNKNOWN). The CLI
|
|
192
|
-
// passes a string, but the public API must fail with a code.
|
|
52
|
+
// A non-string (truthy) query would crash `.startsWith`/`.indexOf` below
|
|
53
|
+
// with a raw TypeError (no ERR_* code → downgrades to ERR_UNKNOWN). The CLI
|
|
54
|
+
// only ever passes a string, but the public API must fail with a code.
|
|
193
55
|
if (typeof query !== 'string') {
|
|
194
56
|
throw new AstryxError(
|
|
195
57
|
`Invalid query "${String(query)}"`,
|
|
@@ -198,63 +60,17 @@ export async function discover(query, options = {}) {
|
|
|
198
60
|
);
|
|
199
61
|
}
|
|
200
62
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
*/
|
|
209
|
-
const packageView = async catalog => {
|
|
210
|
-
const entry = catalog.packages.find(p => p.package === target.name);
|
|
211
|
-
const installedAs = entry
|
|
212
|
-
? catalogState(entry, installedNames, declared).installedAs
|
|
213
|
-
: undefined;
|
|
214
|
-
const context = {catalog: entry, version: target.version, add, installedAs};
|
|
215
|
-
if (!target.item) return detail(packages, target.name, context);
|
|
216
|
-
const found = item(packages, target.name, target.item, context);
|
|
217
|
-
if (found) return found;
|
|
218
|
-
// Only an installed package has docs to fall back to. For one that is
|
|
219
|
-
// only in a source, the item is what is unknown, not the package.
|
|
220
|
-
if (entry && !packages.some(p => p.name === target.name)) {
|
|
221
|
-
throw new AstryxError(
|
|
222
|
-
`"${target.item}" not found in ${target.name}`,
|
|
223
|
-
similarItems(entry, target.item),
|
|
224
|
-
ERROR_CODES.ERR_UNKNOWN_COMPONENT,
|
|
225
|
-
);
|
|
63
|
+
// Scoped package query: @scope/name or @scope/name/Component
|
|
64
|
+
if (query.startsWith('@')) {
|
|
65
|
+
const slashIdx = query.indexOf('/', query.indexOf('/') + 1);
|
|
66
|
+
if (slashIdx > 0) {
|
|
67
|
+
const pkgName = query.slice(0, slashIdx);
|
|
68
|
+
const compName = query.slice(slashIdx + 1);
|
|
69
|
+
return await doc(packages, pkgName, compName, {lang, zh});
|
|
226
70
|
}
|
|
227
|
-
return
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// An installed component keeps resolving to its full doc, before any source
|
|
231
|
-
// is asked anything.
|
|
232
|
-
const installedPkg = packages.find(p => p.name === target.name);
|
|
233
|
-
if (installedPkg && target.item && !target.version) {
|
|
234
|
-
const resolved = findComponent([installedPkg], target.item);
|
|
235
|
-
if (resolved) return await docFromResult(resolved, {lang, zh});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (target.scoped) {
|
|
239
|
-
return await packageView(
|
|
240
|
-
await catalogFor({package: target.name, version: target.version}),
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const listing = await catalogFor();
|
|
245
|
-
const isPackage =
|
|
246
|
-
installedPkg != null ||
|
|
247
|
-
listing.packages.some(p => p.package === target.name);
|
|
248
|
-
if (isPackage && (target.version || target.item || target.name === query)) {
|
|
249
|
-
return await packageView(
|
|
250
|
-
await catalogFor({package: target.name, version: target.version}),
|
|
251
|
-
);
|
|
71
|
+
return detail(packages, query);
|
|
252
72
|
}
|
|
253
73
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
type,
|
|
257
|
-
only,
|
|
258
|
-
limit: limit ?? DEFAULT_LIMIT,
|
|
259
|
-
});
|
|
74
|
+
// Free-text search across all packages.
|
|
75
|
+
return await search(packages, query, {lang, zh});
|
|
260
76
|
}
|
|
@@ -68,19 +68,10 @@ describe('discover() dispatcher routing', () => {
|
|
|
68
68
|
it('free-text query with multiple matches -> discover.search envelope', async () => {
|
|
69
69
|
// 'a' is a substring of both Alpha and Beta (no exact match), so the search
|
|
70
70
|
// leaf returns the multi-match discover.search envelope. Proves free-text
|
|
71
|
-
// routing reaches the search leaf and projects its envelope.
|
|
72
|
-
// matches package names, so the package itself is listed after them.
|
|
71
|
+
// routing reaches the search leaf and projects its envelope.
|
|
73
72
|
const r = await discover('a');
|
|
74
73
|
expect(r.type).toBe('discover.search');
|
|
75
|
-
expect(
|
|
76
|
-
r.data.matches.filter(m => m.kind === 'component').map(m => m.component),
|
|
77
|
-
).toEqual(['Alpha', 'Beta']);
|
|
78
|
-
expect(r.data.matches).toContainEqual({
|
|
79
|
-
package: '@acme/widgets',
|
|
80
|
-
component: '@acme/widgets',
|
|
81
|
-
kind: 'package',
|
|
82
|
-
installed: true,
|
|
83
|
-
});
|
|
74
|
+
expect(r.data.matches.map(m => m.component).sort()).toEqual(['Alpha', 'Beta']);
|
|
84
75
|
});
|
|
85
76
|
|
|
86
77
|
it('free-text query with no match still routes to the search leaf (coded not-found)', async () => {
|