@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.
Files changed (174) hide show
  1. package/README.md +9 -8
  2. package/api/build/help/help.mjs +1 -1
  3. package/api/component/_adapter.d.mts +41 -1
  4. package/api/component/_adapter.mjs +93 -7
  5. package/api/component/component.mjs +22 -5
  6. package/api/component/component.test.mjs +420 -167
  7. package/api/component/integration-export-name.test.mjs +644 -0
  8. package/api/component/list/list.mjs +42 -4
  9. package/api/docs/_adapter.d.mts +11 -0
  10. package/api/docs/_adapter.mjs +244 -1
  11. package/api/docs/integrationDocs.test.mjs +124 -0
  12. package/api/doctor/theme-checks.mjs +1 -1
  13. package/api/gap-report/gap-report.mjs +67 -4
  14. package/api/index.d.mts +2 -0
  15. package/api/index.mjs +2 -0
  16. package/api/init/run/run.mjs +2 -2
  17. package/api/integration/_adapter.d.mts +12 -0
  18. package/api/integration/_adapter.mjs +54 -0
  19. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  20. package/api/integration/add-contribution.mjs +8 -4
  21. package/api/integration/add-contribution.test.mjs +4 -2
  22. package/api/integration/authoring-checks.mjs +11 -2
  23. package/api/integration/authoring-checks.test.mjs +39 -0
  24. package/api/integration/authoring-checks.type.d.mts +9 -0
  25. package/api/integration/authoring-checks.type.mjs +14 -1
  26. package/api/integration/check/check.d.mts +96 -0
  27. package/api/integration/check/check.mjs +357 -0
  28. package/api/integration/check/check.test.mjs +489 -0
  29. package/api/integration/integration-check.type.d.mts +118 -0
  30. package/api/integration/integration-check.type.mjs +84 -0
  31. package/api/integration/integrationCheck.doc.d.mts +10 -0
  32. package/api/integration/integrationCheck.doc.mjs +73 -0
  33. package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
  34. package/api/integration/pack-check.mjs +61 -107
  35. package/api/integration/pack-check.test.mjs +359 -0
  36. package/api/integration/validate-integration.mjs +8 -0
  37. package/api/integration/validate-integration.test.mjs +11 -0
  38. package/api/integration/validate-integration.type.d.mts +8 -0
  39. package/api/integration/validate-integration.type.mjs +12 -1
  40. package/api/integration/validateIntegration.doc.mjs +17 -3
  41. package/api/search/search.d.mts +9 -0
  42. package/api/search/search.integration-check.test.mjs +24 -0
  43. package/api/search/search.mjs +108 -16
  44. package/api/search/search.test.mjs +36 -0
  45. package/api/swizzle/copy/copy.mjs +28 -14
  46. package/api/template/template.test.mjs +120 -3
  47. package/api/theme/build/build.family.test.mjs +64 -0
  48. package/api/theme/build/build.mjs +180 -24
  49. package/api/theme/themeBuild.doc.mjs +4 -4
  50. package/assets/codemods/transforms/next/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  51. package/assets/codemods/transforms/next/index.mjs +11 -1
  52. package/assets/codemods/transforms/next/migrate-list-edge-compensation-to-edge.mjs +198 -0
  53. package/assets/docs/README.md +10 -0
  54. package/assets/docs/author-a-theme.doc.mjs +1 -1
  55. package/assets/docs/getting-started.doc.mjs +3 -3
  56. package/assets/docs/theme.doc.dense.mjs +1 -1
  57. package/assets/docs/theme.doc.mjs +1 -1
  58. package/assets/docs/theme.doc.zh.mjs +1 -1
  59. package/assets/docs/tree/add-a-component.doc.mjs +10 -0
  60. package/assets/docs/tree/block-template.doc.mjs +2 -2
  61. package/assets/docs/tree/check-the-props.doc.mjs +196 -0
  62. package/assets/docs/tree/check-your-docs.doc.mjs +44 -3
  63. package/assets/docs/tree/checks.doc.mjs +21 -13
  64. package/assets/docs/tree/component-family.doc.mjs +5 -1
  65. package/assets/docs/tree/component-imports.doc.mjs +8 -0
  66. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  67. package/assets/docs/tree/links.doc.mjs +2 -2
  68. package/assets/docs/tree/page-template.doc.mjs +1 -1
  69. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  70. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  71. package/assets/docs/tree/single-component.doc.mjs +3 -2
  72. package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
  73. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  74. package/assets/docs/tree/versioning.doc.mjs +1 -0
  75. package/assets/docs/tree/write-the-template-file.doc.mjs +2 -1
  76. package/assets/docs/use-a-theme.doc.mjs +2 -2
  77. package/assets/docs/working-with-ai.doc.mjs +43 -4
  78. package/assets/templates/blocks/components/Drawer/DrawerForm.doc.mjs +24 -0
  79. package/assets/templates/blocks/components/Drawer/DrawerForm.tsx +62 -0
  80. package/assets/templates/blocks/components/Drawer/DrawerScrim.doc.mjs +22 -0
  81. package/assets/templates/blocks/components/Drawer/DrawerScrim.tsx +68 -0
  82. package/assets/templates/blocks/components/Drawer/DrawerShowcase.doc.mjs +22 -0
  83. package/assets/templates/blocks/components/Drawer/DrawerShowcase.tsx +41 -0
  84. package/assets/templates/blocks/components/Drawer/DrawerStart.doc.mjs +21 -0
  85. package/assets/templates/blocks/components/Drawer/DrawerStart.tsx +41 -0
  86. package/assets/templates/blocks/components/Drawer/DrawerWidths.doc.mjs +22 -0
  87. package/assets/templates/blocks/components/Drawer/DrawerWidths.tsx +54 -0
  88. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.doc.mjs +15 -0
  89. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.tsx +73 -0
  90. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs +22 -0
  91. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.tsx +49 -0
  92. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.doc.mjs +21 -0
  93. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.tsx +42 -0
  94. package/assets/templates/pages/detail-page/page.tsx +3 -3
  95. package/assets/templates/pages/table-inbox/page.tsx +10 -8
  96. package/assets/theme.template.ts +1 -1
  97. package/authoring/doctypes/base/type.ts +6 -0
  98. package/authoring/doctypes/component/component.doc.mjs +4 -4
  99. package/authoring/doctypes/component/type.ts +6 -3
  100. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  101. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  102. package/authoring/doctypes/template/parse.d.mts +6 -2
  103. package/authoring/doctypes/template/parse.mjs +3 -1
  104. package/authoring/doctypes/template/template.doc.mjs +1 -1
  105. package/authoring/doctypes/template/type.ts +22 -0
  106. package/authoring/index.d.ts +1 -0
  107. package/authoring/integration/integration.doc.mjs +8 -1
  108. package/authoring/integration/parse.test.mjs +10 -0
  109. package/authoring/integration/schema.d.mts +2 -0
  110. package/authoring/integration/schema.mjs +1 -0
  111. package/authoring/integration/type.ts +4 -0
  112. package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
  113. package/clients/cli/commands/build-theme.mjs +1 -1
  114. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  115. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  116. package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
  117. package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
  118. package/clients/cli/commands/doctor-integration.test.mjs +837 -0
  119. package/clients/cli/commands/doctor.mjs +190 -0
  120. package/clients/cli/commands/integration-check.doc.mjs +85 -0
  121. package/clients/cli/commands/integration-check.test.mjs +267 -0
  122. package/clients/cli/commands/integration.doc.mjs +6 -2
  123. package/clients/cli/commands/integration.mjs +107 -1
  124. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  125. package/clients/cli/commands/theme-build.doc.mjs +2 -2
  126. package/clients/cli/index.mjs +1 -0
  127. package/clients/cli/lib/component-format.mjs +5 -1
  128. package/foundation/config/project.mjs +82 -0
  129. package/foundation/discovery/authoring-self-docs.mjs +1 -0
  130. package/foundation/discovery/component-discovery.d.mts +190 -10
  131. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  132. package/foundation/discovery/component-discovery.mjs +864 -56
  133. package/foundation/discovery/component-discovery.test.mjs +96 -0
  134. package/foundation/discovery/component-replacement.mjs +18 -0
  135. package/foundation/discovery/component-replacement.test.mjs +40 -0
  136. package/foundation/discovery/doc-types.d.mts +157 -0
  137. package/foundation/discovery/doc-types.mjs +1224 -0
  138. package/foundation/discovery/doc-types.test.mjs +652 -0
  139. package/foundation/discovery/docs-discovery.d.mts +81 -0
  140. package/foundation/discovery/docs-discovery.mjs +57 -0
  141. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  142. package/foundation/discovery/integration-doc-files.mjs +231 -0
  143. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  144. package/foundation/discovery/module-exports.d.mts +27 -0
  145. package/foundation/discovery/module-exports.mjs +356 -31
  146. package/foundation/discovery/module-exports.test.mjs +172 -0
  147. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  148. package/foundation/discovery/package-tsconfig.mjs +228 -0
  149. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  150. package/foundation/discovery/project-typescript.d.mts +23 -0
  151. package/foundation/discovery/project-typescript.mjs +61 -0
  152. package/foundation/discovery/prop-drift.d.mts +278 -0
  153. package/foundation/discovery/prop-drift.mjs +2263 -0
  154. package/foundation/discovery/prop-drift.test.mjs +2219 -0
  155. package/foundation/discovery/template-adapter.mjs +74 -5
  156. package/foundation/discovery/type-match.d.mts +53 -0
  157. package/foundation/discovery/type-match.mjs +660 -0
  158. package/foundation/discovery/type-match.test.mjs +169 -0
  159. package/foundation/discovery/type-text.d.mts +68 -0
  160. package/foundation/discovery/type-text.mjs +658 -0
  161. package/foundation/discovery/type-text.test.mjs +196 -0
  162. package/foundation/discovery/unresolved-references.d.mts +62 -0
  163. package/foundation/discovery/unresolved-references.mjs +790 -0
  164. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  165. package/foundation/doc-compiler/doc-loads.test.mjs +11 -1
  166. package/foundation/doc-compiler/inputs.mjs +23 -0
  167. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  168. package/foundation/doc-compiler/links.d.mts +9 -5
  169. package/foundation/doc-compiler/links.mjs +4 -2
  170. package/foundation/integrations/contribution-inventory.mjs +6 -1
  171. package/foundation/response/response-types.doc.mjs +7 -2
  172. package/foundation/xle/registry.mjs +4 -0
  173. package/foundation/xle/registry.test.mjs +28 -0
  174. 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(/path separator|invalid theme name/i);
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(['theme', 'build', path.relative(project, themeFile)], project);
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 an --out path outside the project as ERR_PATH_TRAVERSAL', async () => {
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', '../escaped.css'],
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(registered.has(envelope.code)).toBe(true);
129
- expect(fs.existsSync(path.join(tmpDir, 'escaped.css'))).toBe(false);
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(['theme', 'build', themeFile, '-o', outFile]);
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: 'Check integration component names and replacements against Core',
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
- {label: 'Check the local integration', cli: 'astryx doctor integration components'},
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, and replaces findings for a package without the CLI range, are warnings',
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
- {code: 0, when: 'no error-severity issues (warnings are allowed)'},
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'],