@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
|
@@ -1,687 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file Tests for the doc compiler: lowering, linking, the compiled-node
|
|
5
|
-
* contract, the lenses, and the rule that readers go through the compiler.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'node:fs';
|
|
9
|
-
import * as path from 'node:path';
|
|
10
|
-
import {fileURLToPath} from 'node:url';
|
|
11
|
-
import jscodeshift from 'jscodeshift';
|
|
12
|
-
import {describe, expect, it} from 'vitest';
|
|
13
|
-
import {parseDoc} from '../../authoring/doctypes/parse.mjs';
|
|
14
|
-
import {
|
|
15
|
-
COMPILED_DOC_SCHEMA_VERSION,
|
|
16
|
-
linkReferenceTopic,
|
|
17
|
-
lowerReferenceTopic,
|
|
18
|
-
} from './compile.mjs';
|
|
19
|
-
import {parseCompiledReferenceNode} from './ir.mjs';
|
|
20
|
-
import {detailView, indexView, readerSections, sectionView} from './lenses.mjs';
|
|
21
|
-
|
|
22
|
-
const CLI_ROOT = path.resolve(
|
|
23
|
-
path.dirname(fileURLToPath(import.meta.url)),
|
|
24
|
-
'../..',
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {string} name
|
|
29
|
-
* @param {any[]} sections
|
|
30
|
-
* @param {object} [fields]
|
|
31
|
-
*/
|
|
32
|
-
function authored(name, sections, fields = {}) {
|
|
33
|
-
return parseDoc(
|
|
34
|
-
{
|
|
35
|
-
name,
|
|
36
|
-
title: `${name} title`,
|
|
37
|
-
description: `About ${name}.`,
|
|
38
|
-
...fields,
|
|
39
|
-
sections,
|
|
40
|
-
},
|
|
41
|
-
`${name}.doc.mjs`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** @param {any} doc @param {object} [more] */
|
|
46
|
-
const file = (doc, more = {}) => ({file: `${doc.name}.doc.mjs`, doc, ...more});
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @param {any} base
|
|
50
|
-
* @param {any[]} [extensions]
|
|
51
|
-
* @param {string | null} [lang]
|
|
52
|
-
*/
|
|
53
|
-
const input = (base, extensions = [], lang = null) => ({
|
|
54
|
-
id: base.doc?.name ?? 'demo',
|
|
55
|
-
provider: '@acme/base',
|
|
56
|
-
replaces: null,
|
|
57
|
-
lang,
|
|
58
|
-
base,
|
|
59
|
-
extensions,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const prose = (/** @type {string} */ text) => ({type: 'prose', text});
|
|
63
|
-
|
|
64
|
-
const demo = () =>
|
|
65
|
-
authored('demo', [
|
|
66
|
-
{title: 'Quick Start', content: [prose('Start here.')]},
|
|
67
|
-
{title: 'Props', content: [prose('Every prop.')]},
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
/** @param {string} name @param {string} text */
|
|
71
|
-
const extension = (name, text) => ({
|
|
72
|
-
...file(
|
|
73
|
-
authored(name, [{title: 'Quick Start', content: [prose(text)]}], {
|
|
74
|
-
extends: 'demo',
|
|
75
|
-
}),
|
|
76
|
-
),
|
|
77
|
-
provider: `@acme/${name}`,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('lowerReferenceTopic', () => {
|
|
81
|
-
it('lays the overlay over authored titles and keys every section', () => {
|
|
82
|
-
const overlay = {
|
|
83
|
-
description: '演示。',
|
|
84
|
-
sections: [
|
|
85
|
-
{
|
|
86
|
-
section: 'Quick Start',
|
|
87
|
-
title: '快速开始',
|
|
88
|
-
content: [prose('从这里开始。')],
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
};
|
|
92
|
-
const node = lowerReferenceTopic(input(file(demo(), {overlay}), [], 'zh'));
|
|
93
|
-
expect(node.schemaVersion).toBe(COMPILED_DOC_SCHEMA_VERSION);
|
|
94
|
-
expect(node.stage).toBe('lowered');
|
|
95
|
-
expect(node.doc.description).toBe('演示。');
|
|
96
|
-
expect(
|
|
97
|
-
node.doc.sections.map((/** @type {any} */ s) => [s.id, s.title]),
|
|
98
|
-
).toEqual([
|
|
99
|
-
['quick-start', '快速开始'],
|
|
100
|
-
['props', 'Props'],
|
|
101
|
-
]);
|
|
102
|
-
expect(node.sourceTitles).toEqual({
|
|
103
|
-
'quick-start': 'Quick Start',
|
|
104
|
-
props: 'Props',
|
|
105
|
-
});
|
|
106
|
-
expect(parseCompiledReferenceNode(node)).toBe(node);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('merges extensions by key in order and names every provider', () => {
|
|
110
|
-
const acme = authored(
|
|
111
|
-
'demo-acme',
|
|
112
|
-
[
|
|
113
|
-
{title: 'Quick Start', content: [prose('Acme start.')]},
|
|
114
|
-
{title: 'Acme Palettes', content: [prose('Palettes.')]},
|
|
115
|
-
],
|
|
116
|
-
{extends: 'demo'},
|
|
117
|
-
);
|
|
118
|
-
const node = lowerReferenceTopic(
|
|
119
|
-
input(file(demo()), [{...file(acme), provider: '@acme/ext'}]),
|
|
120
|
-
);
|
|
121
|
-
expect(node.doc.sections.map((/** @type {any} */ s) => s.id)).toEqual([
|
|
122
|
-
'quick-start',
|
|
123
|
-
'props',
|
|
124
|
-
'acme-palettes',
|
|
125
|
-
]);
|
|
126
|
-
expect(node.doc.sections[0].content).toEqual([prose('Acme start.')]);
|
|
127
|
-
expect(node.provenance).toEqual({
|
|
128
|
-
provider: '@acme/base',
|
|
129
|
-
replaces: null,
|
|
130
|
-
extensions: ['@acme/ext'],
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('applies extensions in configuration order, so the last one wins', () => {
|
|
135
|
-
const node = lowerReferenceTopic(
|
|
136
|
-
input(file(demo()), [
|
|
137
|
-
extension('one', 'First.'),
|
|
138
|
-
extension('two', 'Second.'),
|
|
139
|
-
]),
|
|
140
|
-
);
|
|
141
|
-
expect(node.doc.sections[0].content).toEqual([prose('Second.')]);
|
|
142
|
-
expect(node.provenance.extensions).toEqual(['@acme/one', '@acme/two']);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('reports problems in the order a reader meets them', () => {
|
|
146
|
-
const broken = new Error('extension failed to load');
|
|
147
|
-
const invalidBase = file(
|
|
148
|
-
authored('demo', [{title: 'Only', content: [prose('x')]}]),
|
|
149
|
-
);
|
|
150
|
-
invalidBase.doc = {...invalidBase.doc, title: ''};
|
|
151
|
-
expect(() =>
|
|
152
|
-
lowerReferenceTopic(
|
|
153
|
-
input(invalidBase, [
|
|
154
|
-
{file: 'ext.doc.mjs', error: broken, provider: '@acme/ext'},
|
|
155
|
-
]),
|
|
156
|
-
),
|
|
157
|
-
).toThrow(/demo\.doc\.mjs is invalid/);
|
|
158
|
-
|
|
159
|
-
const overlayError = new Error('overlay failed to load');
|
|
160
|
-
expect(() =>
|
|
161
|
-
lowerReferenceTopic(input(file(demo(), {overlayError}), [], 'zh')),
|
|
162
|
-
).toThrow(overlayError);
|
|
163
|
-
|
|
164
|
-
expect(() =>
|
|
165
|
-
lowerReferenceTopic(
|
|
166
|
-
input(file(demo()), [
|
|
167
|
-
{file: 'ext.doc.mjs', error: broken, provider: '@acme/ext'},
|
|
168
|
-
]),
|
|
169
|
-
),
|
|
170
|
-
).toThrow(broken);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('carries authored values the way JSON does', () => {
|
|
174
|
-
const doc = {
|
|
175
|
-
...demo(),
|
|
176
|
-
when: new Date(0),
|
|
177
|
-
render: () => 1,
|
|
178
|
-
note: undefined,
|
|
179
|
-
};
|
|
180
|
-
doc.sections = [
|
|
181
|
-
{...doc.sections[0], previewType: undefined},
|
|
182
|
-
doc.sections[1],
|
|
183
|
-
];
|
|
184
|
-
const node = lowerReferenceTopic(input(file(doc)));
|
|
185
|
-
expect(node.doc.when).toBe('1970-01-01T00:00:00.000Z');
|
|
186
|
-
expect('render' in node.doc).toBe(false);
|
|
187
|
-
expect('note' in node.doc).toBe(false);
|
|
188
|
-
expect('previewType' in node.doc.sections[0]).toBe(false);
|
|
189
|
-
expect(parseCompiledReferenceNode(node)).toBe(node);
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('linking and lenses', () => {
|
|
194
|
-
const tokens = () =>
|
|
195
|
-
lowerReferenceTopic(
|
|
196
|
-
input(
|
|
197
|
-
file(
|
|
198
|
-
authored('tokens', [
|
|
199
|
-
{
|
|
200
|
-
title: 'Spacing',
|
|
201
|
-
previewType: 'spacing-bar',
|
|
202
|
-
content: [
|
|
203
|
-
{type: 'table', headers: ['Token'], rows: [['--space-1']]},
|
|
204
|
-
],
|
|
205
|
-
},
|
|
206
|
-
]),
|
|
207
|
-
{overlay: {sections: [{section: 'Spacing', title: '间距'}]}},
|
|
208
|
-
),
|
|
209
|
-
[],
|
|
210
|
-
'zh',
|
|
211
|
-
),
|
|
212
|
-
);
|
|
213
|
-
const guide = () =>
|
|
214
|
-
lowerReferenceTopic(
|
|
215
|
-
input(
|
|
216
|
-
file(
|
|
217
|
-
authored('guide', [
|
|
218
|
-
{
|
|
219
|
-
title: 'By Title',
|
|
220
|
-
content: [
|
|
221
|
-
{type: 'token-ref', topic: 'tokens', section: 'SPACING'},
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
title: 'By Key',
|
|
226
|
-
content: [
|
|
227
|
-
{type: 'token-ref', topic: 'tokens', section: 'spacing'},
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
title: 'No Topic',
|
|
232
|
-
content: [{type: 'token-ref', topic: 'nope', section: 'x'}],
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
title: 'No Section',
|
|
236
|
-
content: [{type: 'token-ref', topic: 'tokens', section: 'nope'}],
|
|
237
|
-
},
|
|
238
|
-
]),
|
|
239
|
-
),
|
|
240
|
-
[],
|
|
241
|
-
'zh',
|
|
242
|
-
),
|
|
243
|
-
);
|
|
244
|
-
const targets = async (/** @type {string} */ name) =>
|
|
245
|
-
name === 'tokens' ? tokens() : null;
|
|
246
|
-
|
|
247
|
-
it('resolves a reference by key or authored title in the reading language', async () => {
|
|
248
|
-
const linked = await linkReferenceTopic(guide(), targets);
|
|
249
|
-
expect(linked.stage).toBe('linked');
|
|
250
|
-
expect(parseCompiledReferenceNode(linked)).toBe(linked);
|
|
251
|
-
const [byTitle, byKey, noTopic, noSection] = detailView(linked).sections;
|
|
252
|
-
for (const section of [byTitle, byKey]) {
|
|
253
|
-
expect(section.content).toEqual([
|
|
254
|
-
{type: 'table', headers: ['Token'], rows: [['--space-1']]},
|
|
255
|
-
]);
|
|
256
|
-
expect(section.previewType).toBe('spacing-bar');
|
|
257
|
-
}
|
|
258
|
-
expect(noTopic.content).toEqual([
|
|
259
|
-
prose('[token-ref: unknown topic "nope"]'),
|
|
260
|
-
]);
|
|
261
|
-
expect(noSection.content).toEqual([
|
|
262
|
-
prose('[token-ref: section "nope" not found in "tokens"]'),
|
|
263
|
-
]);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('takes the preview type of the last reference that has one', async () => {
|
|
267
|
-
const palette = lowerReferenceTopic(
|
|
268
|
-
input(
|
|
269
|
-
file(
|
|
270
|
-
authored('palette', [
|
|
271
|
-
{
|
|
272
|
-
title: 'Spacing',
|
|
273
|
-
previewType: 'spacing-bar',
|
|
274
|
-
content: [{type: 'table', headers: ['S'], rows: [['1']]}],
|
|
275
|
-
},
|
|
276
|
-
{title: 'Plain', content: [prose('No preview.')]},
|
|
277
|
-
{
|
|
278
|
-
title: 'Radius',
|
|
279
|
-
previewType: 'radius-box',
|
|
280
|
-
content: [{type: 'table', headers: ['R'], rows: [['2']]}],
|
|
281
|
-
},
|
|
282
|
-
]),
|
|
283
|
-
),
|
|
284
|
-
),
|
|
285
|
-
);
|
|
286
|
-
const both = lowerReferenceTopic(
|
|
287
|
-
input(
|
|
288
|
-
file(
|
|
289
|
-
authored('guide', [
|
|
290
|
-
{
|
|
291
|
-
title: 'Both',
|
|
292
|
-
content: ['spacing', 'plain', 'radius'].map(section => ({
|
|
293
|
-
type: 'token-ref',
|
|
294
|
-
topic: 'palette',
|
|
295
|
-
section,
|
|
296
|
-
})),
|
|
297
|
-
},
|
|
298
|
-
]),
|
|
299
|
-
),
|
|
300
|
-
),
|
|
301
|
-
);
|
|
302
|
-
const [section] = detailView(
|
|
303
|
-
await linkReferenceTopic(both, async () => palette),
|
|
304
|
-
).sections;
|
|
305
|
-
expect(section.previewType).toBe('radius-box');
|
|
306
|
-
expect(Object.keys(section)).toEqual([
|
|
307
|
-
'title',
|
|
308
|
-
'content',
|
|
309
|
-
'id',
|
|
310
|
-
'previewType',
|
|
311
|
-
]);
|
|
312
|
-
expect(section.content.map((/** @type {any} */ b) => b.type)).toEqual([
|
|
313
|
-
'table',
|
|
314
|
-
'prose',
|
|
315
|
-
'table',
|
|
316
|
-
]);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it('gives every inlined copy of a referenced section its own blocks', async () => {
|
|
320
|
-
const shared = tokens();
|
|
321
|
-
const sameTarget = async (/** @type {string} */ name) =>
|
|
322
|
-
name === 'tokens' ? shared : null;
|
|
323
|
-
const twice = lowerReferenceTopic(
|
|
324
|
-
input(
|
|
325
|
-
file(
|
|
326
|
-
authored('guide', [
|
|
327
|
-
{
|
|
328
|
-
title: 'First',
|
|
329
|
-
content: [
|
|
330
|
-
{type: 'token-ref', topic: 'tokens', section: 'spacing'},
|
|
331
|
-
],
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
title: 'Second',
|
|
335
|
-
content: [
|
|
336
|
-
{type: 'token-ref', topic: 'tokens', section: 'spacing'},
|
|
337
|
-
],
|
|
338
|
-
},
|
|
339
|
-
]),
|
|
340
|
-
),
|
|
341
|
-
[],
|
|
342
|
-
'zh',
|
|
343
|
-
),
|
|
344
|
-
);
|
|
345
|
-
const [first, second] = detailView(
|
|
346
|
-
await linkReferenceTopic(twice, sameTarget),
|
|
347
|
-
).sections;
|
|
348
|
-
expect(first.content).toEqual(second.content);
|
|
349
|
-
expect(first.content[0]).not.toBe(second.content[0]);
|
|
350
|
-
expect(first.content[0]).not.toBe(shared.doc.sections[0].content[0]);
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
it('hands out views that share nothing with the node', async () => {
|
|
354
|
-
const lowered = lowerReferenceTopic(
|
|
355
|
-
input(file({...demo(), extra: {tags: ['a']}})),
|
|
356
|
-
);
|
|
357
|
-
// Authored sections hold only scalars besides content; a node read back
|
|
358
|
-
// from JSON may hold more, and the lens copies that too.
|
|
359
|
-
lowered.doc.sections[0] = {
|
|
360
|
-
...lowered.doc.sections[0],
|
|
361
|
-
meta: {level: 1},
|
|
362
|
-
content: [{type: 'table', headers: ['A'], rows: [['1']]}],
|
|
363
|
-
};
|
|
364
|
-
const node = await linkReferenceTopic(lowered, async () => null);
|
|
365
|
-
const view = detailView(node);
|
|
366
|
-
expect(JSON.stringify(view)).toBe(JSON.stringify(node.doc));
|
|
367
|
-
expect(view.extra).not.toBe(node.doc.extra);
|
|
368
|
-
expect(view.sections[0].meta).not.toBe(node.doc.sections[0].meta);
|
|
369
|
-
expect(view.sections[0].content[0]).not.toBe(
|
|
370
|
-
node.doc.sections[0].content[0],
|
|
371
|
-
);
|
|
372
|
-
expect(view.sections[0].content[0].rows).not.toBe(
|
|
373
|
-
node.doc.sections[0].content[0].rows,
|
|
374
|
-
);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('reads the index and section lookups without linking', () => {
|
|
378
|
-
const node = guide();
|
|
379
|
-
expect(indexView(node).sections.map(s => s.id)).toEqual([
|
|
380
|
-
'by-title',
|
|
381
|
-
'by-key',
|
|
382
|
-
'no-topic',
|
|
383
|
-
'no-section',
|
|
384
|
-
]);
|
|
385
|
-
expect(readerSections(node).map(s => s.title)).toEqual([
|
|
386
|
-
'By Title',
|
|
387
|
-
'By Key',
|
|
388
|
-
'No Topic',
|
|
389
|
-
'No Section',
|
|
390
|
-
]);
|
|
391
|
-
expect(() => sectionView(node, node.doc.sections[0])).toThrow(
|
|
392
|
-
/before it was linked/,
|
|
393
|
-
);
|
|
394
|
-
expect(() => detailView(node)).toThrow(/must be linked/);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe('parseCompiledReferenceNode', () => {
|
|
399
|
-
const node = () => lowerReferenceTopic(input(file(demo())));
|
|
400
|
-
const linked = async () =>
|
|
401
|
-
linkReferenceTopic(
|
|
402
|
-
lowerReferenceTopic(
|
|
403
|
-
input(
|
|
404
|
-
file(
|
|
405
|
-
authored('guide', [
|
|
406
|
-
{
|
|
407
|
-
title: 'Ref',
|
|
408
|
-
content: [{type: 'token-ref', topic: 'nope', section: 'x'}],
|
|
409
|
-
},
|
|
410
|
-
]),
|
|
411
|
-
),
|
|
412
|
-
),
|
|
413
|
-
),
|
|
414
|
-
async () => null,
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
it('returns a valid node as given', () => {
|
|
418
|
-
const value = JSON.parse(JSON.stringify(node()));
|
|
419
|
-
expect(parseCompiledReferenceNode(value)).toBe(value);
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
it('names an unsupported schema version', () => {
|
|
423
|
-
expect(() =>
|
|
424
|
-
parseCompiledReferenceNode({...node(), schemaVersion: 99}),
|
|
425
|
-
).toThrow(/schema version 99 is not supported/);
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('rejects a node that lost an authored title or duplicated a key', () => {
|
|
429
|
-
expect(() =>
|
|
430
|
-
parseCompiledReferenceNode({...node(), sourceTitles: {}}),
|
|
431
|
-
).toThrow(/no authored title for section "quick-start"/);
|
|
432
|
-
const twice = node();
|
|
433
|
-
twice.doc.sections[1] = {...twice.doc.sections[1], id: 'quick-start'};
|
|
434
|
-
expect(() => parseCompiledReferenceNode(twice)).toThrow(
|
|
435
|
-
/two sections have the key/,
|
|
436
|
-
);
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
it.each([
|
|
440
|
-
[
|
|
441
|
-
'a function in a block',
|
|
442
|
-
(/** @type {any} */ n) => {
|
|
443
|
-
n.doc.sections[0].content[0].text = () => 1;
|
|
444
|
-
},
|
|
445
|
-
/a function is not JSON/,
|
|
446
|
-
],
|
|
447
|
-
[
|
|
448
|
-
'a Date',
|
|
449
|
-
(/** @type {any} */ n) => {
|
|
450
|
-
n.doc.when = new Date(0);
|
|
451
|
-
},
|
|
452
|
-
/a Date is not JSON/,
|
|
453
|
-
],
|
|
454
|
-
[
|
|
455
|
-
'an undefined value',
|
|
456
|
-
(/** @type {any} */ n) => {
|
|
457
|
-
n.doc.note = undefined;
|
|
458
|
-
},
|
|
459
|
-
/undefined is not JSON/,
|
|
460
|
-
],
|
|
461
|
-
[
|
|
462
|
-
'a symbol key',
|
|
463
|
-
(/** @type {any} */ n) => {
|
|
464
|
-
n.doc[Symbol('x')] = 1;
|
|
465
|
-
},
|
|
466
|
-
/symbol keys/,
|
|
467
|
-
],
|
|
468
|
-
[
|
|
469
|
-
'a cycle',
|
|
470
|
-
(/** @type {any} */ n) => {
|
|
471
|
-
n.doc.self = n.doc;
|
|
472
|
-
},
|
|
473
|
-
/refers back to itself/,
|
|
474
|
-
],
|
|
475
|
-
[
|
|
476
|
-
'a block that is a number',
|
|
477
|
-
(/** @type {any} */ n) => {
|
|
478
|
-
n.doc.sections[0].content.push(42);
|
|
479
|
-
},
|
|
480
|
-
/expected a block with a type/,
|
|
481
|
-
],
|
|
482
|
-
[
|
|
483
|
-
'a null block',
|
|
484
|
-
(/** @type {any} */ n) => {
|
|
485
|
-
n.doc.sections[0].content.push(null);
|
|
486
|
-
},
|
|
487
|
-
/expected a block with a type/,
|
|
488
|
-
],
|
|
489
|
-
[
|
|
490
|
-
'a block with no type',
|
|
491
|
-
(/** @type {any} */ n) => {
|
|
492
|
-
n.doc.sections[0].content.push({text: 'x'});
|
|
493
|
-
},
|
|
494
|
-
/expected a block with a type/,
|
|
495
|
-
],
|
|
496
|
-
[
|
|
497
|
-
'a path in the provenance',
|
|
498
|
-
(/** @type {any} */ n) => {
|
|
499
|
-
n.provenance.provider = '/home/me/acme';
|
|
500
|
-
},
|
|
501
|
-
/naming packages, not paths/,
|
|
502
|
-
],
|
|
503
|
-
[
|
|
504
|
-
'a resolution on a lowered node',
|
|
505
|
-
(/** @type {any} */ n) => {
|
|
506
|
-
n.doc.sections[0].content.push({
|
|
507
|
-
type: 'token-ref',
|
|
508
|
-
topic: 't',
|
|
509
|
-
section: 's',
|
|
510
|
-
resolved: {status: 'unknown-topic'},
|
|
511
|
-
});
|
|
512
|
-
},
|
|
513
|
-
/a lowered node carries no resolution/,
|
|
514
|
-
],
|
|
515
|
-
])('rejects %s', (_, mutate, message) => {
|
|
516
|
-
const value = node();
|
|
517
|
-
mutate(value);
|
|
518
|
-
expect(() => parseCompiledReferenceNode(value)).toThrow(message);
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
it('rejects a linked node with an unresolved or malformed reference', async () => {
|
|
522
|
-
const unresolved = await linked();
|
|
523
|
-
delete unresolved.doc.sections[0].content[0].resolved;
|
|
524
|
-
expect(() => parseCompiledReferenceNode(unresolved)).toThrow(
|
|
525
|
-
/a linked node resolves every reference/,
|
|
526
|
-
);
|
|
527
|
-
const malformed = await linked();
|
|
528
|
-
malformed.doc.sections[0].content[0] = {
|
|
529
|
-
...malformed.doc.sections[0].content[0],
|
|
530
|
-
resolved: {status: 'maybe'},
|
|
531
|
-
};
|
|
532
|
-
expect(() => parseCompiledReferenceNode(malformed)).toThrow(
|
|
533
|
-
/token reference to "nope"/,
|
|
534
|
-
);
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
describe('readers go through the compiler', () => {
|
|
539
|
-
/** Compiler functions, by module. ESLint enforces the same list. */
|
|
540
|
-
const RESTRICTED = {
|
|
541
|
-
'foundation/discovery/docs-discovery.mjs': ['mergeTopic'],
|
|
542
|
-
'foundation/discovery/docs-section-key.mjs': ['withSectionKeys'],
|
|
543
|
-
'foundation/doc-compiler/compile.mjs': [
|
|
544
|
-
'lowerReferenceTopic',
|
|
545
|
-
'linkReferenceTopic',
|
|
546
|
-
'linkReferenceSection',
|
|
547
|
-
],
|
|
548
|
-
};
|
|
549
|
-
/** The docs adapter and leaves drive the compiler; nothing else does. */
|
|
550
|
-
const ALLOWED = {
|
|
551
|
-
'api/docs/_adapter.mjs': ['lowerReferenceTopic', 'linkReferenceTopic'],
|
|
552
|
-
'api/docs/detail/detail.mjs': ['linkReferenceTopic'],
|
|
553
|
-
'api/docs/detail/section/section.mjs': ['linkReferenceSection'],
|
|
554
|
-
};
|
|
555
|
-
const j = jscodeshift.withParser('babel');
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Every import of a compiler function that `rel` may not make: named under
|
|
559
|
-
* any alias, namespace, re-export, and dynamic import().
|
|
560
|
-
* @param {string} source
|
|
561
|
-
* @param {string} rel path under packages/cli
|
|
562
|
-
* @returns {string[]}
|
|
563
|
-
*/
|
|
564
|
-
function forbiddenImports(source, rel) {
|
|
565
|
-
/** @type {string[]} */
|
|
566
|
-
const allowed = ALLOWED[/** @type {keyof typeof ALLOWED} */ (rel)] ?? [];
|
|
567
|
-
/** @type {string[]} */
|
|
568
|
-
const found = [];
|
|
569
|
-
/** @param {unknown} specifier @returns {string[]} */
|
|
570
|
-
const bannedFrom = specifier => {
|
|
571
|
-
if (typeof specifier !== 'string' || !specifier.startsWith('.')) {
|
|
572
|
-
return [];
|
|
573
|
-
}
|
|
574
|
-
const target = path.posix.normalize(
|
|
575
|
-
path.posix.join(path.posix.dirname(rel), specifier),
|
|
576
|
-
);
|
|
577
|
-
const names =
|
|
578
|
-
RESTRICTED[/** @type {keyof typeof RESTRICTED} */ (target)] ?? [];
|
|
579
|
-
return names.filter(name => !allowed.includes(name));
|
|
580
|
-
};
|
|
581
|
-
/** @param {any} node */
|
|
582
|
-
const checkStatic = node => {
|
|
583
|
-
const banned = bannedFrom(node.source?.value);
|
|
584
|
-
if (banned.length === 0) return;
|
|
585
|
-
if (node.type === 'ExportAllDeclaration') {
|
|
586
|
-
found.push(`${rel}: export * from ${node.source.value}`);
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
for (const spec of node.specifiers ?? []) {
|
|
590
|
-
const name =
|
|
591
|
-
spec.type === 'ImportNamespaceSpecifier'
|
|
592
|
-
? '*'
|
|
593
|
-
: spec.type === 'ImportDefaultSpecifier'
|
|
594
|
-
? 'default'
|
|
595
|
-
: (spec.imported ?? spec.local)?.name;
|
|
596
|
-
if (name === '*' || name === 'default' || banned.includes(name)) {
|
|
597
|
-
found.push(`${rel}: ${name} from ${node.source.value}`);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
/** @param {any} node */
|
|
602
|
-
const checkDynamic = node => {
|
|
603
|
-
const specifier = node.source?.value ?? node.arguments?.[0]?.value;
|
|
604
|
-
if (bannedFrom(specifier).length > 0) {
|
|
605
|
-
found.push(`${rel}: import(${specifier})`);
|
|
606
|
-
}
|
|
607
|
-
};
|
|
608
|
-
const root = j(source);
|
|
609
|
-
root.find(j.ImportDeclaration).forEach(p => checkStatic(p.node));
|
|
610
|
-
root
|
|
611
|
-
.find(j.ExportNamedDeclaration)
|
|
612
|
-
.forEach(p => p.node.source && checkStatic(p.node));
|
|
613
|
-
root.find(j.ExportAllDeclaration).forEach(p => checkStatic(p.node));
|
|
614
|
-
root.find(j.ImportExpression).forEach(p => checkDynamic(p.node));
|
|
615
|
-
root
|
|
616
|
-
.find(j.CallExpression, {callee: {type: 'Import'}})
|
|
617
|
-
.forEach(p => checkDynamic(p.node));
|
|
618
|
-
return found;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
/** @param {string} dir @returns {string[]} */
|
|
622
|
-
const sources = dir =>
|
|
623
|
-
fs.readdirSync(dir, {withFileTypes: true}).flatMap(entry => {
|
|
624
|
-
const full = path.join(dir, entry.name);
|
|
625
|
-
if (entry.isDirectory()) {
|
|
626
|
-
return entry.name === 'node_modules' ? [] : sources(full);
|
|
627
|
-
}
|
|
628
|
-
return entry.name.endsWith('.mjs') && !entry.name.endsWith('.test.mjs')
|
|
629
|
-
? [full]
|
|
630
|
-
: [];
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
it('keeps compiler functions out of api/ and clients/', () => {
|
|
634
|
-
const offenders = ['api', 'clients']
|
|
635
|
-
.flatMap(dir => sources(path.join(CLI_ROOT, dir)))
|
|
636
|
-
.flatMap(full =>
|
|
637
|
-
forbiddenImports(
|
|
638
|
-
fs.readFileSync(full, 'utf8'),
|
|
639
|
-
path.relative(CLI_ROOT, full).split(path.sep).join('/'),
|
|
640
|
-
),
|
|
641
|
-
);
|
|
642
|
-
expect(offenders).toEqual([]);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
it('catches every way around the rule', () => {
|
|
646
|
-
const doctor = 'api/doctor/doctor.mjs';
|
|
647
|
-
const discovery = '../../foundation/discovery/docs-discovery.mjs';
|
|
648
|
-
expect(
|
|
649
|
-
forbiddenImports(
|
|
650
|
-
`import {mergeTopic as merge} from '${discovery}'; merge({}, {});`,
|
|
651
|
-
doctor,
|
|
652
|
-
),
|
|
653
|
-
).toHaveLength(1);
|
|
654
|
-
expect(
|
|
655
|
-
forbiddenImports(
|
|
656
|
-
`import * as dd from '${discovery}'; dd['mergeTopic']({}, {});`,
|
|
657
|
-
doctor,
|
|
658
|
-
),
|
|
659
|
-
).toHaveLength(1);
|
|
660
|
-
expect(
|
|
661
|
-
forbiddenImports(
|
|
662
|
-
`async function f() { const dd = await import('${discovery}'); dd.mergeTopic.call(null, {}, {}); }`,
|
|
663
|
-
doctor,
|
|
664
|
-
),
|
|
665
|
-
).toHaveLength(1);
|
|
666
|
-
expect(
|
|
667
|
-
forbiddenImports(
|
|
668
|
-
`export {withSectionKeys} from '../../foundation/discovery/docs-section-key.mjs';`,
|
|
669
|
-
doctor,
|
|
670
|
-
),
|
|
671
|
-
).toHaveLength(1);
|
|
672
|
-
expect(
|
|
673
|
-
forbiddenImports(`import {DocsCatalog} from '${discovery}';`, doctor),
|
|
674
|
-
).toEqual([]);
|
|
675
|
-
const leaf = 'api/docs/detail/detail.mjs';
|
|
676
|
-
const compiler = '../../../foundation/doc-compiler/compile.mjs';
|
|
677
|
-
expect(
|
|
678
|
-
forbiddenImports(`import {linkReferenceTopic} from '${compiler}';`, leaf),
|
|
679
|
-
).toEqual([]);
|
|
680
|
-
expect(
|
|
681
|
-
forbiddenImports(
|
|
682
|
-
`import {lowerReferenceTopic} from '${compiler}';`,
|
|
683
|
-
leaf,
|
|
684
|
-
),
|
|
685
|
-
).toHaveLength(1);
|
|
686
|
-
});
|
|
687
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
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
|
-
* Validate a compiled reference node.
|
|
6
|
-
* @param {unknown} value
|
|
7
|
-
* @returns {import('./compile.mjs').CompiledReferenceNode}
|
|
8
|
-
*/
|
|
9
|
-
export function parseCompiledReferenceNode(value: unknown): import("./compile.mjs").CompiledReferenceNode;
|