@astryxdesign/cli 0.1.7-canary.c0ff997 → 0.1.7-canary.cbfb48e
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/docs/working-with-ai.doc.mjs +2 -2
- package/package.json +9 -9
- package/src/codemods/__tests__/registry.test.mjs +0 -1
- package/src/codemods/registry.mjs +0 -1
- package/src/codemods/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +7 -42
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +0 -43
- package/src/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +3 -102
- package/src/codemods/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +6 -72
- package/src/commands/agent-docs.mjs +6 -80
- package/src/commands/agent-docs.test.mjs +6 -112
- package/src/commands/interactive-guard.test.mjs +1 -1
- package/src/commands/json-contract.test.mjs +1 -1
- package/src/commands/upgrade.mjs +27 -130
- package/src/commands/upgrade.test.mjs +0 -154
- package/src/types/upgrade.d.ts +2 -39
- 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 +9 -5
- 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 +6 -2
- package/templates/blocks/components/Stack/StackFillItem.tsx +6 -2
- 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 +11 -3
- package/templates/pages/detail-page/page.tsx +12 -5
- package/templates/pages/documentation-design/page.tsx +1 -1
- package/templates/pages/messaging-shell/page.tsx +8 -6
- package/templates/pages/table-grouped/page.tsx +14 -9
- package/templates/pages/table-page/page.tsx +9 -7
- package/templates/pages/table-page-heatmap-status/page.tsx +13 -5
- package/src/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +0 -161
- package/src/codemods/transforms/v0.1.8/index.mjs +0 -19
- package/src/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +0 -234
|
@@ -42,13 +42,13 @@ export const docs = {
|
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
type: 'prose',
|
|
45
|
-
text: '
|
|
45
|
+
text: 'If you prefer to target a specific file format:',
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
type: 'code',
|
|
49
49
|
lang: 'bash',
|
|
50
50
|
label: 'Manual options',
|
|
51
|
-
code: `npx @astryxdesign/cli init --features agents --agent claude #
|
|
51
|
+
code: `npx @astryxdesign/cli init --features agents --agent claude # CLAUDE.md
|
|
52
52
|
npx @astryxdesign/cli init --features agents --agent cursor # .cursorrules
|
|
53
53
|
npx @astryxdesign/cli init --features agents --agent codex # AGENTS.md (Copilot, Codex, etc.)`,
|
|
54
54
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.7-canary.
|
|
3
|
+
"version": "0.1.7-canary.cbfb48e",
|
|
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",
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@astryxdesign/charts": "0.1.7-canary.
|
|
83
|
-
"@astryxdesign/core": "0.1.7-canary.
|
|
84
|
-
"@astryxdesign/lab": "0.1.7-canary.
|
|
85
|
-
"@astryxdesign/theme-neutral": "0.1.7-canary.
|
|
82
|
+
"@astryxdesign/charts": "0.1.7-canary.cbfb48e",
|
|
83
|
+
"@astryxdesign/core": "0.1.7-canary.cbfb48e",
|
|
84
|
+
"@astryxdesign/lab": "0.1.7-canary.cbfb48e",
|
|
85
|
+
"@astryxdesign/theme-neutral": "0.1.7-canary.cbfb48e",
|
|
86
86
|
"gpt-tokenizer": "^3.4.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
@@ -100,10 +100,10 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@astryxdesign/charts": "0.1.7-canary.
|
|
104
|
-
"@astryxdesign/core": "0.1.7-canary.
|
|
105
|
-
"@astryxdesign/lab": "0.1.7-canary.
|
|
106
|
-
"@astryxdesign/theme-neutral": "0.1.7-canary.
|
|
103
|
+
"@astryxdesign/charts": "0.1.7-canary.cbfb48e",
|
|
104
|
+
"@astryxdesign/core": "0.1.7-canary.cbfb48e",
|
|
105
|
+
"@astryxdesign/lab": "0.1.7-canary.cbfb48e",
|
|
106
|
+
"@astryxdesign/theme-neutral": "0.1.7-canary.cbfb48e",
|
|
107
107
|
"gpt-tokenizer": "^3.4.0"
|
|
108
108
|
},
|
|
109
109
|
"scripts": {
|
|
@@ -22,7 +22,6 @@ 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')],
|
|
26
25
|
]);
|
|
27
26
|
|
|
28
27
|
// Re-export from the shared utility so registry callers and other consumers
|
|
@@ -166,50 +166,15 @@ 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 normally when there is NO local collision', async () => {
|
|
170
170
|
const input = [
|
|
171
|
-
`
|
|
172
|
-
`
|
|
173
|
-
` useXDSTruncation: () => ({ref: vi.fn(), isTruncated: true, fullText: ''}),`,
|
|
174
|
-
`}));`,
|
|
175
|
-
].join('\n');
|
|
176
|
-
const output = await applyTransform(input);
|
|
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
|
-
`}));`,
|
|
171
|
+
`import {XDSCodeBlock} from '@xds/core/CodeBlock';`,
|
|
172
|
+
`export const App = () => <XDSCodeBlock code="x" />;`,
|
|
203
173
|
].join('\n');
|
|
204
174
|
const output = await applyTransform(input);
|
|
205
|
-
expect(output).
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const input = [`const config = {`, ` useXDSWhatever: true,`, `};`].join(
|
|
210
|
-
'\n',
|
|
211
|
-
);
|
|
212
|
-
const output = await applyTransform(input);
|
|
213
|
-
expect(output).toBe(input);
|
|
175
|
+
expect(output).toContain('{CodeBlock}');
|
|
176
|
+
expect(output).toContain('@xds/core/CodeBlock');
|
|
177
|
+
expect(output).toContain('<CodeBlock code="x" />');
|
|
178
|
+
expect(output).not.toContain('AstryxCodeBlock');
|
|
214
179
|
});
|
|
215
180
|
});
|
|
@@ -89,47 +89,4 @@ 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
|
-
});
|
|
135
92
|
});
|
|
@@ -35,19 +35,11 @@
|
|
|
35
35
|
* including type references in generic type-argument positions such as
|
|
36
36
|
* `useMemo<XDSTableColumn<Issue>[]>(...)`
|
|
37
37
|
* 3. Re-exports from @xds/core: export {XDSButton} from '@xds/core/Button'
|
|
38
|
-
* 4. Object-property keys inside a test-framework mock factory for an
|
|
39
|
-
* `@xds/core` module: the partial-mock override in
|
|
40
|
-
* `vi.mock('@xds/core/Text', async orig => ({...await orig(),
|
|
41
|
-
* useXDSTruncation: () => ...}))` must be un-prefixed to `useTruncation`,
|
|
42
|
-
* otherwise the key targets a name the renamed module no longer exports and
|
|
43
|
-
* the mock silently overrides nothing.
|
|
44
38
|
*
|
|
45
39
|
* Does NOT touch:
|
|
46
40
|
* - import source paths (`@xds/core/Button` stays -- subpath dirs are unchanged
|
|
47
41
|
* by the prefix migration)
|
|
48
42
|
* - identifiers not bound to an @xds/core import
|
|
49
|
-
* - object-property keys outside an `@xds/core` mock factory (an unrelated
|
|
50
|
-
* `{useXDSFoo: ...}` literal, or a mock of some other package, is untouched)
|
|
51
43
|
* - the `XDSBaseProps`-style names only when they are NOT imported from core
|
|
52
44
|
*/
|
|
53
45
|
|
|
@@ -56,8 +48,7 @@ export const meta = {
|
|
|
56
48
|
description:
|
|
57
49
|
'Rewrites @xds/core imports and their usages from the prefixed names ' +
|
|
58
50
|
'(XDSButton, useXDSTheme, XDSIconRegistry, XDSButtonProps) to their bare ' +
|
|
59
|
-
'names (Button, useTheme, IconRegistry, ButtonProps)
|
|
60
|
-
'keys in a vi.mock/jest.mock factory for an @xds/core module. Only renames ' +
|
|
51
|
+
'names (Button, useTheme, IconRegistry, ButtonProps). Only renames ' +
|
|
61
52
|
'bindings imported from @xds/core, leaving unrelated identifiers untouched.',
|
|
62
53
|
pr: '#2880',
|
|
63
54
|
};
|
|
@@ -83,81 +74,6 @@ function bareName(name) {
|
|
|
83
74
|
return null;
|
|
84
75
|
}
|
|
85
76
|
|
|
86
|
-
// A test-framework mock of an `@xds/core` module (`vi.mock('@xds/core/Text',
|
|
87
|
-
// factory)`). We only rewrite override keys inside such a factory, so scope the
|
|
88
|
-
// detection tightly: callee must be `vi.mock`/`vi.doMock`/`jest.mock`/
|
|
89
|
-
// `jest.doMock` (or a bare `mock`) AND the first argument must be a string
|
|
90
|
-
// literal that resolves to `@xds/core` (bare or subpath).
|
|
91
|
-
const MOCK_METHOD_NAMES = new Set(['mock', 'doMock']);
|
|
92
|
-
const MOCK_OBJECT_NAMES = new Set(['vi', 'jest']);
|
|
93
|
-
|
|
94
|
-
function isMockCallee(callee) {
|
|
95
|
-
if (
|
|
96
|
-
callee.type === 'MemberExpression' &&
|
|
97
|
-
!callee.computed &&
|
|
98
|
-
callee.object.type === 'Identifier' &&
|
|
99
|
-
MOCK_OBJECT_NAMES.has(callee.object.name) &&
|
|
100
|
-
callee.property.type === 'Identifier' &&
|
|
101
|
-
MOCK_METHOD_NAMES.has(callee.property.name)
|
|
102
|
-
) {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
return callee.type === 'Identifier' && callee.name === 'mock';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function isXdsCoreMockCall(node) {
|
|
109
|
-
if (node.type !== 'CallExpression' || !isMockCallee(node.callee))
|
|
110
|
-
return false;
|
|
111
|
-
const [arg] = node.arguments;
|
|
112
|
-
const value =
|
|
113
|
-
arg && (arg.type === 'StringLiteral' || arg.type === 'Literal')
|
|
114
|
-
? arg.value
|
|
115
|
-
: null;
|
|
116
|
-
return typeof value === 'string' && XDS_CORE_SOURCE.test(value);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Rename XDS-prefixed object-property KEYS (not values, not nested objects)
|
|
120
|
-
// within the given AST subtree. Used only on a recognized @xds/core mock
|
|
121
|
-
// factory, so the scope guard lives at the call site.
|
|
122
|
-
function renameMockFactoryKeys(node, seen, onChange) {
|
|
123
|
-
if (!node || typeof node !== 'object') return;
|
|
124
|
-
if (Array.isArray(node)) {
|
|
125
|
-
for (const child of node) renameMockFactoryKeys(child, seen, onChange);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (seen.has(node)) return;
|
|
129
|
-
seen.add(node);
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
(node.type === 'ObjectProperty' || node.type === 'Property') &&
|
|
133
|
-
!node.computed &&
|
|
134
|
-
node.key &&
|
|
135
|
-
node.key.type === 'Identifier'
|
|
136
|
-
) {
|
|
137
|
-
const bare = bareName(node.key.name);
|
|
138
|
-
if (bare && bare !== node.key.name) {
|
|
139
|
-
node.key.name = bare;
|
|
140
|
-
onChange();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
for (const key of Object.keys(node)) {
|
|
145
|
-
if (
|
|
146
|
-
key === 'loc' ||
|
|
147
|
-
key === 'start' ||
|
|
148
|
-
key === 'end' ||
|
|
149
|
-
key === 'range' ||
|
|
150
|
-
key === 'comments' ||
|
|
151
|
-
key === 'leadingComments' ||
|
|
152
|
-
key === 'trailingComments' ||
|
|
153
|
-
key === 'tokens'
|
|
154
|
-
) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
renameMockFactoryKeys(node[key], seen, onChange);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
77
|
export default function transformer(file, api) {
|
|
162
78
|
const j = api.jscodeshift;
|
|
163
79
|
const root = j(file.source);
|
|
@@ -274,26 +190,11 @@ export default function transformer(file, api) {
|
|
|
274
190
|
}
|
|
275
191
|
});
|
|
276
192
|
|
|
277
|
-
// 3. Un-prefix override keys inside @xds/core mock factories. Independent of
|
|
278
|
-
// the import bindings above: the override key is a factory object property,
|
|
279
|
-
// not a reference to an imported binding, so it must match the renamed export
|
|
280
|
-
// name of the mocked module regardless of what the file imports.
|
|
281
|
-
const seenMockNodes = new Set();
|
|
282
|
-
root.find(j.CallExpression).forEach(path => {
|
|
283
|
-
if (!isXdsCoreMockCall(path.node)) return;
|
|
284
|
-
// Only walk the factory argument(s), never the module-path string.
|
|
285
|
-
for (const arg of path.node.arguments.slice(1)) {
|
|
286
|
-
renameMockFactoryKeys(arg, seenMockNodes, () => {
|
|
287
|
-
hasChanges = true;
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
|
|
292
193
|
if (localRenames.size === 0) {
|
|
293
194
|
return hasChanges ? root.toSource() : undefined;
|
|
294
195
|
}
|
|
295
196
|
|
|
296
|
-
//
|
|
197
|
+
// 3. Rename references to renamed local bindings
|
|
297
198
|
// Identifiers (type refs, value refs, etc.)
|
|
298
199
|
root.find(j.Identifier).forEach(path => {
|
|
299
200
|
const newName = localRenames.get(path.node.name);
|
|
@@ -330,7 +231,7 @@ export default function transformer(file, api) {
|
|
|
330
231
|
}
|
|
331
232
|
});
|
|
332
233
|
|
|
333
|
-
//
|
|
234
|
+
// 4. Rename TS type references in generic type-argument positions.
|
|
334
235
|
//
|
|
335
236
|
// `j.find(j.Identifier)` (and `j.find(j.TSTypeReference)`) do NOT visit
|
|
336
237
|
// identifiers nested inside generic type arguments with the tsx/babel parser
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
export const meta = {
|
|
13
13
|
title: 'Migrate module specifiers from @xds/* to @astryxdesign/*',
|
|
14
14
|
description:
|
|
15
|
-
'Updates JS/TS import, export, dynamic import, require
|
|
16
|
-
'
|
|
17
|
-
'@xds/* to the @astryxdesign/* packages used by Astryx v0.1.0.',
|
|
15
|
+
'Updates JS/TS import, export, dynamic import, and require module ' +
|
|
16
|
+
'specifiers from @xds/* to the @astryxdesign/* packages used by Astryx v0.1.0.',
|
|
18
17
|
pr: '#3092',
|
|
19
18
|
fileExtensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'],
|
|
20
19
|
};
|
|
@@ -74,30 +73,6 @@ function isCollapsedThemeSource(value) {
|
|
|
74
73
|
return false;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
// Test-framework mock calls take the mocked module path as their first
|
|
78
|
-
// argument (`vi.mock('@xds/core/Text', factory)`). That path is a plain string
|
|
79
|
-
// literal -- not an ImportDeclaration source -- so it slips past the import/
|
|
80
|
-
// export handlers and, left unrewritten, the mock would target the stale
|
|
81
|
-
// `@xds/*` path and stop intercepting the renamed `@astryxdesign/*` import.
|
|
82
|
-
// Recognized callees: `vi.mock`, `vi.doMock`, `jest.mock`, `jest.doMock`, and
|
|
83
|
-
// a bare `mock(...)` (destructured from the test runner).
|
|
84
|
-
const MOCK_METHOD_NAMES = new Set(['mock', 'doMock']);
|
|
85
|
-
const MOCK_OBJECT_NAMES = new Set(['vi', 'jest']);
|
|
86
|
-
|
|
87
|
-
function isMockCall(callee) {
|
|
88
|
-
if (
|
|
89
|
-
callee.type === 'MemberExpression' &&
|
|
90
|
-
!callee.computed &&
|
|
91
|
-
callee.object.type === 'Identifier' &&
|
|
92
|
-
MOCK_OBJECT_NAMES.has(callee.object.name) &&
|
|
93
|
-
callee.property.type === 'Identifier' &&
|
|
94
|
-
MOCK_METHOD_NAMES.has(callee.property.name)
|
|
95
|
-
) {
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
return callee.type === 'Identifier' && callee.name === 'mock';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
76
|
function remapThemeExportNames(importPath, j) {
|
|
102
77
|
let changed = false;
|
|
103
78
|
importPath.node.specifiers = (importPath.node.specifiers || []).map(spec => {
|
|
@@ -143,53 +118,12 @@ export default function transformer(file, api) {
|
|
|
143
118
|
const isRequire =
|
|
144
119
|
path.node.callee.type === 'Identifier' &&
|
|
145
120
|
path.node.callee.name === 'require';
|
|
146
|
-
if (isDynamicImport
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
121
|
+
if (!isDynamicImport && !isRequire) return;
|
|
122
|
+
const [arg] = path.node.arguments;
|
|
123
|
+
if (arg?.type === 'StringLiteral' || arg?.type === 'Literal') {
|
|
124
|
+
hasChanges = rewriteLiteral(arg) || hasChanges;
|
|
151
125
|
}
|
|
152
126
|
});
|
|
153
127
|
|
|
154
|
-
// TS `import(...)` TYPE specifiers -- e.g. the `import('@xds/core/Text')` in
|
|
155
|
-
// `typeof import('@xds/core/Text')` -- parse to a `TSImportType` node, not a
|
|
156
|
-
// dynamic-import CallExpression, so the handler above never reaches them.
|
|
157
|
-
// ast-types' traversal also doesn't descend into a `TSImportType` nested in a
|
|
158
|
-
// generic type-argument position (e.g. `orig<typeof import('@xds/core/Text')>()`),
|
|
159
|
-
// so `j.find(j.TSImportType)` misses it. Walk the raw AST to cover every
|
|
160
|
-
// position. Re-visiting a node the loop above handled is harmless: the
|
|
161
|
-
// literal is already rewritten, so the second pass is a no-op.
|
|
162
|
-
const seenTypeImportNodes = new Set();
|
|
163
|
-
const rewriteTSImportTypes = node => {
|
|
164
|
-
if (!node || typeof node !== 'object') return;
|
|
165
|
-
if (Array.isArray(node)) {
|
|
166
|
-
for (const child of node) rewriteTSImportTypes(child);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
if (seenTypeImportNodes.has(node)) return;
|
|
170
|
-
seenTypeImportNodes.add(node);
|
|
171
|
-
|
|
172
|
-
if (node.type === 'TSImportType' && node.argument) {
|
|
173
|
-
hasChanges = rewriteLiteral(node.argument) || hasChanges;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
for (const key of Object.keys(node)) {
|
|
177
|
-
if (
|
|
178
|
-
key === 'loc' ||
|
|
179
|
-
key === 'start' ||
|
|
180
|
-
key === 'end' ||
|
|
181
|
-
key === 'range' ||
|
|
182
|
-
key === 'comments' ||
|
|
183
|
-
key === 'leadingComments' ||
|
|
184
|
-
key === 'trailingComments' ||
|
|
185
|
-
key === 'tokens'
|
|
186
|
-
) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
rewriteTSImportTypes(node[key]);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
rewriteTSImportTypes(root.get().node);
|
|
193
|
-
|
|
194
128
|
return hasChanges ? root.toSource() : undefined;
|
|
195
129
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Hermes Agent: .hermes.md or HERMES.md (existing), else AGENTS.md
|
|
13
13
|
*
|
|
14
14
|
* Auto-detect: discovers existing files and updates them in place.
|
|
15
|
-
* Default (no existing files): creates
|
|
15
|
+
* Default (no existing files): creates .claude/CLAUDE.md.
|
|
16
16
|
*
|
|
17
17
|
* --agent <tool>: target a specific tool preset (claude, cursor, codex, hermes, all)
|
|
18
18
|
* --agent-docs-path <path>: explicit file path(s)
|
|
@@ -104,78 +104,6 @@ export function isAstryxInitialized(targetDir = process.cwd()) {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/**
|
|
108
|
-
* Parse the Astryx version a managed block was generated for, from its header
|
|
109
|
-
* line ("Astryx v1.2.3 · N components"). Returns null when the block predates
|
|
110
|
-
* the versioned header (e.g. a legacy XDS block) or has no header at all.
|
|
111
|
-
*
|
|
112
|
-
* @param {string} content File contents, or just the block text.
|
|
113
|
-
* @returns {string|null}
|
|
114
|
-
*/
|
|
115
|
-
export function parseBlockVersion(content) {
|
|
116
|
-
const match = /Astryx v(\d+\.\d+\.\d+[^\s·]*)/.exec(content ?? '');
|
|
117
|
-
return match ? match[1] : null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Read-only staleness assessment of the managed agent-docs block(s) against the
|
|
122
|
-
* installed core version. This is the detection half of the `astryx upgrade`
|
|
123
|
-
* agent-docs refresh: it never writes, so `upgrade` can run it on EVERY path —
|
|
124
|
-
* including the up-to-date / no-codemods short-circuits — and decide whether to
|
|
125
|
-
* rewrite a stale block, nudge an uninitialized repo, or stay silent.
|
|
126
|
-
*
|
|
127
|
-
* A managed block is:
|
|
128
|
-
* - `stale` — it carries a legacy XDS marker, has no parseable version, or
|
|
129
|
-
* records a version other than the installed one.
|
|
130
|
-
* - `current` — every managed block already matches the installed version.
|
|
131
|
-
* And the project is `missing` when no managed block exists anywhere (the repo
|
|
132
|
-
* has agent-doc files without our markers, or none at all — i.e. never `init`ed).
|
|
133
|
-
*
|
|
134
|
-
* @param {string} targetDir
|
|
135
|
-
* @param {string} [installedVersion] Defaults to the installed core version.
|
|
136
|
-
* @returns {{
|
|
137
|
-
* installedVersion: string,
|
|
138
|
-
* status: 'missing' | 'stale' | 'current',
|
|
139
|
-
* files: Array<{path: string, blockVersion: string|null, legacy: boolean, stale: boolean}>,
|
|
140
|
-
* staleFiles: string[],
|
|
141
|
-
* blockVersions: string[],
|
|
142
|
-
* }}
|
|
143
|
-
*/
|
|
144
|
-
export function inspectAgentDocs(targetDir, installedVersion) {
|
|
145
|
-
const version = installedVersion ?? getXdsVersion(findCoreDir(targetDir));
|
|
146
|
-
const files = [];
|
|
147
|
-
|
|
148
|
-
for (const rel of discoverAgentDocs(targetDir)) {
|
|
149
|
-
let content;
|
|
150
|
-
try {
|
|
151
|
-
content = fs.readFileSync(path.join(targetDir, rel), 'utf-8');
|
|
152
|
-
} catch {
|
|
153
|
-
continue; // Unreadable — treat as absent.
|
|
154
|
-
}
|
|
155
|
-
const hasNew = content.includes(MARKER_START);
|
|
156
|
-
const hasLegacy = content.includes(LEGACY_MARKER_START);
|
|
157
|
-
if (!hasNew && !hasLegacy) continue; // Not a block we manage.
|
|
158
|
-
|
|
159
|
-
const legacy = !hasNew && hasLegacy;
|
|
160
|
-
const blockVersion = parseBlockVersion(content);
|
|
161
|
-
const stale = legacy || blockVersion == null || blockVersion !== version;
|
|
162
|
-
files.push({path: rel, blockVersion, legacy, stale});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const staleEntries = files.filter(f => f.stale);
|
|
166
|
-
const staleFiles = staleEntries.map(f => f.path);
|
|
167
|
-
const blockVersions = [
|
|
168
|
-
...new Set(staleEntries.map(f => f.blockVersion).filter(Boolean)),
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
let status;
|
|
172
|
-
if (files.length === 0) status = 'missing';
|
|
173
|
-
else if (staleFiles.length > 0) status = 'stale';
|
|
174
|
-
else status = 'current';
|
|
175
|
-
|
|
176
|
-
return {installedVersion: version, status, files, staleFiles, blockVersions};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
107
|
/**
|
|
180
108
|
* Resolve which file(s) to write for a given agent tool preset.
|
|
181
109
|
* Searches for existing files first, falls back to default creation path.
|
|
@@ -511,7 +439,7 @@ export function removeAgentDocs(targetDir) {
|
|
|
511
439
|
*
|
|
512
440
|
* Strategy (when no agent/paths specified):
|
|
513
441
|
* - Discover all existing agent doc files and update them
|
|
514
|
-
* - If nothing found, create
|
|
442
|
+
* - If nothing found, create .claude/CLAUDE.md as default
|
|
515
443
|
*
|
|
516
444
|
* @param {string} targetDir
|
|
517
445
|
* @param {object} [options]
|
|
@@ -586,16 +514,14 @@ export function installAgentDocs(targetDir, {zh = false, lang, agent, paths, onl
|
|
|
586
514
|
return written;
|
|
587
515
|
}
|
|
588
516
|
|
|
589
|
-
// Nothing exists — create
|
|
590
|
-
// AGENTS.md is the tool-agnostic standard (Codex/Copilot, Cursor, and most
|
|
591
|
-
// agents read it), so it's the safe default. Claude-specific output is opt-in
|
|
592
|
-
// via `--agent claude` (→ .claude/CLAUDE.md); `--agent all` writes both.
|
|
517
|
+
// Nothing exists — create .claude/CLAUDE.md as default (skip if onlyReplace)
|
|
593
518
|
if (onlyReplace) return written;
|
|
594
519
|
|
|
595
|
-
const defaultPath =
|
|
520
|
+
const defaultPath = CLAUDE_DIR_MD;
|
|
521
|
+
fs.mkdirSync(path.join(targetDir, '.claude'), {recursive: true});
|
|
596
522
|
injectXdsBlock(path.join(targetDir, defaultPath), compressedIndex, {
|
|
597
523
|
createIfMissing: true,
|
|
598
|
-
header: `#
|
|
524
|
+
header: `# CLAUDE.md\n\nProject-specific guidance for AI coding agents.`,
|
|
599
525
|
});
|
|
600
526
|
written.push(defaultPath);
|
|
601
527
|
return written;
|