@astryxdesign/cli 0.7.2-canary.fc12a18 → 0.7.2-canary.fdd384f
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 +9 -8
- 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/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 +9 -0
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +108 -16
- 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/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 +10 -0
- package/assets/docs/tree/block-template.doc.mjs +2 -2
- package/assets/docs/tree/check-the-props.doc.mjs +196 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +44 -3
- package/assets/docs/tree/checks.doc.mjs +21 -13
- package/assets/docs/tree/component-family.doc.mjs +5 -1
- package/assets/docs/tree/component-imports.doc.mjs +8 -0
- package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
- package/assets/docs/tree/links.doc.mjs +2 -2
- package/assets/docs/tree/page-template.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
- package/assets/docs/tree/single-component.doc.mjs +3 -2
- package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/versioning.doc.mjs +1 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +2 -1
- 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/doctypes/base/type.ts +6 -0
- package/authoring/doctypes/component/component.doc.mjs +4 -4
- package/authoring/doctypes/component/type.ts +6 -3
- 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 +1 -1
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/integration.doc.mjs +8 -1
- 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/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/clients/cli/lib/component-format.mjs +5 -1
- package/foundation/config/project.mjs +82 -0
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- 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
|
@@ -9,6 +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 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.
|
|
12
18
|
*
|
|
13
19
|
* Runs the CLI in-process (shared runCli harness) against a tiny synthetic theme file.
|
|
14
20
|
*/
|
|
@@ -22,6 +28,7 @@ import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
|
22
28
|
import {themeBuild} from '../../../api/theme/build/build.mjs';
|
|
23
29
|
import {AstryxError} from '../../../api/error.mjs';
|
|
24
30
|
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
31
|
+
import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
|
|
25
32
|
|
|
26
33
|
function writeTheme(dir, name) {
|
|
27
34
|
fs.mkdirSync(dir, {recursive: true});
|
|
@@ -59,7 +66,9 @@ describe('theme build path safety', () => {
|
|
|
59
66
|
);
|
|
60
67
|
|
|
61
68
|
expect(result.code).not.toBe(0);
|
|
62
|
-
expect(result.stderr + result.stdout).toMatch(
|
|
69
|
+
expect(result.stderr + result.stdout).toMatch(
|
|
70
|
+
/path separator|invalid theme name/i,
|
|
71
|
+
);
|
|
63
72
|
|
|
64
73
|
// The traversal target must not exist.
|
|
65
74
|
expect(fs.existsSync(path.join(tmpDir, 'escaped.js'))).toBe(false);
|
|
@@ -92,7 +101,10 @@ describe('theme build path safety', () => {
|
|
|
92
101
|
|
|
93
102
|
const themeFile = writeTheme(themesDir, '../leak');
|
|
94
103
|
|
|
95
|
-
await runCli(
|
|
104
|
+
await runCli(
|
|
105
|
+
['theme', 'build', path.relative(project, themeFile)],
|
|
106
|
+
project,
|
|
107
|
+
);
|
|
96
108
|
|
|
97
109
|
// Neither the CSS nor any sibling JS should have been written.
|
|
98
110
|
const entries = fs.readdirSync(themesDir).sort();
|
|
@@ -114,19 +126,200 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
114
126
|
return {result, envelope: JSON.parse(result.stdout)};
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
it('reports
|
|
129
|
+
it('reports a relative --out path outside the project as ERR_PATH_TRAVERSAL, with --check too', async () => {
|
|
118
130
|
const project = path.join(tmpDir, 'project');
|
|
119
131
|
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
120
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
|
+
|
|
121
158
|
const {result, envelope} = await buildJson(
|
|
122
|
-
[path.relative(project, themeFile), '--out',
|
|
159
|
+
[path.relative(project, themeFile), '--out', out],
|
|
123
160
|
project,
|
|
124
161
|
);
|
|
125
162
|
|
|
126
163
|
expect(result.code).toBe(1);
|
|
127
164
|
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
128
|
-
expect(
|
|
129
|
-
|
|
165
|
+
expect(fs.readdirSync(outside)).toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('checks an absolute --out path outside the project read-only', async () => {
|
|
169
|
+
const project = path.join(tmpDir, 'project');
|
|
170
|
+
const outside = path.join(tmpDir, 'outside');
|
|
171
|
+
fs.mkdirSync(outside, {recursive: true});
|
|
172
|
+
const themeFile = path.relative(
|
|
173
|
+
project,
|
|
174
|
+
writeTheme(path.join(project, 'themes'), 'ocean'),
|
|
175
|
+
);
|
|
176
|
+
const out = path.join(outside, 'ocean.css');
|
|
177
|
+
|
|
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([]);
|
|
186
|
+
|
|
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));
|
|
197
|
+
}
|
|
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);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('reports a theme file outside the project as ERR_PATH_TRAVERSAL, because its outputs go beside it; --check only compares them', async () => {
|
|
211
|
+
const project = path.join(tmpDir, 'project');
|
|
212
|
+
fs.mkdirSync(project, {recursive: true});
|
|
213
|
+
const shared = path.join(tmpDir, 'shared');
|
|
214
|
+
const themeFile = writeTheme(shared, 'ocean');
|
|
215
|
+
|
|
216
|
+
for (const file of [themeFile, path.relative(project, themeFile)]) {
|
|
217
|
+
const {result, envelope} = await buildJson([file], project);
|
|
218
|
+
|
|
219
|
+
expect(result.code).toBe(1);
|
|
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);
|
|
226
|
+
}
|
|
227
|
+
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
228
|
+
});
|
|
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
|
+
|
|
297
|
+
it('builds absolute paths inside the project, also when they reach it through a symlink', async () => {
|
|
298
|
+
const project = path.join(tmpDir, 'project');
|
|
299
|
+
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
300
|
+
const alias = path.join(tmpDir, 'alias');
|
|
301
|
+
fs.symlinkSync(project, alias, 'dir');
|
|
302
|
+
|
|
303
|
+
for (const [args, written] of [
|
|
304
|
+
[
|
|
305
|
+
[themeFile, '--out', path.join(project, 'dist', 'direct.css')],
|
|
306
|
+
path.join(project, 'dist', 'direct.css'),
|
|
307
|
+
],
|
|
308
|
+
[
|
|
309
|
+
[themeFile, '--out', path.join(alias, 'dist', 'aliased.css')],
|
|
310
|
+
path.join(project, 'dist', 'aliased.css'),
|
|
311
|
+
],
|
|
312
|
+
[
|
|
313
|
+
[path.join(alias, 'themes', 'theme.mjs')],
|
|
314
|
+
path.join(project, 'themes', 'ocean.css'),
|
|
315
|
+
],
|
|
316
|
+
]) {
|
|
317
|
+
const {result, envelope} = await buildJson(args, project);
|
|
318
|
+
|
|
319
|
+
expect(result.code).toBe(0);
|
|
320
|
+
expect(envelope.type).toBe('theme.build');
|
|
321
|
+
expect(fs.existsSync(written)).toBe(true);
|
|
322
|
+
}
|
|
130
323
|
});
|
|
131
324
|
|
|
132
325
|
it('reports an --out path that escapes through a symlink as ERR_PATH_TRAVERSAL', async () => {
|
|
@@ -177,4 +370,29 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
177
370
|
code: ERROR_CODES.ERR_PATH_TRAVERSAL,
|
|
178
371
|
});
|
|
179
372
|
});
|
|
373
|
+
|
|
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 () => {
|
|
375
|
+
const project = path.join(tmpDir, 'project');
|
|
376
|
+
const inside = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
377
|
+
const shared = path.join(tmpDir, 'shared');
|
|
378
|
+
const outsideTheme = writeTheme(shared, 'ocean');
|
|
379
|
+
const outDir = path.join(tmpDir, 'out');
|
|
380
|
+
|
|
381
|
+
for (const [file, options] of [
|
|
382
|
+
[path.relative(project, inside), {out: path.join(outDir, 'ocean.css')}],
|
|
383
|
+
[outsideTheme, {}],
|
|
384
|
+
]) {
|
|
385
|
+
await expect(
|
|
386
|
+
themeBuild(file, options, {cwd: project}),
|
|
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
|
+
});
|
|
394
|
+
}
|
|
395
|
+
expect(fs.existsSync(outDir)).toBe(false);
|
|
396
|
+
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
397
|
+
});
|
|
180
398
|
});
|
|
@@ -126,7 +126,10 @@ describe('theme build emits a live TextInput selector (#4109)', () => {
|
|
|
126
126
|
`};\n`,
|
|
127
127
|
);
|
|
128
128
|
|
|
129
|
-
const result = await runCli(
|
|
129
|
+
const result = await runCli(
|
|
130
|
+
['theme', 'build', themeFile, '-o', outFile],
|
|
131
|
+
tmpDir,
|
|
132
|
+
);
|
|
130
133
|
const css = fs.readFileSync(outFile, 'utf8');
|
|
131
134
|
|
|
132
135
|
expect(result.status).toBe(0);
|
|
@@ -148,7 +151,7 @@ describe('theme build emits a live TextInput selector (#4109)', () => {
|
|
|
148
151
|
`};\n`,
|
|
149
152
|
);
|
|
150
153
|
|
|
151
|
-
await runCli(['theme', 'build', themeFile, '-o', outFile]);
|
|
154
|
+
await runCli(['theme', 'build', themeFile, '-o', outFile], tmpDir);
|
|
152
155
|
const css = fs.readFileSync(outFile, 'utf8');
|
|
153
156
|
|
|
154
157
|
expect(css).toContain('.astryx-text-input');
|
|
@@ -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,
|
|
@@ -14,7 +14,13 @@ export const doc = {
|
|
|
14
14
|
description:
|
|
15
15
|
'Validates one integration at a time: the local package rooted at cwd, or an ' +
|
|
16
16
|
'installed package resolved by name. It schema-checks the manifest, verifies ' +
|
|
17
|
-
'every declared contribution root, and reports every finding. Safe as a CI gate.'
|
|
17
|
+
'every declared contribution root, and reports every finding. Safe as a CI gate. ' +
|
|
18
|
+
'It then checks every doc the package ships under its roots against the ' +
|
|
19
|
+
"published authoring type for its kind, with the project's own `typescript`, and " +
|
|
20
|
+
'warns about each field that does not match, naming the field and a fix. A doc ' +
|
|
21
|
+
'it cannot fully check is listed with the reason, and test fixtures under ' +
|
|
22
|
+
'`__tests__` in the components or docs root are counted, not checked. Without ' +
|
|
23
|
+
'TypeScript it prints one line saying the docs were not type-checked.',
|
|
18
24
|
fn: 'validateIntegration',
|
|
19
25
|
args: [
|
|
20
26
|
{
|
|
@@ -36,7 +42,10 @@ export const doc = {
|
|
|
36
42
|
},
|
|
37
43
|
],
|
|
38
44
|
exitCodes: [
|
|
39
|
-
{
|
|
45
|
+
{
|
|
46
|
+
code: 0,
|
|
47
|
+
when: 'no error-severity issues (warnings, including doc type findings, are allowed)',
|
|
48
|
+
},
|
|
40
49
|
{code: 1, when: 'one or more error-severity issues'},
|
|
41
50
|
],
|
|
42
51
|
related: ['doctor integration templates', 'template'],
|