@menapo/vi18n 0.0.0-stage → 0.1.0
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/LICENSE +49 -0
- package/README.md +110 -2
- package/dist/apply-Bre-lY9J.js +228 -0
- package/dist/captured-Bj8bO6vd.js +1034 -0
- package/dist/check-D34P-S--.js +1 -0
- package/dist/check-DyrAbv73.js +201 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +41 -0
- package/dist/codec-DuvU2VQy.js +207 -0
- package/dist/collect-Cx4_SKOe.js +13 -0
- package/dist/collisions-CfpZU8e4.js +217 -0
- package/dist/compile-B5Mz6WJm.js +245 -0
- package/dist/compile-Cc97JaW2.js +2502 -0
- package/dist/concurrency-C9UUWuDC.js +96 -0
- package/dist/concurrency-DONL7t4V.js +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.js +39 -0
- package/dist/dir-zw6bGMKQ.js +290 -0
- package/dist/duplicates-yuRljQ6h.js +185 -0
- package/dist/engine-B7p40M__.js +531 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +1241 -0
- package/dist/exclude-Du-Zd_qI.js +26 -0
- package/dist/export-DGsMbcTZ.js +103 -0
- package/dist/extract-Dujlsgnk.js +298 -0
- package/dist/formats-KPIg-08V.js +781 -0
- package/dist/i18next-CrUnKFfh.js +1596 -0
- package/dist/i18next-Rgo-v9l0.js +1 -0
- package/dist/import-Cwdhcywt.js +225 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +239 -0
- package/dist/init-BecCTvKi.js +792 -0
- package/dist/library-D5CWvTQs.js +1882 -0
- package/dist/lingui--PcOOYry.js +833 -0
- package/dist/lint.d.ts +105 -0
- package/dist/lint.js +971 -0
- package/dist/load-CQRz4PpM.js +2950 -0
- package/dist/load-gW-oJt1Q.js +747 -0
- package/dist/locales-DX4MOBFE.js +827 -0
- package/dist/lsp-Cq1Nuu-Q.js +31 -0
- package/dist/match-BGr8EXB3.js +89 -0
- package/dist/mcp-Cf0T7qPW.js +28 -0
- package/dist/merge-driver-CpiiSVIc.js +422 -0
- package/dist/messages-Cxa-5tR0.js +166 -0
- package/dist/migrate-CntqvrW4.js +1 -0
- package/dist/migrate-dPgq9uzy.js +1484 -0
- package/dist/openai-CTaMDcl-.js +346 -0
- package/dist/output-DsaHKHEt.js +164 -0
- package/dist/parse.d.ts +62 -0
- package/dist/parse.js +156 -0
- package/dist/pipeline-C7g11THL.js +2031 -0
- package/dist/pipeline-wxS0SApG.js +1 -0
- package/dist/project-Dwx7iPm6.js +381 -0
- package/dist/project-pXipT8iG.js +1046 -0
- package/dist/pseudo-DRv30iN5.js +145 -0
- package/dist/raw-C4Y6vyGY.js +4708 -0
- package/dist/react-intl-CaBVwJ9u.js +519 -0
- package/dist/restore-Btq66Tid.js +175 -0
- package/dist/rewrite-CiTRAICl.js +803 -0
- package/dist/rich-DCUKl5HM.js +97 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/run-DYD_VXKy.js +694 -0
- package/dist/runtime-DTMmliT1.js +64 -0
- package/dist/scan-worker.d.ts +62 -0
- package/dist/scan-worker.js +1 -0
- package/dist/scoped-BMYpSthj.js +44 -0
- package/dist/server-BDkykMjb.js +11823 -0
- package/dist/server-qv3mz70d.js +10485 -0
- package/dist/server.d.ts +169 -0
- package/dist/server.js +1234 -0
- package/dist/sitemap-DV3o8QD7.js +159 -0
- package/dist/sources-D19czDSA.js +2242 -0
- package/dist/status-DZArW5aE.js +149 -0
- package/dist/stdio-CVaJfxA7.js +29 -0
- package/dist/stdio-FPOKQAgu.js +95 -0
- package/dist/sync-CcVzger_.js +658 -0
- package/dist/t-DC-c9zE5.js +454 -0
- package/dist/test.d.ts +116 -0
- package/dist/test.js +319 -0
- package/dist/translate-DZR3MkSA.js +299 -0
- package/dist/ts-BaU5yOf0.js +4863 -0
- package/dist/types-CgUCkFU1.d.ts +707 -0
- package/dist/types-D3QGM8XO.js +146 -0
- package/dist/types-DsdWBG0b.d.ts +558 -0
- package/dist/types-DtvQ7UdD.js +1948 -0
- package/dist/types-wSh9fCBY.js +11 -0
- package/dist/units-CQ5Q99RC.js +150 -0
- package/dist/url-CRfCOhUI.js +172 -0
- package/dist/url-CsBkRaJy.js +225 -0
- package/dist/validate-BxpupdAf.js +2615 -0
- package/dist/validate-D6DJepZv.js +365 -0
- package/dist/version-BlyLGlOa.js +28 -0
- package/dist/vite-scan-B_YW4X_a.js +242 -0
- package/dist/vite.d.ts +34 -0
- package/dist/vite.js +7558 -0
- package/dist/vue-i18n-CEMnpv7k.js +651 -0
- package/dist/which-C6PFI1Dm.js +116 -0
- package/dist/worker-84U6uYB3.js +1656 -0
- package/package.json +97 -3
- package/schemas/catalog.json +57 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { s as decodeEntities } from "./worker-84U6uYB3.js";
|
|
2
|
+
import { o as importLineEdit } from "./migrate-dPgq9uzy.js";
|
|
3
|
+
//#region src/cli/commands/migrate/js.ts
|
|
4
|
+
/**
|
|
5
|
+
* ESTree analysis for the library codemods (`vi18n migrate --from i18next |
|
|
6
|
+
* react-intl | lingui | vue-i18n`, INTERNALS §2.14): a walker that knows
|
|
7
|
+
* each node's parent and field, the module's imports, every binding with the
|
|
8
|
+
* range it is visible in, and every reference by name. That is enough to
|
|
9
|
+
* tell whether the rewrites consumed every use of a hook result or an import
|
|
10
|
+
* (so it can go), and whether a name is free at a call site (so the vi18n
|
|
11
|
+
* import can take it). Scopes are approximated by source ranges: a wrong
|
|
12
|
+
* answer can only keep a binding or alias an import, never capture a name.
|
|
13
|
+
*/
|
|
14
|
+
function isNode(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
16
|
+
}
|
|
17
|
+
/** Children with the field holding them, in source order. */
|
|
18
|
+
function* childEntries(node) {
|
|
19
|
+
for (const key in node) {
|
|
20
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
21
|
+
const value = node[key];
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
for (const item of value) if (isNode(item)) yield [key, item];
|
|
24
|
+
} else if (isNode(value)) yield [key, value];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function unwrap(node) {
|
|
28
|
+
let current = node;
|
|
29
|
+
while (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSNonNullExpression" || current.type === "TSTypeAssertion" || current.type === "ParenthesizedExpression") current = current.expression;
|
|
30
|
+
return current;
|
|
31
|
+
}
|
|
32
|
+
/** A string literal, a template without substitutions, or a `+` of those; null otherwise. */
|
|
33
|
+
function stringValue(node) {
|
|
34
|
+
if (node === void 0 || node === null) return null;
|
|
35
|
+
const n = unwrap(node);
|
|
36
|
+
if (n.type === "Literal" && typeof n.value === "string") return n.value;
|
|
37
|
+
if (n.type === "TemplateLiteral" && n.expressions.length === 0) {
|
|
38
|
+
const quasi = n.quasis[0];
|
|
39
|
+
return quasi === void 0 ? null : quasi.value.cooked;
|
|
40
|
+
}
|
|
41
|
+
if (n.type === "BinaryExpression" && n.operator === "+") {
|
|
42
|
+
const left = stringValue(n.left);
|
|
43
|
+
const right = stringValue(n.right);
|
|
44
|
+
return left === null || right === null ? null : left + right;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function keyName(property) {
|
|
49
|
+
const key = property.key;
|
|
50
|
+
if (property.computed === true) return stringValue(key);
|
|
51
|
+
if (key.type === "Identifier") return key.name;
|
|
52
|
+
if (key.type === "Literal") return String(key.value);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/** `name` of an Identifier (after unwrapping), else null. */
|
|
56
|
+
function identifierName(node) {
|
|
57
|
+
if (node === void 0 || node === null) return null;
|
|
58
|
+
const n = unwrap(node);
|
|
59
|
+
return n.type === "Identifier" ? n.name : null;
|
|
60
|
+
}
|
|
61
|
+
/** `object.property` of a non-computed member call's callee, else null. */
|
|
62
|
+
function memberParts(node) {
|
|
63
|
+
const n = unwrap(node);
|
|
64
|
+
if (n.type !== "MemberExpression" || n.computed === true) return null;
|
|
65
|
+
const property = n.property;
|
|
66
|
+
if (property.type !== "Identifier" && property.type !== "PrivateIdentifier") return null;
|
|
67
|
+
return {
|
|
68
|
+
object: n.object,
|
|
69
|
+
property: property.name
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** The plain properties of an object literal by key; null when a key is computed or a spread is present. */
|
|
73
|
+
function objectProperties(node) {
|
|
74
|
+
const n = unwrap(node);
|
|
75
|
+
if (n.type !== "ObjectExpression") return null;
|
|
76
|
+
const properties = /* @__PURE__ */ new Map();
|
|
77
|
+
const spreads = [];
|
|
78
|
+
const order = [];
|
|
79
|
+
for (const property of n.properties) {
|
|
80
|
+
order.push(property);
|
|
81
|
+
if (property.type === "SpreadElement") {
|
|
82
|
+
spreads.push(property);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const key = keyName(property);
|
|
86
|
+
if (key === null) return null;
|
|
87
|
+
properties.set(key, property);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
properties,
|
|
91
|
+
spreads,
|
|
92
|
+
order
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const FUNCTIONS = /* @__PURE__ */ new Set([
|
|
96
|
+
"FunctionDeclaration",
|
|
97
|
+
"FunctionExpression",
|
|
98
|
+
"ArrowFunctionExpression"
|
|
99
|
+
]);
|
|
100
|
+
/** Nodes whose range is a block scope for `let`, `const`, `class` and block functions. */
|
|
101
|
+
const BLOCKS = /* @__PURE__ */ new Set([
|
|
102
|
+
"BlockStatement",
|
|
103
|
+
"StaticBlock",
|
|
104
|
+
"ForStatement",
|
|
105
|
+
"ForInStatement",
|
|
106
|
+
"ForOfStatement",
|
|
107
|
+
"SwitchStatement",
|
|
108
|
+
"CatchClause",
|
|
109
|
+
"ClassBody"
|
|
110
|
+
]);
|
|
111
|
+
/** TypeScript signatures whose parameters bind nothing at runtime. */
|
|
112
|
+
const SIGNATURES = /* @__PURE__ */ new Set([
|
|
113
|
+
"TSFunctionType",
|
|
114
|
+
"TSConstructorType",
|
|
115
|
+
"TSMethodSignature",
|
|
116
|
+
"TSCallSignatureDeclaration",
|
|
117
|
+
"TSConstructSignatureDeclaration",
|
|
118
|
+
"TSDeclareFunction",
|
|
119
|
+
"TSEmptyBodyFunctionExpression"
|
|
120
|
+
]);
|
|
121
|
+
function isReferencePosition(node, parent, field) {
|
|
122
|
+
if (parent === null) return true;
|
|
123
|
+
switch (parent.type) {
|
|
124
|
+
case "MemberExpression": return field !== "property" || parent.computed === true;
|
|
125
|
+
case "Property":
|
|
126
|
+
case "MethodDefinition":
|
|
127
|
+
case "PropertyDefinition":
|
|
128
|
+
case "AccessorProperty":
|
|
129
|
+
case "TSAbstractMethodDefinition":
|
|
130
|
+
case "TSAbstractPropertyDefinition":
|
|
131
|
+
case "TSPropertySignature":
|
|
132
|
+
case "TSMethodSignature": return field !== "key" || parent.computed === true;
|
|
133
|
+
case "ImportSpecifier":
|
|
134
|
+
case "ImportDefaultSpecifier":
|
|
135
|
+
case "ImportNamespaceSpecifier":
|
|
136
|
+
case "ImportAttribute":
|
|
137
|
+
case "LabeledStatement":
|
|
138
|
+
case "BreakStatement":
|
|
139
|
+
case "ContinueStatement":
|
|
140
|
+
case "MetaProperty":
|
|
141
|
+
case "TSEnumMember":
|
|
142
|
+
case "TSTypeParameter":
|
|
143
|
+
case "TSImportType": return false;
|
|
144
|
+
case "TSQualifiedName": return field === "left";
|
|
145
|
+
case "ExportSpecifier": return field === "local" && node === parent.local;
|
|
146
|
+
default: return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Component names: `<Trans>`, `<Foo.Bar>`'s `Foo`; lowercase names are elements, not bindings. */
|
|
150
|
+
function isJsxReference(node, parent, field) {
|
|
151
|
+
if (parent === null) return false;
|
|
152
|
+
const name = node.name;
|
|
153
|
+
if (parent.type === "JSXMemberExpression") return field === "object";
|
|
154
|
+
if (parent.type === "JSXOpeningElement" || parent.type === "JSXClosingElement") return field === "name" && !/^[a-z]/.test(name) && !name.includes("-");
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
function analyzeModule(program) {
|
|
158
|
+
const imports = [];
|
|
159
|
+
const bindings = [];
|
|
160
|
+
const bindingIds = /* @__PURE__ */ new Set();
|
|
161
|
+
const references = /* @__PURE__ */ new Map();
|
|
162
|
+
const parents = /* @__PURE__ */ new Map();
|
|
163
|
+
const blocks = [program];
|
|
164
|
+
const functions = [program];
|
|
165
|
+
const range = (n) => ({
|
|
166
|
+
start: n.start,
|
|
167
|
+
end: n.end
|
|
168
|
+
});
|
|
169
|
+
const declare = (pattern, scope, kind, declarator, slot = null) => {
|
|
170
|
+
if (!isNode(pattern)) return;
|
|
171
|
+
switch (pattern.type) {
|
|
172
|
+
case "Identifier":
|
|
173
|
+
bindingIds.add(pattern);
|
|
174
|
+
bindings.push({
|
|
175
|
+
name: pattern.name,
|
|
176
|
+
id: pattern,
|
|
177
|
+
scope: range(scope),
|
|
178
|
+
kind,
|
|
179
|
+
declarator,
|
|
180
|
+
slot
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
case "ObjectPattern":
|
|
184
|
+
for (const property of pattern.properties) if (property.type === "RestElement") declare(property, scope, kind, declarator, property);
|
|
185
|
+
else declare(property.value, scope, kind, declarator, property);
|
|
186
|
+
return;
|
|
187
|
+
case "ArrayPattern":
|
|
188
|
+
for (const element of pattern.elements) if (element !== null) declare(element, scope, kind, declarator, element);
|
|
189
|
+
return;
|
|
190
|
+
case "AssignmentPattern":
|
|
191
|
+
declare(pattern.left, scope, kind, declarator, slot);
|
|
192
|
+
return;
|
|
193
|
+
case "RestElement":
|
|
194
|
+
declare(pattern.argument, scope, kind, declarator, slot);
|
|
195
|
+
return;
|
|
196
|
+
case "TSParameterProperty":
|
|
197
|
+
declare(pattern.parameter, scope, kind, declarator, slot);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
const visit = (node, parent, field) => {
|
|
202
|
+
if (parent !== null && field !== null) parents.set(node, {
|
|
203
|
+
node: parent,
|
|
204
|
+
field
|
|
205
|
+
});
|
|
206
|
+
const block = blocks[blocks.length - 1];
|
|
207
|
+
const fn = functions[functions.length - 1];
|
|
208
|
+
switch (node.type) {
|
|
209
|
+
case "ImportDeclaration": {
|
|
210
|
+
const info = {
|
|
211
|
+
node,
|
|
212
|
+
source: node.source.value,
|
|
213
|
+
typeOnly: node.importKind === "type",
|
|
214
|
+
specifiers: []
|
|
215
|
+
};
|
|
216
|
+
for (const specifier of node.specifiers ?? []) {
|
|
217
|
+
const local = specifier.local;
|
|
218
|
+
const imported = specifier.type === "ImportDefaultSpecifier" ? "default" : specifier.type === "ImportNamespaceSpecifier" ? "*" : specifier.imported.name ?? specifier.imported.value;
|
|
219
|
+
info.specifiers.push({
|
|
220
|
+
local: local.name,
|
|
221
|
+
imported,
|
|
222
|
+
node: specifier,
|
|
223
|
+
typeOnly: info.typeOnly || specifier.importKind === "type"
|
|
224
|
+
});
|
|
225
|
+
declare(local, program, "import", null, specifier);
|
|
226
|
+
}
|
|
227
|
+
imports.push(info);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "VariableDeclaration":
|
|
231
|
+
for (const declarator of node.declarations) declare(declarator.id, node.kind === "var" ? fn : block, node.kind === "var" ? "var" : "lexical", declarator);
|
|
232
|
+
break;
|
|
233
|
+
case "FunctionDeclaration":
|
|
234
|
+
if (isNode(node.id)) declare(node.id, block, "function", null);
|
|
235
|
+
break;
|
|
236
|
+
case "ClassDeclaration":
|
|
237
|
+
if (isNode(node.id)) declare(node.id, block, "class", null);
|
|
238
|
+
break;
|
|
239
|
+
case "ClassExpression":
|
|
240
|
+
case "FunctionExpression":
|
|
241
|
+
if (isNode(node.id)) declare(node.id, node, node.type === "ClassExpression" ? "class" : "function", null);
|
|
242
|
+
break;
|
|
243
|
+
case "CatchClause":
|
|
244
|
+
declare(node.param, node, "catch", null);
|
|
245
|
+
break;
|
|
246
|
+
case "TSInterfaceDeclaration":
|
|
247
|
+
case "TSTypeAliasDeclaration":
|
|
248
|
+
case "TSEnumDeclaration":
|
|
249
|
+
case "TSModuleDeclaration": if (isNode(node.id)) declare(node.id, block, "type", null);
|
|
250
|
+
}
|
|
251
|
+
if (FUNCTIONS.has(node.type)) for (const param of node.params) declare(param, node, "param", null);
|
|
252
|
+
else if (SIGNATURES.has(node.type)) for (const param of node.params ?? []) declare(param, node, "type", null);
|
|
253
|
+
if (node.type === "Identifier" && !bindingIds.has(node)) {
|
|
254
|
+
if (isReferencePosition(node, parent, field)) addReference(references, node);
|
|
255
|
+
} else if (node.type === "JSXIdentifier" && isJsxReference(node, parent, field)) addReference(references, node);
|
|
256
|
+
const isFunction = FUNCTIONS.has(node.type);
|
|
257
|
+
const isBlock = BLOCKS.has(node.type) || isFunction;
|
|
258
|
+
if (isFunction) functions.push(node);
|
|
259
|
+
if (isBlock) blocks.push(node);
|
|
260
|
+
for (const [key, child] of childEntries(node)) visit(child, node, key);
|
|
261
|
+
if (isBlock) blocks.pop();
|
|
262
|
+
if (isFunction) functions.pop();
|
|
263
|
+
};
|
|
264
|
+
visit(program, null, null);
|
|
265
|
+
return {
|
|
266
|
+
program,
|
|
267
|
+
imports,
|
|
268
|
+
bindings,
|
|
269
|
+
references,
|
|
270
|
+
parentOf: (node) => parents.get(node) ?? null
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function addReference(references, node) {
|
|
274
|
+
const name = node.name;
|
|
275
|
+
let list = references.get(name);
|
|
276
|
+
if (list === void 0) references.set(name, list = []);
|
|
277
|
+
list.push(node);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Whether every reference to `binding` (a reference to its name inside its
|
|
281
|
+
* scope) is one the rewrites consumed: it can then be removed.
|
|
282
|
+
*/
|
|
283
|
+
function isConsumed(info, binding, consumed, extra = []) {
|
|
284
|
+
const inScope = (offset) => binding.scope.start <= offset && offset < binding.scope.end;
|
|
285
|
+
for (const ref of info.references.get(binding.name) ?? []) if (inScope(ref.start) && !consumed.has(ref.start)) return false;
|
|
286
|
+
return extra.every((offset) => !inScope(offset) || consumed.has(offset));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Whether an import of `name` at module level would resolve at every offset
|
|
290
|
+
* in `sites` and capture nothing else: no binding left in place declares it
|
|
291
|
+
* at module level or around a site, and no reference the rewrites keep reads
|
|
292
|
+
* a global of that name.
|
|
293
|
+
*/
|
|
294
|
+
function isNameFree(info, name, sites, removed, consumed) {
|
|
295
|
+
const kept = info.bindings.filter((b) => b.name === name && !removed.has(b.id));
|
|
296
|
+
for (const b of kept) {
|
|
297
|
+
if (b.scope.start === info.program.start && b.scope.end === info.program.end) return false;
|
|
298
|
+
if (sites.some((offset) => b.scope.start <= offset && offset < b.scope.end)) return false;
|
|
299
|
+
}
|
|
300
|
+
for (const ref of info.references.get(name) ?? []) {
|
|
301
|
+
if (consumed.has(ref.start)) continue;
|
|
302
|
+
if (!kept.some((b) => b.scope.start <= ref.start && ref.start < b.scope.end)) return false;
|
|
303
|
+
}
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
/** The name of a JSX element as written (`Trans`, `Foo.Bar`, `div`). */
|
|
307
|
+
function jsxName(element) {
|
|
308
|
+
const name = element.openingElement.name;
|
|
309
|
+
const print = (n) => n.type === "JSXMemberExpression" ? `${print(n.object)}.${n.property.name}` : n.type === "JSXNamespacedName" ? `${n.namespace.name}:${n.name.name}` : n.name;
|
|
310
|
+
return print(name);
|
|
311
|
+
}
|
|
312
|
+
/** JSXIdentifier nodes naming an element (opening and closing), for consumption. */
|
|
313
|
+
function jsxNameNodes(element) {
|
|
314
|
+
const out = [];
|
|
315
|
+
const root = (n) => n.type === "JSXMemberExpression" ? root(n.object) : n;
|
|
316
|
+
out.push(root(element.openingElement.name));
|
|
317
|
+
const closing = element.closingElement;
|
|
318
|
+
if (isNode(closing)) out.push(root(closing.name));
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
function jsxAttributes(element) {
|
|
322
|
+
const named = /* @__PURE__ */ new Map();
|
|
323
|
+
const spreads = [];
|
|
324
|
+
for (const attribute of element.openingElement.attributes) {
|
|
325
|
+
if (attribute.type === "JSXSpreadAttribute") {
|
|
326
|
+
spreads.push(attribute);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const name = attribute.name;
|
|
330
|
+
const key = name.type === "JSXNamespacedName" ? `${name.namespace.name}:${name.name.name}` : name.name;
|
|
331
|
+
named.set(key, {
|
|
332
|
+
attribute,
|
|
333
|
+
value: isNode(attribute.value) ? attribute.value : null
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
named,
|
|
338
|
+
spreads
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/** An attribute's expression: the literal itself, or what `{…}` holds; null for `{}` and flags. */
|
|
342
|
+
function attributeExpression(value) {
|
|
343
|
+
if (value === null) return null;
|
|
344
|
+
if (value.type === "JSXExpressionContainer") {
|
|
345
|
+
const expression = value.expression;
|
|
346
|
+
return expression.type === "JSXEmptyExpression" ? null : expression;
|
|
347
|
+
}
|
|
348
|
+
return value;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* A static attribute string (`id="x"`, `id={"x"}`, ``id={`x`}``), else null.
|
|
352
|
+
* Oxc keeps entities as written; JSX decodes them in a quoted attribute (not
|
|
353
|
+
* in a string inside `{…}`), as the old library's Babel or TypeScript build did.
|
|
354
|
+
*/
|
|
355
|
+
function attributeString(value) {
|
|
356
|
+
const expression = attributeExpression(value);
|
|
357
|
+
if (expression === null) return null;
|
|
358
|
+
const text = stringValue(expression);
|
|
359
|
+
return text !== null && value?.type === "Literal" ? decodeEntities(text) : text;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* JSX text as React's transform reads it: lines trimmed, lines that are
|
|
363
|
+
* only whitespace dropped, the rest joined by one space; entities decoded
|
|
364
|
+
* first (Oxc's `value` keeps them as written; Babel's had them decoded).
|
|
365
|
+
*/
|
|
366
|
+
function cleanJsxText(value) {
|
|
367
|
+
const lines = decodeEntities(value).split(/\r\n|\n|\r/);
|
|
368
|
+
let last = 0;
|
|
369
|
+
for (let i = 0; i < lines.length; i++) if (/[^ \t]/.test(lines[i])) last = i;
|
|
370
|
+
let out = "";
|
|
371
|
+
for (let i = 0; i < lines.length; i++) {
|
|
372
|
+
let line = lines[i].replace(/\t/g, " ");
|
|
373
|
+
if (i !== 0) line = line.replace(/^ +/, "");
|
|
374
|
+
if (i !== lines.length - 1) line = line.replace(/ +$/, "");
|
|
375
|
+
if (line === "") continue;
|
|
376
|
+
out += i === last ? line : line + " ";
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A JSX element's children as React passes them: text cleaned (and dropped
|
|
382
|
+
* when empty), empty expression containers (comments) dropped.
|
|
383
|
+
*/
|
|
384
|
+
function jsxChildren(element) {
|
|
385
|
+
const out = [];
|
|
386
|
+
for (const child of element.children) if (child.type === "JSXText") {
|
|
387
|
+
if (cleanJsxText(child.value) !== "") out.push(child);
|
|
388
|
+
} else if (child.type === "JSXExpressionContainer" && child.expression.type === "JSXEmptyExpression") continue;
|
|
389
|
+
else out.push(child);
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
/** Whether a node sits where JSX expects children (so a replacement needs `{…}`). */
|
|
393
|
+
function inJsxChildren(info, node) {
|
|
394
|
+
const parent = info.parentOf(node);
|
|
395
|
+
return parent !== null && parent.field === "children" && (parent.node.type === "JSXElement" || parent.node.type === "JSXFragment");
|
|
396
|
+
}
|
|
397
|
+
/** A statement's range widened to whole lines when it stands alone on them, so no blank line is left. */
|
|
398
|
+
function lineRange(text, start, end) {
|
|
399
|
+
let s = start;
|
|
400
|
+
while (s > 0 && (text[s - 1] === " " || text[s - 1] === " ")) s--;
|
|
401
|
+
let e = end;
|
|
402
|
+
while (e < text.length && (text[e] === " " || text[e] === " ")) e++;
|
|
403
|
+
if (!((s === 0 || text[s - 1] === "\n") && (e === text.length || text[e] === "\n" || text[e] === "\r"))) return {
|
|
404
|
+
start,
|
|
405
|
+
end
|
|
406
|
+
};
|
|
407
|
+
if (text[e] === "\r") e++;
|
|
408
|
+
if (text[e] === "\n") e++;
|
|
409
|
+
return {
|
|
410
|
+
start: s,
|
|
411
|
+
end: e
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The range that removes one element of a comma-separated list (`items`,
|
|
416
|
+
* in order) together with its separator: the comma after it, or before it
|
|
417
|
+
* when it is the last.
|
|
418
|
+
*/
|
|
419
|
+
function listItemRange(items, index) {
|
|
420
|
+
const item = items[index];
|
|
421
|
+
const next = items[index + 1];
|
|
422
|
+
if (next !== void 0) return {
|
|
423
|
+
start: item.start,
|
|
424
|
+
end: next.start
|
|
425
|
+
};
|
|
426
|
+
const previous = items[index - 1];
|
|
427
|
+
if (previous !== void 0) return {
|
|
428
|
+
start: previous.end,
|
|
429
|
+
end: item.end
|
|
430
|
+
};
|
|
431
|
+
return {
|
|
432
|
+
start: item.start,
|
|
433
|
+
end: item.end
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/** Escapes text for a JS string literal in `quote`. */
|
|
437
|
+
function jsString(value, quote = "\"") {
|
|
438
|
+
if (quote === "\"") return JSON.stringify(value);
|
|
439
|
+
return "'" + JSON.stringify(value).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'") + "'";
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/cli/commands/migrate/rewrite.ts
|
|
443
|
+
/**
|
|
444
|
+
* Planned file edits for the library codemods (INTERNALS §2.14). An edit
|
|
445
|
+
* replaces a range with text rendered only once every message has its id, so
|
|
446
|
+
* a call site prints `t("Text")` or `t({ id, message })` as the id
|
|
447
|
+
* assignment decides; edits nest (a `<Trans>` inside another one's values),
|
|
448
|
+
* and an outer edit reads its inner ranges through `src()` with the inner
|
|
449
|
+
* edits applied. `ModuleBuilder` adds what every module rewrite needs:
|
|
450
|
+
* removing the old library's hook bindings and imports once the rewrites
|
|
451
|
+
* consumed every use, and choosing local names for the vi18n imports.
|
|
452
|
+
*/
|
|
453
|
+
const RUNTIME_MODULE = "@menapo/vi18n";
|
|
454
|
+
function contains(outer, inner) {
|
|
455
|
+
if (inner.start < outer.start || inner.end > outer.end) return false;
|
|
456
|
+
if (inner.start === inner.end) return inner.start > outer.start && inner.start < outer.end;
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
function sortEdits(edits) {
|
|
460
|
+
return [...edits].sort((a, b) => {
|
|
461
|
+
if (a.start !== b.start) return a.start - b.start;
|
|
462
|
+
const aEmpty = a.start === a.end;
|
|
463
|
+
if (aEmpty !== (b.start === b.end)) return aEmpty ? -1 : 1;
|
|
464
|
+
return b.end - a.end;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* The file with every edit applied, and the top-level edits as the plan
|
|
469
|
+
* lists them (`from` as written, `to` as rendered).
|
|
470
|
+
*/
|
|
471
|
+
function renderPlan(plan, describe) {
|
|
472
|
+
const text = plan.text;
|
|
473
|
+
const listed = [];
|
|
474
|
+
const used = /* @__PURE__ */ new Set();
|
|
475
|
+
let rewritten = 0;
|
|
476
|
+
const renderRange = (edits, start, end, top) => {
|
|
477
|
+
let out = "";
|
|
478
|
+
let pos = start;
|
|
479
|
+
for (let i = 0; i < edits.length;) {
|
|
480
|
+
const edit = edits[i];
|
|
481
|
+
const nested = [];
|
|
482
|
+
let j = i + 1;
|
|
483
|
+
while (j < edits.length && contains(edit, edits[j])) nested.push(edits[j++]);
|
|
484
|
+
if (edit.start < pos) throw new Error(`vi18n migrate: overlapping edits at ${edit.start} in ${plan.file}`);
|
|
485
|
+
const rendered = renderEdit(edit, nested);
|
|
486
|
+
out += text.slice(pos, edit.start) + rendered;
|
|
487
|
+
if (top && edit.listed !== false) listed.push({
|
|
488
|
+
start: edit.start,
|
|
489
|
+
from: text.slice(edit.start, edit.end),
|
|
490
|
+
to: rendered
|
|
491
|
+
});
|
|
492
|
+
pos = edit.end;
|
|
493
|
+
i = j;
|
|
494
|
+
}
|
|
495
|
+
return out + text.slice(pos, end);
|
|
496
|
+
};
|
|
497
|
+
const renderEdit = (edit, nested) => {
|
|
498
|
+
if (edit.keys !== void 0) {
|
|
499
|
+
rewritten++;
|
|
500
|
+
for (const key of edit.keys) used.add(key);
|
|
501
|
+
}
|
|
502
|
+
if (typeof edit.text === "string") return edit.text;
|
|
503
|
+
const markup = edit.markup ?? null;
|
|
504
|
+
const renderer = {
|
|
505
|
+
src: (start, end) => renderRange(nested.filter((e) => e.start >= start && e.end <= end), start, end, false),
|
|
506
|
+
message: (key) => describe(key, markup === null || markup.quote !== "\"" ? "\"" : "'", markup),
|
|
507
|
+
fn: plan.locals,
|
|
508
|
+
types: plan.types
|
|
509
|
+
};
|
|
510
|
+
return edit.text(renderer);
|
|
511
|
+
};
|
|
512
|
+
return {
|
|
513
|
+
text: renderRange(sortEdits(plan.edits), 0, text.length, true),
|
|
514
|
+
listed,
|
|
515
|
+
used,
|
|
516
|
+
rewritten
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
const ALIASES = {
|
|
520
|
+
t: [
|
|
521
|
+
"t",
|
|
522
|
+
"vt",
|
|
523
|
+
"vi18nT"
|
|
524
|
+
],
|
|
525
|
+
msg: [
|
|
526
|
+
"msg",
|
|
527
|
+
"vmsg",
|
|
528
|
+
"vi18nMsg"
|
|
529
|
+
],
|
|
530
|
+
rich: [
|
|
531
|
+
"rich",
|
|
532
|
+
"vrich",
|
|
533
|
+
"vi18nRich"
|
|
534
|
+
]
|
|
535
|
+
};
|
|
536
|
+
var ModuleBuilder = class {
|
|
537
|
+
edits = [];
|
|
538
|
+
consumed = /* @__PURE__ */ new Set();
|
|
539
|
+
used = /* @__PURE__ */ new Set();
|
|
540
|
+
manual = [];
|
|
541
|
+
/** Offsets where a vi18n function is called. */
|
|
542
|
+
sites = [];
|
|
543
|
+
fns = /* @__PURE__ */ new Set();
|
|
544
|
+
/** `source` → type names to import (`react` → `ReactNode`). */
|
|
545
|
+
typeImports = /* @__PURE__ */ new Map();
|
|
546
|
+
/** Bindings of the old library's hooks (`const { t } = useTranslation()`), removed once consumed. */
|
|
547
|
+
hooks = [];
|
|
548
|
+
/** Call sites rewritten. */
|
|
549
|
+
rewritten = 0;
|
|
550
|
+
options;
|
|
551
|
+
constructor(options) {
|
|
552
|
+
this.options = options;
|
|
553
|
+
}
|
|
554
|
+
get text() {
|
|
555
|
+
return this.options.text;
|
|
556
|
+
}
|
|
557
|
+
get info() {
|
|
558
|
+
return this.options.info;
|
|
559
|
+
}
|
|
560
|
+
/** Marks reference nodes (identifiers, component names) as gone after the rewrite. */
|
|
561
|
+
consume(nodes) {
|
|
562
|
+
for (const node of nodes) this.consumed.add(node.start);
|
|
563
|
+
}
|
|
564
|
+
/** Every reference inside a range, e.g. a removed element's attributes. */
|
|
565
|
+
consumeRange(range) {
|
|
566
|
+
for (const refs of this.info.references.values()) for (const ref of refs) if (ref.start >= range.start && ref.end <= range.end) this.consumed.add(ref.start);
|
|
567
|
+
}
|
|
568
|
+
/** A rewritten call site. */
|
|
569
|
+
site(range, render, options) {
|
|
570
|
+
for (const key of options.keys) this.used.add(key);
|
|
571
|
+
for (const fn of options.uses) this.fns.add(fn);
|
|
572
|
+
this.consume(options.consume ?? []);
|
|
573
|
+
this.sites.push(range.start);
|
|
574
|
+
this.rewritten++;
|
|
575
|
+
this.edits.push({
|
|
576
|
+
start: range.start,
|
|
577
|
+
end: range.end,
|
|
578
|
+
text: render,
|
|
579
|
+
markup: options.markup ?? null,
|
|
580
|
+
keys: [...options.keys]
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/** A mechanical replacement that is not a message (an unwrapped provider). */
|
|
584
|
+
replace(range, text, consume = []) {
|
|
585
|
+
this.consume(consume);
|
|
586
|
+
this.edits.push({
|
|
587
|
+
start: range.start,
|
|
588
|
+
end: range.end,
|
|
589
|
+
text
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
note(offset, message, keys = []) {
|
|
593
|
+
this.manual.push({
|
|
594
|
+
offset,
|
|
595
|
+
message,
|
|
596
|
+
keys: [...keys]
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
typeImport(source, name) {
|
|
600
|
+
for (const declaration of this.info.imports) {
|
|
601
|
+
if (declaration.source !== source) continue;
|
|
602
|
+
for (const specifier of declaration.specifiers) if (specifier.imported === name) return specifier.local;
|
|
603
|
+
}
|
|
604
|
+
let names = this.typeImports.get(source);
|
|
605
|
+
if (names === void 0) this.typeImports.set(source, names = /* @__PURE__ */ new Set());
|
|
606
|
+
names.add(name);
|
|
607
|
+
return name;
|
|
608
|
+
}
|
|
609
|
+
finish() {
|
|
610
|
+
const { info, text } = this.options;
|
|
611
|
+
const removed = /* @__PURE__ */ new Set();
|
|
612
|
+
const byDeclarator = /* @__PURE__ */ new Map();
|
|
613
|
+
for (const binding of this.hooks) {
|
|
614
|
+
if (!isConsumed(info, binding, this.consumed, this.outsideOf(binding.name))) continue;
|
|
615
|
+
removed.add(binding.id);
|
|
616
|
+
const declarator = binding.declarator;
|
|
617
|
+
let list = byDeclarator.get(declarator);
|
|
618
|
+
if (list === void 0) byDeclarator.set(declarator, list = []);
|
|
619
|
+
list.push(binding);
|
|
620
|
+
}
|
|
621
|
+
for (const [declarator, bindings] of byDeclarator) {
|
|
622
|
+
if (!bindings.some((b) => [...(info.references.get(b.name) ?? []).map((r) => r.start), ...this.outsideOf(b.name)].some((offset) => this.consumed.has(offset) && b.scope.start <= offset && offset < b.scope.end))) {
|
|
623
|
+
for (const b of bindings) removed.delete(b.id);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
this.removeBindings(declarator, bindings, removed);
|
|
627
|
+
}
|
|
628
|
+
for (const declaration of info.imports) {
|
|
629
|
+
if (this.options.isOldSource(declaration.source)) continue;
|
|
630
|
+
for (const specifier of declaration.specifiers) {
|
|
631
|
+
const binding = info.bindings.find((b) => b.slot === specifier.node);
|
|
632
|
+
const refs = info.references.get(specifier.local) ?? [];
|
|
633
|
+
if (binding === void 0 || refs.length === 0) continue;
|
|
634
|
+
if (!isConsumed(info, binding, this.consumed, this.outsideOf(binding.name))) continue;
|
|
635
|
+
this.note(declaration.node.start, `${specifier.local} (from ${declaration.source}) is no longer used after the rewrite: remove the import once nothing else needs that module`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
let replaceable = null;
|
|
639
|
+
const removedImports = /* @__PURE__ */ new Set();
|
|
640
|
+
for (const declaration of info.imports) {
|
|
641
|
+
if (!this.options.isOldSource(declaration.source) || declaration.specifiers.length === 0) continue;
|
|
642
|
+
const kept = declaration.specifiers.filter((specifier) => {
|
|
643
|
+
const binding = info.bindings.find((b) => b.slot === specifier.node);
|
|
644
|
+
return binding === void 0 || !isConsumed(info, binding, this.consumed, this.outsideOf(binding.name));
|
|
645
|
+
});
|
|
646
|
+
if (kept.length === declaration.specifiers.length) continue;
|
|
647
|
+
for (const specifier of declaration.specifiers) {
|
|
648
|
+
if (kept.includes(specifier)) continue;
|
|
649
|
+
const binding = info.bindings.find((b) => b.slot === specifier.node);
|
|
650
|
+
if (binding !== void 0) removed.add(binding.id);
|
|
651
|
+
}
|
|
652
|
+
if (kept.length === 0) {
|
|
653
|
+
removedImports.add(declaration.node);
|
|
654
|
+
const edit = {
|
|
655
|
+
...lineRange(text, declaration.node.start, declaration.node.end),
|
|
656
|
+
text: ""
|
|
657
|
+
};
|
|
658
|
+
this.edits.push(edit);
|
|
659
|
+
replaceable ??= edit;
|
|
660
|
+
} else this.edits.push({
|
|
661
|
+
start: declaration.node.start,
|
|
662
|
+
end: declaration.node.end,
|
|
663
|
+
text: rebuildImport(text, declaration.node, kept.map((s) => s.node))
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const locals = {
|
|
667
|
+
t: "t",
|
|
668
|
+
msg: "msg",
|
|
669
|
+
rich: "rich"
|
|
670
|
+
};
|
|
671
|
+
const needed = [];
|
|
672
|
+
const taken = /* @__PURE__ */ new Set();
|
|
673
|
+
for (const fn of [
|
|
674
|
+
"msg",
|
|
675
|
+
"rich",
|
|
676
|
+
"t"
|
|
677
|
+
]) {
|
|
678
|
+
if (!this.fns.has(fn)) continue;
|
|
679
|
+
const existing = this.existingImport(fn);
|
|
680
|
+
if (existing !== null) {
|
|
681
|
+
locals[fn] = existing;
|
|
682
|
+
taken.add(existing);
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
const free = (name) => !taken.has(name) && isNameFree(info, name, this.sites, removed, this.consumed) && (this.options.outside?.get(name) ?? []).every((offset) => this.consumedOutside(offset));
|
|
686
|
+
let name = ALIASES[fn].find(free);
|
|
687
|
+
for (let n = 2; name === void 0; n++) if (free(`${fn}${n}_`)) name = `${fn}${n}_`;
|
|
688
|
+
locals[fn] = name;
|
|
689
|
+
taken.add(name);
|
|
690
|
+
needed.push([fn, name]);
|
|
691
|
+
}
|
|
692
|
+
const types = {};
|
|
693
|
+
const lines = [];
|
|
694
|
+
if (needed.length > 0) {
|
|
695
|
+
const specifiers = needed.map(([fn, local]) => fn === local ? fn : `${fn} as ${local}`);
|
|
696
|
+
lines.push(`import { ${specifiers.join(", ")} } from "${RUNTIME_MODULE}";`);
|
|
697
|
+
}
|
|
698
|
+
for (const [source, names] of this.typeImports) {
|
|
699
|
+
for (const name of names) types[name] = name;
|
|
700
|
+
lines.push(`import type { ${[...names].sort().join(", ")} } from "${source}";`);
|
|
701
|
+
}
|
|
702
|
+
for (const declaration of info.imports) for (const specifier of declaration.specifiers) if (!(specifier.imported in types)) types[specifier.imported] = specifier.local;
|
|
703
|
+
if (lines.length > 0) {
|
|
704
|
+
if (replaceable !== null && this.options.placeImport === void 0) {
|
|
705
|
+
replaceable.text = lines.join("\n") + "\n";
|
|
706
|
+
replaceable.listed = false;
|
|
707
|
+
} else {
|
|
708
|
+
const edits = this.options.placeImport?.(lines.join("\n"), removedImports) ?? [importLineEdit(this.options.file, text, lines.join("\n"), info.program)];
|
|
709
|
+
for (const edit of edits) this.edits.push({
|
|
710
|
+
...edit,
|
|
711
|
+
listed: false
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
file: this.options.file,
|
|
717
|
+
text,
|
|
718
|
+
edits: this.edits,
|
|
719
|
+
used: this.used,
|
|
720
|
+
manual: this.manual,
|
|
721
|
+
rewritten: this.rewritten,
|
|
722
|
+
locals,
|
|
723
|
+
types
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
/** Offsets of outside references (template calls) to `name`. */
|
|
727
|
+
outsideOf(name) {
|
|
728
|
+
return [...this.options.outside?.get(name) ?? []];
|
|
729
|
+
}
|
|
730
|
+
consumedOutside(offset) {
|
|
731
|
+
return this.consumed.has(offset);
|
|
732
|
+
}
|
|
733
|
+
existingImport(fn) {
|
|
734
|
+
for (const declaration of this.info.imports) {
|
|
735
|
+
if (declaration.source !== "@menapo/vi18n" || declaration.typeOnly) continue;
|
|
736
|
+
for (const specifier of declaration.specifiers) if (specifier.imported === fn && !specifier.typeOnly) return specifier.local;
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Removes destructured hook bindings: the whole declaration when nothing
|
|
742
|
+
* of it stays (the hook's own references are then consumed too), else the
|
|
743
|
+
* properties or array slots.
|
|
744
|
+
*/
|
|
745
|
+
removeBindings(declarator, bindings, removed) {
|
|
746
|
+
const { info, text } = this.options;
|
|
747
|
+
const pattern = declarator.id;
|
|
748
|
+
if (pattern.type === "Identifier" || pattern.type === "ObjectPattern" && pattern.properties.every((p) => bindings.some((b) => b.slot === p)) || pattern.type === "ArrayPattern" && pattern.elements.every((e) => e === null || bindings.some((b) => b.slot === e))) {
|
|
749
|
+
const declaration = info.parentOf(declarator)?.node;
|
|
750
|
+
if (declaration === void 0) return;
|
|
751
|
+
const declarators = declaration.declarations;
|
|
752
|
+
const init = declarator.init;
|
|
753
|
+
if (init !== null) this.consumeRange(init);
|
|
754
|
+
if (declarators.length === 1) this.edits.push({
|
|
755
|
+
...lineRange(text, declaration.start, declaration.end),
|
|
756
|
+
text: ""
|
|
757
|
+
});
|
|
758
|
+
else this.edits.push({
|
|
759
|
+
...listItemRange(declarators, declarators.indexOf(declarator)),
|
|
760
|
+
text: ""
|
|
761
|
+
});
|
|
762
|
+
for (const b of info.bindings) if (b.declarator === declarator) removed.add(b.id);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (pattern.type === "ObjectPattern") {
|
|
766
|
+
const properties = pattern.properties;
|
|
767
|
+
const kept = properties.filter((p) => !bindings.some((b) => b.slot === p));
|
|
768
|
+
const first = properties[0];
|
|
769
|
+
const last = properties[properties.length - 1];
|
|
770
|
+
this.edits.push({
|
|
771
|
+
start: first.start,
|
|
772
|
+
end: last.end,
|
|
773
|
+
text: kept.map((p) => text.slice(p.start, p.end)).join(", ")
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (pattern.type === "ArrayPattern") for (const binding of bindings) {
|
|
778
|
+
const slot = binding.slot;
|
|
779
|
+
this.edits.push({
|
|
780
|
+
start: slot.start,
|
|
781
|
+
end: slot.end,
|
|
782
|
+
text: ""
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
/** An import declaration printed with only `specifiers` (default, namespace, then named). */
|
|
788
|
+
function rebuildImport(text, declaration, specifiers) {
|
|
789
|
+
const source = text.slice(declaration.source.start, declaration.source.end);
|
|
790
|
+
const head = [];
|
|
791
|
+
const named = [];
|
|
792
|
+
for (const specifier of specifiers) {
|
|
793
|
+
const code = text.slice(specifier.start, specifier.end);
|
|
794
|
+
if (specifier.type === "ImportSpecifier") named.push(code);
|
|
795
|
+
else head.push(code);
|
|
796
|
+
}
|
|
797
|
+
if (named.length > 0) head.push(`{ ${named.join(", ")} }`);
|
|
798
|
+
const kind = declaration.importKind === "type" ? "type " : "";
|
|
799
|
+
const end = text.slice(declaration.start, declaration.end).trimEnd().endsWith(";") ? ";" : "";
|
|
800
|
+
return `import ${kind}${head.join(", ")} from ${source}${end}`;
|
|
801
|
+
}
|
|
802
|
+
//#endregion
|
|
803
|
+
export { keyName as _, attributeExpression as a, stringValue as b, cleanJsxText as c, isNode as d, jsString as f, jsxNameNodes as g, jsxName as h, analyzeModule as i, identifierName as l, jsxChildren as m, RUNTIME_MODULE as n, attributeString as o, jsxAttributes as p, renderPlan as r, childEntries as s, ModuleBuilder as t, inJsxChildren as u, memberParts as v, unwrap as x, objectProperties as y };
|