wuchale 0.21.1 → 0.22.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.
@@ -49,13 +49,15 @@ export declare class Transformer<RTCtxT = {}> {
49
49
  visitAwaitExpression: (node: Estree.AwaitExpression) => Message[];
50
50
  visitAssignmentExpression: (node: Estree.BinaryExpression) => Message[];
51
51
  visitAssignmentPattern: (node: Estree.AssignmentPattern) => Message[];
52
- visitExpressionStatement: (node: Estree.ExpressionStatement) => Message[];
53
52
  visitForOfStatement: (node: Estree.ForOfStatement) => Message[];
54
53
  visitForInStatement: (node: Estree.ForInStatement) => Message[];
55
54
  visitForStatement: (node: Estree.ForStatement) => Message[];
56
55
  getMemberChainName: (node: Estree.MemberExpression) => string;
57
56
  getCalleeName: (callee: Estree.Expression | Estree.Super) => string;
57
+ getActualExpression(expr?: Estree.Expression | Estree.TSAsExpression | Estree.TSTypeAssertion): Estree.Expression | null;
58
+ withUpdateTLDetails(visit: (atTopLevel: boolean) => Message[]): Message[];
58
59
  defaultVisitVariableDeclarator: (node: Estree.VariableDeclarator) => Message[];
60
+ visitExpressionStatement: (node: Estree.ExpressionStatement) => Message[];
59
61
  visitVariableDeclarator: (node: Estree.VariableDeclarator) => Message[];
60
62
  visitVariableDeclaration: (node: Estree.VariableDeclaration) => Message[];
61
63
  visitExportNamedDeclaration: (node: Estree.ExportNamedDeclaration) => Message[];
@@ -76,12 +78,8 @@ export declare class Transformer<RTCtxT = {}> {
76
78
  visitTaggedTemplateExpression: (node: Estree.TaggedTemplateExpression) => Message[];
77
79
  visitSwitchStatement: (node: Estree.SwitchStatement) => Message[];
78
80
  visitTryStatement: (node: Estree.TryStatement) => Message[];
79
- visitTSAsExpression: (node: {
80
- expression: Estree.AnyNode;
81
- }) => Message[];
82
- visitTSTypeAssertion: (node: {
83
- expression: Estree.AnyNode;
84
- }) => Message[];
81
+ visitTSAsExpression: (node: Estree.TSAsExpression) => Message[];
82
+ visitTSTypeAssertion: (node: Estree.TSTypeAssertion) => Message[];
85
83
  visitProgram: (node: Estree.Program) => Message[];
86
84
  visitWithCommentDirectives: (node: Estree.AnyNode, func: Function) => any;
87
85
  visit: (node: Estree.AnyNode) => Message[];
@@ -113,16 +113,13 @@ export class Transformer {
113
113
  }
114
114
  const wrapInit = initReactive ? rtConf.reactive.wrapInit : rtConf.plain.wrapInit;
115
115
  const expr = initReactive ? catalogExpr.reactive : catalogExpr.plain;
116
- return `\nconst ${this.currentRtVar} = ${wrapInit(expr)}\n`;
116
+ return `\nconst ${this.currentRtVar} = ${wrapInit(expr)};\n`;
117
117
  };
118
118
  }
119
- fullHeuristicDetails = (detailsBase) => {
120
- const details = { ...this.heuristciDetails, ...detailsBase };
121
- if (details.declaring == null && details.insideProgram) {
122
- details.declaring = 'expression';
123
- }
124
- return details;
125
- };
119
+ fullHeuristicDetails = (detailsBase) => ({
120
+ ...this.heuristciDetails,
121
+ ...detailsBase,
122
+ });
126
123
  getHeuristicMessageType = (msg) => {
127
124
  const msgStr = msg.msgStr.join('\n');
128
125
  if (!msgStr) {
@@ -334,7 +331,6 @@ export class Transformer {
334
331
  ...this.visit(node.left),
335
332
  ...this.visit(node.right),
336
333
  ];
337
- visitExpressionStatement = (node) => this.visit(node.expression);
338
334
  visitForOfStatement = (node) => [
339
335
  ...this.visit(node.left),
340
336
  ...this.visit(node.right),
@@ -390,29 +386,53 @@ export class Transformer {
390
386
  }
391
387
  return `[${callee.type}]`;
392
388
  };
393
- defaultVisitVariableDeclarator = (node) => {
389
+ getActualExpression(expr) {
390
+ if (!expr) {
391
+ return null;
392
+ }
393
+ if (expr.type === 'TSAsExpression' || expr.type === 'TSTypeAssertion') {
394
+ return expr.expression;
395
+ }
396
+ return expr;
397
+ }
398
+ withUpdateTLDetails(visit) {
394
399
  const atTopLevelDefn = this.heuristciDetails.insideProgram && !this.heuristciDetails.declaring;
400
+ const msgs = visit(atTopLevelDefn);
401
+ if (atTopLevelDefn) {
402
+ this.heuristciDetails.topLevelCall = undefined; // reset
403
+ this.heuristciDetails.declaring = undefined;
404
+ }
405
+ return msgs;
406
+ }
407
+ defaultVisitVariableDeclarator = (node) => this.withUpdateTLDetails(topLevel => {
395
408
  if (!node.init) {
396
409
  return [];
397
410
  }
398
- if (atTopLevelDefn) {
399
- if (node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') {
411
+ const init = this.getActualExpression(node.init);
412
+ if (topLevel) {
413
+ if (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') {
400
414
  this.heuristciDetails.declaring = 'function';
401
415
  }
402
416
  else {
403
417
  this.heuristciDetails.declaring = 'variable';
404
- if (node.init.type === 'CallExpression') {
405
- this.heuristciDetails.topLevelCall = this.getCalleeName(node.init.callee);
406
- }
407
418
  }
408
419
  }
409
- const msgs = [...this.visit(node.id), ...this.visit(node.init)];
410
- if (atTopLevelDefn) {
411
- this.heuristciDetails.topLevelCall = undefined; // reset
412
- this.heuristciDetails.declaring = undefined;
420
+ const msgs = this.visit(node.id);
421
+ if (topLevel && this.heuristciDetails.declaring === 'variable' && init.type === 'CallExpression') {
422
+ this.heuristciDetails.topLevelCall = this.getCalleeName(init.callee);
413
423
  }
414
- return msgs;
415
- };
424
+ return [...msgs, ...this.visit(node.init)];
425
+ });
426
+ visitExpressionStatement = (node) => this.withUpdateTLDetails(topLevel => {
427
+ const expr = this.getActualExpression(node.expression);
428
+ if (topLevel) {
429
+ this.heuristciDetails.declaring = 'expression';
430
+ if (expr.type === 'CallExpression') {
431
+ this.heuristciDetails.topLevelCall = this.getCalleeName(expr.callee);
432
+ }
433
+ }
434
+ return this.visit(expr);
435
+ });
416
436
  // for e.g. svelte to surrounded with $derived
417
437
  visitVariableDeclarator = this.defaultVisitVariableDeclarator;
418
438
  visitVariableDeclaration = (node) => node.declarations.flatMap(this.visitVariableDeclarator);
package/dist/adapters.js CHANGED
@@ -71,7 +71,7 @@ export function createHeuristic(opts) {
71
71
  if (msg.details.scope === 'attribute') {
72
72
  return 'message';
73
73
  }
74
- if (msg.details.declaring === 'expression' && !msg.details.funcName) {
74
+ if (msg.details.declaring === 'expression' && msg.details.funcName == null) {
75
75
  return false;
76
76
  }
77
77
  if (!msg.details.call ||
package/dist/ai/index.js CHANGED
@@ -87,10 +87,11 @@ export default class AIQueue {
87
87
  const unTranslated = batch.messages.slice(translated.length);
88
88
  for (const [i, outItem] of translated.entries()) {
89
89
  const item = batch.messages[i];
90
- const sourceComp = item.id.map(i => compileTranslation(i, ''));
90
+ const id = item.translations.get(this.sourceLocale);
91
+ const sourceComp = id.map(i => compileTranslation(i, ''));
91
92
  for (const loc of batch.targetLocales) {
92
93
  const translation = outItem[loc];
93
- if (translation?.length !== item.id.length) {
94
+ if (translation?.length !== id.length) {
94
95
  unTranslated.push(item);
95
96
  break;
96
97
  }
@@ -0,0 +1,32 @@
1
+ export declare function toViteError(err: unknown, adapterKey: string, filename: string): never;
2
+ export declare function trimViteQueries(id: string): string;
3
+ type HotUpdateCtx = {
4
+ file: string;
5
+ server: {
6
+ ws: {
7
+ send: Function;
8
+ };
9
+ moduleGraph: {
10
+ getModulesByFile: Function;
11
+ invalidateModule: Function;
12
+ };
13
+ };
14
+ read: () => string | Promise<string>;
15
+ timestamp: number;
16
+ };
17
+ export declare const wuchale: (configPath?: string, hmrDelayThreshold?: number) => {
18
+ name: string;
19
+ configResolved(config: {
20
+ env: {
21
+ DEV?: boolean;
22
+ };
23
+ }): Promise<void>;
24
+ handleHotUpdate(ctx: HotUpdateCtx): Promise<never[] | undefined>;
25
+ transform: {
26
+ order: "pre";
27
+ handler(code: string, id: string, options?: {
28
+ ssr?: boolean | undefined;
29
+ }): Promise<import("wuchale").TransformOutputCode>;
30
+ };
31
+ };
32
+ export {};
@@ -0,0 +1,70 @@
1
+ import { dirname } from 'node:path';
2
+ import { inspect } from 'node:util';
3
+ import { getConfig } from 'wuchale';
4
+ import { Hub, pluginName } from '../hub.js';
5
+ export function toViteError(err, adapterKey, filename) {
6
+ const prefix = `${adapterKey}: transform failed for ${filename}`;
7
+ // Ensure we always throw an Error instance with a non-empty message so build tools (e.g. Vite)
8
+ // don't end up printing only a generic "error during build:" line.
9
+ if (err instanceof Error) {
10
+ const anyErr = err;
11
+ const frame = typeof anyErr.frame === 'string' ? anyErr.frame : undefined;
12
+ if (!err.message || !err.message.startsWith(prefix)) {
13
+ const details = err.message ? `\n${err.message}` : '';
14
+ const frameText = frame ? `\n\n${frame}` : '';
15
+ err.message = `${prefix}${details}${frameText}`;
16
+ }
17
+ // Preserve useful metadata that some tooling expects.
18
+ if (anyErr.id == null)
19
+ anyErr.id = filename;
20
+ if (anyErr.loc == null && anyErr.start?.line != null && anyErr.start?.column != null) {
21
+ anyErr.loc = { file: filename, line: anyErr.start.line, column: anyErr.start.column };
22
+ }
23
+ throw err;
24
+ }
25
+ const rendered = typeof err === 'string' ? err : inspect(err, { depth: 5, breakLength: 120, maxStringLength: 10_000 });
26
+ throw new Error(`${prefix}\n${rendered}`);
27
+ }
28
+ export function trimViteQueries(id) {
29
+ let queryIndex = id.indexOf('?v=');
30
+ if (queryIndex === -1) {
31
+ queryIndex = id.indexOf('?t=');
32
+ }
33
+ if (queryIndex >= 0 && !id.includes('&', queryIndex)) {
34
+ // trim after this, like ?v=b65b2c3b when it's from node_modules
35
+ id = id.slice(0, queryIndex);
36
+ }
37
+ return id;
38
+ }
39
+ export const wuchale = (configPath, hmrDelayThreshold = 1000) => {
40
+ const hub = new Hub(() => getConfig(configPath), dirname(configPath ?? '.'), hmrDelayThreshold);
41
+ return {
42
+ name: pluginName,
43
+ async configResolved(config) {
44
+ await hub.init(config.env.DEV ? 'dev' : 'build');
45
+ },
46
+ async handleHotUpdate(ctx) {
47
+ const changeInfo = await hub.onFileChange(ctx.file, ctx.read);
48
+ if (!changeInfo) {
49
+ return;
50
+ }
51
+ const invalidatedModules = new Set();
52
+ for (const fileID of changeInfo.invalidate ?? []) {
53
+ for (const module of ctx.server.moduleGraph.getModulesByFile(fileID) ?? []) {
54
+ ctx.server.moduleGraph.invalidateModule(module, invalidatedModules, ctx.timestamp, false);
55
+ }
56
+ }
57
+ if (!changeInfo.sourceTriggered) {
58
+ ctx.server.ws.send({ type: 'full-reload' });
59
+ return [];
60
+ }
61
+ },
62
+ transform: {
63
+ order: 'pre',
64
+ async handler(code, id, options) {
65
+ const [output] = await hub.transform(code, trimViteQueries(id), options?.ssr);
66
+ return output;
67
+ },
68
+ },
69
+ };
70
+ };
@@ -0,0 +1,3 @@
1
+ import { type Config } from '../config.js';
2
+ export declare const checkHelp: string;
3
+ export declare function check(config: Config, root: string, full: boolean): Promise<void>;
@@ -0,0 +1,35 @@
1
+ import {} from '../config.js';
2
+ import { readOnlyFS } from '../fs.js';
3
+ import { Hub } from '../hub.js';
4
+ import { color } from '../log.js';
5
+ export const checkHelp = `
6
+ Usage:
7
+ ${color.cyan('wuchale check {options}')}
8
+
9
+ Options:
10
+ ${color.cyan('--full')} check if there are unextracted and newly obsolete messages in source code as well
11
+ ${color.cyan('--help')}, ${color.cyan('-h')} Show this help
12
+ `;
13
+ const checkErrMsgs = {
14
+ notEquivalent: 'Not equivalent',
15
+ unequalLength: 'Unequal length',
16
+ };
17
+ export async function check(config, root, full) {
18
+ // console.log because if the user invokes this command, they want full info regardless of config
19
+ const hub = new Hub(() => config, root, 0, readOnlyFS);
20
+ await hub.init('cli');
21
+ const { checked, errors, syncs } = await hub.check(full);
22
+ for (const err of errors) {
23
+ console.error(`${color.magenta(err.adapter)}: ${color.red(checkErrMsgs[err.type])}`);
24
+ console.error(` ${color.grey('Source:')} ${err.source}`);
25
+ console.error(` ${color.grey('Target locale:')} ${err.locale}`);
26
+ console.error(` ${color.grey('Translation:')} ${err.translation}`);
27
+ }
28
+ for (const key of syncs) {
29
+ console.error(`${color.red(key)}: Pending changes`);
30
+ }
31
+ if (errors.length > 0 || syncs.length > 0) {
32
+ process.exit(1);
33
+ }
34
+ console.log(color.green(`${checked} items checked. No errors found`));
35
+ }
package/dist/cli/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { dirname } from 'node:path';
2
3
  import { parseArgs } from 'node:util';
3
4
  import { defaultConfigNames, getConfig } from '../config.js';
5
+ import { Hub } from '../hub.js';
4
6
  import { color, logLevels } from '../log.js';
5
- import { extract } from './extract.js';
6
- import { status } from './status.js';
7
+ import { check, checkHelp } from './check.js';
8
+ import { status, statusHelp } from './status.js';
7
9
  const { positionals, values } = parseArgs({
8
10
  options: {
9
11
  config: {
@@ -19,6 +21,14 @@ const { positionals, values } = parseArgs({
19
21
  short: 'w',
20
22
  default: false,
21
23
  },
24
+ json: {
25
+ type: 'boolean',
26
+ default: false,
27
+ },
28
+ full: {
29
+ type: 'boolean',
30
+ default: false,
31
+ },
22
32
  sync: {
23
33
  type: 'boolean',
24
34
  default: false,
@@ -44,30 +54,51 @@ Commands:
44
54
  ${color.grey('[none]')} Extract/compile messages from the codebase into catalogs
45
55
  deleting unused messages if ${color.cyan('--clean')} is specified
46
56
  ${color.cyan('status')} Show current status
57
+ ${color.cyan('check')} Check for errors
47
58
 
48
59
  Options:
49
60
  ${color.cyan('--config')} use another config file instead of ${defaultConfigNames.map(color.cyan).join('|')}
50
- ${color.cyan('--clean')}, ${color.cyan('-c')} (only when no commands) remove unused messages from catalogs
51
- ${color.cyan('--watch')}, ${color.cyan('-w')} (only when no commands) continuously watch for file changes
52
- ${color.cyan('--sync')} (only when no commands) extract sequentially instead of in parallel
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
53
64
  ${color.cyan('--log-level')}, ${color.cyan('-l')} {${Object.keys(logLevels).map(color.cyan)}} (only when no commands) set log level
54
65
  ${color.cyan('--help')}, ${color.cyan('-h')} Show this help
66
+
67
+ You can specify ${color.cyan('--help')} after a sub-command for more.
55
68
  `;
56
69
  async function configRootLocales() {
57
70
  const config = await getConfig(values.config);
58
71
  config.logLevel = values['log-level'];
59
- return [config, values.config ?? process.cwd(), config.locales];
72
+ const root = values.config ? dirname(values.config) : process.cwd();
73
+ return [config, root, config.locales];
74
+ }
75
+ if (cmd === 'status') {
76
+ if (values.help) {
77
+ console.log(statusHelp);
78
+ }
79
+ else {
80
+ const [config, root] = await configRootLocales();
81
+ await status(config, root, values.json);
82
+ }
60
83
  }
61
- if (values.help) {
84
+ else if (cmd === 'check') {
85
+ if (values.help) {
86
+ console.log(checkHelp);
87
+ }
88
+ else {
89
+ const [config, root] = await configRootLocales();
90
+ await check(config, root, values.full);
91
+ }
92
+ }
93
+ else if (values.help) {
62
94
  console.log('wuchale cli');
63
95
  console.log(help.trimEnd());
64
96
  }
65
97
  else if (cmd == null) {
66
98
  const [config, root] = await configRootLocales();
67
- await extract(config, root, values.clean, values.watch, values.sync);
68
- }
69
- else if (cmd === 'status') {
70
- await status(...(await configRootLocales()));
99
+ const hub = new Hub(() => config, root);
100
+ await hub.init('cli');
101
+ await hub.directVisit(values.clean, values.watch, values.sync);
71
102
  }
72
103
  else {
73
104
  console.warn(`${color.yellow('Unknown command')}: ${cmd}`);
@@ -1,2 +1,3 @@
1
1
  import { type Config } from '../config.js';
2
- export declare function status(config: Config, root: string, locales: string[]): Promise<void>;
2
+ export declare const statusHelp: string;
3
+ export declare function status(config: Config, root: string, json: boolean): Promise<void>;
@@ -1,59 +1,47 @@
1
1
  import { relative } from 'node:path';
2
2
  import { getLanguageName } from '../config.js';
3
- import { AdapterHandler } from '../handler/index.js';
4
- import { SharedStates } from '../handler/state.js';
5
- import { color, Logger } from '../log.js';
6
- import { itemIsObsolete, itemIsUrl } from '../storage.js';
7
- async function statCatalog(locale, catalog, urls) {
8
- const stats = { Total: 0, Untranslated: 0, Obsolete: 0 };
9
- for (const item of catalog.values()) {
10
- if (itemIsUrl(item) !== urls) {
11
- continue;
12
- }
13
- stats.Total++;
14
- if (!item.translations.get(locale)[0]) {
15
- stats.Untranslated++;
16
- }
17
- if (itemIsObsolete(item)) {
18
- stats.Obsolete++;
19
- }
20
- }
21
- return stats;
22
- }
23
- export async function status(config, root, locales) {
3
+ import { readOnlyFS } from '../fs.js';
4
+ import { Hub } from '../hub.js';
5
+ import { color } from '../log.js';
6
+ export const statusHelp = `
7
+ Usage:
8
+ ${color.cyan('wuchale status {options}')}
9
+
10
+ Options:
11
+ ${color.cyan('--json')} output info as structured JSON instead of table and text
12
+ ${color.cyan('--help')}, ${color.cyan('-h')} Show this help
13
+ `;
14
+ export async function status(config, root, json) {
24
15
  // console.log because if the user invokes this command, they want full info regardless of config
25
- console.log(`Locales: ${locales.map(l => color.cyan(`${l} (${getLanguageName(l)})`)).join(', ')}`);
26
- const sharedStates = new SharedStates();
27
- for (const [key, adapter] of Object.entries(config.adapters)) {
28
- const handler = new AdapterHandler(adapter, key, config, 'cli', root, new Logger(config.logLevel));
29
- handler.initSharedState(sharedStates);
30
- const state = handler.sharedState;
31
- const loaderPath = await handler.files.getLoaderPath();
32
- console.log(`${color.magenta(key)}: ${color.cyan(state.catalog.size)} messages`);
33
- if (loaderPath) {
16
+ const hub = new Hub(() => config, root, 0, readOnlyFS);
17
+ await hub.init('cli');
18
+ if (json) {
19
+ console.log(JSON.stringify(await hub.status(), null, process.stdout.isTTY ? ' ' : undefined));
20
+ return;
21
+ }
22
+ for (const stat of await hub.status()) {
23
+ console.log(`${color.magenta(stat.key)}:`);
24
+ if (stat.loaders) {
34
25
  console.log(` Loader files:`);
35
- for (const [side, path] of Object.entries(loaderPath)) {
26
+ for (const [side, path] of Object.entries(stat.loaders)) {
36
27
  console.log(` ${color.cyan(side)}: ${color.cyan(relative(root, path))}`);
37
28
  }
38
29
  }
39
30
  else {
40
31
  console.warn(color.yellow(' No loader file found.'));
41
- console.log(` Run ${color.cyan('npx wuchale init')} to initialize.`);
32
+ console.log(` Run ${color.cyan('npx wuchale')} to initialize.`);
42
33
  }
34
+ if (!stat.storage.own) {
35
+ console.log(` Storage shared with ${color.magenta(stat.storage.ownerKey)}`);
36
+ continue;
37
+ }
38
+ console.log(` Messages: ${color.cyan(stat.storage.total)} (${color.cyan(stat.storage.url)} URL)`);
43
39
  const statsData = {};
44
- for (const locale of locales) {
45
- const locName = getLanguageName(locale);
46
- for (const [name, url] of [
47
- [locName, false],
48
- [`${locName} URL`, true],
49
- ]) {
50
- await state.load(locales);
51
- const stats = await statCatalog(locale, state.catalog, url);
52
- if (stats.Total === 0) {
53
- continue;
54
- }
55
- statsData[name] = stats;
56
- }
40
+ for (const det of stat.storage.details) {
41
+ statsData[getLanguageName(det.locale)] = {
42
+ Obsolete: det.obsolete,
43
+ Untranslated: det.untranslated,
44
+ };
57
45
  }
58
46
  console.table(statsData);
59
47
  }
package/dist/fs.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export type FS = {
2
+ read(file: string): string | Promise<string>;
3
+ write(file: string, content: string): void | Promise<void>;
4
+ mkdir(path: string): void | Promise<void>;
5
+ exists(path: string): boolean | Promise<boolean>;
6
+ };
7
+ export declare const defaultFS: FS;
8
+ export declare const readOnlyFS: FS;
package/dist/fs.js ADDED
@@ -0,0 +1,29 @@
1
+ import { mkdir, readFile, statfs, writeFile } from 'node:fs/promises';
2
+ export const defaultFS = {
3
+ async read(file) {
4
+ return await readFile(file, 'utf-8');
5
+ },
6
+ async write(file, content) {
7
+ await writeFile(file, content);
8
+ },
9
+ async mkdir(path) {
10
+ await mkdir(path, { recursive: true });
11
+ },
12
+ async exists(path) {
13
+ try {
14
+ await statfs(path);
15
+ return true;
16
+ }
17
+ catch (err) {
18
+ if (err.code !== 'ENOENT') {
19
+ throw err;
20
+ }
21
+ return false;
22
+ }
23
+ },
24
+ };
25
+ export const readOnlyFS = {
26
+ ...defaultFS,
27
+ write: () => { },
28
+ mkdir: () => { },
29
+ };
@@ -1,10 +1,18 @@
1
1
  import type { Adapter, GlobConf, LoaderPath } from '../adapters.js';
2
2
  import type { CompiledElement } from '../compile.js';
3
+ import type { FS } from '../fs.js';
3
4
  import { type URLManifest } from '../url.js';
5
+ export declare const dataFileName = "data.js";
4
6
  export declare const generatedDir = ".wuchale";
7
+ export type ManifestEntryObj = {
8
+ text: string | string[];
9
+ context?: string;
10
+ isUrl?: boolean;
11
+ };
12
+ export type ManifestEntry = string | string[] | ManifestEntryObj | null;
5
13
  export declare const objKeyLocale: (locale: string) => string;
6
14
  export declare function normalizeSep(path: string): string;
7
- export declare function globConfToArgs(conf: GlobConf, localesDir: string, outDir?: string): [string[], {
15
+ export declare function globConfToArgs(conf: GlobConf, cwd: string, localesDir: string, outDir?: string): [string[], {
8
16
  ignore: string[];
9
17
  }];
10
18
  export declare class Files {
@@ -14,7 +22,7 @@ export declare class Files {
14
22
  loaderPath: LoaderPath;
15
23
  proxyPath: string;
16
24
  proxySyncPath: string;
17
- constructor(adapter: Adapter, key: string, localesDir: string, root: string);
25
+ constructor(adapter: Adapter, key: string, localesDir: string, fs: FS, root: string);
18
26
  getLoaderPaths(): LoaderPath[];
19
27
  getLoaderPath(): Promise<LoaderPath>;
20
28
  getCompiledFilePath(loc: string, id: string | null): string;
@@ -23,8 +31,10 @@ export declare class Files {
23
31
  genProxy(locales: string[], loadIDs: string[], loadIDsImport: string[]): string;
24
32
  genProxySync(locales: string[], loadIDs: string[], loadIDsImport: string[]): string;
25
33
  writeProxies: (locales: string[], loadIDs: string[], loadIDsImport: string[]) => Promise<void>;
26
- init: (locales: string[], ownerKey: string) => Promise<void>;
34
+ init: (ownerKey: string) => Promise<void>;
27
35
  writeUrlFiles: (manifest: URLManifest, fallbackLocale: string) => Promise<void>;
36
+ getManifestFilePath(id: string | null): string;
37
+ writeManifest: (keys: ManifestEntry[], id: string | null) => Promise<void>;
28
38
  writeCatalogModule: (compiledData: CompiledElement[], pluralRule: string | null, locale: string, hmrVersion: number | null, loadID: string | null) => Promise<void>;
29
39
  writeTransformed: (filename: string, content: string) => Promise<void>;
30
40
  getImportLoaderPath(forServer: boolean, relativeTo: string): string;