@astryxdesign/cli 0.1.7 → 0.1.8-canary.4942fe4
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/CHANGELOG.md +40 -0
- package/README.md +115 -19
- package/docs/cli-integrations.doc.mjs +150 -0
- package/docs/getting-started.doc.mjs +9 -9
- package/docs/migration.doc.mjs +18 -18
- package/docs/principles.doc.dense.mjs +1 -1
- package/docs/principles.doc.mjs +6 -6
- package/docs/principles.doc.zh.mjs +1 -1
- package/docs/styling-libraries.doc.mjs +3 -3
- package/docs/styling.doc.mjs +4 -4
- package/docs/theme.doc.dense.mjs +2 -2
- package/docs/theme.doc.mjs +7 -7
- package/docs/theme.doc.zh.mjs +1 -1
- package/docs/tokens.doc.mjs +1 -1
- package/docs/working-with-ai.doc.mjs +19 -19
- package/package.json +11 -10
- package/scripts/postinstall.mjs +74 -0
- package/src/api/doctor.mjs +3 -3
- package/src/codemods/__tests__/registry.test.mjs +1 -0
- package/src/codemods/ensure-jscodeshift.mjs +11 -27
- package/src/codemods/registry.mjs +1 -0
- package/src/codemods/run-codemod.mjs +1 -1
- package/src/codemods/runner.mjs +2 -2
- package/src/codemods/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +42 -7
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +43 -0
- package/src/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +102 -3
- package/src/codemods/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +72 -6
- package/src/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +161 -0
- package/src/codemods/transforms/v0.1.8/index.mjs +19 -0
- package/src/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +234 -0
- package/src/commands/agent-docs.mjs +136 -20
- package/src/commands/agent-docs.test.mjs +123 -10
- package/src/commands/build-theme.mjs +10 -71
- package/src/commands/build.mjs +15 -15
- package/src/commands/cli-postinstall.test.mjs +42 -0
- package/src/commands/component/index.mjs +4 -4
- package/src/commands/discover.mjs +7 -5
- package/src/commands/docs.mjs +4 -4
- package/src/commands/hook/index.mjs +4 -4
- package/src/commands/init.mjs +48 -152
- package/src/commands/init.next-steps.test.mjs +1 -1
- package/src/commands/interactive-guard.test.mjs +19 -22
- package/src/commands/json-contract.test.mjs +2 -2
- package/src/commands/layout.mjs +1 -1
- package/src/commands/search.mjs +4 -4
- package/src/commands/setup-nudge.test.mjs +108 -0
- package/src/commands/swizzle.mjs +11 -34
- package/src/commands/template.mjs +11 -31
- package/src/commands/upgrade.mjs +138 -32
- package/src/commands/upgrade.test.mjs +155 -1
- package/src/index.mjs +40 -6
- package/src/lib/component-format.mjs +2 -1
- package/src/lib/term-log.mjs +48 -0
- package/src/types/upgrade.d.ts +39 -2
- package/src/utils/package-manager.mjs +78 -0
- package/src/utils/package-manager.test.mjs +108 -1
- package/src/utils/path-safety.mjs +0 -18
- package/src/utils/update-check.mjs +2 -1
- package/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +4 -4
- package/templates/blocks/components/Avatar/AvatarGroup.tsx +2 -2
- package/templates/blocks/components/Avatar/AvatarInitialsFallback.tsx +1 -1
- package/templates/blocks/components/Avatar/AvatarShowcase.tsx +4 -4
- package/templates/blocks/components/Avatar/AvatarUserCard.tsx +1 -1
- package/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
- package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
- package/templates/blocks/components/AvatarGroup/AvatarGroupShowcase.tsx +2 -2
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowCustomText.tsx +1 -1
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowDefault.tsx +1 -1
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowShowcase.tsx +2 -2
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotShowcase.tsx +3 -3
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +3 -3
- package/templates/blocks/components/Carousel/CarouselSnap.tsx +1 -1
- package/templates/blocks/components/ChatMessage/ChatMessageAvatarName.tsx +2 -2
- package/templates/blocks/components/ChatMessage/ChatMessageMultiBubble.tsx +1 -1
- package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleGrouping.tsx +1 -1
- package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleMetadata.tsx +1 -1
- package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +5 -9
- package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -1
- package/templates/blocks/components/CodeBlock/CodeBlockTerminal.tsx +1 -1
- package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -1
- package/templates/blocks/components/Item/ItemWithMedia.tsx +2 -2
- package/templates/blocks/components/ListItem/ListItemWithMedia.tsx +2 -2
- package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -6
- package/templates/blocks/components/Stack/StackFillItem.tsx +2 -6
- package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
- package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
- package/templates/blocks/components/TypeaheadItem/TypeaheadItemShowcase.tsx +1 -1
- package/templates/pages/ai-chat/page.tsx +4 -4
- package/templates/pages/dashboard-portfolio/page.tsx +3 -11
- package/templates/pages/detail-page/page.tsx +5 -12
- package/templates/pages/documentation-design/page.tsx +1 -1
- package/templates/pages/messaging-shell/page.tsx +6 -8
- package/templates/pages/table-grouped/page.tsx +9 -14
- package/templates/pages/table-page/page.tsx +7 -9
- package/templates/pages/table-page-heatmap-status/page.tsx +5 -13
- package/docs/integration-authoring.md +0 -105
- package/src/utils/interactive.mjs +0 -76
- package/src/utils/interactive.test.mjs +0 -70
package/docs/theme.doc.mjs
CHANGED
|
@@ -148,7 +148,7 @@ function App() {
|
|
|
148
148
|
type: 'code',
|
|
149
149
|
lang: 'bash',
|
|
150
150
|
label: 'Scaffold with CLI',
|
|
151
|
-
code: '
|
|
151
|
+
code: 'astryx theme',
|
|
152
152
|
},
|
|
153
153
|
],
|
|
154
154
|
},
|
|
@@ -287,7 +287,7 @@ const brandTheme = defineTheme({
|
|
|
287
287
|
},
|
|
288
288
|
{
|
|
289
289
|
type: 'prose',
|
|
290
|
-
text: 'Run `
|
|
290
|
+
text: 'Run `astryx component <Name>` to see a component\'s theming targets, public CSS variables, and which standard CSS properties are supported.',
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
293
|
type: 'list',
|
|
@@ -361,13 +361,13 @@ const brandTheme = defineTheme({
|
|
|
361
361
|
content: [
|
|
362
362
|
{
|
|
363
363
|
type: 'prose',
|
|
364
|
-
text: '`
|
|
364
|
+
text: '`astryx theme build` compiles a defineTheme file into production-ready artifacts. Recommended for SSR apps (Next.js, Remix) where styles must be present on first paint.',
|
|
365
365
|
},
|
|
366
366
|
{
|
|
367
367
|
type: 'code',
|
|
368
368
|
lang: 'bash',
|
|
369
369
|
label: 'Build a theme',
|
|
370
|
-
code: '
|
|
370
|
+
code: 'astryx theme build ./src/themes/ocean.ts',
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
type: 'prose',
|
|
@@ -432,7 +432,7 @@ import './themes/ocean.css';
|
|
|
432
432
|
[
|
|
433
433
|
'Import (custom theme)',
|
|
434
434
|
'defineTheme() directly',
|
|
435
|
-
"Built .js + .css from `
|
|
435
|
+
"Built .js + .css from `astryx theme build`",
|
|
436
436
|
],
|
|
437
437
|
[
|
|
438
438
|
'How it works',
|
|
@@ -462,7 +462,7 @@ import './themes/ocean.css';
|
|
|
462
462
|
items: [
|
|
463
463
|
'Use the /built subpath + theme.css for production SSR apps.',
|
|
464
464
|
'Use runtime themes during development for fast iteration.',
|
|
465
|
-
'Run `
|
|
465
|
+
'Run `astryx theme build` for custom themes to get the built artifacts.',
|
|
466
466
|
],
|
|
467
467
|
},
|
|
468
468
|
{
|
|
@@ -610,7 +610,7 @@ function ChartConfig() {
|
|
|
610
610
|
},
|
|
611
611
|
{
|
|
612
612
|
type: 'prose',
|
|
613
|
-
text: 'See `
|
|
613
|
+
text: 'See `astryx docs styling-libraries` for styling-library interop and `astryx docs tokens` for the full token reference.',
|
|
614
614
|
},
|
|
615
615
|
],
|
|
616
616
|
},
|
package/docs/theme.doc.zh.mjs
CHANGED
|
@@ -10,7 +10,7 @@ export const docsZh = {
|
|
|
10
10
|
{ section: 'Theme Props', title: 'Theme 属性', content: [null] },
|
|
11
11
|
{ section: 'Creating a Custom Theme', title: '创建自定义主题', content: [{ type: 'prose', text: '使用 CLI 向导(推荐)或手动 defineTheme。只覆盖与默认值不同的令牌。' }, null] },
|
|
12
12
|
{ section: 'defineTheme', title: 'defineTheme', content: [{ type: 'prose', text: '支持比例配置(typography、radius、motion)+ 显式令牌覆盖 + 组件覆盖。' }, null, null] },
|
|
13
|
-
{ section: 'Building Themes for Production', title: '生产构建', content: [{ type: 'prose', text: '
|
|
13
|
+
{ section: 'Building Themes for Production', title: '生产构建', content: [{ type: 'prose', text: 'astryx theme build 将 defineTheme 编译为静态 CSS。输出 .css + .js(__built:true)+ .d.ts。' }, null, null, null, null] },
|
|
14
14
|
{ section: 'Runtime vs Built Themes', title: '运行时 vs 构建', content: [{ type: 'prose', text: '运行时:useInsertionEffect 在客户端注入样式。构建:静态 CSS 在首次渲染时就存在。SSR 应用请使用 /built + theme.css。' }, null, null, null] },
|
|
15
15
|
{ section: 'Light/Dark Mode', title: '亮/暗模式', content: [{ type: 'prose', text: "令牌值使用 [light, dark] 元组实现自动模式切换。Theme 上 mode='system'(默认)跟随系统偏好。" }, null, null] },
|
|
16
16
|
{ section: 'Nesting Themes', title: '嵌套主题', content: [{ type: 'prose', text: '将不同部分包裹在独立的 <Theme> 提供者中。' }, null] },
|
package/docs/tokens.doc.mjs
CHANGED
|
@@ -1068,7 +1068,7 @@ export const docs = {
|
|
|
1068
1068
|
},
|
|
1069
1069
|
{
|
|
1070
1070
|
"type": "prose",
|
|
1071
|
-
"text": "See `
|
|
1071
|
+
"text": "See `astryx docs styling` for how to apply tokens via xstyle, className, and compound component patterns. See `astryx docs theme` for overriding tokens with defineTheme."
|
|
1072
1072
|
}
|
|
1073
1073
|
]
|
|
1074
1074
|
}
|
|
@@ -34,7 +34,7 @@ export const docs = {
|
|
|
34
34
|
type: 'code',
|
|
35
35
|
lang: 'text',
|
|
36
36
|
label: 'Paste this into your AI',
|
|
37
|
-
code: 'Install @astryxdesign/cli and run `npx
|
|
37
|
+
code: 'Install @astryxdesign/cli and run `npx @astryxdesign/cli init --features agents` to set up your Astryx context. Read the generated file.',
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
type: 'prose',
|
|
@@ -42,15 +42,15 @@ export const docs = {
|
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
type: 'prose',
|
|
45
|
-
text: '
|
|
45
|
+
text: 'By default this creates `AGENTS.md` (the tool-agnostic standard most agents read). To target a specific tool\'s file instead:',
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
type: 'code',
|
|
49
49
|
lang: 'bash',
|
|
50
50
|
label: 'Manual options',
|
|
51
|
-
code: `npx
|
|
52
|
-
npx
|
|
53
|
-
npx
|
|
51
|
+
code: `npx @astryxdesign/cli init --features agents --agent claude # .claude/CLAUDE.md
|
|
52
|
+
npx @astryxdesign/cli init --features agents --agent cursor # .cursorrules
|
|
53
|
+
npx @astryxdesign/cli init --features agents --agent codex # AGENTS.md (Copilot, Codex, etc.)`,
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
},
|
|
@@ -65,9 +65,9 @@ npx astryx init --features agents --agent codex # AGENTS.md (Copilot, Codex,
|
|
|
65
65
|
type: 'list',
|
|
66
66
|
style: 'ordered',
|
|
67
67
|
items: [
|
|
68
|
-
'`
|
|
69
|
-
'`
|
|
70
|
-
'`
|
|
68
|
+
'`astryx template --list`: find a related page pattern to use as reference',
|
|
69
|
+
'`astryx template <name> --skeleton`: study the layout structure',
|
|
70
|
+
'`astryx component <Name>`: read props and examples for every component used',
|
|
71
71
|
],
|
|
72
72
|
},
|
|
73
73
|
{
|
|
@@ -88,7 +88,7 @@ npx astryx init --features agents --agent codex # AGENTS.md (Copilot, Codex,
|
|
|
88
88
|
lang: 'bash',
|
|
89
89
|
label: 'Install as a Cursor user rule',
|
|
90
90
|
code: `mkdir -p ~/.cursor/rules
|
|
91
|
-
npx
|
|
91
|
+
npx @astryxdesign/cli init --features agents --agent-docs-path ~/.cursor/rules/xds.mdc`,
|
|
92
92
|
},
|
|
93
93
|
],
|
|
94
94
|
},
|
|
@@ -109,12 +109,12 @@ npx astryx init --features agents --agent-docs-path ~/.cursor/rules/xds.mdc`,
|
|
|
109
109
|
2. How do you make an Dialog non-dismissible?
|
|
110
110
|
3. What prop does Selector use for its items?
|
|
111
111
|
|
|
112
|
-
If you don't know all three, run \`npx
|
|
112
|
+
If you don't know all three, run \`npx @astryxdesign/cli init --features agents\` to generate agent docs, then read the generated file.`,
|
|
113
113
|
},
|
|
114
114
|
],
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
|
-
title: 'The
|
|
117
|
+
title: 'The astryx Pattern',
|
|
118
118
|
content: [
|
|
119
119
|
{
|
|
120
120
|
type: 'prose',
|
|
@@ -130,16 +130,16 @@ If you don't know all three, run \`npx astryx init --features agents\` to genera
|
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
type: 'prose',
|
|
133
|
-
text: 'With this alias, agents use `
|
|
133
|
+
text: 'With this alias, agents use `astryx component --list` instead of guessing the binary path. The `--` separator is standard npm convention for passing flags to scripts.',
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
type: 'code',
|
|
137
137
|
lang: 'bash',
|
|
138
138
|
label: 'Reliable CLI invocation',
|
|
139
|
-
code: `
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
code: `astryx component --list
|
|
140
|
+
astryx component Dialog --dense
|
|
141
|
+
astryx docs styling --dense
|
|
142
|
+
astryx docs tokens --dense`,
|
|
143
143
|
},
|
|
144
144
|
],
|
|
145
145
|
},
|
|
@@ -154,9 +154,9 @@ npx astryx docs tokens --dense`,
|
|
|
154
154
|
type: 'code',
|
|
155
155
|
lang: 'bash',
|
|
156
156
|
label: 'Dense output for pasting into AI conversations',
|
|
157
|
-
code: `
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
code: `astryx component Dialog --dense
|
|
158
|
+
astryx docs styling --dense
|
|
159
|
+
astryx docs tokens --dense`,
|
|
160
160
|
},
|
|
161
161
|
],
|
|
162
162
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8-canary.4942fe4",
|
|
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",
|
|
@@ -67,22 +67,22 @@
|
|
|
67
67
|
"files": [
|
|
68
68
|
"bin",
|
|
69
69
|
"src",
|
|
70
|
+
"scripts/postinstall.mjs",
|
|
70
71
|
"templates",
|
|
71
72
|
"docs",
|
|
72
73
|
"CHANGELOG.md"
|
|
73
74
|
],
|
|
74
75
|
"dependencies": {
|
|
75
|
-
"@clack/prompts": "^1.7.0",
|
|
76
76
|
"commander": "^12.1.0",
|
|
77
77
|
"jiti": "^2.7.0",
|
|
78
78
|
"jscodeshift": "^17.3.0",
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@astryxdesign/charts": "
|
|
83
|
-
"@astryxdesign/core": "
|
|
84
|
-
"@astryxdesign/lab": "
|
|
85
|
-
"@astryxdesign/theme-neutral": "
|
|
82
|
+
"@astryxdesign/charts": "0.1.8-canary.4942fe4",
|
|
83
|
+
"@astryxdesign/core": "0.1.8-canary.4942fe4",
|
|
84
|
+
"@astryxdesign/lab": "0.1.8-canary.4942fe4",
|
|
85
|
+
"@astryxdesign/theme-neutral": "0.1.8-canary.4942fe4",
|
|
86
86
|
"gpt-tokenizer": "^3.4.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
@@ -100,14 +100,15 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@astryxdesign/charts": "
|
|
104
|
-
"@astryxdesign/core": "
|
|
105
|
-
"@astryxdesign/lab": "
|
|
106
|
-
"@astryxdesign/theme-neutral": "
|
|
103
|
+
"@astryxdesign/charts": "0.1.8-canary.4942fe4",
|
|
104
|
+
"@astryxdesign/core": "0.1.8-canary.4942fe4",
|
|
105
|
+
"@astryxdesign/lab": "0.1.8-canary.4942fe4",
|
|
106
|
+
"@astryxdesign/theme-neutral": "0.1.8-canary.4942fe4",
|
|
107
107
|
"gpt-tokenizer": "^3.4.0"
|
|
108
108
|
},
|
|
109
109
|
"scripts": {
|
|
110
110
|
"astryx": "node bin/astryx.mjs",
|
|
111
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
111
112
|
"typecheck:template-docs": "tsc --project tsconfig.template-docs.json",
|
|
112
113
|
"typecheck:json-api": "tsc --project tsconfig.json-api.json && tsc --project tsconfig.api-contract.json"
|
|
113
114
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file @astryxdesign/cli postinstall — nudge to run `astryx init`.
|
|
5
|
+
*
|
|
6
|
+
* Enforcement layer 2 of making `astryx init` foolproof: when the CLI is
|
|
7
|
+
* installed as a project dependency and the project hasn't run init yet, print a
|
|
8
|
+
* one-line next-step so agents/humans discover it.
|
|
9
|
+
*
|
|
10
|
+
* Reuses the ONE setup check (isAstryxInitialized from agent-docs.mjs — a
|
|
11
|
+
* dep-free import chain, safe at install time). Non-interactive, never fails the
|
|
12
|
+
* install, and stays quiet:
|
|
13
|
+
* - in the monorepo/source build (not under node_modules),
|
|
14
|
+
* - during npx's transient fetch (npx runs the bin — likely `init` — right
|
|
15
|
+
* after, so nudging here would double up), and
|
|
16
|
+
* - once setup is already done.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {fileURLToPath, pathToFileURL} from 'node:url';
|
|
20
|
+
|
|
21
|
+
const HERE = fileURLToPath(import.meta.url);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pure decision: should the postinstall print the setup nudge? Split out so the
|
|
25
|
+
* matrix is unit-testable without an actual npm install.
|
|
26
|
+
*
|
|
27
|
+
* @param {object} opts
|
|
28
|
+
* @param {string} opts.scriptPath - Absolute path of this script (location tells
|
|
29
|
+
* us dependency vs monorepo vs npx-cache).
|
|
30
|
+
* @param {string} [opts.npmCommand] - process.env.npm_command ('install', 'exec', …).
|
|
31
|
+
* @param {boolean} opts.isSetUp - Whether the project already ran init.
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
export function shouldNudge({scriptPath, npmCommand, isSetUp} = {}) {
|
|
35
|
+
if (!scriptPath || !scriptPath.includes('node_modules')) return false; // monorepo/source build
|
|
36
|
+
if (scriptPath.includes('_npx') || npmCommand === 'exec') return false; // npx transient — init runs next
|
|
37
|
+
if (isSetUp) return false; // already set up — stay quiet
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @param {string} root @returns {Promise<boolean>} */
|
|
42
|
+
async function projectIsSetUp(root) {
|
|
43
|
+
try {
|
|
44
|
+
const {isAstryxInitialized} = await import('../src/commands/agent-docs.mjs');
|
|
45
|
+
return isAstryxInitialized(root);
|
|
46
|
+
} catch {
|
|
47
|
+
return false; // best-effort — if the check can't load, fall through and nudge
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main() {
|
|
52
|
+
const root = process.env.INIT_CWD || process.cwd();
|
|
53
|
+
const nudge = shouldNudge({
|
|
54
|
+
scriptPath: HERE,
|
|
55
|
+
npmCommand: process.env.npm_command,
|
|
56
|
+
isSetUp: await projectIsSetUp(root),
|
|
57
|
+
});
|
|
58
|
+
if (nudge) {
|
|
59
|
+
// Scoped package form (`@astryxdesign/cli`) — always resolves to us. Bare
|
|
60
|
+
// `npx astryx` would fetch an unrelated look-alike package (see PR #4151).
|
|
61
|
+
// After that lands, switch this to its getCliInvocation() single source of truth.
|
|
62
|
+
process.stdout.write(
|
|
63
|
+
'\nNext step: run `npx @astryxdesign/cli init` to finish setup and install the Astryx agent prompt.\n\n',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Run only when executed directly (`node scripts/postinstall.mjs`), never when
|
|
69
|
+
// imported by tests.
|
|
70
|
+
if (import.meta.url === pathToFileURL(process.argv[1] || '').href) {
|
|
71
|
+
main()
|
|
72
|
+
.catch(() => {})
|
|
73
|
+
.finally(() => process.exit(0));
|
|
74
|
+
}
|
package/src/api/doctor.mjs
CHANGED
|
@@ -26,7 +26,7 @@ import {createRequire} from 'node:module';
|
|
|
26
26
|
|
|
27
27
|
import {MIN_NODE_VERSION, isNodeVersionSupported} from '../lib/node-version.mjs';
|
|
28
28
|
import {CLI_ROOT, findCoreDir} from '../utils/paths.mjs';
|
|
29
|
-
import {detectPackageManager} from '../utils/package-manager.mjs';
|
|
29
|
+
import {detectPackageManager, getCliInvocation} from '../utils/package-manager.mjs';
|
|
30
30
|
import {findConfigPath, Project} from '../lib/project.mjs';
|
|
31
31
|
import {semverCompare} from '../utils/semver.mjs';
|
|
32
32
|
|
|
@@ -343,7 +343,7 @@ export function checkAgentDocs(ctx) {
|
|
|
343
343
|
label: 'AI agent docs',
|
|
344
344
|
status: 'info',
|
|
345
345
|
message: 'No agent docs (CLAUDE.md / AGENTS.md / .cursorrules) found.',
|
|
346
|
-
fix:
|
|
346
|
+
fix: `Generate agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
|
|
347
347
|
};
|
|
348
348
|
}
|
|
349
349
|
|
|
@@ -365,7 +365,7 @@ export function checkAgentDocs(ctx) {
|
|
|
365
365
|
label: 'AI agent docs',
|
|
366
366
|
status: 'warn',
|
|
367
367
|
message: `Agent docs present (${present.join(', ')}) but no Astryx section markers found.`,
|
|
368
|
-
fix:
|
|
368
|
+
fix: `Add the Astryx section to your agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
|
|
369
369
|
};
|
|
370
370
|
}
|
|
371
371
|
|
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Lazy jscodeshift installer
|
|
5
5
|
*
|
|
6
|
-
* Checks if jscodeshift is available and
|
|
6
|
+
* Checks if jscodeshift is available and installs it on-demand.
|
|
7
7
|
* Keeps the CLI lean — jscodeshift is only needed for codemods.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* or the command will fail with a helpful error message.
|
|
9
|
+
* Non-interactive by default (no prompts): pass `installDeps: true` to
|
|
10
|
+
* auto-install, otherwise the command fails fast with a helpful error.
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
13
|
import {execSync} from 'node:child_process';
|
|
15
|
-
import * as p from '
|
|
14
|
+
import * as p from '../lib/term-log.mjs';
|
|
16
15
|
import {detectPackageManager} from '../utils/package-manager.mjs';
|
|
17
16
|
|
|
18
17
|
/**
|
|
@@ -30,32 +29,17 @@ export async function ensureJscodeshift({installDeps = false, silent = false} =
|
|
|
30
29
|
} catch {
|
|
31
30
|
log.warn('jscodeshift is required for codemods but not installed.');
|
|
32
31
|
|
|
33
|
-
const isInteractive = process.stdout.isTTY && !process.env.CI;
|
|
34
|
-
|
|
35
32
|
if (installDeps) {
|
|
36
|
-
// Explicit opt-in — install without prompting
|
|
33
|
+
// Explicit opt-in — install without prompting.
|
|
37
34
|
return installJscodeshift(silent);
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Interactive TTY — prompt as before
|
|
50
|
-
const shouldInstall = await p.confirm({
|
|
51
|
-
message: 'Install jscodeshift now?',
|
|
52
|
-
initialValue: true,
|
|
53
|
-
});
|
|
54
|
-
if (p.isCancel(shouldInstall) || !shouldInstall) {
|
|
55
|
-
p.log.error('Cannot run codemods without jscodeshift.');
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
return installJscodeshift(silent);
|
|
37
|
+
// Non-interactive by default: fail fast with a helpful message instead of
|
|
38
|
+
// prompting (the CLI never blocks on a TTY).
|
|
39
|
+
log.error(
|
|
40
|
+
'Cannot run codemods without jscodeshift. Use --install-deps to auto-install.',
|
|
41
|
+
);
|
|
42
|
+
return false;
|
|
59
43
|
}
|
|
60
44
|
}
|
|
61
45
|
|
|
@@ -22,6 +22,7 @@ const registry = new Map([
|
|
|
22
22
|
['0.1.3', () => import('./transforms/v0.1.3/index.mjs')],
|
|
23
23
|
['0.1.5', () => import('./transforms/v0.1.5/index.mjs')],
|
|
24
24
|
['0.1.7', () => import('./transforms/v0.1.7/index.mjs')],
|
|
25
|
+
['0.1.8', () => import('./transforms/v0.1.8/index.mjs')],
|
|
25
26
|
]);
|
|
26
27
|
|
|
27
28
|
// Re-export from the shared utility so registry callers and other consumers
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
import * as fs from 'node:fs';
|
|
31
31
|
import * as path from 'node:path';
|
|
32
|
-
import * as p from '
|
|
32
|
+
import * as p from '../lib/term-log.mjs';
|
|
33
33
|
import {findConfigPath} from '../lib/project.mjs';
|
|
34
34
|
import {fixDirectiveCorruption, validateOutput} from './runner.mjs';
|
|
35
35
|
|
package/src/codemods/runner.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import * as fs from 'node:fs';
|
|
12
12
|
import * as path from 'node:path';
|
|
13
|
-
import * as p from '
|
|
13
|
+
import * as p from '../lib/term-log.mjs';
|
|
14
14
|
import {humanLog} from '../lib/json.mjs';
|
|
15
15
|
import {runConfigCodemod} from './run-codemod.mjs';
|
|
16
16
|
|
|
@@ -184,7 +184,7 @@ export async function runCodemods(
|
|
|
184
184
|
versionManifests,
|
|
185
185
|
{apply, path: srcPath, codemod, skipCodemods, silent = false},
|
|
186
186
|
) {
|
|
187
|
-
// No-op stub object so silent mode skips
|
|
187
|
+
// No-op stub object so silent mode skips log output entirely without
|
|
188
188
|
// littering the body with `if (!silent)` guards.
|
|
189
189
|
const log = silent
|
|
190
190
|
? {step() {}, info() {}, success() {}, warn() {}, error() {}, message() {}}
|
|
@@ -166,15 +166,50 @@ describe('drop-xds-prefix-imports', () => {
|
|
|
166
166
|
expect(output).toContain('export const other = Card;');
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
it('un-prefixes
|
|
169
|
+
it('un-prefixes override keys inside an @xds/core mock factory', async () => {
|
|
170
170
|
const input = [
|
|
171
|
-
`
|
|
172
|
-
`
|
|
171
|
+
`vi.mock('@xds/core/Text', async orig => ({`,
|
|
172
|
+
` ...(await orig<typeof import('@xds/core/Text')>()),`,
|
|
173
|
+
` useXDSTruncation: () => ({ref: vi.fn(), isTruncated: true, fullText: ''}),`,
|
|
174
|
+
`}));`,
|
|
173
175
|
].join('\n');
|
|
174
176
|
const output = await applyTransform(input);
|
|
175
|
-
expect(output).toContain('
|
|
176
|
-
expect(output).toContain('
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
expect(output).toContain('useTruncation:');
|
|
178
|
+
expect(output).not.toContain('useXDSTruncation');
|
|
179
|
+
// This codemod does not touch the module-path string (that is the
|
|
180
|
+
// module-specifiers codemod's job); the @xds/core/Text path stays here.
|
|
181
|
+
expect(output).toContain(`vi.mock('@xds/core/Text'`);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('un-prefixes component override keys inside a bare @xds/core jest.mock factory', async () => {
|
|
185
|
+
const input = [
|
|
186
|
+
`jest.mock('@xds/core', () => ({`,
|
|
187
|
+
` XDSButton: () => null,`,
|
|
188
|
+
` useXDSToast: () => ({}),`,
|
|
189
|
+
`}));`,
|
|
190
|
+
].join('\n');
|
|
191
|
+
const output = await applyTransform(input);
|
|
192
|
+
expect(output).toContain('Button:');
|
|
193
|
+
expect(output).toContain('useToast:');
|
|
194
|
+
expect(output).not.toContain('XDSButton');
|
|
195
|
+
expect(output).not.toContain('useXDSToast');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('does NOT touch mock factory keys for a non-@xds package', async () => {
|
|
199
|
+
const input = [
|
|
200
|
+
`vi.mock('some-other-pkg', () => ({`,
|
|
201
|
+
` useXDSFoo: () => 1,`,
|
|
202
|
+
`}));`,
|
|
203
|
+
].join('\n');
|
|
204
|
+
const output = await applyTransform(input);
|
|
205
|
+
expect(output).toBe(input);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('does NOT touch useXDS keys in an unrelated object literal (not a mock factory)', async () => {
|
|
209
|
+
const input = [`const config = {`, ` useXDSWhatever: true,`, `};`].join(
|
|
210
|
+
'\n',
|
|
211
|
+
);
|
|
212
|
+
const output = await applyTransform(input);
|
|
213
|
+
expect(output).toBe(input);
|
|
179
214
|
});
|
|
180
215
|
});
|
|
@@ -89,4 +89,47 @@ describe('migrate-xds-module-specifiers', () => {
|
|
|
89
89
|
expect(output).not.toContain('defaultTheme');
|
|
90
90
|
expect(output).not.toContain('neutralTheme as');
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
it('rewrites the mocked module path in vi.mock (including the inline import() type arg)', async () => {
|
|
94
|
+
const input = [
|
|
95
|
+
"vi.mock('@xds/core/Text', async orig => ({",
|
|
96
|
+
" ...(await orig<typeof import('@xds/core/Text')>()),",
|
|
97
|
+
'}));',
|
|
98
|
+
].join('\n');
|
|
99
|
+
const output = await applyTransform(input);
|
|
100
|
+
expect(output).toMatch(/vi\.mock\(['"]@astryxdesign\/core\/Text['"]/);
|
|
101
|
+
// The inline dynamic import() type argument is rewritten too.
|
|
102
|
+
expect(output).toMatch(/import\(['"]@astryxdesign\/core\/Text['"]\)/);
|
|
103
|
+
expect(output).not.toContain('@xds/');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('rewrites the mocked module path in jest.mock (bare @xds/core)', async () => {
|
|
107
|
+
const output = await applyTransform(
|
|
108
|
+
"jest.mock('@xds/core', () => ({}));",
|
|
109
|
+
'test.ts',
|
|
110
|
+
);
|
|
111
|
+
expect(output).toMatch(/jest\.mock\(['"]@astryxdesign\/core['"]/);
|
|
112
|
+
expect(output).not.toContain('@xds/');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('rewrites vi.doMock / jest.doMock and a bare mock() call', async () => {
|
|
116
|
+
const output = await applyTransform(
|
|
117
|
+
[
|
|
118
|
+
"vi.doMock('@xds/lab/Thing', () => ({}));",
|
|
119
|
+
"jest.doMock('@xds/core/Text', () => ({}));",
|
|
120
|
+
"mock('@xds/core', () => ({}));",
|
|
121
|
+
].join('\n'),
|
|
122
|
+
'test.ts',
|
|
123
|
+
);
|
|
124
|
+
expect(output).toMatch(/vi\.doMock\(['"]@astryxdesign\/lab\/Thing['"]/);
|
|
125
|
+
expect(output).toMatch(/jest\.doMock\(['"]@astryxdesign\/core\/Text['"]/);
|
|
126
|
+
expect(output).toMatch(/[^.]mock\(['"]@astryxdesign\/core['"]/);
|
|
127
|
+
expect(output).not.toContain('@xds/');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('does NOT rewrite the mock path for a non-@xds package', async () => {
|
|
131
|
+
const input = "vi.mock('some-other-pkg', () => ({useXDSFoo: () => 1}));";
|
|
132
|
+
const output = await applyTransform(input);
|
|
133
|
+
expect(output).toBe(input);
|
|
134
|
+
});
|
|
92
135
|
});
|