@astryxdesign/cli 0.7.2-canary.e2909cf → 0.7.2-canary.e4bdd70
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/integration-export-name.test.mjs +644 -0
- 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/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 +51 -97
- 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 +93 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +133 -22
- package/api/swizzle/copy/copy.mjs +28 -14
- package/api/theme/build/build.family.test.mjs +54 -6
- package/api/theme/build/build.mjs +114 -17
- package/api/theme/themeBuild.doc.mjs +4 -4
- 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/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/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/command/command.doc.mjs +14 -12
- package/authoring/doctypes/component/component.doc.mjs +29 -26
- 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 +155 -21
- 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/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-build.doc.mjs +2 -2
- package/clients/cli/index.mjs +1 -0
- package/foundation/config/project.mjs +82 -0
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/component-discovery.d.mts +178 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +659 -62
- 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/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 +6 -0
- 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/package.json +9 -9
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file SchemaDoc for the `IntegrationTemplateDoc` doc-type — the template
|
|
5
|
+
* metadata an integration package ships: TemplateDoc, with the package's own
|
|
6
|
+
* `category`. Colocated with the type it describes (`type.ts`).
|
|
7
|
+
* @position packages/cli/authoring/doctypes/template — doc-type documentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** @type {import('@astryxdesign/cli/authoring').SchemaDoc} */
|
|
11
|
+
export const doc = {
|
|
12
|
+
type: 'schema',
|
|
13
|
+
name: 'integration-template-doc',
|
|
14
|
+
displayName: 'IntegrationTemplateDoc',
|
|
15
|
+
namespace: 'authoring',
|
|
16
|
+
description:
|
|
17
|
+
"The doc-type for an integration package's template metadata. It has every " +
|
|
18
|
+
"TemplateDoc field and rule, except that `category` is the package's own " +
|
|
19
|
+
'string instead of a TemplateCategory value.',
|
|
20
|
+
appliesTo: '<Name>.doc.mjs',
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
name: 'category',
|
|
24
|
+
type: 'string',
|
|
25
|
+
description:
|
|
26
|
+
"The package's own category. Bare `astryx template` groups the package's templates by it.",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
examples: [
|
|
30
|
+
{
|
|
31
|
+
label: 'Block template in an integration package',
|
|
32
|
+
code: `/** @type {import('@astryxdesign/cli/authoring').IntegrationTemplateDoc} */
|
|
33
|
+
export default {
|
|
34
|
+
type: 'block',
|
|
35
|
+
name: 'acme-pricing-table',
|
|
36
|
+
displayName: 'Pricing Table',
|
|
37
|
+
description: 'Plans side by side, with the chosen plan highlighted.',
|
|
38
|
+
aspectRatio: 16 / 9,
|
|
39
|
+
category: 'Acme - Commerce',
|
|
40
|
+
};`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
notes: [
|
|
44
|
+
{
|
|
45
|
+
type: 'prose',
|
|
46
|
+
text: "Every other field, and the page and block variants, are TemplateDoc's. Core's own templates use TemplateDoc, whose `category` is a TemplateCategory value. `astryx doctor integration validate` checks an integration's template docs against IntegrationTemplateDoc.",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
@@ -18,7 +18,9 @@ export type BlockTemplateDoc = import("./type.js").BlockTemplateDoc;
|
|
|
18
18
|
* Templates as they load. Integration templates already published omit
|
|
19
19
|
* `displayName` and `aspectRatio` and group themselves under their own
|
|
20
20
|
* `category`, so the loader accepts those (template discovery falls back to an
|
|
21
|
-
* aspect ratio of 1). The published
|
|
21
|
+
* aspect ratio of 1). The published `TemplateDoc` keeps asking authors for all
|
|
22
|
+
* three; `IntegrationTemplateDoc`, for an integration package's templates,
|
|
23
|
+
* takes the package's own `category`.
|
|
22
24
|
*/
|
|
23
25
|
export type LoadedPageTemplateDoc = Omit<PageTemplateDoc, "displayName" | "category"> & {
|
|
24
26
|
displayName?: string;
|
|
@@ -28,7 +30,9 @@ export type LoadedPageTemplateDoc = Omit<PageTemplateDoc, "displayName" | "categ
|
|
|
28
30
|
* Templates as they load. Integration templates already published omit
|
|
29
31
|
* `displayName` and `aspectRatio` and group themselves under their own
|
|
30
32
|
* `category`, so the loader accepts those (template discovery falls back to an
|
|
31
|
-
* aspect ratio of 1). The published
|
|
33
|
+
* aspect ratio of 1). The published `TemplateDoc` keeps asking authors for all
|
|
34
|
+
* three; `IntegrationTemplateDoc`, for an integration package's templates,
|
|
35
|
+
* takes the package's own `category`.
|
|
32
36
|
*/
|
|
33
37
|
export type LoadedBlockTemplateDoc = Omit<BlockTemplateDoc, "displayName" | "aspectRatio" | "category"> & {
|
|
34
38
|
displayName?: string;
|
|
@@ -78,7 +78,9 @@ const blockTemplateSchema = z
|
|
|
78
78
|
* Templates as they load. Integration templates already published omit
|
|
79
79
|
* `displayName` and `aspectRatio` and group themselves under their own
|
|
80
80
|
* `category`, so the loader accepts those (template discovery falls back to an
|
|
81
|
-
* aspect ratio of 1). The published
|
|
81
|
+
* aspect ratio of 1). The published `TemplateDoc` keeps asking authors for all
|
|
82
|
+
* three; `IntegrationTemplateDoc`, for an integration package's templates,
|
|
83
|
+
* takes the package's own `category`.
|
|
82
84
|
*
|
|
83
85
|
* @typedef {Omit<PageTemplateDoc, 'displayName' | 'category'>
|
|
84
86
|
* & {displayName?: string, category?: string}} LoadedPageTemplateDoc
|
|
@@ -31,14 +31,14 @@ export const doc = {
|
|
|
31
31
|
name: 'name',
|
|
32
32
|
type: 'string',
|
|
33
33
|
description:
|
|
34
|
-
|
|
34
|
+
"Stable identifier for block templates; change `displayName`, not `name`, to edit their visible label in the gallery. For page templates it is a human-readable label, while the existing template-directory/CLI slug owns the default registry path. `astryx template --list` and `astryx search` print `name` as the label of both, and `astryx template <id>` finds a template by its id, never by `name`: an integration template's id is its path under the templates folder without the `.doc.*` suffix.",
|
|
35
35
|
required: true,
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
name: 'displayName',
|
|
39
39
|
type: 'string',
|
|
40
40
|
description:
|
|
41
|
-
"Human-readable label for the gallery
|
|
41
|
+
"Human-readable label for the gallery. The CLI prints `name` instead; `astryx template --list --json` also returns an integration template's `displayName`. Spaces out block names that mirror a PascalCase component ('ChatMessageMetadata' → 'Chat Message Metadata').",
|
|
42
42
|
required: true,
|
|
43
43
|
},
|
|
44
44
|
{
|
|
@@ -162,7 +162,7 @@ export default {
|
|
|
162
162
|
notes: [
|
|
163
163
|
{
|
|
164
164
|
type: 'prose',
|
|
165
|
-
text: 'When it loads, a template may leave out `displayName` and `aspectRatio` and use its own `category`, as integration templates already published do; a block with no `aspectRatio` previews at 1. Write to the type anyway; it is the contract
|
|
165
|
+
text: 'When it loads, a template may leave out `displayName` and `aspectRatio` and use its own `category`, as integration templates already published do; a block with no `aspectRatio` previews at 1. Write to the type anyway; it is the contract, and `astryx doctor integration validate` warns about an integration template that leaves out `displayName` or `aspectRatio`.',
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
type: 'prose',
|
|
@@ -201,3 +201,25 @@ export type TemplateCategory =
|
|
|
201
201
|
| 'Shell - Blank';
|
|
202
202
|
|
|
203
203
|
export type TemplateDoc = PageTemplateDoc | BlockTemplateDoc;
|
|
204
|
+
|
|
205
|
+
/** A page template an integration package ships, with its own `category`. */
|
|
206
|
+
export interface IntegrationPageTemplateDoc extends Omit<
|
|
207
|
+
PageTemplateDoc,
|
|
208
|
+
'category'
|
|
209
|
+
> {
|
|
210
|
+
category?: string;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** A block template an integration package ships, with its own `category`. */
|
|
214
|
+
export interface IntegrationBlockTemplateDoc extends Omit<
|
|
215
|
+
BlockTemplateDoc,
|
|
216
|
+
'category'
|
|
217
|
+
> {
|
|
218
|
+
category?: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The doc-type for an integration package's template metadata: TemplateDoc,
|
|
222
|
+
* with `category` the package's own string (bare `astryx template` groups
|
|
223
|
+
* the package's templates by it) instead of a TemplateCategory value. */
|
|
224
|
+
export type IntegrationTemplateDoc =
|
|
225
|
+
IntegrationPageTemplateDoc | IntegrationBlockTemplateDoc;
|
|
@@ -12,7 +12,7 @@ export const doc = {
|
|
|
12
12
|
displayName: 'ThemeDoc',
|
|
13
13
|
namespace: 'authoring',
|
|
14
14
|
description:
|
|
15
|
-
|
|
15
|
+
"The strongly typed descriptor for one source theme: bundled with the CLI, shipped by an integration, or kept in the project's src/themes, where `theme eject` writes one.",
|
|
16
16
|
appliesTo: '<theme-source-stem>.doc.mjs',
|
|
17
17
|
fields: [
|
|
18
18
|
{
|
|
@@ -65,7 +65,7 @@ export default {
|
|
|
65
65
|
notes: [
|
|
66
66
|
{
|
|
67
67
|
type: 'prose',
|
|
68
|
-
text:
|
|
68
|
+
text: "ThemeDoc is static metadata. The descriptor holds one default-exported object of literal string and boolean fields, directly under a `/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */` comment, and nothing else: without that comment, or with any other statement, it fails to load. Discovery validates it without executing the descriptor or theme source.",
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
type: 'prose',
|
|
@@ -15,7 +15,7 @@ export const doc = {
|
|
|
15
15
|
displayName: 'GapReportHandler',
|
|
16
16
|
namespace: 'authoring',
|
|
17
17
|
description:
|
|
18
|
-
|
|
18
|
+
"A handler for `astryx gap-report`: a plain object with an `audience` and a `handle` function. Set it as `gapReport` in astryx.config, or export it as `gapReport` from an integration manifest. Handlers add up rather than replace each other: the project handler first, then each loaded integration's in load order (the `integrations` list, then autolinked ones), whichever package the report is about.",
|
|
19
19
|
appliesTo:
|
|
20
20
|
'`gapReport` in astryx.config.*, or the `gapReport` named export of astryx.integration.*',
|
|
21
21
|
fields: [
|
|
@@ -104,7 +104,7 @@ export const doc = {
|
|
|
104
104
|
name: 'report.target.version',
|
|
105
105
|
type: 'string',
|
|
106
106
|
description:
|
|
107
|
-
|
|
107
|
+
"The integration's version from its package.json. Null for Core, and when the package.json has none.",
|
|
108
108
|
required: true,
|
|
109
109
|
},
|
|
110
110
|
{
|
|
@@ -12,7 +12,7 @@ export const doc = {
|
|
|
12
12
|
displayName: 'Provider and artifact identity',
|
|
13
13
|
namespace: 'authoring',
|
|
14
14
|
description:
|
|
15
|
-
|
|
15
|
+
"Separates stable provider/artifact identity from package instances and runtime lifecycle state. Provider identity names integration manifests and settles provider conflicts. Docs-tree nodes are identified by doc identity; the tree covers the CLI's docs and each integration's. Artifact IDs for kinds outside the docs tree, provider instances, and compiler-input entries are defined for every provider-owned artifact; no command builds them.",
|
|
16
16
|
appliesTo:
|
|
17
17
|
'Integration manifests, provider conflicts, and docs-tree node ids.',
|
|
18
18
|
fields: [
|
|
@@ -41,14 +41,14 @@ export const doc = {
|
|
|
41
41
|
name: 'ProviderInstance',
|
|
42
42
|
type: '{ id; providerId; packageName; packageVersion; sourceDigest }',
|
|
43
43
|
description:
|
|
44
|
-
'One immutable package version and source/content digest.
|
|
44
|
+
'One immutable package version and source/content digest. It carries no installed, configured, loaded, selected, or healthy state.',
|
|
45
45
|
required: true,
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
name: 'AuthoredDocEntry',
|
|
49
49
|
type: '{ id; provider; kind; stableName; source; authored }',
|
|
50
50
|
description:
|
|
51
|
-
'Normalized compiler input.
|
|
51
|
+
'Normalized compiler input. `stableName`, the key in its doc ID, is given separately from the authored display name; source paths remain package-relative, and provider provenance plus the authored snapshot are immutable.',
|
|
52
52
|
required: true,
|
|
53
53
|
},
|
|
54
54
|
],
|
package/authoring/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type {HookDoc} from './doctypes/types.js'; // useToast.doc.mjs
|
|
|
28
28
|
export type {FunctionDoc} from './doctypes/types.js'; // search.doc.mjs (hook | api)
|
|
29
29
|
export type {ReferenceDoc} from './doctypes/types.js'; // theming.doc.mjs
|
|
30
30
|
export type {TemplateDoc} from './doctypes/types.js'; // Foo.doc.mjs
|
|
31
|
+
export type {IntegrationTemplateDoc} from './doctypes/types.js'; // an integration's Foo.doc.mjs
|
|
31
32
|
export type {SchemaDoc} from './doctypes/types.js'; // config.doc.mjs (object shape)
|
|
32
33
|
export type {CommandDoc} from './doctypes/types.js'; // search.doc.mjs (CLI command)
|
|
33
34
|
export type {EnumDoc} from './doctypes/types.js'; // error-codes.doc.mjs (vocabulary)
|
|
@@ -51,7 +51,7 @@ export const doc = {
|
|
|
51
51
|
name: 'docs',
|
|
52
52
|
type: 'string',
|
|
53
53
|
description:
|
|
54
|
-
'The folder that holds your doc topics, relative to package.json. Every {topic}.doc.{ts,mjs,js}
|
|
54
|
+
'The folder that holds your doc topics, relative to package.json. Every {topic}.doc.{ts,mjs,js} under it shows up in `astryx docs`: a flat topic next to the built-in topics, where it can set `replaces` or `extends` to take over a built-in topic or add to it, and a guide with `placement` at its own route in the docs tree, under a namespace doc of the same package.',
|
|
55
55
|
example: "'./docs'",
|
|
56
56
|
},
|
|
57
57
|
{
|
|
@@ -71,9 +71,17 @@ export const doc = {
|
|
|
71
71
|
{
|
|
72
72
|
name: 'issuesUrl',
|
|
73
73
|
type: 'string',
|
|
74
|
-
description:
|
|
74
|
+
description:
|
|
75
|
+
"Where people file issues for this package: an absolute URL, such as its GitHub issues page. A value that is not a URL fails the whole manifest; the scheme is not checked. Nothing warns when it is missing, but without it `astryx gap-report` about this package fails unless the project or a loaded integration has a gapReport handler, and `astryx swizzle` prints no feedback link for this package's components. When no handler is loaded, a github.com issues URL is filed with the GitHub CLI once the reporter passes `--confirm-public`; any other URL is returned for the reporter to open.",
|
|
75
76
|
example: "'https://github.com/acme/widgets/issues'",
|
|
76
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: 'docsiteUrl',
|
|
80
|
+
type: 'string',
|
|
81
|
+
description:
|
|
82
|
+
"Link to this integration's docs site, such as its home page. Catalogs that list the package may show it.",
|
|
83
|
+
example: "'https://widgets.acme.dev'",
|
|
84
|
+
},
|
|
77
85
|
],
|
|
78
86
|
examples: [
|
|
79
87
|
{
|
|
@@ -111,14 +119,14 @@ export const doc = {
|
|
|
111
119
|
},
|
|
112
120
|
{
|
|
113
121
|
type: 'prose',
|
|
114
|
-
text: 'A themes root is
|
|
122
|
+
text: 'A themes root is version-gated: an older CLI lists and adds none of its themes. Depending on its age, that CLI ignores `themes` with an `unknown_manifest_key` warning and loads every other contribution kind, reports the root as invalid because it expects the earlier `manifest.json` theme catalog, or, if it is old enough to treat an unknown field as an error, rejects the whole manifest, so the package contributes nothing there.',
|
|
115
123
|
},
|
|
116
124
|
{
|
|
117
125
|
type: 'prose',
|
|
118
126
|
text:
|
|
119
127
|
'Validate the manifest with `astryx doctor integration validate`. At the ' +
|
|
120
128
|
'load boundary, a known field of the wrong type is an error, issuesUrl ' +
|
|
121
|
-
'must be
|
|
129
|
+
'and docsiteUrl must be valid URLs, and unknown fields become warnings so an older CLI ' +
|
|
122
130
|
'can still load the fields it understands. Before publishing, also run ' +
|
|
123
131
|
'`templates`, `components`, and `docs` under the same `doctor integration` ' +
|
|
124
132
|
'group. Those leaves compare authored identities with Core and explain ' +
|
|
@@ -87,6 +87,16 @@ describe('parseIntegration (load boundary)', () => {
|
|
|
87
87
|
expect(reason({issuesUrl: 'nope'})).toContain('issuesUrl');
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
+
it('accepts a docsiteUrl as a known key', () => {
|
|
91
|
+
const manifest = {docsiteUrl: 'https://widgets.example.com'};
|
|
92
|
+
expect(parseIntegration(manifest)).toEqual(manifest);
|
|
93
|
+
expect(unknownIntegrationKeys(manifest)).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('rejects a non-URL docsiteUrl', () => {
|
|
97
|
+
expect(reason({docsiteUrl: 'nope'})).toContain('docsiteUrl');
|
|
98
|
+
});
|
|
99
|
+
|
|
90
100
|
it('rejects a noncanonical providerId', () => {
|
|
91
101
|
expect(reason({providerId: 'Acme Widgets'})).toContain('providerId');
|
|
92
102
|
});
|
|
@@ -39,6 +39,7 @@ export const integrationBaseSchema: z.ZodObject<{
|
|
|
39
39
|
docs: z.ZodOptional<z.ZodString>;
|
|
40
40
|
themes: z.ZodOptional<z.ZodString>;
|
|
41
41
|
issuesUrl: z.ZodOptional<z.ZodString>;
|
|
42
|
+
docsiteUrl: z.ZodOptional<z.ZodString>;
|
|
42
43
|
}, z.core.$strip>;
|
|
43
44
|
export const integrationSchema: z.ZodObject<{
|
|
44
45
|
providerId: z.ZodOptional<z.ZodString>;
|
|
@@ -48,6 +49,7 @@ export const integrationSchema: z.ZodObject<{
|
|
|
48
49
|
docs: z.ZodOptional<z.ZodString>;
|
|
49
50
|
themes: z.ZodOptional<z.ZodString>;
|
|
50
51
|
issuesUrl: z.ZodOptional<z.ZodString>;
|
|
52
|
+
docsiteUrl: z.ZodOptional<z.ZodString>;
|
|
51
53
|
agentDocs: z.ZodOptional<z.ZodObject<{
|
|
52
54
|
append: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
53
55
|
}, z.core.$strip>>;
|
|
@@ -78,6 +78,7 @@ export const integrationBaseSchema = z.object({
|
|
|
78
78
|
docs: z.string().optional(),
|
|
79
79
|
themes: z.string().optional(),
|
|
80
80
|
issuesUrl: z.string().url().optional(),
|
|
81
|
+
docsiteUrl: z.string().url().optional(),
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
// Unknown top-level keys are stripped rather than rejected. `.strict()` made a key from a
|
|
@@ -44,6 +44,10 @@ export interface AstryxIntegration {
|
|
|
44
44
|
};
|
|
45
45
|
/** Where to file issues/feedback for this integration. */
|
|
46
46
|
issuesUrl?: string;
|
|
47
|
+
/** Link to this integration's docs site, such as its home page. The CLI
|
|
48
|
+
* accepts it as a known field; catalogs that list the package may show
|
|
49
|
+
* it. */
|
|
50
|
+
docsiteUrl?: string;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/**
|
|
@@ -168,7 +168,9 @@ describe('theme build report', () => {
|
|
|
168
168
|
}
|
|
169
169
|
expect(count(out, 'Install in your app')).toBe(1);
|
|
170
170
|
expect(count(out, 'Or with a <link> tag:')).toBe(1);
|
|
171
|
-
expect(
|
|
171
|
+
expect(
|
|
172
|
+
count(out, "import { Theme } from '@astryxdesign/core/theme';"),
|
|
173
|
+
).toBe(2);
|
|
172
174
|
expect(count(out, '[note]')).toBe(1);
|
|
173
175
|
expect(count(out, 'note: Font')).toBe(0);
|
|
174
176
|
expect(count(out, 'fonts.googleapis.com/css2')).toBe(1);
|
|
@@ -397,7 +397,7 @@ function printThemeApp(result) {
|
|
|
397
397
|
emit(
|
|
398
398
|
text('Wire the generated module once in the app root:'),
|
|
399
399
|
code(
|
|
400
|
-
"import {Theme} from '@astryxdesign/core';\n" +
|
|
400
|
+
"import {Theme} from '@astryxdesign/core/theme';\n" +
|
|
401
401
|
`import {themes, defaultThemeSlug} from './${stem}';\n\n` +
|
|
402
402
|
'<Theme theme={themes[defaultThemeSlug]}>\n <App />\n</Theme>',
|
|
403
403
|
),
|
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
* - Multi-file write is atomic: if one file fails, none are left behind.
|
|
10
10
|
* - An `--out` escape, or an output directory that cannot be created, fails
|
|
11
11
|
* with a registered error code.
|
|
12
|
-
* - Every output stays inside the working directory: an
|
|
13
|
-
* path or a theme file outside it fails before anything
|
|
14
|
-
* absolute paths inside it build, also through a
|
|
12
|
+
* - Every output a build writes stays inside the working directory: an
|
|
13
|
+
* absolute `--out` path or a theme file outside it fails before anything
|
|
14
|
+
* is written, while absolute paths inside it build, also through a
|
|
15
|
+
* symlinked spelling. `--check` writes nothing, so it compares those
|
|
16
|
+
* outside outputs read-only, and a stale report for them ends with a
|
|
17
|
+
* remedy that works: the directory to build and check from.
|
|
15
18
|
*
|
|
16
19
|
* Runs the CLI in-process (shared runCli harness) against a tiny synthetic theme file.
|
|
17
20
|
*/
|
|
@@ -25,6 +28,7 @@ import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
|
25
28
|
import {themeBuild} from '../../../api/theme/build/build.mjs';
|
|
26
29
|
import {AstryxError} from '../../../api/error.mjs';
|
|
27
30
|
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
31
|
+
import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
|
|
28
32
|
|
|
29
33
|
function writeTheme(dir, name) {
|
|
30
34
|
fs.mkdirSync(dir, {recursive: true});
|
|
@@ -62,7 +66,9 @@ describe('theme build path safety', () => {
|
|
|
62
66
|
);
|
|
63
67
|
|
|
64
68
|
expect(result.code).not.toBe(0);
|
|
65
|
-
expect(result.stderr + result.stdout).toMatch(
|
|
69
|
+
expect(result.stderr + result.stdout).toMatch(
|
|
70
|
+
/path separator|invalid theme name/i,
|
|
71
|
+
);
|
|
66
72
|
|
|
67
73
|
// The traversal target must not exist.
|
|
68
74
|
expect(fs.existsSync(path.join(tmpDir, 'escaped.js'))).toBe(false);
|
|
@@ -95,7 +101,10 @@ describe('theme build path safety', () => {
|
|
|
95
101
|
|
|
96
102
|
const themeFile = writeTheme(themesDir, '../leak');
|
|
97
103
|
|
|
98
|
-
await runCli(
|
|
104
|
+
await runCli(
|
|
105
|
+
['theme', 'build', path.relative(project, themeFile)],
|
|
106
|
+
project,
|
|
107
|
+
);
|
|
99
108
|
|
|
100
109
|
// Neither the CSS nor any sibling JS should have been written.
|
|
101
110
|
const entries = fs.readdirSync(themesDir).sort();
|
|
@@ -117,41 +126,88 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
117
126
|
return {result, envelope: JSON.parse(result.stdout)};
|
|
118
127
|
}
|
|
119
128
|
|
|
120
|
-
it('reports
|
|
129
|
+
it('reports a relative --out path outside the project as ERR_PATH_TRAVERSAL, with --check too', async () => {
|
|
121
130
|
const project = path.join(tmpDir, 'project');
|
|
122
131
|
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
123
132
|
|
|
133
|
+
for (const extra of [[], ['--check']]) {
|
|
134
|
+
const {result, envelope} = await buildJson(
|
|
135
|
+
[
|
|
136
|
+
path.relative(project, themeFile),
|
|
137
|
+
'--out',
|
|
138
|
+
'../escaped.css',
|
|
139
|
+
...extra,
|
|
140
|
+
],
|
|
141
|
+
project,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(result.code).toBe(1);
|
|
145
|
+
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
146
|
+
expect(registered.has(envelope.code)).toBe(true);
|
|
147
|
+
}
|
|
148
|
+
expect(fs.existsSync(path.join(tmpDir, 'escaped.css'))).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('reports an absolute --out path outside the project as ERR_PATH_TRAVERSAL', async () => {
|
|
152
|
+
const project = path.join(tmpDir, 'project');
|
|
153
|
+
const outside = path.join(tmpDir, 'outside');
|
|
154
|
+
fs.mkdirSync(outside, {recursive: true});
|
|
155
|
+
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
156
|
+
const out = path.join(outside, 'ocean.css');
|
|
157
|
+
|
|
124
158
|
const {result, envelope} = await buildJson(
|
|
125
|
-
[path.relative(project, themeFile), '--out',
|
|
159
|
+
[path.relative(project, themeFile), '--out', out],
|
|
126
160
|
project,
|
|
127
161
|
);
|
|
128
162
|
|
|
129
163
|
expect(result.code).toBe(1);
|
|
130
164
|
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
131
|
-
expect(
|
|
132
|
-
expect(fs.existsSync(path.join(tmpDir, 'escaped.css'))).toBe(false);
|
|
165
|
+
expect(fs.readdirSync(outside)).toEqual([]);
|
|
133
166
|
});
|
|
134
167
|
|
|
135
|
-
it('
|
|
168
|
+
it('checks an absolute --out path outside the project read-only', async () => {
|
|
136
169
|
const project = path.join(tmpDir, 'project');
|
|
137
170
|
const outside = path.join(tmpDir, 'outside');
|
|
138
171
|
fs.mkdirSync(outside, {recursive: true});
|
|
139
|
-
const themeFile =
|
|
172
|
+
const themeFile = path.relative(
|
|
173
|
+
project,
|
|
174
|
+
writeTheme(path.join(project, 'themes'), 'ocean'),
|
|
175
|
+
);
|
|
140
176
|
const out = path.join(outside, 'ocean.css');
|
|
141
177
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
178
|
+
const missing = await buildJson(
|
|
179
|
+
[themeFile, '--out', out, '--check'],
|
|
180
|
+
project,
|
|
181
|
+
);
|
|
182
|
+
expect(missing.result.code).toBe(1);
|
|
183
|
+
expect(missing.envelope.type).toBe('theme.build.check');
|
|
184
|
+
expect(missing.envelope.data.upToDate).toBe(false);
|
|
185
|
+
expect(fs.readdirSync(outside)).toEqual([]);
|
|
147
186
|
|
|
148
|
-
|
|
149
|
-
|
|
187
|
+
// Outputs built inside the project compare equal once copied outside:
|
|
188
|
+
// only the volatile Command line, which --check ignores, names --out.
|
|
189
|
+
const dist = path.join(project, 'dist');
|
|
190
|
+
const built = await buildJson(
|
|
191
|
+
[themeFile, '--out', path.join(dist, 'ocean.css')],
|
|
192
|
+
project,
|
|
193
|
+
);
|
|
194
|
+
expect(built.result.code).toBe(0);
|
|
195
|
+
for (const file of fs.readdirSync(dist)) {
|
|
196
|
+
fs.copyFileSync(path.join(dist, file), path.join(outside, file));
|
|
150
197
|
}
|
|
151
|
-
|
|
198
|
+
const copied = fs.readdirSync(outside).sort();
|
|
199
|
+
|
|
200
|
+
const current = await buildJson(
|
|
201
|
+
[themeFile, '--out', out, '--check'],
|
|
202
|
+
project,
|
|
203
|
+
);
|
|
204
|
+
expect(current.result.code).toBe(0);
|
|
205
|
+
expect(current.envelope.type).toBe('theme.build.check');
|
|
206
|
+
expect(current.envelope.data.upToDate).toBe(true);
|
|
207
|
+
expect(fs.readdirSync(outside).sort()).toEqual(copied);
|
|
152
208
|
});
|
|
153
209
|
|
|
154
|
-
it('reports a theme file outside the project as ERR_PATH_TRAVERSAL, because its outputs go beside it', async () => {
|
|
210
|
+
it('reports a theme file outside the project as ERR_PATH_TRAVERSAL, because its outputs go beside it; --check only compares them', async () => {
|
|
155
211
|
const project = path.join(tmpDir, 'project');
|
|
156
212
|
fs.mkdirSync(project, {recursive: true});
|
|
157
213
|
const shared = path.join(tmpDir, 'shared');
|
|
@@ -162,10 +218,82 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
162
218
|
|
|
163
219
|
expect(result.code).toBe(1);
|
|
164
220
|
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
221
|
+
|
|
222
|
+
const checked = await buildJson([file, '--check'], project);
|
|
223
|
+
expect(checked.result.code).toBe(1);
|
|
224
|
+
expect(checked.envelope.type).toBe('theme.build.check');
|
|
225
|
+
expect(checked.envelope.data.upToDate).toBe(false);
|
|
165
226
|
}
|
|
166
227
|
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
167
228
|
});
|
|
168
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Read the remedy that ends a stale report for outputs outside cwd: the
|
|
232
|
+
* directory to run from, then the build and check arguments after `theme
|
|
233
|
+
* build`. Each command starts with the install-aware invocation for it.
|
|
234
|
+
* @param {string} stderr
|
|
235
|
+
*/
|
|
236
|
+
function readRemedy(stderr) {
|
|
237
|
+
const lines = stderr.split('\n').map(line => line.trim());
|
|
238
|
+
const cd = lines.find(line => line.startsWith('cd '));
|
|
239
|
+
expect(cd).toBeDefined();
|
|
240
|
+
const dir = /** @type {string} */ (cd).slice('cd '.length);
|
|
241
|
+
const stem = `${getCliInvocation(dir)} theme build `;
|
|
242
|
+
const commands = lines.filter(line => line.startsWith(stem));
|
|
243
|
+
expect(commands).toHaveLength(2);
|
|
244
|
+
const [build, check] = commands.map(line =>
|
|
245
|
+
line.slice(stem.length).split(' '),
|
|
246
|
+
);
|
|
247
|
+
expect(check).toEqual([...build, '--check']);
|
|
248
|
+
return {dir, build, check};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
it('ends a stale report for outputs outside cwd with a remedy that works, and keeps the report for outputs inside it', async () => {
|
|
252
|
+
const project = path.join(tmpDir, 'project');
|
|
253
|
+
const inside = path.relative(
|
|
254
|
+
project,
|
|
255
|
+
writeTheme(path.join(project, 'themes'), 'ocean'),
|
|
256
|
+
);
|
|
257
|
+
const shared = writeTheme(path.join(tmpDir, 'shared'), 'ocean');
|
|
258
|
+
const outside = path.join(tmpDir, 'outside');
|
|
259
|
+
|
|
260
|
+
for (const args of [
|
|
261
|
+
[path.relative(project, shared)],
|
|
262
|
+
[inside, '--out', path.join(outside, 'ocean.css')],
|
|
263
|
+
]) {
|
|
264
|
+
const stale = await runCli(
|
|
265
|
+
['theme', 'build', ...args, '--check'],
|
|
266
|
+
project,
|
|
267
|
+
);
|
|
268
|
+
expect(stale.code).toBe(1);
|
|
269
|
+
// The old remedy ran the build from cwd, which refuses these outputs.
|
|
270
|
+
expect(stale.stderr).not.toContain('Rebuild with:');
|
|
271
|
+
expect(stale.stderr).not.toContain(`theme build ${args.join(' ')}`);
|
|
272
|
+
|
|
273
|
+
// Following the remedy builds, and the check from there passes.
|
|
274
|
+
const remedy = readRemedy(stale.stderr);
|
|
275
|
+
const built = await runCli(
|
|
276
|
+
['theme', 'build', ...remedy.build],
|
|
277
|
+
remedy.dir,
|
|
278
|
+
);
|
|
279
|
+
expect(built.code).toBe(0);
|
|
280
|
+
const checked = await runCli(
|
|
281
|
+
['--json', 'theme', 'build', ...remedy.check],
|
|
282
|
+
remedy.dir,
|
|
283
|
+
);
|
|
284
|
+
expect(checked.code).toBe(0);
|
|
285
|
+
expect(JSON.parse(checked.stdout).data.upToDate).toBe(true);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Outputs inside cwd keep the plain rebuild line.
|
|
289
|
+
const local = await runCli(['theme', 'build', inside, '--check'], project);
|
|
290
|
+
expect(local.code).toBe(1);
|
|
291
|
+
expect(local.stderr).toContain(
|
|
292
|
+
`Rebuild with: astryx theme build ${inside}`,
|
|
293
|
+
);
|
|
294
|
+
expect(local.stderr).not.toMatch(/^\s*cd /m);
|
|
295
|
+
});
|
|
296
|
+
|
|
169
297
|
it('builds absolute paths inside the project, also when they reach it through a symlink', async () => {
|
|
170
298
|
const project = path.join(tmpDir, 'project');
|
|
171
299
|
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
@@ -243,7 +371,7 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
243
371
|
});
|
|
244
372
|
});
|
|
245
373
|
|
|
246
|
-
it('themeBuild() rejects an output outside cwd
|
|
374
|
+
it('themeBuild() rejects an output outside cwd, an absolute out path or the default beside a theme file outside cwd, and checks it read-only', async () => {
|
|
247
375
|
const project = path.join(tmpDir, 'project');
|
|
248
376
|
const inside = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
249
377
|
const shared = path.join(tmpDir, 'shared');
|
|
@@ -257,6 +385,12 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
257
385
|
await expect(
|
|
258
386
|
themeBuild(file, options, {cwd: project}),
|
|
259
387
|
).rejects.toMatchObject({code: ERROR_CODES.ERR_PATH_TRAVERSAL});
|
|
388
|
+
await expect(
|
|
389
|
+
themeBuild(file, {...options, check: true}, {cwd: project}),
|
|
390
|
+
).resolves.toMatchObject({
|
|
391
|
+
type: 'theme.build.check',
|
|
392
|
+
data: {upToDate: false},
|
|
393
|
+
});
|
|
260
394
|
}
|
|
261
395
|
expect(fs.existsSync(outDir)).toBe(false);
|
|
262
396
|
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
@@ -8,7 +8,8 @@ export const doc = {
|
|
|
8
8
|
name: 'doctor integration components',
|
|
9
9
|
displayName: 'astryx doctor integration components',
|
|
10
10
|
namespace: 'cli/commands',
|
|
11
|
-
summary:
|
|
11
|
+
summary:
|
|
12
|
+
'Check integration component names, replacements, and documented props',
|
|
12
13
|
description:
|
|
13
14
|
'Compares one local or installed integration with Core component names. A ' +
|
|
14
15
|
'conflict is allowed and exits successfully, but the report recommends ' +
|
|
@@ -17,7 +18,14 @@ export const doc = {
|
|
|
17
18
|
'range that turns replacement on, a missing Core target, an invalid value, a ' +
|
|
18
19
|
'component named after a different Core component, or two replacements for one ' +
|
|
19
20
|
'Core component is an error. In a package without that range they are warnings, ' +
|
|
20
|
-
'with a warning for each such component, naming the range to declare.'
|
|
21
|
+
'with a warning for each such component, naming the range to declare. ' +
|
|
22
|
+
"Then it compares each component doc's props with the component's TypeScript " +
|
|
23
|
+
"props, using the project's own `typescript`, and warns about a documented prop " +
|
|
24
|
+
'the code lacks, a prop the package declares that the doc omits, a wrong ' +
|
|
25
|
+
'`required`, and a type that does not match where both sides read exactly ' +
|
|
26
|
+
'(it lists every other documented type as not compared, with the reason). ' +
|
|
27
|
+
'Without TypeScript it prints one ' +
|
|
28
|
+
'line saying the comparison was skipped.',
|
|
21
29
|
fn: 'integrationComponentConflicts',
|
|
22
30
|
args: [
|
|
23
31
|
{
|
|
@@ -29,7 +37,10 @@ export const doc = {
|
|
|
29
37
|
},
|
|
30
38
|
],
|
|
31
39
|
examples: [
|
|
32
|
-
{
|
|
40
|
+
{
|
|
41
|
+
label: 'Check the local integration',
|
|
42
|
+
cli: 'astryx doctor integration components',
|
|
43
|
+
},
|
|
33
44
|
{
|
|
34
45
|
label: 'Check an installed integration',
|
|
35
46
|
cli: 'astryx doctor integration components @acme/widgets --json',
|
|
@@ -38,7 +49,7 @@ export const doc = {
|
|
|
38
49
|
exitCodes: [
|
|
39
50
|
{
|
|
40
51
|
code: 0,
|
|
41
|
-
when: 'the check completed; component name conflicts,
|
|
52
|
+
when: 'the check completed; component name conflicts, replaces findings for a package without the CLI range, and props that differ from the code are warnings',
|
|
42
53
|
},
|
|
43
54
|
{
|
|
44
55
|
code: 1,
|