@astryxdesign/cli 0.7.12-canary.ccb4a6d → 0.7.12-canary.e0821c1
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/api/component/__fixtures__/resolutions/modules.txt +2 -2
- package/api/docs/_adapter.d.mts +22 -0
- package/api/docs/_adapter.mjs +81 -3
- package/api/docs/docs.doc.mjs +5 -5
- package/api/docs/docs.type.d.mts +21 -0
- package/api/docs/docs.type.mjs +11 -0
- package/api/docs/list/list.d.mts +5 -1
- package/api/docs/list/list.mjs +72 -6
- package/api/docs/node/node.mjs +2 -40
- package/api/index.d.mts +1 -0
- package/api/index.mjs +1 -0
- package/api/search/_adapter.d.mts +22 -1
- package/api/search/_adapter.mjs +37 -20
- package/api/search/exportSearchIndex.doc.d.mts +10 -0
- package/api/search/exportSearchIndex.doc.mjs +58 -0
- package/api/search/rankSearchIndex.doc.d.mts +10 -0
- package/api/search/rankSearchIndex.doc.mjs +79 -0
- package/api/search/search-index.d.mts +44 -0
- package/api/search/search-index.mjs +270 -0
- package/api/search/search.type.d.mts +28 -0
- package/api/search/search.type.mjs +17 -0
- package/api/swizzle/copy/copy.mjs +72 -3
- package/api/swizzle/swizzle.doc.mjs +7 -3
- package/api/swizzle/swizzle.type.d.mts +5 -0
- package/api/swizzle/swizzle.type.mjs +1 -0
- package/assets/docs/browser-support.doc.mjs +2 -2
- package/assets/docs/styling-overview.doc.mjs +4 -0
- package/assets/docs/tree/render-pages.doc.mjs +1 -1
- package/assets/templates/blocks/components/Calendar/useCalendarConstraintsHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Calendar/useCalendarConstraintsHookUsage.tsx +146 -0
- package/assets/templates/blocks/components/Calendar/useCalendarNavigationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Calendar/useCalendarNavigationHookUsage.tsx +173 -0
- package/assets/templates/themes/butter/butterTheme.ts +1 -1
- package/assets/templates/themes/stone/stoneTheme.ts +1 -1
- package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
- package/clients/cli/commands/docs.doc.mjs +5 -1
- package/clients/cli/commands/docs.mjs +30 -2
- package/clients/cli/commands/swizzle.doc.mjs +4 -0
- package/clients/cli/commands/swizzle.mjs +12 -0
- package/foundation/render/charts.d.mts +59 -0
- package/foundation/render/charts.mjs +216 -0
- package/foundation/render/page-worker.mjs +236 -0
- package/foundation/render/render-page.mjs +26 -10
- package/package.json +7 -6
|
@@ -247,9 +247,9 @@ useAutoMediaMode useAutoMediaMode hooks/useAnimatedLayout.doc.mjs hooks/useAutoM
|
|
|
247
247
|
useBackdropPress useBackdropPress Layer/Layer.doc.mjs Layer/useBackdropPress.ts
|
|
248
248
|
useBaseTablePlugins useBaseTablePlugins Table/Table.doc.mjs Table/useBaseTablePlugins.ts
|
|
249
249
|
useBusyAnnouncement useBusyAnnouncement hooks/useAnimatedLayout.doc.mjs hooks/useBusyAnnouncement.ts
|
|
250
|
-
useCalendarConstraints useCalendarConstraints Calendar/
|
|
250
|
+
useCalendarConstraints useCalendarConstraints Calendar/useCalendarConstraints.doc.mjs Calendar/hooks/useCalendarConstraints.ts
|
|
251
251
|
useCalendarDays useCalendarDays Calendar/useCalendarDays.doc.mjs Calendar/hooks/useCalendarDays.ts
|
|
252
|
-
useCalendarNavigation useCalendarNavigation Calendar/
|
|
252
|
+
useCalendarNavigation useCalendarNavigation Calendar/useCalendarNavigation.doc.mjs Calendar/hooks/useCalendarNavigation.ts
|
|
253
253
|
useChatComposerTokens useChatComposerTokens Chat/Chat.doc.mjs Chat/useChatComposerTokens.ts
|
|
254
254
|
useChatDictation useChatDictation Chat/Chat.doc.mjs Chat/useChatDictation.ts
|
|
255
255
|
useChatNewMessages useChatNewMessages Chat/Chat.doc.mjs Chat/useChatNewMessages.ts
|
package/api/docs/_adapter.d.mts
CHANGED
|
@@ -319,6 +319,28 @@ export function nameOwner(tree: DocsTree, catalog: DocsCatalog, name: string): {
|
|
|
319
319
|
kind: "node";
|
|
320
320
|
node: TreeNode;
|
|
321
321
|
} | null;
|
|
322
|
+
/**
|
|
323
|
+
* `stability: 'experimental'` for a node whose doc is marked experimental, or
|
|
324
|
+
* that sits under an experimental namespace (spec:AST-017 FR27), the way help
|
|
325
|
+
* marks an experimental command; nothing for a stable one, so stable entries
|
|
326
|
+
* read as they always have.
|
|
327
|
+
* @param {DocsTree} tree
|
|
328
|
+
* @param {TreeNode} node
|
|
329
|
+
* @returns {{stability?: 'experimental'}}
|
|
330
|
+
*/
|
|
331
|
+
export function stabilityOf(tree: DocsTree, node: TreeNode): {
|
|
332
|
+
stability?: "experimental";
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* The fields every child carries: what it is, and the route that opens it.
|
|
336
|
+
* Shared, so a child reads the same whether a namespace read or the topic
|
|
337
|
+
* list names it — including the package, which is the integration's for a
|
|
338
|
+
* guide an integration replaced, not the package whose route it took.
|
|
339
|
+
* @param {DocsTree} tree
|
|
340
|
+
* @param {TreeNode} child
|
|
341
|
+
* @returns {import('./docs.type.mjs').DocsNodeChild}
|
|
342
|
+
*/
|
|
343
|
+
export function childIdentity(tree: DocsTree, child: TreeNode): import("./docs.type.mjs").DocsNodeChild;
|
|
322
344
|
/**
|
|
323
345
|
* Whether a topic answers to its own name: what the topic list and search
|
|
324
346
|
* offer must open that topic.
|
package/api/docs/_adapter.mjs
CHANGED
|
@@ -1765,9 +1765,87 @@ export function nameOwner(tree, catalog, name) {
|
|
|
1765
1765
|
return entry == null ? null : {kind: 'topic', entry};
|
|
1766
1766
|
}
|
|
1767
1767
|
const entry = catalog.resolve(name);
|
|
1768
|
-
if (entry
|
|
1769
|
-
|
|
1770
|
-
|
|
1768
|
+
if (entry != null) {
|
|
1769
|
+
const owner = routeOwner(tree, entry);
|
|
1770
|
+
return owner == null ? {kind: 'topic', entry} : {kind: 'node', node: owner};
|
|
1771
|
+
}
|
|
1772
|
+
return childOwner(tree, name);
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* The child a reader names without its namespace, or with a route that leaves
|
|
1777
|
+
* out the prefix the child's own name repeats (spec:AST-046 FR5). A namespace
|
|
1778
|
+
* whose children repeat its name makes its own routes stutter: `tokens` holds
|
|
1779
|
+
* `tokens-color`, so the route is `tokens/tokens-color`, while the namespace
|
|
1780
|
+
* listing shows the bare `tokens-color`. Both spellings name one child, so
|
|
1781
|
+
* that child answers. Two or more children answering is ambiguous, and the
|
|
1782
|
+
* read falls through to the suggestion list, unchanged. An experimental doc
|
|
1783
|
+
* never answers to a bare name: its project reaches it only by a marked route
|
|
1784
|
+
* (spec:AST-017 FR27).
|
|
1785
|
+
* @param {DocsTree} tree
|
|
1786
|
+
* @param {string} name
|
|
1787
|
+
* @returns {{kind: 'node', node: TreeNode} | null}
|
|
1788
|
+
*/
|
|
1789
|
+
function childOwner(tree, name) {
|
|
1790
|
+
const wanted = name.toLowerCase();
|
|
1791
|
+
const slash = wanted.lastIndexOf('/');
|
|
1792
|
+
const parent = slash > 0 ? wanted.slice(0, slash) : null;
|
|
1793
|
+
const unstuttered =
|
|
1794
|
+
parent == null
|
|
1795
|
+
? null
|
|
1796
|
+
: `${parent}/${parent.slice(parent.lastIndexOf('/') + 1)}-${wanted.slice(slash + 1)}`;
|
|
1797
|
+
const matches = [...tree.nodes.values()].filter(
|
|
1798
|
+
node =>
|
|
1799
|
+
node.ref?.flatTopic == null &&
|
|
1800
|
+
node.route.includes('/') &&
|
|
1801
|
+
// An experimental doc is reachable only by a route its project marked
|
|
1802
|
+
// as experimental (spec:AST-017 FR27): a bare name never opens one.
|
|
1803
|
+
!isExperimentalTreeNode(tree, node) &&
|
|
1804
|
+
(node.name?.toLowerCase() === wanted ||
|
|
1805
|
+
(unstuttered != null && node.route.toLowerCase() === unstuttered)),
|
|
1806
|
+
);
|
|
1807
|
+
return matches.length === 1 ? {kind: 'node', node: matches[0]} : null;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/**
|
|
1811
|
+
* `stability: 'experimental'` for a node whose doc is marked experimental, or
|
|
1812
|
+
* that sits under an experimental namespace (spec:AST-017 FR27), the way help
|
|
1813
|
+
* marks an experimental command; nothing for a stable one, so stable entries
|
|
1814
|
+
* read as they always have.
|
|
1815
|
+
* @param {DocsTree} tree
|
|
1816
|
+
* @param {TreeNode} node
|
|
1817
|
+
* @returns {{stability?: 'experimental'}}
|
|
1818
|
+
*/
|
|
1819
|
+
export function stabilityOf(tree, node) {
|
|
1820
|
+
return isExperimentalTreeNode(tree, node) ? {stability: 'experimental'} : {};
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* The fields every child carries: what it is, and the route that opens it.
|
|
1825
|
+
* Shared, so a child reads the same whether a namespace read or the topic
|
|
1826
|
+
* list names it — including the package, which is the integration's for a
|
|
1827
|
+
* guide an integration replaced, not the package whose route it took.
|
|
1828
|
+
* @param {DocsTree} tree
|
|
1829
|
+
* @param {TreeNode} child
|
|
1830
|
+
* @returns {import('./docs.type.mjs').DocsNodeChild}
|
|
1831
|
+
*/
|
|
1832
|
+
export function childIdentity(tree, child) {
|
|
1833
|
+
return {
|
|
1834
|
+
route: child.route,
|
|
1835
|
+
name: child.route.slice(child.route.lastIndexOf('/') + 1),
|
|
1836
|
+
package: child.guideOverlay?.replacement?.package ?? child.provider,
|
|
1837
|
+
// A guide an integration's doc took the place of names whose it was.
|
|
1838
|
+
...(child.guideOverlay?.replacement
|
|
1839
|
+
? {
|
|
1840
|
+
replaces: child.route,
|
|
1841
|
+
replacedPackage: child.guideOverlay.replacedPackage ?? child.provider,
|
|
1842
|
+
}
|
|
1843
|
+
: {}),
|
|
1844
|
+
kind: child.kind,
|
|
1845
|
+
title: child.title,
|
|
1846
|
+
summary: child.summary,
|
|
1847
|
+
...stabilityOf(tree, child),
|
|
1848
|
+
};
|
|
1771
1849
|
}
|
|
1772
1850
|
|
|
1773
1851
|
/**
|
package/api/docs/docs.doc.mjs
CHANGED
|
@@ -54,7 +54,7 @@ export const doc = {
|
|
|
54
54
|
name: 'topic',
|
|
55
55
|
type: 'string',
|
|
56
56
|
description:
|
|
57
|
-
"Doc topic to load (e.g. 'principles'),
|
|
57
|
+
"Doc topic to load (e.g. 'principles'), a docs-tree route (e.g. 'cli/api/functions/search'), or the name of one node of the docs tree: its own name (e.g. 'tokens-color'), or its route without the prefix its name repeats (e.g. 'tokens/color'), where exactly one node answers to it. Omit to list all topics.",
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
name: 'section',
|
|
@@ -88,13 +88,13 @@ export const doc = {
|
|
|
88
88
|
name: 'options.depth',
|
|
89
89
|
type: "number | 'all'",
|
|
90
90
|
description:
|
|
91
|
-
"How many levels below a docs-tree namespace to read: 0 for the namespace alone, 1 for its children (the default), 'all' for every level. Where a read stops, a child with docs below it carries childCount. A doc with nothing below it reads the same at any depth.",
|
|
91
|
+
"How many levels below a docs-tree namespace to read: 0 for the namespace alone, 1 for its children (the default), 'all' for every level. Where a read stops, a child with docs below it carries childCount. A doc with nothing below it reads the same at any depth. On the topic list, where each namespace otherwise lists alone, it names the docs below each one, as deep as asked; without it the list is unchanged.",
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
name: 'options.detail',
|
|
95
95
|
type: "'brief' | 'compact' | 'full'",
|
|
96
96
|
description:
|
|
97
|
-
"How much of each doc below the named one a depth read returns: brief (the default) is its identity; compact and full add its text (a guide's sections, a namespace's or typed doc's content). Given alone, it reads one level down.",
|
|
97
|
+
"How much of each doc below the named one a depth read returns: brief (the default) is its identity; compact and full add its text (a guide's sections, a namespace's or typed doc's content). Given alone, it reads one level down. The topic list names each doc below a namespace by its identity whatever the detail.",
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
name: 'options.cwd',
|
|
@@ -107,7 +107,7 @@ export const doc = {
|
|
|
107
107
|
{
|
|
108
108
|
type: 'docs.list',
|
|
109
109
|
description:
|
|
110
|
-
"Every reference-doc topic in read order, as DocsListEntry[] ({topic, description, package, replaces?, replacedPackage?}), each readable with docs(topic). meta.namespaces lists the docs tree's top-level namespaces ({topic, description, package}), and meta.notLoaded each package whose docs did not load ({package, message}).",
|
|
110
|
+
"Every reference-doc topic in read order, as DocsListEntry[] ({topic, description, package, replaces?, replacedPackage?}), each readable with docs(topic). meta.namespaces lists the docs tree's top-level namespaces ({topic, description, package}); with depth, each also carries children, the docs below it as a namespace read names them ({topic, description, route, name, package, kind, title, summary, replaces?, replacedPackage?, stability?}), nested the same way to the depth asked for, and childCount where the depth stops on a doc with docs below it. meta.notLoaded names each package whose docs did not load ({package, message}).",
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
type: 'docs.detail',
|
|
@@ -141,7 +141,7 @@ export const doc = {
|
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
code: 'ERR_UNKNOWN_TOPIC',
|
|
144
|
-
when: 'the topic is not a string, or matches no topic and no docs
|
|
144
|
+
when: 'the topic is not a string, or matches no topic, no docs-tree route, and no one node of the docs tree by its own name or by its route without the prefix its name repeats; a name more than one node answers to opens none of them, and the suggestions list them',
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
code: 'ERR_UNKNOWN_SECTION',
|
package/api/docs/docs.type.d.mts
CHANGED
|
@@ -56,6 +56,27 @@ export type DocsListNamespace = {
|
|
|
56
56
|
* the package that owns it
|
|
57
57
|
*/
|
|
58
58
|
package: string;
|
|
59
|
+
/**
|
|
60
|
+
* the docs below it, present only for
|
|
61
|
+
* a read that passed `depth` and only where the namespace has children
|
|
62
|
+
*/
|
|
63
|
+
children?: DocsListChild[] | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* how many docs sit right below it, where a
|
|
66
|
+
* depth read stops here and it has docs below it
|
|
67
|
+
*/
|
|
68
|
+
childCount?: number | undefined;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* One doc below a namespace on the topic list, as `astryx docs --depth` names
|
|
72
|
+
* it: everything a namespace read gives a child, plus the two fields every
|
|
73
|
+
* list row carries, so one row reads the same way wherever it came from.
|
|
74
|
+
*/
|
|
75
|
+
export type DocsListChild = DocsNodeChild & {
|
|
76
|
+
topic: string;
|
|
77
|
+
description: string;
|
|
78
|
+
children?: DocsListChild[];
|
|
79
|
+
childCount?: number;
|
|
59
80
|
};
|
|
60
81
|
/**
|
|
61
82
|
* astryx --json docs <topic> --index
|
package/api/docs/docs.type.mjs
CHANGED
|
@@ -45,6 +45,17 @@
|
|
|
45
45
|
* @property {string} topic its route, the argument that opens it
|
|
46
46
|
* @property {string} description
|
|
47
47
|
* @property {string} package the package that owns it
|
|
48
|
+
* @property {DocsListChild[]} [children] the docs below it, present only for
|
|
49
|
+
* a read that passed `depth` and only where the namespace has children
|
|
50
|
+
* @property {number} [childCount] how many docs sit right below it, where a
|
|
51
|
+
* depth read stops here and it has docs below it
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* One doc below a namespace on the topic list, as `astryx docs --depth` names
|
|
56
|
+
* it: everything a namespace read gives a child, plus the two fields every
|
|
57
|
+
* list row carries, so one row reads the same way wherever it came from.
|
|
58
|
+
* @typedef {DocsNodeChild & {topic: string, description: string, children?: DocsListChild[], childCount?: number}} DocsListChild
|
|
48
59
|
*/
|
|
49
60
|
|
|
50
61
|
/**
|
package/api/docs/list/list.d.mts
CHANGED
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* @param {object} [options]
|
|
6
6
|
* @param {string} [options.cwd]
|
|
7
|
+
* @param {number | 'all'} [options.depth] how many levels of the docs tree to
|
|
8
|
+
* list below each namespace; without it the namespaces list alone, as they
|
|
9
|
+
* always have
|
|
7
10
|
* @returns {Promise<import('../docs.type.mjs').DocsListResponse>}
|
|
8
11
|
*/
|
|
9
|
-
export function list({ cwd }?: {
|
|
12
|
+
export function list({ cwd, depth }?: {
|
|
10
13
|
cwd?: string | undefined;
|
|
14
|
+
depth?: number | "all" | undefined;
|
|
11
15
|
}): Promise<import("../docs.type.mjs").DocsListResponse>;
|
package/api/docs/list/list.mjs
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import {loadTopicFile} from '../../../foundation/doc-compiler/read.mjs';
|
|
18
18
|
import {
|
|
19
|
+
childIdentity,
|
|
19
20
|
holdsOwnName,
|
|
20
21
|
linkDocText,
|
|
21
22
|
loadDocsCatalog,
|
|
@@ -25,9 +26,12 @@ import {
|
|
|
25
26
|
/**
|
|
26
27
|
* @param {object} [options]
|
|
27
28
|
* @param {string} [options.cwd]
|
|
29
|
+
* @param {number | 'all'} [options.depth] how many levels of the docs tree to
|
|
30
|
+
* list below each namespace; without it the namespaces list alone, as they
|
|
31
|
+
* always have
|
|
28
32
|
* @returns {Promise<import('../docs.type.mjs').DocsListResponse>}
|
|
29
33
|
*/
|
|
30
|
-
export async function list({cwd} = {}) {
|
|
34
|
+
export async function list({cwd, depth} = {}) {
|
|
31
35
|
const catalog = await loadDocsCatalog(cwd);
|
|
32
36
|
const tree = await projectTree(catalog);
|
|
33
37
|
/** @type {Array<import('../docs.type.mjs').DocsListEntry>} */
|
|
@@ -64,11 +68,11 @@ export async function list({cwd} = {}) {
|
|
|
64
68
|
// The docs tree's top-level namespaces, the CLI's and each integration's,
|
|
65
69
|
// each the way into a whole branch (spec:AST-046 FR7). They go in `meta`,
|
|
66
70
|
// so `data` stays the topic list 0.6 returned: every entry reads as a topic.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
// With `depth`, each one also carries the branch below it, so one read
|
|
72
|
+
// learns every route: without it a reader knows only that the namespace is
|
|
73
|
+
// there, and the children it must name are invisible until it opens each one.
|
|
74
|
+
const levels = depthLevels(depth);
|
|
75
|
+
const namespaces = tree.roots().map(root => branch(tree, root, levels));
|
|
72
76
|
/** @type {NonNullable<import('../docs.type.mjs').DocsListResponse['meta']>} */
|
|
73
77
|
const meta = {};
|
|
74
78
|
if (namespaces.length > 0) meta.namespaces = namespaces;
|
|
@@ -79,3 +83,65 @@ export async function list({cwd} = {}) {
|
|
|
79
83
|
? {type: 'docs.list', data: entries}
|
|
80
84
|
: {type: 'docs.list', data: entries, meta};
|
|
81
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* How many levels below each namespace the list carries: none without
|
|
89
|
+
* `depth`, every level for `'all'`. `docs <namespace> --depth` reads the same
|
|
90
|
+
* way, so one flag means one thing everywhere.
|
|
91
|
+
* @param {number | 'all'} [depth]
|
|
92
|
+
* @returns {number | null} null for a list read with no depth, which carries
|
|
93
|
+
* the namespaces alone
|
|
94
|
+
*/
|
|
95
|
+
function depthLevels(depth) {
|
|
96
|
+
if (depth == null) return null;
|
|
97
|
+
return depth === 'all' ? Infinity : depth;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* One namespace of the list, with the branch below it to `levels`. A child
|
|
102
|
+
* reads exactly as a namespace read gives it (`childIdentity`): the route
|
|
103
|
+
* that opens it, and the package that owns it — the integration's, for a
|
|
104
|
+
* guide an integration replaced, with the package whose route it took beside
|
|
105
|
+
* it. Every line of the list is then an argument a reader can pass straight
|
|
106
|
+
* back to `astryx docs`, attributed the way the read attributes it. Where the
|
|
107
|
+
* depth stops on a doc with docs below it, the row says how many, as a
|
|
108
|
+
* namespace read's child does; `levels` of null is a read with no depth,
|
|
109
|
+
* which says nothing about what is below.
|
|
110
|
+
* @param {import('../../../foundation/doc-compiler/tree.mjs').DocsTree} tree
|
|
111
|
+
* @param {import('../../../foundation/doc-compiler/tree.mjs').TreeNode} node
|
|
112
|
+
* @param {number | null} levels
|
|
113
|
+
* @returns {import('../docs.type.mjs').DocsListNamespace}
|
|
114
|
+
*/
|
|
115
|
+
function branch(tree, node, levels) {
|
|
116
|
+
/** @type {import('../docs.type.mjs').DocsListNamespace} */
|
|
117
|
+
const listed = {
|
|
118
|
+
topic: node.route,
|
|
119
|
+
description: node.summary,
|
|
120
|
+
package: node.provider,
|
|
121
|
+
};
|
|
122
|
+
if (levels == null) return listed;
|
|
123
|
+
const below = node.slots.flatMap(slot => slot.children);
|
|
124
|
+
if (levels <= 0) {
|
|
125
|
+
if (below.length > 0) listed.childCount = below.length;
|
|
126
|
+
return listed;
|
|
127
|
+
}
|
|
128
|
+
const children = below
|
|
129
|
+
.map(route => tree.get(route))
|
|
130
|
+
.filter(child => child != null)
|
|
131
|
+
.map(child => {
|
|
132
|
+
const node = /** @type {import('../../../foundation/doc-compiler/tree.mjs').TreeNode} */ (
|
|
133
|
+
child
|
|
134
|
+
);
|
|
135
|
+
const {topic: _route, ...rest} = branch(tree, node, levels - 1);
|
|
136
|
+
const identity = childIdentity(tree, node);
|
|
137
|
+
return /** @type {import('../docs.type.mjs').DocsListChild} */ ({
|
|
138
|
+
...identity,
|
|
139
|
+
topic: identity.route,
|
|
140
|
+
description: identity.summary,
|
|
141
|
+
...(rest.children ? {children: rest.children} : {}),
|
|
142
|
+
...(rest.childCount ? {childCount: rest.childCount} : {}),
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
if (children.length > 0) listed.children = children;
|
|
146
|
+
return listed;
|
|
147
|
+
}
|
package/api/docs/node/node.mjs
CHANGED
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {detailView} from '../../../foundation/doc-compiler/lenses.mjs';
|
|
19
|
-
import {isExperimentalTreeNode} from '../../../foundation/doc-compiler/tree.mjs';
|
|
20
19
|
import {
|
|
20
|
+
childIdentity,
|
|
21
21
|
compileTopic,
|
|
22
22
|
guideEntry,
|
|
23
23
|
nodeContent,
|
|
24
24
|
placeLinks,
|
|
25
25
|
resolveDocsArgument,
|
|
26
|
+
stabilityOf,
|
|
26
27
|
unknownTopicError,
|
|
27
28
|
} from '../_adapter.mjs';
|
|
28
29
|
|
|
@@ -87,45 +88,6 @@ export async function nodeView(catalog, tree, node, read) {
|
|
|
87
88
|
return view;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
/**
|
|
91
|
-
* `stability: 'experimental'` for a node whose doc is marked experimental, or
|
|
92
|
-
* that sits under an experimental namespace (spec:AST-017 FR27), the way help
|
|
93
|
-
* marks an experimental command; nothing for a stable one, so stable entries
|
|
94
|
-
* read as they always have.
|
|
95
|
-
* @param {DocsTree} tree
|
|
96
|
-
* @param {TreeNode} node
|
|
97
|
-
* @returns {{stability?: 'experimental'}}
|
|
98
|
-
*/
|
|
99
|
-
function stabilityOf(tree, node) {
|
|
100
|
-
return isExperimentalTreeNode(tree, node) ? {stability: 'experimental'} : {};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* The fields every child carries: what it is, and the route that opens it.
|
|
105
|
-
* @param {DocsTree} tree
|
|
106
|
-
* @param {TreeNode} child
|
|
107
|
-
* @returns {import('../docs.type.mjs').DocsNodeChild}
|
|
108
|
-
*/
|
|
109
|
-
function childIdentity(tree, child) {
|
|
110
|
-
return {
|
|
111
|
-
route: child.route,
|
|
112
|
-
name: child.route.slice(child.route.lastIndexOf('/') + 1),
|
|
113
|
-
package: child.guideOverlay?.replacement?.package ?? child.provider,
|
|
114
|
-
// A guide an integration's doc took the place of names whose it was.
|
|
115
|
-
...(child.guideOverlay?.replacement
|
|
116
|
-
? {
|
|
117
|
-
replaces: child.route,
|
|
118
|
-
replacedPackage:
|
|
119
|
-
child.guideOverlay.replacedPackage ?? child.provider,
|
|
120
|
-
}
|
|
121
|
-
: {}),
|
|
122
|
-
kind: child.kind,
|
|
123
|
-
title: child.title,
|
|
124
|
-
summary: child.summary,
|
|
125
|
-
...stabilityOf(tree, child),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
91
|
/**
|
|
130
92
|
* @param {TreeNode['slots']} slots
|
|
131
93
|
* @returns {number}
|
package/api/index.d.mts
CHANGED
|
@@ -47,6 +47,7 @@ export * from "./integration/authoring-checks.type.mjs";
|
|
|
47
47
|
export * from "./integration/integration-check.type.mjs";
|
|
48
48
|
export type Logger = import("./logger.mjs").Logger;
|
|
49
49
|
export { themeBuild, themeAdd, themeRemove, themeUse, themeEject, themeTemplate, themeList, themeListAvailable, themeListCopySources, themeTargets, themePaletteGenerate, generateTonalPalette, listThemes } from "./theme/theme.mjs";
|
|
50
|
+
export { exportSearchIndex, rankSearchIndex } from "./search/search-index.mjs";
|
|
50
51
|
export { integrationAdd, integrationAddAgentDoc, integrationAddCodemod, integrationAddComponent, integrationAddDoc, integrationAddTemplate } from "./integration/add-contribution.mjs";
|
|
51
52
|
export { validateIntegration, summarizeIssues } from "./integration/validate-integration.mjs";
|
|
52
53
|
export { integrationTemplateConflicts, integrationComponentConflicts, integrationDocConflicts } from "./integration/authoring-checks.mjs";
|
package/api/index.mjs
CHANGED
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
} from './theme/theme.mjs';
|
|
49
49
|
export {hook} from './hook/hook.mjs';
|
|
50
50
|
export {search} from './search/search.mjs';
|
|
51
|
+
export {exportSearchIndex, rankSearchIndex} from './search/search-index.mjs';
|
|
51
52
|
export {build} from './build/build.mjs';
|
|
52
53
|
export {swizzle} from './swizzle/swizzle.mjs';
|
|
53
54
|
export {gapReport} from './gap-report/gap-report.mjs';
|
|
@@ -16,7 +16,7 @@ export function requireSearchCoreDir(cwd: string): string;
|
|
|
16
16
|
* in the CLI's searches. A caller that runs its own operation shares it.
|
|
17
17
|
*
|
|
18
18
|
* @param {{cwd?: string, type?: string}} options
|
|
19
|
-
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null}>}
|
|
19
|
+
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null, coreSkippedCode: string | null}>}
|
|
20
20
|
*/
|
|
21
21
|
export function gatherCandidates(options: {
|
|
22
22
|
cwd?: string;
|
|
@@ -27,7 +27,28 @@ export function gatherCandidates(options: {
|
|
|
27
27
|
searchedComponents: boolean;
|
|
28
28
|
outside: boolean;
|
|
29
29
|
coreSkipped: string | null;
|
|
30
|
+
coreSkippedCode: string | null;
|
|
30
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* The Core a search of `type` reads, and the domains it gathers. `astryx docs`
|
|
34
|
+
* reads docs without @astryxdesign/core, and `astryx theme list` reads themes
|
|
35
|
+
* without it, so a docs or themes search never looks for Core. Every other
|
|
36
|
+
* domain reads Core: the project's, else the answering release's own,
|
|
37
|
+
* installed into the per-user cache on first need (spec AST-081 FR3). A Core
|
|
38
|
+
* domain asked for by name throws the lookup's coded error. An open search
|
|
39
|
+
* without Core gathers the docs and themes alone and records why in
|
|
40
|
+
* `coreSkipped`; it never fails silently.
|
|
41
|
+
* @param {string | undefined} type
|
|
42
|
+
* @param {(cwd: string) => string} lookup finds the Core folder, or throws
|
|
43
|
+
* @param {string} cwd
|
|
44
|
+
* @returns {{coreDir: string | null, coreSkipped: string | null, coreSkippedCode: string | null, wants: (domain: string) => boolean}}
|
|
45
|
+
*/
|
|
46
|
+
export function resolveSearchCore(type: string | undefined, lookup: (cwd: string) => string, cwd: string): {
|
|
47
|
+
coreDir: string | null;
|
|
48
|
+
coreSkipped: string | null;
|
|
49
|
+
coreSkippedCode: string | null;
|
|
50
|
+
wants: (domain: string) => boolean;
|
|
51
|
+
};
|
|
31
52
|
/**
|
|
32
53
|
* gatherCandidates, reused within one process: a later search in the same
|
|
33
54
|
* folder, for the same `type`, ranks the candidates an earlier search
|
package/api/search/_adapter.mjs
CHANGED
|
@@ -43,46 +43,62 @@ export function requireSearchCoreDir(cwd) {
|
|
|
43
43
|
* in the CLI's searches. A caller that runs its own operation shares it.
|
|
44
44
|
*
|
|
45
45
|
* @param {{cwd?: string, type?: string}} options
|
|
46
|
-
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null}>}
|
|
46
|
+
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null, coreSkippedCode: string | null}>}
|
|
47
47
|
*/
|
|
48
48
|
export function gatherCandidates(options) {
|
|
49
49
|
return withListingCache(() => gatherDomains(options));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* The Core a search of `type` reads, and the domains it gathers. `astryx docs`
|
|
54
|
+
* reads docs without @astryxdesign/core, and `astryx theme list` reads themes
|
|
55
|
+
* without it, so a docs or themes search never looks for Core. Every other
|
|
56
|
+
* domain reads Core: the project's, else the answering release's own,
|
|
57
|
+
* installed into the per-user cache on first need (spec AST-081 FR3). A Core
|
|
58
|
+
* domain asked for by name throws the lookup's coded error. An open search
|
|
59
|
+
* without Core gathers the docs and themes alone and records why in
|
|
60
|
+
* `coreSkipped`; it never fails silently.
|
|
61
|
+
* @param {string | undefined} type
|
|
62
|
+
* @param {(cwd: string) => string} lookup finds the Core folder, or throws
|
|
63
|
+
* @param {string} cwd
|
|
64
|
+
* @returns {{coreDir: string | null, coreSkipped: string | null, coreSkippedCode: string | null, wants: (domain: string) => boolean}}
|
|
54
65
|
*/
|
|
55
|
-
|
|
56
|
-
const {cwd = process.cwd(), type} = options;
|
|
57
|
-
// `astryx docs` reads docs without @astryxdesign/core, and `astryx theme
|
|
58
|
-
// list` reads themes without it (bundled themes need no project), so a
|
|
59
|
-
// search of either must too. Every other domain reads core: asked for by
|
|
60
|
-
// name, it is an error without core; an open search then covers the docs
|
|
61
|
-
// and themes alone.
|
|
66
|
+
export function resolveSearchCore(type, lookup, cwd) {
|
|
62
67
|
const needsCore = !type || !CORELESS_DOMAINS.includes(type);
|
|
63
|
-
// A domain that reads Core gets it as `component` does: the project's, else
|
|
64
|
-
// the answering release's own, installed into the per-user cache on first
|
|
65
|
-
// need (spec AST-081 FR3). Asked for by name, a Core that cannot be had is
|
|
66
|
-
// the error; an open search answers from the rest and says what it skipped
|
|
67
|
-
// and why, never silently.
|
|
68
68
|
/** @type {string | null} */
|
|
69
69
|
let coreDir = null;
|
|
70
70
|
/** @type {string | null} */
|
|
71
71
|
let coreSkipped = null;
|
|
72
|
+
/** @type {string | null} */
|
|
73
|
+
let coreSkippedCode = null;
|
|
72
74
|
if (needsCore && type) {
|
|
73
|
-
coreDir = coreDirOrThrow(
|
|
75
|
+
coreDir = coreDirOrThrow(lookup, cwd);
|
|
74
76
|
} else if (needsCore) {
|
|
75
77
|
try {
|
|
76
|
-
coreDir =
|
|
78
|
+
coreDir = lookup(cwd);
|
|
77
79
|
} catch (error) {
|
|
78
80
|
coreSkipped = /** @type {Error} */ (error).message;
|
|
81
|
+
const code = /** @type {any} */ (error)?.code;
|
|
82
|
+
coreSkippedCode = typeof code === 'string' ? code : null;
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
/** @param {string} domain */
|
|
86
|
+
const wants = domain =>
|
|
87
|
+
(!type && (coreDir != null || CORELESS_DOMAINS.includes(domain))) ||
|
|
88
|
+
type === domain;
|
|
89
|
+
return {coreDir, coreSkipped, coreSkippedCode, wants};
|
|
90
|
+
}
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @param {{cwd?: string, type?: string}} options
|
|
94
|
+
*/
|
|
95
|
+
async function gatherDomains(options) {
|
|
96
|
+
const {cwd = process.cwd(), type} = options;
|
|
97
|
+
const {coreDir, coreSkipped, coreSkippedCode, wants} = resolveSearchCore(
|
|
98
|
+
type,
|
|
99
|
+
requireSearchCoreDir,
|
|
100
|
+
cwd,
|
|
101
|
+
);
|
|
86
102
|
const [components, hooks, docTopics, templates, themes] = await Promise.all([
|
|
87
103
|
wants('component')
|
|
88
104
|
? gatherComponents(/** @type {string} */ (coreDir), cwd)
|
|
@@ -103,6 +119,7 @@ async function gatherDomains(options) {
|
|
|
103
119
|
searchedComponents: wants('component'),
|
|
104
120
|
outside,
|
|
105
121
|
coreSkipped,
|
|
122
|
+
coreSkippedCode,
|
|
106
123
|
};
|
|
107
124
|
}
|
|
108
125
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file FunctionDoc for `exportSearchIndex()`. Colocated with the API function it
|
|
6
|
+
* documents; the shape source of truth stays in `search.type.mjs`.
|
|
7
|
+
* @position packages/cli/api/search — function documentation
|
|
8
|
+
*/
|
|
9
|
+
/** @type {import('@astryxdesign/cli/authoring').FunctionDoc} */
|
|
10
|
+
export const doc: import("@astryxdesign/cli/authoring").FunctionDoc;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file FunctionDoc for `exportSearchIndex()`. Colocated with the API function it
|
|
5
|
+
* documents; the shape source of truth stays in `search.type.mjs`.
|
|
6
|
+
* @position packages/cli/api/search — function documentation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {import('@astryxdesign/cli/authoring').FunctionDoc} */
|
|
10
|
+
export const doc = {
|
|
11
|
+
type: 'function',
|
|
12
|
+
kind: 'api',
|
|
13
|
+
name: 'exportSearchIndex',
|
|
14
|
+
namespace: 'cli/api',
|
|
15
|
+
stability: 'experimental',
|
|
16
|
+
displayName: 'exportSearchIndex()',
|
|
17
|
+
summary:
|
|
18
|
+
'The candidates search ranks in a folder, as plain JSON to store and rank later with rankSearchIndex().',
|
|
19
|
+
description:
|
|
20
|
+
"Gathers what `search()` would rank in `cwd` and returns it as `{version, candidates}`. Gathering reads each installed package's docs and loads its integration manifest, which runs that package's code, so a catalog that lists packages it must not load makes the index where loading is safe (a folder with those packages installed), stores it, and ranks it with {@link function:rankSearchIndex}. With `package`, the index keeps only that package's candidates, for a catalog that stores one index per package. A component description's inline doc tokens are resolved where the index is made, as `search` prints them; when Core could not be had there, the index says why in `coreSkipped`. Fields that point into the folder are left out, so an index reads the same wherever it was made. The candidates are the ranker's input, not a stable shape: an index is ranked only by the CLI version that made it. Experimental: it may change or be removed in a patch release.",
|
|
21
|
+
importPath: '@astryxdesign/cli/api',
|
|
22
|
+
signature: 'exportSearchIndex(options?: {cwd?: string, package?: string}): Promise<SearchIndex>',
|
|
23
|
+
keywords: ['search index', 'stored search index'],
|
|
24
|
+
params: [
|
|
25
|
+
{
|
|
26
|
+
name: 'options.cwd',
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'The folder to gather from, read as `search()` reads it.',
|
|
29
|
+
default: 'process.cwd()',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'options.package',
|
|
33
|
+
type: 'string',
|
|
34
|
+
description:
|
|
35
|
+
"Keep only the candidates this npm package owns: its components, templates, themes, and doc parts. Without it the index holds everything the folder's search ranks, Core's entries and the CLI's docs included.",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
returns: [
|
|
39
|
+
{
|
|
40
|
+
type: 'SearchIndex',
|
|
41
|
+
description:
|
|
42
|
+
'`version`, the CLI version that made it, and `candidates`, the gathered entries as plain JSON.',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
throws: [
|
|
46
|
+
{
|
|
47
|
+
code: 'ERR_INVALID_ARGUMENT',
|
|
48
|
+
when: '`package` is given and is not a non-empty string',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
examples: [
|
|
52
|
+
{
|
|
53
|
+
label: 'Index one installed integration',
|
|
54
|
+
code: "const index = await exportSearchIndex({cwd: '/tmp/harness', package: '@acme/ui'});",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
related: ['search', 'rankSearchIndex'],
|
|
58
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file FunctionDoc for `rankSearchIndex()`. Colocated with the API function it
|
|
6
|
+
* documents; the shape source of truth stays in `search.type.mjs`.
|
|
7
|
+
* @position packages/cli/api/search — function documentation
|
|
8
|
+
*/
|
|
9
|
+
/** @type {import('@astryxdesign/cli/authoring').FunctionDoc} */
|
|
10
|
+
export const doc: import("@astryxdesign/cli/authoring").FunctionDoc;
|