@astryxdesign/cli 0.1.1-canary.a514b99 → 0.1.1-canary.b6ade74

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.1-canary.a514b99",
3
+ "version": "0.1.1-canary.b6ade74",
4
4
  "displayName": "CLI",
5
5
  "description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
6
6
  "author": "Meta Open Source",
@@ -54,9 +54,9 @@
54
54
  "jscodeshift": "^17.3.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@astryxdesign/core": "0.1.1-canary.a514b99",
58
- "@astryxdesign/lab": "0.1.1-canary.a514b99",
59
- "@astryxdesign/theme-neutral": "0.1.1-canary.a514b99"
57
+ "@astryxdesign/core": "0.1.1-canary.b6ade74",
58
+ "@astryxdesign/lab": "0.1.1-canary.b6ade74",
59
+ "@astryxdesign/theme-neutral": "0.1.1-canary.b6ade74"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@astryxdesign/core": {
@@ -70,9 +70,9 @@
70
70
  }
71
71
  },
72
72
  "devDependencies": {
73
- "@astryxdesign/core": "0.1.1-canary.a514b99",
74
- "@astryxdesign/lab": "0.1.1-canary.a514b99",
75
- "@astryxdesign/theme-neutral": "0.1.1-canary.a514b99"
73
+ "@astryxdesign/core": "0.1.1-canary.b6ade74",
74
+ "@astryxdesign/lab": "0.1.1-canary.b6ade74",
75
+ "@astryxdesign/theme-neutral": "0.1.1-canary.b6ade74"
76
76
  },
77
77
  "scripts": {
78
78
  "astryx": "node bin/astryx.mjs",
@@ -275,6 +275,65 @@ const STRUCTURAL = new Set([
275
275
  'FormLayout', 'Center',
276
276
  ]);
277
277
 
278
+ const SPATIAL_PROPS = [
279
+ 'padding', 'contentPadding', 'gap', 'rowGap', 'columnGap',
280
+ 'columns', 'minChildWidth', 'hasDivider', 'defaultHasDividers',
281
+ 'variant', 'density', 'role', 'height', 'width', 'maxWidth',
282
+ ];
283
+
284
+ /**
285
+ * Copy allowlisted layout props verbatim from a JSX opening-tag fragment.
286
+ * Uses quote/brace matching so object literals and spaced strings stay intact.
287
+ *
288
+ * @param {string} tagText
289
+ * @returns {string[]}
290
+ */
291
+ function extractSpatialAttrs(tagText) {
292
+ const attrs = [];
293
+ for (const name of SPATIAL_PROPS) {
294
+ const eqMatch = tagText.match(new RegExp(`\\b${name}\\s*=\\s*`));
295
+ if (eqMatch) {
296
+ const start = eqMatch.index;
297
+ let i = eqMatch.index + eqMatch[0].length;
298
+ const rest = tagText.slice(i);
299
+
300
+ if (rest[0] === '"' || rest[0] === "'") {
301
+ const q = rest[0];
302
+ i += 1;
303
+ while (i < tagText.length && tagText[i] !== q) {
304
+ if (tagText[i] === '\\') i += 1;
305
+ i += 1;
306
+ }
307
+ if (i < tagText.length) i += 1;
308
+ } else if (rest[0] === '{') {
309
+ let depth = 0;
310
+ while (i < tagText.length) {
311
+ if (tagText[i] === '{') depth += 1;
312
+ else if (tagText[i] === '}') {
313
+ depth -= 1;
314
+ if (depth === 0) {
315
+ i += 1;
316
+ break;
317
+ }
318
+ }
319
+ i += 1;
320
+ }
321
+ } else {
322
+ const bare = rest.match(/^[^\s/>]+/);
323
+ i += bare ? bare[0].length : 0;
324
+ }
325
+
326
+ attrs.push(tagText.slice(start, i).trim());
327
+ continue;
328
+ }
329
+
330
+ if (new RegExp(`\\b${name}(?=[\\s/>])`).test(tagText)) {
331
+ attrs.push(name);
332
+ }
333
+ }
334
+ return attrs;
335
+ }
336
+
278
337
  function extractSkeleton(source) {
279
338
  const lines = source.split('\n');
280
339
  const out = [];
@@ -309,16 +368,7 @@ function extractSkeleton(source) {
309
368
  if (lines[j].includes('>')) break;
310
369
  }
311
370
 
312
- const props = [];
313
- const propRegex = /\b(padding|contentPadding|gap|rowGap|columnGap|columns|minChildWidth|hasDivider|defaultHasDividers|variant|density|role|height|width|maxWidth)\s*[=]\s*\{?\s*['"]?([^}'"\s,/>]+)/g;
314
- let m;
315
- while ((m = propRegex.exec(tagText)) !== null) {
316
- const val = m[2];
317
- if (val === 'true') props.push(m[1]);
318
- else if (/^\d+$/.test(val)) props.push(`${m[1]}={${val}}`);
319
- else props.push(`${m[1]}="${val}"`);
320
- }
321
-
371
+ const props = extractSpatialAttrs(tagText);
322
372
  const hasSpatialProps = props.length > 0;
323
373
  const propStr = hasSpatialProps ? ' ' + props.join(' ') : '';
324
374
  const isVStack = comp === 'VStack' || comp === 'HStack';
@@ -74,5 +74,7 @@ describe('template --skeleton component extraction (prefix-agnostic)', () => {
74
74
  expect(result.data.skeleton.trim().length).toBeGreaterThan(0);
75
75
  expect(result.data.skeleton).toMatch(/<[A-Z]\w+/);
76
76
  expect(result.data.skeleton).not.toContain('<XDS');
77
+
78
+ expect(result.data.skeleton).toContain('columns={{minWidth: 200}}');
77
79
  });
78
80
  });
@@ -29,6 +29,37 @@ import {requireInteractive} from '../utils/interactive.mjs';
29
29
  const VALID_FEATURES = ['agents', 'theme', 'template'];
30
30
  const run = getRunPrefix();
31
31
 
32
+ /**
33
+ * Build the "Next steps" lines printed at the end of `astryx init`.
34
+ *
35
+ * Theme guidance must match the runtime recommendation emitted by core's
36
+ * <Theme> component (packages/core/src/theme/Theme.tsx): the pre-built theme
37
+ * path (`/built` import + `theme.css`) plus the base CSS import, so users
38
+ * don't end up with an unstyled app or the slower runtime style-injection
39
+ * path. See https://github.com/facebook/astryx/issues/3080.
40
+ *
41
+ * Exported for testing.
42
+ *
43
+ * @param {string} runPrefix package-manager run prefix (e.g. `npx`)
44
+ * @returns {string[]} ordered list of human-facing lines
45
+ */
46
+ export function getNextSteps(runPrefix) {
47
+ return [
48
+ '',
49
+ ' Next steps:',
50
+ " 1. Import base styles: import '@astryxdesign/core/reset.css'",
51
+ " and import '@astryxdesign/core/astryx.css'",
52
+ " 2. Import components: import { Button } from '@astryxdesign/core'",
53
+ ' 3. Optionally add a theme (use the pre-built path for performance):',
54
+ " import { neutralTheme } from '@astryxdesign/theme-neutral/built'",
55
+ " import '@astryxdesign/theme-neutral/theme.css'",
56
+ ' <Theme theme={neutralTheme}>...</Theme>',
57
+ ` For custom themes, run \`${runPrefix} astryx theme build <file>\` to generate the built artifacts.`,
58
+ ` 4. ${runPrefix} astryx --help for all commands`,
59
+ '',
60
+ ];
61
+ }
62
+
32
63
  function isCancel(value) {
33
64
  if (p.isCancel(value)) {
34
65
  p.cancel('Setup cancelled.');
@@ -250,13 +281,8 @@ export function registerInit(program) {
250
281
  // Outro
251
282
  p.outro('Design system initialized!');
252
283
 
253
- humanLog('');
254
- humanLog(' Next steps:');
255
- humanLog(" 1. Import components: import { Button } from '@astryxdesign/core'");
256
- humanLog(' 2. Optionally add a theme:');
257
- humanLog(" import { neutralTheme } from '@astryxdesign/theme-neutral'");
258
- humanLog(' <Theme theme={neutralTheme}>...</Theme>');
259
- humanLog(` 3. ${run} astryx --help for all commands`);
260
- humanLog('');
284
+ for (const line of getNextSteps(run)) {
285
+ humanLog(line);
286
+ }
261
287
  });
262
288
  }
@@ -0,0 +1,46 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Regression test for `astryx init` "Next steps" theme guidance.
5
+ *
6
+ * The init command previously steered users toward the slower runtime
7
+ * style-injection path:
8
+ *
9
+ * import { neutralTheme } from '@astryxdesign/theme-neutral'
10
+ * <Theme theme={neutralTheme}>...</Theme>
11
+ *
12
+ * ...which contradicted the runtime console warning emitted by core's
13
+ * <Theme> component (packages/core/src/theme/Theme.tsx) recommending the
14
+ * pre-built path, and left users with an unstyled app because the base CSS
15
+ * imports were never mentioned (facebook/astryx#3080).
16
+ *
17
+ * These assertions lock in the corrected guidance: base CSS imports, the
18
+ * pre-built (`/built` + `theme.css`) theme path, and the custom-theme build
19
+ * command.
20
+ */
21
+
22
+ import {describe, it, expect} from 'vitest';
23
+ import {getNextSteps} from './init.mjs';
24
+
25
+ describe('init Next steps theme guidance', () => {
26
+ const text = getNextSteps('npx').join('\n');
27
+
28
+ it('mentions the base CSS imports so the app is not left unstyled', () => {
29
+ expect(text).toContain("'@astryxdesign/core/reset.css'");
30
+ expect(text).toContain("'@astryxdesign/core/astryx.css'");
31
+ });
32
+
33
+ it('uses the pre-built theme path matching the runtime recommendation', () => {
34
+ expect(text).toContain("'@astryxdesign/theme-neutral/built'");
35
+ expect(text).toContain("'@astryxdesign/theme-neutral/theme.css'");
36
+ });
37
+
38
+ it('mentions building custom themes via `astryx theme build`', () => {
39
+ expect(text).toContain('astryx theme build <file>');
40
+ });
41
+
42
+ it('does not steer users to the runtime style-injection import', () => {
43
+ // The bare source import (no `/built`) is the slow runtime-injection path.
44
+ expect(text).not.toContain("from '@astryxdesign/theme-neutral'");
45
+ });
46
+ });