@astroscope/i18n 0.5.0 → 0.6.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.
Files changed (80) hide show
  1. package/dist/babel-plugin-C8O9spUS.js +120 -0
  2. package/dist/babel-plugin-C8O9spUS.js.map +1 -0
  3. package/dist/client/directives/idle.d.ts +4 -9
  4. package/dist/client/directives/idle.d.ts.map +1 -0
  5. package/dist/client/directives/idle.js +13 -21
  6. package/dist/client/directives/idle.js.map +1 -0
  7. package/dist/client/directives/load.d.ts +4 -9
  8. package/dist/client/directives/load.d.ts.map +1 -0
  9. package/dist/client/directives/load.js +9 -14
  10. package/dist/client/directives/load.js.map +1 -0
  11. package/dist/client/directives/media.d.ts +4 -9
  12. package/dist/client/directives/media.d.ts.map +1 -0
  13. package/dist/client/directives/media.js +16 -24
  14. package/dist/client/directives/media.js.map +1 -0
  15. package/dist/client/directives/only.d.ts +4 -11
  16. package/dist/client/directives/only.d.ts.map +1 -0
  17. package/dist/client/directives/only.js +9 -14
  18. package/dist/client/directives/only.js.map +1 -0
  19. package/dist/client/directives/utils.d.ts +3 -4
  20. package/dist/client/directives/utils.d.ts.map +1 -0
  21. package/dist/client/directives/utils.js +32 -7
  22. package/dist/client/directives/utils.js.map +1 -0
  23. package/dist/client/directives/visible.d.ts +4 -9
  24. package/dist/client/directives/visible.d.ts.map +1 -0
  25. package/dist/client/directives/visible.js +22 -29
  26. package/dist/client/directives/visible.js.map +1 -0
  27. package/dist/client/translate.d.ts +5 -4
  28. package/dist/client/translate.d.ts.map +1 -0
  29. package/dist/client/translate.js +68 -41
  30. package/dist/client/translate.js.map +1 -0
  31. package/dist/compiler-DjYjJc0V.js +45 -0
  32. package/dist/compiler-DjYjJc0V.js.map +1 -0
  33. package/dist/context-CNd4O0Pf.js +247 -0
  34. package/dist/context-CNd4O0Pf.js.map +1 -0
  35. package/dist/extraction/index.d.ts +12 -17
  36. package/dist/extraction/index.d.ts.map +1 -0
  37. package/dist/extraction/index.js +3 -11
  38. package/dist/i18n-DzmH9JmE.d.ts +79 -0
  39. package/dist/i18n-DzmH9JmE.d.ts.map +1 -0
  40. package/dist/index.d.ts +37 -33
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +163 -148
  43. package/dist/index.js.map +1 -0
  44. package/dist/internal.d.ts +6 -6
  45. package/dist/internal.d.ts.map +1 -0
  46. package/dist/internal.js +2 -10
  47. package/dist/manifest-D883_BIS.js +43 -0
  48. package/dist/manifest-D883_BIS.js.map +1 -0
  49. package/dist/rich-CHisXE-A.js +87 -0
  50. package/dist/rich-CHisXE-A.js.map +1 -0
  51. package/dist/{rich-CQUhdwBc.d.ts → rich-CqNMP3tk.d.ts} +4 -2
  52. package/dist/rich-CqNMP3tk.d.ts.map +1 -0
  53. package/dist/server/translate.d.ts +5 -3
  54. package/dist/server/translate.d.ts.map +1 -0
  55. package/dist/server/translate.js +75 -64
  56. package/dist/server/translate.js.map +1 -0
  57. package/dist/state-DeudhdeT.js +20 -0
  58. package/dist/state-DeudhdeT.js.map +1 -0
  59. package/dist/{types-ChMT6Vbd.d.ts → types-BfyoJsoB.d.ts} +11 -10
  60. package/dist/types-BfyoJsoB.d.ts.map +1 -0
  61. package/dist/types-Bo4jMK8g.d.ts +20 -0
  62. package/dist/types-Bo4jMK8g.d.ts.map +1 -0
  63. package/dist/{types-DNRFj1XA.d.ts → types-C98CcjtN.d.ts} +13 -11
  64. package/dist/types-C98CcjtN.d.ts.map +1 -0
  65. package/dist/url-B8334XSD.js +36 -0
  66. package/dist/url-B8334XSD.js.map +1 -0
  67. package/dist/vite-plugin-DVUlcvEH.js +421 -0
  68. package/dist/vite-plugin-DVUlcvEH.js.map +1 -0
  69. package/package.json +13 -12
  70. package/dist/babel-plugin-GOB6FMZ2.js +0 -119
  71. package/dist/chunk-33JVAFGQ.js +0 -44
  72. package/dist/chunk-4A5BKHFN.js +0 -16
  73. package/dist/chunk-7MSGHEST.js +0 -31
  74. package/dist/chunk-FYKC3DAZ.js +0 -36
  75. package/dist/chunk-LXW5WYBS.js +0 -298
  76. package/dist/chunk-PACQPCSW.js +0 -479
  77. package/dist/chunk-VGPVG5JP.js +0 -73
  78. package/dist/i18n-B--L7OQk.d.ts +0 -76
  79. package/dist/types-B_8vAmvv.d.ts +0 -18
  80. package/dist/types-BeDFfFIL.d.ts +0 -7
@@ -0,0 +1,421 @@
1
+ import { n as chunkIdToName } from "./url-B8334XSD.js";
2
+ import { n as getManifest, t as getGlobalState } from "./manifest-D883_BIS.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import MagicString from "magic-string";
6
+ //#region src/extraction/extract.ts
7
+ /** TypeScript extensions (need 'typescript' babel plugin) */
8
+ const TS_EXTENSIONS = [".ts", ".tsx"];
9
+ /** JSX extensions (need 'jsx' babel plugin) */
10
+ const JSX_EXTENSIONS = [".jsx", ".tsx"];
11
+ /** Plain JS extensions */
12
+ const JS_EXTENSIONS = [".js", ".jsx"];
13
+ /**
14
+ * All extensions that may contain t() calls.
15
+ * Includes .astro which requires Astro's compiler before Babel can parse it.
16
+ */
17
+ const ALL_EXTENSIONS = [...[...TS_EXTENSIONS, ...JS_EXTENSIONS], ".astro"];
18
+ /**
19
+ * Extract keys from a single file using Babel.
20
+ * Optionally strips fallbacks (for production builds).
21
+ */
22
+ async function extractKeysFromFile(options) {
23
+ const { filename, code, logger, stripFallbacks = false, onKeyExtracted } = options;
24
+ const keys = [];
25
+ const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([import("@babel/core"), import("./babel-plugin-C8O9spUS.js")]);
26
+ const isTypeScript = TS_EXTENSIONS.some((ext) => filename.endsWith(ext)) || filename.endsWith(".astro");
27
+ const isJSX = JSX_EXTENSIONS.some((ext) => filename.endsWith(ext));
28
+ const result = await transformAsync(code, {
29
+ filename,
30
+ sourceMaps: stripFallbacks,
31
+ babelrc: false,
32
+ configFile: false,
33
+ parserOpts: { plugins: [...isTypeScript ? ["typescript"] : [], ...isJSX ? ["jsx"] : []] },
34
+ plugins: [[i18nExtractPlugin, {
35
+ stripFallbacks,
36
+ onKeyExtracted: (key) => {
37
+ keys.push(key);
38
+ onKeyExtracted?.(key);
39
+ },
40
+ logger
41
+ }]]
42
+ });
43
+ return {
44
+ keys,
45
+ code: result?.code ?? null,
46
+ map: result?.map ?? null
47
+ };
48
+ }
49
+ //#endregion
50
+ //#region src/extraction/key-store.ts
51
+ /**
52
+ * Store for extracted i18n keys.
53
+ * Stores occurrences and produces deduplicated keys with all file locations.
54
+ */
55
+ var KeyStore = class {
56
+ logger;
57
+ consistency;
58
+ /** All key occurrences (may have duplicates) */
59
+ occurrences = [];
60
+ /** Tracks reported inconsistencies to avoid duplicate warnings */
61
+ reportedInconsistencies = /* @__PURE__ */ new Set();
62
+ /** Whether an error-level inconsistency was found (for build failure) */
63
+ hasInconsistencyError = false;
64
+ fileToKeys = /* @__PURE__ */ new Map();
65
+ filesWithI18n = /* @__PURE__ */ new Set();
66
+ constructor(logger, consistency = "error") {
67
+ this.logger = logger;
68
+ this.consistency = consistency;
69
+ }
70
+ /**
71
+ * Add key occurrences for a file, replacing any existing occurrences for that file.
72
+ */
73
+ addFileKeys(filename, keys) {
74
+ const newKeyStrings = keys.map((k) => k.key);
75
+ const oldKeys = this.fileToKeys.get(filename);
76
+ if (oldKeys) {
77
+ const uniqueNew = [...new Set(newKeyStrings)];
78
+ const uniqueOld = [...new Set(oldKeys)];
79
+ const added = uniqueNew.filter((k) => !uniqueOld.includes(k));
80
+ const removed = uniqueOld.filter((k) => !uniqueNew.includes(k));
81
+ const file = filename.split("/").pop();
82
+ if (added.length > 0 || removed.length > 0) {
83
+ const parts = [];
84
+ if (added.length > 0) parts.push(`+${added.length}`);
85
+ if (removed.length > 0) parts.push(`-${removed.length}`);
86
+ this.logger.info(`hmr: ${parts.join(" ")} key(s) in ${file}`);
87
+ }
88
+ }
89
+ if (oldKeys) {
90
+ for (let i = this.occurrences.length - 1; i >= 0; i--) if (this.occurrences[i]?.file === filename) this.occurrences.splice(i, 1);
91
+ }
92
+ this.filesWithI18n.add(filename);
93
+ if (this.consistency !== "off") this.checkConsistency(keys);
94
+ if (keys.length > 0) this.occurrences.push(...keys);
95
+ this.fileToKeys.set(filename, newKeyStrings);
96
+ }
97
+ /**
98
+ * Check new keys for inconsistencies with existing occurrences.
99
+ */
100
+ checkConsistency(newKeys) {
101
+ const existingByKey = /* @__PURE__ */ new Map();
102
+ for (const occ of this.occurrences) if (!existingByKey.has(occ.key)) existingByKey.set(occ.key, occ);
103
+ for (const newKey of newKeys) {
104
+ const existing = existingByKey.get(newKey.key);
105
+ if (!existing) continue;
106
+ const inconsistencies = this.findInconsistencies(newKey, existing);
107
+ for (const inc of inconsistencies) this.reportInconsistency(inc);
108
+ }
109
+ }
110
+ /**
111
+ * Compare two occurrences and find any metadata inconsistencies.
112
+ */
113
+ findInconsistencies(a, b) {
114
+ const result = [];
115
+ const locA = `${a.file}:${a.line}`;
116
+ const locB = `${b.file}:${b.line}`;
117
+ if (a.meta.fallback !== b.meta.fallback) result.push({
118
+ key: a.key,
119
+ field: "fallback",
120
+ locations: [locA, locB],
121
+ values: [a.meta.fallback, b.meta.fallback]
122
+ });
123
+ if (a.meta.description !== b.meta.description) result.push({
124
+ key: a.key,
125
+ field: "description",
126
+ locations: [locA, locB],
127
+ values: [a.meta.description ?? "(none)", b.meta.description ?? "(none)"]
128
+ });
129
+ const varsA = this.serializeVariables(a.meta);
130
+ const varsB = this.serializeVariables(b.meta);
131
+ if (varsA !== varsB) result.push({
132
+ key: a.key,
133
+ field: "variables",
134
+ locations: [locA, locB],
135
+ values: [varsA || "(none)", varsB || "(none)"]
136
+ });
137
+ return result;
138
+ }
139
+ /**
140
+ * Serialize variables for comparison.
141
+ */
142
+ serializeVariables(meta) {
143
+ if (!meta.variables) return "";
144
+ const sorted = {};
145
+ for (const key of Object.keys(meta.variables).sort()) sorted[key] = meta.variables[key];
146
+ return JSON.stringify(sorted);
147
+ }
148
+ /**
149
+ * Report an inconsistency via logger.
150
+ */
151
+ reportInconsistency(inc) {
152
+ const incKey = `${inc.key}:${inc.field}`;
153
+ if (this.reportedInconsistencies.has(incKey)) return;
154
+ this.reportedInconsistencies.add(incKey);
155
+ const message = `inconsistent ${inc.field} for key "${inc.key}"\n ${inc.locations[0]}: ${JSON.stringify(inc.values[0])}\n ${inc.locations[1]}: ${JSON.stringify(inc.values[1])}`;
156
+ if (this.consistency === "error") {
157
+ this.logger.error(message);
158
+ this.hasInconsistencyError = true;
159
+ } else this.logger.warn(message);
160
+ }
161
+ /**
162
+ * Check if any error-level inconsistencies were found.
163
+ * Call this at the end of build to determine if it should fail.
164
+ */
165
+ get hasErrors() {
166
+ return this.hasInconsistencyError;
167
+ }
168
+ /**
169
+ * Get deduplicated keys with all file locations merged.
170
+ * Last occurrence's meta wins for each key.
171
+ */
172
+ get extractedKeys() {
173
+ const keyMap = /* @__PURE__ */ new Map();
174
+ for (const occurrence of this.occurrences) {
175
+ const existing = keyMap.get(occurrence.key);
176
+ const fileLocation = `${occurrence.file}:${occurrence.line}`;
177
+ if (existing) {
178
+ if (!existing.files.includes(fileLocation)) existing.files.push(fileLocation);
179
+ existing.meta = occurrence.meta;
180
+ } else keyMap.set(occurrence.key, {
181
+ key: occurrence.key,
182
+ meta: occurrence.meta,
183
+ files: [fileLocation]
184
+ });
185
+ }
186
+ return Array.from(keyMap.values());
187
+ }
188
+ /**
189
+ * Get count of unique keys.
190
+ */
191
+ get uniqueKeyCount() {
192
+ return new Set(this.occurrences.map((k) => k.key)).size;
193
+ }
194
+ /**
195
+ * Merge another store's data into this one.
196
+ */
197
+ merge(other) {
198
+ for (const [file, keys] of other.fileToKeys) this.fileToKeys.set(file, keys);
199
+ for (const file of other.filesWithI18n) this.filesWithI18n.add(file);
200
+ if (this.consistency !== "off") this.checkConsistency(other.occurrences);
201
+ this.occurrences.push(...other.occurrences);
202
+ }
203
+ /**
204
+ * Internal access to occurrences (for merge)
205
+ */
206
+ get occurrencesList() {
207
+ return this.occurrences;
208
+ }
209
+ };
210
+ //#endregion
211
+ //#region src/extraction/scan.ts
212
+ const GLOB_PATTERN = `src/**/*.{${ALL_EXTENSIONS.map((e) => e.slice(1)).join(",")}}`;
213
+ /**
214
+ * Compile .astro file to JS using Astro's compiler.
215
+ * Returns the compiled code or null if compilation fails.
216
+ */
217
+ async function compileAstro(code, filename) {
218
+ try {
219
+ const { transform } = await import("@astrojs/compiler");
220
+ return (await transform(code, { filename })).code;
221
+ } catch {
222
+ return null;
223
+ }
224
+ }
225
+ /**
226
+ * Eagerly scan all source files for t() calls.
227
+ * Used in dev mode to extract all keys upfront, instead of waiting for
228
+ * Vite's lazy transform hook to process files on-demand.
229
+ */
230
+ async function scan(options) {
231
+ const { projectRoot, logger, consistency } = options;
232
+ const store = new KeyStore(logger, consistency);
233
+ const files = [];
234
+ for await (const entry of fs.promises.glob(GLOB_PATTERN, {
235
+ cwd: projectRoot,
236
+ exclude: (name) => name === "node_modules"
237
+ })) files.push(path.resolve(projectRoot, entry));
238
+ const results = await Promise.all(files.map(async (file) => {
239
+ let code = await fs.promises.readFile(file, "utf-8");
240
+ if (!code.includes("@astroscope/i18n/translate")) return {
241
+ file,
242
+ keys: null
243
+ };
244
+ if (file.endsWith(".astro")) {
245
+ const compiled = await compileAstro(code, file);
246
+ if (!compiled) return {
247
+ file,
248
+ keys: null
249
+ };
250
+ code = compiled;
251
+ }
252
+ const result = await extractKeysFromFile({
253
+ filename: file,
254
+ code,
255
+ logger,
256
+ stripFallbacks: false
257
+ });
258
+ return {
259
+ file,
260
+ keys: result.keys.length > 0 ? result.keys : null
261
+ };
262
+ }));
263
+ for (const { file, keys } of results) if (keys) store.addFileKeys(file, keys);
264
+ return store;
265
+ }
266
+ //#endregion
267
+ //#region src/extraction/vite-plugin.ts
268
+ const VIRTUAL_MODULE_ID = "virtual:@astroscope/i18n/manifest";
269
+ const isVirtualModuleId = (id) => id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}?`);
270
+ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
271
+ const isResolvedVirtualModuleId = (id) => id === RESOLVED_VIRTUAL_MODULE_ID || id.startsWith(`\0${VIRTUAL_MODULE_ID}?`);
272
+ const MANIFEST_FILE_NAME = "i18n-manifest.json";
273
+ /**
274
+ * Vite plugin for i18n extraction, chunk mapping, and loader injection
275
+ *
276
+ * This plugin:
277
+ * 1. Extracts all t() calls using Babel AST parsing
278
+ * 2. Strips fallback arguments in production builds
279
+ * 3. Maps translation keys to output chunks
280
+ * 4. Generates a manifest for syncing with translation providers
281
+ * 5. Injects translation loader into chunks that use t()
282
+ */
283
+ function i18nVitePlugin(options) {
284
+ const { logger, consistency } = options;
285
+ const store = new KeyStore(logger, consistency);
286
+ const syncGlobalState = () => {
287
+ const state = getGlobalState();
288
+ state.extractedKeys = store.extractedKeys;
289
+ state.version++;
290
+ };
291
+ let isBuild = false;
292
+ let projectRoot = "";
293
+ return {
294
+ name: "@astroscope/i18n/extract",
295
+ enforce: "pre",
296
+ configResolved(config) {
297
+ const state = getGlobalState();
298
+ state.projectRoot = config.root;
299
+ isBuild = config.command === "build";
300
+ projectRoot = config.root;
301
+ },
302
+ async configureServer(server) {
303
+ if (server.config.isProduction || isBuild) return;
304
+ const result = await scan({
305
+ projectRoot,
306
+ logger,
307
+ consistency
308
+ });
309
+ store.merge(result);
310
+ syncGlobalState();
311
+ logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);
312
+ },
313
+ resolveId(id) {
314
+ if (isVirtualModuleId(id)) return `\0${id}`;
315
+ },
316
+ load(id) {
317
+ if (isResolvedVirtualModuleId(id)) {
318
+ if (isBuild) return `
319
+ import { readFileSync } from 'node:fs';
320
+ import { fileURLToPath } from 'node:url';
321
+ import { dirname, join } from 'node:path';
322
+
323
+ const __dirname = dirname(fileURLToPath(import.meta.url));
324
+ const manifestJson = JSON.parse(readFileSync(join(__dirname, '${MANIFEST_FILE_NAME}'), 'utf-8'));
325
+
326
+ export const manifest = manifestJson;
327
+ export function getManifest() { return manifestJson; }
328
+ `;
329
+ return `
330
+ import { getManifest as _getManifest } from '@astroscope/i18n/extraction';
331
+ export const manifest = { keys: [], chunks: {} };
332
+ export function getManifest() { return _getManifest(); }
333
+ `;
334
+ }
335
+ },
336
+ async transform(code, filename) {
337
+ if (!ALL_EXTENSIONS.some((ext) => filename.endsWith(ext))) return null;
338
+ if (filename.includes("node_modules")) return null;
339
+ if (!code.includes("@astroscope/i18n/translate")) return null;
340
+ const result = await extractKeysFromFile({
341
+ filename,
342
+ code,
343
+ logger,
344
+ stripFallbacks: isBuild
345
+ });
346
+ store.addFileKeys(filename, result.keys);
347
+ syncGlobalState();
348
+ if (isBuild && result.code) return result.map ? {
349
+ code: result.code,
350
+ map: JSON.stringify(result.map)
351
+ } : { code: result.code };
352
+ return null;
353
+ },
354
+ generateBundle(_, bundle) {
355
+ const state = getGlobalState();
356
+ state.chunkManifest = {};
357
+ state.importsManifest = {};
358
+ const chunksWithI18n = /* @__PURE__ */ new Set();
359
+ const chunkNameToFileName = /* @__PURE__ */ new Map();
360
+ const directImports = /* @__PURE__ */ new Map();
361
+ for (const [fileName, chunk] of Object.entries(bundle)) if (chunk.type === "chunk" && chunk.moduleIds) {
362
+ const keys = /* @__PURE__ */ new Set();
363
+ let hasI18nModule = false;
364
+ for (const moduleId of chunk.moduleIds) {
365
+ store.fileToKeys.get(moduleId)?.forEach((k) => keys.add(k));
366
+ if (store.filesWithI18n.has(moduleId)) hasI18nModule = true;
367
+ }
368
+ const chunkName = chunkIdToName(fileName.replace(/\.js$/, ""));
369
+ if (keys.size) state.chunkManifest[chunkName] = Array.from(keys);
370
+ if (hasI18nModule) chunksWithI18n.add(chunkName);
371
+ chunkNameToFileName.set(chunkName, fileName);
372
+ const imports = [];
373
+ for (const importedFile of chunk.imports) {
374
+ const importedChunkName = chunkIdToName(importedFile.replace(/^.*\//, "").replace(/\.js$/, ""));
375
+ imports.push(importedChunkName);
376
+ }
377
+ directImports.set(chunkName, imports);
378
+ }
379
+ const flattenImports = (chunkName, visited) => {
380
+ if (visited.has(chunkName)) return [];
381
+ visited.add(chunkName);
382
+ const result = /* @__PURE__ */ new Set();
383
+ const imports = directImports.get(chunkName) ?? [];
384
+ for (const imported of imports) {
385
+ if (state.chunkManifest[imported]) result.add(imported);
386
+ for (const descendant of flattenImports(imported, visited)) result.add(descendant);
387
+ }
388
+ visited.delete(chunkName);
389
+ return Array.from(result);
390
+ };
391
+ for (const chunkName of directImports.keys()) {
392
+ const flattened = flattenImports(chunkName, /* @__PURE__ */ new Set());
393
+ if (flattened.length > 0) state.importsManifest[chunkName] = flattened;
394
+ }
395
+ for (const [fileName, chunk] of Object.entries(bundle)) {
396
+ if (chunk.type !== "chunk") continue;
397
+ const chunkName = chunkIdToName(fileName.replace(/\.js$/, ""));
398
+ if (!chunksWithI18n.has(chunkName)) continue;
399
+ const loaderCode = `if(typeof window!=='undefined'&&window.__i18n__){const _=window.__i18n__,h=_.hashes[${JSON.stringify(chunkName)}];if(h)await import(\`/_i18n/\${_.locale}/${chunkName}.\${h}.js\`);}`;
400
+ const s = new MagicString(chunk.code);
401
+ s.prepend(loaderCode);
402
+ chunk.code = s.toString();
403
+ if (chunk.map) chunk.map = s.generateMap({ hires: true });
404
+ }
405
+ logger.info(`manifest: ${Object.keys(state.chunkManifest).length} chunks, ${store.uniqueKeyCount} keys`);
406
+ if (consistency === "error" && store.hasErrors) throw new Error("i18n: build failed due to translation key inconsistencies");
407
+ },
408
+ writeBundle(outputOptions) {
409
+ if (this.environment.name === "client" && outputOptions.dir) {
410
+ const chunksDir = path.resolve(outputOptions.dir, "..", "server", "chunks");
411
+ const manifestPath = path.join(chunksDir, MANIFEST_FILE_NAME);
412
+ if (fs.existsSync(chunksDir)) fs.writeFileSync(manifestPath, JSON.stringify(getManifest()));
413
+ else logger.error(`server chunks directory not found, cannot write manifest to ${manifestPath}`);
414
+ }
415
+ }
416
+ };
417
+ }
418
+ //#endregion
419
+ export { i18nVitePlugin as t };
420
+
421
+ //# sourceMappingURL=vite-plugin-DVUlcvEH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin-DVUlcvEH.js","names":[],"sources":["../src/extraction/extract.ts","../src/extraction/key-store.ts","../src/extraction/scan.ts","../src/extraction/vite-plugin.ts"],"sourcesContent":["import type { FileResult, PluginItem } from '@babel/core';\nimport type { AstroIntegrationLogger } from 'astro';\nimport type { ExtractedKeyOccurrence } from './types.js';\n\n/** TypeScript extensions (need 'typescript' babel plugin) */\nexport const TS_EXTENSIONS = ['.ts', '.tsx'] as const;\n\n/** JSX extensions (need 'jsx' babel plugin) */\nexport const JSX_EXTENSIONS = ['.jsx', '.tsx'] as const;\n\n/** Plain JS extensions */\nexport const JS_EXTENSIONS = ['.js', '.jsx'] as const;\n\n/**\n * Extensions that can be parsed directly by Babel (raw source files).\n * Used for eager scanning before Vite processes them.\n */\nexport const BABEL_EXTENSIONS = [...TS_EXTENSIONS, ...JS_EXTENSIONS] as const;\n\n/**\n * All extensions that may contain t() calls.\n * Includes .astro which requires Astro's compiler before Babel can parse it.\n */\nexport const ALL_EXTENSIONS = [...BABEL_EXTENSIONS, '.astro'] as const;\n\nexport type ExtractOptions = {\n filename: string;\n code: string;\n logger: AstroIntegrationLogger;\n stripFallbacks?: boolean | undefined;\n onKeyExtracted?: ((key: ExtractedKeyOccurrence) => void) | undefined;\n};\n\nexport type ExtractResult = {\n keys: ExtractedKeyOccurrence[];\n code: string | null;\n map: FileResult['map'];\n};\n\n/**\n * Extract keys from a single file using Babel.\n * Optionally strips fallbacks (for production builds).\n */\nexport async function extractKeysFromFile(options: ExtractOptions): Promise<ExtractResult> {\n const { filename, code, logger, stripFallbacks = false, onKeyExtracted } = options;\n\n const keys: ExtractedKeyOccurrence[] = [];\n\n // dynamic imports prevent Babel from being bundled into server runtime\n const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([\n import('@babel/core'),\n import('./babel-plugin.js'),\n ]);\n\n // treat .astro files as TypeScript (otherwise type imports fail)\n const isTypeScript = TS_EXTENSIONS.some((ext) => filename.endsWith(ext)) || filename.endsWith('.astro');\n const isJSX = JSX_EXTENSIONS.some((ext) => filename.endsWith(ext));\n\n const result = await transformAsync(code, {\n filename,\n sourceMaps: stripFallbacks, // only need source maps when transforming\n babelrc: false,\n configFile: false,\n parserOpts: {\n plugins: [...(isTypeScript ? ['typescript' as const] : []), ...(isJSX ? ['jsx' as const] : [])],\n },\n plugins: [\n [\n i18nExtractPlugin,\n {\n stripFallbacks,\n onKeyExtracted: (key: ExtractedKeyOccurrence) => {\n keys.push(key);\n onKeyExtracted?.(key);\n },\n logger,\n },\n ] as PluginItem,\n ],\n });\n\n return {\n keys,\n code: result?.code ?? null,\n map: result?.map ?? null,\n };\n}\n","import type { AstroIntegrationLogger } from 'astro';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport type { TranslationMeta } from '../shared/types.js';\nimport type { ExtractedKey, ExtractedKeyOccurrence } from './types.js';\n\n/**\n * Inconsistency between two occurrences of the same translation key\n */\nexport type KeyInconsistency = {\n key: string;\n field: 'fallback' | 'description' | 'variables';\n locations: [string, string];\n values: [string, string];\n};\n\n/**\n * Store for extracted i18n keys.\n * Stores occurrences and produces deduplicated keys with all file locations.\n */\nexport class KeyStore {\n /** All key occurrences (may have duplicates) */\n private readonly occurrences: ExtractedKeyOccurrence[] = [];\n\n /** Tracks reported inconsistencies to avoid duplicate warnings */\n private readonly reportedInconsistencies = new Set<string>();\n\n /** Whether an error-level inconsistency was found (for build failure) */\n private hasInconsistencyError = false;\n\n readonly fileToKeys = new Map<string, string[]>();\n readonly filesWithI18n = new Set<string>();\n\n constructor(\n private readonly logger: AstroIntegrationLogger,\n private readonly consistency: ConsistencyCheckLevel = 'error',\n ) {}\n\n /**\n * Add key occurrences for a file, replacing any existing occurrences for that file.\n */\n addFileKeys(filename: string, keys: ExtractedKeyOccurrence[]): void {\n const newKeyStrings = keys.map((k) => k.key);\n const oldKeys = this.fileToKeys.get(filename);\n\n // log key changes on HMR\n if (oldKeys) {\n const uniqueNew = [...new Set(newKeyStrings)];\n const uniqueOld = [...new Set(oldKeys)];\n const added = uniqueNew.filter((k) => !uniqueOld.includes(k));\n const removed = uniqueOld.filter((k) => !uniqueNew.includes(k));\n const file = filename.split('/').pop();\n\n if (added.length > 0 || removed.length > 0) {\n const parts: string[] = [];\n\n if (added.length > 0) parts.push(`+${added.length}`);\n if (removed.length > 0) parts.push(`-${removed.length}`);\n\n this.logger.info(`hmr: ${parts.join(' ')} key(s) in ${file}`);\n }\n }\n\n // remove old occurrences for this file (if any)\n if (oldKeys) {\n for (let i = this.occurrences.length - 1; i >= 0; i--) {\n if (this.occurrences[i]?.file === filename) {\n this.occurrences.splice(i, 1);\n }\n }\n }\n\n this.filesWithI18n.add(filename);\n\n // check for inconsistencies before adding new keys\n if (this.consistency !== 'off') {\n this.checkConsistency(keys);\n }\n\n if (keys.length > 0) {\n this.occurrences.push(...keys);\n }\n\n this.fileToKeys.set(filename, newKeyStrings);\n }\n\n /**\n * Check new keys for inconsistencies with existing occurrences.\n */\n private checkConsistency(newKeys: ExtractedKeyOccurrence[]): void {\n // build a map of existing keys (first occurrence for each key)\n const existingByKey = new Map<string, ExtractedKeyOccurrence>();\n\n for (const occ of this.occurrences) {\n if (!existingByKey.has(occ.key)) {\n existingByKey.set(occ.key, occ);\n }\n }\n\n for (const newKey of newKeys) {\n const existing = existingByKey.get(newKey.key);\n\n if (!existing) continue;\n\n const inconsistencies = this.findInconsistencies(newKey, existing);\n\n for (const inc of inconsistencies) {\n this.reportInconsistency(inc);\n }\n }\n }\n\n /**\n * Compare two occurrences and find any metadata inconsistencies.\n */\n private findInconsistencies(a: ExtractedKeyOccurrence, b: ExtractedKeyOccurrence): KeyInconsistency[] {\n const result: KeyInconsistency[] = [];\n const locA = `${a.file}:${a.line}`;\n const locB = `${b.file}:${b.line}`;\n\n // check fallback\n if (a.meta.fallback !== b.meta.fallback) {\n result.push({\n key: a.key,\n field: 'fallback',\n locations: [locA, locB],\n values: [a.meta.fallback, b.meta.fallback],\n });\n }\n\n // check description\n if (a.meta.description !== b.meta.description) {\n result.push({\n key: a.key,\n field: 'description',\n locations: [locA, locB],\n values: [a.meta.description ?? '(none)', b.meta.description ?? '(none)'],\n });\n }\n\n // check variables\n const varsA = this.serializeVariables(a.meta);\n const varsB = this.serializeVariables(b.meta);\n\n if (varsA !== varsB) {\n result.push({\n key: a.key,\n field: 'variables',\n locations: [locA, locB],\n values: [varsA || '(none)', varsB || '(none)'],\n });\n }\n\n return result;\n }\n\n /**\n * Serialize variables for comparison.\n */\n private serializeVariables(meta: TranslationMeta): string {\n if (!meta.variables) return '';\n\n // sort keys for consistent comparison\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(meta.variables).sort()) {\n sorted[key] = meta.variables[key];\n }\n\n return JSON.stringify(sorted);\n }\n\n /**\n * Report an inconsistency via logger.\n */\n private reportInconsistency(inc: KeyInconsistency): void {\n // create a unique key for this inconsistency to avoid duplicate reports\n // use key+field only (not locations) to report once per inconsistent key\n const incKey = `${inc.key}:${inc.field}`;\n\n if (this.reportedInconsistencies.has(incKey)) return;\n\n this.reportedInconsistencies.add(incKey);\n\n const message =\n `inconsistent ${inc.field} for key \"${inc.key}\"\\n` +\n ` ${inc.locations[0]}: ${JSON.stringify(inc.values[0])}\\n` +\n ` ${inc.locations[1]}: ${JSON.stringify(inc.values[1])}`;\n\n if (this.consistency === 'error') {\n this.logger.error(message);\n this.hasInconsistencyError = true;\n } else {\n this.logger.warn(message);\n }\n }\n\n /**\n * Check if any error-level inconsistencies were found.\n * Call this at the end of build to determine if it should fail.\n */\n get hasErrors(): boolean {\n return this.hasInconsistencyError;\n }\n\n /**\n * Get deduplicated keys with all file locations merged.\n * Last occurrence's meta wins for each key.\n */\n get extractedKeys(): ExtractedKey[] {\n const keyMap = new Map<string, ExtractedKey>();\n\n for (const occurrence of this.occurrences) {\n const existing = keyMap.get(occurrence.key);\n const fileLocation = `${occurrence.file}:${occurrence.line}`;\n\n if (existing) {\n // add file location if not already present\n if (!existing.files.includes(fileLocation)) {\n existing.files.push(fileLocation);\n }\n\n // update meta (last one wins)\n existing.meta = occurrence.meta;\n } else {\n keyMap.set(occurrence.key, {\n key: occurrence.key,\n meta: occurrence.meta,\n files: [fileLocation],\n });\n }\n }\n\n return Array.from(keyMap.values());\n }\n\n /**\n * Get count of unique keys.\n */\n get uniqueKeyCount(): number {\n return new Set(this.occurrences.map((k) => k.key)).size;\n }\n\n /**\n * Merge another store's data into this one.\n */\n merge(other: KeyStore): void {\n for (const [file, keys] of other.fileToKeys) {\n this.fileToKeys.set(file, keys);\n }\n\n for (const file of other.filesWithI18n) {\n this.filesWithI18n.add(file);\n }\n\n // check for inconsistencies when merging\n if (this.consistency !== 'off') {\n this.checkConsistency(other.occurrences);\n }\n\n this.occurrences.push(...other.occurrences);\n }\n\n /**\n * Internal access to occurrences (for merge)\n */\n get occurrencesList(): ExtractedKeyOccurrence[] {\n return this.occurrences;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { AstroIntegrationLogger } from 'astro';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport { ALL_EXTENSIONS, extractKeysFromFile } from './extract.js';\nimport { KeyStore } from './key-store.js';\n\nconst GLOB_PATTERN = `src/**/*.{${ALL_EXTENSIONS.map((e) => e.slice(1)).join(',')}}`;\n\n/**\n * Compile .astro file to JS using Astro's compiler.\n * Returns the compiled code or null if compilation fails.\n */\nasync function compileAstro(code: string, filename: string): Promise<string | null> {\n try {\n const { transform } = await import('@astrojs/compiler');\n const result = await transform(code, { filename });\n\n return result.code;\n } catch {\n return null;\n }\n}\n\nexport type ScanOptions = {\n projectRoot: string;\n logger: AstroIntegrationLogger;\n consistency: ConsistencyCheckLevel;\n};\n\n/**\n * Eagerly scan all source files for t() calls.\n * Used in dev mode to extract all keys upfront, instead of waiting for\n * Vite's lazy transform hook to process files on-demand.\n */\nexport async function scan(options: ScanOptions): Promise<KeyStore> {\n const { projectRoot, logger, consistency } = options;\n const store = new KeyStore(logger, consistency);\n\n const files: string[] = [];\n\n for await (const entry of fs.promises.glob(GLOB_PATTERN, {\n cwd: projectRoot,\n exclude: (name) => name === 'node_modules',\n })) {\n files.push(path.resolve(projectRoot, entry));\n }\n\n const results = await Promise.all(\n files.map(async (file) => {\n let code = await fs.promises.readFile(file, 'utf-8');\n\n // quick check: skip files without i18n translate import\n if (!code.includes('@astroscope/i18n/translate')) {\n return { file, keys: null };\n }\n\n // compile .astro files first\n if (file.endsWith('.astro')) {\n const compiled = await compileAstro(code, file);\n\n if (!compiled) {\n return { file, keys: null };\n }\n\n code = compiled;\n }\n\n // extract keys\n const result = await extractKeysFromFile({\n filename: file,\n code,\n logger,\n stripFallbacks: false,\n });\n\n return { file, keys: result.keys.length > 0 ? result.keys : null };\n }),\n );\n\n for (const { file, keys } of results) {\n if (keys) {\n store.addFileKeys(file, keys);\n }\n }\n\n return store;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { AstroIntegrationLogger } from 'astro';\nimport MagicString from 'magic-string';\nimport type { Plugin } from 'vite';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport { chunkIdToName } from '../shared/url.js';\nimport { ALL_EXTENSIONS, extractKeysFromFile } from './extract.js';\nimport { KeyStore } from './key-store.js';\nimport { getGlobalState, getManifest } from './manifest.js';\nimport { scan } from './scan.js';\n\nconst VIRTUAL_MODULE_ID = 'virtual:@astroscope/i18n/manifest';\nconst isVirtualModuleId = (id: string) => id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}?`);\n\nconst RESOLVED_VIRTUAL_MODULE_ID = `\\0${VIRTUAL_MODULE_ID}`;\nconst isResolvedVirtualModuleId = (id: string) =>\n id === RESOLVED_VIRTUAL_MODULE_ID || id.startsWith(`\\0${VIRTUAL_MODULE_ID}?`);\n\n// manifest JSON file name (emitted during build)\nconst MANIFEST_FILE_NAME = 'i18n-manifest.json';\n\nexport type I18nVitePluginOptions = {\n logger: AstroIntegrationLogger;\n consistency: ConsistencyCheckLevel;\n};\n\n/**\n * Vite plugin for i18n extraction, chunk mapping, and loader injection\n *\n * This plugin:\n * 1. Extracts all t() calls using Babel AST parsing\n * 2. Strips fallback arguments in production builds\n * 3. Maps translation keys to output chunks\n * 4. Generates a manifest for syncing with translation providers\n * 5. Injects translation loader into chunks that use t()\n */\nexport function i18nVitePlugin(options: I18nVitePluginOptions): Plugin {\n const { logger, consistency } = options;\n\n const store = new KeyStore(logger, consistency);\n\n // sync store's computed keys to global state (for dev mode live access)\n const syncGlobalState = () => {\n const state = getGlobalState();\n state.extractedKeys = store.extractedKeys;\n state.version++;\n };\n\n let isBuild = false;\n let projectRoot = '';\n\n return {\n name: '@astroscope/i18n/extract',\n enforce: 'pre',\n\n configResolved(config) {\n const state = getGlobalState();\n state.projectRoot = config.root;\n\n isBuild = config.command === 'build';\n projectRoot = config.root;\n },\n\n async configureServer(server) {\n // configureServer can be called during astro build's internal dev server\n // check isProduction to skip eager scanning during build\n if (server.config.isProduction || isBuild) {\n return;\n }\n\n // in dev mode, eagerly scan all files upfront\n // this ensures all t() calls are found immediately\n // (otherwise vite loads lazily as files are requested)\n const result = await scan({ projectRoot, logger, consistency });\n\n store.merge(result);\n\n syncGlobalState();\n\n logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);\n },\n\n resolveId(id) {\n if (isVirtualModuleId(id)) {\n return `\\0${id}`;\n }\n },\n\n load(id) {\n if (isResolvedVirtualModuleId(id)) {\n if (isBuild) {\n // in build mode, load manifest from JSON file at runtime\n return `\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst manifestJson = JSON.parse(readFileSync(join(__dirname, '${MANIFEST_FILE_NAME}'), 'utf-8'));\n\nexport const manifest = manifestJson;\nexport function getManifest() { return manifestJson; }\n`;\n }\n\n // in dev mode, import the getManifest function from the plugin\n // this provides live access to the extraction state as files are transformed\n return `\nimport { getManifest as _getManifest } from '@astroscope/i18n/extraction';\nexport const manifest = { keys: [], chunks: {} };\nexport function getManifest() { return _getManifest(); }\n`;\n }\n },\n\n async transform(code, filename) {\n // only process included file types\n if (!ALL_EXTENSIONS.some((ext) => filename.endsWith(ext))) {\n return null;\n }\n\n // skip node_modules\n if (filename.includes('node_modules')) {\n return null;\n }\n\n // quick check: skip files without i18n translate import\n if (!code.includes('@astroscope/i18n/translate')) {\n return null;\n }\n\n const result = await extractKeysFromFile({\n filename,\n code,\n logger,\n stripFallbacks: isBuild,\n });\n\n store.addFileKeys(filename, result.keys);\n\n syncGlobalState();\n\n // build mode: return transformed code with source map\n // serialize babel's map to a string: vite accepts string source maps and\n // its structural ExistingRawSourceMap type (vite 8) rejects babel's map shape\n if (isBuild && result.code) {\n return result.map ? { code: result.code, map: JSON.stringify(result.map) } : { code: result.code };\n }\n\n return null; // dev mode: keep fallbacks\n },\n\n generateBundle(_, bundle) {\n const state = getGlobalState();\n\n // reset for each build (server + client run separately)\n state.chunkManifest = {};\n state.importsManifest = {};\n\n // first pass: collect keys, determine which chunks have i18n, and build direct imports map\n const chunksWithI18n = new Set<string>();\n const chunkNameToFileName = new Map<string, string>();\n const directImports = new Map<string, string[]>(); // chunk → direct imports (chunks only)\n\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (chunk.type === 'chunk' && chunk.moduleIds) {\n const keys = new Set<string>();\n let hasI18nModule = false;\n\n // collect keys from all (sub)modules in the chunk\n for (const moduleId of chunk.moduleIds) {\n const moduleKeys = store.fileToKeys.get(moduleId);\n\n moduleKeys?.forEach((k) => keys.add(k));\n\n if (store.filesWithI18n.has(moduleId)) {\n hasI18nModule = true;\n }\n }\n\n const chunkName = chunkIdToName(fileName.replace(/\\.js$/, '')); // cut .js extension\n\n if (keys.size) {\n state.chunkManifest[chunkName] = Array.from(keys);\n }\n\n if (hasI18nModule) {\n chunksWithI18n.add(chunkName);\n }\n\n chunkNameToFileName.set(chunkName, fileName);\n\n // store direct imports for flattening later\n const imports: string[] = [];\n\n for (const importedFile of chunk.imports) {\n const baseName = importedFile.replace(/^.*\\//, '').replace(/\\.js$/, '');\n const importedChunkName = chunkIdToName(baseName);\n\n imports.push(importedChunkName);\n }\n\n directImports.set(chunkName, imports);\n }\n }\n\n // build flattened imports manifest\n // tracks all direct and indirect imports that have i18n translations\n // circular dependency detection\n const flattenImports = (chunkName: string, visited: Set<string>): string[] => {\n if (visited.has(chunkName)) return []; // circular dependency, stop\n\n visited.add(chunkName);\n\n const result = new Set<string>();\n const imports = directImports.get(chunkName) ?? [];\n\n for (const imported of imports) {\n // add the imported chunk itself if it has i18n translations\n if (state.chunkManifest[imported]) {\n result.add(imported);\n }\n\n // recursively add all descendants with i18n\n for (const descendant of flattenImports(imported, visited)) {\n result.add(descendant);\n }\n }\n\n visited.delete(chunkName); // allow visiting from different paths\n\n return Array.from(result);\n };\n\n // compute flattened imports for ALL chunks (not just those with i18n)\n // because a chunk without i18n may import chunks that have i18n\n for (const chunkName of directImports.keys()) {\n const flattened = flattenImports(chunkName, new Set());\n\n if (flattened.length > 0) {\n state.importsManifest[chunkName] = flattened;\n }\n }\n\n // second pass: inject loaders (prefetch is handled by directives)\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (chunk.type !== 'chunk') continue;\n\n const chunkName = chunkIdToName(fileName.replace(/\\.js$/, ''));\n\n if (!chunksWithI18n.has(chunkName)) continue;\n\n // simple translation loader - just load own translations\n // prefetching is handled by directives using the imports manifest\n const loaderCode =\n `if(typeof window!=='undefined'&&window.__i18n__){` +\n `const _=window.__i18n__,h=_.hashes[${JSON.stringify(chunkName)}];` +\n `if(h)await import(\\`/_i18n/\\${_.locale}/${chunkName}.\\${h}.js\\`);` +\n `}`;\n\n // inject loader at start of chunk preserving source maps\n const s = new MagicString(chunk.code);\n\n s.prepend(loaderCode);\n chunk.code = s.toString();\n\n if (chunk.map) {\n chunk.map = s.generateMap({ hires: true }) as typeof chunk.map;\n }\n }\n\n logger.info(`manifest: ${Object.keys(state.chunkManifest).length} chunks, ${store.uniqueKeyCount} keys`);\n\n // fail build if consistency check is set to 'error' and inconsistencies were found\n if (consistency === 'error' && store.hasErrors) {\n throw new Error('i18n: build failed due to translation key inconsistencies');\n }\n },\n\n writeBundle(outputOptions) {\n // write the manifest JSON file after bundle is written\n // only emit from client build (has full chunk mapping, runs after server build)\n // write to server directory so it's not publicly accessible (same location as the virtual module)\n if (this.environment.name === 'client' && outputOptions.dir) {\n const chunksDir = path.resolve(outputOptions.dir, '..', 'server', 'chunks');\n const manifestPath = path.join(chunksDir, MANIFEST_FILE_NAME);\n\n if (fs.existsSync(chunksDir)) {\n fs.writeFileSync(manifestPath, JSON.stringify(getManifest()));\n } else {\n logger.error(`server chunks directory not found, cannot write manifest to ${manifestPath}`);\n }\n }\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroscope/i18n",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "i18n for Astro + React islands — MessageFormat 2, automatic tree-shaking, parallel loading, any translation source",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -83,26 +83,27 @@
83
83
  "homepage": "https://github.com/entwico/astroscope/tree/main/packages/i18n#readme",
84
84
  "dependencies": {
85
85
  "@astrojs/compiler": "^4.0.0",
86
- "@babel/core": "^7.29.0",
87
- "@babel/preset-typescript": "^7.28.5",
86
+ "@babel/core": "^8.0.1",
87
+ "@babel/preset-typescript": "^8.0.1",
88
88
  "messageformat": "^4.0.0",
89
89
  "magic-string": "^0.30.21"
90
90
  },
91
91
  "devDependencies": {
92
- "@babel/types": "^7.29.0",
92
+ "@babel/types": "^8.0.0",
93
93
  "@types/babel__core": "^7.20.5",
94
- "astro": "^6.2.2",
95
- "tsup": "^8.5.1",
96
- "typescript": "^5.9.3",
97
- "vite": "^7.0.0",
98
- "@astroscope/excludes": "^0.3.2"
94
+ "astro": "^7.0.3",
95
+ "tsdown": "^0.22.3",
96
+ "typescript": "^6.0.3",
97
+ "vite": "^8.1.0",
98
+ "@astroscope/excludes": "^0.4.0"
99
99
  },
100
100
  "peerDependencies": {
101
- "astro": "^6.0.0",
102
- "@astroscope/excludes": "^0.3.2"
101
+ "astro": "^7.0.0",
102
+ "vite": "^8.0.0",
103
+ "@astroscope/excludes": "^0.4.0"
103
104
  },
104
105
  "scripts": {
105
- "build": "tsup src/index.ts src/internal.ts src/extraction/index.ts src/server/translate.ts src/client/translate.ts src/client/directives/load.ts src/client/directives/idle.ts src/client/directives/visible.ts src/client/directives/media.ts src/client/directives/only.ts src/client/directives/utils.ts --format esm --dts --external astro --external astro/client/load.js --external astro/client/idle.js --external astro/client/visible.js --external astro/client/media.js --external astro/client/only.js --external virtual:@astroscope/i18n/manifest --external @babel/core",
106
+ "build": "tsdown src/index.ts src/internal.ts src/extraction/index.ts src/server/translate.ts src/client/translate.ts src/client/directives/load.ts src/client/directives/idle.ts src/client/directives/visible.ts src/client/directives/media.ts src/client/directives/only.ts src/client/directives/utils.ts --format esm --dts --no-fixed-extension --deps.never-bundle astro --deps.never-bundle astro/client/load.js --deps.never-bundle astro/client/idle.js --deps.never-bundle astro/client/visible.js --deps.never-bundle astro/client/media.js --deps.never-bundle astro/client/only.js --deps.never-bundle virtual:@astroscope/i18n/manifest --deps.never-bundle @babel/core",
106
107
  "typecheck": "tsc --noEmit",
107
108
  "lint": "eslint src --cache",
108
109
  "lint:fix": "eslint src --fix"
@@ -1,119 +0,0 @@
1
- // src/extraction/babel-plugin.ts
2
- function getStringValue(t, node) {
3
- if (t.isStringLiteral(node)) {
4
- return node.value;
5
- }
6
- if (t.isTemplateLiteral(node) && node.quasis.length === 1 && node.expressions.length === 0) {
7
- return node.quasis[0]?.value.cooked ?? null;
8
- }
9
- return null;
10
- }
11
- function extractMeta(t, node, warn) {
12
- const stringValue = getStringValue(t, node);
13
- if (stringValue !== null) {
14
- return { fallback: stringValue };
15
- }
16
- if (t.isTemplateLiteral(node) && node.expressions.length > 0) {
17
- warn("t() meta contains template literal with expressions - cannot extract statically");
18
- return { fallback: "" };
19
- }
20
- if (t.isObjectExpression(node)) {
21
- const meta = { fallback: "" };
22
- for (const prop of node.properties) {
23
- if (t.isSpreadElement(prop)) {
24
- warn("t() meta contains spread element - cannot extract statically");
25
- continue;
26
- }
27
- if (!t.isObjectProperty(prop)) continue;
28
- if (!t.isIdentifier(prop.key)) continue;
29
- const name = prop.key.name;
30
- if (name === "fallback") {
31
- const value = getStringValue(t, prop.value);
32
- if (value !== null) {
33
- meta.fallback = value;
34
- } else {
35
- warn("t() fallback is not a static string - cannot extract");
36
- }
37
- } else if (name === "description") {
38
- const value = getStringValue(t, prop.value);
39
- if (value !== null) {
40
- meta.description = value;
41
- } else {
42
- warn("t() description is not a static string - cannot extract");
43
- }
44
- } else if (name === "variables" && t.isObjectExpression(prop.value)) {
45
- meta.variables = extractVariables(t, prop.value, warn);
46
- }
47
- }
48
- return meta;
49
- }
50
- warn("t() meta is not a static string or object - cannot extract");
51
- return { fallback: "" };
52
- }
53
- function extractVariables(t, obj, warn) {
54
- const result = {};
55
- for (const prop of obj.properties) {
56
- if (t.isSpreadElement(prop)) {
57
- warn("t() variables contains spread element - cannot extract statically");
58
- continue;
59
- }
60
- if (!t.isObjectProperty(prop)) continue;
61
- if (!t.isIdentifier(prop.key)) continue;
62
- const varName = prop.key.name;
63
- if (t.isObjectExpression(prop.value)) {
64
- const varDef = {};
65
- for (const varProp of prop.value.properties) {
66
- if (t.isSpreadElement(varProp)) {
67
- warn(`t() variable "${varName}" contains spread element - cannot extract statically`);
68
- continue;
69
- }
70
- if (!t.isObjectProperty(varProp)) continue;
71
- if (!t.isIdentifier(varProp.key)) continue;
72
- const varPropName = varProp.key.name;
73
- const value = getStringValue(t, varProp.value);
74
- if (value !== null) {
75
- if (varPropName === "fallback") {
76
- varDef.fallback = value;
77
- } else if (varPropName === "description") {
78
- varDef.description = value;
79
- }
80
- } else if (varPropName === "fallback" || varPropName === "description") {
81
- warn(`t() variable "${varName}.${varPropName}" is not a static string - cannot extract`);
82
- }
83
- }
84
- result[varName] = varDef;
85
- }
86
- }
87
- return Object.keys(result).length > 0 ? result : void 0;
88
- }
89
- function i18nExtractPlugin({ types: t }) {
90
- return {
91
- name: "@astroscope/i18n/extract",
92
- visitor: {
93
- CallExpression(path, state) {
94
- if (!path.get("callee").isIdentifier({ name: "t" })) return;
95
- const args = path.node.arguments;
96
- const keyArg = args[0];
97
- if (!t.isExpression(keyArg)) return;
98
- const key = getStringValue(t, keyArg);
99
- if (key === null) return;
100
- const file = state.filename ?? "";
101
- const line = path.node.loc?.start.line ?? 0;
102
- const warn = (message) => state.opts.logger.warn(`${message} at ${file}:${line}`);
103
- const meta = args.length >= 2 && t.isExpression(args[1]) ? extractMeta(t, args[1], warn) : { fallback: "" };
104
- state.opts.onKeyExtracted({ key, meta, file, line });
105
- if (state.opts.stripFallbacks && args.length >= 2) {
106
- const firstArg = args[0];
107
- if (args.length === 2) {
108
- path.node.arguments = [firstArg];
109
- } else {
110
- path.node.arguments = [firstArg, t.identifier("undefined"), args[2]];
111
- }
112
- }
113
- }
114
- }
115
- };
116
- }
117
- export {
118
- i18nExtractPlugin
119
- };