@astryxdesign/cli 0.1.6-canary.ff5dfca → 0.1.7-canary.04cd8f7

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 (50) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +115 -19
  3. package/docs/cli-integrations.doc.mjs +150 -0
  4. package/docs/getting-started.doc.mjs +9 -9
  5. package/docs/migration.doc.mjs +18 -18
  6. package/docs/principles.doc.dense.mjs +1 -1
  7. package/docs/principles.doc.mjs +6 -6
  8. package/docs/principles.doc.zh.mjs +1 -1
  9. package/docs/styling-libraries.doc.mjs +3 -3
  10. package/docs/styling.doc.mjs +4 -4
  11. package/docs/theme.doc.dense.mjs +2 -2
  12. package/docs/theme.doc.mjs +7 -7
  13. package/docs/theme.doc.zh.mjs +1 -1
  14. package/docs/tokens.doc.mjs +1 -1
  15. package/docs/working-with-ai.doc.mjs +18 -18
  16. package/package.json +9 -10
  17. package/src/api/doctor.mjs +3 -3
  18. package/src/codemods/ensure-jscodeshift.mjs +11 -27
  19. package/src/codemods/run-codemod.mjs +1 -1
  20. package/src/codemods/runner.mjs +2 -2
  21. package/src/commands/agent-docs.mjs +7 -8
  22. package/src/commands/agent-docs.test.mjs +11 -4
  23. package/src/commands/build-theme.mjs +10 -71
  24. package/src/commands/build.mjs +15 -15
  25. package/src/commands/component/index.mjs +4 -4
  26. package/src/commands/discover.mjs +7 -5
  27. package/src/commands/docs.mjs +4 -4
  28. package/src/commands/hook/index.mjs +4 -4
  29. package/src/commands/init.mjs +48 -152
  30. package/src/commands/init.next-steps.test.mjs +1 -1
  31. package/src/commands/interactive-guard.test.mjs +19 -22
  32. package/src/commands/json-contract.test.mjs +1 -1
  33. package/src/commands/layout.mjs +1 -1
  34. package/src/commands/search.mjs +4 -4
  35. package/src/commands/swizzle.mjs +11 -34
  36. package/src/commands/template.mjs +11 -31
  37. package/src/commands/upgrade.mjs +9 -6
  38. package/src/commands/upgrade.test.mjs +1 -1
  39. package/src/index.mjs +5 -6
  40. package/src/lib/component-format.mjs +2 -1
  41. package/src/lib/term-log.mjs +48 -0
  42. package/src/utils/package-manager.mjs +78 -0
  43. package/src/utils/package-manager.test.mjs +108 -1
  44. package/src/utils/path-safety.mjs +0 -18
  45. package/src/utils/update-check.mjs +2 -1
  46. package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
  47. package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
  48. package/docs/integration-authoring.md +0 -105
  49. package/src/utils/interactive.mjs +0 -76
  50. package/src/utils/interactive.test.mjs +0 -70
@@ -1,105 +0,0 @@
1
- # Authoring an Astryx Integration
2
-
3
- > **Status:** working notes. This should eventually move to the public wiki
4
- > alongside the rest of the integration-authoring guidance; it lives here for
5
- > now so it ships and is versioned with the CLI.
6
-
7
- An **Integration** is an npm package that contributes components, templates, and/or
8
- codemods to a consumer's design-system workflow. Consumers install the 3rd party
9
- package, add a line to their astryx.config file:
10
-
11
- ```js
12
- import {createConfig} from '@astryxdesign/cli/config';
13
-
14
- export default createConfig({
15
- integrations: ['@acme/astryx-widgets'],
16
- ...
17
- });
18
- ```
19
-
20
- Then the integration's components and templates will be surfaced alongside Astryx
21
- components in the Astryx CLI.
22
-
23
- ```sh
24
- astryx component AcmeCarousel --props
25
- astryx component --list --package @acme/astryx-widgets
26
- ```
27
-
28
- ## The Integration File
29
-
30
- In order to register your package as an Astryx Integration, create an
31
- `astryx.integration.{ts,mjs,js}` file as a sibling to your `package.json`. This file
32
- tells Astryx where to find your components, templates, codemods, etc.
33
-
34
- ```js
35
- // astryx.integration.{ts,mjs,js}
36
- import {createIntegration} from '@astryxdesign/cli/integration';
37
-
38
- export default createIntegration({
39
- components: './components',
40
- templates: './templates',
41
- codemods: './codemods',
42
- issuesUrl: 'https://github.com/acme/widgets/issues',
43
- });
44
- ```
45
-
46
- ## Components
47
-
48
- Your components themselves may be exported from your library as you see fit (consumers
49
- will still import them from your package) but Astryx CLI will look for a .doc.{ts,mjs,js}
50
- file with the same stem e.g. `AcmeCarousel.tsx` and `AcmeCarousel.doc.ts`.
51
-
52
- ```js
53
- // AcmeCarousel.doc.ts
54
- import {createComponentDoc} from '@astryxdesign/cli/doc';
55
-
56
- export default createComponentDoc({
57
- name: 'AcmeCarousel',
58
- description: '...',
59
- ...
60
- });
61
- ```
62
-
63
- ## Templates
64
-
65
- Templates are typically not exported from the package directly, but instead accessed
66
- via the Astryx CLI. Consumers can look through your templates and materialize them
67
- into their apps.
68
-
69
- You define a template with the `createPageTemplate` (for full pages) or `createBlockTemplate`
70
- (for smaller chunks). e.g. `AcmeLandingPage.tsx`, `AcmeLandingPage.template.ts`
71
-
72
- ```js
73
- // AcmeLandingPage.template.ts
74
- import {createPageTemplate} from '@astryxdesign/cli/template';
75
-
76
- export default createPageTemplate({
77
- ...
78
- });
79
- ```
80
-
81
- Note that, since the CLI needs access to the template source code, you need to make sure
82
- that it is included in your published package. This will also allow us to render previews
83
- of templates in the future by bundling your template into a doc site build.
84
-
85
- Typically, this is done via the package.json `exports` key.
86
-
87
- ```jsonc
88
- {
89
- "exports": {
90
- // ...
91
- "./templates/*.tsx": "./templates/*.tsx",
92
- },
93
- }
94
- ```
95
-
96
- In order to verify that it's working, you can test importing the template component like this:
97
-
98
- ```ts
99
- import('@acme/astryx-widgets/templates/AcmeLandingPage.tsx');
100
- ```
101
-
102
- Import **with the `.tsx` extension** — an extensionless specifier won't resolve
103
- under `moduleResolution: bundler`. The extensionful `"./templates/*.tsx"` export
104
- above is what lets that import type-check without consumers enabling
105
- `allowImportingTsExtensions`.
@@ -1,76 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file Interactivity contract for the CLI.
5
- *
6
- * A single source of truth for "can this process prompt the user?". Several
7
- * commands launch @clack/prompts wizards; in a non-interactive context (CI,
8
- * piped stdin/stdout, no TTY) those prompts block forever. Historically each
9
- * command answered this question differently — some checked `stdout.isTTY`,
10
- * one checked `stdin.isTTY`, one added `!process.env.CI`, and some had no
11
- * guard at all. This module centralizes the check so every command behaves
12
- * identically.
13
- *
14
- * Two entry points, for two situations:
15
- *
16
- * - `requireInteractive()` — for commands whose prompt IS the work
17
- * (e.g. `astryx init`, `astryx theme`). With no TTY there is nothing to do, so
18
- * fail fast (exit 1) with actionable, non-interactive guidance.
19
- *
20
- * - `isInteractive()` — for commands with an OPTIONAL secondary prompt
21
- * that runs after the primary work has already succeeded; callers use
22
- * this to skip the prompt gracefully in non-interactive contexts.
23
- */
24
-
25
- /**
26
- * True when the process can safely run an interactive prompt.
27
- *
28
- * Requires BOTH stdin and stdout to be TTYs (clack reads stdin and renders to
29
- * stdout) and that we are not in a CI environment. A CI runner may allocate a
30
- * pseudo-TTY, so `process.env.CI` is an explicit override: never prompt in CI.
31
- *
32
- * @param {object} [env] - Override hook for tests.
33
- * @param {boolean} [env.stdinTTY=process.stdin.isTTY]
34
- * @param {boolean} [env.stdoutTTY=process.stdout.isTTY]
35
- * @param {boolean} [env.ci=Boolean(process.env.CI)]
36
- * @returns {boolean}
37
- */
38
- export function isInteractive({
39
- stdinTTY = Boolean(process.stdin && process.stdin.isTTY),
40
- stdoutTTY = Boolean(process.stdout && process.stdout.isTTY),
41
- ci = Boolean(process.env.CI),
42
- } = {}) {
43
- if (ci) return false;
44
- return stdinTTY && stdoutTTY;
45
- }
46
-
47
- /**
48
- * Guard for commands whose primary action is an interactive wizard. When the
49
- * process is non-interactive, prints an actionable error and exits 1 instead
50
- * of hanging on a prompt that will never receive input.
51
- *
52
- * @param {object} options
53
- * @param {string} options.command - Command name for the message, e.g. 'init'.
54
- * @param {string} options.hint - Concrete non-interactive invocation, e.g.
55
- * '`pnpm astryx init --all` or `--features agents,theme,template`'.
56
- * @param {boolean} [options.json=false] - When true, the command does not
57
- * support --json; we still exit 1 but skip the human-formatted guidance.
58
- * @param {object} [env] - Forwarded to isInteractive (test hook).
59
- * @returns {void} Returns when interactive; otherwise calls process.exit(1).
60
- */
61
- export function requireInteractive({command, hint, json = false} = {}, env) {
62
- if (isInteractive(env)) return;
63
- const name = command ? `astryx ${command}` : 'this command';
64
- console.error(
65
- `Error: \`${name}\` with no flags is interactive and requires a TTY.`,
66
- );
67
- if (hint) {
68
- console.error(`Run non-interactively with: ${hint}`);
69
- }
70
- if (!json) {
71
- console.error(
72
- 'Detected a non-interactive environment (no TTY, piped I/O, or CI=1).',
73
- );
74
- }
75
- process.exit(1);
76
- }
@@ -1,70 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file Unit tests for the interactivity contract.
5
- *
6
- * isInteractive() is pure given its injected env, so these are fast unit
7
- * tests. The "does the command actually fail fast instead of hanging" proof
8
- * lives in the per-command subprocess tests (init/theme non-interactive).
9
- */
10
-
11
- import {describe, it, expect, vi, afterEach} from 'vitest';
12
- import {isInteractive, requireInteractive} from './interactive.mjs';
13
-
14
- describe('isInteractive', () => {
15
- it('is true only when stdin AND stdout are TTYs and not CI', () => {
16
- expect(isInteractive({stdinTTY: true, stdoutTTY: true, ci: false})).toBe(true);
17
- });
18
-
19
- it('is false when stdin is not a TTY (piped input)', () => {
20
- expect(isInteractive({stdinTTY: false, stdoutTTY: true, ci: false})).toBe(false);
21
- });
22
-
23
- it('is false when stdout is not a TTY (piped output)', () => {
24
- expect(isInteractive({stdinTTY: true, stdoutTTY: false, ci: false})).toBe(false);
25
- });
26
-
27
- it('is false in CI even with a pseudo-TTY on both streams', () => {
28
- expect(isInteractive({stdinTTY: true, stdoutTTY: true, ci: true})).toBe(false);
29
- });
30
- });
31
-
32
- describe('requireInteractive', () => {
33
- afterEach(() => {
34
- vi.restoreAllMocks();
35
- });
36
-
37
- it('returns (does not exit) when interactive', () => {
38
- const exit = vi.spyOn(process, 'exit').mockImplementation(() => {
39
- throw new Error('exit should not be called');
40
- });
41
- expect(() =>
42
- requireInteractive(
43
- {command: 'init', hint: '`astryx init --all`'},
44
- {stdinTTY: true, stdoutTTY: true, ci: false},
45
- ),
46
- ).not.toThrow();
47
- expect(exit).not.toHaveBeenCalled();
48
- });
49
-
50
- it('exits 1 with actionable guidance when non-interactive', () => {
51
- const exit = vi
52
- .spyOn(process, 'exit')
53
- .mockImplementation(() => {
54
- throw new Error('__exit__');
55
- });
56
- const err = vi.spyOn(console, 'error').mockImplementation(() => {});
57
- expect(() =>
58
- requireInteractive(
59
- {command: 'theme', hint: '`astryx theme <preset>`'},
60
- {stdinTTY: false, stdoutTTY: false, ci: false},
61
- ),
62
- ).toThrow('__exit__');
63
- expect(exit).toHaveBeenCalledWith(1);
64
- const output = err.mock.calls.map(c => c.join(' ')).join('\n');
65
- expect(output).toMatch(/requires a TTY/i);
66
- expect(output).toMatch(/astryx theme <preset>/);
67
- expect(output).toMatch(/`astryx theme`/);
68
- expect(output).not.toMatch(/\bxds\b/);
69
- });
70
- });