@astryxdesign/cli 0.6.3-canary.6ef7b74 → 0.6.3-canary.6f03d72
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 +1 -2
- package/api/docs/_adapter.d.mts +0 -10
- package/api/docs/_adapter.mjs +11 -67
- package/api/docs/detail/detail.d.mts +0 -15
- package/api/docs/detail/detail.mjs +8 -23
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +17 -37
- package/api/docs/detail/section/section.test.mjs +0 -40
- package/api/docs/docs.d.mts +2 -7
- package/api/docs/docs.doc.mjs +10 -27
- package/api/docs/docs.mjs +9 -16
- package/api/docs/docs.test.mjs +0 -6
- package/api/docs/docs.type.d.mts +0 -37
- package/api/docs/docs.type.mjs +0 -28
- package/api/docs/integrationDocs.test.mjs +0 -106
- package/api/doctor/doctor.d.mts +0 -48
- package/api/doctor/doctor.mjs +0 -236
- package/api/doctor/doctor.test.mjs +0 -196
- package/api/hook/list/list.d.mts +1 -1
- package/api/integration/integration-authoring.type.d.mts +1 -1
- package/api/search/search.d.mts +1 -1
- package/api/search/search.type.d.mts +1 -1
- package/api/template/template.d.mts +1 -1
- package/api/template/template.type.d.mts +1 -1
- package/api/upgrade/_adapter.mjs +5 -71
- package/api/upgrade/upgrade.doc.mjs +3 -4
- package/api/upgrade/upgrade.type.d.mts +1 -1
- package/api/upgrade/upgrade.type.mjs +1 -1
- package/assets/docs/README.md +0 -9
- package/assets/docs/cli-integrations.doc.mjs +15 -77
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +3 -19
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +65 -383
- package/authoring/debug/parse.d.mts +1 -1
- package/authoring/doctypes/_schema.d.mts +21 -676
- package/authoring/doctypes/_schema.mjs +38 -360
- package/authoring/doctypes/base/type.ts +0 -36
- package/authoring/doctypes/command/type.ts +1 -2
- package/authoring/doctypes/component/component.doc.mjs +1 -1
- package/authoring/doctypes/component/type.ts +2 -3
- package/authoring/doctypes/enum/type.ts +1 -3
- package/authoring/doctypes/function/type.ts +2 -6
- package/authoring/doctypes/hook/type.ts +1 -2
- package/authoring/doctypes/legacy.d.mts +2 -4
- package/authoring/doctypes/legacy.mjs +2 -3
- package/authoring/doctypes/parse.d.mts +2 -4
- package/authoring/doctypes/parse.mjs +2 -8
- package/authoring/doctypes/parse.test.mjs +3 -77
- package/authoring/doctypes/reference/parse.mjs +4 -7
- package/authoring/doctypes/reference/reference.doc.mjs +4 -13
- package/authoring/doctypes/reference/type.ts +5 -51
- package/authoring/doctypes/schema/type.ts +1 -2
- package/authoring/doctypes/template/parse.mjs +1 -3
- package/authoring/doctypes/template/parse.test.mjs +2 -8
- package/authoring/doctypes/template/type.ts +2 -2
- package/authoring/doctypes/types.ts +0 -1
- package/authoring/gap-report/parse.d.mts +1 -1
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +0 -32
- package/authoring/index.mjs +0 -1
- package/authoring/integration/integration.doc.mjs +6 -13
- package/authoring/integration/parse.test.mjs +1 -10
- package/authoring/integration/schema.d.mts +0 -2
- package/authoring/integration/schema.mjs +0 -6
- package/authoring/integration/type.ts +5 -22
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/commands/docs.doc.mjs +3 -13
- package/clients/cli/commands/docs.mjs +21 -121
- package/clients/cli/commands/docs.test.mjs +0 -88
- package/clients/cli/commands/theme-palette-generate.doc.mjs +4 -8
- package/clients/cli/commands/upgrade.doc.mjs +2 -2
- package/clients/cli/formatters/index.mjs +1 -162
- package/clients/cli/formatters/index.test.mjs +0 -91
- package/clients/cli/lib/manifest.mjs +2 -7
- package/foundation/config/project.mjs +6 -21
- package/foundation/discovery/component-discovery.d.mts +1 -1
- package/foundation/discovery/component-discovery.mjs +1 -2
- package/foundation/discovery/docs-discovery.d.mts +4 -7
- package/foundation/discovery/docs-discovery.mjs +88 -194
- package/foundation/discovery/docs-discovery.test.mjs +13 -243
- package/foundation/discovery/template-adapter.mjs +1 -2
- package/foundation/integrations/autolink.mjs +5 -12
- package/foundation/integrations/integration-warnings.mjs +0 -6
- package/foundation/integrations/integrations.d.mts +2 -46
- package/foundation/integrations/integrations.mjs +8 -167
- package/foundation/integrations/integrations.test.mjs +1 -384
- package/foundation/integrations/validate-contributions.d.mts +0 -2
- package/foundation/integrations/validate-contributions.mjs +0 -10
- package/foundation/response/json-contract.test.mjs +17 -46
- package/foundation/response/response-types.doc.mjs +1 -6
- package/package.json +9 -9
- package/api/docs/index/index.d.mts +0 -18
- package/api/docs/index/index.mjs +0 -31
- package/api/docs/index/index.test.mjs +0 -62
- package/api/upgrade/project-context.test.mjs +0 -272
- package/assets/docs/authoring.doc.mjs +0 -14
- package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +0 -14
- package/assets/templates/blocks/components/Timer/TimerFormats.tsx +0 -34
- package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +0 -14
- package/assets/templates/blocks/components/Timer/TimerInline.tsx +0 -14
- package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +0 -13
- package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +0 -47
- package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +0 -14
- package/assets/templates/blocks/components/Timer/TimerTypography.tsx +0 -31
- package/authoring/doctypes/base/graph-fields.doc.d.mts +0 -9
- package/authoring/doctypes/base/graph-fields.doc.mjs +0 -55
- package/authoring/doctypes/namespace/namespace.doc.d.mts +0 -9
- package/authoring/doctypes/namespace/namespace.doc.mjs +0 -128
- package/authoring/doctypes/namespace/parse.d.mts +0 -12
- package/authoring/doctypes/namespace/parse.mjs +0 -25
- package/authoring/doctypes/namespace/parse.test.mjs +0 -165
- package/authoring/doctypes/namespace/type.ts +0 -71
- package/authoring/identity/identity.doc.d.mts +0 -9
- package/authoring/identity/identity.doc.mjs +0 -60
- package/authoring/identity/type.ts +0 -132
- package/foundation/discovery/authoring-self-docs.d.mts +0 -69
- package/foundation/discovery/authoring-self-docs.mjs +0 -214
- package/foundation/discovery/authoring-self-docs.test.mjs +0 -95
- package/foundation/discovery/docs-output-budget.d.mts +0 -28
- package/foundation/discovery/docs-output-budget.mjs +0 -50
- package/foundation/discovery/docs-section-key.d.mts +0 -98
- package/foundation/discovery/docs-section-key.mjs +0 -221
- package/foundation/discovery/docs-section-key.test.mjs +0 -224
- package/foundation/identity/provider-identity.d.mts +0 -90
- package/foundation/identity/provider-identity.mjs +0 -320
- package/foundation/identity/provider-identity.test.mjs +0 -254
- package/foundation/identity/providers.d.mts +0 -7
- package/foundation/identity/providers.mjs +0 -16
- package/foundation/integrations/provider-conflicts.test.mjs +0 -125
package/README.md
CHANGED
|
@@ -403,8 +403,7 @@ Every response has a `type` discriminant. The full set is below (generated from
|
|
|
403
403
|
| `component.detail.blocks` | One component's example blocks, as {component, showcase, examples, related} of BlockEntry. |
|
|
404
404
|
| `docs.list` | All reference-doc topics as DocsListEntry[] ({topic, description}), in discovery order. |
|
|
405
405
|
| `docs.detail` | One topic's full ReferenceDoc, with token-ref blocks inlined. |
|
|
406
|
-
| `docs.
|
|
407
|
-
| `docs.detail.section` | One ReferenceSection of a topic, found by key or title, with token-ref blocks inlined. |
|
|
406
|
+
| `docs.detail.section` | A single ReferenceSection of a topic: the first whose title contains the section query. |
|
|
408
407
|
| `blog.list` | The feed URL plus every post parsed from the RSS feed, each with slug, title, description, date, type, authors, link, and plaintext URL. |
|
|
409
408
|
| `blog.detail` | One post's metadata plus the feed URL and the post's full plaintext body. |
|
|
410
409
|
| `discover.list` | The configured external packages (name, category, components, version, description); when empty it carries meta.configured to tell "nothing configured" from "nothing discovered". |
|
package/api/docs/_adapter.d.mts
CHANGED
|
@@ -39,12 +39,6 @@ export function loadReferenceDocs(docPath: string, { lang }?: {
|
|
|
39
39
|
export function loadTopicDoc(entry: import("../../foundation/discovery/docs-discovery.mjs").DocsTopicEntry, { lang }?: {
|
|
40
40
|
lang?: string | null;
|
|
41
41
|
}): Promise<import("./docs.type.mjs").DocsDetailResponse["data"]>;
|
|
42
|
-
/**
|
|
43
|
-
* The overlay languages a topic ships for its own file or any extension.
|
|
44
|
-
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
45
|
-
* @returns {string[]}
|
|
46
|
-
*/
|
|
47
|
-
export function overlayLanguages(entry: import("../../foundation/discovery/docs-discovery.mjs").DocsTopicEntry): string[];
|
|
48
42
|
/**
|
|
49
43
|
* Resolve `topic` against the project's catalog (throwing `ERR_UNKNOWN_TOPIC`
|
|
50
44
|
* when unmatched), and load it with any --dense/--zh overlay and any
|
|
@@ -60,7 +54,6 @@ export function overlayLanguages(entry: import("../../foundation/discovery/docs-
|
|
|
60
54
|
* @returns {Promise<{
|
|
61
55
|
* catalog: DocsCatalog,
|
|
62
56
|
* docsData: import('./docs.type.mjs').DocsDetailResponse['data'],
|
|
63
|
-
* lang: string | null,
|
|
64
57
|
* }>}
|
|
65
58
|
*/
|
|
66
59
|
export function resolveTopicDocs(topic: string, options?: {
|
|
@@ -71,8 +64,5 @@ export function resolveTopicDocs(topic: string, options?: {
|
|
|
71
64
|
}): Promise<{
|
|
72
65
|
catalog: DocsCatalog;
|
|
73
66
|
docsData: import("./docs.type.mjs").DocsDetailResponse["data"];
|
|
74
|
-
lang: string | null;
|
|
75
67
|
}>;
|
|
76
|
-
/** The localized overlays a docs read can apply. */
|
|
77
|
-
export const OVERLAY_LANGUAGES: string[];
|
|
78
68
|
import { DocsCatalog } from '../../foundation/discovery/docs-discovery.mjs';
|
package/api/docs/_adapter.mjs
CHANGED
|
@@ -23,16 +23,9 @@ import {Project} from '../../foundation/config/project.mjs';
|
|
|
23
23
|
import {
|
|
24
24
|
DocsCatalog,
|
|
25
25
|
mergeTopic,
|
|
26
|
-
problemsInTopic,
|
|
27
|
-
withSourceTitle,
|
|
28
26
|
} from '../../foundation/discovery/docs-discovery.mjs';
|
|
29
|
-
import {
|
|
30
|
-
sectionKeyProblems,
|
|
31
|
-
withSectionKeys,
|
|
32
|
-
} from '../../foundation/discovery/docs-section-key.mjs';
|
|
33
27
|
import {AstryxError} from '../error.mjs';
|
|
34
28
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
35
|
-
import {parseDoc} from '../../authoring/doctypes/parse.mjs';
|
|
36
29
|
|
|
37
30
|
/**
|
|
38
31
|
* The project's topics: the built-in ones plus whatever the configured
|
|
@@ -63,20 +56,13 @@ export async function loadDocsCatalog(cwd = process.cwd()) {
|
|
|
63
56
|
*/
|
|
64
57
|
export async function loadReferenceDocs(docPath, {lang} = {}) {
|
|
65
58
|
const mod = await import(pathToFileURL(docPath).href);
|
|
66
|
-
const
|
|
67
|
-
if (!('sections' in parsed)) {
|
|
68
|
-
throw new Error(`${path.basename(docPath)} is not a reference document.`);
|
|
69
|
-
}
|
|
70
|
-
const problems = problemsInTopic(parsed);
|
|
71
|
-
if (problems.length > 0) {
|
|
72
|
-
throw new Error(
|
|
73
|
-
`${path.basename(docPath)} is invalid: ${problems.join('; ')}`,
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
const docs = parsed;
|
|
59
|
+
const docs = mod.docs ?? mod.default;
|
|
77
60
|
if (!lang || lang === 'en') return docs;
|
|
78
61
|
|
|
79
|
-
const
|
|
62
|
+
const dir = path.dirname(docPath);
|
|
63
|
+
const base = path.basename(docPath, '.doc.mjs');
|
|
64
|
+
const locale = lang === 'dense' ? 'dense' : lang;
|
|
65
|
+
const translationPath = path.join(dir, `${base}.doc.${locale}.mjs`);
|
|
80
66
|
if (!fs.existsSync(translationPath)) return docs;
|
|
81
67
|
|
|
82
68
|
const translationMod = await import(pathToFileURL(translationPath).href);
|
|
@@ -100,12 +86,10 @@ export async function loadReferenceDocs(docPath, {lang} = {}) {
|
|
|
100
86
|
...docs,
|
|
101
87
|
description: translation.description || docs.description,
|
|
102
88
|
sections: docs.sections.map(
|
|
103
|
-
(
|
|
104
|
-
/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ section,
|
|
105
|
-
) => {
|
|
89
|
+
(/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ section) => {
|
|
106
90
|
const ts = bySection.get(section.title);
|
|
107
91
|
if (!ts) return section;
|
|
108
|
-
|
|
92
|
+
return {
|
|
109
93
|
...section,
|
|
110
94
|
title: ts.title || section.title,
|
|
111
95
|
content: section.content.map(
|
|
@@ -115,15 +99,12 @@ export async function loadReferenceDocs(docPath, {lang} = {}) {
|
|
|
115
99
|
) => {
|
|
116
100
|
const tb = ts.content?.[bi];
|
|
117
101
|
if (!tb) return block;
|
|
118
|
-
if (tb.type === 'prose' && block.type === 'prose')
|
|
119
|
-
|
|
120
|
-
if (tb.type === 'list' && block.type === 'list')
|
|
121
|
-
return {...block, items: tb.items};
|
|
102
|
+
if (tb.type === 'prose' && block.type === 'prose') return {...block, text: tb.text};
|
|
103
|
+
if (tb.type === 'list' && block.type === 'list') return {...block, items: tb.items};
|
|
122
104
|
return block;
|
|
123
105
|
},
|
|
124
106
|
),
|
|
125
107
|
};
|
|
126
|
-
return withSourceTitle(localized, section.title);
|
|
127
108
|
},
|
|
128
109
|
),
|
|
129
110
|
};
|
|
@@ -146,44 +127,8 @@ export async function loadTopicDoc(entry, {lang} = {}) {
|
|
|
146
127
|
let doc = await loadReferenceDocs(entry.path, {lang});
|
|
147
128
|
for (const extension of entry.extensions) {
|
|
148
129
|
doc = mergeTopic(doc, await loadReferenceDocs(extension.path, {lang}));
|
|
149
|
-
// Merging matches on keys, so this holds unless merge itself regresses.
|
|
150
|
-
const problems = sectionKeyProblems(doc.sections);
|
|
151
|
-
if (problems.length > 0) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`${path.basename(extension.path)}, extending ${entry.name}, leaves two sections with one key: ${problems.join('; ')}`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
130
|
}
|
|
157
|
-
|
|
158
|
-
return withSectionKeys(doc);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** The localized overlays a docs read can apply. */
|
|
162
|
-
export const OVERLAY_LANGUAGES = ['zh', 'dense'];
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Where the `lang` overlay of a doc file lives: `{topic}.doc.{lang}.mjs`.
|
|
166
|
-
* @param {string} docPath
|
|
167
|
-
* @param {string} lang
|
|
168
|
-
* @returns {string}
|
|
169
|
-
*/
|
|
170
|
-
function overlayPath(docPath, lang) {
|
|
171
|
-
return path.join(
|
|
172
|
-
path.dirname(docPath),
|
|
173
|
-
`${path.basename(docPath, '.doc.mjs')}.doc.${lang}.mjs`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* The overlay languages a topic ships for its own file or any extension.
|
|
179
|
-
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
180
|
-
* @returns {string[]}
|
|
181
|
-
*/
|
|
182
|
-
export function overlayLanguages(entry) {
|
|
183
|
-
const files = [entry.path, ...entry.extensions.map(ext => ext.path)];
|
|
184
|
-
return OVERLAY_LANGUAGES.filter(lang =>
|
|
185
|
-
files.some(file => fs.existsSync(overlayPath(file, lang))),
|
|
186
|
-
);
|
|
131
|
+
return doc;
|
|
187
132
|
}
|
|
188
133
|
|
|
189
134
|
/**
|
|
@@ -201,7 +146,6 @@ export function overlayLanguages(entry) {
|
|
|
201
146
|
* @returns {Promise<{
|
|
202
147
|
* catalog: DocsCatalog,
|
|
203
148
|
* docsData: import('./docs.type.mjs').DocsDetailResponse['data'],
|
|
204
|
-
* lang: string | null,
|
|
205
149
|
* }>}
|
|
206
150
|
*/
|
|
207
151
|
export async function resolveTopicDocs(topic, options = {}) {
|
|
@@ -222,5 +166,5 @@ export async function resolveTopicDocs(topic, options = {}) {
|
|
|
222
166
|
}
|
|
223
167
|
|
|
224
168
|
const docsData = await loadTopicDoc(entry, {lang: effectiveLang});
|
|
225
|
-
return {catalog, docsData
|
|
169
|
+
return {catalog, docsData};
|
|
226
170
|
}
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Resolve token-ref blocks by inlining the referenced section's table.
|
|
6
|
-
* This allows section docs to reference token tables without duplicating data.
|
|
7
|
-
*
|
|
8
|
-
* The reference is resolved through the catalog, so a topic may point at one
|
|
9
|
-
* an integration contributed (or replaced) rather than only at a built-in. It
|
|
10
|
-
* names the section by key or authored title, so it resolves in every language.
|
|
11
|
-
* @param {import('../docs.type.mjs').DocsDetailResponse['data']} docsData
|
|
12
|
-
* @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
|
|
13
|
-
* @param {{lang?: string | null}} [options]
|
|
14
|
-
* @returns {Promise<import('../docs.type.mjs').DocsDetailResponse['data']>}
|
|
15
|
-
*/
|
|
16
|
-
export function resolveTokenRefs(docsData: import("../docs.type.mjs").DocsDetailResponse["data"], catalog: import("../../../foundation/discovery/docs-discovery.mjs").DocsCatalog, { lang }?: {
|
|
17
|
-
lang?: string | null;
|
|
18
|
-
}): Promise<import("../docs.type.mjs").DocsDetailResponse["data"]>;
|
|
19
4
|
/**
|
|
20
5
|
* @param {string} topic
|
|
21
6
|
* @param {object} [options]
|
|
@@ -12,25 +12,18 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import {loadTopicDoc, resolveTopicDocs} from '../_adapter.mjs';
|
|
15
|
-
import {
|
|
16
|
-
sectionKey,
|
|
17
|
-
sourceTitle,
|
|
18
|
-
withSourceTitle,
|
|
19
|
-
} from '../../../foundation/discovery/docs-section-key.mjs';
|
|
20
15
|
|
|
21
16
|
/**
|
|
22
17
|
* Resolve token-ref blocks by inlining the referenced section's table.
|
|
23
18
|
* This allows section docs to reference token tables without duplicating data.
|
|
24
19
|
*
|
|
25
20
|
* The reference is resolved through the catalog, so a topic may point at one
|
|
26
|
-
* an integration contributed (or replaced) rather than only at a built-in.
|
|
27
|
-
* names the section by key or authored title, so it resolves in every language.
|
|
21
|
+
* an integration contributed (or replaced) rather than only at a built-in.
|
|
28
22
|
* @param {import('../docs.type.mjs').DocsDetailResponse['data']} docsData
|
|
29
23
|
* @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
|
|
30
|
-
* @param {{lang?: string | null}} [options]
|
|
31
24
|
* @returns {Promise<import('../docs.type.mjs').DocsDetailResponse['data']>}
|
|
32
25
|
*/
|
|
33
|
-
|
|
26
|
+
async function resolveTokenRefs(docsData, catalog) {
|
|
34
27
|
const resolved = {...docsData, sections: [...docsData.sections]};
|
|
35
28
|
for (let si = 0; si < resolved.sections.length; si++) {
|
|
36
29
|
const section = resolved.sections[si];
|
|
@@ -43,12 +36,10 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
|
|
|
43
36
|
newContent.push({type: 'prose', text: `[token-ref: unknown topic "${block.topic}"]`});
|
|
44
37
|
continue;
|
|
45
38
|
}
|
|
46
|
-
const refDocs = await loadTopicDoc(refEntry
|
|
47
|
-
const wanted = block.section.toLowerCase();
|
|
39
|
+
const refDocs = await loadTopicDoc(refEntry);
|
|
48
40
|
const refSection = refDocs.sections.find(
|
|
49
41
|
(/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ s) =>
|
|
50
|
-
|
|
51
|
-
sourceTitle(s).toLowerCase() === wanted,
|
|
42
|
+
s.title.toLowerCase() === block.section.toLowerCase(),
|
|
52
43
|
);
|
|
53
44
|
if (!refSection) {
|
|
54
45
|
newContent.push({type: 'prose', text: `[token-ref: section "${block.section}" not found in "${block.topic}"]`});
|
|
@@ -61,20 +52,14 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
|
|
|
61
52
|
}
|
|
62
53
|
// If the referenced section has a previewType, attach it to our section
|
|
63
54
|
if (refSection.previewType && !section.previewType) {
|
|
64
|
-
resolved.sections[si] =
|
|
65
|
-
{...section, previewType: refSection.previewType, content: newContent},
|
|
66
|
-
sourceTitle(section),
|
|
67
|
-
);
|
|
55
|
+
resolved.sections[si] = {...section, previewType: refSection.previewType, content: newContent};
|
|
68
56
|
}
|
|
69
57
|
} else {
|
|
70
58
|
newContent.push(block);
|
|
71
59
|
}
|
|
72
60
|
}
|
|
73
61
|
if (resolved.sections[si] === section) {
|
|
74
|
-
resolved.sections[si] =
|
|
75
|
-
{...section, content: newContent},
|
|
76
|
-
sourceTitle(section),
|
|
77
|
-
);
|
|
62
|
+
resolved.sections[si] = {...section, content: newContent};
|
|
78
63
|
} else {
|
|
79
64
|
resolved.sections[si].content = newContent;
|
|
80
65
|
}
|
|
@@ -92,7 +77,7 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
|
|
|
92
77
|
* @returns {Promise<import('../docs.type.mjs').DocsDetailResponse>}
|
|
93
78
|
*/
|
|
94
79
|
export async function detail(topic, options = {}) {
|
|
95
|
-
const {catalog, docsData
|
|
96
|
-
const resolved = await resolveTokenRefs(docsData, catalog
|
|
80
|
+
const {catalog, docsData} = await resolveTopicDocs(topic, options);
|
|
81
|
+
const resolved = await resolveTokenRefs(docsData, catalog);
|
|
97
82
|
return {type: 'docs.detail', data: resolved};
|
|
98
83
|
}
|
|
@@ -1,31 +1,25 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @file docs.detail.section leaf — load a single section of a topic.
|
|
4
|
+
* @file docs.detail.section leaf — load a single named section of a topic.
|
|
5
5
|
*
|
|
6
6
|
* @input A topic name, a section query, and optional {lang, zh, dense}. Resolves
|
|
7
|
-
* and loads the topic via the shared adapter, then finds the section
|
|
8
|
-
*
|
|
9
|
-
* @output { type: 'docs.detail.section', data: ReferenceSection }
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* more than one section (the candidates come back as suggestions).
|
|
7
|
+
* and loads the topic via the shared adapter, then finds the first section
|
|
8
|
+
* whose title contains the (case-insensitive) query.
|
|
9
|
+
* @output { type: 'docs.detail.section', data: ReferenceSection } — matching
|
|
10
|
+
* `xds --json docs <topic> <section>`. Throws ERR_UNKNOWN_SECTION when no
|
|
11
|
+
* section title matches.
|
|
13
12
|
* @position Leaf nested under api/docs/detail. Shares discovery/loading/
|
|
14
13
|
* topic-resolution with the detail leaf via _adapter.mjs.
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
import {AstryxError} from '../../../error.mjs';
|
|
18
17
|
import {ERROR_CODES} from '../../../../foundation/response/error-codes.mjs';
|
|
19
|
-
import {
|
|
20
|
-
findDocSection,
|
|
21
|
-
sectionKey,
|
|
22
|
-
} from '../../../../foundation/discovery/docs-section-key.mjs';
|
|
23
18
|
import {resolveTopicDocs} from '../../_adapter.mjs';
|
|
24
|
-
import {resolveTokenRefs} from '../detail.mjs';
|
|
25
19
|
|
|
26
20
|
/**
|
|
27
21
|
* @param {string} topic
|
|
28
|
-
* @param {string} sectionName
|
|
22
|
+
* @param {string} sectionName
|
|
29
23
|
* @param {object} [options]
|
|
30
24
|
* @param {string} [options.lang]
|
|
31
25
|
* @param {boolean} [options.zh]
|
|
@@ -34,9 +28,10 @@ import {resolveTokenRefs} from '../detail.mjs';
|
|
|
34
28
|
* @returns {Promise<import('../../docs.type.mjs').DocsDetailSectionResponse>}
|
|
35
29
|
*/
|
|
36
30
|
export async function section(topic, sectionName, options = {}) {
|
|
37
|
-
// An empty section name must error, not resolve to the first section
|
|
38
|
-
// docs() dispatcher routes a falsy section to
|
|
39
|
-
// safe on its own
|
|
31
|
+
// An empty section name must error, not resolve to the first section via
|
|
32
|
+
// `.includes('')`. The docs() dispatcher routes a falsy section to detail, but
|
|
33
|
+
// the leaf must be safe on its own. A non-string would also throw a raw
|
|
34
|
+
// TypeError below (`.toLowerCase()`) → downgrades to ERR_UNKNOWN.
|
|
40
35
|
if (typeof sectionName !== 'string' || !sectionName.trim()) {
|
|
41
36
|
throw new AstryxError(
|
|
42
37
|
'A section name is required',
|
|
@@ -44,31 +39,16 @@ export async function section(topic, sectionName, options = {}) {
|
|
|
44
39
|
ERROR_CODES.ERR_UNKNOWN_SECTION,
|
|
45
40
|
);
|
|
46
41
|
}
|
|
47
|
-
const {
|
|
42
|
+
const {docsData} = await resolveTopicDocs(topic, options);
|
|
48
43
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
sectionName,
|
|
52
|
-
);
|
|
44
|
+
const normalizedSection = sectionName.toLowerCase();
|
|
45
|
+
const match = docsData.sections.find(s => s.title.toLowerCase().includes(normalizedSection));
|
|
53
46
|
if (!match) {
|
|
54
|
-
const ambiguous = candidates.length > 1;
|
|
55
47
|
throw new AstryxError(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
: `Section "${sectionName}" not found in "${topic}"`,
|
|
59
|
-
(ambiguous ? candidates : docsData.sections).map(s => ({
|
|
60
|
-
name: sectionKey(s),
|
|
61
|
-
reason: s.title,
|
|
62
|
-
})),
|
|
48
|
+
`Section "${sectionName}" not found in "${topic}"`,
|
|
49
|
+
docsData.sections.map(s => ({name: s.title, reason: 'available section'})),
|
|
63
50
|
ERROR_CODES.ERR_UNKNOWN_SECTION,
|
|
64
51
|
);
|
|
65
52
|
}
|
|
66
|
-
|
|
67
|
-
// otherwise a section that is only a token-ref prints blank.
|
|
68
|
-
const {sections} = await resolveTokenRefs(
|
|
69
|
-
{...docsData, sections: [match]},
|
|
70
|
-
catalog,
|
|
71
|
-
{lang},
|
|
72
|
-
);
|
|
73
|
-
return {type: 'docs.detail.section', data: sections[0]};
|
|
53
|
+
return {type: 'docs.detail.section', data: match};
|
|
74
54
|
}
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import {describe, it, expect} from 'vitest';
|
|
11
11
|
import {section} from './section.mjs';
|
|
12
12
|
import {AstryxError} from '../../../error.mjs';
|
|
13
|
-
import {loadDocsCatalog, loadTopicDoc} from '../../_adapter.mjs';
|
|
14
13
|
|
|
15
14
|
const SLOW = 30_000;
|
|
16
15
|
|
|
@@ -40,43 +39,4 @@ describe('docs.detail.section leaf', () => {
|
|
|
40
39
|
code: 'ERR_UNKNOWN_SECTION',
|
|
41
40
|
});
|
|
42
41
|
}, SLOW);
|
|
43
|
-
|
|
44
|
-
it('reads a section by its stable key', async () => {
|
|
45
|
-
const doc = await loadTopicDoc((await loadDocsCatalog()).resolve('theme'));
|
|
46
|
-
const target = doc.sections[doc.sections.length - 1];
|
|
47
|
-
const res = await section('theme', target.id);
|
|
48
|
-
expect(res.data.title).toBe(target.title);
|
|
49
|
-
expect(res.data.id).toBe(target.id);
|
|
50
|
-
}, SLOW);
|
|
51
|
-
|
|
52
|
-
it('refuses a query that matches more than one section', async () => {
|
|
53
|
-
const err = await section('theme', 'e').catch(e => e);
|
|
54
|
-
expect(err).toBeInstanceOf(AstryxError);
|
|
55
|
-
expect(err.code).toBe('ERR_UNKNOWN_SECTION');
|
|
56
|
-
expect(err.message).toMatch(/matches \d+ sections/);
|
|
57
|
-
expect(err.suggestions.length).toBeGreaterThan(1);
|
|
58
|
-
}, SLOW);
|
|
59
|
-
|
|
60
|
-
it.each([null, 'zh', 'dense'])(
|
|
61
|
-
'inlines token refs when a section is read on its own (lang %s)',
|
|
62
|
-
async lang => {
|
|
63
|
-
const catalog = await loadDocsCatalog();
|
|
64
|
-
let checked = 0;
|
|
65
|
-
for (const entry of catalog.entries()) {
|
|
66
|
-
const doc = await loadTopicDoc(entry);
|
|
67
|
-
for (const own of doc.sections) {
|
|
68
|
-
if (!own.content.some(block => block.type === 'token-ref')) continue;
|
|
69
|
-
const res = await section(entry.name, own.id, lang ? {lang} : {});
|
|
70
|
-
expect(res.data.content.length).toBeGreaterThan(0);
|
|
71
|
-
expect(res.data.content.some(block => block.type === 'token-ref')).toBe(
|
|
72
|
-
false,
|
|
73
|
-
);
|
|
74
|
-
expect(JSON.stringify(res.data.content)).not.toContain('[token-ref:');
|
|
75
|
-
checked += 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
expect(checked).toBeGreaterThan(0);
|
|
79
|
-
},
|
|
80
|
-
SLOW,
|
|
81
|
-
);
|
|
82
42
|
});
|
package/api/docs/docs.d.mts
CHANGED
|
@@ -8,12 +8,9 @@
|
|
|
8
8
|
* @param {string} [options.lang]
|
|
9
9
|
* @param {boolean} [options.zh]
|
|
10
10
|
* @param {boolean} [options.dense]
|
|
11
|
-
* @param {boolean} [options.index] return the topic's section index instead of
|
|
12
|
-
* the whole doc
|
|
13
11
|
* @param {string} [options.cwd]
|
|
14
12
|
* @returns {Promise<
|
|
15
13
|
* import('./docs.type.mjs').DocsListResponse |
|
|
16
|
-
* import('./docs.type.mjs').DocsIndexResponse |
|
|
17
14
|
* import('./docs.type.mjs').DocsDetailResponse |
|
|
18
15
|
* import('./docs.type.mjs').DocsDetailSectionResponse
|
|
19
16
|
* >}
|
|
@@ -22,11 +19,9 @@ export function docs(topic?: string, section?: string, options?: {
|
|
|
22
19
|
lang?: string | undefined;
|
|
23
20
|
zh?: boolean | undefined;
|
|
24
21
|
dense?: boolean | undefined;
|
|
25
|
-
index?: boolean | undefined;
|
|
26
22
|
cwd?: string | undefined;
|
|
27
|
-
}): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").
|
|
23
|
+
}): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
|
|
28
24
|
import { list } from './list/list.mjs';
|
|
29
|
-
import { index } from './index/index.mjs';
|
|
30
25
|
import { detail } from './detail/detail.mjs';
|
|
31
26
|
import { section as sectionLeaf } from './detail/section/section.mjs';
|
|
32
|
-
export { list,
|
|
27
|
+
export { list, detail, sectionLeaf as section };
|
package/api/docs/docs.doc.mjs
CHANGED
|
@@ -13,20 +13,18 @@ export const doc = {
|
|
|
13
13
|
name: 'docs',
|
|
14
14
|
displayName: 'docs()',
|
|
15
15
|
summary:
|
|
16
|
-
'Read the reference docs: list every topic, one topic
|
|
16
|
+
'Read the reference docs: list every topic, one topic, or a single section of a topic.',
|
|
17
17
|
description:
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'part of its title (an ambiguous query is refused). Token-ref blocks are ' +
|
|
23
|
-
'inlined in every read. The topic set is the CLI\'s own docs plus the ' +
|
|
18
|
+
'Routes on its arguments: no topic lists every reference-doc topic; a topic ' +
|
|
19
|
+
'returns that full ReferenceDoc (with token-ref blocks inlined); a topic ' +
|
|
20
|
+
'plus a section returns the first section whose title contains the ' +
|
|
21
|
+
'(case-insensitive) query. The topic set is the CLI\'s own docs plus the ' +
|
|
24
22
|
'ones the project\'s configured integrations contribute, including any ' +
|
|
25
23
|
'topic an integration replaces or extends, so it depends on the cwd. ' +
|
|
26
24
|
'Overlay options select localized or dense variants.',
|
|
27
25
|
importPath: '@astryxdesign/cli/api',
|
|
28
26
|
signature:
|
|
29
|
-
'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse |
|
|
27
|
+
'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsDetailResponse | DocsDetailSectionResponse>',
|
|
30
28
|
keywords: [
|
|
31
29
|
'docs',
|
|
32
30
|
'documentation',
|
|
@@ -48,7 +46,7 @@ export const doc = {
|
|
|
48
46
|
name: 'section',
|
|
49
47
|
type: 'string',
|
|
50
48
|
description:
|
|
51
|
-
|
|
49
|
+
'Section within the topic to return; matches the first section title that contains this (case-insensitive).',
|
|
52
50
|
},
|
|
53
51
|
{
|
|
54
52
|
name: 'options.lang',
|
|
@@ -65,12 +63,6 @@ export const doc = {
|
|
|
65
63
|
type: 'boolean',
|
|
66
64
|
description: 'Return the token-efficient dense doc variant.',
|
|
67
65
|
},
|
|
68
|
-
{
|
|
69
|
-
name: 'options.index',
|
|
70
|
-
type: 'boolean',
|
|
71
|
-
description:
|
|
72
|
-
"Return the topic's section index (each section's key, title, and summary) instead of the whole doc.",
|
|
73
|
-
},
|
|
74
66
|
{
|
|
75
67
|
name: 'options.cwd',
|
|
76
68
|
type: 'string',
|
|
@@ -89,15 +81,10 @@ export const doc = {
|
|
|
89
81
|
description:
|
|
90
82
|
"One topic's full ReferenceDoc, with token-ref blocks inlined.",
|
|
91
83
|
},
|
|
92
|
-
{
|
|
93
|
-
type: 'docs.index',
|
|
94
|
-
description:
|
|
95
|
-
"One topic's section index (index: true): {name, title, description, sections: [{id, title, summary}]}.",
|
|
96
|
-
},
|
|
97
84
|
{
|
|
98
85
|
type: 'docs.detail.section',
|
|
99
86
|
description:
|
|
100
|
-
'
|
|
87
|
+
'A single ReferenceSection of the topic: the first whose title contains the section query.',
|
|
101
88
|
},
|
|
102
89
|
],
|
|
103
90
|
throws: [
|
|
@@ -107,17 +94,13 @@ export const doc = {
|
|
|
107
94
|
},
|
|
108
95
|
{
|
|
109
96
|
code: 'ERR_UNKNOWN_SECTION',
|
|
110
|
-
when: 'a section is requested but is empty
|
|
97
|
+
when: 'a section is requested but is empty or matches no section title in the topic',
|
|
111
98
|
},
|
|
112
99
|
],
|
|
113
100
|
examples: [
|
|
114
101
|
{label: 'List topics', code: 'const r = await docs();'},
|
|
115
102
|
{label: 'Load a topic', code: "await docs('principles');"},
|
|
116
|
-
{
|
|
117
|
-
label: "A topic's sections",
|
|
118
|
-
code: "await docs('principles', undefined, {index: true});",
|
|
119
|
-
},
|
|
120
|
-
{label: 'One section by key', code: "await docs('tokens', 'spacing');"},
|
|
103
|
+
{label: 'One section', code: "await docs('tokens', 'spacing');"},
|
|
121
104
|
],
|
|
122
105
|
command: 'docs',
|
|
123
106
|
related: ['search', 'component', 'hook', 'template'],
|
package/api/docs/docs.mjs
CHANGED
|
@@ -3,27 +3,24 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Programmatic API for the docs command.
|
|
5
5
|
*
|
|
6
|
-
* Dispatcher + barrel. `docs()` routes by argument shape into one of
|
|
6
|
+
* Dispatcher + barrel. `docs()` routes by argument shape into one of three
|
|
7
7
|
* leaves, each projecting into a single { type, data } envelope:
|
|
8
8
|
*
|
|
9
|
-
* docs()
|
|
10
|
-
* docs(topic)
|
|
11
|
-
* docs(topic,
|
|
12
|
-
* docs(topic, section) -> section -> docs.detail.section
|
|
9
|
+
* docs() -> list -> docs.list
|
|
10
|
+
* docs(topic) -> detail -> docs.detail
|
|
11
|
+
* docs(topic, section) -> section -> docs.detail.section
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* _adapter.mjs.
|
|
13
|
+
* The leaves live in list/, detail/, and detail/section/; the discovery,
|
|
14
|
+
* overlay loading, and topic resolution they share sit in _adapter.mjs. This
|
|
15
|
+
* module keeps the same `docs` export (and re-exports the leaves) so
|
|
16
|
+
* api/index.mjs and the CLI consumer import from here unchanged.
|
|
19
17
|
*/
|
|
20
18
|
|
|
21
19
|
import {list} from './list/list.mjs';
|
|
22
|
-
import {index} from './index/index.mjs';
|
|
23
20
|
import {detail} from './detail/detail.mjs';
|
|
24
21
|
import {section as sectionLeaf} from './detail/section/section.mjs';
|
|
25
22
|
|
|
26
|
-
export {list,
|
|
23
|
+
export {list, detail, sectionLeaf as section};
|
|
27
24
|
|
|
28
25
|
/**
|
|
29
26
|
* @param {string} [topic]
|
|
@@ -32,12 +29,9 @@ export {list, index, detail, sectionLeaf as section};
|
|
|
32
29
|
* @param {string} [options.lang]
|
|
33
30
|
* @param {boolean} [options.zh]
|
|
34
31
|
* @param {boolean} [options.dense]
|
|
35
|
-
* @param {boolean} [options.index] return the topic's section index instead of
|
|
36
|
-
* the whole doc
|
|
37
32
|
* @param {string} [options.cwd]
|
|
38
33
|
* @returns {Promise<
|
|
39
34
|
* import('./docs.type.mjs').DocsListResponse |
|
|
40
|
-
* import('./docs.type.mjs').DocsIndexResponse |
|
|
41
35
|
* import('./docs.type.mjs').DocsDetailResponse |
|
|
42
36
|
* import('./docs.type.mjs').DocsDetailSectionResponse
|
|
43
37
|
* >}
|
|
@@ -45,6 +39,5 @@ export {list, index, detail, sectionLeaf as section};
|
|
|
45
39
|
export async function docs(topic, section, options = {}) {
|
|
46
40
|
if (!topic) return list(options);
|
|
47
41
|
if (section) return sectionLeaf(topic, section, options);
|
|
48
|
-
if (options.index) return index(topic, options);
|
|
49
42
|
return detail(topic, options);
|
|
50
43
|
}
|
package/api/docs/docs.test.mjs
CHANGED
|
@@ -33,12 +33,6 @@ describe('docs() dispatcher routing', () => {
|
|
|
33
33
|
expect(r.type).toBe('docs.detail');
|
|
34
34
|
}, SLOW);
|
|
35
35
|
|
|
36
|
-
it('topic + index -> docs.index', async () => {
|
|
37
|
-
const {data} = await docs();
|
|
38
|
-
const r = await docs(data[0].topic, undefined, {index: true});
|
|
39
|
-
expect(r.type).toBe('docs.index');
|
|
40
|
-
}, SLOW);
|
|
41
|
-
|
|
42
36
|
it('topic + section -> docs.detail.section', async () => {
|
|
43
37
|
const {data} = await docs();
|
|
44
38
|
let routed = null;
|