@let-value/translate-extract 1.0.6-beta.2

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.
@@ -0,0 +1,710 @@
1
+ import fs from "node:fs";
2
+ import path, { basename, dirname, extname, join, relative, resolve } from "node:path";
3
+ import { globSync } from "glob";
4
+ import fs$1, { readFile } from "node:fs/promises";
5
+ import { assign, memo } from "radash";
6
+ import Parser from "tree-sitter";
7
+ import JavaScript from "tree-sitter-javascript";
8
+ import TS from "tree-sitter-typescript";
9
+ import { ResolverFactory } from "oxc-resolver";
10
+ import * as gettextParser from "gettext-parser";
11
+ import { getFormula, getNPlurals } from "plural-forms";
12
+
13
+ //#region src/plugins/core/queries/comment.ts
14
+ function getReference(node, { path: path$1 }) {
15
+ const line = node.startPosition.row + 1;
16
+ const col = node.startPosition.column + 1;
17
+ const rel = relative(process.cwd(), path$1);
18
+ return `${rel}:${line}:${col}`;
19
+ }
20
+ function getComment(node) {
21
+ const text = node.text;
22
+ if (text.startsWith("/*")) return text.slice(2, -2).replace(/^\s*\*?\s*/gm, "").trim();
23
+ return text.replace(/^\/\/\s?/, "").trim();
24
+ }
25
+ const withComment = (query) => ({
26
+ pattern: `(
27
+ ((comment) @comment)?
28
+ .
29
+ (_ ${query.pattern})
30
+ )`,
31
+ extract(match) {
32
+ const result = query.extract(match);
33
+ if (!result?.translation) return result;
34
+ const comment = match.captures.find((c) => c.name === "comment")?.node;
35
+ if (!comment) return result;
36
+ if (comment) result.translation.comments = {
37
+ ...result.translation.comments,
38
+ extracted: getComment(comment)
39
+ };
40
+ return result;
41
+ }
42
+ });
43
+
44
+ //#endregion
45
+ //#region src/plugins/core/queries/import.ts
46
+ const importQuery = {
47
+ pattern: `
48
+ [
49
+ (import_statement
50
+ source: (string (string_fragment) @import))
51
+ (export_statement
52
+ source: (string (string_fragment) @import))
53
+ (call_expression
54
+ function: (identifier) @func
55
+ arguments: (arguments (string (string_fragment) @import))
56
+ (#eq? @func "require"))
57
+ (call_expression
58
+ function: (member_expression
59
+ object: (identifier) @obj
60
+ property: (property_identifier) @method)
61
+ arguments: (arguments (string (string_fragment) @import))
62
+ (#eq? @obj "require")
63
+ (#eq? @method "resolve"))
64
+ (call_expression
65
+ function: (import)
66
+ arguments: (arguments (string (string_fragment) @import)))
67
+ ]
68
+ `,
69
+ extract(match) {
70
+ const node = match.captures.find((c) => c.name === "import")?.node;
71
+ return node?.text;
72
+ }
73
+ };
74
+
75
+ //#endregion
76
+ //#region src/plugins/core/queries/utils.ts
77
+ const callPattern = (fnName, args, allowMember = true) => `(
78
+ (call_expression
79
+ function: ${allowMember ? `[
80
+ (identifier) @func
81
+ (member_expression property: (property_identifier) @func)
82
+ ]` : `(identifier) @func`}
83
+ arguments: ${args}
84
+ ) @call
85
+ (#eq? @func "${fnName}")
86
+ )`;
87
+ function isDescendant(node, ancestor) {
88
+ let current = node;
89
+ while (current) {
90
+ if (current.id === ancestor.id) return true;
91
+ current = current.parent;
92
+ }
93
+ return false;
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/plugins/core/queries/message.ts
98
+ const notInPlural = (query) => ({
99
+ pattern: query.pattern,
100
+ extract(match) {
101
+ const result = query.extract(match);
102
+ if (!result?.node) return result;
103
+ let parent = result.node.parent;
104
+ if (parent && parent.type === "arguments") parent = parent.parent;
105
+ if (parent && parent.type === "call_expression") {
106
+ const fn = parent.childForFieldName("function");
107
+ if (fn) {
108
+ if (fn.type === "identifier" && (fn.text === "plural" || fn.text === "ngettext" || fn.text === "pgettext" || fn.text === "npgettext") || fn.type === "member_expression" && [
109
+ "plural",
110
+ "ngettext",
111
+ "pgettext",
112
+ "npgettext"
113
+ ].includes(fn.childForFieldName("property")?.text ?? "")) return void 0;
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ });
119
+ const messageArg = `[
120
+ (string (string_fragment) @msgid)
121
+ (object
122
+ (_)*
123
+ (pair
124
+ key: (property_identifier) @id_key
125
+ value: (string (string_fragment) @id)
126
+ (#eq? @id_key "id")
127
+ )?
128
+ (_)*
129
+ (pair
130
+ key: (property_identifier) @msg_key
131
+ value: (string (string_fragment) @message)
132
+ (#eq? @msg_key "message")
133
+ )?
134
+ (_)*
135
+ )
136
+ (template_string) @tpl
137
+ ]`;
138
+ const messageArgs = `[ (arguments ${messageArg}) (template_string) @tpl ]`;
139
+ const extractMessage = (name) => (match) => {
140
+ const node = match.captures.find((c) => c.name === "call")?.node;
141
+ if (!node) return void 0;
142
+ const msgid = match.captures.find((c) => c.name === "msgid")?.node.text;
143
+ if (msgid) return {
144
+ node,
145
+ translation: {
146
+ id: msgid,
147
+ message: [msgid]
148
+ }
149
+ };
150
+ const tpl = match.captures.find((c) => c.name === "tpl")?.node;
151
+ if (tpl) {
152
+ for (const child of tpl.children) {
153
+ if (child.type !== "template_substitution") continue;
154
+ const expr = child.namedChildren[0];
155
+ if (!expr || expr.type !== "identifier") return {
156
+ node,
157
+ error: `${name}() template expressions must be simple identifiers`
158
+ };
159
+ }
160
+ const text = tpl.text.slice(1, -1);
161
+ return {
162
+ node,
163
+ translation: {
164
+ id: text,
165
+ message: [text]
166
+ }
167
+ };
168
+ }
169
+ const id = match.captures.find((c) => c.name === "id")?.node.text;
170
+ const message = match.captures.find((c) => c.name === "message")?.node.text;
171
+ const msgId = id ?? message;
172
+ if (!msgId) return void 0;
173
+ const msgstr = message ?? id ?? "";
174
+ return {
175
+ node,
176
+ translation: {
177
+ id: msgId,
178
+ message: [msgstr]
179
+ }
180
+ };
181
+ };
182
+ const messageQuery = notInPlural(withComment({
183
+ pattern: callPattern("message", messageArgs),
184
+ extract: extractMessage("message")
185
+ }));
186
+ const allowed$1 = new Set([
187
+ "string",
188
+ "object",
189
+ "template_string"
190
+ ]);
191
+ const messageInvalidQuery = notInPlural({
192
+ pattern: callPattern("message", "(arguments (_) @arg)"),
193
+ extract(match) {
194
+ const call = match.captures.find((c) => c.name === "call")?.node;
195
+ const node = match.captures.find((c) => c.name === "arg")?.node;
196
+ if (!call || !node) return void 0;
197
+ if (allowed$1.has(node.type)) return void 0;
198
+ return {
199
+ node,
200
+ error: "message() argument must be a string literal, object literal, or template literal"
201
+ };
202
+ }
203
+ });
204
+
205
+ //#endregion
206
+ //#region src/plugins/core/queries/plural-utils.ts
207
+ const extractPluralForms = (name) => (match) => {
208
+ const call = match.captures.find((c) => c.name === "call")?.node;
209
+ const n = match.captures.find((c) => c.name === "n")?.node;
210
+ if (!call || !n || n.nextNamedSibling) return void 0;
211
+ const msgctxt = match.captures.find((c) => c.name === "msgctxt")?.node?.text;
212
+ const msgNodes = match.captures.filter((c) => c.name === "msg").map((c) => c.node);
213
+ const ids = [];
214
+ const strs = [];
215
+ for (const node of msgNodes) {
216
+ const relevant = match.captures.filter((c) => [
217
+ "msgid",
218
+ "id",
219
+ "message",
220
+ "tpl"
221
+ ].includes(c.name) && isDescendant(c.node, node));
222
+ const subMatch = {
223
+ pattern: 0,
224
+ captures: [{
225
+ name: "call",
226
+ node
227
+ }, ...relevant]
228
+ };
229
+ const result = extractMessage(name)(subMatch);
230
+ if (!result) continue;
231
+ if (result.error) return {
232
+ node: call,
233
+ error: result.error
234
+ };
235
+ if (result.translation) {
236
+ ids.push(result.translation.id);
237
+ strs.push(result.translation.message[0] ?? "");
238
+ }
239
+ }
240
+ if (ids.length === 0) return void 0;
241
+ const translation = {
242
+ id: ids[0],
243
+ plural: ids[1],
244
+ message: strs
245
+ };
246
+ if (msgctxt) translation.context = msgctxt;
247
+ return {
248
+ node: call,
249
+ translation
250
+ };
251
+ };
252
+
253
+ //#endregion
254
+ //#region src/plugins/core/queries/context.ts
255
+ const ctxCall = callPattern("context", `(arguments (string (string_fragment) @msgctxt))`).replace(/@call/g, "@ctx").replace(/@func/g, "@ctxfn");
256
+ const contextMsgQuery = withComment({
257
+ pattern: `(
258
+ (call_expression
259
+ function: (member_expression
260
+ object: ${ctxCall}
261
+ property: (property_identifier) @func
262
+ )
263
+ arguments: ${messageArgs}
264
+ ) @call
265
+ (#eq? @func "message")
266
+ )`,
267
+ extract(match) {
268
+ const result = extractMessage("context.message")(match);
269
+ const contextNode = match.captures.find((c) => c.name === "msgctxt")?.node;
270
+ if (!result || !result.translation || !contextNode) return result;
271
+ return {
272
+ node: result.node,
273
+ translation: {
274
+ ...result.translation,
275
+ context: contextNode.text
276
+ }
277
+ };
278
+ }
279
+ });
280
+ const msgCall$3 = callPattern("message", messageArgs, false).replace(/@call/g, "@msg").replace(/@func/g, "@msgfn");
281
+ const contextPluralQuery = withComment({
282
+ pattern: `(
283
+ (call_expression
284
+ function: (member_expression
285
+ object: ${ctxCall}
286
+ property: (property_identifier) @func
287
+ )
288
+ arguments: (arguments (
289
+ (${msgCall$3} ("," )?)+
290
+ (number) @n
291
+ ))
292
+ ) @call
293
+ (#eq? @func "plural")
294
+ )`,
295
+ extract: extractPluralForms("context.plural")
296
+ });
297
+ const contextInvalidQuery = withComment({
298
+ pattern: ctxCall,
299
+ extract(match) {
300
+ const call = match.captures.find((c) => c.name === "ctx")?.node;
301
+ if (!call) return void 0;
302
+ const parent = call.parent;
303
+ if (parent && parent.type === "member_expression" && parent.childForFieldName("object")?.id === call.id) {
304
+ const property = parent.childForFieldName("property")?.text;
305
+ const grandparent = parent.parent;
306
+ if (grandparent && grandparent.type === "call_expression" && grandparent.childForFieldName("function")?.id === parent.id && (property === "message" || property === "plural")) return void 0;
307
+ }
308
+ return {
309
+ node: call,
310
+ error: "context() must be used with message() or plural() in the same expression"
311
+ };
312
+ }
313
+ });
314
+
315
+ //#endregion
316
+ //#region src/plugins/core/queries/gettext.ts
317
+ const gettextQuery = withComment({
318
+ pattern: callPattern("gettext", messageArgs),
319
+ extract: extractMessage("gettext")
320
+ });
321
+ const allowed = new Set([
322
+ "string",
323
+ "object",
324
+ "template_string",
325
+ "identifier",
326
+ "call_expression"
327
+ ]);
328
+ const gettextInvalidQuery = {
329
+ pattern: callPattern("gettext", "(arguments (_) @arg)"),
330
+ extract(match) {
331
+ const call = match.captures.find((c) => c.name === "call")?.node;
332
+ const node = match.captures.find((c) => c.name === "arg")?.node;
333
+ if (!call || !node) return void 0;
334
+ if (allowed.has(node.type)) return void 0;
335
+ return {
336
+ node,
337
+ error: "gettext() argument must be a string literal, object literal, or template literal"
338
+ };
339
+ }
340
+ };
341
+
342
+ //#endregion
343
+ //#region src/plugins/core/queries/ngettext.ts
344
+ const msgCall$2 = callPattern("message", messageArgs).replace(/@call/g, "@msg").replace(/@func/g, "@msgfn");
345
+ const plainMsg$1 = `(${messageArg}) @msg`;
346
+ const msgArg$1 = `[${msgCall$2} ${plainMsg$1}]`;
347
+ const ngettextQuery = withComment({
348
+ pattern: callPattern("ngettext", `(arguments ${msgArg$1} "," ${msgArg$1} ("," ${msgArg$1})* "," (_) @n)`),
349
+ extract: extractPluralForms("ngettext")
350
+ });
351
+
352
+ //#endregion
353
+ //#region src/plugins/core/queries/npgettext.ts
354
+ const msgCall$1 = callPattern("message", messageArgs).replace(/@call/g, "@msg").replace(/@func/g, "@msgfn");
355
+ const plainMsg = `(${messageArg}) @msg`;
356
+ const msgArg = `[${msgCall$1} ${plainMsg}]`;
357
+ const npgettextQuery = withComment({
358
+ pattern: callPattern("npgettext", `(arguments (string (string_fragment) @msgctxt) "," ${msgArg} "," ${msgArg} ("," ${msgArg})* "," (_) @n)`),
359
+ extract: extractPluralForms("npgettext")
360
+ });
361
+
362
+ //#endregion
363
+ //#region src/plugins/core/queries/pgettext.ts
364
+ const pgettextQuery = withComment({
365
+ pattern: callPattern("pgettext", `(arguments (string (string_fragment) @msgctxt) "," ${messageArg})`),
366
+ extract(match) {
367
+ const result = extractMessage("pgettext")(match);
368
+ const contextNode = match.captures.find((c) => c.name === "msgctxt")?.node;
369
+ if (!result || !contextNode || !result.translation) return result;
370
+ return {
371
+ node: result.node,
372
+ translation: {
373
+ ...result.translation,
374
+ context: contextNode.text
375
+ }
376
+ };
377
+ }
378
+ });
379
+
380
+ //#endregion
381
+ //#region src/plugins/core/queries/plural.ts
382
+ const msgCall = callPattern("message", messageArgs, false).replace(/@call/g, "@msg").replace(/@func/g, "@msgfn");
383
+ const pluralQuery = withComment({
384
+ pattern: callPattern("plural", `(arguments (
385
+ (${msgCall} ("," )?)+
386
+ (number) @n
387
+ ))`, false),
388
+ extract: extractPluralForms("plural")
389
+ });
390
+
391
+ //#endregion
392
+ //#region src/plugins/core/queries/index.ts
393
+ const queries = [
394
+ messageQuery,
395
+ messageInvalidQuery,
396
+ gettextQuery,
397
+ gettextInvalidQuery,
398
+ pluralQuery,
399
+ ngettextQuery,
400
+ pgettextQuery,
401
+ npgettextQuery,
402
+ contextMsgQuery,
403
+ contextPluralQuery,
404
+ contextInvalidQuery
405
+ ];
406
+
407
+ //#endregion
408
+ //#region src/plugins/core/parse.ts
409
+ function getLanguage(ext) {
410
+ switch (ext) {
411
+ case ".ts": return TS.typescript;
412
+ case ".tsx": return TS.tsx;
413
+ default: return JavaScript;
414
+ }
415
+ }
416
+ const getCachedParser = memo(function getCachedParser$1(ext) {
417
+ const parser = new Parser();
418
+ const language = getLanguage(ext);
419
+ parser.setLanguage(language);
420
+ return {
421
+ parser,
422
+ language
423
+ };
424
+ });
425
+ function getParser(path$1) {
426
+ const ext = extname(path$1);
427
+ return getCachedParser(ext);
428
+ }
429
+ function parseSource(source, path$1) {
430
+ const context = { path: path$1 };
431
+ const { parser, language } = getParser(path$1);
432
+ const tree = parser.parse(source);
433
+ const translations = [];
434
+ const imports = [];
435
+ const seen = /* @__PURE__ */ new Set();
436
+ for (const spec of queries) {
437
+ const query = new Parser.Query(language, spec.pattern);
438
+ for (const match of query.matches(tree.rootNode)) {
439
+ const message = spec.extract(match);
440
+ if (!message) continue;
441
+ const { node, translation, error } = message;
442
+ if (seen.has(node.id)) continue;
443
+ seen.add(node.id);
444
+ const reference = getReference(node, context);
445
+ if (translation) translations.push({
446
+ ...translation,
447
+ comments: {
448
+ ...translation.comments,
449
+ reference
450
+ }
451
+ });
452
+ if (error) console.warn(`Parsing error at ${reference}: ${error}`);
453
+ }
454
+ }
455
+ const importTreeQuery = new Parser.Query(language, importQuery.pattern);
456
+ for (const match of importTreeQuery.matches(tree.rootNode)) {
457
+ const imp = importQuery.extract(match);
458
+ if (imp) imports.push(imp);
459
+ }
460
+ return {
461
+ translations,
462
+ imports
463
+ };
464
+ }
465
+
466
+ //#endregion
467
+ //#region src/plugins/core/resolve.ts
468
+ function findTsconfig(dir) {
469
+ let current = dir;
470
+ while (true) {
471
+ const config = path.join(current, "tsconfig.json");
472
+ if (fs.existsSync(config)) return config;
473
+ const parent = path.dirname(current);
474
+ if (parent === current) return void 0;
475
+ current = parent;
476
+ }
477
+ }
478
+ const resolverCache = /* @__PURE__ */ new Map();
479
+ function getResolver(dir) {
480
+ const tsconfig = findTsconfig(dir);
481
+ const key = tsconfig ?? "__default__";
482
+ let resolver = resolverCache.get(key);
483
+ if (!resolver) {
484
+ resolver = new ResolverFactory({
485
+ extensions: [
486
+ ".ts",
487
+ ".tsx",
488
+ ".js",
489
+ ".jsx",
490
+ ".mjs",
491
+ ".cjs",
492
+ ".json"
493
+ ],
494
+ conditionNames: [
495
+ "import",
496
+ "require",
497
+ "node"
498
+ ],
499
+ ...tsconfig ? { tsconfig: { configFile: tsconfig } } : {}
500
+ });
501
+ resolverCache.set(key, resolver);
502
+ }
503
+ return resolver;
504
+ }
505
+ function resolveImports(file, imports) {
506
+ const dir = path.dirname(path.resolve(file));
507
+ const resolver = getResolver(dir);
508
+ const resolved = [];
509
+ for (const spec of imports) {
510
+ const res = resolver.sync(dir, spec);
511
+ if (res.path) resolved.push(res.path);
512
+ }
513
+ return resolved;
514
+ }
515
+
516
+ //#endregion
517
+ //#region src/plugins/core/core.ts
518
+ const filter = /\.([cm]?tsx?|jsx?)$/;
519
+ function core() {
520
+ return {
521
+ name: "core",
522
+ setup(build) {
523
+ build.onResolve({ filter: /.*/ }, ({ entrypoint, path: path$1 }) => {
524
+ return {
525
+ entrypoint,
526
+ path: resolve(path$1)
527
+ };
528
+ });
529
+ build.onLoad({ filter }, async ({ entrypoint, path: path$1 }) => {
530
+ const contents = await readFile(path$1, "utf8");
531
+ return {
532
+ entrypoint,
533
+ path: path$1,
534
+ contents
535
+ };
536
+ });
537
+ build.onExtract({ filter }, ({ entrypoint, path: path$1, contents }) => {
538
+ const { translations, imports } = parseSource(contents, path$1);
539
+ if (build.context.config.walk) {
540
+ const paths = resolveImports(path$1, imports);
541
+ for (const path$2 of paths) build.resolvePath({
542
+ entrypoint,
543
+ path: path$2
544
+ });
545
+ }
546
+ return {
547
+ entrypoint,
548
+ path: path$1,
549
+ translations
550
+ };
551
+ });
552
+ }
553
+ };
554
+ }
555
+
556
+ //#endregion
557
+ //#region src/plugins/po/po.ts
558
+ function formatDate(date) {
559
+ const pad = (n) => n.toString().padStart(2, "0");
560
+ const year = date.getFullYear();
561
+ const month = pad(date.getMonth() + 1);
562
+ const day = pad(date.getDate());
563
+ const hours = pad(date.getHours());
564
+ const minutes = pad(date.getMinutes());
565
+ const tzo = -date.getTimezoneOffset();
566
+ const sign = tzo >= 0 ? "+" : "-";
567
+ const offsetHours = pad(Math.floor(Math.abs(tzo) / 60));
568
+ const offsetMinutes = pad(Math.abs(tzo) % 60);
569
+ return `${year}-${month}-${day} ${hours}:${minutes}${sign}${offsetHours}${offsetMinutes}`;
570
+ }
571
+ function collect(source, locale) {
572
+ const translations = { "": {} };
573
+ const nplurals = locale ? getNPlurals(locale) : void 0;
574
+ for (const { context, id, message, comments, obsolete, plural } of source) {
575
+ const ctx = context || "";
576
+ if (!translations[ctx]) translations[ctx] = {};
577
+ const length = plural ? nplurals ?? message.length : 1;
578
+ translations[ctx][id] = {
579
+ msgctxt: context || void 0,
580
+ msgid: id,
581
+ msgid_plural: plural,
582
+ msgstr: Array.from({ length }, () => ""),
583
+ comments,
584
+ obsolete
585
+ };
586
+ }
587
+ return translations;
588
+ }
589
+ function merge(locale, sources, existing, strategy = "mark", timestamp = /* @__PURE__ */ new Date()) {
590
+ let headers = {};
591
+ let translations = { "": {} };
592
+ const nplurals = getNPlurals(locale);
593
+ if (existing) {
594
+ const parsed = gettextParser.po.parse(existing);
595
+ headers = parsed.headers || {};
596
+ translations = parsed.translations || { "": {} };
597
+ for (const ctx of Object.keys(translations)) for (const id of Object.keys(translations[ctx])) {
598
+ if (ctx === "" && id === "") continue;
599
+ translations[ctx][id].obsolete = true;
600
+ }
601
+ }
602
+ const collected = sources.reduce((acc, { translations: translations$1 }) => assign(acc, translations$1), { "": {} });
603
+ for (const [ctx, msgs] of Object.entries(collected)) {
604
+ if (!translations[ctx]) translations[ctx] = {};
605
+ for (const [id, entry] of Object.entries(msgs)) {
606
+ const existingEntry = translations[ctx][id];
607
+ if (existingEntry) {
608
+ entry.msgstr = existingEntry.msgstr;
609
+ entry.comments = {
610
+ ...entry.comments,
611
+ translator: existingEntry.comments?.translator
612
+ };
613
+ }
614
+ entry.obsolete = false;
615
+ entry.msgstr = entry.msgstr.slice(0, nplurals);
616
+ while (entry.msgstr.length < nplurals) entry.msgstr.push("");
617
+ translations[ctx][id] = entry;
618
+ }
619
+ }
620
+ headers = {
621
+ ...headers,
622
+ "content-type": headers["content-type"] || "text/plain; charset=UTF-8",
623
+ "plural-forms": `nplurals=${nplurals}; plural=${getFormula(locale)};`,
624
+ language: locale,
625
+ "pot-creation-date": formatDate(timestamp),
626
+ "x-generator": "@let-value/translate-extract"
627
+ };
628
+ if (strategy === "remove") {
629
+ for (const ctx of Object.keys(translations)) for (const id of Object.keys(translations[ctx])) if (translations[ctx][id].obsolete) delete translations[ctx][id];
630
+ }
631
+ const poObj = {
632
+ charset: "utf-8",
633
+ headers,
634
+ translations
635
+ };
636
+ return gettextParser.po.compile(poObj).toString();
637
+ }
638
+ function po() {
639
+ return {
640
+ name: "po",
641
+ setup(build) {
642
+ build.onCollect({ filter: /.*/ }, ({ entrypoint, translations,...rest }, ctx) => {
643
+ const record = collect(translations, ctx.locale);
644
+ const destination = `${basename(entrypoint, extname(entrypoint))}.po`;
645
+ return {
646
+ ...rest,
647
+ entrypoint,
648
+ destination,
649
+ translations: record
650
+ };
651
+ });
652
+ build.onGenerate({ filter: /.*\/po$/ }, async ({ path: path$1, locale, collected }, ctx) => {
653
+ const existing = await fs$1.readFile(path$1).catch(() => void 0);
654
+ const out = merge(locale, collected, existing, ctx.config.obsolete, ctx.generatedAt);
655
+ await fs$1.mkdir(dirname(path$1), { recursive: true });
656
+ await fs$1.writeFile(path$1, out);
657
+ });
658
+ }
659
+ };
660
+ }
661
+
662
+ //#endregion
663
+ //#region src/configuration.ts
664
+ const defaultPlugins = [core(), po()];
665
+ const defaultDestination = (locale, _entrypoint, path$1) => join(locale, path$1);
666
+ function defineConfig(config) {
667
+ let plugins;
668
+ const user = config.plugins;
669
+ if (typeof user === "function") plugins = user(defaultPlugins);
670
+ else if (Array.isArray(user)) plugins = [...defaultPlugins, ...user];
671
+ else plugins = defaultPlugins;
672
+ const raw = Array.isArray(config.entrypoints) ? config.entrypoints : [config.entrypoints];
673
+ const entrypoints = [];
674
+ for (const ep of raw) if (typeof ep === "string") {
675
+ const paths = globSync(ep, { nodir: true });
676
+ if (paths.length === 0) entrypoints.push({ entrypoint: ep });
677
+ else for (const path$1 of paths) entrypoints.push({ entrypoint: path$1 });
678
+ } else {
679
+ const { entrypoint, destination: destination$1, obsolete: obsolete$1 } = ep;
680
+ const paths = globSync(entrypoint, { nodir: true });
681
+ if (paths.length === 0) entrypoints.push({
682
+ entrypoint,
683
+ destination: destination$1,
684
+ obsolete: obsolete$1
685
+ });
686
+ else for (const path$1 of paths) entrypoints.push({
687
+ entrypoint: path$1,
688
+ destination: destination$1,
689
+ obsolete: obsolete$1
690
+ });
691
+ }
692
+ const defaultLocale = config.defaultLocale ?? "en";
693
+ const locales = config.locales ?? [defaultLocale];
694
+ const destination = config.destination ?? defaultDestination;
695
+ const obsolete = config.obsolete ?? "mark";
696
+ const walk = config.walk ?? true;
697
+ return {
698
+ plugins,
699
+ entrypoints,
700
+ defaultLocale,
701
+ locales,
702
+ destination,
703
+ obsolete,
704
+ walk
705
+ };
706
+ }
707
+
708
+ //#endregion
709
+ export { defineConfig };
710
+ //# sourceMappingURL=index.js.map