@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
|
@@ -3,29 +3,18 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file docs command — Print Astryx reference docs
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* reader can open one section by its key.
|
|
6
|
+
* Auto-discovers .doc.mjs files from the docs/ directory.
|
|
8
7
|
* Supports --detail (full|compact|brief) and --lang (en|zh|dense).
|
|
9
8
|
*
|
|
10
9
|
* Usage:
|
|
11
10
|
* astryx docs List available topics
|
|
12
|
-
* astryx docs <topic> Print
|
|
13
|
-
* astryx docs <topic> --index List the topic's sections
|
|
11
|
+
* astryx docs <topic> Print full doc
|
|
14
12
|
* astryx docs <topic> <section> Print one section
|
|
15
13
|
*/
|
|
16
14
|
|
|
17
15
|
import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
|
|
18
16
|
import {jsonOut} from '../../../foundation/response/json.mjs';
|
|
19
|
-
import {
|
|
20
|
-
emit,
|
|
21
|
-
section,
|
|
22
|
-
records,
|
|
23
|
-
text,
|
|
24
|
-
code,
|
|
25
|
-
wrapText,
|
|
26
|
-
displayWidth,
|
|
27
|
-
WRAP_WIDTH,
|
|
28
|
-
} from '../formatters/index.mjs';
|
|
17
|
+
import {emit, section, records, text, code} from '../formatters/index.mjs';
|
|
29
18
|
import {cliError} from '../lib/cli-error.mjs';
|
|
30
19
|
import {defineCommand} from '../lib/define-command.mjs';
|
|
31
20
|
import {resultSet} from '../../../foundation/debug/index.mjs';
|
|
@@ -52,28 +41,6 @@ function formatTable(headers, rows) {
|
|
|
52
41
|
return `${head}\n${sep}\n${body}`;
|
|
53
42
|
}
|
|
54
43
|
|
|
55
|
-
/**
|
|
56
|
-
* A table too wide for {@link WRAP_WIDTH}: one `header: cell` line per cell and
|
|
57
|
-
* a blank line between rows, so nothing runs off the side of a terminal.
|
|
58
|
-
* @param {string[]} headers
|
|
59
|
-
* @param {string[][]} rows
|
|
60
|
-
* @returns {string}
|
|
61
|
-
*/
|
|
62
|
-
function formatTableVertical(headers, rows) {
|
|
63
|
-
const width = Math.max(...headers.map(h => h.length)) + 2;
|
|
64
|
-
return rows
|
|
65
|
-
.map(row =>
|
|
66
|
-
headers
|
|
67
|
-
.map((h, i) =>
|
|
68
|
-
wrapText(`${`${h}:`.padEnd(width)}${row[i] ?? ''}`, {
|
|
69
|
-
indent: ' '.repeat(width),
|
|
70
|
-
}),
|
|
71
|
-
)
|
|
72
|
-
.join('\n'),
|
|
73
|
-
)
|
|
74
|
-
.join('\n\n');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
44
|
/**
|
|
78
45
|
* @param {string[]} headers
|
|
79
46
|
* @param {string[][]} rows
|
|
@@ -91,7 +58,7 @@ function formatTableCompact(headers, rows) {
|
|
|
91
58
|
function formatBlock(block, detail) {
|
|
92
59
|
switch (block.type) {
|
|
93
60
|
case 'prose':
|
|
94
|
-
return
|
|
61
|
+
return block.text;
|
|
95
62
|
|
|
96
63
|
case 'heading':
|
|
97
64
|
return `${'#'.repeat(block.level || 3)} ${block.text}`;
|
|
@@ -110,37 +77,16 @@ function formatBlock(block, detail) {
|
|
|
110
77
|
if (detail === 'compact') {
|
|
111
78
|
return formatTableCompact(block.headers, block.rows);
|
|
112
79
|
}
|
|
113
|
-
|
|
114
|
-
const table = formatTable(block.headers, block.rows);
|
|
115
|
-
return table.split('\n').some(line => displayWidth(line) > WRAP_WIDTH)
|
|
116
|
-
? formatTableVertical(block.headers, block.rows)
|
|
117
|
-
: table;
|
|
118
|
-
}
|
|
80
|
+
return formatTable(block.headers, block.rows);
|
|
119
81
|
|
|
120
82
|
case 'list': {
|
|
121
|
-
const prefix =
|
|
122
|
-
block.style === '
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
: block.style === 'do'
|
|
127
|
-
? () => '+ '
|
|
128
|
-
: () => '- ';
|
|
129
|
-
return block.items
|
|
130
|
-
.map((item, i) => {
|
|
131
|
-
const head = prefix(i);
|
|
132
|
-
return wrapText(`${head}${item}`, {indent: ' '.repeat(head.length)});
|
|
133
|
-
})
|
|
134
|
-
.join('\n');
|
|
83
|
+
const prefix = block.style === 'ordered' ? (/** @type {number} */ i) => `${i + 1}. `
|
|
84
|
+
: block.style === 'dont' ? () => 'x '
|
|
85
|
+
: block.style === 'do' ? () => '+ '
|
|
86
|
+
: () => '- ';
|
|
87
|
+
return block.items.map((item, i) => `${prefix(i)}${item}`).join('\n');
|
|
135
88
|
}
|
|
136
89
|
|
|
137
|
-
case 'workflow':
|
|
138
|
-
case 'collection':
|
|
139
|
-
case 'reference':
|
|
140
|
-
throw new Error(
|
|
141
|
-
`Documentation block "${block.type}" requires the compiled graph renderer.`,
|
|
142
|
-
);
|
|
143
|
-
|
|
144
90
|
default:
|
|
145
91
|
return null;
|
|
146
92
|
}
|
|
@@ -161,8 +107,7 @@ function formatSection(section, detail) {
|
|
|
161
107
|
return `${section.title}: ${first.split('\n')[0]}`;
|
|
162
108
|
}
|
|
163
109
|
|
|
164
|
-
const heading =
|
|
165
|
-
detail === 'compact' ? `[${section.title}]` : `## ${section.title}`;
|
|
110
|
+
const heading = detail === 'compact' ? `[${section.title}]` : `## ${section.title}`;
|
|
166
111
|
return `${heading}\n\n${blocks.join('\n\n')}`;
|
|
167
112
|
}
|
|
168
113
|
|
|
@@ -173,51 +118,25 @@ function formatSection(section, detail) {
|
|
|
173
118
|
*/
|
|
174
119
|
function formatReferenceFull(docs, detail) {
|
|
175
120
|
if (detail === 'brief') {
|
|
176
|
-
const header =
|
|
121
|
+
const header = `${docs.title}: ${docs.description}`;
|
|
177
122
|
const sections = docs.sections.map(s => formatSection(s, detail));
|
|
178
123
|
return `${header}\n${sections.join('\n')}`;
|
|
179
124
|
}
|
|
180
125
|
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
? `# ${docs.title}\n${description}`
|
|
185
|
-
: `# ${docs.title}\n\n${description}`;
|
|
126
|
+
const header = detail === 'compact'
|
|
127
|
+
? `# ${docs.title}\n${docs.description}`
|
|
128
|
+
: `# ${docs.title}\n\n${docs.description}`;
|
|
186
129
|
const sections = docs.sections.map(s => formatSection(s, detail));
|
|
187
130
|
const sep = detail === 'compact' ? '\n\n' : '\n\n';
|
|
188
131
|
return `${header}\n\n${sections.join(sep)}`;
|
|
189
132
|
}
|
|
190
133
|
|
|
191
|
-
/**
|
|
192
|
-
* A topic's section index: what the topic is, one line per section with the
|
|
193
|
-
* key to read it by, and how to read further.
|
|
194
|
-
* @param {import('../../../api/docs/docs.type.mjs').DocsIndex} index
|
|
195
|
-
* @param {string} run
|
|
196
|
-
*/
|
|
197
|
-
function emitIndex(index, run) {
|
|
198
|
-
emit(
|
|
199
|
-
section(index.title, index.description ? wrapText(index.description) : undefined),
|
|
200
|
-
records(index.sections, {
|
|
201
|
-
fields: ['id', 'title', 'summary'],
|
|
202
|
-
layout: 'inline',
|
|
203
|
-
overflow: 'truncate',
|
|
204
|
-
}),
|
|
205
|
-
text(
|
|
206
|
-
[
|
|
207
|
-
`Read one section: ${run} docs ${index.name} <section>`,
|
|
208
|
-
`Read everything: ${run} docs ${index.name}`,
|
|
209
|
-
].join('\n'),
|
|
210
|
-
),
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
134
|
/**
|
|
215
135
|
* What the run answered with. A named topic (or one of its sections) resolves
|
|
216
136
|
* or throws, so it is always a direct match of one doc; the bare form lists
|
|
217
137
|
* every topic there is.
|
|
218
138
|
*
|
|
219
139
|
* @param {import('../../../api/docs/docs.type.mjs').DocsListResponse
|
|
220
|
-
* | import('../../../api/docs/docs.type.mjs').DocsIndexResponse
|
|
221
140
|
* | import('../../../api/docs/docs.type.mjs').DocsDetailResponse
|
|
222
141
|
* | import('../../../api/docs/docs.type.mjs').DocsDetailSectionResponse} result
|
|
223
142
|
* @returns {import('../../../foundation/debug/command-result.mjs').CommandResult}
|
|
@@ -236,11 +155,7 @@ function summarize(result) {
|
|
|
236
155
|
export function registerDocs(program) {
|
|
237
156
|
defineCommand(program, docsCommand, {
|
|
238
157
|
fn: docsFn,
|
|
239
|
-
action: async (
|
|
240
|
-
/** @type {string | undefined} */ topic,
|
|
241
|
-
/** @type {string | undefined} */ sectionName,
|
|
242
|
-
/** @type {{index?: boolean}} */ options = {},
|
|
243
|
-
) => {
|
|
158
|
+
action: async (/** @type {string | undefined} */ topic, /** @type {string | undefined} */ sectionName) => {
|
|
244
159
|
const run = getCliInvocation();
|
|
245
160
|
const lang = program.opts().lang || null;
|
|
246
161
|
const zh = program.opts().zh || false;
|
|
@@ -250,17 +165,11 @@ export function registerDocs(program) {
|
|
|
250
165
|
|
|
251
166
|
let result;
|
|
252
167
|
try {
|
|
253
|
-
result = await docsApi(topic, sectionName, {
|
|
254
|
-
lang,
|
|
255
|
-
zh,
|
|
256
|
-
dense,
|
|
257
|
-
index: Boolean(options.index),
|
|
258
|
-
});
|
|
168
|
+
result = await docsApi(topic, sectionName, {lang, zh, dense});
|
|
259
169
|
} catch (e) {
|
|
260
170
|
// docs API throws structured errors with {name, reason} suggestions —
|
|
261
171
|
// pass them through untouched so the CLI envelope matches the API.
|
|
262
|
-
const err =
|
|
263
|
-
/** @type {import('../../../api/error.mjs').AstryxError} */ (e);
|
|
172
|
+
const err = /** @type {import('../../../api/error.mjs').AstryxError} */ (e);
|
|
264
173
|
return cliError(err.message, {
|
|
265
174
|
suggestions: err.suggestions || [],
|
|
266
175
|
code: err.code,
|
|
@@ -279,26 +188,17 @@ export function registerDocs(program) {
|
|
|
279
188
|
// description), then the usage footer as plain prose.
|
|
280
189
|
emit(
|
|
281
190
|
section('Available docs'),
|
|
282
|
-
records(result.data, {
|
|
283
|
-
fields: ['topic', 'description'],
|
|
284
|
-
layout: 'inline',
|
|
285
|
-
}),
|
|
191
|
+
records(result.data, {fields: ['topic', 'description']}),
|
|
286
192
|
text(
|
|
287
193
|
[
|
|
288
|
-
`Usage: ${run} docs <topic
|
|
289
|
-
` ${run} docs <topic>
|
|
290
|
-
` ${run} docs <topic> <section> read one section`,
|
|
194
|
+
`Usage: ${run} docs <topic>`,
|
|
195
|
+
` ${run} docs <topic> <section>`,
|
|
291
196
|
].join('\n'),
|
|
292
197
|
),
|
|
293
198
|
);
|
|
294
199
|
break;
|
|
295
200
|
}
|
|
296
201
|
|
|
297
|
-
case 'docs.index': {
|
|
298
|
-
emitIndex(result.data, run);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
202
|
case 'docs.detail': {
|
|
303
203
|
emit(code(formatReferenceFull(result.data, detail)));
|
|
304
204
|
break;
|
|
@@ -6,8 +6,6 @@ import * as path from 'node:path';
|
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import {Command} from 'commander';
|
|
8
8
|
import {registerDocs} from './docs.mjs';
|
|
9
|
-
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
10
|
-
import {displayWidth} from '../formatters/index.mjs';
|
|
11
9
|
|
|
12
10
|
let tmpDir;
|
|
13
11
|
|
|
@@ -102,89 +100,3 @@ describe('migration docs', () => {
|
|
|
102
100
|
expect(output).toContain('Map shadcn and Radix Primitives');
|
|
103
101
|
});
|
|
104
102
|
});
|
|
105
|
-
|
|
106
|
-
describe('progressive reads', () => {
|
|
107
|
-
const SLOW = 60_000;
|
|
108
|
-
/** @param {string} out */
|
|
109
|
-
const widest = out => Math.max(...out.split('\n').map(line => line.length));
|
|
110
|
-
|
|
111
|
-
it('lists every topic on one line each', async () => {
|
|
112
|
-
const {status, stdout} = await runCli(['docs']);
|
|
113
|
-
expect(status).toBe(0);
|
|
114
|
-
expect(stdout).toMatch(/^principles +\S/m);
|
|
115
|
-
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
116
|
-
}, SLOW);
|
|
117
|
-
|
|
118
|
-
it("prints a topic's section index with the keys to read by", async () => {
|
|
119
|
-
const {status, stdout} = await runCli(['docs', 'theme', '--index']);
|
|
120
|
-
expect(status).toBe(0);
|
|
121
|
-
expect(stdout).toMatch(/^quick-start +Quick Start/m);
|
|
122
|
-
expect(stdout).toContain('docs theme <section>');
|
|
123
|
-
expect(stdout).toMatch(/Read everything: +\S.* docs theme$/m);
|
|
124
|
-
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
125
|
-
}, SLOW);
|
|
126
|
-
|
|
127
|
-
it('prints one section by its key', async () => {
|
|
128
|
-
const {status, stdout} = await runCli(['docs', 'theme', 'quick-start']);
|
|
129
|
-
expect(status).toBe(0);
|
|
130
|
-
expect(stdout).toMatch(/^## Quick Start/m);
|
|
131
|
-
}, SLOW);
|
|
132
|
-
|
|
133
|
-
it('prints the whole topic by default, as before', async () => {
|
|
134
|
-
const index = await runCli(['docs', 'theme', '--index']);
|
|
135
|
-
const full = await runCli(['docs', 'theme']);
|
|
136
|
-
expect(full.status).toBe(0);
|
|
137
|
-
expect(full.stdout).toMatch(/^## Quick Start/m);
|
|
138
|
-
expect(full.stdout.length).toBeGreaterThan(index.stdout.length * 3);
|
|
139
|
-
expect((await runCli(['--detail', 'full', 'docs', 'theme'])).stdout).toBe(
|
|
140
|
-
full.stdout,
|
|
141
|
-
);
|
|
142
|
-
expect(widest(full.stdout.replace(/```[\s\S]*?```/g, ''))).toBeLessThanOrEqual(
|
|
143
|
-
120,
|
|
144
|
-
);
|
|
145
|
-
}, SLOW);
|
|
146
|
-
|
|
147
|
-
it('returns the matching envelopes as JSON', async () => {
|
|
148
|
-
const envelope = async args => JSON.parse((await runCli([...args, '--json'])).stdout);
|
|
149
|
-
expect((await envelope(['docs', 'theme'])).type).toBe('docs.detail');
|
|
150
|
-
expect((await envelope(['docs', 'theme', '--index'])).type).toBe(
|
|
151
|
-
'docs.index',
|
|
152
|
-
);
|
|
153
|
-
expect((await envelope(['docs', 'theme', 'quick-start'])).type).toBe(
|
|
154
|
-
'docs.detail.section',
|
|
155
|
-
);
|
|
156
|
-
}, SLOW);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('text width in every language', () => {
|
|
160
|
-
const SLOW = 60_000;
|
|
161
|
-
/** Widest line outside code blocks, in terminal columns. */
|
|
162
|
-
const widest = out => {
|
|
163
|
-
let inCode = false;
|
|
164
|
-
let max = 0;
|
|
165
|
-
for (const line of out.split('\n')) {
|
|
166
|
-
if (/^\s*```/.test(line)) {
|
|
167
|
-
inCode = !inCode;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
// A single unbreakable token (a long URL) cannot wrap without breaking it.
|
|
171
|
-
const oneToken = !/\s/.test(line.trim());
|
|
172
|
-
if (!inCode && !line.startsWith('#') && !oneToken) {
|
|
173
|
-
max = Math.max(max, displayWidth(line));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return max;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
it.each([
|
|
180
|
-
[['docs', 'theme', '--index', '--lang', 'zh']],
|
|
181
|
-
[['docs', 'theme', '--lang', 'zh']],
|
|
182
|
-
[['--detail', 'full', 'docs', 'theme', '--lang', 'zh']],
|
|
183
|
-
[['--detail', 'full', 'docs', 'internationalization']],
|
|
184
|
-
[['--detail', 'full', 'docs', 'styling']],
|
|
185
|
-
])('%j fits in 120 columns', async args => {
|
|
186
|
-
const {status, stdout} = await runCli(args);
|
|
187
|
-
expect(status).toBe(0);
|
|
188
|
-
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
189
|
-
}, SLOW);
|
|
190
|
-
});
|
|
@@ -120,7 +120,7 @@ describe('integration authoring CLI', () => {
|
|
|
120
120
|
);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
it('
|
|
123
|
+
it('packs the generated contribution and proves the consumer inventory', async () => {
|
|
124
124
|
const added = await runCli(
|
|
125
125
|
['integration', 'add', 'component', 'AcmeWidget', '--json'],
|
|
126
126
|
tmpDir,
|
|
@@ -131,23 +131,19 @@ describe('integration authoring CLI', () => {
|
|
|
131
131
|
['integration', 'pack', '--check', '--json'],
|
|
132
132
|
tmpDir,
|
|
133
133
|
);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
expect(checked.status).not.toBe(0);
|
|
137
|
-
const envelope = parseEnvelope(checked.stdout);
|
|
138
|
-
expect(envelope).toMatchObject({
|
|
134
|
+
expect(checked.status).toBe(0);
|
|
135
|
+
expect(parseEnvelope(checked.stdout)).toMatchObject({
|
|
139
136
|
type: 'integration.pack-check',
|
|
140
137
|
data: {
|
|
141
138
|
name: '@acme/widgets',
|
|
142
|
-
|
|
139
|
+
version: '1.0.0',
|
|
140
|
+
packable: true,
|
|
141
|
+
contributions: {
|
|
142
|
+
local: {components: ['AcmeWidget']},
|
|
143
|
+
packed: {components: ['AcmeWidget']},
|
|
144
|
+
},
|
|
143
145
|
},
|
|
144
146
|
});
|
|
145
|
-
expect(envelope.data.issues).toContainEqual(
|
|
146
|
-
expect.objectContaining({
|
|
147
|
-
severity: 'error',
|
|
148
|
-
message: expect.stringContaining('AcmeWidget'),
|
|
149
|
-
}),
|
|
150
|
-
);
|
|
151
147
|
});
|
|
152
148
|
|
|
153
149
|
it('requires the explicit --check gate on pack', async () => {
|
|
@@ -18,11 +18,7 @@ export const doc = {
|
|
|
18
18
|
'Without --out it prints a preview. With --out it writes a candidate file and detached ' +
|
|
19
19
|
'receipt. --preview writes a standardized, self-contained HTML review artifact. ' +
|
|
20
20
|
'TypeScript output is directly importable and contains no generator dependency. ' +
|
|
21
|
-
'JSON is also supported. Existing author-owned files are left untouched unless --overwrite is explicit.
|
|
22
|
-
'When used in a theme integration, keep the palette request under the theme slug, ' +
|
|
23
|
-
'write the candidate and receipt under that same slug, import the candidate from the theme source, ' +
|
|
24
|
-
"and list all three paths in the theme catalog entry's `files` array " +
|
|
25
|
-
'so `astryx theme add` copies them into the consumer project.',
|
|
21
|
+
'JSON is also supported. Existing author-owned files are left untouched unless --overwrite is explicit.',
|
|
26
22
|
fn: 'themePaletteGenerate',
|
|
27
23
|
args: [{name: 'config', param: 'configPath', required: true}],
|
|
28
24
|
options: [
|
|
@@ -46,15 +42,15 @@ export const doc = {
|
|
|
46
42
|
examples: [
|
|
47
43
|
{
|
|
48
44
|
label: 'Preview candidate JSON',
|
|
49
|
-
cli: 'astryx theme palette generate
|
|
45
|
+
cli: 'astryx theme palette generate palette.config.json',
|
|
50
46
|
},
|
|
51
47
|
{
|
|
52
48
|
label: 'Write candidate and receipt',
|
|
53
|
-
cli: 'astryx theme palette generate
|
|
49
|
+
cli: 'astryx theme palette generate palette.config.json --out ocean.palette.ts',
|
|
54
50
|
},
|
|
55
51
|
{
|
|
56
52
|
label: 'Write candidate, receipt, and review preview',
|
|
57
|
-
cli: 'astryx theme palette generate
|
|
53
|
+
cli: 'astryx theme palette generate palette.config.json --out ocean.palette.ts --preview ocean.palette.html',
|
|
58
54
|
},
|
|
59
55
|
],
|
|
60
56
|
exitCodes: [
|
|
@@ -53,10 +53,10 @@ export const doc = {
|
|
|
53
53
|
'Exclude named codemods (repeatable). Re-run past a failed codemod by skipping it.',
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
flag: '--integration <package>',
|
|
56
|
+
flag: '--integration <package-or-file>',
|
|
57
57
|
param: 'options.integration',
|
|
58
58
|
description:
|
|
59
|
-
'Explicit integration
|
|
59
|
+
'Explicit integration package name or integration file path (repeatable)',
|
|
60
60
|
default: [],
|
|
61
61
|
},
|
|
62
62
|
{
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
* which fields to show and in what order.
|
|
12
12
|
*
|
|
13
13
|
* Constraints (deliberately narrow):
|
|
14
|
-
* - Plain ASCII only. No color
|
|
15
|
-
* fixed {@link WRAP_WIDTH}, never at the terminal's width, so output is
|
|
14
|
+
* - Plain ASCII only. No color, no TTY detection, no width wrapping. Output is
|
|
16
15
|
* byte-for-byte deterministic whether printed or piped to an agent.
|
|
17
16
|
* - Renderers return an opaque {@link Block}; `emit` accepts ONLY Blocks, so a
|
|
18
17
|
* stray string can't leak onto stdout (the compiler rejects `emit('x')`).
|
|
@@ -34,50 +33,6 @@ export const BULLET = '-';
|
|
|
34
33
|
export const ERR = '!!';
|
|
35
34
|
export const WARN = '!';
|
|
36
35
|
|
|
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
|
-
|
|
81
36
|
/**
|
|
82
37
|
* An opaque, renderer-produced block of output. Nominal via a private field:
|
|
83
38
|
* nothing outside this file can construct one, so `emit` can trust that whatever
|
|
@@ -112,13 +67,6 @@ export class Block {
|
|
|
112
67
|
* @property {Record<string, string>} [labels] - Rename a key for display.
|
|
113
68
|
* @property {Record<string, (value: any) => string>} [format] - Transform a
|
|
114
69
|
* 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.
|
|
122
70
|
*/
|
|
123
71
|
|
|
124
72
|
/** @param {unknown} v @returns {boolean} */
|
|
@@ -155,72 +103,6 @@ function toAscii(s) {
|
|
|
155
103
|
.replace(/\u00a0/g, ' ');
|
|
156
104
|
}
|
|
157
105
|
|
|
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
|
-
|
|
224
106
|
/**
|
|
225
107
|
* A group label, optionally with an explanatory subtitle rendered on the line(s)
|
|
226
108
|
* directly beneath the heading (no blank line between). Whatever list/records
|
|
@@ -295,53 +177,10 @@ export function record(obj, options = {}) {
|
|
|
295
177
|
* @returns {Block}
|
|
296
178
|
*/
|
|
297
179
|
export function records(items, options = {}) {
|
|
298
|
-
if (options.layout === 'inline') return inlineRecords(items, options);
|
|
299
180
|
const blocks = items.map(o => record(o, options).toString()).filter(Boolean);
|
|
300
181
|
return new Block(blocks.join('\n\n'));
|
|
301
182
|
}
|
|
302
183
|
|
|
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
|
-
|
|
345
184
|
/**
|
|
346
185
|
* A verbatim block — source dumps, layout skeletons, or a markdown doc. Output
|
|
347
186
|
* is byte-for-byte (NOT ASCII-normalized), so it survives piping
|