wuchale 0.23.4 → 0.24.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 (46) hide show
  1. package/dist/adapter-utils/mixed-visitor.d.ts +21 -14
  2. package/dist/adapter-utils/mixed-visitor.js +110 -101
  3. package/dist/adapter-vanilla/index.js +8 -6
  4. package/dist/adapter-vanilla/inertvisitors.d.ts +31 -0
  5. package/dist/adapter-vanilla/inertvisitors.js +86 -0
  6. package/dist/adapter-vanilla/transformer.d.ts +67 -95
  7. package/dist/adapter-vanilla/transformer.js +288 -241
  8. package/dist/adapters.d.ts +16 -12
  9. package/dist/adapters.js +33 -13
  10. package/dist/ai/gemini.js +1 -1
  11. package/dist/ai/index.js +3 -1
  12. package/dist/bundlers/vite.d.ts +1 -1
  13. package/dist/bundlers/vite.js +3 -3
  14. package/dist/cli/check.js +1 -1
  15. package/dist/cli/index.js +12 -5
  16. package/dist/cli/status.js +1 -1
  17. package/dist/config.d.ts +2 -1
  18. package/dist/config.js +2 -2
  19. package/dist/handler/files.d.ts +11 -13
  20. package/dist/handler/files.js +37 -44
  21. package/dist/handler/index.d.ts +7 -4
  22. package/dist/handler/index.js +85 -62
  23. package/dist/handler/state.d.ts +10 -11
  24. package/dist/handler/state.js +40 -28
  25. package/dist/handler/url.d.ts +10 -13
  26. package/dist/handler/url.js +51 -80
  27. package/dist/hub.d.ts +1 -1
  28. package/dist/hub.js +16 -14
  29. package/dist/index.d.ts +3 -3
  30. package/dist/index.js +2 -2
  31. package/dist/load-utils/index.d.ts +6 -8
  32. package/dist/load-utils/index.js +11 -11
  33. package/dist/load-utils/pure.d.ts +2 -2
  34. package/dist/load-utils/server.d.ts +3 -3
  35. package/dist/load-utils/server.js +4 -7
  36. package/dist/pofile.d.ts +5 -5
  37. package/dist/pofile.js +23 -35
  38. package/dist/storage.d.ts +6 -3
  39. package/dist/storage.js +98 -0
  40. package/dist/url.d.ts +12 -10
  41. package/dist/url.js +206 -31
  42. package/package.json +4 -5
  43. package/src/adapter-vanilla/loaders/bundle.js +1 -1
  44. package/src/adapter-vanilla/loaders/server.js +3 -3
  45. package/src/adapter-vanilla/loaders/vite.js +2 -2
  46. package/src/adapter-vanilla/loaders/vite.ssr.js +3 -3
@@ -24,13 +24,14 @@ export type HeuristicDetails = HeuristicDetailsBase & {
24
24
  call?: string | undefined;
25
25
  /** inside an export const ... etc */
26
26
  exported?: boolean | undefined;
27
+ /** object property path */
28
+ property?: string | undefined;
27
29
  };
28
30
  export type MessageType = 'message' | 'url';
29
31
  export type Message = {
30
32
  msgStr: string[];
31
- plural: boolean;
32
33
  context?: string | undefined;
33
- placeholders: [number, string][];
34
+ placeholders: [string, string][];
34
35
  details: HeuristicDetails;
35
36
  type: MessageType;
36
37
  };
@@ -45,6 +46,7 @@ export declare const defaultHeuristicOpts: {
45
46
  ignoreCalls: string[];
46
47
  urlAttribs: string[][];
47
48
  urlCalls: string[];
49
+ urlProps: string[];
48
50
  };
49
51
  export type CreateHeuristicOpts = typeof defaultHeuristicOpts;
50
52
  export declare function createHeuristic(opts: CreateHeuristicOpts): HeuristicFunc;
@@ -52,11 +54,12 @@ export declare function createHeuristic(opts: CreateHeuristicOpts): HeuristicFun
52
54
  export declare const defaultHeuristic: HeuristicFunc;
53
55
  /** Default heuristic which ignores messages outside functions in the `script` scope */
54
56
  export declare const defaultHeuristicFuncOnly: HeuristicFunc;
55
- export declare const defaultGenerateLoadID: (filename: string) => string;
56
57
  export declare class IndexTracker {
57
- indices: Map<string, number>;
58
- nextIndex: number;
58
+ #private;
59
+ constructor(bypassHas: boolean);
59
60
  get: (msgStr: string) => number;
61
+ has: (msgStr: string) => boolean;
62
+ getAll: () => MapIterator<[string, number]>;
60
63
  }
61
64
  export type GlobConf = string | string[] | {
62
65
  include: string | string[];
@@ -66,8 +69,8 @@ export type RuntimeExpr = {
66
69
  plain: string;
67
70
  reactive: string;
68
71
  };
69
- export type UrlMatcher = (url: string) => string | null | undefined;
70
- type TransformCtx = {
72
+ export type UrlMatcher = (url: string) => readonly [number, string[]] | null;
73
+ export type TransformCtx = {
71
74
  content: string;
72
75
  filename: string;
73
76
  index: IndexTracker;
@@ -111,16 +114,17 @@ export type URLConf = {
111
114
  patterns?: string[];
112
115
  localize?: boolean | string;
113
116
  };
117
+ export type LoadGroupPatt = string | string[];
114
118
  export type AdapterPassThruOpts = {
115
119
  sourceLocale?: string;
116
120
  files: GlobConf;
117
121
  storage: StorageFactory;
118
- /** if writing transformed code to a directory is desired, specify this */
119
- outDir?: string;
120
- granularLoad: boolean;
121
- bundleLoad: boolean;
122
+ loading: {
123
+ direct: boolean;
124
+ granular: boolean;
125
+ group: LoadGroupPatt[];
126
+ };
122
127
  url?: URLConf;
123
- generateLoadID: (filename: string) => string;
124
128
  runtime: RuntimeConf;
125
129
  };
126
130
  export type Adapter = AdapterPassThruOpts & {
package/dist/adapters.js CHANGED
@@ -5,7 +5,6 @@ export function newMessage(init) {
5
5
  }
6
6
  return {
7
7
  msgStr: init.msgStr,
8
- plural: init.plural ?? false,
9
8
  placeholders: init.placeholders ?? [],
10
9
  type: init.type ?? 'message',
11
10
  context: init.context,
@@ -23,6 +22,7 @@ export const defaultHeuristicOpts = {
23
22
  ignoreCalls: ['fetch'],
24
23
  urlAttribs: [['a', 'href']],
25
24
  urlCalls: [],
25
+ urlProps: ['href', 'link', 'url'],
26
26
  };
27
27
  export function createHeuristic(opts) {
28
28
  return msg => {
@@ -46,6 +46,13 @@ export function createHeuristic(opts) {
46
46
  }
47
47
  }
48
48
  }
49
+ if (msg.details.property) {
50
+ for (const prop of opts.urlProps) {
51
+ if (msg.details.property === prop || msg.details.property?.endsWith(`.${prop}`)) {
52
+ return 'url';
53
+ }
54
+ }
55
+ }
49
56
  if (msg.details.attribute) {
50
57
  for (const [element, attrib] of opts.urlAttribs) {
51
58
  if (msg.details.element === element && msg.details.attribute === attrib) {
@@ -61,11 +68,19 @@ export function createHeuristic(opts) {
61
68
  return 'message';
62
69
  }
63
70
  // script and attribute
64
- // ignore:
65
- // non-letter beginnings
66
- // lower-case English letter beginnings
67
- // containing only upper-case English and non-letters
68
- if (!/\p{L}/u.test(msgStr[0]) || /[a-z]/.test(msgStr[0]) || /^([A-Z]|\P{L})+$/u.test(msgStr)) {
71
+ if (/^([A-Z]|\P{L})+$/u.test(msgStr)) {
72
+ // only upper-case English and non-letters
73
+ return false;
74
+ }
75
+ if (/^\{\d+\}/.test(msgStr)) {
76
+ // template literals that begin with a placeholder expression
77
+ if (!/\s\p{L}/u.test(msgStr)) {
78
+ // should contain spaces and letters
79
+ return false;
80
+ }
81
+ }
82
+ else if (/[a-z]|\P{L}/u.test(msgStr[0])) {
83
+ // ignore non-letter and lower-case English beginnings
69
84
  return false;
70
85
  }
71
86
  if (msg.details.scope === 'attribute') {
@@ -90,18 +105,23 @@ export const defaultHeuristicFuncOnly = msg => {
90
105
  }
91
106
  return false;
92
107
  };
93
- export const defaultGenerateLoadID = (filename) => filename.replace(/[^a-zA-Z0-9_]+/g, '_');
94
108
  export class IndexTracker {
95
- indices = new Map();
96
- nextIndex = 0;
109
+ #indices = new Map();
110
+ #nextIndex = 0;
111
+ #bypassHas;
112
+ constructor(bypassHas) {
113
+ this.#bypassHas = bypassHas;
114
+ }
97
115
  get = (msgStr) => {
98
- let index = this.indices.get(msgStr);
116
+ let index = this.#indices.get(msgStr);
99
117
  if (index != null) {
100
118
  return index;
101
119
  }
102
- index = this.nextIndex;
103
- this.indices.set(msgStr, index);
104
- this.nextIndex++;
120
+ index = this.#nextIndex;
121
+ this.#indices.set(msgStr, index);
122
+ this.#nextIndex++;
105
123
  return index;
106
124
  };
125
+ has = (msgStr) => this.#bypassHas || this.#indices.has(msgStr);
126
+ getAll = () => this.#indices.entries();
107
127
  }
package/dist/ai/gemini.js CHANGED
@@ -15,7 +15,7 @@ function prepareData(content, instruction, think) {
15
15
  },
16
16
  };
17
17
  }
18
- export function gemini({ apiKey = 'env', model = 'gemini-2.5-flash', group = {}, batchSize = 50, think = false, parallel = 4, } = {}) {
18
+ export function gemini({ apiKey = 'env', model = 'gemini-3.5-flash', group = {}, batchSize = 50, think = false, parallel = 4, } = {}) {
19
19
  if (apiKey === 'env') {
20
20
  apiKey = process.env['GEMINI_API_KEY'] ?? '';
21
21
  }
package/dist/ai/index.js CHANGED
@@ -43,7 +43,9 @@ Respond with a JSON array matching the order of the input items. Each element is
43
43
  Output schema:
44
44
  ${JSON.stringify(outputSchema)}
45
45
 
46
- Respond ONLY with raw compact JSON. Do not wrap it in markdown code fences or add any other text.
46
+ CRITICAL!:
47
+ - ALWAYS Respond with a raw compact JSON array even if there is only one item.
48
+ - NEVER wrap it in markdown code fences or any other surrounding text.
47
49
  `.trim();
48
50
  // implements a queue for a sequential translation useful for vite's transform during dev
49
51
  // as vite can do async transform
@@ -31,7 +31,7 @@ export declare const wuchale: ({ configPath, hmrDelayThreshold, trimQueryParams
31
31
  order: "pre";
32
32
  handler(code: string, id: string, options?: {
33
33
  ssr?: boolean | undefined;
34
- }): Promise<import("wuchale").TransformOutputCode>;
34
+ }): Promise<import("../adapters.js").TransformOutputCode>;
35
35
  };
36
36
  };
37
37
  export {};
@@ -1,5 +1,5 @@
1
1
  import { dirname } from 'node:path';
2
- import { getConfig } from 'wuchale';
2
+ import { getConfig } from '../config.js';
3
3
  import { Hub, pluginName } from '../hub.js';
4
4
  export function toViteError(err, adapterKey, filename) {
5
5
  const prefix = `${adapterKey}: transform failed for ${filename}`;
@@ -49,11 +49,11 @@ export function trimViteQueries(id, trimParams) {
49
49
  }
50
50
  export const wuchale = ({ configPath, hmrDelayThreshold = 1000, trimQueryParams } = {}) => {
51
51
  let hub;
52
- const trimParams = new Set([...(trimQueryParams ?? []), 'v', 't', 'sentry-auto-wrap']);
52
+ const trimParams = new Set([...(trimQueryParams ?? []), 'v', 't', 'sentry-auto-wrap', 'tsr-split']);
53
53
  return {
54
54
  name: pluginName,
55
55
  async configResolved(config) {
56
- hub = await Hub.create(config.env.DEV ? 'dev' : 'build', () => getConfig(configPath), dirname(configPath ?? '.'), hmrDelayThreshold, undefined, toViteError);
56
+ hub = await Hub.create(config.env.DEV ? 'dev' : 'build', () => getConfig(configPath), dirname(configPath ?? '.'), [], hmrDelayThreshold, undefined, toViteError);
57
57
  },
58
58
  async handleHotUpdate(ctx) {
59
59
  const changeInfo = await hub.onFileChange(ctx.file, ctx.read);
package/dist/cli/check.js CHANGED
@@ -15,7 +15,7 @@ const checkErrMsgs = {
15
15
  };
16
16
  export async function check(config, root, full) {
17
17
  // disable ai as this is a check, not persisted
18
- const hub = await Hub.create('cli', () => ({ ...config, ai: null }), root, 0, readOnlyFS);
18
+ const hub = await Hub.create('cli', () => ({ ...config, ai: null }), root, [], 0, readOnlyFS);
19
19
  const { checked, errors, syncs } = await hub.check(full);
20
20
  // console.log because if the user invokes this command, they want full info regardless of config
21
21
  for (const err of errors) {
package/dist/cli/index.js CHANGED
@@ -38,6 +38,9 @@ const { positionals, values } = parseArgs({
38
38
  short: 'l',
39
39
  default: 'info',
40
40
  },
41
+ modify: {
42
+ type: 'string',
43
+ },
41
44
  help: {
42
45
  type: 'boolean',
43
46
  short: 'h',
@@ -57,10 +60,11 @@ Commands:
57
60
  ${color.cyan('check')} Check for errors
58
61
 
59
62
  Options:
60
- ${color.cyan('--config')} use another config file instead of ${defaultConfigNames.map(color.cyan).join('|')}
61
- ${color.cyan('--clean')}, ${color.cyan('-c')} remove unused messages from catalogs
62
- ${color.cyan('--watch')}, ${color.cyan('-w')} continuously watch for file changes
63
- ${color.cyan('--sync')} extract sequentially instead of in parallel
63
+ ${color.cyan('--config')} Use another config file instead of ${defaultConfigNames.map(color.cyan).join('|')}
64
+ ${color.cyan('--clean')}, ${color.cyan('-c')} Remove unused messages from catalogs
65
+ ${color.cyan('--watch')}, ${color.cyan('-w')} Continuously watch for file changes
66
+ ${color.cyan('--sync')} Extract sequentially instead of in parallel
67
+ ${color.cyan('--modify a1,a2')} Modify files in place for adapters ${color.cyan('a1')}, ${color.cyan('a2')}, etc.
64
68
  ${color.cyan('--log-level')}, ${color.cyan('-l')} {${Object.keys(logLevels).map(color.cyan)}} (only when no commands) set log level
65
69
  ${color.cyan('--help')}, ${color.cyan('-h')} Show this help
66
70
 
@@ -96,7 +100,10 @@ else if (values.help) {
96
100
  }
97
101
  else if (cmd == null) {
98
102
  const [config, root] = await configRootLocales();
99
- const hub = await Hub.create('cli', () => config, root);
103
+ const hub = await Hub.create('cli', () => config, root, values.modify
104
+ ?.split(',')
105
+ ?.map(a => a.trim())
106
+ ?.filter(a => a) ?? []);
100
107
  await hub.directVisit(values.clean, values.watch, values.sync);
101
108
  }
102
109
  else {
@@ -13,7 +13,7 @@ Options:
13
13
  `;
14
14
  export async function status(config, root, json) {
15
15
  // console.log because if the user invokes this command, they want full info regardless of config
16
- const hub = await Hub.create('cli', () => config, root, 0, readOnlyFS);
16
+ const hub = await Hub.create('cli', () => config, root, [], 0, readOnlyFS);
17
17
  if (json) {
18
18
  console.log(JSON.stringify(await hub.status(), null, process.stdout.isTTY ? ' ' : undefined));
19
19
  return;
package/dist/config.d.ts CHANGED
@@ -8,9 +8,10 @@ export type ConfigPartial = {
8
8
  ai: AI | null;
9
9
  logLevel: LogLevel;
10
10
  };
11
+ export type DevMode = false | 'read' | 'add' | 'refs' | 'clean';
11
12
  export type Config = ConfigPartial & {
12
13
  adapters: Record<string, Adapter>;
13
- hmr: boolean;
14
+ dev: DevMode;
14
15
  };
15
16
  export type DeepPartial<T> = {
16
17
  [K in keyof T]?: T[K] extends (infer A)[] ? DeepPartial<A>[] : T[K] extends (...args: any[]) => any ? T[K] : T[K] extends object ? DeepPartial<T[K]> : T[K];
package/dist/config.js CHANGED
@@ -5,14 +5,14 @@ export const defaultConfig = {
5
5
  fallback: {},
6
6
  localesDir: 'src/locales',
7
7
  adapters: {},
8
- hmr: true,
8
+ dev: 'refs',
9
9
  ai: defaultGemini,
10
10
  logLevel: 'info',
11
11
  };
12
12
  function deepFill(target, defaults) {
13
13
  for (const [key, def] of Object.entries(defaults)) {
14
14
  const value = target[key];
15
- if (Array.isArray(value)) {
15
+ if (value !== undefined && (typeof value !== 'object' || Array.isArray(value))) {
16
16
  continue;
17
17
  }
18
18
  if (!def || Array.isArray(def) || typeof def !== 'object') {
@@ -1,9 +1,10 @@
1
- import type { Adapter, GlobConf, LoaderPath } from '../adapters.js';
1
+ import type { Adapter, GlobConf, LoaderPath, LoadGroupPatt } from '../adapters.js';
2
2
  import type { CompiledElement } from '../compile.js';
3
3
  import type { FS } from '../fs.js';
4
4
  import type { URLManifest } from '../url.js';
5
5
  export declare const dataFileName = "data.js";
6
6
  export declare const generatedDir = ".wuchale";
7
+ export declare const defaultLoadID = 0;
7
8
  export type ManifestEntryObj = {
8
9
  text: string | string[];
9
10
  context?: string | undefined;
@@ -22,25 +23,22 @@ export type FilesOptsCreate = FilesOptsCreatePass & {
22
23
  };
23
24
  export declare const objKeyLocale: (locale: string) => string;
24
25
  export declare function normalizeSep(path: string): string;
25
- export declare function globConfToArgs(conf: GlobConf, root: string, localesDir: string, outDir?: string): [string[], {
26
- ignore: string[];
27
- }];
26
+ export declare function globConfToArgs(conf: GlobConf, localesDir: string): [string[], string[]];
28
27
  export declare function getLoaderPath(adapter: Adapter, key: string, localesDirAbs: string, root: string, fs: FS): Promise<LoaderPath>;
29
28
  export declare class Files {
30
29
  #private;
31
30
  readonly loaderPath: LoaderPath;
32
31
  private constructor();
33
- getCompiledFilePath(loc: string, id: string | null): string;
32
+ getCompiledFilePath(loc: string, id: number | null): string;
34
33
  getImportPath(filename: string, importer?: string): string;
35
- genProxyContent(catalogs: string[], loadIDs: string[], syncImports?: string[]): string;
36
- genProxy(locales: string[], loadIDs: string[], loadIDsImport: string[]): string;
37
- genProxySync(locales: string[], loadIDs: string[], loadIDsImport: string[]): string;
38
- writeProxies: (locales: string[], loadIDs: string[], loadIDsImport: string[]) => Promise<void>;
34
+ genProxyContent(catalogs: string[], patterns: LoadGroupPatt[], locales: string[], syncImports?: string[]): string;
35
+ genProxy(locales: string[], patterns: LoadGroupPatt[]): string;
36
+ genProxySync(locales: string[], patterns: LoadGroupPatt[]): string;
37
+ writeProxies: (locales: string[], groupPatterns: LoadGroupPatt[]) => Promise<void>;
39
38
  static create: (opts: FilesOptsCreate) => Promise<Files>;
40
39
  writeUrlFiles: (manifest: URLManifest, fallbackLocale: string) => Promise<void>;
41
- getManifestFilePath(id: string | null): string;
42
- writeManifest: (keys: ManifestEntry[], id: string | null) => Promise<void>;
43
- writeCatalogModule: (compiledData: CompiledElement[], pluralRule: string | null, locale: string, hmrVersion: number | null, loadID: string | null) => Promise<void>;
44
- writeTransformed: (filename: string, content: string) => Promise<void>;
40
+ getManifestFilePath(id: number | null): string;
41
+ writeManifest: (keys: ManifestEntry[], id: number | null) => Promise<void>;
42
+ writeCatalogModule: (compiledData: CompiledElement[], pluralRule: string | null, locale: string, hmrVersion: number | null, loadID: number | null) => Promise<void>;
45
43
  getImportLoaderPath(forServer: boolean, relativeTo: string): string;
46
44
  }
@@ -2,6 +2,7 @@ import { dirname, relative, resolve } from 'node:path';
2
2
  import { platform } from 'node:process';
3
3
  export const dataFileName = 'data.js';
4
4
  export const generatedDir = '.wuchale';
5
+ export const defaultLoadID = 0;
5
6
  export const objKeyLocale = (locale) => (locale.includes('-') ? `'${locale}'` : locale);
6
7
  export function normalizeSep(path) {
7
8
  if (platform !== 'win32') {
@@ -9,13 +10,10 @@ export function normalizeSep(path) {
9
10
  }
10
11
  return path.replaceAll('\\', '/');
11
12
  }
12
- export function globConfToArgs(conf, root, localesDir, outDir) {
13
+ export function globConfToArgs(conf, localesDir) {
13
14
  let patterns = [];
14
15
  // ignore generated files
15
- const options = { ignore: [`${localesDir}/**/*`], cwd: root };
16
- if (outDir) {
17
- options.ignore.push(outDir);
18
- }
16
+ const ignore = [`${localesDir}/**/*`];
19
17
  if (typeof conf === 'string') {
20
18
  patterns = [conf];
21
19
  }
@@ -30,13 +28,13 @@ export function globConfToArgs(conf, root, localesDir, outDir) {
30
28
  patterns = conf.include;
31
29
  }
32
30
  if (typeof conf.ignore === 'string') {
33
- options.ignore.push(conf.ignore);
31
+ ignore.push(conf.ignore);
34
32
  }
35
33
  else {
36
- options.ignore.push(...conf.ignore);
34
+ ignore.push(...conf.ignore);
37
35
  }
38
36
  }
39
- return [patterns.map(normalizeSep), options];
37
+ return [patterns.map(normalizeSep), ignore];
40
38
  }
41
39
  export async function getLoaderPath(adapter, key, localesDirAbs, root, fs) {
42
40
  const loaderPathHead = resolve(localesDirAbs, `${key}.loader`);
@@ -107,7 +105,7 @@ export class Files {
107
105
  }
108
106
  getCompiledFilePath(loc, id) {
109
107
  const ownerKey = this.#opts.ownerKey;
110
- return resolve(this.#opts.localesDirAbs, generatedDir, `${ownerKey}.${id ?? ownerKey}.${loc}.compiled.js`);
108
+ return resolve(this.#opts.localesDirAbs, generatedDir, `${ownerKey}.${id ?? defaultLoadID}.${loc}.compiled.js`);
111
109
  }
112
110
  getImportPath(filename, importer) {
113
111
  const relTo = importer ? resolve(this.#opts.root, importer) : filename;
@@ -118,48 +116,51 @@ export class Files {
118
116
  return filename;
119
117
  }
120
118
  // typed to work regardless of user's noUncheckedIndexedAccess setting in tsconfig
121
- genProxyContent(catalogs, loadIDs, syncImports) {
119
+ genProxyContent(catalogs, patterns, locales, syncImports) {
122
120
  const baseType = 'import("wuchale/runtime").CatalogModule';
121
+ const byLocale = catalogs.map((locCats, i) => `${objKeyLocale(locales[i])}: ${locCats}`).join(',');
123
122
  return `
124
123
  ${syncImports?.join('\n') ?? ''}
125
124
  /** @typedef {${syncImports ? baseType : `() => Promise<${baseType}>`}} CatalogMod */
126
- /** @typedef {{[locale: string]: CatalogMod}} KeyCatalogs */
127
- /** @type {{[loadID: string]: KeyCatalogs}} */
128
- const catalogs = {${catalogs.join(',')}}
129
- export const loadCatalog = (/** @type {string} */ loadID, /** @type {string} */ locale) => {
130
- return /** @type {CatalogMod} */ (/** @type {KeyCatalogs} */ (catalogs[loadID])[locale])${syncImports ? '' : '()'}
125
+ /** @type {{[locale: string]: CatalogMod[]}} */
126
+ const catalogs = {${byLocale}}
127
+ export const loadCatalog = (/** @type {number} */ loadID, /** @type {string} */ locale) => {
128
+ return /** @type {CatalogMod} */ (/** @type {CatalogMod[]} */ (catalogs[locale])[loadID])${syncImports ? '' : '()'}
131
129
  }
132
- export const loadIDs = ['${loadIDs.join("', '")}']
130
+ export const loadCount = ${patterns.length}
131
+ // not essential. in case it is needed and for debugging
132
+ export const patterns = ${JSON.stringify(patterns)}
133
133
  `;
134
134
  }
135
- genProxy(locales, loadIDs, loadIDsImport) {
135
+ genProxy(locales, patterns) {
136
136
  const imports = [];
137
- for (const [i, id] of loadIDs.entries()) {
138
- const importsByLocale = [];
139
- for (const loc of locales) {
140
- importsByLocale.push(`${objKeyLocale(loc)}: () => import('${this.getImportPath(this.getCompiledFilePath(loc, loadIDsImport[i]))}')`);
137
+ for (const loc of locales) {
138
+ const importsForLocale = [];
139
+ for (const [loadID] of patterns.entries()) {
140
+ importsForLocale.push(`() => import('${this.getImportPath(this.getCompiledFilePath(loc, loadID))}')`);
141
141
  }
142
- imports.push(`${id}: {${importsByLocale.join(',')}}`);
142
+ imports.push(`[${importsForLocale.join(',')}]`);
143
143
  }
144
- return this.genProxyContent(imports, loadIDs);
144
+ return this.genProxyContent(imports, patterns, locales);
145
145
  }
146
- genProxySync(locales, loadIDs, loadIDsImport) {
146
+ genProxySync(locales, patterns) {
147
147
  const imports = [];
148
148
  const object = [];
149
- for (const [il, id] of loadIDs.entries()) {
150
- const importedByLocale = [];
151
- for (const [i, loc] of locales.entries()) {
152
- const locKey = `_w_c_${id}_${i}_`;
153
- imports.push(`import * as ${locKey} from '${this.getImportPath(this.getCompiledFilePath(loc, loadIDsImport[il]))}'`);
154
- importedByLocale.push(`${objKeyLocale(loc)}: ${locKey}`);
149
+ for (const [i, loc] of locales.entries()) {
150
+ const importedForLocale = [];
151
+ for (const [loadID] of patterns.entries()) {
152
+ const locKey = `_w_c_${loadID}_${i}_`;
153
+ imports.push(`import * as ${locKey} from '${this.getImportPath(this.getCompiledFilePath(loc, loadID))}'`);
154
+ importedForLocale.push(locKey);
155
155
  }
156
- object.push(`${id}: {${importedByLocale.join(',')}}`);
156
+ object.push(`[${importedForLocale.join(',')}]`);
157
157
  }
158
- return this.genProxyContent(object, loadIDs, imports);
158
+ return this.genProxyContent(object, patterns, locales, imports);
159
159
  }
160
- writeProxies = async (locales, loadIDs, loadIDsImport) => {
161
- await this.#opts.fs.write(this.#proxyPath, this.genProxy(locales, loadIDs, loadIDsImport));
162
- await this.#opts.fs.write(this.#proxySyncPath, this.genProxySync(locales, loadIDs, loadIDsImport));
160
+ writeProxies = async (locales, groupPatterns) => {
161
+ const allPatterns = [this.#opts.key, ...groupPatterns];
162
+ await this.#opts.fs.write(this.#proxyPath, this.genProxy(locales, allPatterns));
163
+ await this.#opts.fs.write(this.#proxySyncPath, this.genProxySync(locales, allPatterns));
163
164
  };
164
165
  static create = async (opts) => {
165
166
  const { adapter, key, localesDirAbs, root, fs } = opts;
@@ -211,7 +212,7 @@ export class Files {
211
212
  };
212
213
  getManifestFilePath(id) {
213
214
  const ownerKey = this.#opts.ownerKey;
214
- return resolve(this.#opts.localesDirAbs, generatedDir, `${ownerKey}.${id ?? ownerKey}.manifest.js`);
215
+ return resolve(this.#opts.localesDirAbs, generatedDir, `${ownerKey}.${id ?? defaultLoadID}.manifest.js`);
215
216
  }
216
217
  writeManifest = async (keys, id) => {
217
218
  const content = `/** @type {(string | string[] | {text: string | string[], context?: string, isUrl?: boolean})[]} */\n` +
@@ -243,14 +244,6 @@ export class Files {
243
244
  }
244
245
  await this.#opts.fs.write(this.getCompiledFilePath(locale, loadID), module);
245
246
  };
246
- writeTransformed = async (filename, content) => {
247
- if (!this.#opts.adapter.outDir) {
248
- return;
249
- }
250
- const fname = resolve(`${this.#opts.adapter.outDir}/${filename}`);
251
- await this.#opts.fs.mkdir(dirname(fname));
252
- await this.#opts.fs.write(fname, content);
253
- };
254
247
  getImportLoaderPath(forServer, relativeTo) {
255
248
  return this.getImportPath(forServer ? this.#opts.loaderPath.server : this.#opts.loaderPath.client, relativeTo);
256
249
  }
@@ -1,7 +1,7 @@
1
1
  import { type Matcher } from 'picomatch';
2
2
  import type { Adapter, Message, TransformOutputCode } from '../adapters.js';
3
3
  import AIQueue from '../ai/index.js';
4
- import type { ConfigPartial } from '../config.js';
4
+ import type { ConfigPartial, DevMode } from '../config.js';
5
5
  import type { Logger } from '../log.js';
6
6
  import { type FileRef, type Item } from '../storage.js';
7
7
  import { Files, type FilesOptsCreatePass } from './files.js';
@@ -12,14 +12,17 @@ type TrackedRefs = Map<string, {
12
12
  ref: FileRef;
13
13
  used: number;
14
14
  }>;
15
- /** return two arrays: the corresponding one, and the one to import from in the case of shared catalogs */
16
- export declare function getLoadIDs(adapter: Adapter, key: string, ownerKey: string, granularStates: Iterable<GranularState>, sourceLocale: string): [string[], string[]];
15
+ export declare function getLoadIDs(adapter: Adapter, granularStates: Iterable<GranularState>, sourceLocale: string): number[];
16
+ export declare const newItemsAllowed: (mode: Mode, devMode: DevMode) => boolean;
17
+ export declare function getFallbackChains(fallbackConf: Record<string, string>, locales: string[], sourceLocale: string): Map<string, string[]>;
17
18
  type HandlerOptsCreate = FilesOptsCreatePass & {
18
19
  config: ConfigPartial;
19
20
  mode: Mode;
20
21
  log: Logger;
21
22
  sourceLocale: string;
22
23
  sharedState: SharedState;
24
+ devMode: DevMode;
25
+ modifyInplace: boolean;
23
26
  };
24
27
  export declare class AdapterHandler {
25
28
  #private;
@@ -40,7 +43,7 @@ export declare class AdapterHandler {
40
43
  compile: (hmrVersion?: number) => Promise<void>;
41
44
  saveStorageCompile: () => Promise<void>;
42
45
  writeCompiled: (loc: string, hmrVersion?: number) => Promise<void>;
43
- getCompiledFallback(index: number, loc: string): string | import("../compile.js").CompositePayload[];
46
+ getCompiledFallback(index: number, locale: string): string | import("../compile.js").CompositePayload[];
44
47
  popTrackedRefs: (filename: string) => TrackedRefs;
45
48
  updateRef: (item: Item, key: string, filename: string, msgInfo: Message, trackedRefrences: TrackedRefs) => boolean;
46
49
  cleanTrackedRefs: (trackedRefs: TrackedRefs) => {