@astryxdesign/cli 0.7.2-canary.f16dc9f → 0.7.2-canary.f2b60a0
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 +15 -14
- package/api/build/help/help.mjs +1 -1
- package/api/component/_adapter.d.mts +41 -1
- package/api/component/_adapter.mjs +93 -7
- package/api/component/component.mjs +22 -5
- package/api/component/component.test.mjs +420 -167
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- package/api/docs/_adapter.d.mts +11 -0
- package/api/docs/_adapter.mjs +244 -1
- package/api/docs/integrationDocs.test.mjs +124 -0
- package/api/doctor/doctor.mjs +27 -3
- package/api/doctor/doctor.test.mjs +43 -0
- package/api/doctor/theme-checks.mjs +1 -1
- package/api/gap-report/gap-report.mjs +67 -4
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/init/run/run.mjs +2 -2
- package/api/integration/_adapter.d.mts +12 -0
- package/api/integration/_adapter.mjs +54 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +11 -2
- package/api/integration/authoring-checks.test.mjs +39 -0
- package/api/integration/authoring-checks.type.d.mts +9 -0
- package/api/integration/authoring-checks.type.mjs +14 -1
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
- package/api/integration/pack-check.mjs +61 -107
- package/api/integration/pack-check.test.mjs +359 -0
- package/api/integration/validate-integration.mjs +8 -0
- package/api/integration/validate-integration.test.mjs +11 -0
- package/api/integration/validate-integration.type.d.mts +8 -0
- package/api/integration/validate-integration.type.mjs +12 -1
- package/api/integration/validateIntegration.doc.mjs +17 -3
- package/api/search/search.d.mts +22 -6
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +141 -25
- package/api/search/search.test.mjs +36 -0
- package/api/swizzle/copy/copy.mjs +28 -14
- package/api/template/template.test.mjs +120 -3
- package/api/theme/build/build.family.test.mjs +64 -0
- package/api/theme/build/build.mjs +180 -24
- package/api/theme/themeBuild.doc.mjs +4 -4
- package/assets/codemods/transforms/next/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
- package/assets/codemods/transforms/next/index.mjs +11 -1
- package/assets/codemods/transforms/next/migrate-list-edge-compensation-to-edge.mjs +198 -0
- package/assets/docs/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +3 -3
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +11 -1
- package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +10 -7
- package/assets/docs/tree/agent-guidance.doc.mjs +2 -2
- package/assets/docs/tree/block-template.doc.mjs +8 -20
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/check-the-props.doc.mjs +196 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +51 -5
- package/assets/docs/tree/checks.doc.mjs +24 -16
- package/assets/docs/tree/codemods.doc.mjs +4 -4
- package/assets/docs/tree/component-family.doc.mjs +8 -6
- package/assets/docs/tree/component-imports.doc.mjs +10 -2
- package/assets/docs/tree/components.doc.mjs +1 -1
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +7 -18
- package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
- package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
- package/assets/docs/tree/docs.doc.mjs +3 -3
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +6 -6
- package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
- package/assets/docs/tree/generate-a-palette.doc.mjs +5 -3
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +1 -1
- package/assets/docs/tree/integrations.doc.mjs +8 -0
- package/assets/docs/tree/links.doc.mjs +6 -5
- package/assets/docs/tree/migration-setup.doc.mjs +1 -0
- package/assets/docs/tree/migration-verification.doc.mjs +0 -1
- package/assets/docs/tree/migration.doc.mjs +0 -1
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +4 -8
- package/assets/docs/tree/publishing.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +37 -21
- package/assets/docs/tree/replace-a-core-component.doc.mjs +4 -6
- package/assets/docs/tree/replace-a-core-template.doc.mjs +6 -8
- package/assets/docs/tree/sections-and-placement.doc.mjs +4 -6
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +4 -2
- package/assets/docs/tree/short-and-findable.doc.mjs +6 -5
- package/assets/docs/tree/single-component.doc.mjs +15 -6
- package/assets/docs/tree/start-a-template.doc.mjs +3 -3
- package/assets/docs/tree/subcomponent.doc.mjs +6 -6
- package/assets/docs/tree/template-assets.doc.mjs +2 -2
- package/assets/docs/tree/template-doc-overview.doc.mjs +4 -15
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +2 -2
- package/assets/docs/tree/templates.doc.mjs +1 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +3 -3
- package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
- package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
- package/assets/docs/tree/troubleshooting.doc.mjs +13 -5
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/upgrading.doc.mjs +3 -3
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +3 -1
- package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
- package/assets/docs/tree/versioning.doc.mjs +7 -6
- package/assets/docs/tree/write-the-template-file.doc.mjs +4 -3
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +43 -4
- package/assets/templates/blocks/components/Drawer/DrawerForm.doc.mjs +24 -0
- package/assets/templates/blocks/components/Drawer/DrawerForm.tsx +62 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.tsx +68 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.doc.mjs +21 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.tsx +54 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.tsx +73 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs +22 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.tsx +49 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.doc.mjs +21 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.tsx +42 -0
- package/assets/templates/pages/detail-page/page.tsx +3 -3
- package/assets/templates/pages/table-inbox/page.tsx +10 -8
- package/assets/theme.template.ts +1 -1
- package/authoring/codemod/codemod.doc.mjs +7 -3
- package/authoring/config/config.doc.mjs +9 -6
- package/authoring/config/type.ts +4 -2
- package/authoring/discover/discover.doc.mjs +1 -1
- package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
- package/authoring/doctypes/base/type.ts +6 -0
- package/authoring/doctypes/command/command.doc.mjs +14 -12
- package/authoring/doctypes/component/component.doc.mjs +31 -28
- package/authoring/doctypes/component/type.ts +13 -8
- package/authoring/doctypes/enum/enum.doc.mjs +7 -4
- package/authoring/doctypes/function/function.doc.mjs +9 -8
- package/authoring/doctypes/hook/hook.doc.mjs +5 -4
- package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
- package/authoring/doctypes/reference/reference.doc.mjs +11 -10
- package/authoring/doctypes/schema/schema.doc.mjs +9 -9
- package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/doctypes/theme/theme.doc.mjs +2 -2
- package/authoring/gap-report/gap-report.doc.mjs +2 -2
- package/authoring/identity/identity.doc.mjs +3 -3
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/integration.doc.mjs +12 -4
- package/authoring/integration/parse.test.mjs +10 -0
- package/authoring/integration/schema.d.mts +2 -0
- package/authoring/integration/schema.mjs +1 -0
- package/authoring/integration/type.ts +4 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
- package/clients/cli/commands/build-theme.mjs +1 -1
- package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
- package/clients/cli/commands/doctor-integration.test.mjs +837 -0
- package/clients/cli/commands/doctor.mjs +190 -0
- package/clients/cli/commands/integration-check.doc.mjs +85 -0
- package/clients/cli/commands/integration-check.test.mjs +267 -0
- package/clients/cli/commands/integration.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +107 -1
- package/clients/cli/commands/setup-nudge.test.mjs +79 -1
- package/clients/cli/commands/swizzle.mjs +5 -3
- package/clients/cli/commands/swizzle.routing.test.mjs +18 -1
- package/clients/cli/commands/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-build.doc.mjs +2 -2
- package/clients/cli/index.mjs +30 -5
- package/clients/cli/lib/component-format.mjs +5 -1
- package/foundation/agent-docs/agent-doc-state.d.mts +14 -0
- package/foundation/agent-docs/agent-doc-state.mjs +38 -0
- package/foundation/agent-docs/agent-docs.d.mts +2 -1
- package/foundation/agent-docs/agent-docs.mjs +8 -6
- package/foundation/config/project.mjs +82 -0
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/component-discovery.d.mts +190 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +864 -56
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +18 -0
- package/foundation/discovery/component-replacement.test.mjs +40 -0
- package/foundation/discovery/doc-types.d.mts +157 -0
- package/foundation/discovery/doc-types.mjs +1224 -0
- package/foundation/discovery/doc-types.test.mjs +652 -0
- package/foundation/discovery/docs-discovery.d.mts +81 -0
- package/foundation/discovery/docs-discovery.mjs +57 -0
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -0
- package/foundation/discovery/module-exports.d.mts +27 -0
- package/foundation/discovery/module-exports.mjs +356 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +23 -0
- package/foundation/discovery/project-typescript.mjs +61 -0
- package/foundation/discovery/prop-drift.d.mts +278 -0
- package/foundation/discovery/prop-drift.mjs +2263 -0
- package/foundation/discovery/prop-drift.test.mjs +2219 -0
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/discovery/type-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +11 -1
- package/foundation/doc-compiler/inputs.mjs +23 -0
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/links.d.mts +9 -5
- package/foundation/doc-compiler/links.mjs +4 -2
- package/foundation/integrations/contribution-inventory.mjs +6 -1
- package/foundation/response/response-types.doc.mjs +7 -2
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +10 -10
- package/scripts/postinstall.mjs +3 -3
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
/** @file CLI bindings for integration authoring and package verification. */
|
|
4
4
|
|
|
5
5
|
import {integrationAdd} from '../../../api/integration/add-contribution.mjs';
|
|
6
|
+
import {integrationCheck} from '../../../api/integration/check/check.mjs';
|
|
6
7
|
import {integrationPackCheck} from '../../../api/integration/pack-check.mjs';
|
|
7
8
|
import {jsonOut} from '../../../foundation/response/json.mjs';
|
|
8
9
|
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
9
10
|
import {NO_RESULT_SET} from '../../../foundation/debug/index.mjs';
|
|
10
|
-
import {emit, list, section, text} from '../formatters/index.mjs';
|
|
11
|
+
import {emit, list, records, section, text} from '../formatters/index.mjs';
|
|
11
12
|
import {cliError} from '../lib/cli-error.mjs';
|
|
12
13
|
import {defineCommand} from '../lib/define-command.mjs';
|
|
13
14
|
import {doc as integrationGroup} from './integration.doc.mjs';
|
|
14
15
|
import {doc as integrationAddCommand} from './integration-add.doc.mjs';
|
|
16
|
+
import {doc as integrationCheckCommand} from './integration-check.doc.mjs';
|
|
15
17
|
import {doc as integrationVerifyCommand} from './integration-verify.doc.mjs';
|
|
16
18
|
import {doc as integrationPackCommand} from './integration-pack.doc.mjs';
|
|
17
19
|
import {doc as integrationAddFn} from '../../../api/integration/integrationAdd.doc.mjs';
|
|
20
|
+
import {doc as integrationCheckFn} from '../../../api/integration/integrationCheck.doc.mjs';
|
|
21
|
+
import {printIntegrationCheckResponse} from './doctor.mjs';
|
|
18
22
|
import {doc as integrationPackCheckFn} from '../../../api/integration/integrationPackCheck.doc.mjs';
|
|
19
23
|
|
|
20
24
|
/**
|
|
@@ -88,6 +92,102 @@ async function verifyPackage(program) {
|
|
|
88
92
|
return NO_RESULT_SET;
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Run `integration check` and print each check as its own subcommand prints
|
|
97
|
+
* it, then one summary: each check's errors, warnings and skipped work, and
|
|
98
|
+
* what decided the exit code.
|
|
99
|
+
* @param {import('commander').Command} program
|
|
100
|
+
* @param {{strict?: boolean}} options
|
|
101
|
+
*/
|
|
102
|
+
async function checkPackage(program, options) {
|
|
103
|
+
const result = await integrationCheck({strict: options.strict === true});
|
|
104
|
+
if (program.opts().json) {
|
|
105
|
+
jsonOut(result);
|
|
106
|
+
} else {
|
|
107
|
+
const {data} = result;
|
|
108
|
+
const noPackage = data.checks[0]?.response.data.issues.some(
|
|
109
|
+
issue => issue.code === 'no_package',
|
|
110
|
+
);
|
|
111
|
+
if (noPackage) {
|
|
112
|
+
emit(
|
|
113
|
+
text(
|
|
114
|
+
'Nothing was checked: no package.json found. Run this command inside a package directory.',
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
} else if (!data.validated) {
|
|
118
|
+
emit(
|
|
119
|
+
text(
|
|
120
|
+
'Nothing was checked: no astryx.integration.* found next to package.json. ' +
|
|
121
|
+
"Run this command in an integration package's folder.",
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
} else {
|
|
125
|
+
data.checks.forEach((check, index) => {
|
|
126
|
+
// A blank line keeps each check's output apart from the next.
|
|
127
|
+
if (index > 0) emit(text(''));
|
|
128
|
+
printIntegrationCheckResponse(check.response);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const plural = (/** @type {number} */ n, /** @type {string} */ word) =>
|
|
132
|
+
`${n} ${word}${n === 1 ? '' : 's'}`;
|
|
133
|
+
const counts =
|
|
134
|
+
`${plural(data.errors, 'error')}, ${plural(data.warnings, 'warning')}, ` +
|
|
135
|
+
`${data.skipped.length} skipped`;
|
|
136
|
+
const notCompared =
|
|
137
|
+
data.unchecked > 0
|
|
138
|
+
? ` ${plural(data.unchecked, 'item')} could not be compared; the checks above list them.`
|
|
139
|
+
: '';
|
|
140
|
+
const closing = noPackage
|
|
141
|
+
? `[fail] ${counts}. No package.json was found here, so nothing was checked.`
|
|
142
|
+
: data.checks.some(check => check.failed)
|
|
143
|
+
? `[fail] ${counts}. Address the items marked [fail] above.`
|
|
144
|
+
: data.failed
|
|
145
|
+
? `[fail] ${counts}. --strict fails on every warning and on skipped work; fix them, or drop --strict to fail only on errors.`
|
|
146
|
+
: data.warnings > 0 || data.skipped.length > 0
|
|
147
|
+
? `[ok] ${counts}. No check failed; review the warnings and skipped work above (--strict fails on them).${notCompared}`
|
|
148
|
+
: notCompared
|
|
149
|
+
? `[ok] ${counts}. No check failed.${notCompared}`
|
|
150
|
+
: `[ok] ${counts}. Every check passed.`;
|
|
151
|
+
const identity = noPackage
|
|
152
|
+
? '(no package.json)'
|
|
153
|
+
: data.version != null
|
|
154
|
+
? `${data.name}@${data.version}`
|
|
155
|
+
: data.name;
|
|
156
|
+
emit(
|
|
157
|
+
// --strict lists each warning and each skipped check again, as the
|
|
158
|
+
// error it now is, in the issue format the checks use.
|
|
159
|
+
...(data.failures.length > 0
|
|
160
|
+
? [
|
|
161
|
+
section('Failing under --strict'),
|
|
162
|
+
records(data.failures, {
|
|
163
|
+
fields: [
|
|
164
|
+
'severity',
|
|
165
|
+
'check',
|
|
166
|
+
'code',
|
|
167
|
+
'component',
|
|
168
|
+
'prop',
|
|
169
|
+
'message',
|
|
170
|
+
'fix',
|
|
171
|
+
'file',
|
|
172
|
+
'line',
|
|
173
|
+
],
|
|
174
|
+
format: {severity: () => '[fail]'},
|
|
175
|
+
}),
|
|
176
|
+
]
|
|
177
|
+
: []),
|
|
178
|
+
section(
|
|
179
|
+
`Integration check: ${identity ?? '(local package)'}${data.strict ? ' (strict)' : ''}`,
|
|
180
|
+
),
|
|
181
|
+
records(data.checks, {
|
|
182
|
+
fields: ['check', 'failed', 'errors', 'warnings', 'skipped'],
|
|
183
|
+
}),
|
|
184
|
+
text(closing),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (result.data.failed) process.exitCode = 1;
|
|
188
|
+
return NO_RESULT_SET;
|
|
189
|
+
}
|
|
190
|
+
|
|
91
191
|
/** @param {import('commander').Command} program */
|
|
92
192
|
export function registerIntegration(program) {
|
|
93
193
|
/** @type {import('commander').Command} */
|
|
@@ -159,6 +259,12 @@ export function registerIntegration(program) {
|
|
|
159
259
|
});
|
|
160
260
|
addCmd.allowExcessArguments(false);
|
|
161
261
|
|
|
262
|
+
// It takes no package name, so a name passed by mistake is an error, not a
|
|
263
|
+
// silent check of the current folder.
|
|
264
|
+
defineCommand(integration, integrationCheckCommand, {
|
|
265
|
+
fn: integrationCheckFn,
|
|
266
|
+
action: async options => checkPackage(program, options),
|
|
267
|
+
}).allowExcessArguments(false);
|
|
162
268
|
defineCommand(integration, integrationVerifyCommand, {
|
|
163
269
|
fn: integrationPackCheckFn,
|
|
164
270
|
action: async () => verifyPackage(program),
|
|
@@ -16,9 +16,24 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
|
16
16
|
import * as fs from 'node:fs';
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import * as os from 'node:os';
|
|
19
|
-
import {
|
|
19
|
+
import {fileURLToPath} from 'node:url';
|
|
20
|
+
import {
|
|
21
|
+
isAstryxInitialized,
|
|
22
|
+
isAstryxPromptInReach,
|
|
23
|
+
} from '../../../foundation/agent-docs/agent-docs.mjs';
|
|
20
24
|
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
21
25
|
|
|
26
|
+
/** This source checkout: the tests run the CLI from it. */
|
|
27
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../..');
|
|
28
|
+
|
|
29
|
+
/** Is `dir` inside a git repository? The temp folder normally is not. */
|
|
30
|
+
function insideRepository(dir) {
|
|
31
|
+
for (let d = path.resolve(dir); ; d = path.dirname(d)) {
|
|
32
|
+
if (fs.existsSync(path.join(d, '.git'))) return true;
|
|
33
|
+
if (path.dirname(d) === d) return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
const MARKER = '<!-- ASTRYX:START -->';
|
|
23
38
|
const NUDGE = /finish setup and install the Astryx agent prompt/;
|
|
24
39
|
|
|
@@ -61,6 +76,41 @@ describe('isAstryxInitialized — centralized setup check (one place)', () => {
|
|
|
61
76
|
});
|
|
62
77
|
});
|
|
63
78
|
|
|
79
|
+
// Agents read the agent docs between the repository root and the folder they
|
|
80
|
+
// work in, so a monorepo initialized at its root is set up in every package.
|
|
81
|
+
describe('isAstryxPromptInReach — up to the repository root', () => {
|
|
82
|
+
it('finds init\'s block at the repository root from a workspace package', () => {
|
|
83
|
+
fs.mkdirSync(path.join(tmp, '.git'));
|
|
84
|
+
write('AGENTS.md', MARKER);
|
|
85
|
+
write('packages/app/package.json', '{"name":"app"}');
|
|
86
|
+
expect(isAstryxPromptInReach(path.join(tmp, 'packages/app'))).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('treats a worktree .git file as the repository root', () => {
|
|
90
|
+
write('.git', 'gitdir: /elsewhere\n');
|
|
91
|
+
write('CLAUDE.md', MARKER);
|
|
92
|
+
write('packages/app/package.json', '{"name":"app"}');
|
|
93
|
+
expect(isAstryxPromptInReach(path.join(tmp, 'packages/app'))).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('does not look above the repository root', () => {
|
|
97
|
+
write('AGENTS.md', MARKER);
|
|
98
|
+
fs.mkdirSync(path.join(tmp, 'repo/.git'), {recursive: true});
|
|
99
|
+
write('repo/pkg/package.json', '{"name":"pkg"}');
|
|
100
|
+
expect(isAstryxPromptInReach(path.join(tmp, 'repo/pkg'))).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it.skipIf(insideRepository(os.tmpdir()))(
|
|
104
|
+
'checks only the folder itself outside a repository',
|
|
105
|
+
() => {
|
|
106
|
+
write('AGENTS.md', MARKER);
|
|
107
|
+
write('pkg/package.json', '{"name":"pkg"}');
|
|
108
|
+
expect(isAstryxPromptInReach(tmp)).toBe(true);
|
|
109
|
+
expect(isAstryxPromptInReach(path.join(tmp, 'pkg'))).toBe(false);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
64
114
|
describe('enforcement layer 3 — per-command setup nudge', () => {
|
|
65
115
|
const asProject = () => write('package.json', '{"name":"t"}');
|
|
66
116
|
|
|
@@ -99,4 +149,32 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
|
|
|
99
149
|
asProject();
|
|
100
150
|
expect((await runCli(['init'], tmp)).stderr).not.toMatch(NUDGE);
|
|
101
151
|
});
|
|
152
|
+
|
|
153
|
+
it('is quiet in a workspace package when init\'s block is at the repository root', async () => {
|
|
154
|
+
fs.mkdirSync(path.join(tmp, '.git'));
|
|
155
|
+
write('AGENTS.md', MARKER);
|
|
156
|
+
write('packages/app/package.json', '{"name":"app"}');
|
|
157
|
+
const r = await runCli(['docs', 'tokens'], path.join(tmp, 'packages/app'));
|
|
158
|
+
expect(r.stderr).not.toMatch(NUDGE);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('still nudges in a workspace package with no block up to the repository root', async () => {
|
|
162
|
+
fs.mkdirSync(path.join(tmp, '.git'));
|
|
163
|
+
write('AGENTS.md', 'project notes, no astryx block');
|
|
164
|
+
write('packages/app/package.json', '{"name":"app"}');
|
|
165
|
+
const r = await runCli(['docs', 'tokens'], path.join(tmp, 'packages/app'));
|
|
166
|
+
expect(r.stderr).toMatch(NUDGE);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// The tests run the CLI from this checkout, so a project inside it is the
|
|
170
|
+
// contributor flow. The same project in the temp folder nudges (above).
|
|
171
|
+
it('is quiet inside the source checkout the CLI runs from', async () => {
|
|
172
|
+
const inside = fs.mkdtempSync(path.join(REPO, '.astryx-setup-nudge-'));
|
|
173
|
+
try {
|
|
174
|
+
fs.writeFileSync(path.join(inside, 'package.json'), '{"name":"t"}');
|
|
175
|
+
expect((await runCli(['docs', 'tokens'], inside)).stderr).not.toMatch(NUDGE);
|
|
176
|
+
} finally {
|
|
177
|
+
fs.rmSync(inside, {recursive: true, force: true});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
102
180
|
});
|
|
@@ -91,14 +91,16 @@ export function registerSwizzle(program) {
|
|
|
91
91
|
),
|
|
92
92
|
];
|
|
93
93
|
|
|
94
|
-
// StyleX build requirement — swizzled StyleX source
|
|
95
|
-
//
|
|
94
|
+
// StyleX build requirement — swizzled StyleX source throws on import
|
|
95
|
+
// ("Unexpected 'stylex.create' call at runtime") unless the consumer's
|
|
96
|
+
// build runs a StyleX compiler, so the page does not render at all.
|
|
96
97
|
if (usesStyleX) {
|
|
97
98
|
out.push(
|
|
98
99
|
text(
|
|
99
100
|
[
|
|
100
101
|
`${WARN} These components use StyleX and require a StyleX compiler in your build.`,
|
|
101
|
-
|
|
102
|
+
` Without one, importing them throws "Unexpected 'stylex.create' call at runtime"`,
|
|
103
|
+
' and the page does not render. See setup per framework:',
|
|
102
104
|
` ${run} docs styling`,
|
|
103
105
|
' Next.js note: the StyleX Babel plugin disables SWC and breaks next/font -',
|
|
104
106
|
' use an SWC-based StyleX transform instead (covered in the guide).',
|
|
@@ -15,8 +15,16 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
|
15
15
|
import * as fs from 'node:fs';
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import * as os from 'node:os';
|
|
18
|
+
import {createRequire} from 'node:module';
|
|
19
|
+
import {fileURLToPath} from 'node:url';
|
|
18
20
|
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
19
21
|
|
|
22
|
+
/** The StyleX runtime that Core ships with, resolved the way Core resolves it. */
|
|
23
|
+
const CORE_PACKAGE = path.resolve(
|
|
24
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
25
|
+
'../../../../core/package.json',
|
|
26
|
+
);
|
|
27
|
+
|
|
20
28
|
/**
|
|
21
29
|
* Build a fake @astryxdesign/core under <project>/node_modules with a single
|
|
22
30
|
* swizzleable Button component (bare `Button.tsx`, no doc).
|
|
@@ -303,12 +311,21 @@ describe('swizzle — StyleX build setup note (#3373)', () => {
|
|
|
303
311
|
const humanResult = await runCli(['swizzle', 'Styled', '-f'], project);
|
|
304
312
|
expect(humanResult.code).toBe(0);
|
|
305
313
|
expect(humanResult.stdout).toMatch(/StyleX compiler/i);
|
|
306
|
-
expect(humanResult.stdout).toMatch(/
|
|
314
|
+
expect(humanResult.stdout).toMatch(/importing them throws "Unexpected 'stylex\.create' call at runtime"/);
|
|
315
|
+
expect(humanResult.stdout).not.toMatch(/unstyled/i);
|
|
307
316
|
expect(humanResult.stdout).toMatch(/next\/font/i);
|
|
308
317
|
// The fixture has no `astryx` bin installed, so the note names the scoped package.
|
|
309
318
|
expect(humanResult.stdout).toMatch(/@astryxdesign\/cli docs styling/);
|
|
310
319
|
});
|
|
311
320
|
|
|
321
|
+
// The note quotes what StyleX does without a compiler; this keeps it true.
|
|
322
|
+
it('matches what the StyleX runtime does when styles are not compiled', () => {
|
|
323
|
+
const stylex = createRequire(CORE_PACKAGE)('@stylexjs/stylex');
|
|
324
|
+
expect(() => stylex.create({root: {color: 'red'}})).toThrow(
|
|
325
|
+
/Unexpected 'stylex\.create' call at runtime/,
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
312
329
|
it('does not print the StyleX note for components without StyleX', async () => {
|
|
313
330
|
buildStyleXCore(project);
|
|
314
331
|
writeProjectPackageJson(project);
|
|
@@ -341,6 +341,11 @@ const CASES = [
|
|
|
341
341
|
args: ['doctor', 'integration', 'validate'],
|
|
342
342
|
skipFieldChecks: true,
|
|
343
343
|
},
|
|
344
|
+
{
|
|
345
|
+
name: 'integration check',
|
|
346
|
+
args: ['integration', 'check'],
|
|
347
|
+
skipFieldChecks: true,
|
|
348
|
+
},
|
|
344
349
|
{
|
|
345
350
|
name: 'doctor integration components',
|
|
346
351
|
args: ['doctor', 'integration', 'components'],
|
|
@@ -30,7 +30,7 @@ export const doc = {
|
|
|
30
30
|
{
|
|
31
31
|
flag: '--family',
|
|
32
32
|
description:
|
|
33
|
-
'Build the selected extension family into one keyed CSS, JS, and declaration set. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
|
|
33
|
+
'Build the selected extension family into one keyed CSS, JS, and declaration set, written beside the family root theme file, which must be inside the working directory to build (ERR_PATH_TRAVERSAL); --check only reads, so it compares outputs anywhere. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
flag: '--family-key <key>',
|
|
@@ -41,7 +41,7 @@ export const doc = {
|
|
|
41
41
|
flag: '-o, --out <path>',
|
|
42
42
|
param: 'options.out',
|
|
43
43
|
description:
|
|
44
|
-
'Output CSS file path; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). A relative path
|
|
44
|
+
'Output CSS file path, relative or absolute; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). A build writes only inside the working directory, the defaults beside the source too (ERR_PATH_TRAVERSAL); --check writes nothing, so it compares outputs anywhere and refuses only a relative path that escapes the working directory. Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
flag: '--icons-specifier <specifier>',
|
package/clients/cli/index.mjs
CHANGED
|
@@ -26,7 +26,8 @@ import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
|
26
26
|
import {installJsonShim} from './lib/json-shim.mjs';
|
|
27
27
|
import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
|
|
28
28
|
import {doc as manifestDoc} from './commands/manifest.doc.mjs';
|
|
29
|
-
import {isAstryxInitialized} from '../../foundation/agent-docs/agent-docs.mjs';
|
|
29
|
+
import {isAstryxInitialized, isAstryxPromptInReach} from '../../foundation/agent-docs/agent-docs.mjs';
|
|
30
|
+
import {CLI_ROOT} from '../../foundation/fs/paths.mjs';
|
|
30
31
|
import * as debug from '../../foundation/debug/index.mjs';
|
|
31
32
|
|
|
32
33
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -98,6 +99,7 @@ export const JSON_SUPPORTED = new Set([
|
|
|
98
99
|
'theme targets',
|
|
99
100
|
'theme palette generate',
|
|
100
101
|
'integration add',
|
|
102
|
+
'integration check',
|
|
101
103
|
'integration verify',
|
|
102
104
|
'integration pack',
|
|
103
105
|
'upgrade',
|
|
@@ -276,6 +278,26 @@ const commands = [
|
|
|
276
278
|
];
|
|
277
279
|
|
|
278
280
|
const SETUP_NUDGE_EXEMPT = new Set(['init', 'agent-docs']);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Is `cwd` inside the CLI's own source checkout while the CLI runs from it?
|
|
284
|
+
* An installed CLI always lives under `node_modules`; a CLI running from source
|
|
285
|
+
* does not, and its checkout is the pnpm workspace that holds it. Developing
|
|
286
|
+
* Astryx is not an app to set up, so the setup nudge stays quiet there.
|
|
287
|
+
* @param {string} cwd
|
|
288
|
+
* @returns {boolean}
|
|
289
|
+
*/
|
|
290
|
+
function inOwnSourceCheckout(cwd) {
|
|
291
|
+
if (CLI_ROOT.split(path.sep).includes('node_modules')) return false;
|
|
292
|
+
let checkout = CLI_ROOT;
|
|
293
|
+
while (!fs.existsSync(path.join(checkout, 'pnpm-workspace.yaml'))) {
|
|
294
|
+
const parent = path.dirname(checkout);
|
|
295
|
+
if (parent === checkout) return false;
|
|
296
|
+
checkout = parent;
|
|
297
|
+
}
|
|
298
|
+
const rel = path.relative(checkout, path.resolve(cwd));
|
|
299
|
+
return rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
|
|
300
|
+
}
|
|
279
301
|
/** An integration package's manifest: the package is not an app to set up. */
|
|
280
302
|
const INTEGRATION_MANIFEST_FILES = [
|
|
281
303
|
'astryx.integration.ts',
|
|
@@ -527,8 +549,9 @@ export async function createProgram() {
|
|
|
527
549
|
|
|
528
550
|
/**
|
|
529
551
|
* Enforcement layer 3 — setup nudge. If this project hasn't run `astryx init`
|
|
530
|
-
* yet (no Astryx marker in any agent-doc file
|
|
531
|
-
* the user/agent that
|
|
552
|
+
* yet (no Astryx marker in any agent-doc file between the project and its
|
|
553
|
+
* repository root — see isAstryxPromptInReach), remind the user/agent that
|
|
554
|
+
* setup is missing.
|
|
532
555
|
*
|
|
533
556
|
* Uses `preAction` (not postAction) so it fires for EVERY valid command — even
|
|
534
557
|
* ones whose action errors or calls process.exit (postAction is skipped then).
|
|
@@ -538,7 +561,8 @@ export async function createProgram() {
|
|
|
538
561
|
* consumers parse stdout, not stderr — a stderr nudge would not reach them anyway.
|
|
539
562
|
* The core/cli postinstall layers already nudge at install time regardless of
|
|
540
563
|
* --json; a machine-readable nudge could later be an envelope field. Also skipped
|
|
541
|
-
* for the installer commands themselves
|
|
564
|
+
* for the installer commands themselves, outside a project (no package.json),
|
|
565
|
+
* and inside the CLI's own source checkout when it runs from there.
|
|
542
566
|
*/
|
|
543
567
|
program.hook('preAction', (thisCommand, actionCommand) => {
|
|
544
568
|
try {
|
|
@@ -548,7 +572,8 @@ export async function createProgram() {
|
|
|
548
572
|
if (!fs.existsSync(path.join(cwd, 'package.json'))) return; // not a project
|
|
549
573
|
// An integration package is not an app: `init` is not its next step.
|
|
550
574
|
if (INTEGRATION_MANIFEST_FILES.some(file => fs.existsSync(path.join(cwd, file)))) return;
|
|
551
|
-
if (
|
|
575
|
+
if (inOwnSourceCheckout(cwd)) return; // developing Astryx itself, not an app
|
|
576
|
+
if (isAstryxPromptInReach(cwd)) return; // already set up — stay quiet
|
|
552
577
|
// Same wording as the core/cli postinstall nudges. #4151's getCliInvocation()
|
|
553
578
|
// renders the correct form for THIS project — scoped `npx @astryxdesign/cli`
|
|
554
579
|
// one-off, or `<pm> astryx` when installed — never the bare `npx astryx` footgun.
|
|
@@ -68,8 +68,12 @@ function formatPropsTable(props) {
|
|
|
68
68
|
for (const p of props) {
|
|
69
69
|
const def = p.default ? `\`${mdCell(p.default)}\`` : '-';
|
|
70
70
|
const req = p.required ? ' **(required)**' : '';
|
|
71
|
+
// The text projection of the prop doc's `stability` field: an
|
|
72
|
+
// experimental prop is outside the stable compatibility promise.
|
|
73
|
+
const stability =
|
|
74
|
+
p.stability === 'experimental' ? ' **(experimental)**' : '';
|
|
71
75
|
lines.push(
|
|
72
|
-
`| \`${mdCell(p.name)}\` | \`${mdCell(p.type)}\` | ${def} | ${mdCell(p.description)}${req} |`,
|
|
76
|
+
`| \`${mdCell(p.name)}\` | \`${mdCell(p.type)}\` | ${def} | ${mdCell(p.description)}${req}${stability} |`,
|
|
73
77
|
);
|
|
74
78
|
}
|
|
75
79
|
return lines.join('\n');
|
|
@@ -20,6 +20,20 @@ export function discoverAgentDocs(targetDir: string): string[];
|
|
|
20
20
|
* @returns {boolean}
|
|
21
21
|
*/
|
|
22
22
|
export function isAstryxInitialized(targetDir?: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Does an agent working in `startDir` already have the Astryx prompt in reach?
|
|
25
|
+
*
|
|
26
|
+
* Coding agents read the agent docs between the repository root and the folder
|
|
27
|
+
* they work in. So inside a git repository, init's block in any folder from
|
|
28
|
+
* `startDir` up to the repository root counts: a workspace package of a
|
|
29
|
+
* monorepo initialized at its root is set up. Outside a repository only
|
|
30
|
+
* `startDir` itself is checked, as {@link isAstryxInitialized} does. Both the
|
|
31
|
+
* per-command nudge and the postinstall nudges ask this.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} [startDir=process.cwd()]
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
export function isAstryxPromptInReach(startDir?: string): boolean;
|
|
23
37
|
/**
|
|
24
38
|
* Pure decision: should a postinstall print the setup nudge? Shared by BOTH
|
|
25
39
|
* postinstall layers, so the matrix cannot drift between them, and unit-testable
|
|
@@ -96,6 +96,44 @@ export function isAstryxInitialized(targetDir = process.cwd()) {
|
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Does an agent working in `startDir` already have the Astryx prompt in reach?
|
|
101
|
+
*
|
|
102
|
+
* Coding agents read the agent docs between the repository root and the folder
|
|
103
|
+
* they work in. So inside a git repository, init's block in any folder from
|
|
104
|
+
* `startDir` up to the repository root counts: a workspace package of a
|
|
105
|
+
* monorepo initialized at its root is set up. Outside a repository only
|
|
106
|
+
* `startDir` itself is checked, as {@link isAstryxInitialized} does. Both the
|
|
107
|
+
* per-command nudge and the postinstall nudges ask this.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} [startDir=process.cwd()]
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
export function isAstryxPromptInReach(startDir = process.cwd()) {
|
|
113
|
+
const start = path.resolve(startDir);
|
|
114
|
+
const repositoryRoot = findRepositoryRoot(start);
|
|
115
|
+
if (!repositoryRoot) return isAstryxInitialized(start);
|
|
116
|
+
for (let dir = start; ; dir = path.dirname(dir)) {
|
|
117
|
+
if (isAstryxInitialized(dir)) return true;
|
|
118
|
+
if (dir === repositoryRoot) return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The nearest folder at or above `start` that holds `.git` (a folder, or the
|
|
124
|
+
* file a worktree uses), or null outside a repository.
|
|
125
|
+
* @param {string} start
|
|
126
|
+
* @returns {string | null}
|
|
127
|
+
*/
|
|
128
|
+
function findRepositoryRoot(start) {
|
|
129
|
+
for (let dir = start; ; ) {
|
|
130
|
+
if (fs.existsSync(path.join(dir, '.git'))) return dir;
|
|
131
|
+
const parent = path.dirname(dir);
|
|
132
|
+
if (parent === dir) return null;
|
|
133
|
+
dir = parent;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
99
137
|
/**
|
|
100
138
|
* The one-line next-step both postinstall nudges print. Kept here so layer 1
|
|
101
139
|
* (core) and layer 2 (cli) can never word it differently.
|
|
@@ -226,4 +226,5 @@ export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, on
|
|
|
226
226
|
}): string[];
|
|
227
227
|
import { discoverAgentDocs } from './agent-doc-state.mjs';
|
|
228
228
|
import { isAstryxInitialized } from './agent-doc-state.mjs';
|
|
229
|
-
|
|
229
|
+
import { isAstryxPromptInReach } from './agent-doc-state.mjs';
|
|
230
|
+
export { discoverAgentDocs, isAstryxInitialized, isAstryxPromptInReach };
|
|
@@ -42,15 +42,17 @@ import {
|
|
|
42
42
|
LEGACY_MARKER_END,
|
|
43
43
|
discoverAgentDocs,
|
|
44
44
|
isAstryxInitialized,
|
|
45
|
+
isAstryxPromptInReach,
|
|
45
46
|
} from './agent-doc-state.mjs';
|
|
46
47
|
|
|
47
48
|
// The agent-doc locations, markers, and the setup-state predicates
|
|
48
|
-
// (discoverAgentDocs, isAstryxInitialized) are the ONE
|
|
49
|
-
// live in the dependency-free leaf
|
|
50
|
-
// nudge (enforcement layer 2) can load
|
|
51
|
-
//
|
|
52
|
-
// clients/cli/index.mjs, tests)
|
|
53
|
-
|
|
49
|
+
// (discoverAgentDocs, isAstryxInitialized, isAstryxPromptInReach) are the ONE
|
|
50
|
+
// canonical contract. They live in the dependency-free leaf
|
|
51
|
+
// ./agent-doc-state.mjs so the postinstall nudge (enforcement layer 2) can load
|
|
52
|
+
// them safely at install time. Re-exported here so existing importers
|
|
53
|
+
// (init/upgrade commands, the layer-3 nudge in clients/cli/index.mjs, tests)
|
|
54
|
+
// keep their `from './agent-docs.mjs'` paths.
|
|
55
|
+
export {discoverAgentDocs, isAstryxInitialized, isAstryxPromptInReach};
|
|
54
56
|
|
|
55
57
|
const MAX_PROJECT_AGENT_DOC_LINES = 32;
|
|
56
58
|
const MANAGED_MARKER_TEXT = /(?:ASTRYX|XDS):(START|END)/u;
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
applyTemplateReplacements,
|
|
61
61
|
effectiveTemplateDiscovery,
|
|
62
62
|
discoverAllUnresolved as discoverTemplates,
|
|
63
|
+
discoverCoreTemplates,
|
|
63
64
|
discoverIntegrationTemplatesForOne,
|
|
64
65
|
} from '../discovery/template-adapter.mjs';
|
|
65
66
|
import {
|
|
@@ -821,6 +822,85 @@ export class Project {
|
|
|
821
822
|
});
|
|
822
823
|
}
|
|
823
824
|
|
|
825
|
+
/**
|
|
826
|
+
* The components and templates this project reads, as links name them: by
|
|
827
|
+
* owner provider id, kind, and name (a template by its id).
|
|
828
|
+
* @returns {Promise<import('../discovery/docs-discovery.mjs').LinkableContributions>}
|
|
829
|
+
*/
|
|
830
|
+
async #linkableContributions() {
|
|
831
|
+
/** @type {Map<string, string>} */
|
|
832
|
+
const providers = new Map([[CORE_PACKAGE, CORE_PACKAGE]]);
|
|
833
|
+
for (const integration of this.#loadedIntegrations) {
|
|
834
|
+
if (integration?.name) {
|
|
835
|
+
providers.set(
|
|
836
|
+
integration.name,
|
|
837
|
+
integration.providerId ?? integration.name,
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
const providerOf = (/** @type {string | undefined} */ pkg) =>
|
|
842
|
+
providers.get(pkg ?? CORE_PACKAGE) ?? pkg ?? CORE_PACKAGE;
|
|
843
|
+
// An active replacement answers to the Core name it replaces, so a link
|
|
844
|
+
// to that Core component has to select Core (spec:AST-035 FR11).
|
|
845
|
+
/** @type {Map<string, string>} */
|
|
846
|
+
const replacing = new Map();
|
|
847
|
+
if (this.#loadedIntegrations.length > 0) {
|
|
848
|
+
const {active} = await resolveComponentReplacements(
|
|
849
|
+
findCoreDir(this.#cwd),
|
|
850
|
+
this.#loadedIntegrations,
|
|
851
|
+
);
|
|
852
|
+
for (const each of active) {
|
|
853
|
+
replacing.set(`${each.package}\0${each.name}`, each.target);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const components = (await this.components()).map(record => {
|
|
857
|
+
const replaces = replacing.get(`${record.package}\0${record.name}`);
|
|
858
|
+
return {
|
|
859
|
+
providerId: providerOf(record.package),
|
|
860
|
+
package: record.package,
|
|
861
|
+
name: record.name,
|
|
862
|
+
docPath: record.docPath,
|
|
863
|
+
...(replaces == null ? {} : {replaces}),
|
|
864
|
+
};
|
|
865
|
+
});
|
|
866
|
+
/** @param {any} template @param {string} pkg */
|
|
867
|
+
const linkable = (template, pkg) => ({
|
|
868
|
+
providerId: providerOf(pkg),
|
|
869
|
+
package: pkg,
|
|
870
|
+
type: template.type,
|
|
871
|
+
id: template.dirName,
|
|
872
|
+
name: template.name,
|
|
873
|
+
...(template.displayName == null
|
|
874
|
+
? {}
|
|
875
|
+
: {displayName: template.displayName}),
|
|
876
|
+
...(template.description == null
|
|
877
|
+
? {}
|
|
878
|
+
: {description: template.description}),
|
|
879
|
+
...(typeof template.replaces === 'string'
|
|
880
|
+
? {replaces: template.replaces}
|
|
881
|
+
: {}),
|
|
882
|
+
});
|
|
883
|
+
/** @param {any} template */
|
|
884
|
+
const isPageOrBlock = template =>
|
|
885
|
+
template.type === 'page' || template.type === 'block';
|
|
886
|
+
// Every Core template, a replaced one too, since `--package
|
|
887
|
+
// @astryxdesign/core` still opens it; everyone else's as unqualified
|
|
888
|
+
// lookups see them, an active replacement with the id it takes over.
|
|
889
|
+
const templates = [
|
|
890
|
+
...(await discoverCoreTemplates())
|
|
891
|
+
.filter(isPageOrBlock)
|
|
892
|
+
.map(template => linkable(template, CORE_PACKAGE)),
|
|
893
|
+
.../** @type {any[]} */ (await this.templates())
|
|
894
|
+
.filter(
|
|
895
|
+
template =>
|
|
896
|
+
isPageOrBlock(template) &&
|
|
897
|
+
(template.package ?? CORE_PACKAGE) !== CORE_PACKAGE,
|
|
898
|
+
)
|
|
899
|
+
.map(template => linkable(template, template.package)),
|
|
900
|
+
];
|
|
901
|
+
return {components, templates};
|
|
902
|
+
}
|
|
903
|
+
|
|
824
904
|
/**
|
|
825
905
|
* The CLI's own doc topics plus the ones the configured integrations
|
|
826
906
|
* contribute, resolved into one catalog: additions, replacements (with the
|
|
@@ -838,6 +918,8 @@ export class Project {
|
|
|
838
918
|
async docs() {
|
|
839
919
|
return this.#memo('docs', async () => {
|
|
840
920
|
const catalog = DocsCatalog.fromBuiltins();
|
|
921
|
+
// Links name component, page, and block docs too (spec:AST-047 FR9).
|
|
922
|
+
catalog.setContributions(() => this.#linkableContributions());
|
|
841
923
|
let rank = 0;
|
|
842
924
|
|
|
843
925
|
for (const integration of this.#loadedIntegrations) {
|
|
@@ -46,6 +46,7 @@ export const AUTHORING_SELF_DOCS = [
|
|
|
46
46
|
'doctypes/reference/reference.doc.mjs',
|
|
47
47
|
'doctypes/schema/schema.doc.mjs',
|
|
48
48
|
'doctypes/template/template.doc.mjs',
|
|
49
|
+
'doctypes/template/integration-template.doc.mjs',
|
|
49
50
|
'doctypes/theme/theme.doc.mjs',
|
|
50
51
|
];
|
|
51
52
|
|
|
@@ -192,7 +193,7 @@ export function buildAuthoringReferenceDoc(docs) {
|
|
|
192
193
|
title: 'Authoring Reference',
|
|
193
194
|
category: 'guide',
|
|
194
195
|
description:
|
|
195
|
-
'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type.',
|
|
196
|
+
'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The step-by-step guides are under `astryx docs cli/integrations`.',
|
|
196
197
|
sections: docs.map(selfDocSection),
|
|
197
198
|
});
|
|
198
199
|
}
|