@saykit/config 0.2.0 → 0.4.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/README.md +19 -0
- package/dist/commands/index.cjs +39 -79
- package/dist/commands/index.mjs +39 -79
- package/dist/features/catalogue/index.cjs +65 -0
- package/dist/features/catalogue/index.d.cts +68 -0
- package/dist/features/catalogue/index.d.mts +68 -0
- package/dist/features/catalogue/index.mjs +55 -0
- package/dist/features/loader/index.cjs +1 -1
- package/dist/features/loader/index.d.cts +1 -1
- package/dist/features/loader/index.d.mts +1 -1
- package/dist/features/loader/index.mjs +1 -1
- package/dist/features/messages/index.cjs +1 -1
- package/dist/features/messages/index.mjs +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +7 -0
- package/dist/{shapes-CJyTfZXd.d.cts → shapes-Ba3xS10n.d.cts} +1 -0
- package/dist/{shapes-DETrtvZf.d.mts → shapes-NPfwlInG.d.mts} +1 -0
- package/dist/storage-C4rOvLGi.cjs +103 -0
- package/dist/storage-CqKPYfCR.mjs +67 -0
- package/package.json +11 -1
- /package/dist/{hash-CDfMT76E.cjs → hash-8It7TJpB.cjs} +0 -0
- /package/dist/{hash-Cs0dRdGf.mjs → hash-DyC8GzMa.mjs} +0 -0
- /package/dist/{loader-DnWt_9U2.cjs → loader-B25O-vF6.cjs} +0 -0
- /package/dist/{loader-CGuahnrc.mjs → loader-C2eolE_1.mjs} +0 -0
package/README.md
CHANGED
|
@@ -21,6 +21,12 @@ import js from '@saykit/transform-js';
|
|
|
21
21
|
|
|
22
22
|
export default defineConfig({
|
|
23
23
|
locales: ['en', 'fr'],
|
|
24
|
+
// Optional per-locale fallback chains, most specific first. The source locale
|
|
25
|
+
// (the first entry in `locales`) is always the final fallback.
|
|
26
|
+
fallbackLocales: {
|
|
27
|
+
'en-NZ': ['en-GB'],
|
|
28
|
+
'es-MX': 'es',
|
|
29
|
+
},
|
|
24
30
|
buckets: [
|
|
25
31
|
{
|
|
26
32
|
include: ['src/**/*.ts'],
|
|
@@ -37,8 +43,21 @@ export default defineConfig({
|
|
|
37
43
|
```sh
|
|
38
44
|
saykit extract # extract messages once
|
|
39
45
|
saykit extract --watch # extract and watch for changes
|
|
46
|
+
saykit clean # prune dead entries from other locales
|
|
40
47
|
```
|
|
41
48
|
|
|
49
|
+
Extraction only writes the **source** locale (the first entry in `locales`). Locales that don't
|
|
50
|
+
have a file yet are bootstrapped with an empty, header-only catalogue so a TMS can register them;
|
|
51
|
+
existing translation files are left untouched. Translated content is owned by your TMS.
|
|
52
|
+
|
|
53
|
+
At load time (via the unplugin or Babel plugin), each locale module is filled from its fallback
|
|
54
|
+
chain, so an untranslated key resolves to a fallback locale and ultimately the source string — the
|
|
55
|
+
runtime still loads a single locale.
|
|
56
|
+
|
|
57
|
+
`saykit clean` only ever subtracts from non-source locales: it drops entries that no longer exist in
|
|
58
|
+
the source catalogue and entries with an empty translation, and leaves everything else exactly as it
|
|
59
|
+
is. It never adds source keys to a locale file, that stays your TMS's job.
|
|
60
|
+
|
|
42
61
|
## Documentation
|
|
43
62
|
|
|
44
63
|
Full configuration guide and CLI reference at [saykit.js.org](https://saykit.js.org).
|
package/dist/commands/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
require("../index.cjs");
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const require_storage = require("../storage-C4rOvLGi.cjs");
|
|
4
|
+
const require_loader = require("../loader-B25O-vF6.cjs");
|
|
5
5
|
let _commander_js_extra_typings = require("@commander-js/extra-typings");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
6
7
|
let node_path = require("node:path");
|
|
7
8
|
let node_async_hooks = require("node:async_hooks");
|
|
8
|
-
let node_fs_promises = require("node:fs/promises");
|
|
9
9
|
//#region src/features/logger.ts
|
|
10
10
|
const RESET = "\x1B[0m";
|
|
11
11
|
const DIM = "\x1B[2m";
|
|
@@ -45,6 +45,26 @@ var Logger = class {
|
|
|
45
45
|
};
|
|
46
46
|
new node_async_hooks.AsyncLocalStorage({ defaultValue: new Logger() });
|
|
47
47
|
//#endregion
|
|
48
|
+
//#region src/commands/clean.ts
|
|
49
|
+
var clean_default = new _commander_js_extra_typings.Command("clean").description("Remove orphaned and untranslated entries from non-source locale files").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).action(async (options) => {
|
|
50
|
+
const config = require_loader.resolveConfig();
|
|
51
|
+
const logger = new Logger(options);
|
|
52
|
+
logger.header("🧹 Cleaning Locales");
|
|
53
|
+
const [sourceLocale, ...otherLocales] = config.locales;
|
|
54
|
+
for (const bucket of config.buckets) {
|
|
55
|
+
const sourceMessages = await require_storage.readCatalogueMessages(bucket, sourceLocale);
|
|
56
|
+
logger.info(`Cleaning ${otherLocales.length} locale(s) against ${sourceLocale}`);
|
|
57
|
+
for (const locale of otherLocales) {
|
|
58
|
+
logger.step(`Cleaning ${locale}`);
|
|
59
|
+
const existingMessages = await require_storage.readCatalogueMessages(bucket, locale);
|
|
60
|
+
const prunedMessages = require_storage.pruneLocaleMessages(existingMessages, sourceMessages);
|
|
61
|
+
logger.step(`Removed ${existingMessages.length - prunedMessages.length} entries from ${locale}`);
|
|
62
|
+
await require_storage.writeCatalogueMessages(bucket, locale, prunedMessages);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
logger.success("Locales cleaned");
|
|
66
|
+
});
|
|
67
|
+
//#endregion
|
|
48
68
|
//#region src/features/catalogue/extractor.ts
|
|
49
69
|
async function extractMessagesFromFile(path, bucket) {
|
|
50
70
|
const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
|
|
@@ -56,74 +76,6 @@ async function extractMessagesFromFile(path, bucket) {
|
|
|
56
76
|
}));
|
|
57
77
|
}
|
|
58
78
|
//#endregion
|
|
59
|
-
//#region src/features/catalogue/merge.ts
|
|
60
|
-
function mergeUnique(...items) {
|
|
61
|
-
return Array.from(new Set(items.flat()));
|
|
62
|
-
}
|
|
63
|
-
function getMessageKey(message) {
|
|
64
|
-
return message.id ?? require_hash.generateHash(message.message, message.context);
|
|
65
|
-
}
|
|
66
|
-
function mergeExtractedMessages(messages) {
|
|
67
|
-
const mergedMessages = messages.reduce((map, message) => {
|
|
68
|
-
const key = getMessageKey(message);
|
|
69
|
-
const existing = map.get(key) ?? message;
|
|
70
|
-
map.set(key, {
|
|
71
|
-
...existing,
|
|
72
|
-
comments: mergeUnique(...existing.comments, ...message.comments),
|
|
73
|
-
references: mergeUnique(...existing.references, ...message.references)
|
|
74
|
-
});
|
|
75
|
-
return map;
|
|
76
|
-
}, /* @__PURE__ */ new Map());
|
|
77
|
-
return Array.from(mergedMessages.values());
|
|
78
|
-
}
|
|
79
|
-
function reconcileLocaleMessages(existingMessages, nextMessages) {
|
|
80
|
-
const existingMessagesByKey = existingMessages.reduce((map, message) => {
|
|
81
|
-
map.set(getMessageKey(message), message);
|
|
82
|
-
return map;
|
|
83
|
-
}, /* @__PURE__ */ new Map());
|
|
84
|
-
const reconciledMessages = nextMessages.reduce((map, message) => {
|
|
85
|
-
const key = getMessageKey(message);
|
|
86
|
-
const existingMessage = existingMessagesByKey.get(key);
|
|
87
|
-
map.set(key, {
|
|
88
|
-
message: message.message,
|
|
89
|
-
translation: void 0,
|
|
90
|
-
...existingMessage,
|
|
91
|
-
id: message.id,
|
|
92
|
-
context: message.context,
|
|
93
|
-
comments: message.comments,
|
|
94
|
-
references: message.references
|
|
95
|
-
});
|
|
96
|
-
return map;
|
|
97
|
-
}, /* @__PURE__ */ new Map());
|
|
98
|
-
return Array.from(reconciledMessages.values());
|
|
99
|
-
}
|
|
100
|
-
//#endregion
|
|
101
|
-
//#region src/features/catalogue/path.ts
|
|
102
|
-
function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
|
|
103
|
-
return (0, node_path.resolve)(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
|
|
104
|
-
}
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/features/catalogue/storage.ts
|
|
107
|
-
const DECLARATION_CONTENT = `
|
|
108
|
-
declare const messages: Record<string, string>;
|
|
109
|
-
export default messages;
|
|
110
|
-
`.trim();
|
|
111
|
-
async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
|
|
112
|
-
const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
|
|
113
|
-
if (!content) return [];
|
|
114
|
-
return bucket.formatter.parse(content);
|
|
115
|
-
}
|
|
116
|
-
async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
|
|
117
|
-
const existingContent = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => void 0);
|
|
118
|
-
const catalogueContent = bucket.formatter.stringify(messages, {
|
|
119
|
-
locale,
|
|
120
|
-
existingContent
|
|
121
|
-
});
|
|
122
|
-
const declarationPath = `${path}.d.ts`;
|
|
123
|
-
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
|
|
124
|
-
await Promise.all([(0, node_fs_promises.writeFile)(path, catalogueContent), (0, node_fs_promises.writeFile)(declarationPath, DECLARATION_CONTENT)]);
|
|
125
|
-
}
|
|
126
|
-
//#endregion
|
|
127
79
|
//#region src/features/watch.ts
|
|
128
80
|
/**
|
|
129
81
|
* Expand a buckets include and exclude patterns into a flat list of file paths.
|
|
@@ -183,6 +135,9 @@ var BucketWorker = class {
|
|
|
183
135
|
};
|
|
184
136
|
//#endregion
|
|
185
137
|
//#region src/features/workers/extract-worker.ts
|
|
138
|
+
function exists(path) {
|
|
139
|
+
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
140
|
+
}
|
|
186
141
|
var BucketExtractWorker = class extends BucketWorker {
|
|
187
142
|
#indexedMessagesByPath = /* @__PURE__ */ new Map();
|
|
188
143
|
get #indexedMessages() {
|
|
@@ -212,13 +167,18 @@ var BucketExtractWorker = class extends BucketWorker {
|
|
|
212
167
|
this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
|
|
213
168
|
}
|
|
214
169
|
async write() {
|
|
215
|
-
const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
await
|
|
170
|
+
const mergedMessages = require_storage.mergeExtractedMessages(this.#indexedMessages);
|
|
171
|
+
const [sourceLocale, ...otherLocales] = this.config.locales;
|
|
172
|
+
this.logger.info(`Writing ${mergedMessages.length} messages to ${sourceLocale}`);
|
|
173
|
+
this.logger.step(`Writing locale file for ${sourceLocale} to disk`);
|
|
174
|
+
await require_storage.writeCatalogueMessages(this.bucket, sourceLocale, mergedMessages);
|
|
175
|
+
for (const locale of otherLocales) {
|
|
176
|
+
if (await exists(require_storage.expandBucketOutputPath(this.bucket, locale))) {
|
|
177
|
+
this.logger.step(`Skipping existing locale file for ${locale}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
this.logger.step(`Creating empty locale file for ${locale}`);
|
|
181
|
+
await require_storage.writeCatalogueMessages(this.bucket, locale, []);
|
|
222
182
|
}
|
|
223
183
|
this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
|
|
224
184
|
}
|
|
@@ -248,5 +208,5 @@ var extract_default = new _commander_js_extra_typings.Command("extract").descrip
|
|
|
248
208
|
});
|
|
249
209
|
//#endregion
|
|
250
210
|
//#region src/commands/index.ts
|
|
251
|
-
_commander_js_extra_typings.program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
|
|
211
|
+
_commander_js_extra_typings.program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).addCommand(clean_default).parse();
|
|
252
212
|
//#endregion
|
package/dist/commands/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
3
|
-
import { t as
|
|
2
|
+
import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, t as readCatalogueMessages } from "../storage-CqKPYfCR.mjs";
|
|
3
|
+
import { t as resolveConfig } from "../loader-C2eolE_1.mjs";
|
|
4
4
|
import { Command, program } from "@commander-js/extra-typings";
|
|
5
|
-
import {
|
|
5
|
+
import { access, glob, readFile, watch } from "node:fs/promises";
|
|
6
|
+
import { join, relative } from "node:path";
|
|
6
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
-
import { glob, mkdir, readFile, watch, writeFile } from "node:fs/promises";
|
|
8
8
|
//#region src/features/logger.ts
|
|
9
9
|
const RESET = "\x1B[0m";
|
|
10
10
|
const DIM = "\x1B[2m";
|
|
@@ -44,6 +44,26 @@ var Logger = class {
|
|
|
44
44
|
};
|
|
45
45
|
new AsyncLocalStorage({ defaultValue: new Logger() });
|
|
46
46
|
//#endregion
|
|
47
|
+
//#region src/commands/clean.ts
|
|
48
|
+
var clean_default = new Command("clean").description("Remove orphaned and untranslated entries from non-source locale files").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).action(async (options) => {
|
|
49
|
+
const config = resolveConfig();
|
|
50
|
+
const logger = new Logger(options);
|
|
51
|
+
logger.header("🧹 Cleaning Locales");
|
|
52
|
+
const [sourceLocale, ...otherLocales] = config.locales;
|
|
53
|
+
for (const bucket of config.buckets) {
|
|
54
|
+
const sourceMessages = await readCatalogueMessages(bucket, sourceLocale);
|
|
55
|
+
logger.info(`Cleaning ${otherLocales.length} locale(s) against ${sourceLocale}`);
|
|
56
|
+
for (const locale of otherLocales) {
|
|
57
|
+
logger.step(`Cleaning ${locale}`);
|
|
58
|
+
const existingMessages = await readCatalogueMessages(bucket, locale);
|
|
59
|
+
const prunedMessages = pruneLocaleMessages(existingMessages, sourceMessages);
|
|
60
|
+
logger.step(`Removed ${existingMessages.length - prunedMessages.length} entries from ${locale}`);
|
|
61
|
+
await writeCatalogueMessages(bucket, locale, prunedMessages);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
logger.success("Locales cleaned");
|
|
65
|
+
});
|
|
66
|
+
//#endregion
|
|
47
67
|
//#region src/features/catalogue/extractor.ts
|
|
48
68
|
async function extractMessagesFromFile(path, bucket) {
|
|
49
69
|
const content = await readFile(path, "utf8").catch(() => "");
|
|
@@ -55,74 +75,6 @@ async function extractMessagesFromFile(path, bucket) {
|
|
|
55
75
|
}));
|
|
56
76
|
}
|
|
57
77
|
//#endregion
|
|
58
|
-
//#region src/features/catalogue/merge.ts
|
|
59
|
-
function mergeUnique(...items) {
|
|
60
|
-
return Array.from(new Set(items.flat()));
|
|
61
|
-
}
|
|
62
|
-
function getMessageKey(message) {
|
|
63
|
-
return message.id ?? generateHash(message.message, message.context);
|
|
64
|
-
}
|
|
65
|
-
function mergeExtractedMessages(messages) {
|
|
66
|
-
const mergedMessages = messages.reduce((map, message) => {
|
|
67
|
-
const key = getMessageKey(message);
|
|
68
|
-
const existing = map.get(key) ?? message;
|
|
69
|
-
map.set(key, {
|
|
70
|
-
...existing,
|
|
71
|
-
comments: mergeUnique(...existing.comments, ...message.comments),
|
|
72
|
-
references: mergeUnique(...existing.references, ...message.references)
|
|
73
|
-
});
|
|
74
|
-
return map;
|
|
75
|
-
}, /* @__PURE__ */ new Map());
|
|
76
|
-
return Array.from(mergedMessages.values());
|
|
77
|
-
}
|
|
78
|
-
function reconcileLocaleMessages(existingMessages, nextMessages) {
|
|
79
|
-
const existingMessagesByKey = existingMessages.reduce((map, message) => {
|
|
80
|
-
map.set(getMessageKey(message), message);
|
|
81
|
-
return map;
|
|
82
|
-
}, /* @__PURE__ */ new Map());
|
|
83
|
-
const reconciledMessages = nextMessages.reduce((map, message) => {
|
|
84
|
-
const key = getMessageKey(message);
|
|
85
|
-
const existingMessage = existingMessagesByKey.get(key);
|
|
86
|
-
map.set(key, {
|
|
87
|
-
message: message.message,
|
|
88
|
-
translation: void 0,
|
|
89
|
-
...existingMessage,
|
|
90
|
-
id: message.id,
|
|
91
|
-
context: message.context,
|
|
92
|
-
comments: message.comments,
|
|
93
|
-
references: message.references
|
|
94
|
-
});
|
|
95
|
-
return map;
|
|
96
|
-
}, /* @__PURE__ */ new Map());
|
|
97
|
-
return Array.from(reconciledMessages.values());
|
|
98
|
-
}
|
|
99
|
-
//#endregion
|
|
100
|
-
//#region src/features/catalogue/path.ts
|
|
101
|
-
function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
|
|
102
|
-
return resolve(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
|
|
103
|
-
}
|
|
104
|
-
//#endregion
|
|
105
|
-
//#region src/features/catalogue/storage.ts
|
|
106
|
-
const DECLARATION_CONTENT = `
|
|
107
|
-
declare const messages: Record<string, string>;
|
|
108
|
-
export default messages;
|
|
109
|
-
`.trim();
|
|
110
|
-
async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
|
|
111
|
-
const content = await readFile(path, "utf8").catch(() => "");
|
|
112
|
-
if (!content) return [];
|
|
113
|
-
return bucket.formatter.parse(content);
|
|
114
|
-
}
|
|
115
|
-
async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
|
|
116
|
-
const existingContent = await readFile(path, "utf8").catch(() => void 0);
|
|
117
|
-
const catalogueContent = bucket.formatter.stringify(messages, {
|
|
118
|
-
locale,
|
|
119
|
-
existingContent
|
|
120
|
-
});
|
|
121
|
-
const declarationPath = `${path}.d.ts`;
|
|
122
|
-
await mkdir(dirname(path), { recursive: true });
|
|
123
|
-
await Promise.all([writeFile(path, catalogueContent), writeFile(declarationPath, DECLARATION_CONTENT)]);
|
|
124
|
-
}
|
|
125
|
-
//#endregion
|
|
126
78
|
//#region src/features/watch.ts
|
|
127
79
|
/**
|
|
128
80
|
* Expand a buckets include and exclude patterns into a flat list of file paths.
|
|
@@ -182,6 +134,9 @@ var BucketWorker = class {
|
|
|
182
134
|
};
|
|
183
135
|
//#endregion
|
|
184
136
|
//#region src/features/workers/extract-worker.ts
|
|
137
|
+
function exists(path) {
|
|
138
|
+
return access(path).then(() => true, () => false);
|
|
139
|
+
}
|
|
185
140
|
var BucketExtractWorker = class extends BucketWorker {
|
|
186
141
|
#indexedMessagesByPath = /* @__PURE__ */ new Map();
|
|
187
142
|
get #indexedMessages() {
|
|
@@ -212,12 +167,17 @@ var BucketExtractWorker = class extends BucketWorker {
|
|
|
212
167
|
}
|
|
213
168
|
async write() {
|
|
214
169
|
const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
await
|
|
170
|
+
const [sourceLocale, ...otherLocales] = this.config.locales;
|
|
171
|
+
this.logger.info(`Writing ${mergedMessages.length} messages to ${sourceLocale}`);
|
|
172
|
+
this.logger.step(`Writing locale file for ${sourceLocale} to disk`);
|
|
173
|
+
await writeCatalogueMessages(this.bucket, sourceLocale, mergedMessages);
|
|
174
|
+
for (const locale of otherLocales) {
|
|
175
|
+
if (await exists(expandBucketOutputPath(this.bucket, locale))) {
|
|
176
|
+
this.logger.step(`Skipping existing locale file for ${locale}`);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
this.logger.step(`Creating empty locale file for ${locale}`);
|
|
180
|
+
await writeCatalogueMessages(this.bucket, locale, []);
|
|
221
181
|
}
|
|
222
182
|
this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
|
|
223
183
|
}
|
|
@@ -247,6 +207,6 @@ var extract_default = new Command("extract").description("Extract messages from
|
|
|
247
207
|
});
|
|
248
208
|
//#endregion
|
|
249
209
|
//#region src/commands/index.ts
|
|
250
|
-
program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
|
|
210
|
+
program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).addCommand(clean_default).parse();
|
|
251
211
|
//#endregion
|
|
252
212
|
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../index.cjs");
|
|
3
|
+
const require_hash = require("../../hash-8It7TJpB.cjs");
|
|
4
|
+
const require_storage = require("../../storage-C4rOvLGi.cjs");
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
//#region src/features/catalogue/record.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the fallback chain for a locale, most specific first. The source
|
|
9
|
+
* locale (the first configured locale) is always the final fallback, so an
|
|
10
|
+
* untranslated key ultimately resolves to the source string.
|
|
11
|
+
*/
|
|
12
|
+
function resolveFallbackChain(config, locale) {
|
|
13
|
+
const source = config.locales[0];
|
|
14
|
+
const configured = config.fallbackLocales?.[locale];
|
|
15
|
+
const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
|
|
16
|
+
return Array.from(new Set([
|
|
17
|
+
locale,
|
|
18
|
+
...fallbacks,
|
|
19
|
+
source
|
|
20
|
+
]));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the catalogue files that contribute to a locale module, most specific
|
|
24
|
+
* first. When the path does not map to a configured locale it is loaded on its
|
|
25
|
+
* own.
|
|
26
|
+
*/
|
|
27
|
+
function resolveCatalogueSources(config, bucket, path) {
|
|
28
|
+
const resolved = (0, node_path.resolve)(path);
|
|
29
|
+
const locale = config.locales.find((l) => require_storage.expandBucketOutputPath(bucket, l) === resolved);
|
|
30
|
+
return {
|
|
31
|
+
locale,
|
|
32
|
+
sources: locale ? resolveFallbackChain(config, locale).map((l) => require_storage.expandBucketOutputPath(bucket, l)) : [resolved]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Assemble a `{ id: string }` record from catalogue file contents. `contents`
|
|
37
|
+
* must be aligned with the `sources` returned by {@link resolveCatalogueSources}
|
|
38
|
+
* (most specific first): more specific locales override their fallbacks, and any
|
|
39
|
+
* key still untranslated falls back to its source message.
|
|
40
|
+
*/
|
|
41
|
+
function assembleCatalogueRecord(bucket, contents) {
|
|
42
|
+
const record = {};
|
|
43
|
+
for (const content of [...contents].reverse()) {
|
|
44
|
+
if (!content) continue;
|
|
45
|
+
for (const message of bucket.formatter.parse(content)) {
|
|
46
|
+
const key = message.id || require_hash.generateHash(message.message, message.context);
|
|
47
|
+
if (message.translation) {
|
|
48
|
+
record[key] = message.translation;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!record[key] && message.message) record[key] = message.message;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return record;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
exports.assembleCatalogueRecord = assembleCatalogueRecord;
|
|
58
|
+
exports.declarationPathFor = require_storage.declarationPathFor;
|
|
59
|
+
exports.expandBucketOutputPath = require_storage.expandBucketOutputPath;
|
|
60
|
+
exports.mergeExtractedMessages = require_storage.mergeExtractedMessages;
|
|
61
|
+
exports.pruneLocaleMessages = require_storage.pruneLocaleMessages;
|
|
62
|
+
exports.readCatalogueMessages = require_storage.readCatalogueMessages;
|
|
63
|
+
exports.resolveCatalogueSources = resolveCatalogueSources;
|
|
64
|
+
exports.resolveFallbackChain = resolveFallbackChain;
|
|
65
|
+
exports.writeCatalogueMessages = require_storage.writeCatalogueMessages;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { i as Message, n as Config, t as Bucket } from "../../shapes-Ba3xS10n.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/features/catalogue/merge.d.ts
|
|
4
|
+
declare function mergeExtractedMessages(messages: Message[]): {
|
|
5
|
+
message: string;
|
|
6
|
+
comments: string[];
|
|
7
|
+
references: string[];
|
|
8
|
+
translation?: string | undefined;
|
|
9
|
+
id?: string | undefined;
|
|
10
|
+
context?: string | undefined;
|
|
11
|
+
}[];
|
|
12
|
+
declare function pruneLocaleMessages(existingMessages: Message[], sourceMessages: Message[]): {
|
|
13
|
+
message: string;
|
|
14
|
+
comments: string[];
|
|
15
|
+
references: string[];
|
|
16
|
+
translation?: string | undefined;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
context?: string | undefined;
|
|
19
|
+
}[];
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/features/catalogue/path.d.ts
|
|
22
|
+
declare function expandBucketOutputPath(bucket: Bucket, locale: string, extension?: `.${string}`): string;
|
|
23
|
+
/**
|
|
24
|
+
* The declaration file that types a catalogue, e.g. `en.json` -> `en.d.json.ts`.
|
|
25
|
+
*
|
|
26
|
+
* TypeScript resolves `./en.json` by stripping the extension and looking for
|
|
27
|
+
* `en.d.json.ts`; the `en.json.d.ts` form is only consulted for extensions the
|
|
28
|
+
* resolver does not recognise. Non-JS extensions additionally require
|
|
29
|
+
* `allowArbitraryExtensions` in the consumer's tsconfig.
|
|
30
|
+
*/
|
|
31
|
+
declare function declarationPathFor(cataloguePath: string): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/features/catalogue/record.d.ts
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the fallback chain for a locale, most specific first. The source
|
|
36
|
+
* locale (the first configured locale) is always the final fallback, so an
|
|
37
|
+
* untranslated key ultimately resolves to the source string.
|
|
38
|
+
*/
|
|
39
|
+
declare function resolveFallbackChain(config: Config, locale: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the catalogue files that contribute to a locale module, most specific
|
|
42
|
+
* first. When the path does not map to a configured locale it is loaded on its
|
|
43
|
+
* own.
|
|
44
|
+
*/
|
|
45
|
+
declare function resolveCatalogueSources(config: Config, bucket: Bucket, path: string): {
|
|
46
|
+
locale: string | undefined;
|
|
47
|
+
sources: string[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Assemble a `{ id: string }` record from catalogue file contents. `contents`
|
|
51
|
+
* must be aligned with the `sources` returned by {@link resolveCatalogueSources}
|
|
52
|
+
* (most specific first): more specific locales override their fallbacks, and any
|
|
53
|
+
* key still untranslated falls back to its source message.
|
|
54
|
+
*/
|
|
55
|
+
declare function assembleCatalogueRecord(bucket: Bucket, contents: string[]): Record<string, string>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/features/catalogue/storage.d.ts
|
|
58
|
+
declare function readCatalogueMessages(bucket: Bucket, locale: string, path?: string): Promise<{
|
|
59
|
+
message: string;
|
|
60
|
+
comments: string[];
|
|
61
|
+
references: string[];
|
|
62
|
+
translation?: string | undefined;
|
|
63
|
+
id?: string | undefined;
|
|
64
|
+
context?: string | undefined;
|
|
65
|
+
}[]>;
|
|
66
|
+
declare function writeCatalogueMessages(bucket: Bucket, locale: string, messages: Message[], path?: string): Promise<void>;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { assembleCatalogueRecord, declarationPathFor, expandBucketOutputPath, mergeExtractedMessages, pruneLocaleMessages, readCatalogueMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { i as Message, n as Config, t as Bucket } from "../../shapes-NPfwlInG.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/features/catalogue/merge.d.ts
|
|
4
|
+
declare function mergeExtractedMessages(messages: Message[]): {
|
|
5
|
+
message: string;
|
|
6
|
+
comments: string[];
|
|
7
|
+
references: string[];
|
|
8
|
+
translation?: string | undefined;
|
|
9
|
+
id?: string | undefined;
|
|
10
|
+
context?: string | undefined;
|
|
11
|
+
}[];
|
|
12
|
+
declare function pruneLocaleMessages(existingMessages: Message[], sourceMessages: Message[]): {
|
|
13
|
+
message: string;
|
|
14
|
+
comments: string[];
|
|
15
|
+
references: string[];
|
|
16
|
+
translation?: string | undefined;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
context?: string | undefined;
|
|
19
|
+
}[];
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/features/catalogue/path.d.ts
|
|
22
|
+
declare function expandBucketOutputPath(bucket: Bucket, locale: string, extension?: `.${string}`): string;
|
|
23
|
+
/**
|
|
24
|
+
* The declaration file that types a catalogue, e.g. `en.json` -> `en.d.json.ts`.
|
|
25
|
+
*
|
|
26
|
+
* TypeScript resolves `./en.json` by stripping the extension and looking for
|
|
27
|
+
* `en.d.json.ts`; the `en.json.d.ts` form is only consulted for extensions the
|
|
28
|
+
* resolver does not recognise. Non-JS extensions additionally require
|
|
29
|
+
* `allowArbitraryExtensions` in the consumer's tsconfig.
|
|
30
|
+
*/
|
|
31
|
+
declare function declarationPathFor(cataloguePath: string): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/features/catalogue/record.d.ts
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the fallback chain for a locale, most specific first. The source
|
|
36
|
+
* locale (the first configured locale) is always the final fallback, so an
|
|
37
|
+
* untranslated key ultimately resolves to the source string.
|
|
38
|
+
*/
|
|
39
|
+
declare function resolveFallbackChain(config: Config, locale: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the catalogue files that contribute to a locale module, most specific
|
|
42
|
+
* first. When the path does not map to a configured locale it is loaded on its
|
|
43
|
+
* own.
|
|
44
|
+
*/
|
|
45
|
+
declare function resolveCatalogueSources(config: Config, bucket: Bucket, path: string): {
|
|
46
|
+
locale: string | undefined;
|
|
47
|
+
sources: string[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Assemble a `{ id: string }` record from catalogue file contents. `contents`
|
|
51
|
+
* must be aligned with the `sources` returned by {@link resolveCatalogueSources}
|
|
52
|
+
* (most specific first): more specific locales override their fallbacks, and any
|
|
53
|
+
* key still untranslated falls back to its source message.
|
|
54
|
+
*/
|
|
55
|
+
declare function assembleCatalogueRecord(bucket: Bucket, contents: string[]): Record<string, string>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/features/catalogue/storage.d.ts
|
|
58
|
+
declare function readCatalogueMessages(bucket: Bucket, locale: string, path?: string): Promise<{
|
|
59
|
+
message: string;
|
|
60
|
+
comments: string[];
|
|
61
|
+
references: string[];
|
|
62
|
+
translation?: string | undefined;
|
|
63
|
+
id?: string | undefined;
|
|
64
|
+
context?: string | undefined;
|
|
65
|
+
}[]>;
|
|
66
|
+
declare function writeCatalogueMessages(bucket: Bucket, locale: string, messages: Message[], path?: string): Promise<void>;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { assembleCatalogueRecord, declarationPathFor, expandBucketOutputPath, mergeExtractedMessages, pruneLocaleMessages, readCatalogueMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
|
|
2
|
+
import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, r as declarationPathFor, t as readCatalogueMessages } from "../../storage-CqKPYfCR.mjs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
//#region src/features/catalogue/record.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the fallback chain for a locale, most specific first. The source
|
|
7
|
+
* locale (the first configured locale) is always the final fallback, so an
|
|
8
|
+
* untranslated key ultimately resolves to the source string.
|
|
9
|
+
*/
|
|
10
|
+
function resolveFallbackChain(config, locale) {
|
|
11
|
+
const source = config.locales[0];
|
|
12
|
+
const configured = config.fallbackLocales?.[locale];
|
|
13
|
+
const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
|
|
14
|
+
return Array.from(new Set([
|
|
15
|
+
locale,
|
|
16
|
+
...fallbacks,
|
|
17
|
+
source
|
|
18
|
+
]));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the catalogue files that contribute to a locale module, most specific
|
|
22
|
+
* first. When the path does not map to a configured locale it is loaded on its
|
|
23
|
+
* own.
|
|
24
|
+
*/
|
|
25
|
+
function resolveCatalogueSources(config, bucket, path) {
|
|
26
|
+
const resolved = resolve(path);
|
|
27
|
+
const locale = config.locales.find((l) => expandBucketOutputPath(bucket, l) === resolved);
|
|
28
|
+
return {
|
|
29
|
+
locale,
|
|
30
|
+
sources: locale ? resolveFallbackChain(config, locale).map((l) => expandBucketOutputPath(bucket, l)) : [resolved]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Assemble a `{ id: string }` record from catalogue file contents. `contents`
|
|
35
|
+
* must be aligned with the `sources` returned by {@link resolveCatalogueSources}
|
|
36
|
+
* (most specific first): more specific locales override their fallbacks, and any
|
|
37
|
+
* key still untranslated falls back to its source message.
|
|
38
|
+
*/
|
|
39
|
+
function assembleCatalogueRecord(bucket, contents) {
|
|
40
|
+
const record = {};
|
|
41
|
+
for (const content of [...contents].reverse()) {
|
|
42
|
+
if (!content) continue;
|
|
43
|
+
for (const message of bucket.formatter.parse(content)) {
|
|
44
|
+
const key = message.id || generateHash(message.message, message.context);
|
|
45
|
+
if (message.translation) {
|
|
46
|
+
record[key] = message.translation;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!record[key] && message.message) record[key] = message.message;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return record;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { assembleCatalogueRecord, declarationPathFor, expandBucketOutputPath, mergeExtractedMessages, pruneLocaleMessages, readCatalogueMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as resolveConfig } from "../../loader-
|
|
1
|
+
import { t as resolveConfig } from "../../loader-C2eolE_1.mjs";
|
|
2
2
|
export { resolveConfig };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hash = require("../../hash-
|
|
2
|
+
const require_hash = require("../../hash-8It7TJpB.cjs");
|
|
3
3
|
//#region src/features/messages/identifier.ts
|
|
4
4
|
const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
|
|
5
5
|
function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as generateHash } from "../../hash-
|
|
1
|
+
import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
|
|
2
2
|
//#region src/features/messages/identifier.ts
|
|
3
3
|
const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
|
|
4
4
|
function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
|
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,13 @@ const Bucket = zod.object({
|
|
|
65
65
|
}));
|
|
66
66
|
const Config = zod.object({
|
|
67
67
|
locales: zod.tuple([zod.string()], zod.string()),
|
|
68
|
+
/**
|
|
69
|
+
* Per-locale fallback chains, most specific first, e.g.
|
|
70
|
+
* `{ 'en-NZ': ['en-GB'], 'es-MX': 'es' }`. The source locale (the first entry
|
|
71
|
+
* in {@link Config.locales}) is always appended as the final fallback, so an
|
|
72
|
+
* untranslated key ultimately resolves to the source string.
|
|
73
|
+
*/
|
|
74
|
+
fallbackLocales: zod.record(zod.string(), zod.string().or(zod.string().array())).optional(),
|
|
68
75
|
buckets: Bucket.array()
|
|
69
76
|
});
|
|
70
77
|
//#endregion
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-
|
|
1
|
+
import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-Ba3xS10n.cjs";
|
|
2
2
|
import { input } from "zod";
|
|
3
3
|
import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
|
|
4
4
|
|
|
@@ -33,6 +33,7 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
|
|
|
33
33
|
};
|
|
34
34
|
exclude?: string[] | undefined;
|
|
35
35
|
}[];
|
|
36
|
+
fallbackLocales?: Record<string, string | string[]> | undefined;
|
|
36
37
|
};
|
|
37
38
|
//#endregion
|
|
38
39
|
export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-
|
|
1
|
+
import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-NPfwlInG.mjs";
|
|
2
2
|
import { input } from "zod";
|
|
3
3
|
import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
|
|
4
4
|
|
|
@@ -33,6 +33,7 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
|
|
|
33
33
|
};
|
|
34
34
|
exclude?: string[] | undefined;
|
|
35
35
|
}[];
|
|
36
|
+
fallbackLocales?: Record<string, string | string[]> | undefined;
|
|
36
37
|
};
|
|
37
38
|
//#endregion
|
|
38
39
|
export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -40,6 +40,13 @@ const Bucket = z.object({
|
|
|
40
40
|
}));
|
|
41
41
|
const Config = z.object({
|
|
42
42
|
locales: z.tuple([z.string()], z.string()),
|
|
43
|
+
/**
|
|
44
|
+
* Per-locale fallback chains, most specific first, e.g.
|
|
45
|
+
* `{ 'en-NZ': ['en-GB'], 'es-MX': 'es' }`. The source locale (the first entry
|
|
46
|
+
* in {@link Config.locales}) is always appended as the final fallback, so an
|
|
47
|
+
* untranslated key ultimately resolves to the source string.
|
|
48
|
+
*/
|
|
49
|
+
fallbackLocales: z.record(z.string(), z.string().or(z.string().array())).optional(),
|
|
43
50
|
buckets: Bucket.array()
|
|
44
51
|
});
|
|
45
52
|
//#endregion
|
|
@@ -131,6 +131,7 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
|
|
|
131
131
|
type Bucket = z.infer<typeof Bucket>;
|
|
132
132
|
declare const Config: z.ZodObject<{
|
|
133
133
|
locales: z.ZodTuple<[z.ZodString], z.ZodString>;
|
|
134
|
+
fallbackLocales: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>>;
|
|
134
135
|
buckets: z.ZodArray<z.ZodPipe<z.ZodObject<{
|
|
135
136
|
include: z.ZodArray<z.ZodString>;
|
|
136
137
|
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -131,6 +131,7 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
|
|
|
131
131
|
type Bucket = z.infer<typeof Bucket>;
|
|
132
132
|
declare const Config: z.ZodObject<{
|
|
133
133
|
locales: z.ZodTuple<[z.ZodString], z.ZodString>;
|
|
134
|
+
fallbackLocales: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>>;
|
|
134
135
|
buckets: z.ZodArray<z.ZodPipe<z.ZodObject<{
|
|
135
136
|
include: z.ZodArray<z.ZodString>;
|
|
136
137
|
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require("./index.cjs");
|
|
2
|
+
const require_hash = require("./hash-8It7TJpB.cjs");
|
|
3
|
+
let node_fs_promises = require("node:fs/promises");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
//#region src/features/catalogue/merge.ts
|
|
6
|
+
function mergeUnique(...items) {
|
|
7
|
+
return Array.from(new Set(items.flat()));
|
|
8
|
+
}
|
|
9
|
+
function getMessageKey(message) {
|
|
10
|
+
return message.id ?? require_hash.generateHash(message.message, message.context);
|
|
11
|
+
}
|
|
12
|
+
function mergeExtractedMessages(messages) {
|
|
13
|
+
const mergedMessages = messages.reduce((map, message) => {
|
|
14
|
+
const key = getMessageKey(message);
|
|
15
|
+
const existing = map.get(key) ?? message;
|
|
16
|
+
map.set(key, {
|
|
17
|
+
...existing,
|
|
18
|
+
comments: mergeUnique(...existing.comments, ...message.comments),
|
|
19
|
+
references: mergeUnique(...existing.references, ...message.references)
|
|
20
|
+
});
|
|
21
|
+
return map;
|
|
22
|
+
}, /* @__PURE__ */ new Map());
|
|
23
|
+
return Array.from(mergedMessages.values());
|
|
24
|
+
}
|
|
25
|
+
function pruneLocaleMessages(existingMessages, sourceMessages) {
|
|
26
|
+
const sourceKeys = new Set(sourceMessages.map(getMessageKey));
|
|
27
|
+
return existingMessages.filter((message) => sourceKeys.has(getMessageKey(message)) && Boolean(message.translation));
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/features/catalogue/path.ts
|
|
31
|
+
function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
|
|
32
|
+
return (0, node_path.resolve)(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The declaration file that types a catalogue, e.g. `en.json` -> `en.d.json.ts`.
|
|
36
|
+
*
|
|
37
|
+
* TypeScript resolves `./en.json` by stripping the extension and looking for
|
|
38
|
+
* `en.d.json.ts`; the `en.json.d.ts` form is only consulted for extensions the
|
|
39
|
+
* resolver does not recognise. Non-JS extensions additionally require
|
|
40
|
+
* `allowArbitraryExtensions` in the consumer's tsconfig.
|
|
41
|
+
*/
|
|
42
|
+
function declarationPathFor(cataloguePath) {
|
|
43
|
+
const { dir, name, ext } = (0, node_path.parse)(cataloguePath);
|
|
44
|
+
return (0, node_path.resolve)(dir, `${name}.d${ext}.ts`);
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/features/catalogue/storage.ts
|
|
48
|
+
const DECLARATION_CONTENT = `
|
|
49
|
+
declare const messages: Record<string, string>;
|
|
50
|
+
export default messages;
|
|
51
|
+
`.trimStart();
|
|
52
|
+
async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
|
|
53
|
+
const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
|
|
54
|
+
if (!content) return [];
|
|
55
|
+
return bucket.formatter.parse(content);
|
|
56
|
+
}
|
|
57
|
+
async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
|
|
58
|
+
const existingContent = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => void 0);
|
|
59
|
+
const catalogueContent = bucket.formatter.stringify(messages, {
|
|
60
|
+
locale,
|
|
61
|
+
existingContent
|
|
62
|
+
});
|
|
63
|
+
const declarationPath = declarationPathFor(path);
|
|
64
|
+
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
|
|
65
|
+
await Promise.all([(0, node_fs_promises.writeFile)(path, catalogueContent), (0, node_fs_promises.writeFile)(declarationPath, DECLARATION_CONTENT)]);
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
Object.defineProperty(exports, "declarationPathFor", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
get: function() {
|
|
71
|
+
return declarationPathFor;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(exports, "expandBucketOutputPath", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
get: function() {
|
|
77
|
+
return expandBucketOutputPath;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(exports, "mergeExtractedMessages", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get: function() {
|
|
83
|
+
return mergeExtractedMessages;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(exports, "pruneLocaleMessages", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
get: function() {
|
|
89
|
+
return pruneLocaleMessages;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
Object.defineProperty(exports, "readCatalogueMessages", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
get: function() {
|
|
95
|
+
return readCatalogueMessages;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(exports, "writeCatalogueMessages", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
get: function() {
|
|
101
|
+
return writeCatalogueMessages;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { t as generateHash } from "./hash-DyC8GzMa.mjs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, parse, resolve } from "node:path";
|
|
4
|
+
//#region src/features/catalogue/merge.ts
|
|
5
|
+
function mergeUnique(...items) {
|
|
6
|
+
return Array.from(new Set(items.flat()));
|
|
7
|
+
}
|
|
8
|
+
function getMessageKey(message) {
|
|
9
|
+
return message.id ?? generateHash(message.message, message.context);
|
|
10
|
+
}
|
|
11
|
+
function mergeExtractedMessages(messages) {
|
|
12
|
+
const mergedMessages = messages.reduce((map, message) => {
|
|
13
|
+
const key = getMessageKey(message);
|
|
14
|
+
const existing = map.get(key) ?? message;
|
|
15
|
+
map.set(key, {
|
|
16
|
+
...existing,
|
|
17
|
+
comments: mergeUnique(...existing.comments, ...message.comments),
|
|
18
|
+
references: mergeUnique(...existing.references, ...message.references)
|
|
19
|
+
});
|
|
20
|
+
return map;
|
|
21
|
+
}, /* @__PURE__ */ new Map());
|
|
22
|
+
return Array.from(mergedMessages.values());
|
|
23
|
+
}
|
|
24
|
+
function pruneLocaleMessages(existingMessages, sourceMessages) {
|
|
25
|
+
const sourceKeys = new Set(sourceMessages.map(getMessageKey));
|
|
26
|
+
return existingMessages.filter((message) => sourceKeys.has(getMessageKey(message)) && Boolean(message.translation));
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/features/catalogue/path.ts
|
|
30
|
+
function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
|
|
31
|
+
return resolve(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The declaration file that types a catalogue, e.g. `en.json` -> `en.d.json.ts`.
|
|
35
|
+
*
|
|
36
|
+
* TypeScript resolves `./en.json` by stripping the extension and looking for
|
|
37
|
+
* `en.d.json.ts`; the `en.json.d.ts` form is only consulted for extensions the
|
|
38
|
+
* resolver does not recognise. Non-JS extensions additionally require
|
|
39
|
+
* `allowArbitraryExtensions` in the consumer's tsconfig.
|
|
40
|
+
*/
|
|
41
|
+
function declarationPathFor(cataloguePath) {
|
|
42
|
+
const { dir, name, ext } = parse(cataloguePath);
|
|
43
|
+
return resolve(dir, `${name}.d${ext}.ts`);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/features/catalogue/storage.ts
|
|
47
|
+
const DECLARATION_CONTENT = `
|
|
48
|
+
declare const messages: Record<string, string>;
|
|
49
|
+
export default messages;
|
|
50
|
+
`.trimStart();
|
|
51
|
+
async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
|
|
52
|
+
const content = await readFile(path, "utf8").catch(() => "");
|
|
53
|
+
if (!content) return [];
|
|
54
|
+
return bucket.formatter.parse(content);
|
|
55
|
+
}
|
|
56
|
+
async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
|
|
57
|
+
const existingContent = await readFile(path, "utf8").catch(() => void 0);
|
|
58
|
+
const catalogueContent = bucket.formatter.stringify(messages, {
|
|
59
|
+
locale,
|
|
60
|
+
existingContent
|
|
61
|
+
});
|
|
62
|
+
const declarationPath = declarationPathFor(path);
|
|
63
|
+
await mkdir(dirname(path), { recursive: true });
|
|
64
|
+
await Promise.all([writeFile(path, catalogueContent), writeFile(declarationPath, DECLARATION_CONTENT)]);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { mergeExtractedMessages as a, expandBucketOutputPath as i, writeCatalogueMessages as n, pruneLocaleMessages as o, declarationPathFor as r, readCatalogueMessages as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saykit/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI and configuration tooling for saykit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
"default": "./dist/index.mjs"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
+
"./features/catalogue": {
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/features/catalogue/index.d.cts",
|
|
43
|
+
"default": "./dist/features/catalogue/index.cjs"
|
|
44
|
+
},
|
|
45
|
+
"import": {
|
|
46
|
+
"types": "./dist/features/catalogue/index.d.mts",
|
|
47
|
+
"default": "./dist/features/catalogue/index.mjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
40
50
|
"./features/loader": {
|
|
41
51
|
"require": {
|
|
42
52
|
"types": "./dist/features/loader/index.d.cts",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|