@astroscope/i18n 0.2.3 → 0.2.4

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.
@@ -23,7 +23,12 @@ function getManifest() {
23
23
  ...key,
24
24
  // make file paths relative to project root for better readability
25
25
  // and avoiding leaking absolute paths from dev or ci environments
26
- file: state.projectRoot ? path.relative(state.projectRoot, key.file) : key.file
26
+ files: key.files.map((fileLocation) => {
27
+ const [filePath, line] = fileLocation.split(":");
28
+ if (!filePath) return fileLocation;
29
+ const relativePath = state.projectRoot ? path.relative(state.projectRoot, filePath) : filePath;
30
+ return line ? `${relativePath}:${line}` : relativePath;
31
+ })
27
32
  }));
28
33
  return { keys, chunks: state.chunkManifest, imports: state.importsManifest };
29
34
  }
@@ -83,18 +88,21 @@ var KeyStore = class {
83
88
  constructor(logger) {
84
89
  this.logger = logger;
85
90
  }
86
- extractedKeys = [];
91
+ /** All key occurrences (may have duplicates) */
92
+ occurrences = [];
87
93
  fileToKeys = /* @__PURE__ */ new Map();
88
94
  filesWithI18n = /* @__PURE__ */ new Set();
89
95
  /**
90
- * Add keys for a file, replacing any existing keys for that file.
96
+ * Add key occurrences for a file, replacing any existing occurrences for that file.
91
97
  */
92
98
  addFileKeys(filename, keys) {
93
- const oldKeys = this.fileToKeys.get(filename);
94
99
  const newKeyStrings = keys.map((k) => k.key);
100
+ const oldKeys = this.fileToKeys.get(filename);
95
101
  if (oldKeys) {
96
- const added = newKeyStrings.filter((k) => !oldKeys.includes(k));
97
- const removed = oldKeys.filter((k) => !newKeyStrings.includes(k));
102
+ const uniqueNew = [...new Set(newKeyStrings)];
103
+ const uniqueOld = [...new Set(oldKeys)];
104
+ const added = uniqueNew.filter((k) => !uniqueOld.includes(k));
105
+ const removed = uniqueOld.filter((k) => !uniqueNew.includes(k));
98
106
  const file = filename.split("/").pop();
99
107
  if (added.length > 0 || removed.length > 0) {
100
108
  const parts = [];
@@ -104,23 +112,47 @@ var KeyStore = class {
104
112
  }
105
113
  }
106
114
  if (oldKeys) {
107
- for (let i = this.extractedKeys.length - 1; i >= 0; i--) {
108
- if (this.extractedKeys[i]?.file === filename) {
109
- this.extractedKeys.splice(i, 1);
115
+ for (let i = this.occurrences.length - 1; i >= 0; i--) {
116
+ if (this.occurrences[i]?.file === filename) {
117
+ this.occurrences.splice(i, 1);
110
118
  }
111
119
  }
112
120
  }
113
121
  this.filesWithI18n.add(filename);
114
122
  if (keys.length > 0) {
115
- this.extractedKeys.push(...keys);
123
+ this.occurrences.push(...keys);
116
124
  }
117
125
  this.fileToKeys.set(filename, newKeyStrings);
118
126
  }
127
+ /**
128
+ * Get deduplicated keys with all file locations merged.
129
+ * Last occurrence's meta wins for each key.
130
+ */
131
+ get extractedKeys() {
132
+ const keyMap = /* @__PURE__ */ new Map();
133
+ for (const occurrence of this.occurrences) {
134
+ const existing = keyMap.get(occurrence.key);
135
+ const fileLocation = `${occurrence.file}:${occurrence.line}`;
136
+ if (existing) {
137
+ if (!existing.files.includes(fileLocation)) {
138
+ existing.files.push(fileLocation);
139
+ }
140
+ existing.meta = occurrence.meta;
141
+ } else {
142
+ keyMap.set(occurrence.key, {
143
+ key: occurrence.key,
144
+ meta: occurrence.meta,
145
+ files: [fileLocation]
146
+ });
147
+ }
148
+ }
149
+ return Array.from(keyMap.values());
150
+ }
119
151
  /**
120
152
  * Get count of unique keys.
121
153
  */
122
154
  get uniqueKeyCount() {
123
- return new Set(this.extractedKeys.map((k) => k.key)).size;
155
+ return new Set(this.occurrences.map((k) => k.key)).size;
124
156
  }
125
157
  /**
126
158
  * Merge another store's data into this one.
@@ -132,7 +164,13 @@ var KeyStore = class {
132
164
  for (const file of other.filesWithI18n) {
133
165
  this.filesWithI18n.add(file);
134
166
  }
135
- this.extractedKeys.push(...other.extractedKeys);
167
+ this.occurrences.push(...other.occurrences);
168
+ }
169
+ /**
170
+ * Internal access to occurrences (for merge)
171
+ */
172
+ get occurrencesList() {
173
+ return this.occurrences;
136
174
  }
137
175
  };
138
176
 
@@ -195,6 +233,10 @@ var MANIFEST_FILE_NAME = "i18n-manifest.json";
195
233
  function i18nVitePlugin(options) {
196
234
  const { logger } = options;
197
235
  const store = new KeyStore(logger);
236
+ const syncGlobalState = () => {
237
+ const state = getGlobalState();
238
+ state.extractedKeys = store.extractedKeys;
239
+ };
198
240
  let isBuild = false;
199
241
  let isSSR = false;
200
242
  let projectRoot = "";
@@ -204,7 +246,6 @@ function i18nVitePlugin(options) {
204
246
  configResolved(config) {
205
247
  const state = getGlobalState();
206
248
  state.projectRoot = config.root;
207
- state.extractedKeys = store.extractedKeys;
208
249
  isBuild = config.command === "build";
209
250
  isSSR = !!config.build.ssr;
210
251
  projectRoot = config.root;
@@ -215,6 +256,7 @@ function i18nVitePlugin(options) {
215
256
  }
216
257
  const result = await scan(projectRoot, logger);
217
258
  store.merge(result);
259
+ syncGlobalState();
218
260
  logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);
219
261
  },
220
262
  resolveId(id) {
@@ -261,6 +303,7 @@ export function getManifest() { return _getManifest(); }
261
303
  stripFallbacks: isBuild
262
304
  });
263
305
  store.addFileKeys(filename, result.keys);
306
+ syncGlobalState();
264
307
  if (isBuild && result.code) {
265
308
  return { code: result.code, map: result.map };
266
309
  }
@@ -1,5 +1,5 @@
1
- import { a as ExtractionManifest } from '../types-DTUho6gi.js';
2
- export { C as ChunkManifest, E as ExtractedKey } from '../types-DTUho6gi.js';
1
+ import { a as ExtractionManifest } from '../types-moTgocy7.js';
2
+ export { C as ChunkManifest, E as ExtractedKey } from '../types-moTgocy7.js';
3
3
  import { AstroIntegrationLogger } from 'astro';
4
4
  import { Plugin } from 'vite';
5
5
  import '../types-DNRFj1XA.js';
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getManifest,
3
3
  i18nVitePlugin
4
- } from "../chunk-4IUQAJ6R.js";
4
+ } from "../chunk-3VF2YMJG.js";
5
5
  import "../chunk-4A5BKHFN.js";
6
6
  export {
7
7
  getManifest,
@@ -1,4 +1,4 @@
1
- import { a as ExtractionManifest } from './types-DTUho6gi.js';
1
+ import { a as ExtractionManifest } from './types-moTgocy7.js';
2
2
  import { T as TranslationMeta, C as CompiledTranslations, R as RawTranslations } from './types-DNRFj1XA.js';
3
3
 
4
4
  /**
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { AstroIntegration, MiddlewareHandler, APIContext } from 'astro';
2
- export { F as FallbackBehavior, I as I18nConfig, i as i18n } from './i18n-BBAAVYz_.js';
2
+ export { F as FallbackBehavior, I as I18nConfig, i as i18n } from './i18n-CbSjmzlu.js';
3
3
  import { ExcludePattern } from '@astroscope/excludes';
4
4
  export { R as RawTranslations, T as TranslationMeta } from './types-DNRFj1XA.js';
5
- export { E as ExtractedKey, a as ExtractionManifest } from './types-DTUho6gi.js';
5
+ export { E as ExtractedKey, a as ExtractionManifest } from './types-moTgocy7.js';
6
6
 
7
7
  interface I18nOptions {
8
8
  }
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  i18nVitePlugin
3
- } from "./chunk-4IUQAJ6R.js";
3
+ } from "./chunk-3VF2YMJG.js";
4
4
  import {
5
5
  i18n,
6
6
  runWithContext
@@ -1,7 +1,7 @@
1
- import { a as I18nContext } from './i18n-BBAAVYz_.js';
2
- export { i as i18n } from './i18n-BBAAVYz_.js';
1
+ import { a as I18nContext } from './i18n-CbSjmzlu.js';
2
+ export { i as i18n } from './i18n-CbSjmzlu.js';
3
3
  export { I as I18nClientState } from './types-DNRFj1XA.js';
4
- import './types-DTUho6gi.js';
4
+ import './types-moTgocy7.js';
5
5
 
6
6
  /**
7
7
  * Get the current i18n context
@@ -1,13 +1,13 @@
1
1
  import { T as TranslationMeta } from './types-DNRFj1XA.js';
2
2
 
3
3
  /**
4
- * Extracted metadata for a single translation key
4
+ * Deduplicated translation key with all file locations (used in manifest)
5
5
  */
6
6
  type ExtractedKey = {
7
7
  key: string;
8
8
  meta: TranslationMeta;
9
- file: string;
10
- line: number;
9
+ /** All locations where this key is used (file:line format) */
10
+ files: string[];
11
11
  };
12
12
  /**
13
13
  * Chunk-to-keys mapping produced by Vite plugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroscope/i18n",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "i18n for Astro + React islands — automatic tree-shaking, parallel loading, any translation source",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",