@astryxdesign/cli 0.6.3-canary.f22695a → 0.6.3
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/build/build.type.d.mts +2 -2
- package/api/build/build.type.mjs +2 -2
- package/api/component/component.type.d.mts +6 -6
- package/api/component/component.type.mjs +19 -19
- package/api/discover/discover.type.d.mts +4 -4
- package/api/discover/discover.type.mjs +10 -10
- package/api/docs/_adapter.d.mts +24 -37
- package/api/docs/_adapter.mjs +83 -169
- package/api/docs/detail/detail.mjs +63 -14
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +20 -44
- package/api/docs/detail/section/section.test.mjs +0 -41
- 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 +3 -40
- package/api/docs/docs.type.mjs +8 -36
- package/api/docs/integrationDocs.test.mjs +0 -106
- package/api/doctor/doctor.d.mts +0 -48
- package/api/doctor/doctor.mjs +0 -232
- package/api/doctor/doctor.test.mjs +0 -196
- package/api/hook/hook.type.d.mts +3 -3
- package/api/hook/hook.type.mjs +11 -11
- package/api/hook/list/list.d.mts +1 -1
- package/api/integration/add-contribution.mjs +3 -5
- package/api/integration/add-contribution.test.mjs +4 -4
- package/api/integration/integration-authoring.type.d.mts +1 -1
- package/api/integration/pack-check.mjs +7 -49
- package/api/integration/pack-check.test.mjs +0 -249
- package/api/search/search.d.mts +1 -1
- package/api/search/search.mjs +5 -5
- package/api/search/search.type.d.mts +2 -2
- package/api/search/search.type.mjs +1 -1
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/template.d.mts +1 -1
- package/api/template/template.type.d.mts +6 -6
- package/api/template/template.type.mjs +12 -12
- package/api/theme/build/build.mjs +6 -20
- package/api/theme/build/build.test.mjs +0 -127
- package/api/theme/palette/generate/generate.mjs +1 -1
- package/api/theme/palette/generate/generator.d.mts +13 -10
- package/api/theme/palette/generate/generator.mjs +3 -7
- package/api/theme/theme.type.d.mts +11 -170
- package/api/theme/theme.type.mjs +27 -94
- package/api/upgrade/_adapter.mjs +5 -71
- package/api/upgrade/upgrade.doc.mjs +3 -4
- package/api/upgrade/upgrade.type.d.mts +5 -5
- package/api/upgrade/upgrade.type.mjs +11 -11
- package/assets/codemods/integration-discovery.mjs +2 -40
- package/assets/codemods/integration-discovery.test.mjs +0 -58
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +5 -27
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +5 -20
- package/assets/docs/README.md +0 -9
- package/assets/docs/cli-integrations.doc.mjs +15 -86
- package/assets/docs/styling-libraries.doc.mjs +1 -1
- package/assets/docs/working-with-ai.doc.mjs +1 -1
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +3 -19
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +65 -383
- package/authoring/_shared/contract.ts +0 -22
- package/authoring/codemod/codemod.doc.mjs +1 -6
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +6 -8
- package/authoring/config/parse.d.mts +13 -13
- package/authoring/config/parse.mjs +8 -8
- package/authoring/config/type.ts +3 -3
- package/authoring/debug/parse.d.mts +5 -5
- package/authoring/debug/parse.mjs +3 -3
- package/authoring/doctypes/_schema.d.mts +23 -788
- package/authoring/doctypes/_schema.mjs +39 -492
- package/authoring/doctypes/base/type.ts +0 -40
- package/authoring/doctypes/command/command.doc.mjs +2 -3
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +2 -3
- package/authoring/doctypes/component/component.doc.mjs +3 -6
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +3 -4
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -3
- package/authoring/doctypes/function/function.doc.mjs +0 -4
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +2 -6
- package/authoring/doctypes/hook/hook.doc.mjs +0 -4
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +2 -3
- package/authoring/doctypes/legacy.d.mts +6 -8
- package/authoring/doctypes/legacy.mjs +4 -5
- package/authoring/doctypes/parse.d.mts +18 -20
- package/authoring/doctypes/parse.mjs +10 -16
- package/authoring/doctypes/parse.test.mjs +3 -77
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +5 -8
- package/authoring/doctypes/reference/reference.doc.mjs +4 -17
- package/authoring/doctypes/reference/type.ts +5 -51
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +2 -3
- package/authoring/doctypes/template/parse.d.mts +1 -92
- package/authoring/doctypes/template/parse.mjs +2 -36
- package/authoring/doctypes/template/parse.test.mjs +2 -8
- package/authoring/doctypes/template/template.doc.mjs +0 -4
- package/authoring/doctypes/template/type.ts +2 -5
- package/authoring/doctypes/types.ts +9 -10
- package/authoring/gap-report/parse.d.mts +10 -10
- package/authoring/gap-report/parse.mjs +6 -6
- package/authoring/gap-report/type.ts +1 -1
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +17 -49
- package/authoring/index.mjs +0 -1
- package/authoring/integration/integration.doc.mjs +6 -13
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +1 -10
- package/authoring/integration/schema.d.mts +4 -6
- package/authoring/integration/schema.mjs +3 -9
- package/authoring/integration/type.ts +6 -23
- 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/integration-authoring.test.mjs +9 -13
- 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 -11
- package/foundation/discovery/docs-discovery.mjs +88 -208
- package/foundation/discovery/docs-discovery.test.mjs +13 -279
- 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 +11 -9
- package/api/docs/compiled-topics.test.mjs +0 -78
- package/api/docs/index/index.d.mts +0 -18
- package/api/docs/index/index.mjs +0 -32
- 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 -62
- package/authoring/doctypes/load-contract.test.mjs +0 -207
- package/authoring/doctypes/namespace/namespace.doc.d.mts +0 -9
- package/authoring/doctypes/namespace/namespace.doc.mjs +0 -132
- 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 -61
- 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 -154
- 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/doc-compiler/compile.d.mts +0 -162
- package/foundation/doc-compiler/compile.mjs +0 -262
- package/foundation/doc-compiler/doc-compiler.test.mjs +0 -687
- package/foundation/doc-compiler/ir.d.mts +0 -9
- package/foundation/doc-compiler/ir.mjs +0 -287
- package/foundation/doc-compiler/lenses.d.mts +0 -33
- package/foundation/doc-compiler/lenses.mjs +0 -127
- 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/api/docs/_adapter.mjs
CHANGED
|
@@ -7,27 +7,25 @@
|
|
|
7
7
|
* packages/cli/assets/docs/{topic}.doc.mjs plus every topic the configured
|
|
8
8
|
* integrations contribute — and, when a --dense/--zh overlay is requested,
|
|
9
9
|
* the sibling {topic}.doc.dense.mjs / {topic}.doc.zh.mjs.
|
|
10
|
-
* @output Catalog access,
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @position Sits beside docs.mjs (api/docs/).
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* @output Catalog access, overlay- and extension-merged reference-doc data,
|
|
11
|
+
* and a combined resolve step ({catalog, docsData}) that the detail and
|
|
12
|
+
* section leaves share.
|
|
13
|
+
* @position Sits beside docs.mjs (api/docs/). Owns everything ≥2 leaves need so
|
|
14
|
+
* no leaf re-implements resolution, overlay merging, or unknown-topic
|
|
15
|
+
* handling. Discovery itself lives in foundation/discovery/docs-discovery,
|
|
16
|
+
* which api/search and the agent-docs block read through the same catalog.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import * as fs from 'node:fs';
|
|
20
20
|
import * as path from 'node:path';
|
|
21
21
|
import {pathToFileURL} from 'node:url';
|
|
22
22
|
import {Project} from '../../foundation/config/project.mjs';
|
|
23
|
-
import {DocsCatalog} from '../../foundation/discovery/docs-discovery.mjs';
|
|
24
23
|
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} from '../../foundation/
|
|
24
|
+
DocsCatalog,
|
|
25
|
+
mergeTopic,
|
|
26
|
+
} from '../../foundation/discovery/docs-discovery.mjs';
|
|
28
27
|
import {AstryxError} from '../error.mjs';
|
|
29
28
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
30
|
-
import {parseDoc} from '../../authoring/doctypes/parse.mjs';
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
31
|
* The project's topics: the built-in ones plus whatever the configured
|
|
@@ -51,176 +49,93 @@ export async function loadDocsCatalog(cwd = process.cwd()) {
|
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
/** The localized overlays a docs read can apply. */
|
|
55
|
-
export const OVERLAY_LANGUAGES = ['zh', 'dense'];
|
|
56
|
-
|
|
57
52
|
/**
|
|
58
|
-
* Where the `lang` overlay of a doc file lives: `{topic}.doc.{lang}.mjs`.
|
|
59
53
|
* @param {string} docPath
|
|
60
|
-
* @param {string}
|
|
61
|
-
* @returns {
|
|
62
|
-
*/
|
|
63
|
-
function overlayPath(docPath, lang) {
|
|
64
|
-
return path.join(
|
|
65
|
-
path.dirname(docPath),
|
|
66
|
-
`${path.basename(docPath, '.doc.mjs')}.doc.${lang}.mjs`,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* The overlay languages a topic ships for its own file or any extension.
|
|
72
|
-
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
73
|
-
* @returns {string[]}
|
|
54
|
+
* @param {{lang?: string|null}} [opts]
|
|
55
|
+
* @returns {Promise<import('./docs.type.mjs').DocsDetailResponse['data']>}
|
|
74
56
|
*/
|
|
75
|
-
export function
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const mod = await import(pathToFileURL(docPath).href);
|
|
103
|
-
doc = parseDoc(mod.docs ?? mod.default, file);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
return {file, error};
|
|
106
|
-
}
|
|
107
|
-
if (!lang) return {file, doc};
|
|
108
|
-
const translationPath = overlayPath(docPath, lang);
|
|
109
|
-
if (!fs.existsSync(translationPath)) return {file, doc};
|
|
110
|
-
try {
|
|
111
|
-
const translationMod = await import(pathToFileURL(translationPath).href);
|
|
112
|
-
return {
|
|
113
|
-
file,
|
|
114
|
-
doc,
|
|
115
|
-
overlay: translationMod.docsZh || translationMod.docsDense || null,
|
|
116
|
-
};
|
|
117
|
-
} catch (overlayError) {
|
|
118
|
-
return {file, doc, overlayError};
|
|
57
|
+
export async function loadReferenceDocs(docPath, {lang} = {}) {
|
|
58
|
+
const mod = await import(pathToFileURL(docPath).href);
|
|
59
|
+
const docs = mod.docs ?? mod.default;
|
|
60
|
+
if (!lang || lang === 'en') return docs;
|
|
61
|
+
|
|
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`);
|
|
66
|
+
if (!fs.existsSync(translationPath)) return docs;
|
|
67
|
+
|
|
68
|
+
const translationMod = await import(pathToFileURL(translationPath).href);
|
|
69
|
+
const translation = translationMod.docsZh || translationMod.docsDense;
|
|
70
|
+
if (!translation) return docs;
|
|
71
|
+
|
|
72
|
+
// Overlays are keyed to a base section by title (`section`), not by array
|
|
73
|
+
// position. Position-keying silently grafted each overlay title onto whatever
|
|
74
|
+
// base section happened to share its index, so an overlay that omitted or
|
|
75
|
+
// reordered a section corrupted every section after it — `docs tokens --dense`
|
|
76
|
+
// printed the colour table under a "Spacing" heading (#2182). An overlay may
|
|
77
|
+
// now cover any subset of sections, in any order; sections it does not name
|
|
78
|
+
// keep their base content.
|
|
79
|
+
/** @type {Map<string, any>} */
|
|
80
|
+
const bySection = new Map();
|
|
81
|
+
for (const ts of translation.sections ?? []) {
|
|
82
|
+
if (ts?.section != null) bySection.set(ts.section, ts);
|
|
119
83
|
}
|
|
120
|
-
}
|
|
121
84
|
|
|
122
|
-
/**
|
|
123
|
-
* Everything the compiler needs for one topic, read from disk.
|
|
124
|
-
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
125
|
-
* @param {string | null} lang
|
|
126
|
-
* @returns {Promise<import('../../foundation/doc-compiler/compile.mjs').ReferenceTopicInput>}
|
|
127
|
-
*/
|
|
128
|
-
async function loadCompilerInput(entry, lang) {
|
|
129
|
-
const extensions = [];
|
|
130
|
-
for (const extension of entry.extensions) {
|
|
131
|
-
extensions.push({
|
|
132
|
-
...(await loadAuthoredFile(extension.path, lang)),
|
|
133
|
-
provider: extension.package,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
85
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
86
|
+
...docs,
|
|
87
|
+
description: translation.description || docs.description,
|
|
88
|
+
sections: docs.sections.map(
|
|
89
|
+
(/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ section) => {
|
|
90
|
+
const ts = bySection.get(section.title);
|
|
91
|
+
if (!ts) return section;
|
|
92
|
+
return {
|
|
93
|
+
...section,
|
|
94
|
+
title: ts.title || section.title,
|
|
95
|
+
content: section.content.map(
|
|
96
|
+
(
|
|
97
|
+
/** @type {import('@astryxdesign/cli/authoring').ReferenceContentBlock} */ block,
|
|
98
|
+
/** @type {number} */ bi,
|
|
99
|
+
) => {
|
|
100
|
+
const tb = ts.content?.[bi];
|
|
101
|
+
if (!tb) return block;
|
|
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};
|
|
104
|
+
return block;
|
|
105
|
+
},
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
),
|
|
143
110
|
};
|
|
144
111
|
}
|
|
145
112
|
|
|
146
|
-
/** @type {WeakMap<DocsCatalog, Map<string, Promise<import('../../foundation/doc-compiler/compile.mjs').CompiledReferenceNode>>>} */
|
|
147
|
-
const loweredByCatalog = new WeakMap();
|
|
148
|
-
|
|
149
113
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
114
|
+
* Load one catalog entry: its own doc, plus any extension an integration
|
|
115
|
+
* merged onto it, in configuration order.
|
|
116
|
+
*
|
|
117
|
+
* A localization overlay applies to each file before the extensions are
|
|
118
|
+
* merged, so an extension written in the base language stays readable under
|
|
119
|
+
* `--dense`/`--zh` (it replaces its own sections and leaves the rest
|
|
120
|
+
* translated) rather than being dropped.
|
|
121
|
+
*
|
|
155
122
|
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
156
|
-
* @param {string
|
|
157
|
-
* @returns {Promise<import('
|
|
158
|
-
*/
|
|
159
|
-
export function lowerTopic(catalog, entry, lang = null) {
|
|
160
|
-
const overlay = overlayLanguage(lang);
|
|
161
|
-
let cache = loweredByCatalog.get(catalog);
|
|
162
|
-
if (!cache) {
|
|
163
|
-
cache = new Map();
|
|
164
|
-
loweredByCatalog.set(catalog, cache);
|
|
165
|
-
}
|
|
166
|
-
const key = `${entry.name.toLowerCase()}\u0000${overlay ?? ''}`;
|
|
167
|
-
let lowered = cache.get(key);
|
|
168
|
-
if (!lowered) {
|
|
169
|
-
lowered = loadCompilerInput(entry, overlay).then(input =>
|
|
170
|
-
deepFreeze(lowerReferenceTopic(input)),
|
|
171
|
-
);
|
|
172
|
-
cache.set(key, lowered);
|
|
173
|
-
}
|
|
174
|
-
return lowered;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Freeze a value and everything in it.
|
|
179
|
-
* @template T
|
|
180
|
-
* @param {T} value
|
|
181
|
-
* @returns {T}
|
|
123
|
+
* @param {{lang?: string|null}} [opts]
|
|
124
|
+
* @returns {Promise<import('./docs.type.mjs').DocsDetailResponse['data']>}
|
|
182
125
|
*/
|
|
183
|
-
function
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
126
|
+
export async function loadTopicDoc(entry, {lang} = {}) {
|
|
127
|
+
let doc = await loadReferenceDocs(entry.path, {lang});
|
|
128
|
+
for (const extension of entry.extensions) {
|
|
129
|
+
doc = mergeTopic(doc, await loadReferenceDocs(extension.path, {lang}));
|
|
187
130
|
}
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* How a token reference finds its target: the topic it names in `catalog`,
|
|
193
|
-
* lowered for the same language.
|
|
194
|
-
* @param {DocsCatalog} catalog
|
|
195
|
-
* @param {string | null} lang
|
|
196
|
-
* @returns {(topic: string) => Promise<import('../../foundation/doc-compiler/compile.mjs').CompiledReferenceNode | null>}
|
|
197
|
-
*/
|
|
198
|
-
export function referenceTargets(catalog, lang) {
|
|
199
|
-
return async topic => {
|
|
200
|
-
const target = catalog.resolve(topic);
|
|
201
|
-
return target ? lowerTopic(catalog, target, lang) : null;
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* One topic, compiled for `lang`: lowered, then every token reference linked.
|
|
207
|
-
* @param {DocsCatalog} catalog
|
|
208
|
-
* @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
|
|
209
|
-
* @param {string | null} [lang]
|
|
210
|
-
* @returns {Promise<import('../../foundation/doc-compiler/compile.mjs').CompiledReferenceNode>}
|
|
211
|
-
*/
|
|
212
|
-
export async function compileTopic(catalog, entry, lang = null) {
|
|
213
|
-
return linkReferenceTopic(
|
|
214
|
-
await lowerTopic(catalog, entry, lang),
|
|
215
|
-
referenceTargets(catalog, lang),
|
|
216
|
-
);
|
|
131
|
+
return doc;
|
|
217
132
|
}
|
|
218
133
|
|
|
219
134
|
/**
|
|
220
135
|
* Resolve `topic` against the project's catalog (throwing `ERR_UNKNOWN_TOPIC`
|
|
221
|
-
* when unmatched) and
|
|
222
|
-
* integration extension applied. Shared by the leaves so
|
|
223
|
-
* and unknown-topic handling live in exactly one place.
|
|
136
|
+
* when unmatched), and load it with any --dense/--zh overlay and any
|
|
137
|
+
* integration extension applied. Shared by the detail and section leaves so
|
|
138
|
+
* topic normalization and unknown-topic handling live in exactly one place.
|
|
224
139
|
*
|
|
225
140
|
* @param {string} topic
|
|
226
141
|
* @param {object} [options]
|
|
@@ -230,8 +145,7 @@ export async function compileTopic(catalog, entry, lang = null) {
|
|
|
230
145
|
* @param {string} [options.cwd]
|
|
231
146
|
* @returns {Promise<{
|
|
232
147
|
* catalog: DocsCatalog,
|
|
233
|
-
*
|
|
234
|
-
* lang: string | null,
|
|
148
|
+
* docsData: import('./docs.type.mjs').DocsDetailResponse['data'],
|
|
235
149
|
* }>}
|
|
236
150
|
*/
|
|
237
151
|
export async function resolveTopicDocs(topic, options = {}) {
|
|
@@ -251,6 +165,6 @@ export async function resolveTopicDocs(topic, options = {}) {
|
|
|
251
165
|
);
|
|
252
166
|
}
|
|
253
167
|
|
|
254
|
-
const
|
|
255
|
-
return {catalog,
|
|
168
|
+
const docsData = await loadTopicDoc(entry, {lang: effectiveLang});
|
|
169
|
+
return {catalog, docsData};
|
|
256
170
|
}
|
|
@@ -3,17 +3,69 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file docs.detail leaf — load one topic's full reference doc.
|
|
5
5
|
*
|
|
6
|
-
* @input A topic name plus optional {lang, zh, dense
|
|
7
|
-
* via the shared adapter
|
|
6
|
+
* @input A topic name plus optional {lang, zh, dense}. Resolves and loads the
|
|
7
|
+
* topic via the shared adapter, then inlines any token-ref blocks.
|
|
8
8
|
* @output { type: 'docs.detail', data: ReferenceDoc } — the full doc with
|
|
9
|
-
* token-refs
|
|
10
|
-
* @position Leaf under api/docs.
|
|
11
|
-
*
|
|
9
|
+
* token-refs resolved, matching `xds --json docs <topic>`.
|
|
10
|
+
* @position Leaf under api/docs. Owns resolveTokenRefs (used only here); shares
|
|
11
|
+
* discovery/loading/topic-resolution with the section leaf via _adapter.mjs.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import {loadTopicDoc, resolveTopicDocs} from '../_adapter.mjs';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve token-ref blocks by inlining the referenced section's table.
|
|
18
|
+
* This allows section docs to reference token tables without duplicating data.
|
|
19
|
+
*
|
|
20
|
+
* The reference is resolved through the catalog, so a topic may point at one
|
|
21
|
+
* an integration contributed (or replaced) rather than only at a built-in.
|
|
22
|
+
* @param {import('../docs.type.mjs').DocsDetailResponse['data']} docsData
|
|
23
|
+
* @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
|
|
24
|
+
* @returns {Promise<import('../docs.type.mjs').DocsDetailResponse['data']>}
|
|
25
|
+
*/
|
|
26
|
+
async function resolveTokenRefs(docsData, catalog) {
|
|
27
|
+
const resolved = {...docsData, sections: [...docsData.sections]};
|
|
28
|
+
for (let si = 0; si < resolved.sections.length; si++) {
|
|
29
|
+
const section = resolved.sections[si];
|
|
30
|
+
/** @type {import('@astryxdesign/cli/authoring').ReferenceContentBlock[]} */
|
|
31
|
+
const newContent = [];
|
|
32
|
+
for (const block of section.content) {
|
|
33
|
+
if (block.type === 'token-ref') {
|
|
34
|
+
const refEntry = catalog.resolve(block.topic);
|
|
35
|
+
if (!refEntry) {
|
|
36
|
+
newContent.push({type: 'prose', text: `[token-ref: unknown topic "${block.topic}"]`});
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const refDocs = await loadTopicDoc(refEntry);
|
|
40
|
+
const refSection = refDocs.sections.find(
|
|
41
|
+
(/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ s) =>
|
|
42
|
+
s.title.toLowerCase() === block.section.toLowerCase(),
|
|
43
|
+
);
|
|
44
|
+
if (!refSection) {
|
|
45
|
+
newContent.push({type: 'prose', text: `[token-ref: section "${block.section}" not found in "${block.topic}"]`});
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Inline the referenced section's content blocks (tables, prose, etc.)
|
|
49
|
+
// and carry over the previewType
|
|
50
|
+
for (const refBlock of refSection.content) {
|
|
51
|
+
newContent.push(refBlock);
|
|
52
|
+
}
|
|
53
|
+
// If the referenced section has a previewType, attach it to our section
|
|
54
|
+
if (refSection.previewType && !section.previewType) {
|
|
55
|
+
resolved.sections[si] = {...section, previewType: refSection.previewType, content: newContent};
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
newContent.push(block);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (resolved.sections[si] === section) {
|
|
62
|
+
resolved.sections[si] = {...section, content: newContent};
|
|
63
|
+
} else {
|
|
64
|
+
resolved.sections[si].content = newContent;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return resolved;
|
|
68
|
+
}
|
|
17
69
|
|
|
18
70
|
/**
|
|
19
71
|
* @param {string} topic
|
|
@@ -25,10 +77,7 @@ import {referenceTargets, resolveTopicDocs} from '../_adapter.mjs';
|
|
|
25
77
|
* @returns {Promise<import('../docs.type.mjs').DocsDetailResponse>}
|
|
26
78
|
*/
|
|
27
79
|
export async function detail(topic, options = {}) {
|
|
28
|
-
const {catalog,
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
referenceTargets(catalog, lang),
|
|
32
|
-
);
|
|
33
|
-
return {type: 'docs.detail', data: detailView(linked)};
|
|
80
|
+
const {catalog, docsData} = await resolveTopicDocs(topic, options);
|
|
81
|
+
const resolved = await resolveTokenRefs(docsData, catalog);
|
|
82
|
+
return {type: 'docs.detail', data: resolved};
|
|
34
83
|
}
|
|
@@ -1,36 +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
|
-
* the topic via the shared adapter, finds the section
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @position Leaf nested under api/docs/detail. Shares topic resolution with the
|
|
15
|
-
* detail leaf via _adapter.mjs and reads through the compiler's lenses.
|
|
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.
|
|
12
|
+
* @position Leaf nested under api/docs/detail. Shares discovery/loading/
|
|
13
|
+
* topic-resolution with the detail leaf via _adapter.mjs.
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
16
|
import {AstryxError} from '../../../error.mjs';
|
|
19
17
|
import {ERROR_CODES} from '../../../../foundation/response/error-codes.mjs';
|
|
20
|
-
import {
|
|
21
|
-
findDocSection,
|
|
22
|
-
sectionKey,
|
|
23
|
-
} from '../../../../foundation/discovery/docs-section-key.mjs';
|
|
24
|
-
import {linkReferenceSection} from '../../../../foundation/doc-compiler/compile.mjs';
|
|
25
|
-
import {
|
|
26
|
-
readerSections,
|
|
27
|
-
sectionView,
|
|
28
|
-
} from '../../../../foundation/doc-compiler/lenses.mjs';
|
|
29
|
-
import {referenceTargets, resolveTopicDocs} from '../../_adapter.mjs';
|
|
18
|
+
import {resolveTopicDocs} from '../../_adapter.mjs';
|
|
30
19
|
|
|
31
20
|
/**
|
|
32
21
|
* @param {string} topic
|
|
33
|
-
* @param {string} sectionName
|
|
22
|
+
* @param {string} sectionName
|
|
34
23
|
* @param {object} [options]
|
|
35
24
|
* @param {string} [options.lang]
|
|
36
25
|
* @param {boolean} [options.zh]
|
|
@@ -39,9 +28,10 @@ import {referenceTargets, resolveTopicDocs} from '../../_adapter.mjs';
|
|
|
39
28
|
* @returns {Promise<import('../../docs.type.mjs').DocsDetailSectionResponse>}
|
|
40
29
|
*/
|
|
41
30
|
export async function section(topic, sectionName, options = {}) {
|
|
42
|
-
// An empty section name must error, not resolve to the first section
|
|
43
|
-
// docs() dispatcher routes a falsy section to
|
|
44
|
-
// 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.
|
|
45
35
|
if (typeof sectionName !== 'string' || !sectionName.trim()) {
|
|
46
36
|
throw new AstryxError(
|
|
47
37
|
'A section name is required',
|
|
@@ -49,30 +39,16 @@ export async function section(topic, sectionName, options = {}) {
|
|
|
49
39
|
ERROR_CODES.ERR_UNKNOWN_SECTION,
|
|
50
40
|
);
|
|
51
41
|
}
|
|
42
|
+
const {docsData} = await resolveTopicDocs(topic, options);
|
|
52
43
|
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const {section: match, candidates} = findDocSection(sections, sectionName);
|
|
44
|
+
const normalizedSection = sectionName.toLowerCase();
|
|
45
|
+
const match = docsData.sections.find(s => s.title.toLowerCase().includes(normalizedSection));
|
|
56
46
|
if (!match) {
|
|
57
|
-
const ambiguous = candidates.length > 1;
|
|
58
47
|
throw new AstryxError(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
: `Section "${sectionName}" not found in "${topic}"`,
|
|
62
|
-
(ambiguous ? candidates : sections).map(s => ({
|
|
63
|
-
name: sectionKey(s),
|
|
64
|
-
reason: s.title,
|
|
65
|
-
})),
|
|
48
|
+
`Section "${sectionName}" not found in "${topic}"`,
|
|
49
|
+
docsData.sections.map(s => ({name: s.title, reason: 'available section'})),
|
|
66
50
|
ERROR_CODES.ERR_UNKNOWN_SECTION,
|
|
67
51
|
);
|
|
68
52
|
}
|
|
69
|
-
|
|
70
|
-
// A section read on its own inlines its token refs, as the whole topic does;
|
|
71
|
-
// otherwise a section that is only a token-ref prints blank. Only this
|
|
72
|
-
// section is linked, so a broken reference elsewhere cannot fail the read.
|
|
73
|
-
const linked = await linkReferenceSection(
|
|
74
|
-
match,
|
|
75
|
-
referenceTargets(catalog, lang),
|
|
76
|
-
);
|
|
77
|
-
return {type: 'docs.detail.section', data: sectionView(node, linked)};
|
|
53
|
+
return {type: 'docs.detail.section', data: match};
|
|
78
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, lowerTopic} from '../../_adapter.mjs';
|
|
14
13
|
|
|
15
14
|
const SLOW = 30_000;
|
|
16
15
|
|
|
@@ -40,44 +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 catalog = await loadDocsCatalog();
|
|
46
|
-
const {doc} = await lowerTopic(catalog, catalog.resolve('theme'));
|
|
47
|
-
const target = doc.sections[doc.sections.length - 1];
|
|
48
|
-
const res = await section('theme', target.id);
|
|
49
|
-
expect(res.data.title).toBe(target.title);
|
|
50
|
-
expect(res.data.id).toBe(target.id);
|
|
51
|
-
}, SLOW);
|
|
52
|
-
|
|
53
|
-
it('refuses a query that matches more than one section', async () => {
|
|
54
|
-
const err = await section('theme', 'e').catch(e => e);
|
|
55
|
-
expect(err).toBeInstanceOf(AstryxError);
|
|
56
|
-
expect(err.code).toBe('ERR_UNKNOWN_SECTION');
|
|
57
|
-
expect(err.message).toMatch(/matches \d+ sections/);
|
|
58
|
-
expect(err.suggestions.length).toBeGreaterThan(1);
|
|
59
|
-
}, SLOW);
|
|
60
|
-
|
|
61
|
-
it.each([null, 'zh', 'dense'])(
|
|
62
|
-
'inlines token refs when a section is read on its own (lang %s)',
|
|
63
|
-
async lang => {
|
|
64
|
-
const catalog = await loadDocsCatalog();
|
|
65
|
-
let checked = 0;
|
|
66
|
-
for (const entry of catalog.entries()) {
|
|
67
|
-
const {doc} = await lowerTopic(catalog, entry);
|
|
68
|
-
for (const own of doc.sections) {
|
|
69
|
-
if (!own.content.some(block => block.type === 'token-ref')) continue;
|
|
70
|
-
const res = await section(entry.name, own.id, lang ? {lang} : {});
|
|
71
|
-
expect(res.data.content.length).toBeGreaterThan(0);
|
|
72
|
-
expect(res.data.content.some(block => block.type === 'token-ref')).toBe(
|
|
73
|
-
false,
|
|
74
|
-
);
|
|
75
|
-
expect(JSON.stringify(res.data.content)).not.toContain('[token-ref:');
|
|
76
|
-
checked += 1;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
expect(checked).toBeGreaterThan(0);
|
|
80
|
-
},
|
|
81
|
-
SLOW,
|
|
82
|
-
);
|
|
83
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 };
|