wuchale 0.21.2 → 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.
- package/dist/adapter-vanilla/transformer.js +8 -7
- package/dist/ai/index.js +3 -2
- package/dist/bundlers/vite.d.ts +32 -0
- package/dist/bundlers/vite.js +70 -0
- package/dist/cli/check.d.ts +3 -0
- package/dist/cli/check.js +35 -0
- package/dist/cli/index.js +42 -11
- package/dist/cli/status.d.ts +2 -1
- package/dist/cli/status.js +33 -45
- package/dist/fs.d.ts +8 -0
- package/dist/fs.js +29 -0
- package/dist/handler/files.d.ts +13 -3
- package/dist/handler/files.js +26 -29
- package/dist/handler/index.d.ts +6 -6
- package/dist/handler/index.js +73 -51
- package/dist/handler/state.d.ts +0 -4
- package/dist/handler/state.js +26 -19
- package/dist/handler/url.d.ts +3 -2
- package/dist/handler/url.js +11 -8
- package/dist/hub.d.ts +56 -0
- package/dist/hub.js +348 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pofile.js +8 -7
- package/dist/storage.d.ts +0 -1
- package/package.json +10 -2
- package/dist/cli/extract.d.ts +0 -2
- package/dist/cli/extract.js +0 -87
|
@@ -113,7 +113,7 @@ 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)}
|
|
116
|
+
return `\nconst ${this.currentRtVar} = ${wrapInit(expr)};\n`;
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
fullHeuristicDetails = (detailsBase) => ({
|
|
@@ -408,19 +408,20 @@ export class Transformer {
|
|
|
408
408
|
if (!node.init) {
|
|
409
409
|
return [];
|
|
410
410
|
}
|
|
411
|
+
const init = this.getActualExpression(node.init);
|
|
411
412
|
if (topLevel) {
|
|
412
|
-
|
|
413
|
-
if (init?.type === 'ArrowFunctionExpression' || init?.type === 'FunctionExpression') {
|
|
413
|
+
if (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') {
|
|
414
414
|
this.heuristciDetails.declaring = 'function';
|
|
415
415
|
}
|
|
416
416
|
else {
|
|
417
417
|
this.heuristciDetails.declaring = 'variable';
|
|
418
|
-
if (init?.type === 'CallExpression') {
|
|
419
|
-
this.heuristciDetails.topLevelCall = this.getCalleeName(init.callee);
|
|
420
|
-
}
|
|
421
418
|
}
|
|
422
419
|
}
|
|
423
|
-
|
|
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);
|
|
423
|
+
}
|
|
424
|
+
return [...msgs, ...this.visit(node.init)];
|
|
424
425
|
});
|
|
425
426
|
visitExpressionStatement = (node) => this.withUpdateTLDetails(topLevel => {
|
|
426
427
|
const expr = this.getActualExpression(node.expression);
|
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
|
|
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 !==
|
|
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,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 {
|
|
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')}
|
|
51
|
-
${color.cyan('--watch')}, ${color.cyan('-w')}
|
|
52
|
-
${color.cyan('--sync')}
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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}`);
|
package/dist/cli/status.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { type Config } from '../config.js';
|
|
2
|
-
export declare
|
|
2
|
+
export declare const statusHelp: string;
|
|
3
|
+
export declare function status(config: Config, root: string, json: boolean): Promise<void>;
|
package/dist/cli/status.js
CHANGED
|
@@ -1,59 +1,47 @@
|
|
|
1
1
|
import { relative } from 'node:path';
|
|
2
2
|
import { getLanguageName } from '../config.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { color
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.log(`${color.magenta(key)}
|
|
33
|
-
if (
|
|
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(
|
|
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
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
+
};
|
package/dist/handler/files.d.ts
CHANGED
|
@@ -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: (
|
|
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;
|
package/dist/handler/files.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { mkdir, readFile, statfs, writeFile } from 'node:fs/promises';
|
|
2
1
|
import { dirname, relative, resolve } from 'node:path';
|
|
3
2
|
import { platform } from 'node:process';
|
|
4
3
|
import { catalogVarName } from '../runtime.js';
|
|
5
4
|
import {} from '../url.js';
|
|
6
|
-
const dataFileName = 'data.js';
|
|
5
|
+
export const dataFileName = 'data.js';
|
|
7
6
|
export const generatedDir = '.wuchale';
|
|
8
7
|
export const objKeyLocale = (locale) => (locale.includes('-') ? `'${locale}'` : locale);
|
|
9
8
|
export function normalizeSep(path) {
|
|
@@ -12,10 +11,10 @@ export function normalizeSep(path) {
|
|
|
12
11
|
}
|
|
13
12
|
return path.replaceAll('\\', '/');
|
|
14
13
|
}
|
|
15
|
-
export function globConfToArgs(conf, localesDir, outDir) {
|
|
14
|
+
export function globConfToArgs(conf, cwd, localesDir, outDir) {
|
|
16
15
|
let patterns = [];
|
|
17
16
|
// ignore generated files
|
|
18
|
-
const options = { ignore: [localesDir] };
|
|
17
|
+
const options = { ignore: [localesDir], cwd };
|
|
19
18
|
if (outDir) {
|
|
20
19
|
options.ignore.push(outDir);
|
|
21
20
|
}
|
|
@@ -45,6 +44,7 @@ export class Files {
|
|
|
45
44
|
key;
|
|
46
45
|
ownerKey;
|
|
47
46
|
#adapter;
|
|
47
|
+
#fs;
|
|
48
48
|
// paths
|
|
49
49
|
loaderPath;
|
|
50
50
|
proxyPath;
|
|
@@ -53,10 +53,11 @@ export class Files {
|
|
|
53
53
|
#urlsFname;
|
|
54
54
|
#localesDir;
|
|
55
55
|
#projectRoot;
|
|
56
|
-
constructor(adapter, key, localesDir, root) {
|
|
56
|
+
constructor(adapter, key, localesDir, fs, root) {
|
|
57
57
|
this.key = key;
|
|
58
58
|
this.#adapter = adapter;
|
|
59
59
|
this.#localesDir = localesDir;
|
|
60
|
+
this.#fs = fs;
|
|
60
61
|
this.#projectRoot = root;
|
|
61
62
|
}
|
|
62
63
|
getLoaderPaths() {
|
|
@@ -84,13 +85,7 @@ export class Files {
|
|
|
84
85
|
for (const path of paths) {
|
|
85
86
|
let bothExist = true;
|
|
86
87
|
for (const side in path) {
|
|
87
|
-
|
|
88
|
-
await statfs(path[side]);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
if (err.code !== 'ENOENT') {
|
|
92
|
-
throw err;
|
|
93
|
-
}
|
|
88
|
+
if (!(await this.#fs.exists(path[side]))) {
|
|
94
89
|
bothExist = false;
|
|
95
90
|
break;
|
|
96
91
|
}
|
|
@@ -169,19 +164,12 @@ export class Files {
|
|
|
169
164
|
return this.genProxyContent(object, loadIDs, imports);
|
|
170
165
|
}
|
|
171
166
|
writeProxies = async (locales, loadIDs, loadIDsImport) => {
|
|
172
|
-
await
|
|
173
|
-
await
|
|
167
|
+
await this.#fs.write(this.proxyPath, this.genProxy(locales, loadIDs, loadIDsImport));
|
|
168
|
+
await this.#fs.write(this.proxySyncPath, this.genProxySync(locales, loadIDs, loadIDsImport));
|
|
174
169
|
};
|
|
175
|
-
init = async (
|
|
170
|
+
init = async (ownerKey) => {
|
|
176
171
|
this.ownerKey = ownerKey;
|
|
177
172
|
await this.#initPaths();
|
|
178
|
-
await mkdir(resolve(this.#localesDir, generatedDir), { recursive: true });
|
|
179
|
-
// data file
|
|
180
|
-
await writeFile(resolve(this.#localesDir, dataFileName), [
|
|
181
|
-
`/** @typedef {('${locales.join("'|'")}')} Locale */`,
|
|
182
|
-
`/** @type {Locale[]} */`,
|
|
183
|
-
`export const locales = ['${locales.join("','")}']`,
|
|
184
|
-
].join('\n'));
|
|
185
173
|
if (this.#adapter.defaultLoaderPath == null) {
|
|
186
174
|
// using custom loaders
|
|
187
175
|
return;
|
|
@@ -194,13 +182,13 @@ export class Files {
|
|
|
194
182
|
else {
|
|
195
183
|
loaderTemplate = this.#adapter.defaultLoaderPath[side];
|
|
196
184
|
}
|
|
197
|
-
const loaderContent = (await
|
|
185
|
+
const loaderContent = (await this.#fs.read(loaderTemplate))
|
|
198
186
|
.toString()
|
|
199
187
|
.replaceAll('${PROXY}', `./${generatedDir}/${this.#proxyFileName()}`)
|
|
200
188
|
.replaceAll('${PROXY_SYNC}', `./${generatedDir}/${this.#proxyFileName(true)}`)
|
|
201
189
|
.replaceAll('${DATA}', `./${dataFileName}`)
|
|
202
190
|
.replaceAll('${KEY}', this.key);
|
|
203
|
-
await
|
|
191
|
+
await this.#fs.write(this.loaderPath[side], loaderContent);
|
|
204
192
|
}
|
|
205
193
|
};
|
|
206
194
|
writeUrlFiles = async (manifest, fallbackLocale) => {
|
|
@@ -211,7 +199,7 @@ export class Files {
|
|
|
211
199
|
`/** @type {import('wuchale/url').URLManifest} */`,
|
|
212
200
|
`export default ${JSON.stringify(manifest)}`,
|
|
213
201
|
].join('\n');
|
|
214
|
-
await
|
|
202
|
+
await this.#fs.write(this.#urlManifestFname, urlManifestData);
|
|
215
203
|
const urlFileContent = [
|
|
216
204
|
'import {URLMatcher, deLocalizeDefault} from "wuchale/url"',
|
|
217
205
|
`import {locales} from "./${dataFileName}"`,
|
|
@@ -219,7 +207,16 @@ export class Files {
|
|
|
219
207
|
`export const getLocale = (/** @type {URL} */ url) => deLocalizeDefault(url.pathname, locales)[1] ?? '${fallbackLocale}'`,
|
|
220
208
|
`export const matchUrl = URLMatcher(manifest, locales)`,
|
|
221
209
|
].join('\n');
|
|
222
|
-
await
|
|
210
|
+
await this.#fs.write(this.#urlsFname, urlFileContent);
|
|
211
|
+
};
|
|
212
|
+
getManifestFilePath(id) {
|
|
213
|
+
const ownerKey = this.ownerKey;
|
|
214
|
+
return resolve(this.#localesDir, generatedDir, `${ownerKey}.${id ?? ownerKey}.manifest.js`);
|
|
215
|
+
}
|
|
216
|
+
writeManifest = async (keys, id) => {
|
|
217
|
+
const content = `/** @type {(string | {text: string | string[], context?: string, isUrl?: boolean} | null)[]} */\n` +
|
|
218
|
+
`export const keys = ${JSON.stringify(keys)}`;
|
|
219
|
+
await this.#fs.write(this.getManifestFilePath(id), content);
|
|
223
220
|
};
|
|
224
221
|
writeCatalogModule = async (compiledData, pluralRule, locale, hmrVersion, loadID) => {
|
|
225
222
|
const compiledItems = JSON.stringify(compiledData);
|
|
@@ -244,15 +241,15 @@ export class Files {
|
|
|
244
241
|
}
|
|
245
242
|
`;
|
|
246
243
|
}
|
|
247
|
-
await
|
|
244
|
+
await this.#fs.write(this.getCompiledFilePath(locale, loadID), module);
|
|
248
245
|
};
|
|
249
246
|
writeTransformed = async (filename, content) => {
|
|
250
247
|
if (!this.#adapter.outDir) {
|
|
251
248
|
return;
|
|
252
249
|
}
|
|
253
250
|
const fname = resolve(this.#adapter.outDir + '/' + filename);
|
|
254
|
-
await mkdir(dirname(fname)
|
|
255
|
-
await
|
|
251
|
+
await this.#fs.mkdir(dirname(fname));
|
|
252
|
+
await this.#fs.write(fname, content);
|
|
256
253
|
};
|
|
257
254
|
getImportLoaderPath(forServer, relativeTo) {
|
|
258
255
|
return this.getImportPath(forServer ? this.loaderPath.server : this.loaderPath.client, relativeTo);
|
package/dist/handler/index.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { type Matcher } from 'picomatch';
|
|
|
2
2
|
import type { Adapter, Message, TransformOutputCode } from '../adapters.js';
|
|
3
3
|
import { type CompiledElement } from '../compile.js';
|
|
4
4
|
import type { ConfigPartial } from '../config.js';
|
|
5
|
+
import type { FS } from '../fs.js';
|
|
5
6
|
import type { Logger } from '../log.js';
|
|
6
7
|
import { type FileRef, type Item } from '../storage.js';
|
|
7
8
|
import { Files } from './files.js';
|
|
8
|
-
import { type SharedState,
|
|
9
|
+
import { type SharedState, State } from './state.js';
|
|
9
10
|
import { URLHandler } from './url.js';
|
|
10
11
|
export type Mode = 'dev' | 'build' | 'cli';
|
|
11
12
|
type TrackedRefs = Map<string, {
|
|
@@ -16,19 +17,18 @@ export declare class AdapterHandler {
|
|
|
16
17
|
#private;
|
|
17
18
|
key: string;
|
|
18
19
|
/** config.locales and adapter's sourceLocale */
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
readonly sourceLocale: string;
|
|
21
|
+
readonly adapter: Adapter;
|
|
21
22
|
fileMatches: Matcher;
|
|
22
23
|
sharedState: SharedState;
|
|
23
24
|
granularState: State;
|
|
24
25
|
files: Files;
|
|
25
26
|
url: URLHandler;
|
|
26
27
|
onBeforeSave: () => void;
|
|
27
|
-
constructor(adapter: Adapter, key: string, config: ConfigPartial, mode: Mode, projectRoot: string, log: Logger);
|
|
28
|
+
constructor(adapter: Adapter, key: string, config: ConfigPartial, mode: Mode, fs: FS, projectRoot: string, log: Logger);
|
|
28
29
|
/** return two arrays: the corresponding one, and the one to import from in the case of shared catalogs */
|
|
29
30
|
getLoadIDs(): [string[], string[]];
|
|
30
|
-
|
|
31
|
-
init: (sharedStates: SharedStates) => Promise<void>;
|
|
31
|
+
init: (sharedState: SharedState) => Promise<void>;
|
|
32
32
|
loadStorage: () => Promise<void>;
|
|
33
33
|
saveStorage: () => Promise<void>;
|
|
34
34
|
compile: (hmrVersion?: number) => Promise<void>;
|