@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.ddb63c3
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 +20 -1
- package/api/blog/blog.doc.mjs +1 -0
- package/api/build/build.doc.mjs +1 -0
- package/api/component/component.doc.mjs +1 -0
- package/api/discover/discover.doc.mjs +1 -0
- package/api/docs/_adapter.d.mts +3 -8
- package/api/docs/_adapter.mjs +13 -105
- package/api/docs/docs.doc.mjs +1 -0
- package/api/docs/list/list.mjs +3 -7
- package/api/doctor/doctor.d.mts +25 -3
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +185 -13
- package/api/doctor/doctor.test.mjs +326 -1
- package/api/gap-report/gap-report.doc.mjs +1 -0
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/hook/list/list.mjs +69 -17
- package/api/init/init.doc.mjs +1 -0
- package/api/integration/add-contribution.mjs +7 -7
- package/api/integration/add-contribution.test.mjs +37 -3
- package/api/integration/add-theme.mjs +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.test.mjs +8 -4
- package/api/integration/integration-block-exports.test.mjs +10 -6
- package/api/integration/integrationAdd.doc.mjs +1 -0
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +1 -0
- package/api/integration/integrationAddDoc.doc.mjs +1 -0
- package/api/integration/integrationAddTemplate.doc.mjs +1 -0
- package/api/integration/integrationAddTheme.doc.mjs +6 -5
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
- package/api/integration/integrationDocConflicts.doc.mjs +1 -0
- package/api/integration/integrationPackCheck.doc.mjs +1 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +1 -0
- package/api/integration/pack-check.test.mjs +17 -47
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +50 -102
- package/api/integration/validate-integration.test.mjs +85 -23
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +1 -0
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +1 -0
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -0
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search.doc.mjs +1 -0
- package/api/search/search.mjs +16 -6
- package/api/swizzle/swizzle.doc.mjs +1 -0
- package/api/template/template-suffix.test.mjs +41 -21
- package/api/template/template.doc.mjs +1 -0
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +10 -17
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/generateTonalPalette.doc.mjs +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +1 -0
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +12 -3
- package/api/upgrade/_adapter.mjs +81 -66
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +1 -0
- package/api/upgrade/upgrade.doc.mjs +1 -0
- package/assets/codemods/integration-discovery.mjs +8 -2
- package/assets/codemods/integration-discovery.test.mjs +15 -0
- package/assets/codemods/runner.mjs +115 -5
- package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +216 -0
- package/assets/codemods/transforms/next/index.mjs +11 -1
- package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/docs/cli-integrations.doc.mjs +17 -30
- package/assets/docs/cli.doc.mjs +15 -0
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
- package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
- package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
- package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
- package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
- package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
- package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
- package/authoring/codemod/codemod.doc.mjs +1 -1
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +1 -1
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/doctypes/component/type.ts +2 -2
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -1
- package/authoring/doctypes/function/function.doc.mjs +1 -1
- package/authoring/doctypes/function/type.ts +1 -1
- package/authoring/doctypes/hook/type.ts +2 -2
- package/authoring/doctypes/load-contract.test.mjs +2 -1
- package/authoring/doctypes/parse.d.mts +4 -2
- package/authoring/doctypes/parse.mjs +7 -3
- package/authoring/doctypes/reference/reference.doc.mjs +1 -1
- package/authoring/doctypes/reference/type.ts +2 -2
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -2
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/theme/parse.d.mts +35 -0
- package/authoring/doctypes/theme/parse.mjs +76 -0
- package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
- package/authoring/doctypes/theme/theme.doc.mjs +79 -0
- package/authoring/doctypes/theme/type.ts +42 -0
- package/authoring/doctypes/types.ts +2 -1
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +6 -4
- package/authoring/index.mjs +2 -1
- package/authoring/integration/integration.doc.mjs +2 -2
- package/authoring/integration/type.ts +3 -9
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/build.doc.mjs +1 -1
- package/clients/cli/commands/component.doc.mjs +1 -1
- package/clients/cli/commands/discover.doc.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.test.mjs +15 -7
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/gap-report.doc.mjs +1 -1
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/init.doc.mjs +1 -1
- package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
- package/clients/cli/commands/integration-add.doc.mjs +1 -1
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/layout-check.doc.mjs +1 -1
- package/clients/cli/commands/layout-expand.doc.mjs +1 -1
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +1 -1
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/theme-add.doc.mjs +2 -2
- package/clients/cli/commands/theme-build.doc.mjs +1 -1
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.doc.mjs +1 -1
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +1 -1
- package/clients/cli/lib/hook-format.mjs +14 -5
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +8 -0
- package/foundation/config/project.mjs +50 -21
- package/foundation/config/project.test.mjs +3 -16
- package/foundation/discovery/authoring-self-docs.d.mts +6 -0
- package/foundation/discovery/authoring-self-docs.mjs +17 -7
- package/foundation/discovery/authoring-surface.d.mts +74 -0
- package/foundation/discovery/authoring-surface.mjs +525 -0
- package/foundation/discovery/authoring-surface.test.mjs +392 -0
- package/foundation/discovery/cli-self-docs.d.mts +113 -0
- package/foundation/discovery/cli-self-docs.mjs +514 -0
- package/foundation/discovery/cli-self-docs.test.mjs +437 -0
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +3 -2
- package/foundation/discovery/docs-discovery.mjs +8 -12
- package/foundation/discovery/docs-discovery.test.mjs +8 -5
- package/foundation/discovery/template-adapter.d.mts +7 -0
- package/foundation/discovery/template-adapter.mjs +27 -40
- package/foundation/discovery/template-adapter.test.mjs +15 -0
- package/foundation/discovery/theme-discovery.d.mts +67 -7
- package/foundation/discovery/theme-discovery.mjs +916 -186
- package/foundation/discovery/theme-discovery.test.mjs +613 -219
- package/foundation/doc-compiler/bundle.d.mts +47 -0
- package/foundation/doc-compiler/bundle.mjs +218 -0
- package/foundation/doc-compiler/bundle.test.mjs +255 -0
- package/foundation/doc-compiler/compile.d.mts +200 -0
- package/foundation/doc-compiler/compile.mjs +254 -5
- package/foundation/doc-compiler/diagnostics.d.mts +126 -0
- package/foundation/doc-compiler/diagnostics.mjs +277 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +286 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +13 -0
- package/foundation/doc-compiler/ir.mjs +189 -12
- package/foundation/doc-compiler/lower-doc.test.mjs +492 -0
- package/foundation/doc-compiler/overlays.d.mts +37 -0
- package/foundation/doc-compiler/overlays.mjs +206 -0
- package/foundation/doc-compiler/parse-readable.d.mts +9 -0
- package/foundation/doc-compiler/parse-readable.mjs +29 -0
- package/foundation/doc-compiler/read.d.mts +126 -0
- package/foundation/doc-compiler/read.mjs +320 -0
- package/foundation/doc-compiler/read.test.mjs +313 -0
- package/foundation/doc-compiler/source.d.mts +33 -0
- package/foundation/doc-compiler/source.mjs +128 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -57
- package/foundation/integrations/contribution-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +1 -1
- package/foundation/integrations/contribution-inventory.mjs +17 -20
- package/foundation/integrations/contribution-inventory.test.mjs +67 -27
- package/foundation/integrations/integrations.d.mts +3 -0
- package/foundation/integrations/integrations.mjs +11 -97
- package/foundation/integrations/provider-ledger.test.mjs +275 -0
- package/foundation/integrations/provider-resolution.d.mts +152 -0
- package/foundation/integrations/provider-resolution.mjs +576 -0
- package/foundation/integrations/provider-resolution.test.mjs +369 -0
- package/foundation/integrations/theme-descriptor.d.mts +8 -0
- package/foundation/integrations/theme-descriptor.mjs +44 -0
- package/foundation/integrations/validate-contributions.mjs +104 -10
- package/foundation/response/error-codes.doc.mjs +2 -2
- package/foundation/response/error-codes.mjs +1 -1
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.mjs +22 -10
- package/package.json +9 -9
- package/assets/templates/themes/manifest.json +0 -95
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file lowerDoc, the compiled-node parsers, and diagnostics: every doc kind
|
|
5
|
+
* lowers into one sealed node shape, and every failure has one diagnostic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {describe, expect, it} from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
COMPILED_DOC_KINDS,
|
|
11
|
+
COMPILED_DOC_SCHEMA_VERSION,
|
|
12
|
+
lowerDoc,
|
|
13
|
+
} from './compile.mjs';
|
|
14
|
+
import {
|
|
15
|
+
DIAGNOSTIC_CODES,
|
|
16
|
+
diagnostic,
|
|
17
|
+
diagnosticProblem,
|
|
18
|
+
sortDiagnostics,
|
|
19
|
+
} from './diagnostics.mjs';
|
|
20
|
+
import {parseCompiledDocNode, parseCompiledDocsBundle} from './ir.mjs';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {any} doc
|
|
24
|
+
* @param {object} [more]
|
|
25
|
+
* @returns {any}
|
|
26
|
+
*/
|
|
27
|
+
const input = (doc, more = {}) => ({
|
|
28
|
+
id: '@acme/kit:components:Card',
|
|
29
|
+
root: 'components',
|
|
30
|
+
provider: '@acme/kit',
|
|
31
|
+
source: '@acme/kit/src/Card.doc.mjs',
|
|
32
|
+
lang: null,
|
|
33
|
+
file: {file: 'Card.doc.mjs', doc},
|
|
34
|
+
...more,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const card = {
|
|
38
|
+
type: 'component',
|
|
39
|
+
name: 'Card',
|
|
40
|
+
displayName: 'Card',
|
|
41
|
+
usage: {description: 'A card.'},
|
|
42
|
+
props: [{name: 'title', type: 'string', description: 'The title.'}],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe('lowerDoc', () => {
|
|
46
|
+
it('lowers each root into its kind, with package provenance and JSON doc', () => {
|
|
47
|
+
const cases = [
|
|
48
|
+
['components', card, 'component'],
|
|
49
|
+
[
|
|
50
|
+
'components',
|
|
51
|
+
{name: 'Old', description: 'Legacy.', props: []},
|
|
52
|
+
'component',
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
'hooks',
|
|
56
|
+
{
|
|
57
|
+
type: 'function',
|
|
58
|
+
kind: 'hook',
|
|
59
|
+
name: 'useThing',
|
|
60
|
+
displayName: 'useThing',
|
|
61
|
+
description: 'Does a thing.',
|
|
62
|
+
params: [],
|
|
63
|
+
returns: [],
|
|
64
|
+
},
|
|
65
|
+
'function',
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'templates',
|
|
69
|
+
{type: 'page', name: 'Home', description: 'A page.'},
|
|
70
|
+
'page',
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
'templates',
|
|
74
|
+
{type: 'block', name: 'Hero', description: 'A block.'},
|
|
75
|
+
'block',
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'themes',
|
|
79
|
+
{
|
|
80
|
+
type: 'theme',
|
|
81
|
+
name: 'ocean',
|
|
82
|
+
displayName: 'Ocean',
|
|
83
|
+
description: 'Blue.',
|
|
84
|
+
maintained: true,
|
|
85
|
+
},
|
|
86
|
+
'theme',
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
'self-docs',
|
|
90
|
+
{type: 'enum', name: 'codes', description: 'Codes.', values: []},
|
|
91
|
+
'enum',
|
|
92
|
+
],
|
|
93
|
+
];
|
|
94
|
+
for (const [root, doc, kind] of cases) {
|
|
95
|
+
const {node, diagnostics} = lowerDoc(input(doc, {root}));
|
|
96
|
+
expect({root, kind: node?.kind}).toEqual({root, kind});
|
|
97
|
+
expect(node?.provenance).toEqual({
|
|
98
|
+
provider: '@acme/kit',
|
|
99
|
+
source: '@acme/kit/src/Card.doc.mjs',
|
|
100
|
+
});
|
|
101
|
+
expect(node?.schemaVersion).toBe(COMPILED_DOC_SCHEMA_VERSION);
|
|
102
|
+
expect(() => parseCompiledDocNode(node)).not.toThrow();
|
|
103
|
+
// Legacy and stamped docs parse cleanly; nothing to report.
|
|
104
|
+
expect(diagnostics.filter(d => d.code !== 'invalid_doc')).toEqual([]);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('carries the authored doc as JSON: key order kept, undefined dropped', () => {
|
|
109
|
+
const {node} = lowerDoc(
|
|
110
|
+
input({
|
|
111
|
+
...card,
|
|
112
|
+
playground: {defaults: {onOpenChange: undefined, size: 'md'}},
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
expect(Object.keys(node?.doc)).toEqual([
|
|
116
|
+
'type',
|
|
117
|
+
'name',
|
|
118
|
+
'displayName',
|
|
119
|
+
'usage',
|
|
120
|
+
'props',
|
|
121
|
+
'playground',
|
|
122
|
+
]);
|
|
123
|
+
expect(node?.doc.playground).toEqual({defaults: {size: 'md'}});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('lays the translation for the reading language over the doc', () => {
|
|
127
|
+
const {node} = lowerDoc(
|
|
128
|
+
input(card, {
|
|
129
|
+
lang: 'zh',
|
|
130
|
+
file: {
|
|
131
|
+
file: 'Card.doc.mjs',
|
|
132
|
+
doc: card,
|
|
133
|
+
overlay: {
|
|
134
|
+
usage: {description: '卡片。'},
|
|
135
|
+
propDescriptions: {title: '标题。'},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
expect(node?.lang).toBe('zh');
|
|
141
|
+
expect(node?.doc.usage.description).toBe('卡片。');
|
|
142
|
+
expect(node?.doc.props[0]).toEqual({
|
|
143
|
+
name: 'title',
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: '标题。',
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('keeps a doc that fails its parser, and reports why', () => {
|
|
150
|
+
const {node, diagnostics, failure} = lowerDoc(
|
|
151
|
+
input({type: 'component', name: 'Card'}),
|
|
152
|
+
);
|
|
153
|
+
expect(node?.doc).toEqual({type: 'component', name: 'Card'});
|
|
154
|
+
expect(diagnostics.map(d => d.code)).toEqual(['invalid_doc']);
|
|
155
|
+
expect(failure).toBeInstanceOf(Error);
|
|
156
|
+
expect(diagnostics[0].message).toBe(/** @type {Error} */ (failure).message);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("keeps a doc of another kind, in its root's kind, and names the mismatch", () => {
|
|
160
|
+
const {node, diagnostics} = lowerDoc(
|
|
161
|
+
input({type: 'page', name: 'Home', description: 'A page.'}),
|
|
162
|
+
);
|
|
163
|
+
expect(node?.kind).toBe('component');
|
|
164
|
+
expect(diagnostics.map(d => d.code)).toEqual(['wrong_kind']);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('yields no node, and records what failed, when the file cannot be read', () => {
|
|
168
|
+
const thrown = new SyntaxError('Unexpected token');
|
|
169
|
+
const loaded = lowerDoc(
|
|
170
|
+
input(card, {file: {file: 'Card.doc.mjs', error: thrown}}),
|
|
171
|
+
);
|
|
172
|
+
expect(loaded.node).toBeNull();
|
|
173
|
+
expect(loaded.loadFailure).toBe(thrown);
|
|
174
|
+
expect(loaded.diagnostics.map(d => d.code)).toEqual(['load_failed']);
|
|
175
|
+
const empty = lowerDoc(
|
|
176
|
+
input(card, {file: {file: 'Card.doc.mjs', doc: null}}),
|
|
177
|
+
);
|
|
178
|
+
expect(empty).toMatchObject({
|
|
179
|
+
node: null,
|
|
180
|
+
missing: true,
|
|
181
|
+
missingValue: null,
|
|
182
|
+
});
|
|
183
|
+
expect(empty.diagnostics.map(d => d.code)).toEqual(['missing_export']);
|
|
184
|
+
const overlaid = lowerDoc(
|
|
185
|
+
input(card, {
|
|
186
|
+
file: {file: 'Card.doc.mjs', doc: card, overlayError: thrown},
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
expect(overlaid).toMatchObject({node: null, overlayFailure: thrown});
|
|
190
|
+
expect(overlaid.view).toBeUndefined();
|
|
191
|
+
expect(overlaid.diagnostics.map(d => d.code)).toEqual(['overlay_failed']);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('keeps the parse failure first when the translation also breaks', () => {
|
|
195
|
+
const {failure, overlayFailure, diagnostics} = lowerDoc(
|
|
196
|
+
input(
|
|
197
|
+
{type: 'component', name: 'Card'},
|
|
198
|
+
{
|
|
199
|
+
lang: 'zh',
|
|
200
|
+
file: {
|
|
201
|
+
file: 'Card.doc.mjs',
|
|
202
|
+
doc: {type: 'component', name: 'Card'},
|
|
203
|
+
overlay: {
|
|
204
|
+
get props() {
|
|
205
|
+
throw new TypeError('bad translation');
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
),
|
|
211
|
+
);
|
|
212
|
+
expect(/** @type {Error} */ (failure).message).toMatch(
|
|
213
|
+
/Card\.doc\.mjs is invalid/,
|
|
214
|
+
);
|
|
215
|
+
expect(overlayFailure).toBeInstanceOf(TypeError);
|
|
216
|
+
expect(diagnostics.map(d => d.code)).toEqual([
|
|
217
|
+
'invalid_doc',
|
|
218
|
+
'overlay_failed',
|
|
219
|
+
]);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('refuses a theme descriptor outside the themes root, as readers always did', () => {
|
|
223
|
+
const theme = {
|
|
224
|
+
type: 'theme',
|
|
225
|
+
name: 'ocean',
|
|
226
|
+
displayName: 'Ocean',
|
|
227
|
+
description: 'd',
|
|
228
|
+
maintained: true,
|
|
229
|
+
};
|
|
230
|
+
for (const root of ['components', 'hooks', 'self-docs']) {
|
|
231
|
+
const {failure} = lowerDoc(
|
|
232
|
+
input(theme, {root, label: '/x/Ocean.doc.mjs'}),
|
|
233
|
+
);
|
|
234
|
+
expect(/** @type {Error} */ (failure).message).toBe(
|
|
235
|
+
'/x/Ocean.doc.mjs has unsupported type "theme".',
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('hands readers the authored value, and only the node goes through JSON', () => {
|
|
241
|
+
const cyclic = /** @type {any} */ ({
|
|
242
|
+
...card,
|
|
243
|
+
big: 10n,
|
|
244
|
+
when: new Date(0),
|
|
245
|
+
fn() {},
|
|
246
|
+
});
|
|
247
|
+
cyclic.self = cyclic;
|
|
248
|
+
cyclic.gone = undefined;
|
|
249
|
+
const {view, node, diagnostics} = lowerDoc(input(cyclic));
|
|
250
|
+
expect(view).toBe(cyclic);
|
|
251
|
+
expect(node).toBeNull();
|
|
252
|
+
expect(diagnostics.at(-1)?.code).toBe('not_json');
|
|
253
|
+
expect(diagnostics.at(-1)?.message).not.toMatch(/^\//);
|
|
254
|
+
const read = lowerDoc(input(cyclic), {check: false, node: false});
|
|
255
|
+
expect(read).toEqual({node: null, diagnostics: [], view: cyclic});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('never puts a machine path in a diagnostic', () => {
|
|
259
|
+
const {diagnostics} = lowerDoc(
|
|
260
|
+
input(
|
|
261
|
+
{type: 'component', name: 'Card'},
|
|
262
|
+
{label: '/home/someone/kit/Card.doc.mjs'},
|
|
263
|
+
),
|
|
264
|
+
);
|
|
265
|
+
expect(diagnostics[0].message).not.toContain('/home/someone');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('refuses a value JSON cannot hold', () => {
|
|
269
|
+
const cyclic = /** @type {any} */ ({...card});
|
|
270
|
+
cyclic.self = cyclic;
|
|
271
|
+
for (const doc of [cyclic, {...card, big: 1n}]) {
|
|
272
|
+
const {node, diagnostics} = lowerDoc(input(doc));
|
|
273
|
+
expect(node).toBeNull();
|
|
274
|
+
expect(diagnostics.at(-1)?.code).toBe('not_json');
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("carries the parser's result only when asked to", () => {
|
|
279
|
+
const block = {
|
|
280
|
+
type: 'block',
|
|
281
|
+
name: 'Hero',
|
|
282
|
+
description: 'A block.',
|
|
283
|
+
extra: 1,
|
|
284
|
+
};
|
|
285
|
+
const authored = lowerDoc(input(block, {root: 'templates'}));
|
|
286
|
+
const parsed = lowerDoc(input(block, {root: 'templates', useParsed: true}));
|
|
287
|
+
expect(authored.node?.doc.extra).toBe(1);
|
|
288
|
+
expect(parsed.node?.doc).toEqual(
|
|
289
|
+
expect.objectContaining({type: 'block', name: 'Hero'}),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('diagnostics', () => {
|
|
295
|
+
it('fixes phase, severity, scope, and remediation per code', () => {
|
|
296
|
+
for (const [code, rule] of Object.entries(DIAGNOSTIC_CODES)) {
|
|
297
|
+
const d = diagnostic(code, {message: 'x'});
|
|
298
|
+
expect(d).toEqual({
|
|
299
|
+
code,
|
|
300
|
+
phase: rule.phase,
|
|
301
|
+
severity: rule.severity,
|
|
302
|
+
scope: rule.scope,
|
|
303
|
+
provider: null,
|
|
304
|
+
source: null,
|
|
305
|
+
field: null,
|
|
306
|
+
message: 'x',
|
|
307
|
+
remediation: rule.remediation,
|
|
308
|
+
});
|
|
309
|
+
expect(diagnosticProblem(d)).toBeNull();
|
|
310
|
+
}
|
|
311
|
+
expect(() => diagnostic('nope', {message: 'x'})).toThrow(/Unknown/);
|
|
312
|
+
for (const name of [
|
|
313
|
+
'toString',
|
|
314
|
+
'constructor',
|
|
315
|
+
'__proto__',
|
|
316
|
+
'hasOwnProperty',
|
|
317
|
+
]) {
|
|
318
|
+
expect(() => diagnostic(name, {message: 'x'})).toThrow(/Unknown/);
|
|
319
|
+
expect(
|
|
320
|
+
diagnosticProblem({
|
|
321
|
+
...diagnostic('invalid_doc', {message: 'x'}),
|
|
322
|
+
code: name,
|
|
323
|
+
}),
|
|
324
|
+
).toMatch(/not a compiler diagnostic code/);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('rejects a diagnostic a reader reinterpreted', () => {
|
|
329
|
+
const d = diagnostic('invalid_doc', {message: 'x'});
|
|
330
|
+
expect(diagnosticProblem({...d, severity: 'warning'})).toMatch(
|
|
331
|
+
/always has severity/,
|
|
332
|
+
);
|
|
333
|
+
expect(diagnosticProblem({...d, code: 'made_up'})).toMatch(
|
|
334
|
+
/not a compiler diagnostic code/,
|
|
335
|
+
);
|
|
336
|
+
expect(diagnosticProblem({...d, extra: 1})).toMatch(/unknown fields/);
|
|
337
|
+
expect(diagnosticProblem({...d, source: ''})).toMatch(/source/);
|
|
338
|
+
for (const source of [
|
|
339
|
+
'/tmp/x/Card.doc.mjs',
|
|
340
|
+
'C:\\x\\Card.doc.mjs',
|
|
341
|
+
'@acme/kit/../x',
|
|
342
|
+
]) {
|
|
343
|
+
expect(diagnosticProblem({...d, source})).toMatch(
|
|
344
|
+
/not a location on one machine/,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('sorts by source, then phase, code, field, and message', () => {
|
|
350
|
+
const at = (
|
|
351
|
+
/** @type {string} */ code,
|
|
352
|
+
/** @type {string|null} */ source,
|
|
353
|
+
field = null,
|
|
354
|
+
) => diagnostic(code, {source, field, message: `${code} ${field}`});
|
|
355
|
+
const list = [
|
|
356
|
+
at('unresolved_reference', 'b/x', 'sections.b'),
|
|
357
|
+
at('invalid_doc', 'b/x'),
|
|
358
|
+
at('load_failed', 'b/x'),
|
|
359
|
+
at('unresolved_reference', 'b/x', 'sections.a'),
|
|
360
|
+
at('invalid_doc', null),
|
|
361
|
+
at('duplicate_id', 'a/x'),
|
|
362
|
+
];
|
|
363
|
+
const sorted = sortDiagnostics(list);
|
|
364
|
+
expect(sorted.map(d => `${d.source}:${d.code}:${d.field}`)).toEqual([
|
|
365
|
+
'a/x:duplicate_id:null',
|
|
366
|
+
'b/x:load_failed:null',
|
|
367
|
+
'b/x:invalid_doc:null',
|
|
368
|
+
'b/x:unresolved_reference:sections.a',
|
|
369
|
+
'b/x:unresolved_reference:sections.b',
|
|
370
|
+
'null:invalid_doc:null',
|
|
371
|
+
]);
|
|
372
|
+
expect(sortDiagnostics([...list].reverse())).toEqual(sorted);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
describe('parseCompiledDocNode and parseCompiledDocsBundle', () => {
|
|
377
|
+
const node = () =>
|
|
378
|
+
/** @type {any} */ (structuredClone(lowerDoc(input(card)).node));
|
|
379
|
+
const bundle = () => ({
|
|
380
|
+
schemaVersion: COMPILED_DOC_SCHEMA_VERSION,
|
|
381
|
+
lang: null,
|
|
382
|
+
nodes: [node()],
|
|
383
|
+
diagnostics: [diagnostic('unresolved_reference', {message: 'x'})],
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('accepts a node of every kind and a bundle, as given', () => {
|
|
387
|
+
const value = bundle();
|
|
388
|
+
expect(parseCompiledDocsBundle(value)).toBe(value);
|
|
389
|
+
expect(COMPILED_DOC_KINDS).toHaveLength(9);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('names schema skew with its own diagnostic, before anything else', () => {
|
|
393
|
+
for (const parse of [parseCompiledDocNode, parseCompiledDocsBundle]) {
|
|
394
|
+
try {
|
|
395
|
+
parse({schemaVersion: 99, nope: true});
|
|
396
|
+
throw new Error('expected a throw');
|
|
397
|
+
} catch (error) {
|
|
398
|
+
expect(/** @type {any} */ (error).message).toMatch(
|
|
399
|
+
/schema version 99 is not supported/,
|
|
400
|
+
);
|
|
401
|
+
expect(/** @type {any} */ (error).diagnostic.code).toBe(
|
|
402
|
+
'unsupported_schema',
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it.each([
|
|
409
|
+
[
|
|
410
|
+
'an unknown field',
|
|
411
|
+
(/** @type {any} */ n) => (n.extra = 1),
|
|
412
|
+
/unknown fields: extra/,
|
|
413
|
+
],
|
|
414
|
+
[
|
|
415
|
+
'an unknown kind',
|
|
416
|
+
(/** @type {any} */ n) => (n.kind = 'namespace'),
|
|
417
|
+
/not a compiled doc kind/,
|
|
418
|
+
],
|
|
419
|
+
[
|
|
420
|
+
'a machine path as provider',
|
|
421
|
+
(/** @type {any} */ n) => (n.provenance.provider = '/home/x'),
|
|
422
|
+
/not a machine path/,
|
|
423
|
+
],
|
|
424
|
+
[
|
|
425
|
+
'a machine path as source',
|
|
426
|
+
(/** @type {any} */ n) => (n.provenance.source = 'C:\\x\\Card.doc.mjs'),
|
|
427
|
+
/not a machine path/,
|
|
428
|
+
],
|
|
429
|
+
[
|
|
430
|
+
'a source that climbs out of its package',
|
|
431
|
+
(/** @type {any} */ n) => (n.provenance.source = '@acme/kit/../x'),
|
|
432
|
+
/not a machine path/,
|
|
433
|
+
],
|
|
434
|
+
[
|
|
435
|
+
'a stage other than lowered',
|
|
436
|
+
(/** @type {any} */ n) => (n.stage = 'linked'),
|
|
437
|
+
/stage/,
|
|
438
|
+
],
|
|
439
|
+
[
|
|
440
|
+
'a doc without a name',
|
|
441
|
+
(/** @type {any} */ n) => delete n.doc.name,
|
|
442
|
+
/doc with a name/,
|
|
443
|
+
],
|
|
444
|
+
[
|
|
445
|
+
'a page node without a page doc',
|
|
446
|
+
(/** @type {any} */ n) => {
|
|
447
|
+
n.kind = 'page';
|
|
448
|
+
n.doc.type = 'block';
|
|
449
|
+
},
|
|
450
|
+
/a page node holds a page doc/,
|
|
451
|
+
],
|
|
452
|
+
[
|
|
453
|
+
'a function in the doc',
|
|
454
|
+
(/** @type {any} */ n) => (n.doc.render = () => null),
|
|
455
|
+
/is not JSON/,
|
|
456
|
+
],
|
|
457
|
+
[
|
|
458
|
+
'undefined in the doc',
|
|
459
|
+
(/** @type {any} */ n) => (n.doc.gone = undefined),
|
|
460
|
+
/undefined is not JSON/,
|
|
461
|
+
],
|
|
462
|
+
])('rejects %s', (_name, mutate, message) => {
|
|
463
|
+
const value = node();
|
|
464
|
+
mutate(value);
|
|
465
|
+
expect(() => parseCompiledDocNode(value)).toThrow(message);
|
|
466
|
+
try {
|
|
467
|
+
parseCompiledDocNode(value);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
expect(/** @type {any} */ (error).diagnostic.code).toBe('invalid_bundle');
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('rejects a bundle with a duplicate id, a bad node, or a reinterpreted diagnostic', () => {
|
|
474
|
+
const twice = bundle();
|
|
475
|
+
twice.nodes.push(node());
|
|
476
|
+
expect(() => parseCompiledDocsBundle(twice)).toThrow(/appears twice/);
|
|
477
|
+
const bad = bundle();
|
|
478
|
+
bad.nodes[0].kind = 'nope';
|
|
479
|
+
expect(() => parseCompiledDocsBundle(bad)).toThrow(/nodes\[0\]\.kind/);
|
|
480
|
+
const reinterpreted = bundle();
|
|
481
|
+
reinterpreted.diagnostics[0] = {
|
|
482
|
+
...reinterpreted.diagnostics[0],
|
|
483
|
+
severity: 'error',
|
|
484
|
+
};
|
|
485
|
+
expect(() => parseCompiledDocsBundle(reinterpreted)).toThrow(
|
|
486
|
+
/diagnostics\[0\]/,
|
|
487
|
+
);
|
|
488
|
+
expect(() => parseCompiledDocsBundle({...bundle(), extra: 1})).toThrow(
|
|
489
|
+
/unknown fields/,
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
* @file Translation overlays for component and hook docs.
|
|
6
|
+
*
|
|
7
|
+
* @input An authored component or hook doc and the translation its module
|
|
8
|
+
* exports for the reading language (`docsZh`, `docsDense`).
|
|
9
|
+
* @output The doc in that language: translated text laid over the authored
|
|
10
|
+
* doc, never dropping a prop, param, or field the translation has not
|
|
11
|
+
* caught up with.
|
|
12
|
+
* @position Used by ./compile.mjs when it lowers a component or hook doc, so
|
|
13
|
+
* every reader sees one overlay rule. Reference topics overlay by section in
|
|
14
|
+
* ./compile.mjs instead.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The translation a doc module exports for `lang`, or null.
|
|
18
|
+
* @param {Record<string, any>} mod
|
|
19
|
+
* @param {string | null} lang
|
|
20
|
+
* @returns {any}
|
|
21
|
+
*/
|
|
22
|
+
export function translationFor(mod: Record<string, any>, lang: string | null): any;
|
|
23
|
+
/**
|
|
24
|
+
* Lay a translation over an authored component or hook doc. A translation
|
|
25
|
+
* that carries props is a full translated doc, overlaid prop by prop; any other
|
|
26
|
+
* is a set of translated strings merged onto the doc.
|
|
27
|
+
* @param {any} docs
|
|
28
|
+
* @param {any} translation
|
|
29
|
+
* @returns {any}
|
|
30
|
+
*/
|
|
31
|
+
export function overlayAuthoredDoc(docs: any, translation: any): any;
|
|
32
|
+
/**
|
|
33
|
+
* @param {any} docs
|
|
34
|
+
* @param {any} translation
|
|
35
|
+
* @returns {any}
|
|
36
|
+
*/
|
|
37
|
+
export function mergeTranslation(docs: any, translation: any): any;
|