@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ebaebc4
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 +2 -1
- 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 +37 -24
- package/api/docs/_adapter.mjs +169 -83
- package/api/docs/compiled-topics.test.mjs +78 -0
- package/api/docs/detail/detail.mjs +14 -63
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +44 -20
- package/api/docs/detail/section/section.test.mjs +41 -0
- package/api/docs/docs.d.mts +7 -2
- package/api/docs/docs.doc.mjs +27 -10
- package/api/docs/docs.mjs +16 -9
- package/api/docs/docs.test.mjs +6 -0
- package/api/docs/docs.type.d.mts +40 -3
- package/api/docs/docs.type.mjs +36 -8
- package/api/docs/index/index.d.mts +18 -0
- package/api/docs/index/index.mjs +32 -0
- package/api/docs/index/index.test.mjs +62 -0
- package/api/docs/integrationDocs.test.mjs +106 -0
- package/api/doctor/doctor.d.mts +48 -0
- package/api/doctor/doctor.mjs +232 -0
- package/api/doctor/doctor.test.mjs +196 -0
- 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 +5 -3
- 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 +49 -7
- package/api/integration/pack-check.test.mjs +249 -0
- 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 +20 -6
- package/api/theme/build/build.test.mjs +127 -0
- package/api/theme/palette/generate/generate.mjs +1 -1
- package/api/theme/palette/generate/generator.d.mts +10 -13
- package/api/theme/palette/generate/generator.mjs +7 -3
- package/api/theme/theme.type.d.mts +170 -11
- package/api/theme/theme.type.mjs +94 -27
- package/api/upgrade/_adapter.mjs +71 -5
- package/api/upgrade/project-context.test.mjs +272 -0
- package/api/upgrade/upgrade.doc.mjs +4 -3
- package/api/upgrade/upgrade.type.d.mts +5 -5
- package/api/upgrade/upgrade.type.mjs +11 -11
- package/assets/codemods/integration-discovery.mjs +40 -2
- package/assets/codemods/integration-discovery.test.mjs +58 -0
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
- package/assets/docs/README.md +9 -0
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/cli-integrations.doc.mjs +86 -15
- package/assets/docs/styling-libraries.doc.mjs +1 -1
- package/assets/docs/working-with-ai.doc.mjs +1 -1
- package/authoring/_shared/contract.ts +22 -0
- package/authoring/codemod/codemod.doc.mjs +6 -1
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +8 -6
- 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 +788 -23
- package/authoring/doctypes/_schema.mjs +492 -39
- package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +62 -0
- package/authoring/doctypes/base/type.ts +40 -0
- package/authoring/doctypes/command/command.doc.mjs +3 -2
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +3 -2
- package/authoring/doctypes/component/component.doc.mjs +6 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +4 -3
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +3 -1
- package/authoring/doctypes/function/function.doc.mjs +4 -0
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +6 -2
- package/authoring/doctypes/hook/hook.doc.mjs +4 -0
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +3 -2
- package/authoring/doctypes/legacy.d.mts +8 -6
- package/authoring/doctypes/legacy.mjs +5 -4
- package/authoring/doctypes/load-contract.test.mjs +207 -0
- package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +132 -0
- package/authoring/doctypes/namespace/parse.d.mts +12 -0
- package/authoring/doctypes/namespace/parse.mjs +25 -0
- package/authoring/doctypes/namespace/parse.test.mjs +165 -0
- package/authoring/doctypes/namespace/type.ts +71 -0
- package/authoring/doctypes/parse.d.mts +20 -18
- package/authoring/doctypes/parse.mjs +16 -10
- package/authoring/doctypes/parse.test.mjs +77 -3
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +8 -5
- package/authoring/doctypes/reference/reference.doc.mjs +17 -4
- package/authoring/doctypes/reference/type.ts +51 -5
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +3 -2
- package/authoring/doctypes/template/parse.d.mts +92 -1
- package/authoring/doctypes/template/parse.mjs +36 -2
- package/authoring/doctypes/template/parse.test.mjs +8 -2
- package/authoring/doctypes/template/template.doc.mjs +4 -0
- package/authoring/doctypes/template/type.ts +5 -2
- package/authoring/doctypes/types.ts +10 -9
- 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/identity/identity.doc.d.mts +9 -0
- package/authoring/identity/identity.doc.mjs +61 -0
- package/authoring/identity/type.ts +132 -0
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +49 -17
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +13 -6
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +10 -1
- package/authoring/integration/schema.d.mts +6 -4
- package/authoring/integration/schema.mjs +9 -3
- package/authoring/integration/type.ts +23 -6
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/commands/docs.doc.mjs +13 -3
- package/clients/cli/commands/docs.mjs +121 -21
- package/clients/cli/commands/docs.test.mjs +88 -0
- package/clients/cli/commands/integration-authoring.test.mjs +13 -9
- package/clients/cli/commands/theme-palette-generate.doc.mjs +8 -4
- package/clients/cli/commands/upgrade.doc.mjs +2 -2
- package/clients/cli/formatters/index.mjs +162 -1
- package/clients/cli/formatters/index.test.mjs +91 -0
- package/clients/cli/lib/manifest.mjs +7 -2
- package/foundation/config/project.mjs +21 -6
- package/foundation/discovery/authoring-self-docs.d.mts +69 -0
- package/foundation/discovery/authoring-self-docs.mjs +214 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +154 -0
- package/foundation/discovery/component-discovery.d.mts +1 -1
- package/foundation/discovery/component-discovery.mjs +2 -1
- package/foundation/discovery/docs-discovery.d.mts +11 -4
- package/foundation/discovery/docs-discovery.mjs +208 -88
- package/foundation/discovery/docs-discovery.test.mjs +279 -13
- package/foundation/discovery/docs-output-budget.d.mts +28 -0
- package/foundation/discovery/docs-output-budget.mjs +50 -0
- package/foundation/discovery/docs-section-key.d.mts +98 -0
- package/foundation/discovery/docs-section-key.mjs +221 -0
- package/foundation/discovery/docs-section-key.test.mjs +224 -0
- package/foundation/discovery/template-adapter.mjs +2 -1
- package/foundation/discovery/theming-targets.test.mjs +4 -0
- package/foundation/doc-compiler/compile.d.mts +162 -0
- package/foundation/doc-compiler/compile.mjs +262 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +687 -0
- package/foundation/doc-compiler/ir.d.mts +9 -0
- package/foundation/doc-compiler/ir.mjs +287 -0
- package/foundation/doc-compiler/lenses.d.mts +33 -0
- package/foundation/doc-compiler/lenses.mjs +127 -0
- package/foundation/identity/provider-identity.d.mts +90 -0
- package/foundation/identity/provider-identity.mjs +320 -0
- package/foundation/identity/provider-identity.test.mjs +254 -0
- package/foundation/identity/providers.d.mts +7 -0
- package/foundation/identity/providers.mjs +16 -0
- package/foundation/integrations/autolink.mjs +12 -5
- package/foundation/integrations/integration-warnings.mjs +6 -0
- package/foundation/integrations/integrations.d.mts +46 -2
- package/foundation/integrations/integrations.mjs +167 -8
- package/foundation/integrations/integrations.test.mjs +384 -1
- package/foundation/integrations/provider-conflicts.test.mjs +125 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +10 -0
- package/foundation/response/json-contract.test.mjs +46 -17
- package/foundation/response/response-types.doc.mjs +6 -1
- package/package.json +9 -11
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* which fields to show and in what order.
|
|
12
12
|
*
|
|
13
13
|
* Constraints (deliberately narrow):
|
|
14
|
-
* - Plain ASCII only. No color
|
|
14
|
+
* - Plain ASCII only. No color and no TTY detection. Long lines wrap at a
|
|
15
|
+
* fixed {@link WRAP_WIDTH}, never at the terminal's width, so output is
|
|
15
16
|
* byte-for-byte deterministic whether printed or piped to an agent.
|
|
16
17
|
* - Renderers return an opaque {@link Block}; `emit` accepts ONLY Blocks, so a
|
|
17
18
|
* stray string can't leak onto stdout (the compiler rejects `emit('x')`).
|
|
@@ -33,6 +34,50 @@ export const BULLET = '-';
|
|
|
33
34
|
export const ERR = '!!';
|
|
34
35
|
export const WARN = '!';
|
|
35
36
|
|
|
37
|
+
/** The column long human-output lines wrap at. */
|
|
38
|
+
export const WRAP_WIDTH = 120;
|
|
39
|
+
|
|
40
|
+
/** The widest first column an inline record pads to; a longer value overhangs. */
|
|
41
|
+
const INLINE_LEAD_MAX = 32;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Characters a terminal draws two columns wide: CJK ideographs and
|
|
45
|
+
* punctuation, kana, hangul, and fullwidth forms. A line may break between
|
|
46
|
+
* any two of them.
|
|
47
|
+
*/
|
|
48
|
+
const WIDE_CHAR =
|
|
49
|
+
/[\u1100-\u115f\u2e80-\u303e\u3041-\u33ff\u3400-\u4dbf\u4e00-\u9fff\ua000-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6]/u;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* How many terminal columns a string takes.
|
|
53
|
+
* @param {string} s
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
export function displayWidth(s) {
|
|
57
|
+
let width = 0;
|
|
58
|
+
for (const ch of String(s)) width += WIDE_CHAR.test(ch) ? 2 : 1;
|
|
59
|
+
return width;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Cut a line to `width` columns, ending it with `...` when anything was cut.
|
|
64
|
+
* @param {string} line
|
|
65
|
+
* @param {number} width
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function truncateToWidth(line, width) {
|
|
69
|
+
if (displayWidth(line) <= width) return line;
|
|
70
|
+
let out = '';
|
|
71
|
+
let used = 0;
|
|
72
|
+
for (const ch of line) {
|
|
73
|
+
const w = WIDE_CHAR.test(ch) ? 2 : 1;
|
|
74
|
+
if (used + w > width - 3) break;
|
|
75
|
+
out += ch;
|
|
76
|
+
used += w;
|
|
77
|
+
}
|
|
78
|
+
return `${out.trimEnd()}...`;
|
|
79
|
+
}
|
|
80
|
+
|
|
36
81
|
/**
|
|
37
82
|
* An opaque, renderer-produced block of output. Nominal via a private field:
|
|
38
83
|
* nothing outside this file can construct one, so `emit` can trust that whatever
|
|
@@ -67,6 +112,13 @@ export class Block {
|
|
|
67
112
|
* @property {Record<string, string>} [labels] - Rename a key for display.
|
|
68
113
|
* @property {Record<string, (value: any) => string>} [format] - Transform a
|
|
69
114
|
* value before rendering (e.g. prefix a command with the package manager).
|
|
115
|
+
* @property {'stacked' | 'inline'} [layout] - `stacked` (the default): one
|
|
116
|
+
* `key: value` line per field, a blank line between records. `inline`: one
|
|
117
|
+
* record per line, the first field in a padded column and the rest joined by
|
|
118
|
+
* ` - `, for a list a reader scans.
|
|
119
|
+
* @property {'wrap' | 'truncate'} [overflow] - What an inline record longer
|
|
120
|
+
* than {@link WRAP_WIDTH} does: `wrap` (the default) continues under the
|
|
121
|
+
* first column; `truncate` cuts it to one line.
|
|
70
122
|
*/
|
|
71
123
|
|
|
72
124
|
/** @param {unknown} v @returns {boolean} */
|
|
@@ -103,6 +155,72 @@ function toAscii(s) {
|
|
|
103
155
|
.replace(/\u00a0/g, ' ');
|
|
104
156
|
}
|
|
105
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Word-wrap text at `width`, keeping its own line breaks. A word longer than
|
|
160
|
+
* the width stays whole on a line of its own. Continuation lines start with
|
|
161
|
+
* `indent`.
|
|
162
|
+
* @param {string} input
|
|
163
|
+
* @param {{width?: number, indent?: string}} [options]
|
|
164
|
+
* @returns {string}
|
|
165
|
+
*/
|
|
166
|
+
export function wrapText(input, {width = WRAP_WIDTH, indent = ''} = {}) {
|
|
167
|
+
return String(input)
|
|
168
|
+
.split('\n')
|
|
169
|
+
.map(line => wrapLine(line, width, indent))
|
|
170
|
+
.join('\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} line
|
|
175
|
+
* @param {number} width
|
|
176
|
+
* @param {string} indent
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
function wrapLine(line, width, indent) {
|
|
180
|
+
if (displayWidth(line) <= width) return line;
|
|
181
|
+
const lead = /^\s*/.exec(line)?.[0] ?? '';
|
|
182
|
+
// Tokens a line may break between: words, and each wide character on its
|
|
183
|
+
// own, since CJK text has no spaces to break at. `gap` is what joined a
|
|
184
|
+
// token to the one before it.
|
|
185
|
+
/** @type {{text: string, gap: string}[]} */
|
|
186
|
+
const tokens = [];
|
|
187
|
+
let gap = '';
|
|
188
|
+
let word = '';
|
|
189
|
+
const flush = () => {
|
|
190
|
+
if (word === '') return;
|
|
191
|
+
tokens.push({text: word, gap});
|
|
192
|
+
gap = '';
|
|
193
|
+
word = '';
|
|
194
|
+
};
|
|
195
|
+
for (const ch of line.slice(lead.length)) {
|
|
196
|
+
if (ch === ' ') {
|
|
197
|
+
flush();
|
|
198
|
+
gap = ' ';
|
|
199
|
+
} else if (WIDE_CHAR.test(ch)) {
|
|
200
|
+
flush();
|
|
201
|
+
tokens.push({text: ch, gap});
|
|
202
|
+
gap = '';
|
|
203
|
+
} else {
|
|
204
|
+
word += ch;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
flush();
|
|
208
|
+
/** @type {string[]} */
|
|
209
|
+
const out = [];
|
|
210
|
+
let current = lead;
|
|
211
|
+
tokens.forEach(({text: token, gap: before}, i) => {
|
|
212
|
+
const joined = i === 0 ? current + token : current + before + token;
|
|
213
|
+
if (i > 0 && displayWidth(joined) > width && current.trim() !== '') {
|
|
214
|
+
out.push(current);
|
|
215
|
+
current = indent + token;
|
|
216
|
+
} else {
|
|
217
|
+
current = joined;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
out.push(current);
|
|
221
|
+
return out.join('\n');
|
|
222
|
+
}
|
|
223
|
+
|
|
106
224
|
/**
|
|
107
225
|
* A group label, optionally with an explanatory subtitle rendered on the line(s)
|
|
108
226
|
* directly beneath the heading (no blank line between). Whatever list/records
|
|
@@ -177,10 +295,53 @@ export function record(obj, options = {}) {
|
|
|
177
295
|
* @returns {Block}
|
|
178
296
|
*/
|
|
179
297
|
export function records(items, options = {}) {
|
|
298
|
+
if (options.layout === 'inline') return inlineRecords(items, options);
|
|
180
299
|
const blocks = items.map(o => record(o, options).toString()).filter(Boolean);
|
|
181
300
|
return new Block(blocks.join('\n\n'));
|
|
182
301
|
}
|
|
183
302
|
|
|
303
|
+
/**
|
|
304
|
+
* @param {any[]} items
|
|
305
|
+
* @param {RecordOptions} options
|
|
306
|
+
* @returns {Block}
|
|
307
|
+
*/
|
|
308
|
+
function inlineRecords(items, options) {
|
|
309
|
+
const [lead, ...rest] = (options.fields ?? Object.keys(items[0] ?? {})).filter(
|
|
310
|
+
k => !options.omit?.includes(k),
|
|
311
|
+
);
|
|
312
|
+
if (lead == null) return new Block('');
|
|
313
|
+
/** @param {any} o @param {string} k */
|
|
314
|
+
const value = (o, k) => {
|
|
315
|
+
const fmt = options.format?.[k];
|
|
316
|
+
return fmt ? fmt(o[k]) : renderValue(o[k]);
|
|
317
|
+
};
|
|
318
|
+
const leads = items.map(o => (isEmpty(o[lead]) ? '' : value(o, lead)));
|
|
319
|
+
const column = Math.min(
|
|
320
|
+
Math.max(0, ...leads.map(l => displayWidth(l))),
|
|
321
|
+
INLINE_LEAD_MAX,
|
|
322
|
+
);
|
|
323
|
+
const indent = ' '.repeat(column + 2);
|
|
324
|
+
const lines = items.map((o, i) => {
|
|
325
|
+
const tail = toAscii(
|
|
326
|
+
rest
|
|
327
|
+
.filter(k => !isEmpty(o[k]))
|
|
328
|
+
.map(k => value(o, k))
|
|
329
|
+
.join(' - '),
|
|
330
|
+
);
|
|
331
|
+
if (tail === '') return toAscii(leads[i]);
|
|
332
|
+
const head = toAscii(`${leads[i].padEnd(column)} `);
|
|
333
|
+
if (options.overflow === 'truncate') {
|
|
334
|
+
return truncateToWidth(head + tail, WRAP_WIDTH);
|
|
335
|
+
}
|
|
336
|
+
// Wrap only the tail, so the padded first column survives the wrap.
|
|
337
|
+
const [first, ...more] = wrapText(tail, {
|
|
338
|
+
width: Math.max(20, WRAP_WIDTH - displayWidth(head)),
|
|
339
|
+
}).split('\n');
|
|
340
|
+
return [head + first, ...more.map(line => indent + line)].join('\n');
|
|
341
|
+
});
|
|
342
|
+
return new Block(lines.join('\n'));
|
|
343
|
+
}
|
|
344
|
+
|
|
184
345
|
/**
|
|
185
346
|
* A verbatim block — source dumps, layout skeletons, or a markdown doc. Output
|
|
186
347
|
* is byte-for-byte (NOT ASCII-normalized), so it survives piping
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
list,
|
|
9
9
|
record,
|
|
10
10
|
records,
|
|
11
|
+
wrapText,
|
|
12
|
+
displayWidth,
|
|
13
|
+
WRAP_WIDTH,
|
|
11
14
|
code,
|
|
12
15
|
Block,
|
|
13
16
|
BULLET,
|
|
@@ -148,3 +151,91 @@ describe('emit', () => {
|
|
|
148
151
|
expect(spy).not.toHaveBeenCalled();
|
|
149
152
|
});
|
|
150
153
|
});
|
|
154
|
+
|
|
155
|
+
describe('wrapText', () => {
|
|
156
|
+
it('leaves short lines and its own line breaks alone', () => {
|
|
157
|
+
expect(wrapText('a b\nc')).toBe('a b\nc');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('wraps at the width with a hanging indent', () => {
|
|
161
|
+
expect(wrapText('aaa bbb ccc', {width: 7, indent: ' '})).toBe('aaa bbb\n ccc');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('keeps a word longer than the width whole', () => {
|
|
165
|
+
expect(wrapText('x '.concat('y'.repeat(20)), {width: 10})).toBe(
|
|
166
|
+
`x\n${'y'.repeat(20)}`,
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('records inline layout', () => {
|
|
172
|
+
const items = [
|
|
173
|
+
{id: 'a', title: 'Alpha', summary: 'First.'},
|
|
174
|
+
{id: 'bbb', title: 'Beta', summary: ''},
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
it('puts one record on each line under a padded first column', () => {
|
|
178
|
+
expect(
|
|
179
|
+
records(items, {fields: ['id', 'title', 'summary'], layout: 'inline'}).toString(),
|
|
180
|
+
).toBe('a Alpha - First.\nbbb Beta');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('wraps a long record under the first column', () => {
|
|
184
|
+
const out = records([{id: 'a', text: 'word '.repeat(40).trim()}], {
|
|
185
|
+
layout: 'inline',
|
|
186
|
+
}).toString();
|
|
187
|
+
expect(out.split('\n').length).toBeGreaterThan(1);
|
|
188
|
+
expect(out.split('\n').every(line => line.length <= WRAP_WIDTH)).toBe(true);
|
|
189
|
+
expect(out.split('\n')[1].startsWith(' word')).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('keeps the first column padded on a record that wraps', () => {
|
|
193
|
+
const out = records(
|
|
194
|
+
[
|
|
195
|
+
{id: 'a', text: 'word '.repeat(40).trim()},
|
|
196
|
+
{id: 'longer', text: 'x'},
|
|
197
|
+
],
|
|
198
|
+
{layout: 'inline'},
|
|
199
|
+
).toString();
|
|
200
|
+
const lines = out.split('\n');
|
|
201
|
+
expect(lines[0].startsWith(`a${' '.repeat(7)}word`)).toBe(true);
|
|
202
|
+
expect(lines[1].startsWith(' '.repeat(8) + 'word')).toBe(true);
|
|
203
|
+
expect(lines.at(-1)).toBe('longer x');
|
|
204
|
+
expect(lines.every(line => line.length <= WRAP_WIDTH)).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('cuts a long record to one line when asked', () => {
|
|
208
|
+
const out = records([{id: 'a', text: 'word '.repeat(40).trim()}], {
|
|
209
|
+
layout: 'inline',
|
|
210
|
+
overflow: 'truncate',
|
|
211
|
+
}).toString();
|
|
212
|
+
expect(out.includes('\n')).toBe(false);
|
|
213
|
+
expect(out.length).toBeLessThanOrEqual(WRAP_WIDTH);
|
|
214
|
+
expect(out.endsWith('...')).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('wide characters', () => {
|
|
219
|
+
it('counts CJK characters as two columns', () => {
|
|
220
|
+
expect(displayWidth('亮/暗模式')).toBe(9);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('wraps CJK text, which has no spaces, by columns', () => {
|
|
224
|
+
const out = wrapText('中'.repeat(100), {width: 20});
|
|
225
|
+
expect(out.split('\n').every(line => displayWidth(line) <= 20)).toBe(true);
|
|
226
|
+
expect(out.split('\n')).toHaveLength(10);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('keeps spaces between Latin words next to CJK', () => {
|
|
230
|
+
expect(wrapText('运行 astryx docs 查看', {width: 200})).toBe('运行 astryx docs 查看');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('cuts an inline record to columns, not characters', () => {
|
|
234
|
+
const out = records([{id: 'a', text: '中'.repeat(100)}], {
|
|
235
|
+
layout: 'inline',
|
|
236
|
+
overflow: 'truncate',
|
|
237
|
+
}).toString();
|
|
238
|
+
expect(displayWidth(out)).toBeLessThanOrEqual(WRAP_WIDTH);
|
|
239
|
+
expect(out.endsWith('...')).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -55,7 +55,7 @@ export const RESPONSE_TYPES = {
|
|
|
55
55
|
'component.detail.showcase',
|
|
56
56
|
'component.detail.blocks',
|
|
57
57
|
],
|
|
58
|
-
docs: ['docs.list', 'docs.detail', 'docs.detail.section'],
|
|
58
|
+
docs: ['docs.list', 'docs.index', 'docs.detail', 'docs.detail.section'],
|
|
59
59
|
blog: ['blog.list', 'blog.detail'],
|
|
60
60
|
discover: [
|
|
61
61
|
'discover.list',
|
|
@@ -105,7 +105,12 @@ const EXAMPLES = {
|
|
|
105
105
|
'astryx component XDSButton',
|
|
106
106
|
'astryx component XDSButton --props --json',
|
|
107
107
|
],
|
|
108
|
-
docs: [
|
|
108
|
+
docs: [
|
|
109
|
+
'astryx docs',
|
|
110
|
+
'astryx docs spacing --json',
|
|
111
|
+
'astryx docs theme --index',
|
|
112
|
+
'astryx docs theme quick-start',
|
|
113
|
+
],
|
|
109
114
|
discover: ['astryx discover --json'],
|
|
110
115
|
search: [
|
|
111
116
|
'astryx search modal --json',
|
|
@@ -38,6 +38,7 @@ import {parseConfig} from '../../authoring/config/parse.mjs';
|
|
|
38
38
|
import {
|
|
39
39
|
loadIntegrations,
|
|
40
40
|
loadLocalIntegration,
|
|
41
|
+
markProviderConflicts,
|
|
41
42
|
} from '../integrations/integrations.mjs';
|
|
42
43
|
import {autolinkIntegrations} from '../integrations/autolink.mjs';
|
|
43
44
|
import {
|
|
@@ -203,10 +204,11 @@ export class Project {
|
|
|
203
204
|
/** @type {ProjectIntegrationIssue[]} */
|
|
204
205
|
#issues = [];
|
|
205
206
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
207
|
+
* Loaded integrations whose issues have already been collected (via a
|
|
208
|
+
* discovery method or a direct issues() validation), so issues() can fill in
|
|
209
|
+
* only the ones not yet visited and never double-collect. Keyed by entry, not
|
|
210
|
+
* label: two entries can share a package name (an alias at another version).
|
|
211
|
+
* @type {Set<object>}
|
|
210
212
|
*/
|
|
211
213
|
#visitedIssues = new Set();
|
|
212
214
|
|
|
@@ -277,6 +279,8 @@ export class Project {
|
|
|
277
279
|
loadedIntegrations = await loadIntegrations(integrations, {
|
|
278
280
|
cwd: projectDir,
|
|
279
281
|
fresh,
|
|
282
|
+
// Provider identity is resolved once, below, over the final set.
|
|
283
|
+
resolveProviders: false,
|
|
280
284
|
});
|
|
281
285
|
}
|
|
282
286
|
|
|
@@ -310,6 +314,10 @@ export class Project {
|
|
|
310
314
|
else loadedIntegrations[existing] = localIntegration;
|
|
311
315
|
}
|
|
312
316
|
|
|
317
|
+
// Autolinked and local packages can claim a provider ID that a configured
|
|
318
|
+
// package already holds, so identity is resolved once over the final set.
|
|
319
|
+
loadedIntegrations = markProviderConflicts(loadedIntegrations);
|
|
320
|
+
|
|
313
321
|
// The debug recorder resolves its settings synchronously, long before any
|
|
314
322
|
// command gets here, so this is where a project's `debug` block gets a
|
|
315
323
|
// turn. The event is not written until process exit, so settings applied
|
|
@@ -436,6 +444,13 @@ export class Project {
|
|
|
436
444
|
* @returns {string}
|
|
437
445
|
*/
|
|
438
446
|
#pkgLabel(integration) {
|
|
447
|
+
// A set-aside package is labelled name@version, so its issue never reads as
|
|
448
|
+
// the winner's, even when both share a package name.
|
|
449
|
+
if (integration?.__providerConflict) {
|
|
450
|
+
return integration.version
|
|
451
|
+
? `${integration.name}@${integration.version}`
|
|
452
|
+
: (integration.__spec ?? integration.name);
|
|
453
|
+
}
|
|
439
454
|
return integration?.name ?? integration?.__spec ?? '(integration)';
|
|
440
455
|
}
|
|
441
456
|
|
|
@@ -463,8 +478,8 @@ export class Project {
|
|
|
463
478
|
*/
|
|
464
479
|
async #collectIssues(integration) {
|
|
465
480
|
const pkg = this.#pkgLabel(integration);
|
|
466
|
-
if (this.#visitedIssues.has(
|
|
467
|
-
this.#visitedIssues.add(
|
|
481
|
+
if (this.#visitedIssues.has(integration)) return;
|
|
482
|
+
this.#visitedIssues.add(integration);
|
|
468
483
|
// A manifest that failed to load (throwing import / invalid shape) is
|
|
469
484
|
// recorded as a marker by loadIntegrations — surface it as an issue and
|
|
470
485
|
// skip validation (there's no manifest to validate).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Every `*.doc.mjs` under `root`, relative and sorted.
|
|
6
|
+
* @param {string} [root]
|
|
7
|
+
* @returns {string[]}
|
|
8
|
+
*/
|
|
9
|
+
export function discoverAuthoringSelfDocSources(root?: string): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Import each self-doc. One that fails is reported, never thrown, so one bad
|
|
12
|
+
* file cannot take the rest of the topic down with it.
|
|
13
|
+
* @param {string[]} [sources]
|
|
14
|
+
* @param {string} [root]
|
|
15
|
+
* @returns {Promise<{loaded: {source: string, doc: any}[], failed: {source: string, error: string}[]}>}
|
|
16
|
+
*/
|
|
17
|
+
export function loadAuthoringSelfDocs(sources?: string[], root?: string): Promise<{
|
|
18
|
+
loaded: {
|
|
19
|
+
source: string;
|
|
20
|
+
doc: any;
|
|
21
|
+
}[];
|
|
22
|
+
failed: {
|
|
23
|
+
source: string;
|
|
24
|
+
error: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* The `authoring` topic: one section per self-doc, in the order given.
|
|
29
|
+
* @param {any[]} docs
|
|
30
|
+
* @returns {import('../../authoring/doctypes/reference/type').ReferenceDoc}
|
|
31
|
+
*/
|
|
32
|
+
export function buildAuthoringReferenceDoc(docs: any[]): import("../../authoring/doctypes/reference/type").ReferenceDoc;
|
|
33
|
+
/**
|
|
34
|
+
* The `authoring` topic from every self-doc that loads. One that fails is left
|
|
35
|
+
* out here and reported by {@link auditAuthoringSelfDocs}.
|
|
36
|
+
* @returns {Promise<import('../../authoring/doctypes/reference/type').ReferenceDoc>}
|
|
37
|
+
*/
|
|
38
|
+
export function buildAuthoringTopic(): Promise<import("../../authoring/doctypes/reference/type").ReferenceDoc>;
|
|
39
|
+
/**
|
|
40
|
+
* What stands between a self-doc and a reader of `astryx docs authoring`.
|
|
41
|
+
* @param {{root?: string, sources?: string[], budget?: number}} [options]
|
|
42
|
+
* @returns {Promise<{
|
|
43
|
+
* sections: number,
|
|
44
|
+
* unreachable: string[],
|
|
45
|
+
* failed: {source: string, error: string}[],
|
|
46
|
+
* oversized: {key: string, title: string, bytes: number}[],
|
|
47
|
+
* }>}
|
|
48
|
+
*/
|
|
49
|
+
export function auditAuthoringSelfDocs({ root, sources, budget, }?: {
|
|
50
|
+
root?: string;
|
|
51
|
+
sources?: string[];
|
|
52
|
+
budget?: number;
|
|
53
|
+
}): Promise<{
|
|
54
|
+
sections: number;
|
|
55
|
+
unreachable: string[];
|
|
56
|
+
failed: {
|
|
57
|
+
source: string;
|
|
58
|
+
error: string;
|
|
59
|
+
}[];
|
|
60
|
+
oversized: {
|
|
61
|
+
key: string;
|
|
62
|
+
title: string;
|
|
63
|
+
bytes: number;
|
|
64
|
+
}[];
|
|
65
|
+
}>;
|
|
66
|
+
/** The directory the self-docs live under. */
|
|
67
|
+
export const AUTHORING_ROOT: string;
|
|
68
|
+
/** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
|
|
69
|
+
export const AUTHORING_SELF_DOCS: string[];
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The authoring self-docs, and the `authoring` topic built from them.
|
|
5
|
+
*
|
|
6
|
+
* @input The SchemaDoc each authoring module colocates as `*.doc.mjs` under
|
|
7
|
+
* packages/cli/authoring.
|
|
8
|
+
* @output {@link AUTHORING_SELF_DOCS} (every self-doc, in reading order), the
|
|
9
|
+
* `authoring` reference topic with one section per self-doc, and an audit
|
|
10
|
+
* naming any self-doc the topic cannot reach, any that fails to load, and any
|
|
11
|
+
* section over the docs output budget.
|
|
12
|
+
* @position Read by assets/docs/authoring.doc.mjs (the topic) and by Doctor
|
|
13
|
+
* (the audit). Lives in foundation because authoring/ holds only contracts.
|
|
14
|
+
* A new `*.doc.mjs` under authoring/ must be added to the list, or Doctor and
|
|
15
|
+
* the self-doc tests fail.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as fs from 'node:fs';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import {pathToFileURL} from 'node:url';
|
|
21
|
+
import {CLI_ROOT} from '../fs/paths.mjs';
|
|
22
|
+
import {
|
|
23
|
+
DOC_OUTPUT_BUDGET_BYTES,
|
|
24
|
+
oversizedDocSections,
|
|
25
|
+
} from './docs-output-budget.mjs';
|
|
26
|
+
|
|
27
|
+
/** The directory the self-docs live under. */
|
|
28
|
+
export const AUTHORING_ROOT = path.join(CLI_ROOT, 'authoring');
|
|
29
|
+
|
|
30
|
+
/** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
|
|
31
|
+
export const AUTHORING_SELF_DOCS = [
|
|
32
|
+
'integration/integration.doc.mjs',
|
|
33
|
+
'config/config.doc.mjs',
|
|
34
|
+
'codemod/codemod.doc.mjs',
|
|
35
|
+
'identity/identity.doc.mjs',
|
|
36
|
+
'doctypes/base/graph-fields.doc.mjs',
|
|
37
|
+
'doctypes/component/component.doc.mjs',
|
|
38
|
+
'doctypes/hook/hook.doc.mjs',
|
|
39
|
+
'doctypes/function/function.doc.mjs',
|
|
40
|
+
'doctypes/command/command.doc.mjs',
|
|
41
|
+
'doctypes/enum/enum.doc.mjs',
|
|
42
|
+
'doctypes/namespace/namespace.doc.mjs',
|
|
43
|
+
'doctypes/reference/reference.doc.mjs',
|
|
44
|
+
'doctypes/schema/schema.doc.mjs',
|
|
45
|
+
'doctypes/template/template.doc.mjs',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** Blocks a self-doc note may carry that a topic section can render. */
|
|
49
|
+
const TOPIC_BLOCKS = new Set(['prose', 'list', 'code', 'heading', 'table']);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Every `*.doc.mjs` under `root`, relative and sorted.
|
|
53
|
+
* @param {string} [root]
|
|
54
|
+
* @returns {string[]}
|
|
55
|
+
*/
|
|
56
|
+
export function discoverAuthoringSelfDocSources(root = AUTHORING_ROOT) {
|
|
57
|
+
/** @type {string[]} */
|
|
58
|
+
const found = [];
|
|
59
|
+
/** @param {string} dir */
|
|
60
|
+
const walk = dir => {
|
|
61
|
+
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
62
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('__')) continue;
|
|
63
|
+
const full = path.join(dir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) walk(full);
|
|
65
|
+
else if (entry.name.endsWith('.doc.mjs')) {
|
|
66
|
+
found.push(path.relative(root, full).split(path.sep).join('/'));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
walk(root);
|
|
71
|
+
return found.sort();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Import each self-doc. One that fails is reported, never thrown, so one bad
|
|
76
|
+
* file cannot take the rest of the topic down with it.
|
|
77
|
+
* @param {string[]} [sources]
|
|
78
|
+
* @param {string} [root]
|
|
79
|
+
* @returns {Promise<{loaded: {source: string, doc: any}[], failed: {source: string, error: string}[]}>}
|
|
80
|
+
*/
|
|
81
|
+
export async function loadAuthoringSelfDocs(
|
|
82
|
+
sources = AUTHORING_SELF_DOCS,
|
|
83
|
+
root = AUTHORING_ROOT,
|
|
84
|
+
) {
|
|
85
|
+
const loaded = [];
|
|
86
|
+
const failed = [];
|
|
87
|
+
for (const source of sources) {
|
|
88
|
+
try {
|
|
89
|
+
const mod = await import(pathToFileURL(path.join(root, source)).href);
|
|
90
|
+
const doc = mod.doc ?? mod.docs ?? mod.default;
|
|
91
|
+
if (typeof doc?.name !== 'string' || typeof doc?.description !== 'string') {
|
|
92
|
+
throw new Error('exports no doc with a name and a description');
|
|
93
|
+
}
|
|
94
|
+
loaded.push({source, doc});
|
|
95
|
+
} catch (error) {
|
|
96
|
+
failed.push({
|
|
97
|
+
source,
|
|
98
|
+
error: error instanceof Error ? error.message : String(error),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {loaded, failed};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {any[]} fields
|
|
107
|
+
* @returns {string[][]}
|
|
108
|
+
*/
|
|
109
|
+
function fieldRows(fields) {
|
|
110
|
+
return fields.flatMap(field => [
|
|
111
|
+
[
|
|
112
|
+
String(field.name),
|
|
113
|
+
String(field.type ?? ''),
|
|
114
|
+
field.required ? 'yes' : 'no',
|
|
115
|
+
[
|
|
116
|
+
field.description,
|
|
117
|
+
field.default != null ? `Default: ${field.default}.` : null,
|
|
118
|
+
field.example != null ? `Example: ${field.example}.` : null,
|
|
119
|
+
]
|
|
120
|
+
.filter(Boolean)
|
|
121
|
+
.join(' '),
|
|
122
|
+
],
|
|
123
|
+
...fieldRows(field.fields ?? []),
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* One self-doc as a topic section, keyed by the doc's own name.
|
|
129
|
+
* @param {any} doc
|
|
130
|
+
* @returns {import('../../authoring/doctypes/reference/type').ReferenceSection}
|
|
131
|
+
*/
|
|
132
|
+
function selfDocSection(doc) {
|
|
133
|
+
/** @type {any[]} */
|
|
134
|
+
const content = [{type: 'prose', text: doc.description}];
|
|
135
|
+
if (doc.appliesTo) {
|
|
136
|
+
content.push({type: 'prose', text: `Applies to: ${doc.appliesTo}`});
|
|
137
|
+
}
|
|
138
|
+
const rows = fieldRows(doc.fields ?? []);
|
|
139
|
+
if (rows.length > 0) {
|
|
140
|
+
content.push({
|
|
141
|
+
type: 'table',
|
|
142
|
+
headers: ['Field', 'Type', 'Required', 'Description'],
|
|
143
|
+
rows,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
for (const example of doc.examples ?? []) {
|
|
147
|
+
if (typeof example?.code !== 'string' || example.code.trim() === '') continue;
|
|
148
|
+
content.push({
|
|
149
|
+
type: 'code',
|
|
150
|
+
lang: example.lang ?? 'js',
|
|
151
|
+
...(example.label ? {label: example.label} : {}),
|
|
152
|
+
code: example.code,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
for (const note of doc.notes ?? []) {
|
|
156
|
+
if (TOPIC_BLOCKS.has(note?.type)) content.push(note);
|
|
157
|
+
}
|
|
158
|
+
return {id: doc.name, title: doc.displayName ?? doc.name, content};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The `authoring` topic: one section per self-doc, in the order given.
|
|
163
|
+
* @param {any[]} docs
|
|
164
|
+
* @returns {import('../../authoring/doctypes/reference/type').ReferenceDoc}
|
|
165
|
+
*/
|
|
166
|
+
export function buildAuthoringReferenceDoc(docs) {
|
|
167
|
+
return /** @type {any} */ ({
|
|
168
|
+
name: 'authoring',
|
|
169
|
+
title: 'Authoring Reference',
|
|
170
|
+
category: 'guide',
|
|
171
|
+
description:
|
|
172
|
+
'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type.',
|
|
173
|
+
sections: docs.map(selfDocSection),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The `authoring` topic from every self-doc that loads. One that fails is left
|
|
179
|
+
* out here and reported by {@link auditAuthoringSelfDocs}.
|
|
180
|
+
* @returns {Promise<import('../../authoring/doctypes/reference/type').ReferenceDoc>}
|
|
181
|
+
*/
|
|
182
|
+
export async function buildAuthoringTopic() {
|
|
183
|
+
const {loaded} = await loadAuthoringSelfDocs();
|
|
184
|
+
return buildAuthoringReferenceDoc(loaded.map(entry => entry.doc));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* What stands between a self-doc and a reader of `astryx docs authoring`.
|
|
189
|
+
* @param {{root?: string, sources?: string[], budget?: number}} [options]
|
|
190
|
+
* @returns {Promise<{
|
|
191
|
+
* sections: number,
|
|
192
|
+
* unreachable: string[],
|
|
193
|
+
* failed: {source: string, error: string}[],
|
|
194
|
+
* oversized: {key: string, title: string, bytes: number}[],
|
|
195
|
+
* }>}
|
|
196
|
+
*/
|
|
197
|
+
export async function auditAuthoringSelfDocs({
|
|
198
|
+
root = AUTHORING_ROOT,
|
|
199
|
+
sources = AUTHORING_SELF_DOCS,
|
|
200
|
+
budget = DOC_OUTPUT_BUDGET_BYTES,
|
|
201
|
+
} = {}) {
|
|
202
|
+
const listed = new Set(sources);
|
|
203
|
+
const unreachable = discoverAuthoringSelfDocSources(root).filter(
|
|
204
|
+
source => !listed.has(source),
|
|
205
|
+
);
|
|
206
|
+
const {loaded, failed} = await loadAuthoringSelfDocs(sources, root);
|
|
207
|
+
const topic = buildAuthoringReferenceDoc(loaded.map(entry => entry.doc));
|
|
208
|
+
return {
|
|
209
|
+
sections: topic.sections.length,
|
|
210
|
+
unreachable,
|
|
211
|
+
failed,
|
|
212
|
+
oversized: oversizedDocSections(topic.sections, budget),
|
|
213
|
+
};
|
|
214
|
+
}
|