@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
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @file The canonical `.
|
|
5
|
-
* to the
|
|
4
|
+
* @file The canonical `.doc.{mjs,ts,js}` suffix is discovered identically
|
|
5
|
+
* to the released `.template.{ts,mjs,js}` compatibility suffix.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* both families and assert byte-for-byte-equivalent discovery + scaffolding.
|
|
7
|
+
* New authoring writes `.doc.mjs`, the canonical typed descriptor. Stable
|
|
8
|
+
* 0.6.0 already accepted `.template.*`, so these tests
|
|
9
|
+
* keep that family as a compatibility input and assert equivalent discovery +
|
|
10
|
+
* scaffolding.
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
13
|
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
15
14
|
import * as fs from 'node:fs';
|
|
16
15
|
import * as path from 'node:path';
|
|
17
16
|
import * as os from 'node:os';
|
|
18
|
-
import {
|
|
19
|
-
template,
|
|
20
|
-
findShowcase,
|
|
21
|
-
findRelatedBlocks,
|
|
22
|
-
} from './template.mjs';
|
|
17
|
+
import {template, findShowcase, findRelatedBlocks} from './template.mjs';
|
|
23
18
|
|
|
24
19
|
let tmpDir;
|
|
25
20
|
let originalCwd;
|
|
@@ -82,8 +77,8 @@ afterEach(() => {
|
|
|
82
77
|
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
83
78
|
});
|
|
84
79
|
|
|
85
|
-
describe('integration templates: .
|
|
86
|
-
for (const suffix of ['.
|
|
80
|
+
describe('integration templates: canonical .doc.* plus released .template.* compatibility', () => {
|
|
81
|
+
for (const suffix of ['.doc.mjs', '.doc.ts', '.template.mjs']) {
|
|
87
82
|
it(`discovers + lists a page template authored as ${suffix}`, async () => {
|
|
88
83
|
const pkgDir = installWidgets(tmpDir);
|
|
89
84
|
writeTemplate(pkgDir, 'pricing', {kind: 'page', suffix});
|
|
@@ -124,7 +119,7 @@ describe('integration templates: .template.* discovered like legacy .doc.*', ()
|
|
|
124
119
|
});
|
|
125
120
|
}
|
|
126
121
|
|
|
127
|
-
it('treats
|
|
122
|
+
it('treats canonical .doc.mjs and released .template.ts identically (same shape)', async () => {
|
|
128
123
|
const pkgDir = installWidgets(tmpDir);
|
|
129
124
|
writeTemplate(pkgDir, 'gauge', {kind: 'block', suffix: '.template.ts'});
|
|
130
125
|
writeTemplate(pkgDir, 'chip', {kind: 'block', suffix: '.doc.mjs'});
|
|
@@ -143,12 +138,33 @@ describe('integration templates: .template.* discovered like legacy .doc.*', ()
|
|
|
143
138
|
expect(gauge.type).toBe(chip.type);
|
|
144
139
|
expect(gauge.package).toBe(chip.package);
|
|
145
140
|
});
|
|
141
|
+
it('lists both same-stem specs and reads the id as ambiguous', async () => {
|
|
142
|
+
for (const [first, second] of [
|
|
143
|
+
['.doc.mjs', '.template.ts'],
|
|
144
|
+
['.doc.ts', '.doc.mjs'],
|
|
145
|
+
]) {
|
|
146
|
+
const pkgDir = installWidgets(tmpDir);
|
|
147
|
+
fs.rmSync(path.join(pkgDir, 'templates'), {recursive: true, force: true});
|
|
148
|
+
writeTemplate(pkgDir, 'pricing', {kind: 'page', suffix: first});
|
|
149
|
+
writeTemplate(pkgDir, 'pricing', {kind: 'page', suffix: second});
|
|
150
|
+
|
|
151
|
+
const result = await template(undefined, {list: true, cwd: tmpDir});
|
|
152
|
+
expect(
|
|
153
|
+
result.data.filter(entry => entry.id === 'pricing'),
|
|
154
|
+
`${first} + ${second}`,
|
|
155
|
+
).toHaveLength(2);
|
|
156
|
+
await expect(
|
|
157
|
+
template('pricing', {cwd: tmpDir}),
|
|
158
|
+
`${first} + ${second}`,
|
|
159
|
+
).rejects.toMatchObject({code: 'ERR_AMBIGUOUS_TEMPLATE'});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
146
162
|
});
|
|
147
163
|
|
|
148
|
-
describe('external showcase blocks: .
|
|
164
|
+
describe('external showcase blocks: canonical .doc.* plus released .template.* compatibility', () => {
|
|
149
165
|
/**
|
|
150
166
|
* Create a consumer whose @test/ext package contributes two showcase blocks,
|
|
151
|
-
* one authored
|
|
167
|
+
* one authored with released `Foo.template.ts` and one with canonical `Bar.doc.mjs`.
|
|
152
168
|
*/
|
|
153
169
|
function makeShowcaseFixture() {
|
|
154
170
|
// Symlink the real core package (needed by findCoreDir during discovery).
|
|
@@ -166,7 +182,7 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
|
|
|
166
182
|
const extDir = path.join(tmpDir, 'node_modules', '@test', 'ext');
|
|
167
183
|
const blocksDir = path.join(extDir, 'blocks', 'components');
|
|
168
184
|
|
|
169
|
-
// Foo showcase —
|
|
185
|
+
// Foo showcase — released .template.ts compatibility input.
|
|
170
186
|
const fooDir = path.join(blocksDir, 'Foo');
|
|
171
187
|
fs.mkdirSync(fooDir, {recursive: true});
|
|
172
188
|
fs.writeFileSync(
|
|
@@ -185,7 +201,7 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
|
|
|
185
201
|
"'use client';\nexport default function FooShowcase() { return <div>Foo</div>; }",
|
|
186
202
|
);
|
|
187
203
|
|
|
188
|
-
// Bar showcase —
|
|
204
|
+
// Bar showcase — canonical .doc.mjs (historical named export).
|
|
189
205
|
const barDir = path.join(blocksDir, 'Bar');
|
|
190
206
|
fs.mkdirSync(barDir, {recursive: true});
|
|
191
207
|
fs.writeFileSync(
|
|
@@ -209,7 +225,11 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
|
|
|
209
225
|
path.join(extDir, 'package.json'),
|
|
210
226
|
JSON.stringify({
|
|
211
227
|
name: '@test/ext',
|
|
212
|
-
astryx: {
|
|
228
|
+
astryx: {
|
|
229
|
+
docs: './src',
|
|
230
|
+
category: 'Common',
|
|
231
|
+
blocks: './blocks/components',
|
|
232
|
+
},
|
|
213
233
|
}),
|
|
214
234
|
);
|
|
215
235
|
fs.mkdirSync(path.join(extDir, 'src'), {recursive: true});
|
|
@@ -223,7 +243,7 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
|
|
|
223
243
|
expect(result.filePath).toContain('FooShowcase.tsx');
|
|
224
244
|
});
|
|
225
245
|
|
|
226
|
-
it('findShowcase resolves a
|
|
246
|
+
it('findShowcase resolves a canonical .doc.mjs external block by componentsUsed', async () => {
|
|
227
247
|
makeShowcaseFixture();
|
|
228
248
|
const result = await findShowcase('Bar', tmpDir);
|
|
229
249
|
expect(result).not.toBeNull();
|
package/api/theme/_adapter.d.mts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
export function listThemes(): BundledTheme[];
|
|
14
14
|
/**
|
|
15
15
|
* Bundled themes plus source themes from integrations installed in `cwd`.
|
|
16
|
-
* A project/config load failure degrades to the bundled
|
|
16
|
+
* A project/config load failure degrades to the bundled set, preserving the
|
|
17
17
|
* command's historical usefulness outside a configured project.
|
|
18
18
|
* @param {string} [cwd]
|
|
19
19
|
* @returns {Promise<import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme[]>}
|
|
@@ -35,6 +35,5 @@ export function findTheme(slug?: string, options?: {
|
|
|
35
35
|
*/
|
|
36
36
|
export type BundledTheme = Pick<import("../../foundation/discovery/theme-discovery.mjs").DiscoveredTheme, "slug" | "displayName" | "description" | "maintained" | "entry" | "exportName" | "files">;
|
|
37
37
|
import { BUNDLED_THEME_PACKAGE } from '../../foundation/discovery/theme-discovery.mjs';
|
|
38
|
-
import { MANIFEST_PATH } from '../../foundation/discovery/theme-discovery.mjs';
|
|
39
38
|
import { THEMES_DIR } from '../../foundation/discovery/theme-discovery.mjs';
|
|
40
|
-
export { BUNDLED_THEME_PACKAGE,
|
|
39
|
+
export { BUNDLED_THEME_PACKAGE, THEMES_DIR };
|
package/api/theme/_adapter.mjs
CHANGED
|
@@ -5,20 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* `listThemes()` intentionally keeps its historical synchronous, bundled-only
|
|
7
7
|
* contract. Project-aware callers use `listAvailableThemes()`, which asks
|
|
8
|
-
* Project for the bundled
|
|
8
|
+
* Project for the bundled themes plus every installed integration theme root.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {Project} from '../../foundation/config/project.mjs';
|
|
12
12
|
import {
|
|
13
13
|
BUNDLED_THEME_PACKAGE,
|
|
14
|
-
MANIFEST_PATH,
|
|
15
14
|
THEMES_DIR,
|
|
16
15
|
discoverBundledThemes,
|
|
17
16
|
} from '../../foundation/discovery/theme-discovery.mjs';
|
|
18
17
|
import {AstryxError} from '../error.mjs';
|
|
19
18
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
20
19
|
|
|
21
|
-
export {BUNDLED_THEME_PACKAGE,
|
|
20
|
+
export {BUNDLED_THEME_PACKAGE, THEMES_DIR};
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* A bundled theme entry kept for the historical public `listThemes()` helper.
|
|
@@ -55,7 +54,7 @@ function availableBundledThemes() {
|
|
|
55
54
|
/**
|
|
56
55
|
* Load available themes with any package-owned integration issues found while
|
|
57
56
|
* discovering them. The issue set lets a package-scoped lookup distinguish an
|
|
58
|
-
* absent theme from an installed package whose
|
|
57
|
+
* absent theme from an installed package whose theme descriptors are broken.
|
|
59
58
|
* @param {string} cwd
|
|
60
59
|
* @returns {Promise<{themes: import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme[], issues: Array<import('../../foundation/integrations/issue').AstryxIntegrationIssue & {package: string}>}>}
|
|
61
60
|
*/
|
|
@@ -71,7 +70,7 @@ async function availableThemeState(cwd) {
|
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
72
|
* Bundled themes plus source themes from integrations installed in `cwd`.
|
|
74
|
-
* A project/config load failure degrades to the bundled
|
|
73
|
+
* A project/config load failure degrades to the bundled set, preserving the
|
|
75
74
|
* command's historical usefulness outside a configured project.
|
|
76
75
|
* @param {string} [cwd]
|
|
77
76
|
* @returns {Promise<import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme[]>}
|
|
@@ -37,21 +37,10 @@ beforeEach(() => {
|
|
|
37
37
|
"export default {themes: './themes'};\n",
|
|
38
38
|
);
|
|
39
39
|
fs.writeFileSync(
|
|
40
|
-
path.join(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
slug: 'ocean',
|
|
46
|
-
displayName: 'Ocean',
|
|
47
|
-
description: 'Integration theme that ships a font.',
|
|
48
|
-
maintained: true,
|
|
49
|
-
entry: 'oceanTheme.ts',
|
|
50
|
-
exportName: 'oceanTheme',
|
|
51
|
-
files: ['oceanTheme.ts', 'fonts/ocean.woff2'],
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
}),
|
|
40
|
+
path.join(themeDir, 'oceanTheme.doc.mjs'),
|
|
41
|
+
`/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
|
|
42
|
+
export default {type: 'theme', name: 'ocean', displayName: 'Ocean', description: 'Integration theme that ships a font.', maintained: true};
|
|
43
|
+
`,
|
|
55
44
|
);
|
|
56
45
|
fs.writeFileSync(
|
|
57
46
|
path.join(themeDir, 'oceanTheme.ts'),
|
|
@@ -64,14 +53,18 @@ afterEach(() => {
|
|
|
64
53
|
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
65
54
|
});
|
|
66
55
|
|
|
67
|
-
describe('themeAdd copies every
|
|
56
|
+
describe('themeAdd copies every file of the theme', () => {
|
|
68
57
|
it('copies a binary file byte for byte', async () => {
|
|
69
58
|
const result = await themeAdd('ocean', {
|
|
70
59
|
cwd: tmpDir,
|
|
71
60
|
package: '@acme/themes',
|
|
72
61
|
});
|
|
73
62
|
|
|
74
|
-
expect(result.data.files).toEqual([
|
|
63
|
+
expect(result.data.files).toEqual([
|
|
64
|
+
'oceanTheme.ts',
|
|
65
|
+
'fonts/ocean.woff2',
|
|
66
|
+
'oceanTheme.doc.mjs',
|
|
67
|
+
]);
|
|
75
68
|
const copied = fs.readFileSync(
|
|
76
69
|
path.join(tmpDir, 'src', 'themes', 'ocean', 'fonts', 'ocean.woff2'),
|
|
77
70
|
);
|
|
@@ -30,7 +30,20 @@ describe('themeAdd (api/theme/add)', () => {
|
|
|
30
30
|
expect(result.data.slug).toBe('neutral');
|
|
31
31
|
expect(result.data.package).toBe('@astryxdesign/cli');
|
|
32
32
|
expect(result.data.outputDir).toBe(path.join('src', 'themes', 'neutral'));
|
|
33
|
-
|
|
33
|
+
// Exactly what the bundle has always copied, in that order: the CLI's own
|
|
34
|
+
// descriptor stays behind.
|
|
35
|
+
expect(result.data.files).toEqual([
|
|
36
|
+
'neutralTheme.ts',
|
|
37
|
+
'icons.tsx',
|
|
38
|
+
'neutralPalettes.ts',
|
|
39
|
+
'neutralPalettes.generated.ts',
|
|
40
|
+
'neutralPaletteRefs.generated.ts',
|
|
41
|
+
'neutralPalettes.generated.receipt.json',
|
|
42
|
+
'palette.config.json',
|
|
43
|
+
]);
|
|
44
|
+
expect(
|
|
45
|
+
fs.readdirSync(path.join(tmpDir, 'src', 'themes', 'neutral')).sort(),
|
|
46
|
+
).toEqual([...result.data.files].sort());
|
|
34
47
|
for (const f of result.data.files) {
|
|
35
48
|
expect(
|
|
36
49
|
fs.existsSync(path.join(tmpDir, 'src', 'themes', 'neutral', f)),
|
|
@@ -7,18 +7,14 @@ import {themeAdd} from './add/add.mjs';
|
|
|
7
7
|
|
|
8
8
|
let tmpDir;
|
|
9
9
|
|
|
10
|
-
function installThemeIntegration(
|
|
11
|
-
packageName,
|
|
12
|
-
slug,
|
|
13
|
-
source = 'export const oceanTheme = {};\n',
|
|
14
|
-
extraFiles = {},
|
|
15
|
-
) {
|
|
10
|
+
function installThemeIntegration(packageName, slug, source, extraFiles = {}) {
|
|
16
11
|
const packageDir = path.join(
|
|
17
12
|
tmpDir,
|
|
18
13
|
'node_modules',
|
|
19
14
|
...packageName.split('/'),
|
|
20
15
|
);
|
|
21
16
|
const themeDir = path.join(packageDir, 'themes', slug);
|
|
17
|
+
const stem = `${slug.replace(/-([a-z0-9])/gu, (_, character) => character.toUpperCase())}Theme`;
|
|
22
18
|
fs.mkdirSync(themeDir, {recursive: true});
|
|
23
19
|
fs.writeFileSync(
|
|
24
20
|
path.join(packageDir, 'package.json'),
|
|
@@ -26,26 +22,21 @@ function installThemeIntegration(
|
|
|
26
22
|
);
|
|
27
23
|
fs.writeFileSync(
|
|
28
24
|
path.join(packageDir, 'astryx.integration.mjs'),
|
|
29
|
-
`export default {themes: './themes'}
|
|
25
|
+
`export default {themes: './themes'};
|
|
26
|
+
`,
|
|
30
27
|
);
|
|
31
28
|
fs.writeFileSync(
|
|
32
|
-
path.join(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
exportName: 'oceanTheme',
|
|
43
|
-
files: ['oceanTheme.ts', ...Object.keys(extraFiles)],
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
}),
|
|
29
|
+
path.join(themeDir, `${stem}.doc.mjs`),
|
|
30
|
+
`/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
|
|
31
|
+
export default {type: 'theme', name: '${slug}', displayName: '${slug === 'neutral' ? 'Neutral Plus' : 'Ocean'}', description: 'Integration-owned theme.', maintained: true};
|
|
32
|
+
`,
|
|
33
|
+
);
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
path.join(themeDir, `${stem}.ts`),
|
|
36
|
+
source ??
|
|
37
|
+
`export const ${stem} = {};
|
|
38
|
+
`,
|
|
47
39
|
);
|
|
48
|
-
fs.writeFileSync(path.join(themeDir, 'oceanTheme.ts'), source);
|
|
49
40
|
for (const [relativePath, contents] of Object.entries(extraFiles)) {
|
|
50
41
|
const file = path.join(themeDir, relativePath);
|
|
51
42
|
fs.mkdirSync(path.dirname(file), {recursive: true});
|
|
@@ -85,23 +76,43 @@ describe('themeAdd with integration themes', () => {
|
|
|
85
76
|
).toContain('oceanTheme');
|
|
86
77
|
});
|
|
87
78
|
|
|
88
|
-
it('copies
|
|
79
|
+
it('copies the complete nested integration theme directory', async () => {
|
|
89
80
|
installThemeIntegration(
|
|
90
81
|
'@acme/themes',
|
|
91
82
|
'ocean',
|
|
92
83
|
"import {oceanBlue} from './tokens/colors';\nexport const oceanTheme = {oceanBlue};\n",
|
|
93
|
-
{
|
|
84
|
+
{
|
|
85
|
+
'tokens/colors.ts': "export const oceanBlue = '#0064e0';\n",
|
|
86
|
+
'receipts/palette.json': '{"version":1}\n',
|
|
87
|
+
},
|
|
94
88
|
);
|
|
95
89
|
|
|
96
90
|
const result = await themeAdd('ocean', {cwd: tmpDir});
|
|
97
91
|
|
|
98
|
-
expect(result.data.files).toEqual([
|
|
92
|
+
expect(result.data.files).toEqual([
|
|
93
|
+
'oceanTheme.ts',
|
|
94
|
+
'oceanTheme.doc.mjs',
|
|
95
|
+
'receipts/palette.json',
|
|
96
|
+
'tokens/colors.ts',
|
|
97
|
+
]);
|
|
98
|
+
expect(
|
|
99
|
+
fs.readFileSync(
|
|
100
|
+
path.join(tmpDir, 'src', 'themes', 'ocean', 'oceanTheme.doc.mjs'),
|
|
101
|
+
'utf-8',
|
|
102
|
+
),
|
|
103
|
+
).toContain("type: 'theme'");
|
|
99
104
|
expect(
|
|
100
105
|
fs.readFileSync(
|
|
101
106
|
path.join(tmpDir, 'src', 'themes', 'ocean', 'tokens', 'colors.ts'),
|
|
102
107
|
'utf-8',
|
|
103
108
|
),
|
|
104
109
|
).toContain('oceanBlue');
|
|
110
|
+
expect(
|
|
111
|
+
fs.readFileSync(
|
|
112
|
+
path.join(tmpDir, 'src', 'themes', 'ocean', 'receipts', 'palette.json'),
|
|
113
|
+
'utf-8',
|
|
114
|
+
),
|
|
115
|
+
).toBe('{"version":1}\n');
|
|
105
116
|
});
|
|
106
117
|
|
|
107
118
|
it('rejects a nested destination symlink that escapes the project', async () => {
|
|
@@ -146,7 +157,7 @@ describe('themeAdd with integration themes', () => {
|
|
|
146
157
|
expect(result.data.package).toBe('@acme/themes');
|
|
147
158
|
});
|
|
148
159
|
|
|
149
|
-
it('reports a selected installed package with a corrupted theme
|
|
160
|
+
it('reports a selected installed package with a corrupted theme directory', async () => {
|
|
150
161
|
installThemeIntegration(
|
|
151
162
|
'@acme/themes',
|
|
152
163
|
'ocean',
|
|
@@ -170,7 +181,7 @@ describe('themeAdd with integration themes', () => {
|
|
|
170
181
|
themeAdd('ocean', {cwd: tmpDir, package: '@acme/themes'}),
|
|
171
182
|
).rejects.toMatchObject({
|
|
172
183
|
code: 'ERR_THEME_INVALID',
|
|
173
|
-
message: expect.stringContaining('tokens/colors
|
|
184
|
+
message: expect.stringContaining('./tokens/colors'),
|
|
174
185
|
});
|
|
175
186
|
});
|
|
176
187
|
|
|
@@ -9,8 +9,13 @@ import {BUNDLED_THEME_PACKAGE} from '../../../foundation/discovery/theme-discove
|
|
|
9
9
|
let tmpDir;
|
|
10
10
|
|
|
11
11
|
function installThemeIntegration(packageName = '@acme/themes', slug = 'ocean') {
|
|
12
|
-
const packageDir = path.join(
|
|
12
|
+
const packageDir = path.join(
|
|
13
|
+
tmpDir,
|
|
14
|
+
'node_modules',
|
|
15
|
+
...packageName.split('/'),
|
|
16
|
+
);
|
|
13
17
|
const themeDir = path.join(packageDir, 'themes', slug);
|
|
18
|
+
const stem = `${slug.replace(/-([a-z0-9])/gu, (_, character) => character.toUpperCase())}Theme`;
|
|
14
19
|
fs.mkdirSync(themeDir, {recursive: true});
|
|
15
20
|
fs.writeFileSync(
|
|
16
21
|
path.join(packageDir, 'package.json'),
|
|
@@ -18,28 +23,19 @@ function installThemeIntegration(packageName = '@acme/themes', slug = 'ocean') {
|
|
|
18
23
|
);
|
|
19
24
|
fs.writeFileSync(
|
|
20
25
|
path.join(packageDir, 'astryx.integration.mjs'),
|
|
21
|
-
`export default {themes: './themes'}
|
|
26
|
+
`export default {themes: './themes'};
|
|
27
|
+
`,
|
|
22
28
|
);
|
|
23
29
|
fs.writeFileSync(
|
|
24
|
-
path.join(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
slug,
|
|
30
|
-
displayName: 'Ocean',
|
|
31
|
-
description: 'Blue and calm.',
|
|
32
|
-
maintained: true,
|
|
33
|
-
entry: 'oceanTheme.ts',
|
|
34
|
-
exportName: 'oceanTheme',
|
|
35
|
-
files: ['oceanTheme.ts'],
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
}),
|
|
30
|
+
path.join(themeDir, `${stem}.doc.mjs`),
|
|
31
|
+
`/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
|
|
32
|
+
export default {type: 'theme', name: '${slug}', displayName: 'Ocean', description: 'Blue and calm.', maintained: true};
|
|
33
|
+
`,
|
|
39
34
|
);
|
|
40
35
|
fs.writeFileSync(
|
|
41
|
-
path.join(themeDir,
|
|
42
|
-
|
|
36
|
+
path.join(themeDir, `${stem}.ts`),
|
|
37
|
+
`export const ${stem} = {};
|
|
38
|
+
`,
|
|
43
39
|
);
|
|
44
40
|
}
|
|
45
41
|
|
|
@@ -47,7 +43,10 @@ beforeEach(() => {
|
|
|
47
43
|
tmpDir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-theme-list-'));
|
|
48
44
|
fs.writeFileSync(
|
|
49
45
|
path.join(tmpDir, 'package.json'),
|
|
50
|
-
JSON.stringify({
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
name: 'consumer',
|
|
48
|
+
dependencies: {'@acme/themes': '^1.0.0'},
|
|
49
|
+
}),
|
|
51
50
|
);
|
|
52
51
|
});
|
|
53
52
|
|
|
@@ -9,25 +9,26 @@ export const doc = {
|
|
|
9
9
|
type: 'function',
|
|
10
10
|
kind: 'api',
|
|
11
11
|
name: 'listThemes',
|
|
12
|
+
namespace: 'cli/api',
|
|
12
13
|
displayName: 'listThemes()',
|
|
13
|
-
summary: 'Read the CLI bundled-theme
|
|
14
|
+
summary: 'Read the CLI bundled-theme descriptors.',
|
|
14
15
|
description:
|
|
15
|
-
'Reads templates/themes
|
|
16
|
+
'Reads the typed same-stem descriptors under templates/themes and returns normalized entries synchronously. This low-level helper keeps its historical bundled-only contract; project-aware themeList() and themeAdd() also discover source themes from installed integrations.',
|
|
16
17
|
importPath: '@astryxdesign/cli/api',
|
|
17
18
|
signature: 'listThemes(): BundledTheme[]',
|
|
18
|
-
keywords: ['theme', 'themes', '
|
|
19
|
+
keywords: ['theme', 'themes', 'descriptor', 'bundled', 'adapter', 'list'],
|
|
19
20
|
params: [],
|
|
20
21
|
returns: [
|
|
21
22
|
{
|
|
22
23
|
type: 'BundledTheme[]',
|
|
23
24
|
description:
|
|
24
|
-
'The bundled
|
|
25
|
+
'The normalized bundled descriptor entries: slug, displayName, description, maintained, entry, exportName, and files.',
|
|
25
26
|
},
|
|
26
27
|
],
|
|
27
28
|
throws: [
|
|
28
29
|
{
|
|
29
30
|
code: 'ERR_NO_SOURCE',
|
|
30
|
-
when: 'the bundled
|
|
31
|
+
when: 'the bundled descriptors are missing, invalid, or have missing source files',
|
|
31
32
|
},
|
|
32
33
|
],
|
|
33
34
|
examples: [
|
|
@@ -9,10 +9,11 @@ export const doc = {
|
|
|
9
9
|
type: 'function',
|
|
10
10
|
kind: 'api',
|
|
11
11
|
name: 'themeAdd',
|
|
12
|
+
namespace: 'cli/api',
|
|
12
13
|
displayName: 'themeAdd()',
|
|
13
14
|
summary: 'Copy an available theme into a project as editable source.',
|
|
14
15
|
description:
|
|
15
|
-
|
|
16
|
+
"Resolves a theme from the CLI bundle or an installed integration and copies it into the consumer project: a bundled theme's source files, or an integration theme's complete directory with its descriptor. Writes are staged and existing files require explicit overwrite. Duplicate slugs fail closed until the caller selects an owner package.",
|
|
16
17
|
importPath: '@astryxdesign/cli/api',
|
|
17
18
|
signature:
|
|
18
19
|
'themeAdd(slug: string, options?: {targetPath?: string, overwrite?: boolean, cwd?: string, package?: string}): Promise<ThemeAddResponse>',
|
|
@@ -70,10 +71,10 @@ export const doc = {
|
|
|
70
71
|
{code: 'ERR_AMBIGUOUS_THEME', when: 'more than one package owns the slug'},
|
|
71
72
|
{
|
|
72
73
|
code: 'ERR_THEME_INVALID',
|
|
73
|
-
when: 'the selected installed package has a blocking integration or theme-
|
|
74
|
+
when: 'the selected installed package has a blocking integration or theme-descriptor error',
|
|
74
75
|
},
|
|
75
76
|
{code: 'ERR_PATH_TRAVERSAL', when: 'the target path escapes cwd'},
|
|
76
|
-
{code: 'ERR_NO_SOURCE', when: 'a
|
|
77
|
+
{code: 'ERR_NO_SOURCE', when: 'a theme file to copy is missing'},
|
|
77
78
|
{
|
|
78
79
|
code: 'ERR_FILE_EXISTS',
|
|
79
80
|
when: 'a destination exists and overwrite is not set',
|
|
@@ -9,10 +9,11 @@ export const doc = {
|
|
|
9
9
|
type: 'function',
|
|
10
10
|
kind: 'api',
|
|
11
11
|
name: 'themeList',
|
|
12
|
+
namespace: 'cli/api',
|
|
12
13
|
displayName: 'themeList()',
|
|
13
14
|
summary: 'List themes bundled with this CLI build.',
|
|
14
15
|
description:
|
|
15
|
-
'Projects the bundled
|
|
16
|
+
'Projects the bundled typed theme descriptors into a synchronous theme.list envelope. This preserves the original programmatic API contract. The CLI command uses themeListAvailable() so installed integrations also appear.',
|
|
16
17
|
importPath: '@astryxdesign/cli/api',
|
|
17
18
|
signature: 'themeList(): ThemeListResponse',
|
|
18
19
|
keywords: ['theme', 'list', 'themes', 'bundled', 'available'],
|
|
@@ -27,9 +28,11 @@ export const doc = {
|
|
|
27
28
|
throws: [
|
|
28
29
|
{
|
|
29
30
|
code: 'ERR_NO_SOURCE',
|
|
30
|
-
when: 'the bundled
|
|
31
|
+
when: 'the bundled theme descriptors cannot be read or parsed',
|
|
31
32
|
},
|
|
32
33
|
],
|
|
33
|
-
examples: [
|
|
34
|
+
examples: [
|
|
35
|
+
{label: 'List bundled themes', code: 'const {data} = themeList();'},
|
|
36
|
+
],
|
|
34
37
|
related: ['themeListAvailable', 'themeAdd', 'listThemes'],
|
|
35
38
|
};
|
|
@@ -9,10 +9,11 @@ export const doc = {
|
|
|
9
9
|
type: 'function',
|
|
10
10
|
kind: 'api',
|
|
11
11
|
name: 'themeListAvailable',
|
|
12
|
+
namespace: 'cli/api',
|
|
12
13
|
displayName: 'themeListAvailable()',
|
|
13
14
|
summary: 'List bundled and installed integration themes.',
|
|
14
15
|
description:
|
|
15
|
-
'Loads Project for the requested directory, combines the CLI bundle with source themes from installed integrations, and projects each entry with its owner package. An unreadable project configuration degrades to the bundled
|
|
16
|
+
'Loads Project for the requested directory, combines the CLI bundle with source themes from installed integrations, and projects each entry with its owner package. An unreadable project configuration degrades to the bundled descriptors.',
|
|
16
17
|
importPath: '@astryxdesign/cli/api',
|
|
17
18
|
signature:
|
|
18
19
|
'themeListAvailable(options?: {cwd?: string, package?: string}): Promise<ThemeListResponse>',
|
|
@@ -21,7 +22,8 @@ export const doc = {
|
|
|
21
22
|
{
|
|
22
23
|
name: 'options.cwd',
|
|
23
24
|
type: 'string',
|
|
24
|
-
description:
|
|
25
|
+
description:
|
|
26
|
+
'Project directory whose installed integrations contribute themes.',
|
|
25
27
|
},
|
|
26
28
|
{
|
|
27
29
|
name: 'options.package',
|
|
@@ -39,7 +41,7 @@ export const doc = {
|
|
|
39
41
|
throws: [
|
|
40
42
|
{
|
|
41
43
|
code: 'ERR_NO_SOURCE',
|
|
42
|
-
when: 'the CLI bundled-theme
|
|
44
|
+
when: 'the CLI bundled-theme descriptors cannot be read or parsed',
|
|
43
45
|
},
|
|
44
46
|
],
|
|
45
47
|
examples: [
|