@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,47 @@
|
|
|
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
|
+
* @typedef {import('./compile.mjs').CompiledDocNode} CompiledDocNode
|
|
6
|
+
* @typedef {import('./compile.mjs').CompiledReferenceNode} CompiledReferenceNode
|
|
7
|
+
* @typedef {import('./diagnostics.mjs').CompilerDiagnostic} CompilerDiagnostic
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} CompiledDocsBundle
|
|
11
|
+
* @property {number} schemaVersion
|
|
12
|
+
* @property {string | null} lang
|
|
13
|
+
* @property {Array<CompiledDocNode | CompiledReferenceNode>} nodes in the order
|
|
14
|
+
* the project reads its roots
|
|
15
|
+
* @property {CompilerDiagnostic[]} diagnostics sorted
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Compile every descriptor the project reads.
|
|
19
|
+
*
|
|
20
|
+
* A problem with one descriptor withdraws that node and is reported; it never
|
|
21
|
+
* stops the rest, so every node in the bundle reads back through the sealed
|
|
22
|
+
* parser. A token reference that finds nothing is a warning: readers
|
|
23
|
+
* print a placeholder for it, as they always have.
|
|
24
|
+
*
|
|
25
|
+
* @param {import('../config/project.mjs').Project} project
|
|
26
|
+
* @param {{lang?: string | null}} [options]
|
|
27
|
+
* @returns {Promise<CompiledDocsBundle>}
|
|
28
|
+
*/
|
|
29
|
+
export function compileDocs(project: import("../config/project.mjs").Project, { lang }?: {
|
|
30
|
+
lang?: string | null;
|
|
31
|
+
}): Promise<CompiledDocsBundle>;
|
|
32
|
+
export type CompiledDocNode = import("./compile.mjs").CompiledDocNode;
|
|
33
|
+
export type CompiledReferenceNode = import("./compile.mjs").CompiledReferenceNode;
|
|
34
|
+
export type CompilerDiagnostic = import("./diagnostics.mjs").CompilerDiagnostic;
|
|
35
|
+
export type CompiledDocsBundle = {
|
|
36
|
+
schemaVersion: number;
|
|
37
|
+
lang: string | null;
|
|
38
|
+
/**
|
|
39
|
+
* in the order
|
|
40
|
+
* the project reads its roots
|
|
41
|
+
*/
|
|
42
|
+
nodes: Array<CompiledDocNode | CompiledReferenceNode>;
|
|
43
|
+
/**
|
|
44
|
+
* sorted
|
|
45
|
+
*/
|
|
46
|
+
diagnostics: CompilerDiagnostic[];
|
|
47
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Compiled docs bundle — every descriptor a project reads, compiled at
|
|
5
|
+
* once.
|
|
6
|
+
*
|
|
7
|
+
* @input A loaded Project and a reading language.
|
|
8
|
+
* @output A plain-JSON bundle: the schema version, the language, one compiled
|
|
9
|
+
* node per descriptor (a doc topic with its extensions is one linked node),
|
|
10
|
+
* and every compiler diagnostic, in one deterministic order.
|
|
11
|
+
* @position The whole-project view of the compiler, for checks that need every
|
|
12
|
+
* node at once; today only tests call it. Single reads stay lazy:
|
|
13
|
+
* ./read.mjs and api/docs/_adapter.mjs compile only what a read touches.
|
|
14
|
+
* Internal to the CLI.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
readThemeDescriptorValue,
|
|
19
|
+
themeDescriptorLabel,
|
|
20
|
+
} from '../discovery/theme-discovery.mjs';
|
|
21
|
+
import {
|
|
22
|
+
COMPILED_DOC_SCHEMA_VERSION,
|
|
23
|
+
linkReferenceTopic,
|
|
24
|
+
lowerReferenceTopic,
|
|
25
|
+
} from './compile.mjs';
|
|
26
|
+
import {diagnostic as rawDiagnostic, sortDiagnostics} from './diagnostics.mjs';
|
|
27
|
+
import {collectDocInputs} from './inputs.mjs';
|
|
28
|
+
import {compileDocFile, loadTopicInput} from './read.mjs';
|
|
29
|
+
import {packageSource, scrubPaths} from './source.mjs';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A compiler diagnostic whose message names files by package, never by their
|
|
33
|
+
* location on this machine.
|
|
34
|
+
* @param {string} code
|
|
35
|
+
* @param {Parameters<typeof rawDiagnostic>[1]} at
|
|
36
|
+
*/
|
|
37
|
+
function diagnostic(code, at) {
|
|
38
|
+
return rawDiagnostic(code, {
|
|
39
|
+
...at,
|
|
40
|
+
message: scrubPaths(at.message) || '(the error had no message)',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {import('./compile.mjs').CompiledDocNode} CompiledDocNode
|
|
46
|
+
* @typedef {import('./compile.mjs').CompiledReferenceNode} CompiledReferenceNode
|
|
47
|
+
* @typedef {import('./diagnostics.mjs').CompilerDiagnostic} CompilerDiagnostic
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {object} CompiledDocsBundle
|
|
52
|
+
* @property {number} schemaVersion
|
|
53
|
+
* @property {string | null} lang
|
|
54
|
+
* @property {Array<CompiledDocNode | CompiledReferenceNode>} nodes in the order
|
|
55
|
+
* the project reads its roots
|
|
56
|
+
* @property {CompilerDiagnostic[]} diagnostics sorted
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Compile every descriptor the project reads.
|
|
61
|
+
*
|
|
62
|
+
* A problem with one descriptor withdraws that node and is reported; it never
|
|
63
|
+
* stops the rest, so every node in the bundle reads back through the sealed
|
|
64
|
+
* parser. A token reference that finds nothing is a warning: readers
|
|
65
|
+
* print a placeholder for it, as they always have.
|
|
66
|
+
*
|
|
67
|
+
* @param {import('../config/project.mjs').Project} project
|
|
68
|
+
* @param {{lang?: string | null}} [options]
|
|
69
|
+
* @returns {Promise<CompiledDocsBundle>}
|
|
70
|
+
*/
|
|
71
|
+
export async function compileDocs(project, {lang = null} = {}) {
|
|
72
|
+
const {inputs, problems} = await collectDocInputs(project);
|
|
73
|
+
/** @type {CompilerDiagnostic[]} */
|
|
74
|
+
const diagnostics = problems.map(problem =>
|
|
75
|
+
diagnostic(problem.code, {
|
|
76
|
+
provider: problem.provider,
|
|
77
|
+
source: problem.source,
|
|
78
|
+
message: problem.message,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
const catalog = await project.docs();
|
|
82
|
+
|
|
83
|
+
/** @type {Map<string, CompiledReferenceNode>} lowered topics by name */
|
|
84
|
+
const topics = new Map();
|
|
85
|
+
for (const entry of catalog.entries()) {
|
|
86
|
+
const input = await loadTopicInput(entry, lang);
|
|
87
|
+
const failed = [input.base, ...input.extensions].filter(
|
|
88
|
+
file => 'error' in file || 'overlayError' in file,
|
|
89
|
+
);
|
|
90
|
+
if (failed.length > 0) {
|
|
91
|
+
for (const file of failed) {
|
|
92
|
+
const fromExtension = input.extensions.find(ext => ext === file);
|
|
93
|
+
const path = fromExtension
|
|
94
|
+
? entry.extensions[input.extensions.indexOf(fromExtension)].path
|
|
95
|
+
: entry.path;
|
|
96
|
+
diagnostics.push(
|
|
97
|
+
diagnostic('error' in file ? 'load_failed' : 'overlay_failed', {
|
|
98
|
+
provider: fromExtension ? fromExtension.provider : entry.package,
|
|
99
|
+
source: packageSource(path),
|
|
100
|
+
message: `${file.file}: ${messageOf('error' in file ? file.error : file.overlayError)}`,
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
topics.set(entry.name.toLowerCase(), lowerReferenceTopic(input));
|
|
108
|
+
} catch (error) {
|
|
109
|
+
diagnostics.push(
|
|
110
|
+
diagnostic(
|
|
111
|
+
errorCode(error) === 'not_json' ? 'not_json' : 'invalid_topic',
|
|
112
|
+
{
|
|
113
|
+
provider: entry.package,
|
|
114
|
+
source: packageSource(entry.path),
|
|
115
|
+
message: messageOf(error),
|
|
116
|
+
},
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** @param {string} name */
|
|
122
|
+
const lowerTarget = async name => {
|
|
123
|
+
const target = catalog.resolve(name);
|
|
124
|
+
return target ? (topics.get(target.name.toLowerCase()) ?? null) : null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** @type {Array<CompiledDocNode | CompiledReferenceNode>} */
|
|
128
|
+
const nodes = [];
|
|
129
|
+
for (const input of inputs) {
|
|
130
|
+
if (input.root === 'docs') {
|
|
131
|
+
if (input.role !== 'base') continue;
|
|
132
|
+
const lowered = topics.get(input.name.toLowerCase());
|
|
133
|
+
if (!lowered) continue;
|
|
134
|
+
const linked = await linkReferenceTopic(lowered, lowerTarget);
|
|
135
|
+
diagnostics.push(...unresolvedReferences(linked, input));
|
|
136
|
+
nodes.push(linked);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const result = await compileDocFile(
|
|
140
|
+
input.file,
|
|
141
|
+
{
|
|
142
|
+
root: input.root,
|
|
143
|
+
provider: input.owner,
|
|
144
|
+
id: input.id,
|
|
145
|
+
lang:
|
|
146
|
+
input.root === 'components' || input.root === 'hooks' ? lang : null,
|
|
147
|
+
check: true,
|
|
148
|
+
...(input.root === 'templates' ? {loader: 'template'} : {}),
|
|
149
|
+
...(input.root === 'themes'
|
|
150
|
+
? {
|
|
151
|
+
label: themeDescriptorLabel(input.file, input.owner),
|
|
152
|
+
readStatic: () =>
|
|
153
|
+
readThemeDescriptorValue(
|
|
154
|
+
input.file,
|
|
155
|
+
themeDescriptorLabel(input.file, input.owner),
|
|
156
|
+
),
|
|
157
|
+
}
|
|
158
|
+
: {}),
|
|
159
|
+
},
|
|
160
|
+
{node: true},
|
|
161
|
+
);
|
|
162
|
+
diagnostics.push(...result.diagnostics);
|
|
163
|
+
// A descriptor with a problem of its own withdraws its node.
|
|
164
|
+
if (result.node && !result.diagnostics.some(d => d.severity === 'error')) {
|
|
165
|
+
nodes.push(result.node);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
schemaVersion: COMPILED_DOC_SCHEMA_VERSION,
|
|
171
|
+
lang,
|
|
172
|
+
nodes,
|
|
173
|
+
diagnostics: sortDiagnostics(diagnostics),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* A warning for each token reference in a linked topic that finds no topic or
|
|
179
|
+
* no section.
|
|
180
|
+
* @param {CompiledReferenceNode} node
|
|
181
|
+
* @param {import('./inputs.mjs').DocInput} input
|
|
182
|
+
* @returns {CompilerDiagnostic[]}
|
|
183
|
+
*/
|
|
184
|
+
function unresolvedReferences(node, input) {
|
|
185
|
+
/** @type {CompilerDiagnostic[]} */
|
|
186
|
+
const found = [];
|
|
187
|
+
for (const section of node.doc.sections) {
|
|
188
|
+
for (const block of section.content) {
|
|
189
|
+
if (block?.type !== 'token-ref') continue;
|
|
190
|
+
const status = block.resolved?.status;
|
|
191
|
+
if (status === 'resolved') continue;
|
|
192
|
+
found.push(
|
|
193
|
+
diagnostic('unresolved_reference', {
|
|
194
|
+
provider: input.owner,
|
|
195
|
+
source: input.source,
|
|
196
|
+
field: `sections.${section.id}`,
|
|
197
|
+
message:
|
|
198
|
+
status === 'unknown-topic'
|
|
199
|
+
? `"${node.id}" section "${section.id}" refers to topic "${block.topic}", which this project does not have.`
|
|
200
|
+
: `"${node.id}" section "${section.id}" refers to section "${block.section}" of "${block.topic}", which that topic does not have.`,
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return found;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** @param {unknown} error */
|
|
209
|
+
function messageOf(error) {
|
|
210
|
+
return error instanceof Error ? error.message : String(error);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** @param {unknown} error */
|
|
214
|
+
function errorCode(error) {
|
|
215
|
+
return error && typeof error === 'object' && 'compilerCode' in error
|
|
216
|
+
? error.compilerCode
|
|
217
|
+
: undefined;
|
|
218
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file compileDocs: every descriptor a project reads compiles to one node or
|
|
5
|
+
* a diagnostic, and the bundle reads back through its sealed parser.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import {Project} from '../config/project.mjs';
|
|
12
|
+
import {CLI_ROOT} from '../fs/paths.mjs';
|
|
13
|
+
import {compileDocs} from './bundle.mjs';
|
|
14
|
+
import {COMPILED_DOC_KINDS} from './compile.mjs';
|
|
15
|
+
import {collectDocInputs} from './inputs.mjs';
|
|
16
|
+
import {parseCompiledDocsBundle} from './ir.mjs';
|
|
17
|
+
|
|
18
|
+
const REPO_ROOT = path.resolve(CLI_ROOT, '..', '..');
|
|
19
|
+
|
|
20
|
+
let tmpDir;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
// Inside the repo, so the project finds this checkout's Core.
|
|
24
|
+
tmpDir = fs.mkdtempSync(path.join(REPO_ROOT, '.astryx-doc-bundle-'));
|
|
25
|
+
fs.writeFileSync(
|
|
26
|
+
path.join(tmpDir, 'package.json'),
|
|
27
|
+
JSON.stringify({name: 'consumer', version: '1.0.0'}),
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('compileDocs over what this repo ships', () => {
|
|
36
|
+
it('compiles every descriptor, in every language, with nothing to report', async () => {
|
|
37
|
+
const project = await Project.load(tmpDir);
|
|
38
|
+
const {inputs} = await collectDocInputs(project);
|
|
39
|
+
// One node per input, except that a topic and its extensions are one node.
|
|
40
|
+
const expected = inputs.filter(
|
|
41
|
+
input => input.root !== 'docs' || input.role === 'base',
|
|
42
|
+
).length;
|
|
43
|
+
for (const lang of [null, 'zh', 'dense']) {
|
|
44
|
+
const bundle = await compileDocs(project, {lang});
|
|
45
|
+
expect(bundle.diagnostics).toEqual([]);
|
|
46
|
+
expect(bundle.nodes).toHaveLength(expected);
|
|
47
|
+
expect(new Set(bundle.nodes.map(node => node.kind))).toEqual(
|
|
48
|
+
new Set(COMPILED_DOC_KINDS),
|
|
49
|
+
);
|
|
50
|
+
// Plain JSON that reads back through the sealed parser, as written.
|
|
51
|
+
const copy = JSON.parse(JSON.stringify(bundle));
|
|
52
|
+
expect(parseCompiledDocsBundle(copy)).toEqual(bundle);
|
|
53
|
+
}
|
|
54
|
+
}, 180_000);
|
|
55
|
+
|
|
56
|
+
it('is deterministic', async () => {
|
|
57
|
+
const one = await compileDocs(await Project.load(tmpDir));
|
|
58
|
+
const two = await compileDocs(await Project.load(tmpDir));
|
|
59
|
+
expect(JSON.stringify(two)).toBe(JSON.stringify(one));
|
|
60
|
+
}, 120_000);
|
|
61
|
+
|
|
62
|
+
it('never carries a machine path', async () => {
|
|
63
|
+
const bundle = await compileDocs(await Project.load(tmpDir));
|
|
64
|
+
const text = JSON.stringify(bundle);
|
|
65
|
+
expect(text.includes(REPO_ROOT)).toBe(false);
|
|
66
|
+
expect(text.includes(tmpDir)).toBe(false);
|
|
67
|
+
}, 120_000);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('compileDocs over a broken integration', () => {
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} rel
|
|
73
|
+
* @param {string} text
|
|
74
|
+
*/
|
|
75
|
+
function write(rel, text) {
|
|
76
|
+
const file = path.join(tmpDir, 'node_modules', '@acme', 'kit', rel);
|
|
77
|
+
fs.mkdirSync(path.dirname(file), {recursive: true});
|
|
78
|
+
fs.writeFileSync(file, text);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @param {object} fields */
|
|
82
|
+
const topic = fields =>
|
|
83
|
+
`export default ${JSON.stringify({
|
|
84
|
+
type: 'generic',
|
|
85
|
+
title: 'Acme',
|
|
86
|
+
description: 'Acme notes.',
|
|
87
|
+
sections: [{title: 'Acme', content: [{type: 'prose', text: 'Hi.'}]}],
|
|
88
|
+
...fields,
|
|
89
|
+
})};\n`;
|
|
90
|
+
|
|
91
|
+
beforeEach(() => {
|
|
92
|
+
fs.writeFileSync(
|
|
93
|
+
path.join(tmpDir, 'package.json'),
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
name: 'consumer',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
dependencies: {'@acme/kit': '^1.0.0'},
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
write(
|
|
101
|
+
'package.json',
|
|
102
|
+
JSON.stringify({name: '@acme/kit', version: '1.0.0'}),
|
|
103
|
+
);
|
|
104
|
+
write(
|
|
105
|
+
'astryx.integration.mjs',
|
|
106
|
+
"export default {components: './components', docs: './docs'};\n",
|
|
107
|
+
);
|
|
108
|
+
write(
|
|
109
|
+
'components/AcmeCard.tsx',
|
|
110
|
+
'export function AcmeCard() { return null; }\n',
|
|
111
|
+
);
|
|
112
|
+
write(
|
|
113
|
+
'components/AcmeCard.doc.mjs',
|
|
114
|
+
"export default {type: 'component', name: 'AcmeCard', displayName: 'Acme Card', usage: {description: 'A card.'}, props: []};\n",
|
|
115
|
+
);
|
|
116
|
+
write('docs/acme-guide.doc.mjs', topic({name: 'acme-guide'}));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('warns on a token reference that finds nothing, and still compiles the topic', async () => {
|
|
120
|
+
write(
|
|
121
|
+
'docs/acme-guide.doc.mjs',
|
|
122
|
+
topic({
|
|
123
|
+
name: 'acme-guide',
|
|
124
|
+
sections: [
|
|
125
|
+
{
|
|
126
|
+
title: 'Colors',
|
|
127
|
+
content: [
|
|
128
|
+
{type: 'token-ref', topic: 'no-such-topic', section: 'x'},
|
|
129
|
+
{type: 'token-ref', topic: 'tokens', section: 'no-such-section'},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
const bundle = await compileDocs(await Project.load(tmpDir));
|
|
136
|
+
const warnings = bundle.diagnostics.filter(d => d.provider === '@acme/kit');
|
|
137
|
+
expect(warnings.map(d => [d.code, d.severity, d.field])).toEqual([
|
|
138
|
+
['unresolved_reference', 'warning', 'sections.colors'],
|
|
139
|
+
['unresolved_reference', 'warning', 'sections.colors'],
|
|
140
|
+
]);
|
|
141
|
+
expect(
|
|
142
|
+
warnings.every(d => d.source === '@acme/kit/docs/acme-guide.doc.mjs'),
|
|
143
|
+
).toBe(true);
|
|
144
|
+
expect(bundle.nodes.some(node => node.id === 'acme-guide')).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('reports a component doc that fails to load, and compiles the rest', async () => {
|
|
148
|
+
write(
|
|
149
|
+
'components/AcmeCard.doc.mjs',
|
|
150
|
+
"throw new Error('boom');\nexport default {};\n",
|
|
151
|
+
);
|
|
152
|
+
const project = await Project.load(tmpDir);
|
|
153
|
+
const bundle = await compileDocs(project);
|
|
154
|
+
const found = bundle.diagnostics.filter(d => d.provider === '@acme/kit');
|
|
155
|
+
// Discovery lists the doc; compiling it fails at load, and only it.
|
|
156
|
+
expect(found.map(d => [d.code, d.source])).toEqual([
|
|
157
|
+
['load_failed', '@acme/kit/components/AcmeCard.doc.mjs'],
|
|
158
|
+
]);
|
|
159
|
+
expect(found[0].message).toMatch(/boom/);
|
|
160
|
+
expect(bundle.nodes.some(node => node.kind === 'reference')).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('withdraws a descriptor that fails its parser, and the bundle still reads back', async () => {
|
|
164
|
+
write(
|
|
165
|
+
'components/AcmeCard.doc.mjs',
|
|
166
|
+
"export default {type: 'component', displayName: 'No name'};\n",
|
|
167
|
+
);
|
|
168
|
+
const bundle = await compileDocs(await Project.load(tmpDir));
|
|
169
|
+
const found = bundle.diagnostics.filter(d => d.provider === '@acme/kit');
|
|
170
|
+
expect(found.map(d => d.code)).toEqual(['invalid_doc']);
|
|
171
|
+
expect(
|
|
172
|
+
bundle.nodes.some(node => node.id === '@acme/kit:components:AcmeCard'),
|
|
173
|
+
).toBe(false);
|
|
174
|
+
expect(() =>
|
|
175
|
+
parseCompiledDocsBundle(JSON.parse(JSON.stringify(bundle))),
|
|
176
|
+
).not.toThrow();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('reports a topic value JSON cannot hold with the same code as any other doc', async () => {
|
|
180
|
+
write(
|
|
181
|
+
'docs/acme-guide.doc.mjs',
|
|
182
|
+
topic({name: 'acme-guide'}).replace('};', ', big: 1n};'),
|
|
183
|
+
);
|
|
184
|
+
const bundle = await compileDocs(await Project.load(tmpDir));
|
|
185
|
+
const found = bundle.diagnostics.filter(d => d.provider === '@acme/kit');
|
|
186
|
+
expect(found.map(d => d.code)).toContain('not_json');
|
|
187
|
+
expect(found.map(d => d.code)).not.toContain('invalid_topic');
|
|
188
|
+
// The compiler's code never reaches the error a reader throws.
|
|
189
|
+
const {lowerTopic} = await import('../../api/docs/_adapter.mjs');
|
|
190
|
+
const catalog = await (await Project.load(tmpDir)).docs();
|
|
191
|
+
const entry = /** @type {any} */ (catalog.resolve('acme-guide'));
|
|
192
|
+
const thrown = await lowerTopic(catalog, entry).catch(error => error);
|
|
193
|
+
expect(thrown).toBeInstanceOf(Error);
|
|
194
|
+
expect(/** @type {any} */ (thrown).code).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('gives every extension file its own input, even two from one package', async () => {
|
|
198
|
+
write('docs/theme-a.doc.mjs', topic({name: 'theme-a', extends: 'theme'}));
|
|
199
|
+
write('docs/theme-b.doc.mjs', topic({name: 'theme-b', extends: 'theme'}));
|
|
200
|
+
const project = await Project.load(tmpDir);
|
|
201
|
+
const {problems} = await collectDocInputs(project);
|
|
202
|
+
expect(problems.filter(p => p.code === 'duplicate_id')).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('names a package that has no name by its folder, never by its path', async () => {
|
|
206
|
+
fs.writeFileSync(
|
|
207
|
+
path.join(tmpDir, 'package.json'),
|
|
208
|
+
JSON.stringify({version: '1.0.0'}),
|
|
209
|
+
);
|
|
210
|
+
fs.writeFileSync(
|
|
211
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
212
|
+
"export default {components: './components'};\n",
|
|
213
|
+
);
|
|
214
|
+
fs.mkdirSync(path.join(tmpDir, 'components', 'Local'), {recursive: true});
|
|
215
|
+
fs.writeFileSync(
|
|
216
|
+
path.join(tmpDir, 'components', 'Local', 'Local.tsx'),
|
|
217
|
+
'export {};\n',
|
|
218
|
+
);
|
|
219
|
+
fs.writeFileSync(
|
|
220
|
+
path.join(tmpDir, 'components', 'Local', 'Local.doc.mjs'),
|
|
221
|
+
"export default {type: 'component', name: 'Local', displayName: 'Local', usage: {description: 'd'}, props: []};\n",
|
|
222
|
+
);
|
|
223
|
+
const bundle = await compileDocs(await Project.load(tmpDir));
|
|
224
|
+
const text = JSON.stringify(bundle);
|
|
225
|
+
expect(text.includes(tmpDir)).toBe(false);
|
|
226
|
+
expect(() => parseCompiledDocsBundle(JSON.parse(text))).not.toThrow();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('reports an extension that fails to load against the extension, not its base', async () => {
|
|
230
|
+
write('docs/theme-notes.doc.mjs', "throw new Error('bad extension');\n");
|
|
231
|
+
// Discovery refuses an unloadable topic file, so the catalog never merges
|
|
232
|
+
// it; compile the entry as the catalog would have if it had loaded.
|
|
233
|
+
const project = await Project.load(tmpDir);
|
|
234
|
+
const catalog = await project.docs();
|
|
235
|
+
const theme = /** @type {any} */ (catalog.resolve('theme'));
|
|
236
|
+
theme.extensions.push({
|
|
237
|
+
package: '@acme/kit',
|
|
238
|
+
path: path.join(
|
|
239
|
+
tmpDir,
|
|
240
|
+
'node_modules',
|
|
241
|
+
'@acme',
|
|
242
|
+
'kit',
|
|
243
|
+
'docs',
|
|
244
|
+
'theme-notes.doc.mjs',
|
|
245
|
+
),
|
|
246
|
+
});
|
|
247
|
+
const bundle = await compileDocs(project);
|
|
248
|
+
const found = bundle.diagnostics.filter(d => d.code === 'load_failed');
|
|
249
|
+
expect(found.map(d => [d.provider, d.source])).toEqual([
|
|
250
|
+
['@acme/kit', '@acme/kit/docs/theme-notes.doc.mjs'],
|
|
251
|
+
]);
|
|
252
|
+
// The broken extension withdraws the topic it extends, as a docs read does.
|
|
253
|
+
expect(bundle.nodes.some(node => node.id === 'theme')).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
});
|