@menapo/vi18n 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +110 -2
  3. package/dist/apply-Bre-lY9J.js +228 -0
  4. package/dist/captured-Bj8bO6vd.js +1034 -0
  5. package/dist/check-BIKEuu6B.js +201 -0
  6. package/dist/check-HsmmpJZg.js +1 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.js +41 -0
  9. package/dist/codec-DuvU2VQy.js +207 -0
  10. package/dist/collect-Cx4_SKOe.js +13 -0
  11. package/dist/collisions-CfpZU8e4.js +217 -0
  12. package/dist/compile-BaDh0cHG.js +245 -0
  13. package/dist/compile-Cc97JaW2.js +2502 -0
  14. package/dist/concurrency-C9UUWuDC.js +96 -0
  15. package/dist/concurrency-DONL7t4V.js +1 -0
  16. package/dist/config.d.ts +21 -0
  17. package/dist/config.js +39 -0
  18. package/dist/dir-zw6bGMKQ.js +290 -0
  19. package/dist/duplicates-yuRljQ6h.js +185 -0
  20. package/dist/engine-C2ENBRJe.js +531 -0
  21. package/dist/engine.d.ts +43 -0
  22. package/dist/engine.js +1241 -0
  23. package/dist/exclude-Du-Zd_qI.js +26 -0
  24. package/dist/export-C9Sy6UWv.js +103 -0
  25. package/dist/extract-C3S8btx0.js +298 -0
  26. package/dist/formats-9unqj_fZ.js +781 -0
  27. package/dist/i18next-9HAoxwUI.js +1 -0
  28. package/dist/i18next-BEJgv_S8.js +1596 -0
  29. package/dist/import-nANvQutw.js +225 -0
  30. package/dist/index.d.ts +156 -0
  31. package/dist/index.js +239 -0
  32. package/dist/init-C2ScuW6a.js +792 -0
  33. package/dist/library-D5CWvTQs.js +1882 -0
  34. package/dist/lingui-zUiCl9Um.js +833 -0
  35. package/dist/lint.d.ts +105 -0
  36. package/dist/lint.js +971 -0
  37. package/dist/load-CQRz4PpM.js +2950 -0
  38. package/dist/load-gW-oJt1Q.js +747 -0
  39. package/dist/locales-DX4MOBFE.js +827 -0
  40. package/dist/lsp-RMMMc88P.js +31 -0
  41. package/dist/match-BGr8EXB3.js +89 -0
  42. package/dist/mcp-CHVwBoRP.js +28 -0
  43. package/dist/merge-driver-BTX87aVm.js +422 -0
  44. package/dist/messages-BfjIExyB.js +166 -0
  45. package/dist/migrate-IpLDMXmq.js +1484 -0
  46. package/dist/migrate-rCQuOGsf.js +1 -0
  47. package/dist/openai-CTaMDcl-.js +346 -0
  48. package/dist/output-DWE3SIUA.js +164 -0
  49. package/dist/parse.d.ts +62 -0
  50. package/dist/parse.js +156 -0
  51. package/dist/pipeline-C9Dbotkh.js +2031 -0
  52. package/dist/pipeline-DbX6rlo5.js +1 -0
  53. package/dist/project-Bw9UPIdx.js +1046 -0
  54. package/dist/project-C4GpaN-n.js +381 -0
  55. package/dist/pseudo-DRv30iN5.js +145 -0
  56. package/dist/raw-C4Y6vyGY.js +4708 -0
  57. package/dist/react-intl-BP0u-D5m.js +519 -0
  58. package/dist/restore-CiYv-jxS.js +175 -0
  59. package/dist/rewrite-EsIw_lsq.js +803 -0
  60. package/dist/rich-DCUKl5HM.js +97 -0
  61. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  62. package/dist/run-BEcnBbu-.js +694 -0
  63. package/dist/runtime-DTMmliT1.js +64 -0
  64. package/dist/scan-worker.d.ts +62 -0
  65. package/dist/scan-worker.js +1 -0
  66. package/dist/scoped-BMYpSthj.js +44 -0
  67. package/dist/server-Da1Ot9IN.js +10485 -0
  68. package/dist/server-deBOq2vq.js +11823 -0
  69. package/dist/server.d.ts +169 -0
  70. package/dist/server.js +1234 -0
  71. package/dist/sitemap-DJXzKoza.js +159 -0
  72. package/dist/sources-D19czDSA.js +2242 -0
  73. package/dist/status-zr4jY59Y.js +149 -0
  74. package/dist/stdio-CVaJfxA7.js +29 -0
  75. package/dist/stdio-FPOKQAgu.js +95 -0
  76. package/dist/sync-B9V2KFwb.js +658 -0
  77. package/dist/t-DC-c9zE5.js +454 -0
  78. package/dist/test.d.ts +116 -0
  79. package/dist/test.js +319 -0
  80. package/dist/translate-CQnJfLjO.js +299 -0
  81. package/dist/ts-BaU5yOf0.js +4863 -0
  82. package/dist/types-CgUCkFU1.d.ts +707 -0
  83. package/dist/types-D3QGM8XO.js +146 -0
  84. package/dist/types-DsdWBG0b.d.ts +558 -0
  85. package/dist/types-DtvQ7UdD.js +1948 -0
  86. package/dist/types-wSh9fCBY.js +11 -0
  87. package/dist/units-CQ5Q99RC.js +150 -0
  88. package/dist/url-CRfCOhUI.js +172 -0
  89. package/dist/url-CsBkRaJy.js +225 -0
  90. package/dist/validate-BxpupdAf.js +2615 -0
  91. package/dist/validate-D6DJepZv.js +365 -0
  92. package/dist/version-Byqi2guX.js +28 -0
  93. package/dist/vite-scan-CT_vsp4g.js +242 -0
  94. package/dist/vite.d.ts +34 -0
  95. package/dist/vite.js +7558 -0
  96. package/dist/vue-i18n-g0iGy6CZ.js +651 -0
  97. package/dist/which-BOemZ5Q8.js +116 -0
  98. package/dist/worker-84U6uYB3.js +1656 -0
  99. package/package.json +97 -3
  100. package/schemas/catalog.json +57 -0
@@ -0,0 +1,1656 @@
1
+ import { a as diagnosticError, o as isDiagnosticError } from "./types-D3QGM8XO.js";
2
+ import { t as createCodec } from "./codec-DuvU2VQy.js";
3
+ import { r as libraryIds } from "./library-D5CWvTQs.js";
4
+ import { c as unwrap, d as listImports, l as lineColumn, n as sourceSpecifiers, r as scanModule, s as keyName, t as sourceNeedles, u as locateLiteral } from "./sources-D19czDSA.js";
5
+ import { createRequire } from "node:module";
6
+ import { dirname, join, sep } from "node:path";
7
+ import { hash } from "node:crypto";
8
+ import { readFileSync, realpathSync, statSync } from "node:fs";
9
+ import { pathToFileURL } from "node:url";
10
+ import { isMainThread, parentPort, workerData } from "node:worker_threads";
11
+ import MagicString from "magic-string";
12
+ //#region src/core/extract/parser.ts
13
+ /**
14
+ * Parsing for extraction (INTERNALS §2.5.4–§2.5.5). The parser is Oxc as Vite
15
+ * ships it (`parseSync`), so there is no parser dependency of our own (§6 D16)
16
+ * and the CLI and the plugin see the same ESTree. Once raw transfer starts
17
+ * ({@link armRawParsing}, {@link enableRawParsing}), the same binding parses
18
+ * through it (`raw.ts`, §6 D104) when its Oxc version allows: the same tree,
19
+ * faster.
20
+ */
21
+ let parseSync = null;
22
+ /** The raw-transfer parser, once {@link enableRawParsing} found one usable. */
23
+ let parseRaw = null;
24
+ let rawReady = null;
25
+ function loadParser(filename) {
26
+ if (parseSync !== null) return parseSync;
27
+ let mod;
28
+ try {
29
+ mod = createRequire(import.meta.url)("vite");
30
+ } catch (error) {
31
+ throw diagnosticError({
32
+ code: "extract-parse-error",
33
+ severity: "error",
34
+ message: `Cannot load the parser from "vite" (${error.message}); install vite 8 as a dev dependency`,
35
+ ...filename === void 0 ? {} : { file: filename }
36
+ });
37
+ }
38
+ if (typeof mod.parseSync !== "function") throw diagnosticError({
39
+ code: "extract-parse-error",
40
+ severity: "error",
41
+ message: `The installed "vite" does not export parseSync; install vite 8 or later`,
42
+ ...filename === void 0 ? {} : { file: filename }
43
+ });
44
+ parseSync = mod.parseSync;
45
+ return parseSync;
46
+ }
47
+ /**
48
+ * Names of the platform packages a binding loads from. An owner's other
49
+ * optional dependencies are not where its binding lives, and some load a
50
+ * `.node` file of their own (`fsevents`, which Vite+ declares, on macOS).
51
+ */
52
+ const PLATFORM_PACKAGE = /^(?:@voidzero-dev\/vite-plus|@rolldown\/binding)-/;
53
+ /**
54
+ * {@link BindingOwner} of the `vite` that `from` resolves, or null when it
55
+ * cannot be told for certain. Only what the owner declares counts: a
56
+ * hoisted `rolldown` or `@oxc-project/types` that belongs to another package
57
+ * (Astro's or Nuxt's Rolldown in the same tree) would vouch for a layout
58
+ * the binding in use does not write. The owner must pin
59
+ * `@oxc-project/types` exactly, and the copy it resolves must be that
60
+ * version.
61
+ */
62
+ function bindingOwner(from = import.meta.url) {
63
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) return null;
64
+ try {
65
+ let dir = dirname(realpathSync(createRequire(from).resolve("vite/package.json")));
66
+ let pkg = readPackage(dir);
67
+ if (pkg.dependencies?.["@oxc-project/types"] === void 0) {
68
+ if (pkg.dependencies?.rolldown === void 0) return null;
69
+ dir = dirname(realpathSync(createRequire(join(dir, "package.json")).resolve("rolldown/package.json")));
70
+ pkg = readPackage(dir);
71
+ }
72
+ const pinned = /^=?(\d+\.\d+\.\d+)$/.exec(pkg.dependencies?.["@oxc-project/types"] ?? "")?.[1];
73
+ if (pinned === void 0) return null;
74
+ const ownerRequire = createRequire(join(dir, "package.json"));
75
+ if (ownerRequire("@oxc-project/types/package.json").version !== pinned) return null;
76
+ const dirs = [dir];
77
+ for (const name of Object.keys(pkg.optionalDependencies ?? {})) {
78
+ if (!PLATFORM_PACKAGE.test(name)) continue;
79
+ try {
80
+ dirs.push(dirname(realpathSync(ownerRequire.resolve(`${name}/package.json`))));
81
+ } catch {}
82
+ }
83
+ return {
84
+ dirs,
85
+ oxcVersion: pinned
86
+ };
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ function readPackage(dir) {
92
+ return JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
93
+ }
94
+ /**
95
+ * The native binding of `owner` among the loaded native modules, if it has
96
+ * raw transfer. `vite` exports no handle to it, but loading it put it in
97
+ * the module cache; other bindings sit there too (another Rolldown, the
98
+ * `oxc-parser` devDependency's) and are told apart by where they live, never
99
+ * by load order or file name.
100
+ */
101
+ function ownBinding(owner, cache = createRequire(import.meta.url).cache) {
102
+ for (const key in cache) {
103
+ if (!key.endsWith(".node")) continue;
104
+ let real;
105
+ try {
106
+ real = realpathSync(key);
107
+ } catch {
108
+ continue;
109
+ }
110
+ if (!owner.dirs.some((dir) => real.startsWith(dir + sep))) continue;
111
+ const exports = cache[key]?.exports;
112
+ if (typeof exports?.parseRawSync === "function" && typeof exports.getBufferOffset === "function" && typeof exports.rawTransferSupported === "function") return exports;
113
+ }
114
+ return null;
115
+ }
116
+ let parses = 0;
117
+ let rawAfter = Infinity;
118
+ /**
119
+ * Starts raw transfer once {@link RAW_AFTER} (or `after`) modules more have
120
+ * parsed with `parseSync`, without delaying any of them; `after` 0, or
121
+ * `VI18N_RAW_PARSE=1`, starts it now. Idempotent.
122
+ */
123
+ function armRawParsing(after = 500) {
124
+ if (rawReady !== null || process.env.VI18N_RAW_PARSE === "0") return;
125
+ if (process.env.VI18N_RAW_PARSE === "1") after = 0;
126
+ if (after > 0) rawAfter = Math.min(rawAfter, parses + after);
127
+ else startRaw();
128
+ }
129
+ function startRaw() {
130
+ rawReady ??= (async () => {
131
+ if (process.env.VI18N_RAW_PARSE === "0") return false;
132
+ try {
133
+ const sync = loadParser(void 0);
134
+ const owner = bindingOwner();
135
+ const binding = owner === null ? null : ownBinding(owner);
136
+ if (owner === null || binding === null) return false;
137
+ const raw = await import("./raw-C4Y6vyGY.js");
138
+ if (!raw.LAYOUT_VERSIONS.has(owner.oxcVersion)) return false;
139
+ parseRaw = raw.createRawParser(binding, sync);
140
+ return parseRaw !== null;
141
+ } catch {
142
+ return false;
143
+ }
144
+ })();
145
+ return rawReady;
146
+ }
147
+ /**
148
+ * The parser language for a module id, by the extension of its path (query
149
+ * and `\0` prefix ignored). Framework files (`.vue`, `.svelte`, `.astro`,
150
+ * `.mdx`) reach the post pass as plain JS.
151
+ */
152
+ function langFromId(id) {
153
+ const query = id.indexOf("?");
154
+ const end = query === -1 ? id.length : query;
155
+ const dot = id.lastIndexOf(".", end);
156
+ if (dot === -1 || id.lastIndexOf("/", end) > dot) return "js";
157
+ switch (id.slice(dot + 1, end).toLowerCase()) {
158
+ case "ts":
159
+ case "mts":
160
+ case "cts": return "ts";
161
+ case "tsx": return "tsx";
162
+ case "jsx": return "jsx";
163
+ default: return "js";
164
+ }
165
+ }
166
+ /**
167
+ * Parses `code` as an ES module. Throws a `DiagnosticError`
168
+ * (`extract-parse-error`) on the first syntax error: a partially recovered
169
+ * tree could make the rewriter edit the wrong ranges.
170
+ */
171
+ function parseModule(code, lang, filename) {
172
+ if (parseRaw === null && ++parses >= rawAfter) {
173
+ rawAfter = Infinity;
174
+ startRaw();
175
+ }
176
+ const result = (parseRaw ?? loadParser(filename))(filename ?? `module.${lang}`, code, {
177
+ lang: lang === "js" ? "jsx" : lang,
178
+ sourceType: "module",
179
+ preserveParens: false
180
+ });
181
+ const first = result.errors[0];
182
+ if (first !== void 0) {
183
+ const offset = first.labels?.[0]?.start;
184
+ throw diagnosticError({
185
+ code: "extract-parse-error",
186
+ severity: "error",
187
+ message: `Cannot parse${filename === void 0 ? "" : ` ${filename}`}: ${first.message}`,
188
+ ...filename === void 0 ? {} : { file: filename },
189
+ ...offset === void 0 ? {} : lineColumn(code, offset)
190
+ });
191
+ }
192
+ const parsed = {
193
+ code,
194
+ lang,
195
+ program: result.program
196
+ };
197
+ if (result.comments !== void 0) parsed.comments = result.comments;
198
+ return parsed;
199
+ }
200
+ //#endregion
201
+ //#region src/core/extract/entities.ts
202
+ /** HTML entities JSX text may spell; React and Solid decode them the same way. */
203
+ const ENTITIES = {
204
+ amp: "&",
205
+ lt: "<",
206
+ gt: ">",
207
+ quot: "\"",
208
+ apos: "'",
209
+ nbsp: "\xA0",
210
+ ndash: "–",
211
+ mdash: "—",
212
+ hellip: "…",
213
+ lsquo: "‘",
214
+ rsquo: "’",
215
+ ldquo: "“",
216
+ rdquo: "”",
217
+ copy: "©",
218
+ reg: "®",
219
+ trade: "™",
220
+ middot: "·",
221
+ times: "×"
222
+ };
223
+ function decodeEntities(text) {
224
+ return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (whole, name) => {
225
+ if (name[0] === "#") {
226
+ const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : +name.slice(1);
227
+ return code <= 1114111 ? String.fromCodePoint(code) : whole;
228
+ }
229
+ return Object.hasOwn(ENTITIES, name) ? ENTITIES[name] : whole;
230
+ });
231
+ }
232
+ //#endregion
233
+ //#region src/core/extract/macro.ts
234
+ /** The local the expansion calls, imported once per expanded module. */
235
+ const MACRO_RICH = "__vi18nRich";
236
+ const IMPORT = `import { rich as ${MACRO_RICH} } from "@menapo/vi18n";`;
237
+ /** The handlers' children parameter. */
238
+ const CHILDREN = "__vi18nChildren";
239
+ const TYPE_KEYS = /* @__PURE__ */ new Set([
240
+ "typeAnnotation",
241
+ "typeParameters",
242
+ "typeArguments",
243
+ "returnType"
244
+ ]);
245
+ /** Local names bound to `T` imported from a vi18n import source. */
246
+ function macroNames(imports, sources) {
247
+ const names = /* @__PURE__ */ new Set();
248
+ for (const record of imports) {
249
+ if (!sources.has(record.specifier)) continue;
250
+ for (const { local, imported } of record.bindings) if (imported === "T") names.add(local);
251
+ }
252
+ return names;
253
+ }
254
+ var MacroError = class extends Error {
255
+ code;
256
+ node;
257
+ constructor(code, message, node) {
258
+ super(message);
259
+ this.code = code;
260
+ this.node = node;
261
+ }
262
+ };
263
+ /** JSX's whitespace rule (Babel's `cleanJSXElementLiteralChild`, which Solid follows). */
264
+ function cleanText(raw) {
265
+ const lines = raw.split(/\r\n|\n|\r/);
266
+ let lastNonEmpty = 0;
267
+ for (let i = 0; i < lines.length; i++) if (/[^ \t]/.test(lines[i])) lastNonEmpty = i;
268
+ let out = "";
269
+ for (let i = 0; i < lines.length; i++) {
270
+ let line = lines[i].replace(/\t/g, " ");
271
+ if (i !== 0) line = line.replace(/^ +/, "");
272
+ if (i !== lines.length - 1) line = line.replace(/ +$/, "");
273
+ if (line) out += i === lastNonEmpty ? line : line + " ";
274
+ }
275
+ return out;
276
+ }
277
+ /** Literal text as ICU source: syntax characters quoted, every apostrophe doubled. */
278
+ function icuText(text) {
279
+ return text.replace(/'/g, "''").replace(/[{}<>]+/g, (run) => `'${run}'`);
280
+ }
281
+ /** Tag names the compiler accepts (INTERNALS §2.1): an ASCII letter, then name characters. */
282
+ const TAG_NAME = /^[A-Za-z][\w.-]*$/;
283
+ var Expander = class {
284
+ code;
285
+ constructor(code) {
286
+ this.code = code;
287
+ }
288
+ /** `<T id? context? maxLength?>children</T>` → the `rich()` call. */
289
+ expand(element) {
290
+ const opening = element.openingElement;
291
+ const descriptor = [];
292
+ let spread = null;
293
+ for (const attribute of opening.attributes) {
294
+ if (attribute.type !== "JSXAttribute") throw new MacroError("extract-non-literal-option", "<T> takes only literal id, context and maxLength attributes and values={…}, not a spread", attribute);
295
+ const name = keyName(attribute.name, false);
296
+ const value = attribute.value;
297
+ if (name === "values" && value?.type === "JSXExpressionContainer") {
298
+ const expression = value.expression;
299
+ spread = `...(${this.code.slice(expression.start, expression.end)})`;
300
+ } else descriptor.push(this.attribute(name, attribute));
301
+ }
302
+ const out = {
303
+ message: "",
304
+ values: [],
305
+ tags: /* @__PURE__ */ new Map(),
306
+ args: /* @__PURE__ */ new Map()
307
+ };
308
+ if (spread !== null) out.values.push(spread);
309
+ this.children(element.children, out);
310
+ if (out.message.trim() === "") throw new MacroError("extract-dynamic-message", "<T> needs text to translate", element);
311
+ const message = JSON.stringify(out.message);
312
+ const key = descriptor.length === 0 ? message : `{ message: ${message}, ${descriptor.join(", ")} }`;
313
+ const values = out.values.length === 0 ? "" : `, { ${out.values.join(", ")} }`;
314
+ return `${MACRO_RICH}(${key}${values})`;
315
+ }
316
+ attribute(name, attribute) {
317
+ const value = attribute.value;
318
+ const inner = value?.type === "JSXExpressionContainer" ? unwrap(value.expression) : value;
319
+ const invalid = (what) => new MacroError("extract-non-literal-option", `<T> takes only literal id, context and maxLength attributes and values={…}: ${what}`, attribute);
320
+ if (name === "id" || name === "context") {
321
+ if (inner?.type !== "Literal" || typeof inner.value !== "string") throw invalid(`${name} is not a string literal`);
322
+ return `${name}: ${JSON.stringify(inner.value)}`;
323
+ }
324
+ if (name === "maxLength") {
325
+ if (inner?.type !== "Literal" || typeof inner.value !== "number") throw invalid("maxLength is not a number literal");
326
+ return `maxLength: ${inner.value}`;
327
+ }
328
+ throw invalid(`unknown attribute ${name ?? "?"}`);
329
+ }
330
+ children(children, out) {
331
+ for (const child of children) switch (child.type) {
332
+ case "JSXText":
333
+ out.message += icuText(cleanText(decodeEntities(this.code.slice(child.start, child.end))));
334
+ break;
335
+ case "JSXExpressionContainer":
336
+ this.expression(child.expression, out);
337
+ break;
338
+ case "JSXFragment":
339
+ this.children(child.children, out);
340
+ break;
341
+ case "JSXElement":
342
+ this.element(child, out);
343
+ break;
344
+ default: throw new MacroError("extract-dynamic-message", "<T> children are text, elements, {name} arguments and ICU strings", child);
345
+ }
346
+ }
347
+ /**
348
+ * `{name}` and `{user.name}` are arguments named by their last identifier,
349
+ * `{{ count: items.length }}` names one explicitly, and a string literal
350
+ * is ICU source as written, the way to a plural or a select. Anything else
351
+ * would hide text from translators.
352
+ */
353
+ expression(expression, out) {
354
+ if (expression.type === "JSXEmptyExpression") return;
355
+ const node = unwrap(expression);
356
+ if (node.type === "Literal" && typeof node.value === "string") {
357
+ out.message += node.value;
358
+ return;
359
+ }
360
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
361
+ out.message += node.quasis[0].value.cooked;
362
+ return;
363
+ }
364
+ let name = null;
365
+ let value = node;
366
+ if (node.type === "Identifier") name = node.name;
367
+ else if (node.type === "MemberExpression" && node.computed !== true) name = keyName(node.property, false);
368
+ else if (node.type === "ObjectExpression") {
369
+ const properties = node.properties;
370
+ const only = properties[0];
371
+ if (properties.length === 1 && only?.type === "Property" && only.computed !== true) {
372
+ name = keyName(only.key, false);
373
+ value = only.value;
374
+ }
375
+ }
376
+ if (name === null || !/^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(name)) throw new MacroError("extract-dynamic-message", `<T> takes {name}, {obj.name} or {{ name: value }} as an argument, not {${this.code.slice(node.start, node.end)}}`, node);
377
+ const source = this.code.slice(value.start, value.end);
378
+ const previous = out.args.get(name);
379
+ if (previous !== void 0 && previous !== source) throw new MacroError("extract-dynamic-message", `<T> argument {${name}} stands for two values (${previous}, ${source}); name one with {{ other: … }}`, node);
380
+ if (previous === void 0) {
381
+ out.args.set(name, source);
382
+ out.values.push(source === name ? name : `${JSON.stringify(name)}: ${source}`);
383
+ }
384
+ out.message += `{${name}}`;
385
+ }
386
+ /** A nested element: a tag named after it, its markup moved into the handler. */
387
+ element(element, out) {
388
+ const opening = element.openingElement;
389
+ const closing = element.closingElement;
390
+ const base = this.tagName(opening.name);
391
+ const children = element.children;
392
+ const standalone = closing === null || children.length === 0;
393
+ const open = this.code.slice(opening.start, opening.end);
394
+ const handler = standalone ? `() => ${this.code.slice(element.start, element.end)}` : `(${CHILDREN}) => ${open}{${CHILDREN}}${this.code.slice(closing.start, closing.end)}`;
395
+ let name = base;
396
+ for (let n = 2; out.tags.has(name) && out.tags.get(name) !== handler; n++) name = base + n;
397
+ if (!out.tags.has(name)) {
398
+ out.tags.set(name, handler);
399
+ out.values.push(`${JSON.stringify(name)}: ${handler}`);
400
+ }
401
+ if (standalone) {
402
+ out.message += `<${name}/>`;
403
+ return;
404
+ }
405
+ out.message += `<${name}>`;
406
+ this.children(children, out);
407
+ out.message += `</${name}>`;
408
+ }
409
+ tagName(name) {
410
+ const text = name.type === "JSXIdentifier" ? name.name : name.type === "JSXMemberExpression" ? this.code.slice(name.start, name.end) : "";
411
+ if (!TAG_NAME.test(text)) throw new MacroError("extract-dynamic-message", `<T> cannot name a tag after <${this.code.slice(name.start, name.end)}>: wrap it in an element or component with a plain name`, name);
412
+ return text;
413
+ }
414
+ };
415
+ /**
416
+ * Expands every `<T>` whose name is in `options.names` (a `<T>` inside
417
+ * another is a build error). Returns the diagnostics; the sink holds the
418
+ * edits, and the module gets one `rich` import when anything expanded.
419
+ */
420
+ function expandMacros(parsed, sink, options) {
421
+ const code = parsed.code;
422
+ const program = parsed.program;
423
+ const expander = new Expander(code);
424
+ const diagnostics = [];
425
+ let edited = false;
426
+ const isMacro = (node) => {
427
+ const name = node.openingElement.name;
428
+ return name.type === "JSXIdentifier" && options.names.has(name.name);
429
+ };
430
+ const stack = [{
431
+ node: program,
432
+ child: false
433
+ }];
434
+ for (let next = stack.pop(); next !== void 0; next = stack.pop()) {
435
+ const { node, child } = next;
436
+ if (node.type === "JSXElement" && isMacro(node)) {
437
+ try {
438
+ const nested = findNested(node, isMacro);
439
+ if (nested !== null) throw new MacroError("extract-dynamic-message", "<T> cannot contain another <T>", nested);
440
+ let call = expander.expand(node);
441
+ if (options.keepLines === true) call += "\n".repeat(code.slice(node.start, node.end).split("\n").length - 1);
442
+ sink.overwrite(node.start, node.end, child ? `{${call}}` : `<>{${call}}</>`);
443
+ edited = true;
444
+ } catch (error) {
445
+ if (!(error instanceof MacroError)) throw error;
446
+ diagnostics.push({
447
+ code: error.code,
448
+ severity: "error",
449
+ message: error.message,
450
+ file: options.refFile,
451
+ ...lineColumn(code, error.node.start)
452
+ });
453
+ }
454
+ continue;
455
+ }
456
+ const jsx = node.type === "JSXElement" || node.type === "JSXFragment";
457
+ for (const key in node) {
458
+ if (key === "type" || key === "start" || key === "end" || TYPE_KEYS.has(key)) continue;
459
+ const value = node[key];
460
+ if (value === null || typeof value !== "object") continue;
461
+ const isChildren = jsx && key === "children";
462
+ if (Array.isArray(value)) for (let i = value.length - 1; i >= 0; i--) {
463
+ const item = value[i];
464
+ if (item !== null && typeof item?.type === "string") stack.push({
465
+ node: item,
466
+ child: isChildren
467
+ });
468
+ }
469
+ else if (typeof value.type === "string") stack.push({
470
+ node: value,
471
+ child: false
472
+ });
473
+ }
474
+ }
475
+ if (edited) sink.appendLeft(afterDirectives(program), IMPORT);
476
+ return {
477
+ edited,
478
+ diagnostics
479
+ };
480
+ }
481
+ /** A `<T>` inside `element`'s children, at any depth. */
482
+ function findNested(element, isMacro) {
483
+ const stack = [...element.children];
484
+ for (let node = stack.pop(); node !== void 0; node = stack.pop()) {
485
+ if (node.type === "JSXElement" && isMacro(node)) return node;
486
+ if (node.type === "JSXElement" || node.type === "JSXFragment") stack.push(...node.children);
487
+ }
488
+ return null;
489
+ }
490
+ /** Where an import may go: after `"use client"` and other directives. */
491
+ function afterDirectives(program) {
492
+ let at = 0;
493
+ for (const statement of program.body) {
494
+ if (statement.type !== "ExpressionStatement" || typeof statement.directive !== "string") break;
495
+ at = statement.end;
496
+ }
497
+ return at;
498
+ }
499
+ //#endregion
500
+ //#region src/core/extract/sfc-parse.ts
501
+ /** Thrown inside a walk for a shape parse-only extraction does not cover. */
502
+ var Unproven = class extends Error {};
503
+ function unproven(reason) {
504
+ throw new Unproven(reason);
505
+ }
506
+ function pick$1(module, name) {
507
+ return module[name] ?? module.default?.[name];
508
+ }
509
+ /**
510
+ * An expression from the template on lines of its own, so a trailing `//`
511
+ * comment cannot swallow what follows.
512
+ */
513
+ function expr(text) {
514
+ return "(\n" + text + "\n)";
515
+ }
516
+ /** `code` with `ranges` replaced by spaces, line breaks kept. */
517
+ function blank(code, ranges) {
518
+ if (ranges.length === 0) return code;
519
+ const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
520
+ let out = "";
521
+ let at = 0;
522
+ for (const [start, end] of sorted) {
523
+ if (start < at) continue;
524
+ out += code.slice(at, start) + code.slice(start, end).replace(/[^\n]/g, " ");
525
+ at = end;
526
+ }
527
+ return out + code.slice(at);
528
+ }
529
+ /** Top-level binding names of a program (imports and declarations). */
530
+ function topLevelNames(program, into) {
531
+ const pattern = (node) => {
532
+ if (node === null || node === void 0) return;
533
+ switch (node.type) {
534
+ case "Identifier":
535
+ into.add(node.name);
536
+ return;
537
+ case "ObjectPattern":
538
+ for (const property of node.properties) pattern(property.type === "RestElement" ? property.argument : property.value);
539
+ return;
540
+ case "ArrayPattern":
541
+ for (const element of node.elements) pattern(element);
542
+ return;
543
+ case "AssignmentPattern":
544
+ pattern(node.left);
545
+ return;
546
+ case "RestElement":
547
+ pattern(node.argument);
548
+ return;
549
+ }
550
+ };
551
+ for (let statement of program.body) {
552
+ if ((statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration") && statement.declaration !== null && statement.declaration !== void 0) statement = statement.declaration;
553
+ switch (statement.type) {
554
+ case "ImportDeclaration":
555
+ for (const specifier of statement.specifiers) into.add(specifier.local.name);
556
+ break;
557
+ case "VariableDeclaration":
558
+ for (const declarator of statement.declarations) pattern(declarator.id);
559
+ break;
560
+ case "FunctionDeclaration":
561
+ case "ClassDeclaration":
562
+ case "TSEnumDeclaration": if (statement.id !== null && statement.id !== void 0) into.add(statement.id.name);
563
+ }
564
+ }
565
+ }
566
+ /** `@vue/compiler-core` NodeTypes and ElementTypes of the raw template AST. */
567
+ const ELEMENT = 1;
568
+ const TEXT = 2;
569
+ const COMMENT = 3;
570
+ const INTERPOLATION = 5;
571
+ const ATTRIBUTE = 6;
572
+ const DIRECTIVE = 7;
573
+ const COMPONENT = 1;
574
+ /** The setup macros whose arguments Vue moves into the component options, at module level. */
575
+ const VUE_MACROS = /* @__PURE__ */ new Set([
576
+ "defineProps",
577
+ "withDefaults",
578
+ "defineEmits",
579
+ "defineOptions",
580
+ "defineModel"
581
+ ]);
582
+ /** Components Vue resolves before setup bindings (compiler-core `resolveComponentType`). */
583
+ const VUE_BUILT_INS = /* @__PURE__ */ new Set([
584
+ "component",
585
+ "Component",
586
+ "teleport",
587
+ "Teleport",
588
+ "suspense",
589
+ "Suspense",
590
+ "keep-alive",
591
+ "KeepAlive",
592
+ "base-transition",
593
+ "BaseTransition",
594
+ "transition",
595
+ "Transition",
596
+ "transition-group",
597
+ "TransitionGroup"
598
+ ]);
599
+ /** `v-on` values Vue passes as they are instead of wrapping them in `$event => (…)`. */
600
+ const VUE_FN_EXPRESSION = /^\s*(?:async\s*)?(?:\([^)]*?\)|[\w$]+)\s*(?::[^=]+)?=>|^\s*(?:async\s+)?function(?:\s+[\w$]+)?\s*\(/;
601
+ /** `v-for`'s `alias in source`, as compiler-core splits it. */
602
+ const VUE_FOR = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/;
603
+ const camelize = (name) => name.replace(/-(\w)/g, (_all, c) => c.toUpperCase());
604
+ const capitalize = (name) => name.charAt(0).toUpperCase() + name.slice(1);
605
+ function modifierNames(prop) {
606
+ return (prop.modifiers ?? []).map((m) => typeof m === "string" ? m : m.content);
607
+ }
608
+ var VueTemplate = class {
609
+ out = [];
610
+ /**
611
+ * `bindings` holds the top-level names of `<script setup>` mode, where a
612
+ * component tag naming one is that binding (`_createVNode(T, …)`); null
613
+ * outside it, where Vue resolves components by name at runtime.
614
+ */
615
+ bindings;
616
+ constructor(bindings) {
617
+ this.bindings = bindings;
618
+ }
619
+ children(nodes) {
620
+ for (const node of nodes ?? []) this.node(node);
621
+ }
622
+ node(node) {
623
+ switch (node.type) {
624
+ case TEXT:
625
+ case COMMENT: return;
626
+ case INTERPOLATION:
627
+ this.out.push(expr(node.content.content) + ";");
628
+ return;
629
+ case ELEMENT:
630
+ this.element(node);
631
+ return;
632
+ default: unproven(`vue-node-${node.type}`);
633
+ }
634
+ }
635
+ /** The binding a component tag resolves to in `<script setup>` mode, as compiler-core finds it. */
636
+ componentBinding(tag) {
637
+ if (this.bindings === null || VUE_BUILT_INS.has(tag)) return null;
638
+ for (const name of [
639
+ tag,
640
+ camelize(tag),
641
+ capitalize(camelize(tag))
642
+ ]) if (this.bindings.has(name)) return name;
643
+ return null;
644
+ }
645
+ element(node) {
646
+ const props = node.props ?? [];
647
+ let closers = 0;
648
+ for (const prop of props) if (prop.type === DIRECTIVE && (prop.name === "if" || prop.name === "else-if")) {
649
+ if (prop.exp !== void 0) this.out.push(expr(prop.exp.content) + ";");
650
+ }
651
+ const loop = props.find((p) => p.type === DIRECTIVE && p.name === "for");
652
+ if (loop !== void 0) {
653
+ const match = VUE_FOR.exec(loop.exp?.content ?? "");
654
+ if (match === null) unproven("vue-for");
655
+ const alias = match[1].trim().replace(/^\(|\)$/g, "");
656
+ this.out.push(expr(match[2].trim()) + ";");
657
+ this.out.push(`((${alias}) => {`);
658
+ closers++;
659
+ }
660
+ const component = node.tagType === COMPONENT ? this.componentBinding(node.tag) : null;
661
+ if (this.bindings === null && node.tagType === COMPONENT && hasProp(props, "message")) unproven("vue-options-component");
662
+ const entries = [];
663
+ const statements = [];
664
+ const merged = [];
665
+ let slot;
666
+ for (const prop of props) {
667
+ if (prop.type === ATTRIBUTE) {
668
+ if (component !== null) entries.push(`${JSON.stringify(prop.name)}: ${JSON.stringify(prop.value?.content ?? "")}`);
669
+ continue;
670
+ }
671
+ if (prop.type !== DIRECTIVE) unproven(`vue-prop-${prop.type}`);
672
+ const exp = prop.exp?.content;
673
+ const arg = prop.arg;
674
+ const dynamic = arg !== void 0 && arg.isStatic === false;
675
+ if (dynamic) statements.push(expr(arg.content) + ";");
676
+ switch (prop.name) {
677
+ case "if":
678
+ case "else-if":
679
+ case "else":
680
+ case "for": break;
681
+ case "slot":
682
+ slot = prop;
683
+ break;
684
+ case "bind": {
685
+ if (arg === void 0) {
686
+ if (exp !== void 0) merged.push(expr(exp));
687
+ break;
688
+ }
689
+ if (dynamic) {
690
+ merged.push(`{ [${expr(arg.content)}]: ${exp === void 0 ? "0" : expr(exp)} }`);
691
+ break;
692
+ }
693
+ const modifiers = modifierNames(prop);
694
+ let key = modifiers.includes("camel") ? camelize(arg.content) : arg.content;
695
+ if (modifiers.includes("prop")) key = "." + key;
696
+ else if (modifiers.includes("attr")) key = "^" + key;
697
+ entries.push(`${JSON.stringify(key)}: ${exp === void 0 ? camelize(arg.content) : expr(exp)}`);
698
+ break;
699
+ }
700
+ case "on": {
701
+ const handler = exp === void 0 ? "null" : VUE_FN_EXPRESSION.test(exp) || /^\s*[\w$]+(?:\s*\.\s*[\w$]+)*\s*$/.test(exp) ? expr(exp) : `($event) => {\n${exp}\n}`;
702
+ if (arg === void 0 || dynamic) merged.push(handler);
703
+ else entries.push(`${JSON.stringify("on:" + arg.content)}: ${handler}`);
704
+ break;
705
+ }
706
+ case "model":
707
+ if (exp !== void 0) {
708
+ const key = arg === void 0 || dynamic ? "modelValue" : arg.content;
709
+ if (dynamic) merged.push(expr(exp));
710
+ else entries.push(`${JSON.stringify(key)}: ${expr(exp)}`);
711
+ }
712
+ break;
713
+ case "html":
714
+ if (exp !== void 0) entries.push(`innerHTML: ${expr(exp)}`);
715
+ break;
716
+ case "text":
717
+ if (exp !== void 0) entries.push(`textContent: ${expr(exp)}`);
718
+ break;
719
+ default: if (exp !== void 0) statements.push(expr(exp) + ";");
720
+ }
721
+ }
722
+ let props_ = `{${entries.length === 0 ? "" : "\n" + entries.join(",\n") + "\n"}}`;
723
+ if (merged.length > 0) props_ = `__vi18n_merge(${props_}, ${merged.join(", ")})`;
724
+ this.out.push(component === null ? `__vi18n_node(${props_});` : `__vi18n_vnode(${component}, ${props_});`);
725
+ for (const statement of statements) this.out.push(statement);
726
+ if (slot?.exp !== void 0) {
727
+ this.out.push(`((${slot.exp.content}) => {`);
728
+ closers++;
729
+ }
730
+ this.children(node.children);
731
+ for (let i = 0; i < closers; i++) this.out.push("});");
732
+ }
733
+ };
734
+ function hasProp(props, name) {
735
+ return props.some((p) => p.type === ATTRIBUTE ? p.name === name : p.arg !== void 0 && (p.arg.isStatic === false || camelize(p.arg.content) === name));
736
+ }
737
+ function vueLang(lang) {
738
+ return lang === "ts" || lang === "tsx" || lang === "jsx" ? lang : "js";
739
+ }
740
+ /**
741
+ * Script setup statements that Vue's compileScript moves: imports to module
742
+ * scope, and the arguments of the options macros into the component options
743
+ * (module scope too, so a call directly in them is at module scope there).
744
+ */
745
+ function setupLayout(setup, lang, file) {
746
+ let program;
747
+ try {
748
+ program = parseModule(setup, lang, file).program;
749
+ } catch {
750
+ unproven("script-syntax");
751
+ }
752
+ const imports = [];
753
+ const hoisted = [];
754
+ const blanks = [];
755
+ const macro = (node) => {
756
+ if (node === null || node === void 0) return;
757
+ let call = node;
758
+ while (call.type === "TSAsExpression" || call.type === "TSSatisfiesExpression" || call.type === "TSNonNullExpression") call = call.expression;
759
+ if (call.type !== "CallExpression") return;
760
+ const callee = call.callee;
761
+ if (callee.type !== "Identifier" || !VUE_MACROS.has(callee.name)) return;
762
+ const args = call.arguments;
763
+ if (callee.name === "withDefaults") macro(args[0]);
764
+ const moved = callee.name === "withDefaults" ? args.slice(1) : args;
765
+ if (moved.length === 0) return;
766
+ const start = moved[0].start;
767
+ const end = moved[moved.length - 1].end;
768
+ hoisted.push(expr(setup.slice(start, end)) + ";");
769
+ blanks.push([start, end]);
770
+ };
771
+ for (const statement of program.body) switch (statement.type) {
772
+ case "ImportDeclaration":
773
+ imports.push(setup.slice(statement.start, statement.end));
774
+ blanks.push([statement.start, statement.end]);
775
+ break;
776
+ case "ExportNamedDeclaration":
777
+ case "ExportDefaultDeclaration":
778
+ case "ExportAllDeclaration":
779
+ unproven("vue-setup-export");
780
+ break;
781
+ case "ExpressionStatement":
782
+ macro(statement.expression);
783
+ break;
784
+ case "VariableDeclaration": for (const declarator of statement.declarations) {
785
+ const init = declarator.init;
786
+ const id = declarator.id;
787
+ if (init !== null && id.type === "ObjectPattern" && /\b(?:defineProps|withDefaults)\s*[<(]/.test(setup.slice(init.start, init.end)) && id.properties.some((p) => p.value?.type === "AssignmentPattern")) unproven("vue-props-destructure-default");
788
+ macro(init);
789
+ }
790
+ }
791
+ return {
792
+ imports,
793
+ hoisted,
794
+ body: blank(setup, blanks),
795
+ program
796
+ };
797
+ }
798
+ /**
799
+ * The module parse-only extraction scans for a `.vue` file: `<script>` as it
800
+ * is, `<script setup>` imports and macro arguments at module level, its
801
+ * statements inside a setup function that ends with the template's
802
+ * expressions (the inline template of a production build), or the template
803
+ * in a render function of its own.
804
+ */
805
+ function vueParseOnly(compiler, file, code) {
806
+ const parse = pick$1(compiler, "parse");
807
+ if (parse === void 0) return { fallback: "vue-api" };
808
+ try {
809
+ const { descriptor, errors } = parse(code, {
810
+ filename: file,
811
+ sourceMap: false,
812
+ templateParseOptions: {}
813
+ });
814
+ if (errors.length > 0) return { fallback: "vue-parse-error" };
815
+ const { script, scriptSetup, template } = descriptor;
816
+ if (script?.src !== void 0 || scriptSetup?.src !== void 0 || template?.src !== void 0) return { fallback: "vue-src" };
817
+ if (template !== null && template.lang !== void 0 && template.lang !== "html") return { fallback: "vue-template-lang" };
818
+ if (template !== null && template.ast === void 0) return { fallback: "vue-api" };
819
+ if (template?.content.includes("vi18n-") === true) return { fallback: "template-marker" };
820
+ if (script !== null && scriptSetup !== null && vueLang(script.lang) !== vueLang(scriptSetup.lang)) return { fallback: "vue-script-lang" };
821
+ const lang = vueLang(scriptSetup?.lang ?? script?.lang);
822
+ const parts = [];
823
+ if (script !== null) parts.push(script.content);
824
+ if (scriptSetup === null) {
825
+ const walk = new VueTemplate(null);
826
+ walk.children(template?.ast?.children);
827
+ parts.push("function __vi18n_render() {", ...walk.out, "}");
828
+ return {
829
+ code: parts.join("\n"),
830
+ lang
831
+ };
832
+ }
833
+ const layout = setupLayout(scriptSetup.content, lang, file);
834
+ const bindings = /* @__PURE__ */ new Set();
835
+ topLevelNames(layout.program, bindings);
836
+ if (script !== null) try {
837
+ topLevelNames(parseModule(script.content, lang, file).program, bindings);
838
+ } catch {
839
+ return { fallback: "script-syntax" };
840
+ }
841
+ const walk = new VueTemplate(bindings);
842
+ walk.children(template?.ast?.children);
843
+ parts.push(...layout.imports, ...layout.hoisted);
844
+ parts.push("async function __vi18n_setup() {", layout.body);
845
+ parts.push("function __vi18n_render() {", ...walk.out, "}", "}");
846
+ return {
847
+ code: parts.join("\n"),
848
+ lang
849
+ };
850
+ } catch (error) {
851
+ if (error instanceof Unproven) return { fallback: error.message };
852
+ return { fallback: "vue-parse-error" };
853
+ }
854
+ }
855
+ /** Elements whose attributes and children are plain template code. */
856
+ const SVELTE_ELEMENTS = /* @__PURE__ */ new Set([
857
+ "RegularElement",
858
+ "SvelteElement",
859
+ "SvelteComponent",
860
+ "SvelteSelf",
861
+ "SvelteHead",
862
+ "SvelteBody",
863
+ "SvelteWindow",
864
+ "SvelteDocument",
865
+ "SvelteFragment",
866
+ "SvelteBoundary",
867
+ "SlotElement",
868
+ "TitleElement"
869
+ ]);
870
+ /**
871
+ * Svelte 5's runes; any other `$name` reads a store (`$.store_get(…)` in
872
+ * compiled code, which the scanner cannot follow) or is a compile error.
873
+ */
874
+ const SVELTE_RUNES = /* @__PURE__ */ new Set([
875
+ "$state",
876
+ "$derived",
877
+ "$effect",
878
+ "$props",
879
+ "$bindable",
880
+ "$inspect",
881
+ "$host"
882
+ ]);
883
+ /** True when an identifier anywhere in `root` subscribes to a store. */
884
+ function readsStore(root) {
885
+ const stack = [root];
886
+ const seen = /* @__PURE__ */ new WeakSet();
887
+ while (stack.length > 0) {
888
+ const node = stack.pop();
889
+ if (typeof node !== "object" || node === null || seen.has(node)) continue;
890
+ seen.add(node);
891
+ if (Array.isArray(node)) {
892
+ for (const item of node) stack.push(item);
893
+ continue;
894
+ }
895
+ const record = node;
896
+ if (record.type === "Identifier") {
897
+ const name = record.name;
898
+ if (/^\$[A-Za-z_]/.test(name) && !SVELTE_RUNES.has(name)) return true;
899
+ }
900
+ for (const key in record) if (key !== "loc" && key !== "metadata") stack.push(record[key]);
901
+ }
902
+ return false;
903
+ }
904
+ /** Text for a template literal: the characters it treats specially, escaped. */
905
+ function templateText(text) {
906
+ return text.replace(/[\\`]|\$\{/g, (c) => "\\" + c);
907
+ }
908
+ var SvelteTemplate = class {
909
+ out = [];
910
+ code;
911
+ constructor(code) {
912
+ this.code = code;
913
+ }
914
+ slice(node) {
915
+ return this.code.slice(node.start, node.end);
916
+ }
917
+ expression(node) {
918
+ return expr(this.slice(node));
919
+ }
920
+ fragment(fragment) {
921
+ for (const node of fragment?.nodes ?? []) this.node(node);
922
+ }
923
+ node(node) {
924
+ switch (node.type) {
925
+ case "Text":
926
+ case "Comment":
927
+ case "DebugTag": return;
928
+ case "ExpressionTag":
929
+ case "RenderTag":
930
+ this.out.push(this.expression(node.expression) + ";");
931
+ return;
932
+ case "ConstTag": {
933
+ const declarator = node.declaration.declarations[0];
934
+ this.out.push(`const ${this.slice(declarator.id)} = __vi18n_derived(() => ${this.expression(declarator.init)});`);
935
+ return;
936
+ }
937
+ case "HtmlTag":
938
+ this.out.push(`$.html(null, () => ${this.expression(node.expression)});`);
939
+ return;
940
+ case "IfBlock":
941
+ this.out.push(this.expression(node.test) + ";");
942
+ this.fragment(node.consequent);
943
+ this.fragment(node.alternate);
944
+ return;
945
+ case "EachBlock": {
946
+ this.out.push(this.expression(node.expression) + ";");
947
+ const params = [];
948
+ const context = node.context;
949
+ params.push(context === null ? "__vi18n_item" : this.slice(context));
950
+ if (typeof node.index === "string") params.push(node.index);
951
+ this.out.push(`((${params.join(", ")}) => {`);
952
+ if (node.key !== null && node.key !== void 0) this.out.push(this.expression(node.key) + ";");
953
+ this.fragment(node.body);
954
+ this.out.push("});");
955
+ this.fragment(node.fallback);
956
+ return;
957
+ }
958
+ case "AwaitBlock":
959
+ this.out.push(this.expression(node.expression) + ";");
960
+ this.fragment(node.pending);
961
+ this.scoped(node.value, node.then);
962
+ this.scoped(node.error, node.catch);
963
+ return;
964
+ case "KeyBlock":
965
+ this.out.push(this.expression(node.expression) + ";");
966
+ this.fragment(node.fragment);
967
+ return;
968
+ case "SnippetBlock": {
969
+ const parameters = node.parameters;
970
+ const params = parameters.length === 0 ? "" : this.slice({
971
+ start: parameters[0].start,
972
+ end: parameters[parameters.length - 1].end
973
+ });
974
+ const name = node.expression.name;
975
+ this.out.push(`function ${name}(${params}) {`);
976
+ this.fragment(node.body);
977
+ this.out.push("}");
978
+ return;
979
+ }
980
+ case "Component":
981
+ this.component(node);
982
+ return;
983
+ default:
984
+ if (!SVELTE_ELEMENTS.has(node.type)) unproven(`svelte-node-${node.type}`);
985
+ if (node.type === "SvelteElement") this.out.push(this.expression(node.tag) + ";");
986
+ if (node.type === "SvelteComponent") this.out.push(this.expression(node.expression) + ";");
987
+ for (const attribute of node.attributes) this.attribute(attribute);
988
+ this.fragment(node.fragment);
989
+ }
990
+ }
991
+ scoped(pattern, body) {
992
+ if (body === null) return;
993
+ this.out.push(`((${pattern === null ? "" : this.slice(pattern)}) => {`);
994
+ this.fragment(body);
995
+ this.out.push("});");
996
+ }
997
+ /** An attribute's expressions, beside the element. */
998
+ attribute(attribute) {
999
+ switch (attribute.type) {
1000
+ case "Attribute": {
1001
+ const value = attribute.value;
1002
+ if (value === true) return;
1003
+ for (const part of Array.isArray(value) ? value : [value]) if (part.type === "ExpressionTag") this.out.push(this.expression(part.expression) + ";");
1004
+ return;
1005
+ }
1006
+ case "StyleDirective": {
1007
+ const value = attribute.value;
1008
+ if (value === true) return;
1009
+ for (const part of Array.isArray(value) ? value : [value]) if (part.type === "ExpressionTag") this.out.push(this.expression(part.expression) + ";");
1010
+ return;
1011
+ }
1012
+ case "SpreadAttribute":
1013
+ case "OnDirective":
1014
+ case "BindDirective":
1015
+ case "ClassDirective":
1016
+ case "TransitionDirective":
1017
+ case "AnimateDirective":
1018
+ case "UseDirective":
1019
+ case "AttachTag":
1020
+ if (attribute.expression !== null && attribute.expression !== void 0) this.out.push(this.expression(attribute.expression) + ";");
1021
+ return;
1022
+ default: unproven(`svelte-attribute-${attribute.type}`);
1023
+ }
1024
+ }
1025
+ /** `T(anchor, { props })`, as Svelte calls a component it imports. */
1026
+ component(node) {
1027
+ const entries = [];
1028
+ const merged = [];
1029
+ const statements = [];
1030
+ for (const attribute of node.attributes) {
1031
+ const name = attribute.name;
1032
+ switch (attribute.type) {
1033
+ case "Attribute": {
1034
+ const value = attribute.value;
1035
+ let text;
1036
+ if (value === true) text = "true";
1037
+ else {
1038
+ const parts = Array.isArray(value) ? value : [value];
1039
+ if (parts.length === 1 && parts[0]?.type === "ExpressionTag") text = this.expression(parts[0].expression);
1040
+ else if (parts.every((part) => part.type === "Text")) text = JSON.stringify(parts.map((part) => part.data).join(""));
1041
+ else text = "`" + parts.map((part) => part.type === "Text" ? templateText(part.data) : "${" + this.slice(part.expression) + "}").join("") + "`";
1042
+ }
1043
+ entries.push(`${JSON.stringify(name)}: ${text}`);
1044
+ break;
1045
+ }
1046
+ case "BindDirective":
1047
+ if (name === "this") statements.push(this.expression(attribute.expression) + ";");
1048
+ else entries.push(`${JSON.stringify(name)}: ${this.expression(attribute.expression)}`);
1049
+ break;
1050
+ case "SpreadAttribute":
1051
+ merged.push(this.expression(attribute.expression));
1052
+ break;
1053
+ case "OnDirective":
1054
+ case "AttachTag":
1055
+ if (attribute.expression !== null && attribute.expression !== void 0) statements.push(this.expression(attribute.expression) + ";");
1056
+ break;
1057
+ default: unproven(`svelte-attribute-${attribute.type}`);
1058
+ }
1059
+ }
1060
+ let props = `{${entries.length === 0 ? "" : "\n" + entries.join(",\n") + "\n"}}`;
1061
+ if (merged.length > 0) props = `__vi18n_merge(${props}, ${merged.join(", ")})`;
1062
+ const name = node.name;
1063
+ if (!/^[\p{L}_$][\p{L}\p{N}_$]*(?:\.[\p{L}_$][\p{L}\p{N}_$]*)*$/u.test(name)) unproven("svelte-component-name");
1064
+ this.out.push(`${name}(null, ${props});`);
1065
+ for (const statement of statements) this.out.push(statement);
1066
+ this.fragment(node.fragment);
1067
+ }
1068
+ };
1069
+ function isTs(code, script) {
1070
+ const open = code.slice(script.start, script.content.start);
1071
+ return /\blang\s*=\s*["']?(?:ts|typescript)\b/.test(open);
1072
+ }
1073
+ /**
1074
+ * A script block's code with what Svelte's compiler changes for the scan
1075
+ * undone: imports out (they are module statements), `export` keywords off
1076
+ * (component exports and legacy props stay declarations), and a `const`
1077
+ * holding `$state(text)` read as the text, which is how Svelte compiles a
1078
+ * state that can never be reassigned.
1079
+ */
1080
+ function svelteScript(code, script, hoistImports) {
1081
+ const program = script.content;
1082
+ const base = program.start;
1083
+ const text = code.slice(program.start, program.end);
1084
+ const imports = [];
1085
+ const blanks = [];
1086
+ for (const statement of program.body) {
1087
+ if (statement.type === "ImportDeclaration" && hoistImports) {
1088
+ imports.push(code.slice(statement.start, statement.end));
1089
+ blanks.push([statement.start - base, statement.end - base]);
1090
+ } else if (statement.type === "ExportNamedDeclaration" && hoistImports) {
1091
+ const declaration = statement.declaration;
1092
+ if (declaration === null) blanks.push([statement.start - base, statement.end - base]);
1093
+ else blanks.push([statement.start - base, declaration.start - base]);
1094
+ } else if (statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") {
1095
+ if (hoistImports) unproven("svelte-export");
1096
+ }
1097
+ const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration ?? statement : statement;
1098
+ if (declaration.type !== "VariableDeclaration" || declaration.kind !== "const") continue;
1099
+ for (const declarator of declaration.declarations) {
1100
+ const init = declarator.init;
1101
+ if (init?.type !== "CallExpression" || init.arguments.length !== 1) continue;
1102
+ const callee = code.slice(init.callee.start, init.callee.end);
1103
+ if (callee === "$state" || callee === "$state.raw") blanks.push([init.callee.start - base, init.callee.end - base]);
1104
+ }
1105
+ }
1106
+ return {
1107
+ imports,
1108
+ body: blank(text, blanks)
1109
+ };
1110
+ }
1111
+ /**
1112
+ * The module parse-only extraction scans for a `.svelte` file (Svelte 5):
1113
+ * `<script module>` at module level, instance imports at module level and
1114
+ * the rest of the instance script inside a component function that ends
1115
+ * with the template's expressions.
1116
+ */
1117
+ function svelteParseOnly(compiler, file, code) {
1118
+ const parse = pick$1(compiler, "parse");
1119
+ const version = pick$1(compiler, "VERSION") ?? "5";
1120
+ if (parse === void 0 || Number.parseInt(version, 10) < 5) return { fallback: "svelte-api" };
1121
+ try {
1122
+ let root;
1123
+ try {
1124
+ root = parse(code, {
1125
+ modern: true,
1126
+ filename: file
1127
+ });
1128
+ } catch {
1129
+ return { fallback: "svelte-parse-error" };
1130
+ }
1131
+ const module = root.module ?? null;
1132
+ const scripts = [module, root.instance ?? null].filter((s) => s !== null);
1133
+ let template = code;
1134
+ for (const script of scripts) template = template.slice(0, script.start) + " ".repeat(script.end - script.start) + template.slice(script.end);
1135
+ if (template.includes("vi18n-")) return { fallback: "template-marker" };
1136
+ if (readsStore([root.fragment, ...scripts])) return { fallback: "svelte-store" };
1137
+ const lang = scripts.some((script) => isTs(code, script)) ? "ts" : "js";
1138
+ const parts = [];
1139
+ if (module !== null) parts.push(svelteScript(code, module, false).body);
1140
+ const instance = root.instance ? svelteScript(code, root.instance, true) : null;
1141
+ const walk = new SvelteTemplate(code);
1142
+ walk.fragment(root.fragment);
1143
+ if (instance !== null) parts.push(...instance.imports);
1144
+ parts.push("function __vi18n_component() {");
1145
+ if (instance !== null) parts.push(instance.body);
1146
+ parts.push(...walk.out, "}");
1147
+ return {
1148
+ code: parts.join("\n"),
1149
+ lang
1150
+ };
1151
+ } catch (error) {
1152
+ if (error instanceof Unproven) return { fallback: error.message };
1153
+ return { fallback: "svelte-parse-error" };
1154
+ }
1155
+ }
1156
+ //#endregion
1157
+ //#region src/core/extract/sfc.ts
1158
+ /**
1159
+ * Framework files for the CLI scan (INTERNALS §2.5.6): `.vue`, `.svelte`,
1160
+ * `.astro` and `.mdx` are compiled with the project's own compilers, so the
1161
+ * scanner sees the call shapes the plugin sees after the framework plugins
1162
+ * (`_unref(t)(…)`, `$.html(…, () => t(…))`, `$$render`…). Compilers are
1163
+ * resolved from the file being compiled and imported on first use: a project
1164
+ * without Svelte never loads it, and vi18n depends on none of them.
1165
+ */
1166
+ /**
1167
+ * Where each compiler may be found, in order: the package the project
1168
+ * installs directly, else the framework package that depends on it (pnpm
1169
+ * only exposes direct dependencies, and most Vue apps depend on `vue`, which
1170
+ * re-exports its compiler as `vue/compiler-sfc`).
1171
+ */
1172
+ const COMPILERS = {
1173
+ vue: {
1174
+ name: "Vue",
1175
+ label: "@vue/compiler-sfc (or vue)",
1176
+ candidates: ["@vue/compiler-sfc", "vue/compiler-sfc"]
1177
+ },
1178
+ svelte: {
1179
+ name: "Svelte",
1180
+ label: "svelte",
1181
+ candidates: ["svelte/compiler"]
1182
+ },
1183
+ astro: {
1184
+ name: "Astro",
1185
+ label: "@astrojs/compiler (or astro)",
1186
+ candidates: [
1187
+ "@astrojs/compiler",
1188
+ ["astro", "@astrojs/compiler"],
1189
+ "@astrojs/compiler-rs",
1190
+ ["astro", "@astrojs/compiler-rs"]
1191
+ ]
1192
+ },
1193
+ mdx: {
1194
+ name: "MDX",
1195
+ label: "@mdx-js/mdx",
1196
+ candidates: [
1197
+ "@mdx-js/mdx",
1198
+ ["@mdx-js/rollup", "@mdx-js/mdx"],
1199
+ ["@astrojs/mdx", "@mdx-js/mdx"]
1200
+ ]
1201
+ }
1202
+ };
1203
+ /** The SFC kind of a file by extension, or null for files the scanner parses directly. */
1204
+ function sfcKind(file) {
1205
+ const dot = file.lastIndexOf(".");
1206
+ switch (dot === -1 ? "" : file.slice(dot + 1)) {
1207
+ case "vue": return "vue";
1208
+ case "svelte": return "svelte";
1209
+ case "astro": return "astro";
1210
+ case "mdx": return "mdx";
1211
+ default: return null;
1212
+ }
1213
+ }
1214
+ /**
1215
+ * Resolved compiler path per directory and kind; loaded modules per path.
1216
+ * Only hits are kept: a long-lived process (`--watch`, the dev server's
1217
+ * background scan) must find a compiler installed after it asked.
1218
+ */
1219
+ const resolved = /* @__PURE__ */ new Map();
1220
+ const loaded = /* @__PURE__ */ new Map();
1221
+ function resolveFrom(from, candidate) {
1222
+ try {
1223
+ if (typeof candidate === "string") return createRequire(from).resolve(candidate);
1224
+ const host = createRequire(from).resolve(candidate[0] + "/package.json");
1225
+ return createRequire(host).resolve(candidate[1]);
1226
+ } catch {
1227
+ return null;
1228
+ }
1229
+ }
1230
+ async function loadCompiler(kind, file) {
1231
+ const key = kind + "\0" + dirname(file);
1232
+ let path = resolved.get(key) ?? null;
1233
+ if (path === null) {
1234
+ for (const candidate of COMPILERS[kind].candidates) {
1235
+ path = resolveFrom(file, candidate);
1236
+ if (path !== null) break;
1237
+ }
1238
+ if (path === null) return null;
1239
+ resolved.set(key, path);
1240
+ }
1241
+ let module = loaded.get(path);
1242
+ if (module === void 0) {
1243
+ module = import(pathToFileURL(path).href);
1244
+ loaded.set(path, module);
1245
+ }
1246
+ return module;
1247
+ }
1248
+ /** A named export of a compiler loaded through `import()`, ESM or CommonJS. */
1249
+ function pick(module, name) {
1250
+ return module[name] ?? module.default?.[name];
1251
+ }
1252
+ function compileError(file, detail) {
1253
+ const message = typeof detail === "string" ? detail : detail instanceof Error || typeof detail === "object" && detail !== null && "message" in detail ? String(detail.message) : String(detail);
1254
+ return diagnosticError({
1255
+ code: "extract-parse-error",
1256
+ severity: "error",
1257
+ message: `Cannot compile ${file}: ${message.split("\n")[0]}`,
1258
+ file
1259
+ });
1260
+ }
1261
+ function missingCompiler(kind, file) {
1262
+ return diagnosticError({
1263
+ code: "extract-missing-compiler",
1264
+ severity: "error",
1265
+ message: `No ${COMPILERS[kind].name} compiler found to extract .${kind} files; install ${COMPILERS[kind].label} in the project`,
1266
+ file
1267
+ });
1268
+ }
1269
+ function scriptLang(lang) {
1270
+ return lang === "ts" || lang === "tsx" || lang === "jsx" ? lang : "js";
1271
+ }
1272
+ /**
1273
+ * File access for type-based `defineProps<…>()` that imports its types:
1274
+ * compiler-sfc resolves them through this or a registered TypeScript.
1275
+ */
1276
+ const VUE_FS = {
1277
+ fileExists: (file) => statSync(file, { throwIfNoEntry: false })?.isFile() === true,
1278
+ readFile: (file) => {
1279
+ try {
1280
+ return readFileSync(file, "utf8");
1281
+ } catch {
1282
+ return;
1283
+ }
1284
+ }
1285
+ };
1286
+ const tsRegistered = /* @__PURE__ */ new WeakSet();
1287
+ /**
1288
+ * Lets compiler-sfc resolve types imported from packages, as plugin-vue does,
1289
+ * when the project has a TypeScript with a JS API (TypeScript 7 has none, and
1290
+ * then only relative type imports resolve).
1291
+ */
1292
+ function registerTypeScript(compiler, file) {
1293
+ if (tsRegistered.has(compiler)) return;
1294
+ tsRegistered.add(compiler);
1295
+ const register = pick(compiler, "registerTS");
1296
+ if (register === void 0) return;
1297
+ try {
1298
+ const ts = createRequire(file)("typescript");
1299
+ if (ts.sys !== void 0) register(() => ts);
1300
+ } catch {}
1301
+ }
1302
+ function compileVue(compiler, file, code) {
1303
+ const parse = pick(compiler, "parse");
1304
+ const compileScript = pick(compiler, "compileScript");
1305
+ const compileTemplate = pick(compiler, "compileTemplate");
1306
+ if (parse === void 0 || compileScript === void 0 || compileTemplate === void 0) throw compileError(file, "the installed Vue compiler does not export parse/compileScript/compileTemplate");
1307
+ const { descriptor, errors } = parse(code, {
1308
+ filename: file,
1309
+ sourceMap: false
1310
+ });
1311
+ if (errors.length > 0) throw compileError(file, errors[0]);
1312
+ registerTypeScript(compiler, file);
1313
+ const id = hash("sha256", file, "hex").slice(0, 8);
1314
+ if (descriptor.scriptSetup !== null) {
1315
+ const script = compileScript(descriptor, {
1316
+ id,
1317
+ inlineTemplate: true,
1318
+ sourceMap: false,
1319
+ fs: VUE_FS
1320
+ });
1321
+ return [{
1322
+ code: script.content,
1323
+ lang: scriptLang(script.lang)
1324
+ }];
1325
+ }
1326
+ let out = "";
1327
+ let lang = "js";
1328
+ let bindings;
1329
+ if (descriptor.script !== null) {
1330
+ const script = compileScript(descriptor, {
1331
+ id,
1332
+ sourceMap: false,
1333
+ fs: VUE_FS
1334
+ });
1335
+ out = script.content;
1336
+ lang = scriptLang(script.lang);
1337
+ bindings = script.bindings;
1338
+ }
1339
+ const template = descriptor.template;
1340
+ if (template !== null && template.src === void 0) {
1341
+ const compiled = compileTemplate({
1342
+ source: template.content,
1343
+ filename: file,
1344
+ id,
1345
+ ...template.lang === void 0 ? {} : { preprocessLang: template.lang },
1346
+ compilerOptions: bindings === void 0 ? {} : { bindingMetadata: bindings }
1347
+ });
1348
+ if (compiled.errors.length > 0) throw compileError(file, compiled.errors[0]);
1349
+ out += (out === "" ? "" : "\n") + compiled.code;
1350
+ }
1351
+ return [{
1352
+ code: out,
1353
+ lang
1354
+ }];
1355
+ }
1356
+ /**
1357
+ * Style blocks in another language (`lang="scss"`) need preprocessors the
1358
+ * scan does not run, and Svelte's compiler would reject them. Their content
1359
+ * cannot hold calls, so it is blanked; line breaks stay for readable errors.
1360
+ */
1361
+ function blankForeignStyles(code) {
1362
+ return code.replace(/(<style\b[^>]*\b(?:lang|type)\s*=\s*(?!["']?(?:text\/)?css["'\s>])[^>]*>)([\s\S]*?)(<\/style\s*>)/gi, (_all, open, body, close) => open + body.replace(/[^\n]/g, " ") + close);
1363
+ }
1364
+ function compileSvelte(compiler, file, code) {
1365
+ const compile = pick(compiler, "compile");
1366
+ if (compile === void 0) throw compileError(file, "the installed svelte/compiler does not export compile");
1367
+ const version = pick(compiler, "VERSION") ?? "5";
1368
+ const generate = Number.parseInt(version, 10) < 5 ? "dom" : "client";
1369
+ try {
1370
+ return [{
1371
+ code: compile(blankForeignStyles(code), {
1372
+ filename: file,
1373
+ generate
1374
+ }).js.code,
1375
+ lang: "js"
1376
+ }];
1377
+ } catch (error) {
1378
+ throw compileError(file, error);
1379
+ }
1380
+ }
1381
+ async function compileAstro(compiler, file, code) {
1382
+ const transform = pick(compiler, "transform");
1383
+ if (transform === void 0) throw compileError(file, "the installed Astro compiler does not export transform");
1384
+ let result;
1385
+ try {
1386
+ result = await transform(code, {
1387
+ filename: file,
1388
+ normalizedFilename: file,
1389
+ internalURL: "astro/runtime/server/index.js"
1390
+ });
1391
+ } catch (error) {
1392
+ throw compileError(file, error);
1393
+ }
1394
+ const failure = result.diagnostics?.find((d) => d.severity === 1);
1395
+ if (failure !== void 0) throw compileError(file, failure.text ?? failure.message ?? "syntax error");
1396
+ const modules = [{
1397
+ code: result.code,
1398
+ lang: "ts"
1399
+ }];
1400
+ for (const script of result.scripts ?? []) if (script.type === "inline" && typeof script.code === "string") modules.push({
1401
+ code: script.code,
1402
+ lang: "ts"
1403
+ });
1404
+ return modules;
1405
+ }
1406
+ async function compileMdx(compiler, file, code) {
1407
+ const compile = pick(compiler, "compile");
1408
+ if (compile === void 0) throw compileError(file, "the installed @mdx-js/mdx does not export compile");
1409
+ try {
1410
+ const output = await compile({
1411
+ value: code,
1412
+ path: file
1413
+ }, { development: false });
1414
+ return [{
1415
+ code: String(output),
1416
+ lang: "js"
1417
+ }];
1418
+ } catch (error) {
1419
+ throw compileError(file, error);
1420
+ }
1421
+ }
1422
+ /**
1423
+ * Every module `file` compiles to, component first. Throws a
1424
+ * `DiagnosticError`: `extract-missing-compiler` when the project has no
1425
+ * compiler for the file, `extract-parse-error` when compilation fails.
1426
+ */
1427
+ async function compileModules(file, code) {
1428
+ const kind = sfcKind(file);
1429
+ if (kind === null) throw compileError(file, "not a .vue, .svelte, .astro or .mdx file");
1430
+ const compiler = await loadCompiler(kind, file).catch((error) => {
1431
+ throw compileError(file, error);
1432
+ });
1433
+ if (compiler === null) throw missingCompiler(kind, file);
1434
+ switch (kind) {
1435
+ case "vue": try {
1436
+ return compileVue(compiler, file, code);
1437
+ } catch (error) {
1438
+ throw isDiagnosticError(error) ? error : compileError(file, error);
1439
+ }
1440
+ case "svelte": return compileSvelte(compiler, file, code);
1441
+ case "astro": return compileAstro(compiler, file, code);
1442
+ case "mdx": return compileMdx(compiler, file, code);
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Files extracted by parse-only extraction and files compiled, by fallback
1447
+ * reason, since the process started (`compiled` counts every reason). For
1448
+ * tests and benchmarks; nothing reads them to decide anything.
1449
+ */
1450
+ const sfcStats = {
1451
+ parsed: 0,
1452
+ compiled: 0,
1453
+ fallbacks: /* @__PURE__ */ new Map()
1454
+ };
1455
+ /** Counts a compiled `.vue`/`.svelte` file under the reason parse-only extraction gave up. */
1456
+ function countFallback(reason) {
1457
+ sfcStats.compiled++;
1458
+ sfcStats.fallbacks.set(reason, (sfcStats.fallbacks.get(reason) ?? 0) + 1);
1459
+ }
1460
+ /**
1461
+ * The module parse-only extraction scans for a `.vue` or `.svelte` file
1462
+ * (§2.5.6, §6 D232), or the reason it cannot prove the result equal to
1463
+ * compiling the file: `.astro` and `.mdx` files, a missing compiler or a
1464
+ * file the parser rejects included, which {@link compileModules} then
1465
+ * reports.
1466
+ */
1467
+ async function parseOnlyModule(file, code) {
1468
+ const kind = sfcKind(file);
1469
+ if (kind !== "vue" && kind !== "svelte") return { fallback: kind ?? "not-sfc" };
1470
+ let compiler;
1471
+ try {
1472
+ compiler = await loadCompiler(kind, file);
1473
+ } catch {
1474
+ return { fallback: "compiler" };
1475
+ }
1476
+ if (compiler === null) return { fallback: "compiler" };
1477
+ return kind === "vue" ? vueParseOnly(compiler, file, code) : svelteParseOnly(compiler, file, blankForeignStyles(code));
1478
+ }
1479
+ //#endregion
1480
+ //#region src/core/extract/worker.ts
1481
+ /**
1482
+ * One file's scan, and the `worker_threads` entry of the scan pool
1483
+ * (INTERNALS §2.5.6). `scanProject` runs `scanSource` in-process for small
1484
+ * scans and in this module's workers when many files miss the cache; both
1485
+ * paths run the same function, so the pool cannot change a result.
1486
+ *
1487
+ * Kept free of the cache, the config loader and `VERSION` (a JSON import),
1488
+ * so a worker loads only what scanning needs.
1489
+ */
1490
+ /** The scan's part of `config`, for a worker. */
1491
+ function scanConfig(config) {
1492
+ const { root, sourceLocale, project, extraction, strict } = config;
1493
+ return {
1494
+ root,
1495
+ sourceLocale,
1496
+ project,
1497
+ extraction,
1498
+ strict
1499
+ };
1500
+ }
1501
+ /** Diagnostics that make `ProjectScan.aborted` true. */
1502
+ const ABORT_CODES = /* @__PURE__ */ new Set([
1503
+ "extract-parse-error",
1504
+ "extract-missing-compiler",
1505
+ "extract-empty-root"
1506
+ ]);
1507
+ /**
1508
+ * Assigns original-file positions to occurrences found in compiled output
1509
+ * (§2.5.1): each literal is searched after the previous occurrence of the
1510
+ * same literal, so repeated strings get successive positions. When the
1511
+ * compiler emitted a literal more often than the file spells it, the extra
1512
+ * occurrence keeps the last position found.
1513
+ */
1514
+ function locate(text, occurrences) {
1515
+ const last = /* @__PURE__ */ new Map();
1516
+ for (const occurrence of occurrences) {
1517
+ const previous = last.get(occurrence.source);
1518
+ const found = locateLiteral(text, occurrence.source, previous === void 0 ? 0 : previous.offset + 1);
1519
+ const at = found ?? previous;
1520
+ if (at === void 0) continue;
1521
+ occurrence.line = at.line;
1522
+ occurrence.column = at.column;
1523
+ if (found !== null) last.set(occurrence.source, found);
1524
+ }
1525
+ }
1526
+ /**
1527
+ * The modules of a `.vue`/`.svelte`/`.astro`/`.mdx` file: the parse-only
1528
+ * module when it can stand for compilation (§6 D232), otherwise what the
1529
+ * project's compiler produces. A parse-only module the parser rejects is
1530
+ * compiled too, so a syntax error is reported as compiling reports it.
1531
+ */
1532
+ async function sfcModules(task, compile) {
1533
+ if (!compile) {
1534
+ const quick = await parseOnlyModule(task.file, task.text);
1535
+ if ("code" in quick) try {
1536
+ const parsed = parseModule(quick.code, quick.lang, task.refFile);
1537
+ sfcStats.parsed++;
1538
+ return [{
1539
+ code: quick.code,
1540
+ lang: quick.lang,
1541
+ parsed
1542
+ }];
1543
+ } catch (error) {
1544
+ if (!isDiagnosticError(error)) throw error;
1545
+ countFallback("syntax");
1546
+ }
1547
+ else countFallback(quick.fallback);
1548
+ }
1549
+ return compileModules(task.file, task.text);
1550
+ }
1551
+ function mentionsVi18n(text, needles) {
1552
+ for (const needle of needles) if (text.includes(needle)) return true;
1553
+ return false;
1554
+ }
1555
+ /**
1556
+ * Scans one file: framework files are read parse-only or compiled first
1557
+ * (each resulting module scanned on its own, positions recovered by
1558
+ * search), scripts are parsed as they are. A file whose text names no
1559
+ * import source, wrapper or global cannot bind a vi18n call and is not
1560
+ * parsed at all.
1561
+ */
1562
+ async function scanSource(task, config, codec, options = {}) {
1563
+ const result = {
1564
+ occurrences: [],
1565
+ diagnostics: [],
1566
+ aborted: false
1567
+ };
1568
+ if (!mentionsVi18n(task.text, sourceNeedles(config))) return result;
1569
+ const sfc = sfcKind(task.file) !== null;
1570
+ try {
1571
+ const modules = sfc ? await sfcModules(task, options.compile === true) : [{
1572
+ code: task.text,
1573
+ lang: langFromId(task.file)
1574
+ }];
1575
+ for (const module of modules) {
1576
+ let parsed = module.parsed ?? parseModule(module.code, module.lang, task.refFile);
1577
+ let specifiers = sourceSpecifiers(listImports(parsed), task.file, config);
1578
+ const names = module.lang.endsWith("x") ? macroNames(listImports(parsed), specifiers) : null;
1579
+ if (names !== null && names.size > 0) {
1580
+ const sink = new MagicString(module.code);
1581
+ const expanded = expandMacros(parsed, sink, {
1582
+ names,
1583
+ refFile: task.refFile,
1584
+ keepLines: true
1585
+ });
1586
+ for (const diagnostic of expanded.diagnostics) result.diagnostics.push(diagnostic);
1587
+ if (expanded.edited) {
1588
+ parsed = parseModule(sink.toString(), module.lang, task.refFile);
1589
+ specifiers = sourceSpecifiers(listImports(parsed), task.file, config);
1590
+ }
1591
+ }
1592
+ const scan = scanModule(parsed, {
1593
+ codec,
1594
+ refFile: task.refFile,
1595
+ sourceSpecifiers: specifiers,
1596
+ aliases: config.extraction.aliases,
1597
+ globals: config.extraction.globals,
1598
+ moduleScope: config.strict.moduleScope,
1599
+ warnings: true,
1600
+ exactPositions: !sfc,
1601
+ sourceLocale: config.sourceLocale,
1602
+ ...config.extraction.dependencies.length === 0 ? {} : { libraryIds: libraryIds(config) }
1603
+ });
1604
+ for (const occurrence of scan.occurrences) result.occurrences.push(occurrence);
1605
+ for (const diagnostic of scan.diagnostics) result.diagnostics.push(diagnostic);
1606
+ }
1607
+ } catch (error) {
1608
+ if (!isDiagnosticError(error)) throw error;
1609
+ const diagnostic = {
1610
+ ...error.diagnostic,
1611
+ file: task.refFile
1612
+ };
1613
+ if (sfc) {
1614
+ delete diagnostic.line;
1615
+ delete diagnostic.column;
1616
+ diagnostic.message = diagnostic.message.replaceAll(task.file, task.refFile);
1617
+ }
1618
+ result.occurrences.length = 0;
1619
+ result.diagnostics.push(diagnostic);
1620
+ }
1621
+ if (sfc) locate(task.text, result.occurrences);
1622
+ for (const diagnostic of result.diagnostics) if (ABORT_CODES.has(diagnostic.code)) result.aborted = true;
1623
+ return result;
1624
+ }
1625
+ /** Marks this module's own workers, so importing it in any other thread starts nothing. */
1626
+ const WORKER_MARKER = "vi18n:scan-worker";
1627
+ const init = workerData;
1628
+ if (!isMainThread && parentPort !== null && init?.marker === "vi18n:scan-worker") {
1629
+ const port = parentPort;
1630
+ const config = init.config;
1631
+ const codec = createCodec(init.idLength);
1632
+ armRawParsing();
1633
+ port.on("message", (request) => {
1634
+ (async () => {
1635
+ try {
1636
+ const results = [];
1637
+ for (const task of request.tasks) results.push(await scanSource(task, config, codec));
1638
+ port.postMessage({
1639
+ batch: request.batch,
1640
+ results
1641
+ });
1642
+ } catch (error) {
1643
+ const { message, stack } = error instanceof Error ? error : new Error(String(error));
1644
+ port.postMessage({
1645
+ batch: request.batch,
1646
+ error: stack === void 0 ? { message } : {
1647
+ message,
1648
+ stack
1649
+ }
1650
+ });
1651
+ }
1652
+ })();
1653
+ });
1654
+ }
1655
+ //#endregion
1656
+ export { expandMacros as a, armRawParsing as c, scanSource as i, langFromId as l, WORKER_MARKER as n, macroNames as o, scanConfig as r, decodeEntities as s, ABORT_CODES as t, parseModule as u };