wuchale 0.18.7 → 0.18.9
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.
- package/dist/adapter-utils/mixed-visitor.js +1 -1
- package/dist/adapter-vanilla/index.d.ts +2 -2
- package/dist/adapter-vanilla/index.js +1 -1
- package/dist/adapter-vanilla/transformer.d.ts +7 -7
- package/dist/adapter-vanilla/transformer.js +15 -15
- package/dist/adapters.d.ts +7 -7
- package/dist/adapters.js +4 -3
- package/dist/ai/gemini.d.ts +2 -2
- package/dist/ai/gemini.js +2 -2
- package/dist/cli/extract.js +4 -1
- package/dist/cli/index.js +3 -0
- package/dist/config.d.ts +10 -8
- package/dist/config.js +5 -4
- package/dist/handler.d.ts +7 -5
- package/dist/handler.js +52 -31
- package/dist/load-utils/server.js +3 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/url.d.ts +3 -3
- package/package.json +2 -2
|
@@ -8,5 +8,5 @@ export type VanillaArgs = AdapterArgs<LoadersAvailable>;
|
|
|
8
8
|
export declare function getDefaultLoaderPath(loader: LoaderChoice<LoadersAvailable>, bundle: boolean): string | {
|
|
9
9
|
client: string;
|
|
10
10
|
server: string;
|
|
11
|
-
};
|
|
12
|
-
export declare const adapter: (args?: VanillaArgs) => Adapter;
|
|
11
|
+
} | null;
|
|
12
|
+
export declare const adapter: (args?: Partial<VanillaArgs>) => Adapter;
|
|
@@ -6,7 +6,7 @@ import { type RuntimeVars, type CommentDirectives } from "../adapter-utils/index
|
|
|
6
6
|
export declare const scriptParseOptions: Estree.Options;
|
|
7
7
|
export declare function scriptParseOptionsWithComments(): [Estree.Options, Estree.Comment[][]];
|
|
8
8
|
export declare function parseScript(content: string): [Estree.Program, Estree.Comment[][]];
|
|
9
|
-
type InitRuntimeFunc = (file: string, funcName
|
|
9
|
+
type InitRuntimeFunc = (file: string, funcName?: string, parentFunc?: string, additional?: object) => string | undefined;
|
|
10
10
|
export declare class Transformer {
|
|
11
11
|
index: IndexTracker;
|
|
12
12
|
heuristic: HeuristicFunc;
|
|
@@ -21,19 +21,19 @@ export declare class Transformer {
|
|
|
21
21
|
vars: () => RuntimeVars;
|
|
22
22
|
commentDirectives: CommentDirectives;
|
|
23
23
|
insideProgram: boolean;
|
|
24
|
-
declaring
|
|
24
|
+
declaring?: ScriptDeclType;
|
|
25
25
|
currentFuncNested: boolean;
|
|
26
|
-
currentFuncDef
|
|
26
|
+
currentFuncDef?: string;
|
|
27
27
|
currentCall: string;
|
|
28
|
-
currentTopLevelCall
|
|
28
|
+
currentTopLevelCall?: string;
|
|
29
29
|
/** .start of the first statements in their respective parents, to put the runtime init before */
|
|
30
30
|
realBodyStarts: Set<number>;
|
|
31
31
|
/** for subclasses. right now for svelte, if in <script module> */
|
|
32
32
|
additionalState: object;
|
|
33
|
-
constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, patterns: CodePattern[], catalogExpr: CatalogExpr, rtConf: RuntimeConf, matchUrl:
|
|
33
|
+
constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, patterns: CodePattern[], catalogExpr: CatalogExpr, rtConf: RuntimeConf, matchUrl: UrlMatcher, rtBaseVars?: string[]);
|
|
34
34
|
fullHeuristicDetails: (detailsBase: HeuristicDetailsBase) => HeuristicDetails;
|
|
35
35
|
getHeuristicMessageType: (msg: Message) => HeuristicResultChecked;
|
|
36
|
-
checkHeuristic: (msgStr: string, detailsBase: HeuristicDetailsBase) => [
|
|
36
|
+
checkHeuristic: (msgStr: string, detailsBase: HeuristicDetailsBase) => [MessageType, Message] | [false, null];
|
|
37
37
|
visitLiteral: (node: Estree.Literal, heuristicDetailsBase?: HeuristicDetailsBase) => Message[];
|
|
38
38
|
visitArrayExpression: (node: Estree.ArrayExpression) => Message[];
|
|
39
39
|
visitSequenceExpression: (node: Estree.SequenceExpression) => Message[];
|
|
@@ -67,7 +67,7 @@ export declare class Transformer {
|
|
|
67
67
|
visitExportDefaultDeclaration: (node: Estree.ExportNamedDeclaration) => Message[];
|
|
68
68
|
visitStatementsNSaveRealBodyStart: (nodes: (Estree.Statement | Estree.ModuleDeclaration)[]) => Message[];
|
|
69
69
|
getRealBodyStart: (nodes: (Estree.Statement | Estree.ModuleDeclaration)[]) => number | undefined;
|
|
70
|
-
visitFunctionBody: (node: Estree.BlockStatement | Estree.Expression, name
|
|
70
|
+
visitFunctionBody: (node: Estree.BlockStatement | Estree.Expression, name?: string, end?: number) => Message[];
|
|
71
71
|
visitFunctionDeclaration: (node: Estree.FunctionDeclaration) => Message[];
|
|
72
72
|
visitArrowFunctionExpression: (node: Estree.ArrowFunctionExpression) => Message[];
|
|
73
73
|
visitFunctionExpression: (node: Estree.FunctionExpression) => Message[];
|
|
@@ -27,8 +27,8 @@ export function scriptParseOptionsWithComments() {
|
|
|
27
27
|
accumulateComments.push({
|
|
28
28
|
type: block ? 'Block' : 'Line',
|
|
29
29
|
value: comment,
|
|
30
|
-
start:
|
|
31
|
-
end:
|
|
30
|
+
start: -1,
|
|
31
|
+
end: -1,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
},
|
|
@@ -55,9 +55,9 @@ export class Transformer {
|
|
|
55
55
|
// state
|
|
56
56
|
commentDirectives = {};
|
|
57
57
|
insideProgram = false;
|
|
58
|
-
declaring
|
|
58
|
+
declaring;
|
|
59
59
|
currentFuncNested = false;
|
|
60
|
-
currentFuncDef
|
|
60
|
+
currentFuncDef;
|
|
61
61
|
currentCall;
|
|
62
62
|
currentTopLevelCall;
|
|
63
63
|
/** .start of the first statements in their respective parents, to put the runtime init before */
|
|
@@ -72,7 +72,6 @@ export class Transformer {
|
|
|
72
72
|
this.filename = filename;
|
|
73
73
|
this.matchUrl = matchUrl;
|
|
74
74
|
const topLevelUseReactive = rtConf.useReactive({
|
|
75
|
-
funcName: null,
|
|
76
75
|
nested: false,
|
|
77
76
|
file: filename,
|
|
78
77
|
additional: this.additionalState,
|
|
@@ -97,7 +96,7 @@ export class Transformer {
|
|
|
97
96
|
return useReactive.use ? currentVars.reactive : currentVars.plain;
|
|
98
97
|
};
|
|
99
98
|
this.initRuntime = (file, funcName, parentFunc, additional) => {
|
|
100
|
-
const useReactive = rtConf.useReactive({ funcName, nested: parentFunc != null, file, additional });
|
|
99
|
+
const useReactive = rtConf.useReactive({ funcName, nested: parentFunc != null, file, additional: additional ?? {} });
|
|
101
100
|
if (useReactive.init == null) {
|
|
102
101
|
return;
|
|
103
102
|
}
|
|
@@ -142,9 +141,10 @@ export class Transformer {
|
|
|
142
141
|
}
|
|
143
142
|
const msg = new Message(msgStr, this.fullHeuristicDetails(detailsBase), this.commentDirectives.context);
|
|
144
143
|
const heuRes = this.getHeuristicMessageType(msg);
|
|
145
|
-
if (heuRes) {
|
|
146
|
-
|
|
144
|
+
if (!heuRes) {
|
|
145
|
+
return [false, null];
|
|
147
146
|
}
|
|
147
|
+
msg.type = heuRes;
|
|
148
148
|
return [heuRes, msg];
|
|
149
149
|
};
|
|
150
150
|
visitLiteral = (node, heuristicDetailsBase) => {
|
|
@@ -159,7 +159,7 @@ export class Transformer {
|
|
|
159
159
|
this.mstr.update(start, end, `${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())})`);
|
|
160
160
|
return [msgInfo];
|
|
161
161
|
};
|
|
162
|
-
visitArrayExpression = (node) => node.elements.map(this.visit).flat();
|
|
162
|
+
visitArrayExpression = (node) => node.elements.map(elm => elm ? this.visit(elm) : []).flat();
|
|
163
163
|
visitSequenceExpression = (node) => node.expressions.map(this.visit).flat();
|
|
164
164
|
visitObjectExpression = (node) => node.properties.map(this.visit).flat();
|
|
165
165
|
visitObjectPattern = (node) => node.properties.map(this.visit).flat();
|
|
@@ -208,10 +208,10 @@ export class Transformer {
|
|
|
208
208
|
const msgs = [];
|
|
209
209
|
const updates = [];
|
|
210
210
|
const appends = [];
|
|
211
|
-
let lastArgEnd;
|
|
211
|
+
let lastArgEnd = null;
|
|
212
212
|
for (const [i, arg] of pattern.args.entries()) {
|
|
213
213
|
const argVal = node.arguments[i];
|
|
214
|
-
let argInsertIndex;
|
|
214
|
+
let argInsertIndex = 0; // for now
|
|
215
215
|
if (argVal == null) {
|
|
216
216
|
argInsertIndex = lastArgEnd ?? node.callee.end + 1;
|
|
217
217
|
if (lastArgEnd == null) {
|
|
@@ -271,7 +271,7 @@ export class Transformer {
|
|
|
271
271
|
}
|
|
272
272
|
const candidates = [];
|
|
273
273
|
for (const elm of argVal.elements) {
|
|
274
|
-
if (elm.type !== 'Literal' || typeof elm.value !== 'string') {
|
|
274
|
+
if (!elm || elm.type !== 'Literal' || typeof elm.value !== 'string') {
|
|
275
275
|
return this.defaultVisitCallExpression(node);
|
|
276
276
|
}
|
|
277
277
|
candidates.push(elm.value);
|
|
@@ -385,8 +385,8 @@ export class Transformer {
|
|
|
385
385
|
}
|
|
386
386
|
const msgs = [...this.visit(node.id), ...this.visit(node.init)];
|
|
387
387
|
if (atTopLevelDefn) {
|
|
388
|
-
this.currentTopLevelCall =
|
|
389
|
-
this.declaring =
|
|
388
|
+
this.currentTopLevelCall = undefined; // reset
|
|
389
|
+
this.declaring = undefined;
|
|
390
390
|
}
|
|
391
391
|
return msgs;
|
|
392
392
|
};
|
|
@@ -399,7 +399,7 @@ export class Transformer {
|
|
|
399
399
|
const msgs = [];
|
|
400
400
|
let bodyStart = null;
|
|
401
401
|
for (const bod of nodes) {
|
|
402
|
-
let currentContent;
|
|
402
|
+
let currentContent = ''; // for now
|
|
403
403
|
if (bodyStart == null) {
|
|
404
404
|
currentContent = this.mstr.toString();
|
|
405
405
|
}
|
package/dist/adapters.d.ts
CHANGED
|
@@ -18,11 +18,11 @@ export type MessageType = 'message' | 'url';
|
|
|
18
18
|
export declare class Message {
|
|
19
19
|
msgStr: string[];
|
|
20
20
|
plural: boolean;
|
|
21
|
-
context
|
|
21
|
+
context?: string;
|
|
22
22
|
comments: string[];
|
|
23
23
|
details: HeuristicDetails;
|
|
24
24
|
type: MessageType;
|
|
25
|
-
constructor(msgStr: string | string[], heuristicDetails
|
|
25
|
+
constructor(msgStr: string | (string | null | undefined)[], heuristicDetails?: HeuristicDetails, context?: string);
|
|
26
26
|
toKey: () => string;
|
|
27
27
|
}
|
|
28
28
|
export type HeuristicResultChecked = MessageType | false;
|
|
@@ -33,7 +33,7 @@ export declare const defaultHeuristicOpts: {
|
|
|
33
33
|
ignoreAttribs: string[][];
|
|
34
34
|
ignoreCalls: string[];
|
|
35
35
|
urlAttribs: string[][];
|
|
36
|
-
urlCalls:
|
|
36
|
+
urlCalls: string[];
|
|
37
37
|
};
|
|
38
38
|
export type CreateHeuristicOpts = typeof defaultHeuristicOpts;
|
|
39
39
|
export declare function createHeuristic(opts: CreateHeuristicOpts): HeuristicFunc;
|
|
@@ -111,7 +111,7 @@ export type AdapterPassThruOpts = {
|
|
|
111
111
|
outDir?: string;
|
|
112
112
|
granularLoad: boolean;
|
|
113
113
|
bundleLoad: boolean;
|
|
114
|
-
url
|
|
114
|
+
url?: {
|
|
115
115
|
patterns?: string[];
|
|
116
116
|
localize?: boolean | URLLocalizer;
|
|
117
117
|
};
|
|
@@ -132,9 +132,9 @@ export type CodePattern = {
|
|
|
132
132
|
args: ('message' | 'pluralFunc' | 'locale' | 'other')[];
|
|
133
133
|
};
|
|
134
134
|
export type LoaderChoice<LoadersAvailable> = LoadersAvailable | string & {} | 'custom';
|
|
135
|
-
export type AdapterArgs<LoadersAvailable> =
|
|
135
|
+
export type AdapterArgs<LoadersAvailable> = AdapterPassThruOpts & {
|
|
136
136
|
loader: LoaderChoice<LoadersAvailable>;
|
|
137
|
-
heuristic
|
|
138
|
-
patterns
|
|
137
|
+
heuristic: HeuristicFunc;
|
|
138
|
+
patterns: CodePattern[];
|
|
139
139
|
};
|
|
140
140
|
export {};
|
package/dist/adapters.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const someHeuDet = { file: '', scope: 'markup' };
|
|
1
2
|
export class Message {
|
|
2
3
|
msgStr; // array for plurals
|
|
3
4
|
plural = false;
|
|
@@ -5,7 +6,7 @@ export class Message {
|
|
|
5
6
|
comments = [];
|
|
6
7
|
details;
|
|
7
8
|
type = 'message';
|
|
8
|
-
constructor(msgStr, heuristicDetails, context) {
|
|
9
|
+
constructor(msgStr, heuristicDetails = someHeuDet, context) {
|
|
9
10
|
if (typeof msgStr === 'string') {
|
|
10
11
|
this.msgStr = [msgStr];
|
|
11
12
|
}
|
|
@@ -14,7 +15,7 @@ export class Message {
|
|
|
14
15
|
}
|
|
15
16
|
this.msgStr = this.msgStr.map(msg => msg.split('\n').map(line => line.trim()).join('\n'));
|
|
16
17
|
this.details = heuristicDetails;
|
|
17
|
-
this.context = context
|
|
18
|
+
this.context = context;
|
|
18
19
|
}
|
|
19
20
|
toKey = () => `${this.msgStr.slice(0, 2).join('\n')}\n${this.context ?? ''}`.trim();
|
|
20
21
|
}
|
|
@@ -75,7 +76,7 @@ export function createHeuristic(opts) {
|
|
|
75
76
|
if (msg.details.declaring === 'expression' && !msg.details.funcName) {
|
|
76
77
|
return false;
|
|
77
78
|
}
|
|
78
|
-
if (!msg.details.call
|
|
79
|
+
if (!msg.details.call || !msg.details.call.startsWith('console.') && !opts.ignoreCalls.includes(msg.details.call)) {
|
|
79
80
|
return 'message';
|
|
80
81
|
}
|
|
81
82
|
return false;
|
package/dist/ai/gemini.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ type GeminiOpts = {
|
|
|
5
5
|
think?: boolean;
|
|
6
6
|
parallel?: number;
|
|
7
7
|
};
|
|
8
|
-
export declare function gemini({ apiKey, batchSize, think, parallel }?: GeminiOpts): AI;
|
|
9
|
-
export declare const defaultGemini: AI;
|
|
8
|
+
export declare function gemini({ apiKey, batchSize, think, parallel }?: GeminiOpts): AI | null;
|
|
9
|
+
export declare const defaultGemini: AI | null;
|
|
10
10
|
export {};
|
package/dist/ai/gemini.js
CHANGED
|
@@ -15,7 +15,7 @@ function prepareData(content, instruction, think) {
|
|
|
15
15
|
}
|
|
16
16
|
export function gemini({ apiKey = 'env', batchSize = 50, think = false, parallel = 4 } = {}) {
|
|
17
17
|
if (apiKey === 'env') {
|
|
18
|
-
apiKey = process.env.GEMINI_API_KEY;
|
|
18
|
+
apiKey = process.env.GEMINI_API_KEY ?? '';
|
|
19
19
|
}
|
|
20
20
|
if (!apiKey) {
|
|
21
21
|
return null;
|
|
@@ -35,7 +35,7 @@ export function gemini({ apiKey = 'env', batchSize = 50, think = false, parallel
|
|
|
35
35
|
if (json.error) {
|
|
36
36
|
throw new Error(`error: ${json.error.code} ${json.error.message}`);
|
|
37
37
|
}
|
|
38
|
-
return json.candidates[0]?.content.parts[0].text;
|
|
38
|
+
return json.candidates?.[0]?.content.parts[0].text ?? '';
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
}
|
package/dist/cli/extract.js
CHANGED
|
@@ -17,9 +17,12 @@ export async function extract(config, clean, watch, sync) {
|
|
|
17
17
|
for (const [key, adapter] of Object.entries(config.adapters)) {
|
|
18
18
|
const handler = new AdapterHandler(adapter, key, config, 'cli', process.cwd(), new Logger(config.logLevel));
|
|
19
19
|
await handler.init(sharedState);
|
|
20
|
-
await handler.directScanFS(clean, sync);
|
|
21
20
|
handlers.push(handler);
|
|
22
21
|
}
|
|
22
|
+
// other loop to make sure that all otherFileMatchers are collected
|
|
23
|
+
for (const handler of handlers) {
|
|
24
|
+
await handler.directScanFS(clean, sync);
|
|
25
|
+
}
|
|
23
26
|
if (!watch) {
|
|
24
27
|
console.info('Extraction finished.');
|
|
25
28
|
return;
|
package/dist/cli/index.js
CHANGED
|
@@ -12,13 +12,16 @@ const { positionals, values } = parseArgs({
|
|
|
12
12
|
clean: {
|
|
13
13
|
type: 'boolean',
|
|
14
14
|
short: 'c',
|
|
15
|
+
default: false,
|
|
15
16
|
},
|
|
16
17
|
watch: {
|
|
17
18
|
type: 'boolean',
|
|
18
19
|
short: 'w',
|
|
20
|
+
default: false,
|
|
19
21
|
},
|
|
20
22
|
sync: {
|
|
21
23
|
type: 'boolean',
|
|
24
|
+
default: false,
|
|
22
25
|
},
|
|
23
26
|
help: {
|
|
24
27
|
type: 'boolean',
|
package/dist/config.d.ts
CHANGED
|
@@ -2,18 +2,20 @@ import { type Adapter } from "./adapters.js";
|
|
|
2
2
|
import type { AI } from "./ai/index.js";
|
|
3
3
|
export type LogLevel = 'error' | 'warn' | 'info' | 'verbose';
|
|
4
4
|
export type ConfigPartial = {
|
|
5
|
-
sourceLocale
|
|
6
|
-
otherLocales
|
|
7
|
-
ai
|
|
8
|
-
logLevel
|
|
5
|
+
sourceLocale: string;
|
|
6
|
+
otherLocales: string[];
|
|
7
|
+
ai: AI | null;
|
|
8
|
+
logLevel: LogLevel;
|
|
9
9
|
};
|
|
10
10
|
export type Config = ConfigPartial & {
|
|
11
|
-
adapters
|
|
12
|
-
hmr
|
|
11
|
+
adapters: Record<string, Adapter>;
|
|
12
|
+
hmr: boolean;
|
|
13
13
|
};
|
|
14
|
+
type ConfigWithOptional = Partial<Config>;
|
|
14
15
|
export declare const defaultConfig: Config;
|
|
15
|
-
export declare function defineConfig(config:
|
|
16
|
-
export declare function deepMergeObjects<Type>(source: Type
|
|
16
|
+
export declare function defineConfig(config: ConfigWithOptional): Partial<Config>;
|
|
17
|
+
export declare function deepMergeObjects<Type extends {}>(source: Partial<Type>, target: Type): Type;
|
|
17
18
|
export declare const defaultConfigNames: string[];
|
|
18
19
|
export declare const getLanguageName: (code: string) => string;
|
|
19
20
|
export declare function getConfig(configPath?: string): Promise<Config>;
|
|
21
|
+
export {};
|
package/dist/config.js
CHANGED
|
@@ -35,7 +35,7 @@ export function deepMergeObjects(source, target) {
|
|
|
35
35
|
}
|
|
36
36
|
export const defaultConfigNames = ['js', 'mjs'].map(ext => `wuchale.config.${ext}`);
|
|
37
37
|
const displayName = new Intl.DisplayNames(['en'], { type: 'language' });
|
|
38
|
-
export const getLanguageName = (code) => displayName.of(code);
|
|
38
|
+
export const getLanguageName = (code) => displayName.of(code) ?? code;
|
|
39
39
|
function checkValidLocale(locale) {
|
|
40
40
|
try {
|
|
41
41
|
getLanguageName(locale);
|
|
@@ -45,17 +45,18 @@ function checkValidLocale(locale) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
export async function getConfig(configPath) {
|
|
48
|
-
let module;
|
|
48
|
+
let module = null;
|
|
49
49
|
for (const confName of [configPath, ...defaultConfigNames]) {
|
|
50
50
|
if (!confName) {
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
|
+
const fileUrl = `file://${resolve(confName)}`;
|
|
53
54
|
try {
|
|
54
|
-
module = await import(
|
|
55
|
+
module = await import(fileUrl);
|
|
55
56
|
break;
|
|
56
57
|
}
|
|
57
58
|
catch (err) {
|
|
58
|
-
if (err.code !== 'ERR_MODULE_NOT_FOUND') {
|
|
59
|
+
if (err.code !== 'ERR_MODULE_NOT_FOUND' || err.url != fileUrl) {
|
|
59
60
|
throw err;
|
|
60
61
|
}
|
|
61
62
|
}
|
package/dist/handler.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ type Compiled = {
|
|
|
28
28
|
type CompiledCatalogs = Record<string, Compiled>;
|
|
29
29
|
type SharedState = {
|
|
30
30
|
ownerKey: string;
|
|
31
|
+
otherFileMatches: Matcher[];
|
|
31
32
|
poFilesByLoc: Record<string, POFile>;
|
|
32
33
|
compiled: CompiledCatalogs;
|
|
33
34
|
extractedUrls: Record<string, Catalog>;
|
|
@@ -61,6 +62,7 @@ export declare class AdapterHandler {
|
|
|
61
62
|
getLoaderPath(): Promise<LoaderPath>;
|
|
62
63
|
getCompiledFilePath(loc: string, id: string | null): string;
|
|
63
64
|
getLoadIDs(forImport?: boolean): string[];
|
|
65
|
+
genProxy(catalogs: string[], loadIDs: string[], syncImports?: string[]): string;
|
|
64
66
|
getProxy(): string;
|
|
65
67
|
getProxySync(): string;
|
|
66
68
|
getData(): string;
|
|
@@ -70,12 +72,12 @@ export declare class AdapterHandler {
|
|
|
70
72
|
init: (sharedStates: SharedStates) => Promise<void>;
|
|
71
73
|
urlPatternToTranslate: (pattern: string) => string;
|
|
72
74
|
initUrlPatterns: () => Promise<void>;
|
|
73
|
-
loadCatalogNCompile: (loc: string) => Promise<void>;
|
|
74
|
-
loadCatalogModule: (locale: string, loadID: string, hmrVersion
|
|
75
|
-
matchUrl: (url: string) => string;
|
|
75
|
+
loadCatalogNCompile: (loc: string, hmrVersion?: number) => Promise<void>;
|
|
76
|
+
loadCatalogModule: (locale: string, loadID: string | null, hmrVersion: number) => string;
|
|
77
|
+
matchUrl: (url: string) => string | null;
|
|
76
78
|
getUrlToCompile: (key: string, locale: string) => string;
|
|
77
|
-
compile: (loc: string) => Promise<void>;
|
|
78
|
-
writeCompiled: (loc: string) => Promise<void>;
|
|
79
|
+
compile: (loc: string, hmrVersion?: number) => Promise<void>;
|
|
80
|
+
writeCompiled: (loc: string, hmrVersion?: number) => Promise<void>;
|
|
79
81
|
writeProxies: () => Promise<void>;
|
|
80
82
|
writeTransformed: (filename: string, content: string) => Promise<void>;
|
|
81
83
|
globConfToArgs: (conf: GlobConf) => [string[], {
|
package/dist/handler.js
CHANGED
|
@@ -43,7 +43,7 @@ async function loadCatalogFromPO(filename) {
|
|
|
43
43
|
const po = await loadPOFile(filename);
|
|
44
44
|
const catalog = {};
|
|
45
45
|
for (const item of po.items) {
|
|
46
|
-
const msgInfo = new Message([item.msgid, item.msgid_plural],
|
|
46
|
+
const msgInfo = new Message([item.msgid, item.msgid_plural], undefined, item.msgctxt);
|
|
47
47
|
catalog[msgInfo.toKey()] = item;
|
|
48
48
|
}
|
|
49
49
|
let pluralRule;
|
|
@@ -55,7 +55,12 @@ async function loadCatalogFromPO(filename) {
|
|
|
55
55
|
else {
|
|
56
56
|
pluralRule = defaultPluralRule;
|
|
57
57
|
}
|
|
58
|
-
return {
|
|
58
|
+
return {
|
|
59
|
+
catalog,
|
|
60
|
+
pluralRule,
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
headers: po.headers
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
function poDumpToString(items) {
|
|
61
66
|
const po = new PO();
|
|
@@ -74,7 +79,7 @@ async function saveCatalogToPO(catalog, filename, headers = {}) {
|
|
|
74
79
|
rej(err);
|
|
75
80
|
}
|
|
76
81
|
else {
|
|
77
|
-
res(
|
|
82
|
+
res();
|
|
78
83
|
}
|
|
79
84
|
});
|
|
80
85
|
});
|
|
@@ -208,6 +213,21 @@ export class AdapterHandler {
|
|
|
208
213
|
}
|
|
209
214
|
return loadIDs;
|
|
210
215
|
}
|
|
216
|
+
// typed to work regardless of user's noUncheckedIndexedAccess setting in tsconfig
|
|
217
|
+
genProxy(catalogs, loadIDs, syncImports) {
|
|
218
|
+
const baseType = 'import("wuchale/runtime").CatalogModule';
|
|
219
|
+
return `
|
|
220
|
+
${syncImports?.join('\n') ?? ''}
|
|
221
|
+
/** @typedef {${syncImports ? baseType : `() => Promise<${baseType}>`}} CatalogMod
|
|
222
|
+
/** @typedef {{[locale: string]: CatalogMod}} KeyCatalogs
|
|
223
|
+
/** @type {{[loadID: string]: KeyCatalogs}} */
|
|
224
|
+
const catalogs = {${catalogs.join(',')}}
|
|
225
|
+
export const loadCatalog = (/** @type {string} */ loadID, /** @type {string} */ locale) => {
|
|
226
|
+
return /** @type {CatalogMod} */ (/** @type {KeyCatalogs} */ (catalogs[loadID])[locale])${syncImports ? '' : '()'}
|
|
227
|
+
}
|
|
228
|
+
export const loadIDs = ['${loadIDs.join("', '")}']
|
|
229
|
+
`;
|
|
230
|
+
}
|
|
211
231
|
getProxy() {
|
|
212
232
|
const imports = [];
|
|
213
233
|
const loadIDs = this.getLoadIDs();
|
|
@@ -219,12 +239,7 @@ export class AdapterHandler {
|
|
|
219
239
|
}
|
|
220
240
|
imports.push(`${id}: {${importsByLocale.join(',')}}`);
|
|
221
241
|
}
|
|
222
|
-
return
|
|
223
|
-
/** @type {{[loadID: string]: {[locale: string]: () => Promise<import('wuchale/runtime').CatalogModule>}}} */
|
|
224
|
-
const catalogs = {${imports.join(',')}}
|
|
225
|
-
export const loadCatalog = (/** @type {string} */ loadID, /** @type {string} */ locale) => catalogs[loadID][locale]()
|
|
226
|
-
export const loadIDs = ['${loadIDs.join("', '")}']
|
|
227
|
-
`;
|
|
242
|
+
return this.genProxy(imports, loadIDs);
|
|
228
243
|
}
|
|
229
244
|
getProxySync() {
|
|
230
245
|
const loadIDs = this.getLoadIDs();
|
|
@@ -240,13 +255,7 @@ export class AdapterHandler {
|
|
|
240
255
|
}
|
|
241
256
|
object.push(`${id}: {${importedByLocale.join(',')}}`);
|
|
242
257
|
}
|
|
243
|
-
return
|
|
244
|
-
${imports.join('\n')}
|
|
245
|
-
/** @type {{[loadID: string]: {[locale: string]: import('wuchale/runtime').CatalogModule}}} */
|
|
246
|
-
const catalogs = {${object.join(',')}}
|
|
247
|
-
export const loadCatalog = (/** @type {string} */ loadID, /** @type {string} */ locale) => catalogs[loadID][locale]
|
|
248
|
-
export const loadIDs = ['${loadIDs.join("', '")}']
|
|
249
|
-
`;
|
|
258
|
+
return this.genProxy(object, loadIDs, imports);
|
|
250
259
|
}
|
|
251
260
|
getData() {
|
|
252
261
|
return [
|
|
@@ -343,6 +352,7 @@ export class AdapterHandler {
|
|
|
343
352
|
if (this.sharedState == null) {
|
|
344
353
|
this.sharedState = {
|
|
345
354
|
ownerKey: this.key,
|
|
355
|
+
otherFileMatches: [],
|
|
346
356
|
poFilesByLoc: {},
|
|
347
357
|
indexTracker: new IndexTracker(),
|
|
348
358
|
compiled: {},
|
|
@@ -350,12 +360,15 @@ export class AdapterHandler {
|
|
|
350
360
|
};
|
|
351
361
|
sharedStates[this.#adapter.localesDir] = this.sharedState;
|
|
352
362
|
}
|
|
363
|
+
else {
|
|
364
|
+
this.sharedState.otherFileMatches.push(this.fileMatches);
|
|
365
|
+
}
|
|
353
366
|
this.catalogPathsToLocales = {};
|
|
354
367
|
for (const loc of this.#locales) {
|
|
355
368
|
this.#catalogsFname[loc] = this.catalogFileName(loc);
|
|
356
369
|
// for handleHotUpdate
|
|
357
370
|
this.catalogPathsToLocales[this.#catalogsFname[loc]] = loc;
|
|
358
|
-
if (loc !== this.#config.sourceLocale) {
|
|
371
|
+
if (loc !== this.#config.sourceLocale && this.#config.ai) {
|
|
359
372
|
this.#geminiQueue[loc] = new AIQueue(sourceLocaleName, getLanguageName(loc), this.#config.ai, async () => await this.savePoAndCompile(loc), this.#log);
|
|
360
373
|
}
|
|
361
374
|
if (this.sharedState.ownerKey === this.key) {
|
|
@@ -390,11 +403,11 @@ export class AdapterHandler {
|
|
|
390
403
|
const urlPatternsForTranslate = urlPatterns.map(this.urlPatternToTranslate);
|
|
391
404
|
const urlPatternMsgs = urlPatterns.map((patt, i) => {
|
|
392
405
|
const locPattern = urlPatternsForTranslate[i];
|
|
393
|
-
let context
|
|
406
|
+
let context;
|
|
394
407
|
if (locPattern !== patt) {
|
|
395
408
|
context = `original: ${patt}`;
|
|
396
409
|
}
|
|
397
|
-
return new Message(locPattern,
|
|
410
|
+
return new Message(locPattern, undefined, context);
|
|
398
411
|
});
|
|
399
412
|
const urlPatternCatKeys = urlPatternMsgs.map(msg => msg.toKey());
|
|
400
413
|
for (const [key, item] of Object.entries(catalog)) {
|
|
@@ -448,7 +461,7 @@ export class AdapterHandler {
|
|
|
448
461
|
}
|
|
449
462
|
await this.writeUrlFiles();
|
|
450
463
|
};
|
|
451
|
-
loadCatalogNCompile = async (loc) => {
|
|
464
|
+
loadCatalogNCompile = async (loc, hmrVersion = -1) => {
|
|
452
465
|
if (this.sharedState.ownerKey === this.key) {
|
|
453
466
|
try {
|
|
454
467
|
this.sharedState.poFilesByLoc[loc] = await loadCatalogFromPO(this.#catalogsFname[loc]);
|
|
@@ -460,12 +473,12 @@ export class AdapterHandler {
|
|
|
460
473
|
this.#log.warn(`${color.magenta(this.key)}: Catalog not found for ${color.cyan(loc)}`);
|
|
461
474
|
}
|
|
462
475
|
}
|
|
463
|
-
await this.compile(loc);
|
|
476
|
+
await this.compile(loc, hmrVersion);
|
|
464
477
|
};
|
|
465
|
-
loadCatalogModule = (locale, loadID, hmrVersion
|
|
478
|
+
loadCatalogModule = (locale, loadID, hmrVersion) => {
|
|
466
479
|
let compiledData = this.sharedState.compiled[locale];
|
|
467
480
|
if (this.#adapter.granularLoad) {
|
|
468
|
-
compiledData = this.granularStateByID[loadID]?.compiled?.[locale]
|
|
481
|
+
compiledData = loadID && this.granularStateByID[loadID]?.compiled?.[locale] || { hasPlurals: false, items: [] };
|
|
469
482
|
}
|
|
470
483
|
const compiledItems = JSON.stringify(compiledData.items);
|
|
471
484
|
const plural = `n => ${this.sharedState.poFilesByLoc[locale].pluralRule.plural}`;
|
|
@@ -556,7 +569,7 @@ export class AdapterHandler {
|
|
|
556
569
|
}
|
|
557
570
|
return toCompile;
|
|
558
571
|
};
|
|
559
|
-
compile = async (loc) => {
|
|
572
|
+
compile = async (loc, hmrVersion = -1) => {
|
|
560
573
|
this.sharedState.compiled[loc] ??= { hasPlurals: false, items: [] };
|
|
561
574
|
const catalog = this.sharedState.poFilesByLoc[loc].catalog;
|
|
562
575
|
for (const [key, poItem] of Object.entries({ ...catalog, ...this.sharedState.extractedUrls[loc] })) {
|
|
@@ -596,15 +609,15 @@ export class AdapterHandler {
|
|
|
596
609
|
state.compiled[loc].items[state.indexTracker.get(key)] = compiled;
|
|
597
610
|
}
|
|
598
611
|
}
|
|
599
|
-
await this.writeCompiled(loc);
|
|
612
|
+
await this.writeCompiled(loc, hmrVersion);
|
|
600
613
|
};
|
|
601
|
-
writeCompiled = async (loc) => {
|
|
602
|
-
await writeFile(this.getCompiledFilePath(loc, null), this.loadCatalogModule(loc, null));
|
|
614
|
+
writeCompiled = async (loc, hmrVersion = -1) => {
|
|
615
|
+
await writeFile(this.getCompiledFilePath(loc, null), this.loadCatalogModule(loc, null, hmrVersion));
|
|
603
616
|
if (!this.#adapter.granularLoad) {
|
|
604
617
|
return;
|
|
605
618
|
}
|
|
606
619
|
for (const state of Object.values(this.granularStateByID)) {
|
|
607
|
-
await writeFile(this.getCompiledFilePath(loc, state.id), this.loadCatalogModule(loc, state.id));
|
|
620
|
+
await writeFile(this.getCompiledFilePath(loc, state.id), this.loadCatalogModule(loc, state.id, hmrVersion));
|
|
608
621
|
}
|
|
609
622
|
};
|
|
610
623
|
writeProxies = async () => {
|
|
@@ -760,7 +773,7 @@ export class AdapterHandler {
|
|
|
760
773
|
if (!item.references.includes(filename)) {
|
|
761
774
|
continue;
|
|
762
775
|
}
|
|
763
|
-
const key = new Message([item.msgid, item.msgid_plural],
|
|
776
|
+
const key = new Message([item.msgid, item.msgid_plural], undefined, item.msgctxt).toKey();
|
|
764
777
|
previousReferences[key] = { count: 0, indices: [] };
|
|
765
778
|
for (const [i, ref] of item.references.entries()) {
|
|
766
779
|
if (ref !== filename) {
|
|
@@ -802,7 +815,7 @@ export class AdapterHandler {
|
|
|
802
815
|
let iStartComm;
|
|
803
816
|
if (key in previousReferences) {
|
|
804
817
|
const prevRef = previousReferences[key];
|
|
805
|
-
iStartComm = prevRef.indices.shift() * newComments.length; // cannot be pop for determinism
|
|
818
|
+
iStartComm = (prevRef.indices.shift() ?? 0) * newComments.length; // cannot be pop for determinism
|
|
806
819
|
const prevComments = poItem.extractedComments.slice(iStartComm, iStartComm + newComments.length);
|
|
807
820
|
if (prevComments.length !== newComments.length || prevComments.some((c, i) => c !== newComments[i])) {
|
|
808
821
|
commentsChanged = true;
|
|
@@ -947,7 +960,15 @@ export class AdapterHandler {
|
|
|
947
960
|
}
|
|
948
961
|
else {
|
|
949
962
|
// don't touch other adapters' files. related extracted comments handled by handler
|
|
950
|
-
item.references = item.references.filter(ref =>
|
|
963
|
+
item.references = item.references.filter(ref => {
|
|
964
|
+
if (this.fileMatches(ref)) {
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
if (this.sharedState.ownerKey !== this.key) {
|
|
968
|
+
return true;
|
|
969
|
+
}
|
|
970
|
+
return this.sharedState.otherFileMatches.some(match => match(ref));
|
|
971
|
+
});
|
|
951
972
|
}
|
|
952
973
|
}
|
|
953
974
|
}
|
|
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
3
3
|
// by locale
|
|
4
4
|
const runtimes = {};
|
|
5
5
|
const runtimeCtx = new AsyncLocalStorage();
|
|
6
|
+
const emptyRuntime = toRuntime();
|
|
6
7
|
let warningShown = {};
|
|
7
8
|
export function currentRuntime(key, loadID) {
|
|
8
9
|
const runtime = runtimeCtx.getStore()?.[key]?.[loadID];
|
|
@@ -11,10 +12,11 @@ export function currentRuntime(key, loadID) {
|
|
|
11
12
|
}
|
|
12
13
|
const warnKey = `${key}.${loadID}`;
|
|
13
14
|
if (warningShown[warnKey]) {
|
|
14
|
-
return;
|
|
15
|
+
return emptyRuntime;
|
|
15
16
|
}
|
|
16
17
|
console.warn(`Catalog for '${warnKey}' not found.\n Either 'runWithLocale' was not called or the environment has a problem.`);
|
|
17
18
|
warningShown[warnKey] = true;
|
|
19
|
+
return emptyRuntime;
|
|
18
20
|
}
|
|
19
21
|
export async function loadLocales(key, loadIDs, load, locales) {
|
|
20
22
|
if (loadIDs == null) {
|
package/dist/runtime.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ declare let onInvalidFunc: (i: number, c: CompiledElement[]) => string;
|
|
|
9
9
|
export declare function onInvalid(newOnInvalid: typeof onInvalidFunc): void;
|
|
10
10
|
export type Runtime = {
|
|
11
11
|
_: CatalogModule;
|
|
12
|
-
l
|
|
12
|
+
l?: string;
|
|
13
13
|
cx: (id: number) => Mixed | CompositePayload[];
|
|
14
14
|
tx: (ctx: Mixed, args?: any[], start?: number) => string;
|
|
15
15
|
tt: (tag: CallableFunction, id: number, args?: any[]) => any;
|
package/dist/url.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ type GetLocale = (url: URL, locales: string[]) => string | null;
|
|
|
7
7
|
export type URLLocalizer = (url: string, locale: string) => string;
|
|
8
8
|
export declare const localizeDefault: URLLocalizer;
|
|
9
9
|
export declare const getLocaleDefault: GetLocale;
|
|
10
|
-
type MatchParams = Record<string, string | string[]
|
|
10
|
+
type MatchParams = Partial<Record<string, string | string[]>>;
|
|
11
11
|
export declare const fillParams: (params: MatchParams, destPattern: string) => string;
|
|
12
12
|
type MatchResult = {
|
|
13
|
-
path: string;
|
|
14
|
-
locale: string;
|
|
13
|
+
path: string | null;
|
|
14
|
+
locale: string | null;
|
|
15
15
|
params: MatchParams;
|
|
16
16
|
altPatterns: Record<string, string>;
|
|
17
17
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wuchale",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.9",
|
|
4
4
|
"description": "Protobuf-like i18n from plain code",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "tsc --watch",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"tinyglobby": "^0.2.15"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@types/node": "
|
|
90
|
+
"@types/node": "~24.10.1",
|
|
91
91
|
"@types/picomatch": "^4.0.1",
|
|
92
92
|
"typescript": "^5.9.3"
|
|
93
93
|
},
|