assistant-ui 0.0.112 → 0.0.113
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/dist/codemods/v0-12/assistant-api-to-aui.d.ts.map +1 -1
- package/dist/codemods/v0-12/assistant-api-to-aui.js +87 -62
- package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js +31 -45
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js.map +1 -1
- package/dist/commands/add.d.ts +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +4 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +33 -10
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +2 -1
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +25 -28
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/update.js +6 -7
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +5 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-skill.d.ts +1 -1
- package/dist/lib/agent-skill.d.ts.map +1 -1
- package/dist/lib/agent-skill.js.map +1 -1
- package/dist/lib/create-project.d.ts +2 -3
- package/dist/lib/create-project.d.ts.map +1 -1
- package/dist/lib/create-project.js +17 -42
- package/dist/lib/create-project.js.map +1 -1
- package/dist/lib/handle-cli-error.d.ts +5 -0
- package/dist/lib/handle-cli-error.d.ts.map +1 -0
- package/dist/lib/handle-cli-error.js +17 -0
- package/dist/lib/handle-cli-error.js.map +1 -0
- package/dist/lib/install-ai-sdk-lib.js +8 -1
- package/dist/lib/install-ai-sdk-lib.js.map +1 -1
- package/dist/lib/install-edge-lib.js +4 -8
- package/dist/lib/install-edge-lib.js.map +1 -1
- package/dist/lib/run-spawn.d.ts +16 -2
- package/dist/lib/run-spawn.d.ts.map +1 -1
- package/dist/lib/run-spawn.js +87 -9
- package/dist/lib/run-spawn.js.map +1 -1
- package/dist/lib/transform.d.ts +1 -1
- package/dist/lib/transform.d.ts.map +1 -1
- package/dist/lib/transform.js +12 -25
- package/dist/lib/transform.js.map +1 -1
- package/dist/lib/upgrade.js +20 -16
- package/dist/lib/upgrade.js.map +1 -1
- package/dist/lib/utils/file-scanner.d.ts +9 -1
- package/dist/lib/utils/file-scanner.d.ts.map +1 -1
- package/dist/lib/utils/file-scanner.js +22 -20
- package/dist/lib/utils/file-scanner.js.map +1 -1
- package/dist/lib/utils/package-installer.js +1 -1
- package/dist/lib/utils/package-manager.d.ts +3 -1
- package/dist/lib/utils/package-manager.d.ts.map +1 -1
- package/dist/lib/utils/package-manager.js +22 -10
- package/dist/lib/utils/package-manager.js.map +1 -1
- package/package.json +6 -6
- package/plugin/skills/assistant-ui/SKILL.md +3 -3
- package/src/codemods/v0-12/__tests__/assistant-api-to-aui.test.ts +370 -0
- package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +100 -0
- package/src/codemods/v0-12/assistant-api-to-aui.ts +227 -134
- package/src/codemods/v0-12/primitive-if-to-aui-if.ts +44 -72
- package/src/commands/add.ts +5 -5
- package/src/commands/create.ts +41 -7
- package/src/commands/info.ts +1 -0
- package/src/commands/init.ts +4 -6
- package/src/commands/mcp.ts +20 -29
- package/src/commands/update.ts +6 -8
- package/src/commands/upgrade.ts +5 -2
- package/src/index.ts +2 -7
- package/src/lib/agent-skill.ts +2 -1
- package/src/lib/create-project.ts +24 -58
- package/src/lib/handle-cli-error.test.ts +67 -0
- package/src/lib/handle-cli-error.ts +17 -0
- package/src/lib/install-ai-sdk-lib.ts +13 -1
- package/src/lib/install-edge-lib.ts +7 -8
- package/src/lib/run-spawn.test.ts +148 -0
- package/src/lib/run-spawn.ts +121 -11
- package/src/lib/transform.test.ts +62 -0
- package/src/lib/transform.ts +21 -37
- package/src/lib/upgrade.ts +23 -19
- package/src/lib/utils/file-scanner.ts +26 -20
- package/src/lib/utils/package-manager.ts +29 -12
|
@@ -22,72 +22,8 @@ const isUseAuiCall = (j: any, node: any): boolean => {
|
|
|
22
22
|
);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// Check if a scope node directly contains an 'api' variable declaration
|
|
26
|
-
const scopeHasDirectApiDeclaration = (j: any, scopeNode: any): boolean => {
|
|
27
|
-
// Get the body of the scope
|
|
28
|
-
let body = null;
|
|
29
|
-
if (
|
|
30
|
-
j.FunctionDeclaration.check(scopeNode) ||
|
|
31
|
-
j.FunctionExpression.check(scopeNode)
|
|
32
|
-
) {
|
|
33
|
-
body = scopeNode.body?.body;
|
|
34
|
-
} else if (j.ArrowFunctionExpression.check(scopeNode)) {
|
|
35
|
-
// Arrow functions might have block or expression body
|
|
36
|
-
if (j.BlockStatement.check(scopeNode.body)) {
|
|
37
|
-
body = scopeNode.body.body;
|
|
38
|
-
}
|
|
39
|
-
} else if (j.BlockStatement.check(scopeNode)) {
|
|
40
|
-
body = scopeNode.body;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (!Array.isArray(body)) return false;
|
|
44
|
-
|
|
45
|
-
// Check only direct statements in this scope's body
|
|
46
|
-
for (const statement of body) {
|
|
47
|
-
if (j.VariableDeclaration.check(statement)) {
|
|
48
|
-
for (const declarator of statement.declarations) {
|
|
49
|
-
if (j.Identifier.check(declarator.id) && declarator.id.name === "api") {
|
|
50
|
-
// Don't count it as shadowing if it's from useAui
|
|
51
|
-
if (!isUseAuiCall(j, declarator.init)) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return false;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Check if a path is inside a scope that shadows the api variable
|
|
63
|
-
const isInsideShadowingScope = (j: any, identifierPath: any): boolean => {
|
|
64
|
-
let currentPath = identifierPath.parent;
|
|
65
|
-
|
|
66
|
-
while (currentPath) {
|
|
67
|
-
const node = currentPath.value;
|
|
68
|
-
|
|
69
|
-
// Check if this is a scope-creating node (function, arrow function, block)
|
|
70
|
-
if (
|
|
71
|
-
j.FunctionDeclaration.check(node) ||
|
|
72
|
-
j.FunctionExpression.check(node) ||
|
|
73
|
-
j.ArrowFunctionExpression.check(node) ||
|
|
74
|
-
j.BlockStatement.check(node)
|
|
75
|
-
) {
|
|
76
|
-
if (scopeHasDirectApiDeclaration(j, node)) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
currentPath = currentPath.parent;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return false;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
25
|
const migrateAssistantApiToAui = createTransformer(
|
|
88
26
|
({ j, root, markAsChanged }) => {
|
|
89
|
-
let hasApiFromUseAui = false;
|
|
90
|
-
|
|
91
27
|
// 1. Update imports
|
|
92
28
|
root.find(j.ImportDeclaration).forEach((path: any) => {
|
|
93
29
|
const source = path.value.source.value;
|
|
@@ -122,91 +58,248 @@ const migrateAssistantApiToAui = createTransformer(
|
|
|
122
58
|
}
|
|
123
59
|
});
|
|
124
60
|
|
|
125
|
-
// 2.
|
|
61
|
+
// 2. Collect `api` declarators initialized from useAui / useAssistantApi.
|
|
62
|
+
// References are renamed by binding resolution, so an `api` bound
|
|
63
|
+
// elsewhere (function params, `const { api } = other()`) is never touched.
|
|
64
|
+
const renamedDeclaratorIds = new Set<any>();
|
|
126
65
|
root.find(j.VariableDeclarator).forEach((path: any) => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Only rename if it's called 'api'
|
|
135
|
-
if (oldVarName === "api") {
|
|
136
|
-
path.value.id.name = "aui";
|
|
137
|
-
hasApiFromUseAui = true;
|
|
138
|
-
markAsChanged();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
66
|
+
if (
|
|
67
|
+
isUseAuiCall(j, path.value.init) &&
|
|
68
|
+
j.Identifier.check(path.value.id) &&
|
|
69
|
+
path.value.id.name === "api"
|
|
70
|
+
) {
|
|
71
|
+
renamedDeclaratorIds.add(path.value.id);
|
|
141
72
|
}
|
|
142
73
|
});
|
|
143
74
|
|
|
144
|
-
// 3. Rename
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
75
|
+
// 3. Rename references governed by one of those declarators. Resolution
|
|
76
|
+
// is lexical (nearest enclosing declaration wins) rather than via
|
|
77
|
+
// ast-types scopes, which have no block granularity: a block-scoped
|
|
78
|
+
// `const api = other()` inside the same function must shadow.
|
|
79
|
+
if (renamedDeclaratorIds.size > 0) {
|
|
80
|
+
const patternBindsApi = (id: any): boolean => {
|
|
81
|
+
if (
|
|
82
|
+
id &&
|
|
83
|
+
(id.type === "TSParameterProperty" ||
|
|
84
|
+
j.TSParameterProperty?.check?.(id))
|
|
85
|
+
) {
|
|
86
|
+
return patternBindsApi(id.parameter);
|
|
87
|
+
}
|
|
88
|
+
if (j.Identifier.check(id)) return id.name === "api";
|
|
89
|
+
if (j.ObjectPattern.check(id)) {
|
|
90
|
+
return id.properties.some((prop: any) =>
|
|
91
|
+
patternBindsApi(prop.value ?? prop.argument ?? prop),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (j.ArrayPattern.check(id)) {
|
|
95
|
+
return id.elements.some((el: any) => el && patternBindsApi(el));
|
|
96
|
+
}
|
|
97
|
+
if (j.AssignmentPattern.check(id)) return patternBindsApi(id.left);
|
|
98
|
+
if (j.RestElement.check(id)) return patternBindsApi(id.argument);
|
|
99
|
+
return false;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// What a statement-level node declares for `api`: the declarator id
|
|
103
|
+
// node when it is a plain `const/let/var api = ...`, "foreign" for any
|
|
104
|
+
// other binding of the name (patterns, functions, classes, enums), or
|
|
105
|
+
// undefined when it does not bind `api` at all.
|
|
106
|
+
const declaredApi = (statement: any): any => {
|
|
107
|
+
if (!statement) return undefined;
|
|
108
|
+
if (
|
|
109
|
+
j.ExportNamedDeclaration.check(statement) ||
|
|
110
|
+
j.ExportDefaultDeclaration.check(statement)
|
|
111
|
+
) {
|
|
112
|
+
return declaredApi(statement.declaration);
|
|
113
|
+
}
|
|
114
|
+
if (j.VariableDeclaration.check(statement)) {
|
|
115
|
+
for (const declarator of statement.declarations) {
|
|
116
|
+
if (
|
|
117
|
+
j.Identifier.check(declarator.id) &&
|
|
118
|
+
declarator.id.name === "api"
|
|
119
|
+
)
|
|
120
|
+
return declarator.id;
|
|
121
|
+
if (patternBindsApi(declarator.id)) return "foreign";
|
|
174
122
|
}
|
|
175
|
-
|
|
176
|
-
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
// Type-only declarations do not shadow the value binding.
|
|
126
|
+
if (
|
|
127
|
+
statement.type === "TSTypeAliasDeclaration" ||
|
|
128
|
+
statement.type === "TSInterfaceDeclaration" ||
|
|
129
|
+
statement.type === "TSDeclareFunction"
|
|
130
|
+
)
|
|
131
|
+
return undefined;
|
|
132
|
+
// FunctionDeclaration, ClassDeclaration, TS enums/namespaces, …
|
|
133
|
+
if (
|
|
134
|
+
statement.id &&
|
|
135
|
+
j.Identifier.check(statement.id) &&
|
|
136
|
+
statement.id.name === "api"
|
|
137
|
+
)
|
|
138
|
+
return "foreign";
|
|
139
|
+
return undefined;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const scanStatements = (statements: any[]): any => {
|
|
143
|
+
for (const statement of statements) {
|
|
144
|
+
const found = declaredApi(statement);
|
|
145
|
+
if (found !== undefined) return found;
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Returns the declarator id node governing `api` here, or "foreign"
|
|
151
|
+
// when any other binding of the name shadows it first.
|
|
152
|
+
const governingApiBinding = (path: any): any => {
|
|
153
|
+
let current = path.parent;
|
|
154
|
+
while (current) {
|
|
155
|
+
const node = current.value;
|
|
156
|
+
|
|
157
|
+
// Anything function-like (declarations, expressions, arrows,
|
|
158
|
+
// object/class methods) binds its params.
|
|
159
|
+
if (Array.isArray(node.params) && node.params.some(patternBindsApi))
|
|
160
|
+
return "foreign";
|
|
161
|
+
// A named function/class expression binds its own name in its body.
|
|
177
162
|
if (
|
|
178
|
-
j.
|
|
179
|
-
|
|
180
|
-
|
|
163
|
+
(j.FunctionExpression.check(node) ||
|
|
164
|
+
j.ClassExpression.check(node)) &&
|
|
165
|
+
node.id?.name === "api"
|
|
166
|
+
)
|
|
167
|
+
return "foreign";
|
|
168
|
+
if (
|
|
169
|
+
j.CatchClause.check(node) &&
|
|
170
|
+
node.param &&
|
|
171
|
+
patternBindsApi(node.param)
|
|
172
|
+
)
|
|
173
|
+
return "foreign";
|
|
174
|
+
|
|
175
|
+
let found: any;
|
|
176
|
+
if (j.BlockStatement.check(node) || j.Program.check(node)) {
|
|
177
|
+
found = scanStatements(node.body);
|
|
178
|
+
} else if (j.ForStatement.check(node)) {
|
|
179
|
+
found = declaredApi(node.init);
|
|
180
|
+
} else if (
|
|
181
|
+
j.ForOfStatement.check(node) ||
|
|
182
|
+
j.ForInStatement.check(node)
|
|
181
183
|
) {
|
|
182
|
-
|
|
184
|
+
found = declaredApi(node.left);
|
|
185
|
+
} else if (j.SwitchStatement.check(node)) {
|
|
186
|
+
found = scanStatements(
|
|
187
|
+
node.cases.flatMap((c: any) => c.consequent),
|
|
188
|
+
);
|
|
189
|
+
} else if (j.StaticBlock?.check?.(node)) {
|
|
190
|
+
found = scanStatements(node.body);
|
|
183
191
|
}
|
|
192
|
+
if (found !== undefined) return found;
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
current = current.parent;
|
|
195
|
+
}
|
|
196
|
+
return undefined;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const bindsToRenamedApi = (path: any): boolean => {
|
|
200
|
+
const governing = governingApiBinding(path);
|
|
201
|
+
return governing !== "foreign" && renamedDeclaratorIds.has(governing);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const referencePaths: any[] = [];
|
|
205
|
+
root.find(j.Identifier, { name: "api" }).forEach((path: any) => {
|
|
206
|
+
const parent = path.parent.value;
|
|
207
|
+
if (j.ImportSpecifier.check(parent)) return;
|
|
208
|
+
// Declaration names (variable, function, class, type alias,
|
|
209
|
+
// interface) and TS type positions are not value references.
|
|
210
|
+
if (parent.id === path.value) return;
|
|
211
|
+
if (j.TSTypeReference?.check?.(parent)) return;
|
|
212
|
+
if (j.TSQualifiedName?.check?.(parent)) return;
|
|
213
|
+
// Any non-computed key position is a name, not a reference: object
|
|
214
|
+
// properties, object/class methods, class properties, TS signatures.
|
|
215
|
+
// Esprima-style shorthand reuses one node as key and value, so the
|
|
216
|
+
// value position must survive the guard.
|
|
217
|
+
if (
|
|
218
|
+
parent.key === path.value &&
|
|
219
|
+
!parent.computed &&
|
|
220
|
+
parent.value !== path.value
|
|
221
|
+
)
|
|
222
|
+
return;
|
|
223
|
+
if (
|
|
224
|
+
j.MemberExpression.check(parent) &&
|
|
225
|
+
parent.property === path.value &&
|
|
226
|
+
!parent.computed
|
|
227
|
+
)
|
|
228
|
+
return;
|
|
229
|
+
// JSXIdentifier extends Identifier, so JSX positions land here too:
|
|
230
|
+
// member properties (<config.api/>), namespace names, and lowercase
|
|
231
|
+
// element names (<api/> is an intrinsic tag) are not references.
|
|
232
|
+
if (
|
|
233
|
+
j.JSXMemberExpression?.check?.(parent) &&
|
|
234
|
+
parent.property === path.value
|
|
235
|
+
)
|
|
236
|
+
return;
|
|
237
|
+
if (j.JSXNamespacedName?.check?.(parent)) return;
|
|
238
|
+
if (
|
|
239
|
+
(j.JSXOpeningElement?.check?.(parent) ||
|
|
240
|
+
j.JSXClosingElement?.check?.(parent)) &&
|
|
241
|
+
parent.name === path.value
|
|
242
|
+
)
|
|
243
|
+
return;
|
|
244
|
+
if (j.JSXAttribute.check(parent)) return;
|
|
245
|
+
// The exported name of `export { api }` is the public alias, not a
|
|
246
|
+
// reference; only the local side is renamed (to `aui as api`). A
|
|
247
|
+
// source-bearing re-export binds in the other module, never here.
|
|
248
|
+
if (j.ExportSpecifier.check(parent)) {
|
|
249
|
+
const grandparent = path.parent.parent?.value;
|
|
250
|
+
if (
|
|
251
|
+
j.ExportNamedDeclaration.check(grandparent) &&
|
|
252
|
+
grandparent.source != null
|
|
253
|
+
)
|
|
187
254
|
return;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
if (
|
|
256
|
+
grandparent?.exportKind === "type" ||
|
|
257
|
+
parent.exportKind === "type"
|
|
258
|
+
)
|
|
259
|
+
return;
|
|
260
|
+
if (parent.exported === path.value && parent.local !== path.value)
|
|
192
261
|
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Update the reference
|
|
196
|
-
path.value.name = "aui";
|
|
197
|
-
markAsChanged();
|
|
198
262
|
}
|
|
263
|
+
if (!bindsToRenamedApi(path)) return;
|
|
264
|
+
referencePaths.push(path);
|
|
199
265
|
});
|
|
200
266
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
267
|
+
for (const path of referencePaths) {
|
|
268
|
+
const parent = path.parent.value;
|
|
269
|
+
if (
|
|
270
|
+
(j.Property.check(parent) || j.ObjectProperty.check(parent)) &&
|
|
271
|
+
parent.shorthand &&
|
|
272
|
+
parent.value === path.value
|
|
273
|
+
) {
|
|
274
|
+
// `{ api }` in an object literal: keep the key, rename the value
|
|
275
|
+
parent.shorthand = false;
|
|
276
|
+
parent.key = j.identifier("api");
|
|
277
|
+
parent.value = j.identifier("aui");
|
|
278
|
+
} else if (
|
|
279
|
+
j.ExportSpecifier.check(parent) &&
|
|
280
|
+
parent.local === path.value
|
|
281
|
+
) {
|
|
282
|
+
// `export { api }` / `export { api as name }`: rename the local
|
|
283
|
+
// binding, keep the public name. Replaced wholesale — recast keeps
|
|
284
|
+
// the shorthand form (dropping the alias) when only the fields of
|
|
285
|
+
// the original node change.
|
|
286
|
+
path.parent.replace(
|
|
287
|
+
j.exportSpecifier.from({
|
|
288
|
+
local: j.identifier("aui"),
|
|
289
|
+
exported: j.Identifier.check(parent.exported)
|
|
290
|
+
? j.identifier(parent.exported.name)
|
|
291
|
+
: parent.exported,
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
} else {
|
|
295
|
+
path.value.name = "aui";
|
|
208
296
|
}
|
|
209
|
-
|
|
297
|
+
markAsChanged();
|
|
298
|
+
}
|
|
299
|
+
for (const idNode of renamedDeclaratorIds) {
|
|
300
|
+
idNode.name = "aui";
|
|
301
|
+
markAsChanged();
|
|
302
|
+
}
|
|
210
303
|
}
|
|
211
304
|
|
|
212
305
|
// 4. Update hook call references (in case they're used as values)
|
|
@@ -120,12 +120,18 @@ const fixedConditionMap: Record<string, Record<string, string>> = {
|
|
|
120
120
|
},
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
// A prop value the maps cannot faithfully express as a static condition
|
|
124
|
+
// (dynamic expressions, `{undefined}`); elements carrying one are skipped
|
|
125
|
+
// so runtime behavior is never silently changed.
|
|
126
|
+
const UNSUPPORTED_VALUE: unique symbol = Symbol("unsupported");
|
|
127
|
+
|
|
123
128
|
/**
|
|
124
129
|
* Extract the value of a JSX attribute.
|
|
125
130
|
* - Boolean prop (no value): `<X.If user>` → `true`
|
|
126
131
|
* - `{true}` / `{false}`: → `true` / `false`
|
|
127
132
|
* - `{"positive"}`: → `"positive"`
|
|
128
133
|
* - `{null}`: → `null`
|
|
134
|
+
* - anything else (dynamic expressions): → UNSUPPORTED_VALUE
|
|
129
135
|
*/
|
|
130
136
|
const getAttrValue = (j: any, attr: any): unknown => {
|
|
131
137
|
// Boolean attribute (no value), e.g. `<X.If user>`
|
|
@@ -137,12 +143,14 @@ const getAttrValue = (j: any, attr: any): unknown => {
|
|
|
137
143
|
if (j.JSXExpressionContainer.check(attr.value)) {
|
|
138
144
|
const expr = attr.value.expression;
|
|
139
145
|
if (j.BooleanLiteral.check(expr)) return expr.value;
|
|
146
|
+
// NullLiteral bases Literal in ast-types but carries no `value` field,
|
|
147
|
+
// so it must be recognized before the generic Literal branch.
|
|
148
|
+
if (j.NullLiteral.check(expr)) return null;
|
|
140
149
|
if (j.Literal.check(expr)) {
|
|
141
150
|
if (expr.value === null) return null;
|
|
142
151
|
return expr.value;
|
|
143
152
|
}
|
|
144
|
-
|
|
145
|
-
if (j.Identifier.check(expr) && expr.name === "undefined") return undefined;
|
|
153
|
+
return UNSUPPORTED_VALUE;
|
|
146
154
|
}
|
|
147
155
|
|
|
148
156
|
// String literal
|
|
@@ -150,7 +158,7 @@ const getAttrValue = (j: any, attr: any): unknown => {
|
|
|
150
158
|
return attr.value.value;
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
return
|
|
161
|
+
return UNSUPPORTED_VALUE;
|
|
154
162
|
};
|
|
155
163
|
|
|
156
164
|
const buildConditionString = (fragments: ConditionFragment[]): string => {
|
|
@@ -185,61 +193,52 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
185
193
|
|
|
186
194
|
if (importedPrimitives.size === 0) return;
|
|
187
195
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (!j.JSXMemberExpression.check(name)) return;
|
|
192
|
-
if (!j.JSXIdentifier.check(name.object)) return;
|
|
193
|
-
if (!j.JSXIdentifier.check(name.property)) return;
|
|
194
|
-
|
|
195
|
-
const primitiveName = name.object.name as string;
|
|
196
|
-
const propertyName = name.property.name as string;
|
|
197
|
-
const fixedMap = fixedConditionMap[primitiveName];
|
|
198
|
-
if (!fixedMap) return;
|
|
199
|
-
const conditionBody = fixedMap[propertyName];
|
|
200
|
-
if (!conditionBody) return;
|
|
201
|
-
if (!importedPrimitives.has(primitiveName)) return;
|
|
202
|
-
|
|
203
|
-
// Only transform if there are no props (other than children, which are implicit)
|
|
204
|
-
const attrs: any[] = path.value.attributes || [];
|
|
205
|
-
if (attrs.length > 0) return;
|
|
206
|
-
|
|
196
|
+
// Opening and closing tags are rewritten together per element, so a
|
|
197
|
+
// skipped element can never be left with a mismatched closing tag.
|
|
198
|
+
const convertElementToAuiIf = (elementPath: any, conditionBody: string) => {
|
|
207
199
|
const arrowFnAst = j(`(s) => ${conditionBody}`)
|
|
208
200
|
.find(j.ArrowFunctionExpression)
|
|
209
201
|
.paths()[0]!.value;
|
|
210
202
|
|
|
211
|
-
|
|
212
|
-
|
|
203
|
+
const opening = elementPath.value.openingElement;
|
|
204
|
+
opening.name = j.jsxIdentifier("AuiIf");
|
|
205
|
+
opening.attributes = [
|
|
213
206
|
j.jsxAttribute(
|
|
214
207
|
j.jsxIdentifier("condition"),
|
|
215
208
|
j.jsxExpressionContainer(arrowFnAst),
|
|
216
209
|
),
|
|
217
210
|
];
|
|
211
|
+
if (elementPath.value.closingElement) {
|
|
212
|
+
elementPath.value.closingElement.name = j.jsxIdentifier("AuiIf");
|
|
213
|
+
}
|
|
218
214
|
|
|
219
215
|
needsAuiIfImport = true;
|
|
220
216
|
markAsChanged();
|
|
221
|
-
}
|
|
217
|
+
};
|
|
222
218
|
|
|
223
|
-
//
|
|
224
|
-
root.find(j.
|
|
225
|
-
const name = path.value.name;
|
|
219
|
+
// Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>
|
|
220
|
+
root.find(j.JSXElement).forEach((path: any) => {
|
|
221
|
+
const name = path.value.openingElement.name;
|
|
226
222
|
if (!j.JSXMemberExpression.check(name)) return;
|
|
227
223
|
if (!j.JSXIdentifier.check(name.object)) return;
|
|
228
224
|
if (!j.JSXIdentifier.check(name.property)) return;
|
|
229
225
|
|
|
230
226
|
const primitiveName = name.object.name as string;
|
|
231
227
|
const propertyName = name.property.name as string;
|
|
232
|
-
const
|
|
233
|
-
if (!
|
|
228
|
+
const conditionBody = fixedConditionMap[primitiveName]?.[propertyName];
|
|
229
|
+
if (!conditionBody) return;
|
|
234
230
|
if (!importedPrimitives.has(primitiveName)) return;
|
|
235
231
|
|
|
236
|
-
|
|
237
|
-
|
|
232
|
+
// Only transform if there are no props (other than children, which are implicit)
|
|
233
|
+
const attrs: any[] = path.value.openingElement.attributes || [];
|
|
234
|
+
if (attrs.length > 0) return;
|
|
235
|
+
|
|
236
|
+
convertElementToAuiIf(path, conditionBody);
|
|
238
237
|
});
|
|
239
238
|
|
|
240
239
|
// Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>
|
|
241
|
-
root.find(j.
|
|
242
|
-
const name = path.value.name;
|
|
240
|
+
root.find(j.JSXElement).forEach((path: any) => {
|
|
241
|
+
const name = path.value.openingElement.name;
|
|
243
242
|
|
|
244
243
|
// Check for `<XPrimitive.If ...>`
|
|
245
244
|
if (!j.JSXMemberExpression.check(name)) return;
|
|
@@ -253,7 +252,7 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
253
252
|
if (!importedPrimitives.has(primitiveName)) return;
|
|
254
253
|
|
|
255
254
|
// Extract props
|
|
256
|
-
const attrs: any[] = path.value.attributes || [];
|
|
255
|
+
const attrs: any[] = path.value.openingElement.attributes || [];
|
|
257
256
|
const fragments: ConditionFragment[] = [];
|
|
258
257
|
let hasUnknownProp = false;
|
|
259
258
|
|
|
@@ -265,7 +264,11 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
265
264
|
}
|
|
266
265
|
const propName =
|
|
267
266
|
typeof attr.name.name === "string" ? attr.name.name : null;
|
|
268
|
-
if (!propName)
|
|
267
|
+
if (!propName) {
|
|
268
|
+
// e.g. JSXNamespacedName — not expressible as a condition
|
|
269
|
+
hasUnknownProp = true;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
269
272
|
|
|
270
273
|
const mapper = propMap[propName];
|
|
271
274
|
if (!mapper) {
|
|
@@ -274,6 +277,10 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
const value = getAttrValue(j, attr);
|
|
280
|
+
if (value === UNSUPPORTED_VALUE) {
|
|
281
|
+
hasUnknownProp = true;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
277
284
|
const fragment = mapper(value);
|
|
278
285
|
if (fragment) {
|
|
279
286
|
fragments.push(fragment);
|
|
@@ -283,42 +290,7 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
283
290
|
// If we couldn't map all props, skip this element
|
|
284
291
|
if (hasUnknownProp || fragments.length === 0) return;
|
|
285
292
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// Parse the arrow function as an expression to get a proper AST node
|
|
289
|
-
const arrowFnAst = j(`(s) => ${conditionBody}`)
|
|
290
|
-
.find(j.ArrowFunctionExpression)
|
|
291
|
-
.paths()[0]!.value;
|
|
292
|
-
|
|
293
|
-
// Replace <XPrimitive.If ...> with <AuiIf condition={...}>
|
|
294
|
-
path.value.name = j.jsxIdentifier("AuiIf");
|
|
295
|
-
|
|
296
|
-
// Replace all attributes with a single condition prop
|
|
297
|
-
path.value.attributes = [
|
|
298
|
-
j.jsxAttribute(
|
|
299
|
-
j.jsxIdentifier("condition"),
|
|
300
|
-
j.jsxExpressionContainer(arrowFnAst),
|
|
301
|
-
),
|
|
302
|
-
];
|
|
303
|
-
|
|
304
|
-
needsAuiIfImport = true;
|
|
305
|
-
markAsChanged();
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Update closing elements to match
|
|
309
|
-
root.find(j.JSXClosingElement).forEach((path: any) => {
|
|
310
|
-
const name = path.value.name;
|
|
311
|
-
if (!j.JSXMemberExpression.check(name)) return;
|
|
312
|
-
if (!j.JSXIdentifier.check(name.object)) return;
|
|
313
|
-
if (!j.JSXIdentifier.check(name.property)) return;
|
|
314
|
-
if (name.property.name !== "If") return;
|
|
315
|
-
|
|
316
|
-
const primitiveName = name.object.name;
|
|
317
|
-
if (!primitiveMap[primitiveName]) return;
|
|
318
|
-
if (!importedPrimitives.has(primitiveName)) return;
|
|
319
|
-
|
|
320
|
-
path.value.name = j.jsxIdentifier("AuiIf");
|
|
321
|
-
markAsChanged();
|
|
293
|
+
convertElementToAuiIf(path, buildConditionString(fragments));
|
|
322
294
|
});
|
|
323
295
|
|
|
324
296
|
// Add AuiIf import if needed
|
package/src/commands/add.ts
CHANGED
|
@@ -5,13 +5,12 @@ import {
|
|
|
5
5
|
getComponentsJsonStyle,
|
|
6
6
|
resolveRegistryItemUrl,
|
|
7
7
|
} from "../lib/utils/registry";
|
|
8
|
+
import { dlxCommand, resolvePackageManager } from "../lib/create-project";
|
|
9
|
+
import { runSpawn, SpawnExitError, SpawnSignalError } from "../lib/run-spawn";
|
|
8
10
|
import {
|
|
9
|
-
dlxCommand,
|
|
10
|
-
resolvePackageManager,
|
|
11
|
-
resolvePackageManagerForCwd,
|
|
12
11
|
type PackageManagerName,
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
resolvePackageManagerForCwd,
|
|
13
|
+
} from "../lib/utils/package-manager";
|
|
15
14
|
|
|
16
15
|
export interface AddComponentsPlan {
|
|
17
16
|
command: string;
|
|
@@ -92,6 +91,7 @@ export const add = new Command()
|
|
|
92
91
|
try {
|
|
93
92
|
await runSpawn(command, args, opts.cwd);
|
|
94
93
|
} catch (error) {
|
|
94
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
95
95
|
if (error instanceof SpawnExitError) {
|
|
96
96
|
logger.error(`Process exited with code ${error.code}`);
|
|
97
97
|
process.exit(error.code);
|