wuchale 0.14.6 → 0.15.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.
- package/dist/adapter-utils/index.d.ts +6 -3
- package/dist/adapter-utils/index.js +13 -11
- package/dist/adapter-utils/mixed-visitor.d.ts +2 -0
- package/dist/adapter-utils/mixed-visitor.js +3 -3
- package/dist/adapter-vanilla/index.d.ts +1 -1
- package/dist/adapter-vanilla/index.js +16 -4
- package/dist/adapter-vanilla/transformer.d.ts +16 -10
- package/dist/adapter-vanilla/transformer.js +61 -21
- package/dist/adapters.d.ts +33 -3
- package/dist/cli/init.js +12 -14
- package/dist/cli/status.js +8 -8
- package/dist/handler.d.ts +10 -7
- package/dist/handler.js +86 -36
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
export { MixedVisitor
|
|
2
|
-
export declare const
|
|
1
|
+
export { MixedVisitor } from './mixed-visitor.js';
|
|
2
|
+
export declare const varNames: {
|
|
3
|
+
rt: string;
|
|
3
4
|
hmrUpdate: string;
|
|
4
5
|
rtWrap: string;
|
|
5
|
-
|
|
6
|
+
};
|
|
7
|
+
export declare function runtimeVars(wrapFunc: (expr: string) => string): {
|
|
6
8
|
rtTrans: string;
|
|
7
9
|
rtTPlural: string;
|
|
8
10
|
rtPlural: string;
|
|
@@ -11,5 +13,6 @@ export declare const runtimeVars: {
|
|
|
11
13
|
/** for when nesting, used in adapters with elements */
|
|
12
14
|
nestCtx: string;
|
|
13
15
|
};
|
|
16
|
+
export type RuntimeVars = ReturnType<typeof runtimeVars>;
|
|
14
17
|
export declare function nonWhitespaceText(msgStr: string): [number, string, number];
|
|
15
18
|
export declare function getDependencies(): Promise<Set<string>>;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
export { MixedVisitor } from './mixed-visitor.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const runtimeVars = {
|
|
3
|
+
export const varNames = {
|
|
4
|
+
rt: '_w_runtime_',
|
|
6
5
|
hmrUpdate: '_w_hmrUpdate_',
|
|
7
6
|
rtWrap: '_w_to_rt_',
|
|
8
|
-
rtConst,
|
|
9
|
-
rtTrans: `${rtConst}.t`,
|
|
10
|
-
rtTPlural: `${rtConst}.tp`,
|
|
11
|
-
rtPlural: `${rtConst}._.p`,
|
|
12
|
-
rtCtx: `${rtConst}.cx`,
|
|
13
|
-
rtTransCtx: `${rtConst}.tx`,
|
|
14
|
-
/** for when nesting, used in adapters with elements */
|
|
15
|
-
nestCtx: '_w_ctx_',
|
|
16
7
|
};
|
|
8
|
+
export function runtimeVars(wrapFunc) {
|
|
9
|
+
return {
|
|
10
|
+
rtTrans: `${wrapFunc(varNames.rt)}.t`,
|
|
11
|
+
rtTPlural: `${wrapFunc(varNames.rt)}.tp`,
|
|
12
|
+
rtPlural: `${wrapFunc(varNames.rt)}._.p`,
|
|
13
|
+
rtCtx: `${wrapFunc(varNames.rt)}.cx`,
|
|
14
|
+
rtTransCtx: `${wrapFunc(varNames.rt)}.tx`,
|
|
15
|
+
/** for when nesting, used in adapters with elements */
|
|
16
|
+
nestCtx: '_w_ctx_',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
17
19
|
export function nonWhitespaceText(msgStr) {
|
|
18
20
|
let trimmedS = msgStr.trimStart();
|
|
19
21
|
const startWh = msgStr.length - trimmedS.length;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type MagicString from "magic-string";
|
|
2
2
|
import { IndexTracker, Message, type CommentDirectives, type HeuristicDetailsBase, type HeuristicFunc } from "../adapters.js";
|
|
3
|
+
import { type RuntimeVars } from "./index.js";
|
|
3
4
|
type NestedRanges = [number, number, boolean][];
|
|
4
5
|
type InitProps<NodeT> = {
|
|
6
|
+
vars: () => RuntimeVars;
|
|
5
7
|
mstr: MagicString;
|
|
6
8
|
getRange: (node: NodeT) => {
|
|
7
9
|
start: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Shared logic between adapters for handling nested / mixed elements within elements / fragments
|
|
2
2
|
import { IndexTracker, Message } from "../adapters.js";
|
|
3
|
-
import { nonWhitespaceText
|
|
3
|
+
import { nonWhitespaceText } from "./index.js";
|
|
4
4
|
export class MixedVisitor {
|
|
5
5
|
constructor(props) {
|
|
6
6
|
Object.assign(this, props);
|
|
@@ -149,10 +149,10 @@ export class MixedVisitor {
|
|
|
149
149
|
let begin = '{';
|
|
150
150
|
let end = ')}';
|
|
151
151
|
if (props.inCompoundText) {
|
|
152
|
-
begin += `${
|
|
152
|
+
begin += `${this.vars().rtTransCtx}(${this.vars().nestCtx}`;
|
|
153
153
|
}
|
|
154
154
|
else {
|
|
155
|
-
begin += `${
|
|
155
|
+
begin += `${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())}`;
|
|
156
156
|
}
|
|
157
157
|
if (iArg > 0) {
|
|
158
158
|
begin += ', [';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdapterArgs, Adapter } from "../adapters.js";
|
|
2
2
|
import { Transformer } from "./transformer.js";
|
|
3
3
|
export { Transformer };
|
|
4
|
-
export { parseScript, scriptParseOptions, scriptParseOptionsWithComments
|
|
4
|
+
export { parseScript, scriptParseOptions, scriptParseOptionsWithComments } from './transformer.js';
|
|
5
5
|
export declare const adapter: (args?: AdapterArgs) => Adapter;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// $$ cd .. && npm run test
|
|
2
2
|
import { defaultGenerateLoadID, defaultHeuristicFuncOnly } from '../adapters.js';
|
|
3
3
|
import { deepMergeObjects } from "../config.js";
|
|
4
|
-
import {
|
|
4
|
+
import { Transformer } from "./transformer.js";
|
|
5
5
|
import { getDependencies } from '../adapter-utils/index.js';
|
|
6
6
|
export { Transformer };
|
|
7
|
-
export { parseScript, scriptParseOptions, scriptParseOptionsWithComments
|
|
7
|
+
export { parseScript, scriptParseOptions, scriptParseOptionsWithComments } from './transformer.js';
|
|
8
8
|
const defaultArgs = {
|
|
9
9
|
files: { include: 'src/**/*.{js,ts}', ignore: '**/*.d.ts' },
|
|
10
10
|
catalog: './src/locales/{locale}',
|
|
@@ -14,11 +14,22 @@ const defaultArgs = {
|
|
|
14
14
|
bundleLoad: false,
|
|
15
15
|
generateLoadID: defaultGenerateLoadID,
|
|
16
16
|
writeFiles: {},
|
|
17
|
+
runtime: {
|
|
18
|
+
useReactive: () => ({
|
|
19
|
+
init: false,
|
|
20
|
+
use: false
|
|
21
|
+
}),
|
|
22
|
+
plain: {
|
|
23
|
+
importName: 'default',
|
|
24
|
+
wrapInit: expr => expr,
|
|
25
|
+
wrapUse: expr => expr,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
17
28
|
};
|
|
18
29
|
export const adapter = (args = defaultArgs) => {
|
|
19
|
-
const { heuristic, pluralsFunc, ...rest } = deepMergeObjects(args, defaultArgs);
|
|
30
|
+
const { heuristic, pluralsFunc, runtime, ...rest } = deepMergeObjects(args, defaultArgs);
|
|
20
31
|
return {
|
|
21
|
-
transform: ({ content, filename, index, header }) => new Transformer(content, filename, index, heuristic, pluralsFunc,
|
|
32
|
+
transform: ({ content, filename, index, header }) => new Transformer(content, filename, index, heuristic, pluralsFunc, header.expr, runtime).transform(header.head),
|
|
22
33
|
loaderExts: ['.js', '.ts'],
|
|
23
34
|
defaultLoaders: async () => {
|
|
24
35
|
if (rest.bundleLoad) {
|
|
@@ -34,6 +45,7 @@ export const adapter = (args = defaultArgs) => {
|
|
|
34
45
|
defaultLoaderPath: (loader) => {
|
|
35
46
|
return new URL(`../../src/adapter-vanilla/loaders/${loader}.js`, import.meta.url).pathname;
|
|
36
47
|
},
|
|
48
|
+
runtime,
|
|
37
49
|
...rest,
|
|
38
50
|
docsUrl: 'https://wuchale.dev/adapters/vanilla'
|
|
39
51
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
|
-
import type Estree from
|
|
3
|
-
import type { Program, Options as ParserOptions } from "acorn";
|
|
2
|
+
import type * as Estree from "acorn";
|
|
4
3
|
import { Message } from '../adapters.js';
|
|
5
|
-
import type { CommentDirectives, HeuristicDetailsBase, HeuristicFunc, IndexTracker, ScriptDeclType, TransformOutput } from "../adapters.js";
|
|
6
|
-
|
|
7
|
-
export declare
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
4
|
+
import type { CommentDirectives, HeuristicDetailsBase, HeuristicFunc, IndexTracker, ScriptDeclType, TransformOutput, RuntimeConf, CatalogExpr } from "../adapters.js";
|
|
5
|
+
import { type RuntimeVars } from "../adapter-utils/index.js";
|
|
6
|
+
export declare const scriptParseOptions: Estree.Options;
|
|
7
|
+
export declare function scriptParseOptionsWithComments(): [Estree.Options, Estree.Comment[][]];
|
|
8
|
+
export declare function parseScript(content: string): [Estree.Program, Estree.Comment[][]];
|
|
9
|
+
declare function initRuntimeStmt(rtConf: RuntimeConf, expr: CatalogExpr): (file: string, funcName: string, parentFunc: string, additional: object) => string;
|
|
10
10
|
export declare class Transformer {
|
|
11
11
|
index: IndexTracker;
|
|
12
12
|
heuristic: HeuristicFunc;
|
|
@@ -15,14 +15,18 @@ export declare class Transformer {
|
|
|
15
15
|
filename: string;
|
|
16
16
|
mstr: MagicString;
|
|
17
17
|
pluralFunc: string;
|
|
18
|
-
initRuntime:
|
|
18
|
+
initRuntime: ReturnType<typeof initRuntimeStmt>;
|
|
19
|
+
vars: () => RuntimeVars;
|
|
19
20
|
commentDirectives: CommentDirectives;
|
|
20
21
|
insideProgram: boolean;
|
|
21
22
|
declaring: ScriptDeclType;
|
|
23
|
+
currentFuncNested: boolean;
|
|
22
24
|
currentFuncDef: string | null;
|
|
23
25
|
currentCall: string;
|
|
24
26
|
currentTopLevelCall: string;
|
|
25
|
-
|
|
27
|
+
/** for subclasses. right now for svelte, if in <script module> */
|
|
28
|
+
additionalState: object;
|
|
29
|
+
constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, pluralsFunc: string, catalogExpr: CatalogExpr, rtConf: RuntimeConf);
|
|
26
30
|
checkHeuristicBool: HeuristicFunc<HeuristicDetailsBase>;
|
|
27
31
|
checkHeuristic: (msgStr: string, detailsBase: HeuristicDetailsBase) => [boolean, Message];
|
|
28
32
|
visitLiteral: (node: Estree.Literal & {
|
|
@@ -55,6 +59,7 @@ export declare class Transformer {
|
|
|
55
59
|
visitVariableDeclaration: (node: Estree.VariableDeclaration) => Message[];
|
|
56
60
|
visitExportNamedDeclaration: (node: Estree.ExportNamedDeclaration) => Message[];
|
|
57
61
|
visitExportDefaultDeclaration: (node: Estree.ExportNamedDeclaration) => Message[];
|
|
62
|
+
getRealBodyStart: (nodes: (Estree.Statement | Estree.ModuleDeclaration)[]) => number;
|
|
58
63
|
visitFunctionBody: (node: Estree.BlockStatement | Estree.Expression, name: string | null) => Message[];
|
|
59
64
|
visitFunctionDeclaration: (node: Estree.FunctionDeclaration) => Message[];
|
|
60
65
|
visitArrowFunctionExpression: (node: Estree.ArrowFunctionExpression) => Message[];
|
|
@@ -65,7 +70,8 @@ export declare class Transformer {
|
|
|
65
70
|
visitTemplateLiteral: (node: Estree.TemplateLiteral) => Message[];
|
|
66
71
|
visitProgram: (node: Estree.Program) => Message[];
|
|
67
72
|
processCommentDirectives: (data: string) => CommentDirectives;
|
|
68
|
-
visit: (node: Estree.
|
|
73
|
+
visit: (node: Estree.AnyNode) => Message[];
|
|
69
74
|
finalize: (msgs: Message[], hmrHeaderIndex: number) => TransformOutput;
|
|
70
75
|
transform: (headerHead: string) => TransformOutput;
|
|
71
76
|
}
|
|
77
|
+
export {};
|
|
@@ -3,7 +3,7 @@ import MagicString from "magic-string";
|
|
|
3
3
|
import { Parser } from 'acorn';
|
|
4
4
|
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
5
5
|
import { defaultHeuristicFuncOnly, Message } from '../adapters.js';
|
|
6
|
-
import { runtimeVars } from "../adapter-utils/index.js";
|
|
6
|
+
import { runtimeVars, varNames } from "../adapter-utils/index.js";
|
|
7
7
|
export const scriptParseOptions = {
|
|
8
8
|
sourceType: 'module',
|
|
9
9
|
ecmaVersion: 'latest',
|
|
@@ -27,6 +27,8 @@ export function scriptParseOptionsWithComments() {
|
|
|
27
27
|
accumulateComments.push({
|
|
28
28
|
type: block ? 'Block' : 'Line',
|
|
29
29
|
value: comment,
|
|
30
|
+
start: null,
|
|
31
|
+
end: null,
|
|
30
32
|
});
|
|
31
33
|
}
|
|
32
34
|
},
|
|
@@ -37,8 +39,17 @@ export function parseScript(content) {
|
|
|
37
39
|
const [opts, comments] = scriptParseOptionsWithComments();
|
|
38
40
|
return [ScriptParser.parse(content, opts), comments];
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
+
function initRuntimeStmt(rtConf, expr) {
|
|
43
|
+
return (file, funcName, parentFunc, additional) => {
|
|
44
|
+
const useReactive = rtConf.useReactive({ funcName, nested: parentFunc != null, file, additional });
|
|
45
|
+
if (useReactive.init == null) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const wrapInit = useReactive.init ? rtConf.reactive.wrapInit : rtConf.plain.wrapInit;
|
|
49
|
+
const catalogExpr = useReactive.init ? expr.reactive : expr.plain;
|
|
50
|
+
const runtimeExpr = `${varNames.rtWrap}(${catalogExpr})`;
|
|
51
|
+
return `\nconst ${varNames.rt} = ${wrapInit(runtimeExpr)}\n`;
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
54
|
export class Transformer {
|
|
44
55
|
index;
|
|
@@ -49,22 +60,42 @@ export class Transformer {
|
|
|
49
60
|
filename;
|
|
50
61
|
mstr;
|
|
51
62
|
pluralFunc;
|
|
52
|
-
// null possible because subclasses may disable init inside functions
|
|
53
63
|
initRuntime;
|
|
64
|
+
vars;
|
|
54
65
|
// state
|
|
55
66
|
commentDirectives = {};
|
|
56
67
|
insideProgram = false;
|
|
57
68
|
declaring = null;
|
|
69
|
+
currentFuncNested = false;
|
|
58
70
|
currentFuncDef = null;
|
|
59
71
|
currentCall;
|
|
60
72
|
currentTopLevelCall;
|
|
61
|
-
|
|
73
|
+
/** for subclasses. right now for svelte, if in <script module> */
|
|
74
|
+
additionalState = {};
|
|
75
|
+
constructor(content, filename, index, heuristic, pluralsFunc, catalogExpr, rtConf) {
|
|
62
76
|
this.index = index;
|
|
63
77
|
this.heuristic = heuristic;
|
|
64
78
|
this.pluralFunc = pluralsFunc;
|
|
65
79
|
this.content = content;
|
|
66
80
|
this.filename = filename;
|
|
67
|
-
this.initRuntime =
|
|
81
|
+
this.initRuntime = initRuntimeStmt(rtConf, catalogExpr);
|
|
82
|
+
const topLevelUseReactive = rtConf.useReactive({
|
|
83
|
+
funcName: null,
|
|
84
|
+
nested: false,
|
|
85
|
+
file: filename,
|
|
86
|
+
additional: this.additionalState,
|
|
87
|
+
});
|
|
88
|
+
const reactiveVars = rtConf.reactive?.wrapUse && runtimeVars(rtConf.reactive.wrapUse);
|
|
89
|
+
const plainVars = rtConf.plain?.wrapUse && runtimeVars(rtConf.plain.wrapUse);
|
|
90
|
+
this.vars = () => {
|
|
91
|
+
const useReactive = rtConf.useReactive({
|
|
92
|
+
funcName: this.currentFuncDef,
|
|
93
|
+
nested: this.currentFuncNested,
|
|
94
|
+
file: filename,
|
|
95
|
+
additional: this.additionalState,
|
|
96
|
+
}) ?? topLevelUseReactive;
|
|
97
|
+
return useReactive.use ? reactiveVars : plainVars;
|
|
98
|
+
};
|
|
68
99
|
}
|
|
69
100
|
checkHeuristicBool = (msgStr, detailsBase) => {
|
|
70
101
|
if (!msgStr) {
|
|
@@ -105,7 +136,7 @@ export class Transformer {
|
|
|
105
136
|
if (!pass) {
|
|
106
137
|
return [];
|
|
107
138
|
}
|
|
108
|
-
this.mstr.update(start, end, `${
|
|
139
|
+
this.mstr.update(start, end, `${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())})`);
|
|
109
140
|
return [msgInfo];
|
|
110
141
|
};
|
|
111
142
|
visitArrayExpression = (node) => node.elements.map(this.visit).flat();
|
|
@@ -115,9 +146,7 @@ export class Transformer {
|
|
|
115
146
|
visitProperty = (node) => {
|
|
116
147
|
const msgs = this.visit(node.key);
|
|
117
148
|
if (msgs.length && node.key.type === 'Literal' && typeof node.key.value === 'string' && !node.computed) {
|
|
118
|
-
// @ts-expect-error
|
|
119
149
|
this.mstr.appendRight(node.key.start, '[');
|
|
120
|
-
// @ts-expect-error
|
|
121
150
|
this.mstr.appendLeft(node.key.end, ']');
|
|
122
151
|
}
|
|
123
152
|
msgs.push(...this.visit(node.value));
|
|
@@ -158,7 +187,7 @@ export class Transformer {
|
|
|
158
187
|
const msgInfo = new Message(candidates, 'script', this.commentDirectives.context);
|
|
159
188
|
msgInfo.plural = true;
|
|
160
189
|
const index = this.index.get(msgInfo.toKey());
|
|
161
|
-
const pluralUpdate = `${
|
|
190
|
+
const pluralUpdate = `${this.vars().rtTPlural}(${index}), ${this.vars().rtPlural}`;
|
|
162
191
|
// @ts-ignore
|
|
163
192
|
this.mstr.update(secondArg.start, node.end - 1, pluralUpdate);
|
|
164
193
|
return [msgInfo];
|
|
@@ -266,23 +295,34 @@ export class Transformer {
|
|
|
266
295
|
};
|
|
267
296
|
visitExportNamedDeclaration = (node) => node.declaration ? this.visit(node.declaration) : [];
|
|
268
297
|
visitExportDefaultDeclaration = this.visitExportNamedDeclaration;
|
|
298
|
+
getRealBodyStart = (nodes) => {
|
|
299
|
+
for (const node of nodes) {
|
|
300
|
+
if (node.type === 'ExpressionStatement') {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
return node.start;
|
|
304
|
+
}
|
|
305
|
+
return nodes[0].start;
|
|
306
|
+
};
|
|
269
307
|
visitFunctionBody = (node, name) => {
|
|
270
308
|
const prevFuncDef = this.currentFuncDef;
|
|
271
|
-
|
|
309
|
+
const prevFuncNested = this.currentFuncNested;
|
|
310
|
+
const isBlock = node.type === 'BlockStatement';
|
|
311
|
+
this.currentFuncDef = isBlock ? name : prevFuncDef;
|
|
312
|
+
this.currentFuncNested = isBlock && name != null && prevFuncDef != null;
|
|
272
313
|
const msgs = this.visit(node);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// @ts-expect-error
|
|
277
|
-
node.start + 1, `\n${this.initRuntime}\n`);
|
|
314
|
+
if (msgs.length > 0 && isBlock) {
|
|
315
|
+
const initRuntime = this.initRuntime(this.filename, this.currentFuncDef, prevFuncDef, this.additionalState);
|
|
316
|
+
initRuntime && this.mstr.prependLeft(this.getRealBodyStart(node.body), initRuntime);
|
|
278
317
|
}
|
|
318
|
+
this.currentFuncNested = prevFuncNested;
|
|
279
319
|
this.currentFuncDef = prevFuncDef;
|
|
280
320
|
return msgs;
|
|
281
321
|
};
|
|
282
322
|
visitFunctionDeclaration = (node) => {
|
|
283
323
|
const declaring = this.declaring;
|
|
284
324
|
this.declaring = 'function';
|
|
285
|
-
const msgs = this.visitFunctionBody(node.body, node.id?.name ??
|
|
325
|
+
const msgs = this.visitFunctionBody(node.body, node.id?.name ?? '');
|
|
286
326
|
this.declaring = declaring;
|
|
287
327
|
return msgs;
|
|
288
328
|
};
|
|
@@ -329,7 +369,7 @@ export class Transformer {
|
|
|
329
369
|
this.mstr.update(end, end + 2, ', ');
|
|
330
370
|
}
|
|
331
371
|
const msgInfo = new Message(msgStr, 'script', this.commentDirectives.context);
|
|
332
|
-
let begin = `${
|
|
372
|
+
let begin = `${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())}`;
|
|
333
373
|
let end = ')';
|
|
334
374
|
if (node.expressions.length) {
|
|
335
375
|
begin += ', [';
|
|
@@ -370,8 +410,8 @@ export class Transformer {
|
|
|
370
410
|
visit = (node) => {
|
|
371
411
|
// for estree
|
|
372
412
|
const commentDirectives = { ...this.commentDirectives };
|
|
373
|
-
// @ts-expect-error
|
|
374
413
|
const comments = this.comments[node.start];
|
|
414
|
+
// @ts-expect-error
|
|
375
415
|
for (const comment of node.leadingComments ?? comments ?? []) {
|
|
376
416
|
this.commentDirectives = this.processCommentDirectives(comment.value.trim());
|
|
377
417
|
}
|
|
@@ -394,7 +434,7 @@ export class Transformer {
|
|
|
394
434
|
return {};
|
|
395
435
|
}
|
|
396
436
|
if (hmrData) {
|
|
397
|
-
this.mstr.prependRight(hmrHeaderIndex, `\nconst ${
|
|
437
|
+
this.mstr.prependRight(hmrHeaderIndex, `\nconst ${varNames.hmrUpdate} = ${JSON.stringify(hmrData)}\n`);
|
|
398
438
|
}
|
|
399
439
|
return {
|
|
400
440
|
code: this.mstr.toString(),
|
|
@@ -408,7 +448,7 @@ export class Transformer {
|
|
|
408
448
|
this.mstr = new MagicString(this.content);
|
|
409
449
|
const msgs = this.visit(ast);
|
|
410
450
|
if (msgs.length) {
|
|
411
|
-
this.mstr.appendRight(
|
|
451
|
+
this.mstr.appendRight(this.getRealBodyStart(ast.body), headerHead + '\n');
|
|
412
452
|
}
|
|
413
453
|
return this.finalize(msgs, 0);
|
|
414
454
|
};
|
package/dist/adapters.d.ts
CHANGED
|
@@ -38,9 +38,13 @@ export type GlobConf = string | string[] | {
|
|
|
38
38
|
include: string | string[];
|
|
39
39
|
ignore: string | string[];
|
|
40
40
|
};
|
|
41
|
+
export type CatalogExpr = {
|
|
42
|
+
plain: string;
|
|
43
|
+
reactive: string;
|
|
44
|
+
};
|
|
41
45
|
export type TransformHeader = {
|
|
42
46
|
head: string;
|
|
43
|
-
expr:
|
|
47
|
+
expr: CatalogExpr;
|
|
44
48
|
};
|
|
45
49
|
type TransformCtx = {
|
|
46
50
|
content: string;
|
|
@@ -61,19 +65,45 @@ export type TransformOutput = {
|
|
|
61
65
|
msgs: Message[];
|
|
62
66
|
};
|
|
63
67
|
export type TransformFunc = (ctx: TransformCtx) => TransformOutput;
|
|
68
|
+
export type WrapFunc = (expr: string) => string;
|
|
69
|
+
export type UseReactiveFunc = (details: {
|
|
70
|
+
funcName?: string;
|
|
71
|
+
nested: boolean;
|
|
72
|
+
file: string;
|
|
73
|
+
additional: object;
|
|
74
|
+
}) => {
|
|
75
|
+
/** null to disable initializing */
|
|
76
|
+
init: boolean | null;
|
|
77
|
+
use: boolean;
|
|
78
|
+
};
|
|
79
|
+
type RuntimeConfDetails = {
|
|
80
|
+
wrapInit: WrapFunc;
|
|
81
|
+
wrapUse: WrapFunc;
|
|
82
|
+
importName: 'default' | string;
|
|
83
|
+
};
|
|
84
|
+
export type RuntimeConf = {
|
|
85
|
+
useReactive: UseReactiveFunc;
|
|
86
|
+
plain: RuntimeConfDetails;
|
|
87
|
+
reactive: RuntimeConfDetails;
|
|
88
|
+
};
|
|
89
|
+
export type LoaderPath = {
|
|
90
|
+
client: string;
|
|
91
|
+
ssr: string;
|
|
92
|
+
};
|
|
64
93
|
export type AdapterPassThruOpts = {
|
|
65
94
|
files: GlobConf;
|
|
66
95
|
catalog: string;
|
|
67
96
|
granularLoad: boolean;
|
|
68
97
|
bundleLoad: boolean;
|
|
69
98
|
generateLoadID: (filename: string) => string;
|
|
70
|
-
loaderPath?: string;
|
|
99
|
+
loaderPath?: string | LoaderPath;
|
|
71
100
|
writeFiles: {
|
|
72
101
|
compiled?: boolean;
|
|
73
102
|
proxy?: boolean;
|
|
74
103
|
transformed?: boolean;
|
|
75
104
|
outDir?: string;
|
|
76
105
|
};
|
|
106
|
+
runtime: Partial<RuntimeConf>;
|
|
77
107
|
};
|
|
78
108
|
export type Adapter = AdapterPassThruOpts & {
|
|
79
109
|
transform: TransformFunc;
|
|
@@ -81,7 +111,7 @@ export type Adapter = AdapterPassThruOpts & {
|
|
|
81
111
|
loaderExts: string[];
|
|
82
112
|
/** available loader names, can do auto detection logic to sort, dependencies given */
|
|
83
113
|
defaultLoaders: () => string[] | Promise<string[]>;
|
|
84
|
-
defaultLoaderPath: (loaderName: string) => string;
|
|
114
|
+
defaultLoaderPath: (loaderName: string) => LoaderPath | string;
|
|
85
115
|
docsUrl: string;
|
|
86
116
|
};
|
|
87
117
|
export type AdapterArgs = Partial<AdapterPassThruOpts> & {
|
package/dist/cli/init.js
CHANGED
|
@@ -19,7 +19,7 @@ export async function init(config, locales, logger) {
|
|
|
19
19
|
const loaders = await adapter.defaultLoaders();
|
|
20
20
|
let existing = false;
|
|
21
21
|
if (loaderPath) {
|
|
22
|
-
if (!empty) {
|
|
22
|
+
if (!Object.values(empty).some(side => side)) { // all non empty
|
|
23
23
|
loaders.unshift('existing');
|
|
24
24
|
existing = true;
|
|
25
25
|
}
|
|
@@ -27,8 +27,7 @@ export async function init(config, locales, logger) {
|
|
|
27
27
|
else {
|
|
28
28
|
loaderPath = handler.getLoaderPaths()[0];
|
|
29
29
|
}
|
|
30
|
-
logger.log(`${existing ? 'Edit' : 'Create'} loader for ${adapterName}
|
|
31
|
-
await mkdir(dirname(loaderPath), { recursive: true });
|
|
30
|
+
logger.log(`${existing ? 'Edit' : 'Create'} loader for ${adapterName}`);
|
|
32
31
|
let loader = loaders[0];
|
|
33
32
|
if (loaders.length > 1) {
|
|
34
33
|
loader = await ask(loaders, `Select default loader for adapter: ${adapterName}`, logger);
|
|
@@ -37,19 +36,18 @@ export async function init(config, locales, logger) {
|
|
|
37
36
|
logger.log('Keep existing loader');
|
|
38
37
|
continue;
|
|
39
38
|
}
|
|
40
|
-
|
|
39
|
+
const defaultLoader = adapter.defaultLoaderPath(loader);
|
|
40
|
+
const defaultPaths = typeof defaultLoader === 'string' ? {
|
|
41
|
+
client: defaultLoader,
|
|
42
|
+
ssr: defaultLoader,
|
|
43
|
+
} : defaultLoader;
|
|
44
|
+
for (const [side, path] of Object.entries(defaultPaths)) {
|
|
45
|
+
await mkdir(dirname(path), { recursive: true });
|
|
46
|
+
await copyFile(path, loaderPath[side]);
|
|
47
|
+
keysByLoaderPath[path] = key;
|
|
48
|
+
}
|
|
41
49
|
logger.log(`Initial extract for ${adapterName}`);
|
|
42
50
|
await extractAdap(handler, sharedState, adapter.files, locales, false, logger);
|
|
43
|
-
if (handler.loaderPath in keysByLoaderPath) {
|
|
44
|
-
throw new Error([
|
|
45
|
-
'While catalogs can be shared, the same loader cannot be used by multiple adapters',
|
|
46
|
-
`Conflicting: ${adapterName} and ${color.cyan(keysByLoaderPath[handler.loaderPath])}`,
|
|
47
|
-
'Specify a different loaderPath for one of them.'
|
|
48
|
-
].join('\n'));
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
keysByLoaderPath[handler.loaderPath] = key;
|
|
52
|
-
}
|
|
53
51
|
extractedNew = true;
|
|
54
52
|
logger.log(`\n${adapterName}: Read more at ${color.cyan(adapter.docsUrl)}.`);
|
|
55
53
|
}
|
package/dist/cli/status.js
CHANGED
|
@@ -36,17 +36,17 @@ export async function status(config, locales, logger) {
|
|
|
36
36
|
const { total, obsolete, untranslated } = stats;
|
|
37
37
|
const locName = getLanguageName(locale);
|
|
38
38
|
logger.log([
|
|
39
|
-
` ${locName}: ${color.cyan(`total: ${total}
|
|
40
|
-
color.yellow(`untranslated: ${untranslated}
|
|
39
|
+
` ${locName}: ${color.cyan(`total: ${total}`)}`,
|
|
40
|
+
color.yellow(`untranslated: ${untranslated}`),
|
|
41
41
|
color.grey(`obsolete: ${obsolete}`),
|
|
42
|
-
].join(' '));
|
|
43
|
-
}
|
|
44
|
-
if (loaderPath && !empty) {
|
|
45
|
-
logger.log(` Loader file: ${color.cyan(loaderPath)}`);
|
|
46
|
-
continue;
|
|
42
|
+
].join(', '));
|
|
47
43
|
}
|
|
48
44
|
if (loaderPath) {
|
|
49
|
-
logger.
|
|
45
|
+
logger.log(` Loader files:`);
|
|
46
|
+
for (const [side, path] of Object.entries(loaderPath)) {
|
|
47
|
+
logger.log(` ${color.cyan(side)}: ${color.cyan(path)}${empty[side] ? color.yellow(' (empty)') : ''}`);
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
52
|
logger.warn(' No loader file found.');
|
package/dist/handler.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IndexTracker } from "./adapters.js";
|
|
2
|
-
import type { Adapter, GlobConf } from "./adapters.js";
|
|
2
|
+
import type { Adapter, GlobConf, LoaderPath } from "./adapters.js";
|
|
3
3
|
import { type CompiledElement } from "./compile.js";
|
|
4
4
|
import { type ItemType } from "./gemini.js";
|
|
5
5
|
import { type Matcher } from 'picomatch';
|
|
@@ -35,10 +35,13 @@ type GranularState = {
|
|
|
35
35
|
compiled: CompiledCatalogs;
|
|
36
36
|
indexTracker: IndexTracker;
|
|
37
37
|
};
|
|
38
|
+
type LoaderPathEmpty = {
|
|
39
|
+
[K in keyof LoaderPath]: boolean;
|
|
40
|
+
};
|
|
38
41
|
export declare class AdapterHandler {
|
|
39
42
|
#private;
|
|
40
43
|
key: string;
|
|
41
|
-
loaderPath:
|
|
44
|
+
loaderPath: LoaderPath;
|
|
42
45
|
proxyPath: string;
|
|
43
46
|
outDir: string;
|
|
44
47
|
compiledHead: Record<string, string>;
|
|
@@ -47,11 +50,11 @@ export declare class AdapterHandler {
|
|
|
47
50
|
granularStateByFile: Record<string, GranularState>;
|
|
48
51
|
granularStateByID: Record<string, GranularState>;
|
|
49
52
|
catalogPathsToLocales: Record<string, string>;
|
|
50
|
-
constructor(adapter: Adapter, key: string
|
|
51
|
-
getLoaderPaths():
|
|
53
|
+
constructor(adapter: Adapter, key: string, config: ConfigPartial, mode: Mode, virtualPrefix: string, projectRoot: string, log: Logger);
|
|
54
|
+
getLoaderPaths(): LoaderPath[];
|
|
52
55
|
getLoaderPath(): Promise<{
|
|
53
|
-
path:
|
|
54
|
-
empty:
|
|
56
|
+
path: LoaderPath | null;
|
|
57
|
+
empty: LoaderPathEmpty;
|
|
55
58
|
}>;
|
|
56
59
|
/** Get both catalog virtual module names AND HMR event names */
|
|
57
60
|
virtModEvent: (locale: string, loadID: string | null) => string;
|
|
@@ -70,7 +73,7 @@ export declare class AdapterHandler {
|
|
|
70
73
|
ignore: string[];
|
|
71
74
|
}];
|
|
72
75
|
savePoAndCompile: (loc: string) => Promise<void>;
|
|
73
|
-
transform: (content: string, filename: string, hmrVersion?: number) => Promise<{
|
|
76
|
+
transform: (content: string, filename: string, hmrVersion?: number, ssr?: boolean) => Promise<{
|
|
74
77
|
code?: string;
|
|
75
78
|
map?: any;
|
|
76
79
|
}>;
|
package/dist/handler.js
CHANGED
|
@@ -10,7 +10,7 @@ import { normalize } from "node:path";
|
|
|
10
10
|
import { getLanguageName } from "./config.js";
|
|
11
11
|
import { color } from './log.js';
|
|
12
12
|
import { catalogVarName } from './runtime.js';
|
|
13
|
-
import {
|
|
13
|
+
import { varNames } from './adapter-utils/index.js';
|
|
14
14
|
const defaultPluralRule = {
|
|
15
15
|
nplurals: 2,
|
|
16
16
|
plural: 'n == 1 ? 0 : 1',
|
|
@@ -96,37 +96,53 @@ export class AdapterHandler {
|
|
|
96
96
|
}
|
|
97
97
|
getLoaderPaths() {
|
|
98
98
|
if (this.#adapter.loaderPath != null) {
|
|
99
|
+
if (typeof this.#adapter.loaderPath === 'string') {
|
|
100
|
+
return [{
|
|
101
|
+
client: this.#adapter.loaderPath,
|
|
102
|
+
ssr: this.#adapter.loaderPath,
|
|
103
|
+
}];
|
|
104
|
+
}
|
|
99
105
|
return [this.#adapter.loaderPath];
|
|
100
106
|
}
|
|
101
107
|
const catalogToLoader = this.#adapter.catalog.replace('{locale}', 'loader');
|
|
102
108
|
const paths = [];
|
|
103
109
|
for (const ext of this.#adapter.loaderExts) {
|
|
104
|
-
let path = catalogToLoader
|
|
110
|
+
let path = catalogToLoader;
|
|
105
111
|
if (path.startsWith('./')) {
|
|
106
112
|
path = path.slice(2);
|
|
107
113
|
}
|
|
108
|
-
|
|
114
|
+
const pathClient = path + ext;
|
|
115
|
+
paths.push({ client: pathClient, ssr: path + '.ssr' + ext }, { client: pathClient, ssr: pathClient });
|
|
109
116
|
}
|
|
110
117
|
return paths;
|
|
111
118
|
}
|
|
112
119
|
async getLoaderPath() {
|
|
120
|
+
const empty = { client: true, ssr: true };
|
|
113
121
|
for (const path of this.getLoaderPaths()) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
let bothExist = true;
|
|
123
|
+
for (const side in empty) {
|
|
124
|
+
try {
|
|
125
|
+
const contents = await readFile(path[side]);
|
|
126
|
+
empty[side] = contents.toString().trim() === '';
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err.code !== 'ENOENT') {
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
bothExist = false;
|
|
133
|
+
break;
|
|
121
134
|
}
|
|
135
|
+
}
|
|
136
|
+
if (!bothExist) {
|
|
122
137
|
continue;
|
|
123
138
|
}
|
|
139
|
+
return { path, empty };
|
|
124
140
|
}
|
|
125
|
-
return { path: null, empty
|
|
141
|
+
return { path: null, empty };
|
|
126
142
|
}
|
|
127
143
|
async #initPaths() {
|
|
128
144
|
const { path: loaderPath, empty } = await this.getLoaderPath();
|
|
129
|
-
if (!loaderPath || empty) {
|
|
145
|
+
if (!loaderPath || Object.values(empty).some(side => side)) {
|
|
130
146
|
throw new Error('No valid loader file found.');
|
|
131
147
|
}
|
|
132
148
|
this.loaderPath = loaderPath;
|
|
@@ -366,7 +382,7 @@ export class AdapterHandler {
|
|
|
366
382
|
globConfToArgs = (conf) => {
|
|
367
383
|
let patterns = [];
|
|
368
384
|
// ignore generated files
|
|
369
|
-
const options = { ignore: [this.loaderPath] };
|
|
385
|
+
const options = { ignore: [this.loaderPath.client, this.loaderPath.ssr] };
|
|
370
386
|
if (this.#adapter.writeFiles.proxy) {
|
|
371
387
|
options.ignore.push(this.proxyPath);
|
|
372
388
|
}
|
|
@@ -430,36 +446,67 @@ export class AdapterHandler {
|
|
|
430
446
|
await this.compile(loc);
|
|
431
447
|
}
|
|
432
448
|
};
|
|
433
|
-
#
|
|
449
|
+
#putImportSpec = (varName, alias, importsFuncs) => {
|
|
450
|
+
if (!varName) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (varName === 'default') {
|
|
454
|
+
importsFuncs.unshift(alias); // default imports are first
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
importsFuncs.push(`{${varName} as ${alias}}`);
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
#hmrUpdateFunc = (getFuncName, getFuncNameHmr) => {
|
|
461
|
+
const catalogVar = '_w_catalog_';
|
|
462
|
+
return `
|
|
463
|
+
function ${getFuncName}(loadID) {
|
|
464
|
+
const ${catalogVar} = ${getFuncNameHmr}(loadID)
|
|
465
|
+
${catalogVar}?.update?.(${varNames.hmrUpdate})
|
|
466
|
+
return ${catalogVar}
|
|
467
|
+
}
|
|
468
|
+
`;
|
|
469
|
+
};
|
|
470
|
+
#prepareHeader = (filename, loadID, hasHmr, ssr) => {
|
|
434
471
|
let loaderRelTo = filename;
|
|
435
472
|
if (this.#adapter.writeFiles.transformed) {
|
|
436
473
|
loaderRelTo = resolve(this.outDir + '/' + filename);
|
|
437
474
|
}
|
|
438
|
-
let loaderPath = relative(dirname(loaderRelTo), this.loaderPath);
|
|
475
|
+
let loaderPath = relative(dirname(loaderRelTo), ssr ? this.loaderPath.ssr : this.loaderPath.client);
|
|
439
476
|
if (!loaderPath.startsWith('.')) {
|
|
440
477
|
loaderPath = `./${loaderPath}`;
|
|
441
478
|
}
|
|
442
|
-
const
|
|
443
|
-
|
|
479
|
+
const importsFuncs = [];
|
|
480
|
+
const runtimeConf = this.#adapter.runtime;
|
|
481
|
+
let getFuncPlain = '_w_load_';
|
|
482
|
+
let getFuncReactive = getFuncPlain + 'rx_';
|
|
483
|
+
let head = [];
|
|
444
484
|
if (hasHmr) {
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
head += `import ${getFuncName} from "${loaderPath}"`;
|
|
485
|
+
const getFuncPlainHmr = getFuncPlain;
|
|
486
|
+
const getFuncReactiveHmr = getFuncReactive;
|
|
487
|
+
getFuncPlain += 'hmr_';
|
|
488
|
+
getFuncReactive += 'hmr_';
|
|
489
|
+
if (runtimeConf.plain?.importName) {
|
|
490
|
+
head.push(this.#hmrUpdateFunc(getFuncPlainHmr, getFuncPlain));
|
|
491
|
+
}
|
|
492
|
+
if (runtimeConf.reactive?.importName) {
|
|
493
|
+
head.push(this.#hmrUpdateFunc(getFuncReactiveHmr, getFuncReactive));
|
|
494
|
+
}
|
|
458
495
|
}
|
|
496
|
+
this.#putImportSpec(runtimeConf.plain?.importName, getFuncPlain, importsFuncs);
|
|
497
|
+
this.#putImportSpec(runtimeConf.reactive?.importName, getFuncReactive, importsFuncs);
|
|
498
|
+
head = [
|
|
499
|
+
`import ${varNames.rtWrap} from 'wuchale/runtime'`,
|
|
500
|
+
`import ${importsFuncs.join(',')} from "${loaderPath}"`,
|
|
501
|
+
...head,
|
|
502
|
+
];
|
|
459
503
|
if (!this.#adapter.bundleLoad) {
|
|
460
504
|
return {
|
|
461
|
-
head,
|
|
462
|
-
expr:
|
|
505
|
+
head: head.join('\n'),
|
|
506
|
+
expr: {
|
|
507
|
+
plain: `${getFuncPlain}('${loadID}')`,
|
|
508
|
+
reactive: `${getFuncReactive}('${loadID}')`,
|
|
509
|
+
}
|
|
463
510
|
};
|
|
464
511
|
}
|
|
465
512
|
const imports = [];
|
|
@@ -472,14 +519,17 @@ export class AdapterHandler {
|
|
|
472
519
|
const catalogsVarName = '_w_catalogs_';
|
|
473
520
|
return {
|
|
474
521
|
head: [
|
|
475
|
-
head,
|
|
476
522
|
...imports,
|
|
523
|
+
...head,
|
|
477
524
|
`const ${catalogsVarName} = {${objElms.join(',')}}`
|
|
478
525
|
].join('\n'),
|
|
479
|
-
expr:
|
|
526
|
+
expr: {
|
|
527
|
+
plain: `${getFuncPlain}(${catalogsVarName})`,
|
|
528
|
+
reactive: `${getFuncReactive}(${catalogsVarName})`,
|
|
529
|
+
}
|
|
480
530
|
};
|
|
481
531
|
};
|
|
482
|
-
transform = async (content, filename, hmrVersion = -1) => {
|
|
532
|
+
transform = async (content, filename, hmrVersion = -1, ssr = false) => {
|
|
483
533
|
let indexTracker = this.sharedState.indexTracker;
|
|
484
534
|
let loadID = this.key;
|
|
485
535
|
let compiled = this.sharedState.compiled;
|
|
@@ -493,7 +543,7 @@ export class AdapterHandler {
|
|
|
493
543
|
content,
|
|
494
544
|
filename,
|
|
495
545
|
index: indexTracker,
|
|
496
|
-
header: this.#prepareHeader(filename, loadID, hmrVersion >= 0),
|
|
546
|
+
header: this.#prepareHeader(filename, loadID, hmrVersion >= 0, ssr),
|
|
497
547
|
});
|
|
498
548
|
const hmrKeys = {};
|
|
499
549
|
for (const loc of this.#locales) {
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export { AdapterHandler } from './handler.js';
|
|
|
4
4
|
export type { Mode, SharedStates } from './handler.js';
|
|
5
5
|
export { Logger } from './log.js';
|
|
6
6
|
export { Message, IndexTracker, defaultGenerateLoadID, defaultHeuristic, } from './adapters.js';
|
|
7
|
-
export type { Adapter, AdapterArgs, AdapterPassThruOpts, HeuristicFunc, TransformOutput, TransformHeader, CommentDirectives, } from './adapters.js';
|
|
7
|
+
export type { Adapter, AdapterArgs, AdapterPassThruOpts, RuntimeConf, CatalogExpr, HeuristicFunc, TransformOutput, TransformHeader, CommentDirectives, UseReactiveFunc, } from './adapters.js';
|