@dialexjs/cli 0.9.1 → 0.9.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.
- package/dist/api.d.mts +42 -39
- package/dist/api.d.mts.map +1 -1
- package/dist/api.mjs +1 -1
- package/dist/index.mjs +329 -395
- package/dist/index.mjs.map +1 -1
- package/dist/{project-B27699T2.mjs → project-BTpZGNjh.mjs} +232 -208
- package/dist/project-BTpZGNjh.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/project-B27699T2.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import pc from "picocolors";
|
|
4
|
-
import
|
|
3
|
+
import fs from "node:fs";
|
|
5
4
|
import { generateCode, parseExpression, parseModule, writeFile } from "magicast";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
6
|
import fg from "fast-glob";
|
|
7
7
|
import ignore from "ignore";
|
|
8
8
|
import { argumentSignature, findPlurals, getArguments, isIcuStructured, parseMessage } from "dialexjs/icu";
|
|
@@ -10,6 +10,9 @@ import { createHash } from "node:crypto";
|
|
|
10
10
|
import { DEFAULT_CONFIG } from "dialexjs";
|
|
11
11
|
import { loadConfigSync } from "unconfig";
|
|
12
12
|
import chokidar from "chokidar";
|
|
13
|
+
//#region src/utils/dictionary/types.ts
|
|
14
|
+
const TODO_PREFIX = "[TODO] ";
|
|
15
|
+
//#endregion
|
|
13
16
|
//#region src/utils/ast.ts
|
|
14
17
|
/** Strips `as`, `satisfies` and parentheses around an expression. */
|
|
15
18
|
function unwrapNode(node) {
|
|
@@ -31,8 +34,7 @@ function findProperty(obj, key) {
|
|
|
31
34
|
return obj.properties.find((p) => isPropLike$1(p) && propertyKeyName(p) === key);
|
|
32
35
|
}
|
|
33
36
|
//#endregion
|
|
34
|
-
//#region src/utils/dictionary-
|
|
35
|
-
const TODO_PREFIX = "[TODO] ";
|
|
37
|
+
//#region src/utils/dictionary/ast-helpers.ts
|
|
36
38
|
const IDENTIFIER$1 = /^[A-Za-z_$][\w$]*$/;
|
|
37
39
|
const isPropLike = (p) => p?.type === "ObjectProperty" || p?.type === "Property" || p?.type === "ObjectMethod";
|
|
38
40
|
function stringValue(node) {
|
|
@@ -99,6 +101,8 @@ function isStaticallyComplete(localesObj) {
|
|
|
99
101
|
return isObjectNode(value) && walk(value);
|
|
100
102
|
});
|
|
101
103
|
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/utils/dictionary/io.ts
|
|
102
106
|
/**
|
|
103
107
|
* Parses dictionary source text (for example an unsaved editor buffer). Returns `undefined` when
|
|
104
108
|
* the file does not have an editable shape; throws on syntax errors.
|
|
@@ -134,6 +138,8 @@ async function saveDictionaryFile(df) {
|
|
|
134
138
|
if (!text.endsWith("\n")) fs.writeFileSync(df.file, text + "\n", "utf-8");
|
|
135
139
|
}
|
|
136
140
|
}
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/utils/dictionary/read.ts
|
|
137
143
|
function listLocales(df) {
|
|
138
144
|
return df.localesObj.properties.filter(isPropLike).map(propertyKeyName).filter((k) => k !== void 0);
|
|
139
145
|
}
|
|
@@ -184,6 +190,13 @@ function getString(df, locale, path) {
|
|
|
184
190
|
const prop = getProp(df, locale, path);
|
|
185
191
|
return prop && prop.type !== "ObjectMethod" ? stringValue(prop.value) : void 0;
|
|
186
192
|
}
|
|
193
|
+
/** Leaf paths of a runtime dictionary value (plain objects recurse; everything else is a leaf). */
|
|
194
|
+
function flattenPaths(value, prefix = []) {
|
|
195
|
+
if (value && typeof value === "object" && !Array.isArray(value)) return Object.entries(value).flatMap(([k, v]) => flattenPaths(v, [...prefix, k]));
|
|
196
|
+
return prefix.length > 0 ? [prefix] : [];
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/utils/dictionary/write.ts
|
|
187
200
|
function ensureLocale(df, locale) {
|
|
188
201
|
let obj = localeObject(df, locale);
|
|
189
202
|
if (!obj) {
|
|
@@ -245,11 +258,8 @@ function copyLeaf(df, fromLocale, toLocale, path, mapString) {
|
|
|
245
258
|
}
|
|
246
259
|
return true;
|
|
247
260
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (value && typeof value === "object" && !Array.isArray(value)) return Object.entries(value).flatMap(([k, v]) => flattenPaths(v, [...prefix, k]));
|
|
251
|
-
return prefix.length > 0 ? [prefix] : [];
|
|
252
|
-
}
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/utils/dictionary/locations.ts
|
|
253
263
|
const toRange = (loc) => loc ? {
|
|
254
264
|
start: {
|
|
255
265
|
line: loc.start.line - 1,
|
|
@@ -279,6 +289,153 @@ function getLeafSource(df, locale, path) {
|
|
|
279
289
|
return generateCode(prop.type === "ObjectMethod" ? prop : prop.value).code;
|
|
280
290
|
}
|
|
281
291
|
//#endregion
|
|
292
|
+
//#region src/analysis/cache.ts
|
|
293
|
+
function createAnalysisCache() {
|
|
294
|
+
return {
|
|
295
|
+
dictionaries: /* @__PURE__ */ new Map(),
|
|
296
|
+
sources: /* @__PURE__ */ new Map(),
|
|
297
|
+
gitignore: /* @__PURE__ */ new Map()
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/** Forget the cached `.gitignore` rules, for example after a `.gitignore` file changed. */
|
|
301
|
+
function clearGitignore(cache) {
|
|
302
|
+
cache.gitignore.clear();
|
|
303
|
+
}
|
|
304
|
+
function stampOf(file) {
|
|
305
|
+
try {
|
|
306
|
+
const stat = fs.statSync(file);
|
|
307
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
308
|
+
} catch {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/analysis/load-dictionary.ts
|
|
314
|
+
function fromSyntaxTree(df) {
|
|
315
|
+
const locales = listLocales(df);
|
|
316
|
+
const leaves = /* @__PURE__ */ new Map();
|
|
317
|
+
for (const locale of locales) {
|
|
318
|
+
const entries = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const leaf of listLeaves(df, locale)) entries.set(leaf.path.join("."), {
|
|
320
|
+
kind: leaf.kind,
|
|
321
|
+
value: leaf.value
|
|
322
|
+
});
|
|
323
|
+
leaves.set(locale, entries);
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
name: df.name,
|
|
327
|
+
locales,
|
|
328
|
+
leaves,
|
|
329
|
+
df
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async function fromRuntime(file) {
|
|
333
|
+
try {
|
|
334
|
+
const mod = await import(`${pathToFileURL(file).href}?t=${Date.now()}_${Math.random()}`);
|
|
335
|
+
const definition = mod.default || mod;
|
|
336
|
+
if (!definition || typeof definition !== "object" || !definition.dictionary) return {
|
|
337
|
+
ok: false,
|
|
338
|
+
level: "error",
|
|
339
|
+
code: "invalid-dictionary",
|
|
340
|
+
message: "Default export does not appear to be a valid defineDictionary output."
|
|
341
|
+
};
|
|
342
|
+
const dictionary = definition.dictionary;
|
|
343
|
+
const leaves = /* @__PURE__ */ new Map();
|
|
344
|
+
const invalid = [];
|
|
345
|
+
for (const [locale, content] of Object.entries(dictionary)) {
|
|
346
|
+
if (!content || typeof content !== "object") {
|
|
347
|
+
invalid.push(locale);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const entries = /* @__PURE__ */ new Map();
|
|
351
|
+
for (const leafPath of flattenPaths(content)) {
|
|
352
|
+
let value = content;
|
|
353
|
+
for (const segment of leafPath) value = value?.[segment];
|
|
354
|
+
entries.set(leafPath.join("."), typeof value === "string" ? {
|
|
355
|
+
kind: "string",
|
|
356
|
+
value
|
|
357
|
+
} : { kind: typeof value === "function" ? "function" : "other" });
|
|
358
|
+
}
|
|
359
|
+
leaves.set(locale, entries);
|
|
360
|
+
}
|
|
361
|
+
if (invalid.length > 0) return {
|
|
362
|
+
ok: false,
|
|
363
|
+
level: "error",
|
|
364
|
+
code: "invalid-locale",
|
|
365
|
+
message: invalid.map((l) => `Locale "${l}" content is empty or invalid.`).join("\n")
|
|
366
|
+
};
|
|
367
|
+
return {
|
|
368
|
+
ok: true,
|
|
369
|
+
data: {
|
|
370
|
+
name: String(definition.name ?? ""),
|
|
371
|
+
locales: [...leaves.keys()],
|
|
372
|
+
leaves
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
} catch (error) {
|
|
376
|
+
return {
|
|
377
|
+
ok: false,
|
|
378
|
+
level: "error",
|
|
379
|
+
code: "invalid-dictionary",
|
|
380
|
+
message: `Failed to import dictionary: ${error.message || String(error)}`
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function fromParsed(parsed) {
|
|
385
|
+
try {
|
|
386
|
+
const df = parsed();
|
|
387
|
+
if (df?.complete && df.name) return {
|
|
388
|
+
ok: true,
|
|
389
|
+
data: fromSyntaxTree(df)
|
|
390
|
+
};
|
|
391
|
+
} catch (error) {
|
|
392
|
+
return {
|
|
393
|
+
ok: false,
|
|
394
|
+
level: "error",
|
|
395
|
+
code: "invalid-dictionary",
|
|
396
|
+
message: `Failed to parse dictionary: ${error.message}`
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const UNANALYZABLE = {
|
|
401
|
+
ok: false,
|
|
402
|
+
level: "info",
|
|
403
|
+
code: "unanalyzable",
|
|
404
|
+
message: "This dictionary uses spreads, computed keys, imported values, or a computed name, so it cannot be analyzed statically."
|
|
405
|
+
};
|
|
406
|
+
/** Reads a dictionary from its syntax tree. Never runs project code. */
|
|
407
|
+
function loadDictionaryDataStatic(file) {
|
|
408
|
+
return fromParsed(() => parseDictionaryText(file, fs.readFileSync(file, "utf-8"))) ?? UNANALYZABLE;
|
|
409
|
+
}
|
|
410
|
+
async function loadDictionaryData(file, runtime) {
|
|
411
|
+
const parsed = fromParsed(() => parseDictionaryText(file, fs.readFileSync(file, "utf-8")));
|
|
412
|
+
if (parsed?.ok) return parsed;
|
|
413
|
+
if (runtime) return fromRuntime(file);
|
|
414
|
+
return parsed ?? UNANALYZABLE;
|
|
415
|
+
}
|
|
416
|
+
async function cachedDictionary(file, runtime, cache) {
|
|
417
|
+
const stamp = cache ? stampOf(file) : void 0;
|
|
418
|
+
const hit = stamp ? cache.dictionaries.get(file) : void 0;
|
|
419
|
+
if (hit && hit.stamp === stamp) return hit.loaded;
|
|
420
|
+
const loaded = await loadDictionaryData(file, runtime);
|
|
421
|
+
if (cache && stamp) cache.dictionaries.set(file, {
|
|
422
|
+
stamp,
|
|
423
|
+
loaded
|
|
424
|
+
});
|
|
425
|
+
return loaded;
|
|
426
|
+
}
|
|
427
|
+
function cachedDictionaryStatic(file, cache) {
|
|
428
|
+
const stamp = cache ? stampOf(file) : void 0;
|
|
429
|
+
const hit = stamp ? cache.dictionaries.get(file) : void 0;
|
|
430
|
+
if (hit && hit.stamp === stamp) return hit.loaded;
|
|
431
|
+
const loaded = loadDictionaryDataStatic(file);
|
|
432
|
+
if (cache && stamp) cache.dictionaries.set(file, {
|
|
433
|
+
stamp,
|
|
434
|
+
loaded
|
|
435
|
+
});
|
|
436
|
+
return loaded;
|
|
437
|
+
}
|
|
438
|
+
//#endregion
|
|
282
439
|
//#region src/utils/files.ts
|
|
283
440
|
/** Folders that never hold project dictionaries or sources, in every scanner. */
|
|
284
441
|
const BUILTIN_IGNORE = [
|
|
@@ -523,22 +680,7 @@ function lockFor(root, sourceLocale) {
|
|
|
523
680
|
};
|
|
524
681
|
}
|
|
525
682
|
//#endregion
|
|
526
|
-
//#region src/analysis.ts
|
|
527
|
-
function lintLevel(setting) {
|
|
528
|
-
const level = Array.isArray(setting) ? setting[0] : setting;
|
|
529
|
-
return level === "off" || level === "warn" || level === "error" ? level : void 0;
|
|
530
|
-
}
|
|
531
|
-
function createAnalysisCache() {
|
|
532
|
-
return {
|
|
533
|
-
dictionaries: /* @__PURE__ */ new Map(),
|
|
534
|
-
sources: /* @__PURE__ */ new Map(),
|
|
535
|
-
gitignore: /* @__PURE__ */ new Map()
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
/** Forget the cached `.gitignore` rules, for example after a `.gitignore` file changed. */
|
|
539
|
-
function clearGitignore(cache) {
|
|
540
|
-
cache.gitignore.clear();
|
|
541
|
-
}
|
|
683
|
+
//#region src/analysis/source-scan.ts
|
|
542
684
|
const DEFAULT_SOURCE_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,vue,svelte,astro,mdx}";
|
|
543
685
|
/** `getDictionary("x")`, `useDictionary("x")`, `@DialexDictionary("x")` */
|
|
544
686
|
const DICTIONARY_CALL = /\b(?:getDictionary|useDictionary|DialexDictionary)\(\s*(["'`])([^"'`$\n]+)\1/dg;
|
|
@@ -694,138 +836,6 @@ function positionAt(starts, offset) {
|
|
|
694
836
|
column: offset - starts[low]
|
|
695
837
|
};
|
|
696
838
|
}
|
|
697
|
-
function fromSyntaxTree(df) {
|
|
698
|
-
const locales = listLocales(df);
|
|
699
|
-
const leaves = /* @__PURE__ */ new Map();
|
|
700
|
-
for (const locale of locales) {
|
|
701
|
-
const entries = /* @__PURE__ */ new Map();
|
|
702
|
-
for (const leaf of listLeaves(df, locale)) entries.set(leaf.path.join("."), {
|
|
703
|
-
kind: leaf.kind,
|
|
704
|
-
value: leaf.value
|
|
705
|
-
});
|
|
706
|
-
leaves.set(locale, entries);
|
|
707
|
-
}
|
|
708
|
-
return {
|
|
709
|
-
name: df.name,
|
|
710
|
-
locales,
|
|
711
|
-
leaves,
|
|
712
|
-
df
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
async function fromRuntime(file) {
|
|
716
|
-
try {
|
|
717
|
-
const mod = await import(`${pathToFileURL(file).href}?t=${Date.now()}_${Math.random()}`);
|
|
718
|
-
const definition = mod.default || mod;
|
|
719
|
-
if (!definition || typeof definition !== "object" || !definition.dictionary) return {
|
|
720
|
-
ok: false,
|
|
721
|
-
level: "error",
|
|
722
|
-
code: "invalid-dictionary",
|
|
723
|
-
message: "Default export does not appear to be a valid defineDictionary output."
|
|
724
|
-
};
|
|
725
|
-
const dictionary = definition.dictionary;
|
|
726
|
-
const leaves = /* @__PURE__ */ new Map();
|
|
727
|
-
const invalid = [];
|
|
728
|
-
for (const [locale, content] of Object.entries(dictionary)) {
|
|
729
|
-
if (!content || typeof content !== "object") {
|
|
730
|
-
invalid.push(locale);
|
|
731
|
-
continue;
|
|
732
|
-
}
|
|
733
|
-
const entries = /* @__PURE__ */ new Map();
|
|
734
|
-
for (const leafPath of flattenPaths(content)) {
|
|
735
|
-
let value = content;
|
|
736
|
-
for (const segment of leafPath) value = value?.[segment];
|
|
737
|
-
entries.set(leafPath.join("."), typeof value === "string" ? {
|
|
738
|
-
kind: "string",
|
|
739
|
-
value
|
|
740
|
-
} : { kind: typeof value === "function" ? "function" : "other" });
|
|
741
|
-
}
|
|
742
|
-
leaves.set(locale, entries);
|
|
743
|
-
}
|
|
744
|
-
if (invalid.length > 0) return {
|
|
745
|
-
ok: false,
|
|
746
|
-
level: "error",
|
|
747
|
-
code: "invalid-locale",
|
|
748
|
-
message: invalid.map((l) => `Locale "${l}" content is empty or invalid.`).join("\n")
|
|
749
|
-
};
|
|
750
|
-
return {
|
|
751
|
-
ok: true,
|
|
752
|
-
data: {
|
|
753
|
-
name: String(definition.name ?? ""),
|
|
754
|
-
locales: [...leaves.keys()],
|
|
755
|
-
leaves
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
} catch (error) {
|
|
759
|
-
return {
|
|
760
|
-
ok: false,
|
|
761
|
-
level: "error",
|
|
762
|
-
code: "invalid-dictionary",
|
|
763
|
-
message: `Failed to import dictionary: ${error.message || String(error)}`
|
|
764
|
-
};
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
function fromParsed(parsed) {
|
|
768
|
-
try {
|
|
769
|
-
const df = parsed();
|
|
770
|
-
if (df?.complete && df.name) return {
|
|
771
|
-
ok: true,
|
|
772
|
-
data: fromSyntaxTree(df)
|
|
773
|
-
};
|
|
774
|
-
} catch (error) {
|
|
775
|
-
return {
|
|
776
|
-
ok: false,
|
|
777
|
-
level: "error",
|
|
778
|
-
code: "invalid-dictionary",
|
|
779
|
-
message: `Failed to parse dictionary: ${error.message}`
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
const UNANALYZABLE = {
|
|
784
|
-
ok: false,
|
|
785
|
-
level: "info",
|
|
786
|
-
code: "unanalyzable",
|
|
787
|
-
message: "This dictionary uses spreads, computed keys, imported values, or a computed name, so it cannot be analyzed statically."
|
|
788
|
-
};
|
|
789
|
-
/** Reads a dictionary from its syntax tree. Never runs project code. */
|
|
790
|
-
function loadDictionaryDataStatic(file) {
|
|
791
|
-
return fromParsed(() => parseDictionaryText(file, fs.readFileSync(file, "utf-8"))) ?? UNANALYZABLE;
|
|
792
|
-
}
|
|
793
|
-
async function loadDictionaryData(file, runtime) {
|
|
794
|
-
const parsed = fromParsed(() => parseDictionaryText(file, fs.readFileSync(file, "utf-8")));
|
|
795
|
-
if (parsed?.ok) return parsed;
|
|
796
|
-
if (runtime) return fromRuntime(file);
|
|
797
|
-
return parsed ?? UNANALYZABLE;
|
|
798
|
-
}
|
|
799
|
-
function stampOf(file) {
|
|
800
|
-
try {
|
|
801
|
-
const stat = fs.statSync(file);
|
|
802
|
-
return `${stat.mtimeMs}:${stat.size}`;
|
|
803
|
-
} catch {
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
async function cachedDictionary(file, runtime, cache) {
|
|
808
|
-
const stamp = cache ? stampOf(file) : void 0;
|
|
809
|
-
const hit = stamp ? cache.dictionaries.get(file) : void 0;
|
|
810
|
-
if (hit && hit.stamp === stamp) return hit.loaded;
|
|
811
|
-
const loaded = await loadDictionaryData(file, runtime);
|
|
812
|
-
if (cache && stamp) cache.dictionaries.set(file, {
|
|
813
|
-
stamp,
|
|
814
|
-
loaded
|
|
815
|
-
});
|
|
816
|
-
return loaded;
|
|
817
|
-
}
|
|
818
|
-
function cachedDictionaryStatic(file, cache) {
|
|
819
|
-
const stamp = cache ? stampOf(file) : void 0;
|
|
820
|
-
const hit = stamp ? cache.dictionaries.get(file) : void 0;
|
|
821
|
-
if (hit && hit.stamp === stamp) return hit.loaded;
|
|
822
|
-
const loaded = loadDictionaryDataStatic(file);
|
|
823
|
-
if (cache && stamp) cache.dictionaries.set(file, {
|
|
824
|
-
stamp,
|
|
825
|
-
loaded
|
|
826
|
-
});
|
|
827
|
-
return loaded;
|
|
828
|
-
}
|
|
829
839
|
function scanSource(text) {
|
|
830
840
|
const starts = lineStarts(text);
|
|
831
841
|
const rangeOf = (start, end) => ({
|
|
@@ -842,6 +852,12 @@ function scanSource(text) {
|
|
|
842
852
|
inline
|
|
843
853
|
};
|
|
844
854
|
}
|
|
855
|
+
//#endregion
|
|
856
|
+
//#region src/analysis/steps.ts
|
|
857
|
+
function lintLevel(setting) {
|
|
858
|
+
const level = Array.isArray(setting) ? setting[0] : setting;
|
|
859
|
+
return level === "off" || level === "warn" || level === "error" ? level : void 0;
|
|
860
|
+
}
|
|
845
861
|
/**
|
|
846
862
|
* The analysis itself, written once. It yields a `LoadRequest` for every dictionary so that a
|
|
847
863
|
* synchronous driver (`analyzeProjectSync`) and an asynchronous one (`analyzeProject`, which may
|
|
@@ -1086,6 +1102,8 @@ function* analysisSteps(options) {
|
|
|
1086
1102
|
inlineDictionaries: [...inline]
|
|
1087
1103
|
};
|
|
1088
1104
|
}
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/analysis/index.ts
|
|
1089
1107
|
/** Analyzes a project. With `runtime: true`, dictionaries the syntax tree cannot describe are imported. */
|
|
1090
1108
|
async function analyzeProject(options) {
|
|
1091
1109
|
const steps = analysisSteps(options);
|
|
@@ -1721,7 +1739,7 @@ function relativeImportExtension(root) {
|
|
|
1721
1739
|
return resolution === "node16" || resolution === "nodenext" ? ".js" : "";
|
|
1722
1740
|
}
|
|
1723
1741
|
//#endregion
|
|
1724
|
-
//#region src/commands/generate.ts
|
|
1742
|
+
//#region src/commands/generate/render.ts
|
|
1725
1743
|
/** The folder, next to `dialex.generated.ts`, that holds the per-locale modules. */
|
|
1726
1744
|
const LOCALES_DIR = "dialex.locales";
|
|
1727
1745
|
const safeName = (text) => text.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -1841,6 +1859,58 @@ export default dictionaries;
|
|
|
1841
1859
|
notes
|
|
1842
1860
|
};
|
|
1843
1861
|
}
|
|
1862
|
+
//#endregion
|
|
1863
|
+
//#region src/commands/generate/watch.ts
|
|
1864
|
+
const WATCH_IGNORED = /(^|[\\/])(node_modules|dist|\.next|\.nuxt|\.output|\.turbo|\.git|dialex\.locales)([\\/]|$)/;
|
|
1865
|
+
const CONFIG_FILE = /(^|[\\/])(dialex|i18n)\.config\.[cm]?[jt]s(on)?$/;
|
|
1866
|
+
/**
|
|
1867
|
+
* Regenerates when a dictionary or config file is added, edited or removed. chokidar 4+ no longer
|
|
1868
|
+
* understands globs, so this watches the project directory and decides for itself which events
|
|
1869
|
+
* matter, using the same `include` glob as `generate`.
|
|
1870
|
+
*/
|
|
1871
|
+
function startGenerateWatcher(root, options, regenerate, delay = 100, log = (message) => logger.info(message)) {
|
|
1872
|
+
const dictionaryFiles = () => new Set(renderGenerated(root, options).files);
|
|
1873
|
+
let known = dictionaryFiles();
|
|
1874
|
+
let outputs = /* @__PURE__ */ new Set();
|
|
1875
|
+
let timer;
|
|
1876
|
+
const trigger = () => {
|
|
1877
|
+
if (timer) clearTimeout(timer);
|
|
1878
|
+
timer = setTimeout(() => {
|
|
1879
|
+
regenerate();
|
|
1880
|
+
try {
|
|
1881
|
+
known = dictionaryFiles();
|
|
1882
|
+
const rendered = renderGenerated(root, options);
|
|
1883
|
+
outputs = /* @__PURE__ */ new Set([rendered.outputPath, rendered.dtsPath]);
|
|
1884
|
+
} catch {}
|
|
1885
|
+
}, delay);
|
|
1886
|
+
};
|
|
1887
|
+
const gitIgnored = createGitignoreFilter(root);
|
|
1888
|
+
const watcher = chokidar.watch(root, {
|
|
1889
|
+
ignoreInitial: true,
|
|
1890
|
+
ignored: (file) => WATCH_IGNORED.test(file) || gitIgnored(path.resolve(root, file))
|
|
1891
|
+
});
|
|
1892
|
+
watcher.on("all", (event, filePath) => {
|
|
1893
|
+
const file = path.resolve(root, filePath);
|
|
1894
|
+
if (outputs.has(file)) return;
|
|
1895
|
+
const isConfig = CONFIG_FILE.test(file);
|
|
1896
|
+
if (event === "change") {
|
|
1897
|
+
if (!known.has(file) && !isConfig) return;
|
|
1898
|
+
} else if (event === "add" || event === "unlink") {
|
|
1899
|
+
let now;
|
|
1900
|
+
try {
|
|
1901
|
+
now = dictionaryFiles();
|
|
1902
|
+
} catch {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
if (!(now.size !== known.size || [...now].some((f) => !known.has(f))) && !isConfig) return;
|
|
1906
|
+
} else return;
|
|
1907
|
+
log(`[${event}] ${path.relative(root, file)} changed, regenerating...`);
|
|
1908
|
+
trigger();
|
|
1909
|
+
});
|
|
1910
|
+
return watcher;
|
|
1911
|
+
}
|
|
1912
|
+
//#endregion
|
|
1913
|
+
//#region src/commands/generate/write.ts
|
|
1844
1914
|
function writeIfChanged(file, content) {
|
|
1845
1915
|
if ((fs.existsSync(file) ? fs.readFileSync(file, "utf-8") : "") === content) return;
|
|
1846
1916
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -1868,6 +1938,8 @@ function generateDictionaries(root, options = {}) {
|
|
|
1868
1938
|
notes: rendered.notes
|
|
1869
1939
|
};
|
|
1870
1940
|
}
|
|
1941
|
+
//#endregion
|
|
1942
|
+
//#region src/commands/generate/index.ts
|
|
1871
1943
|
async function runGenerate(options = {}) {
|
|
1872
1944
|
const root = options.cwd || process.cwd();
|
|
1873
1945
|
const quiet = options.json === true;
|
|
@@ -1904,54 +1976,6 @@ async function runGenerate(options = {}) {
|
|
|
1904
1976
|
startGenerateWatcher(root, options, doGenerate);
|
|
1905
1977
|
}
|
|
1906
1978
|
}
|
|
1907
|
-
const WATCH_IGNORED = /(^|[\\/])(node_modules|dist|\.next|\.nuxt|\.output|\.turbo|\.git|dialex\.locales)([\\/]|$)/;
|
|
1908
|
-
const CONFIG_FILE = /(^|[\\/])(dialex|i18n)\.config\.[cm]?[jt]s(on)?$/;
|
|
1909
|
-
/**
|
|
1910
|
-
* Regenerates when a dictionary or config file is added, edited or removed. chokidar 4+ no longer
|
|
1911
|
-
* understands globs, so this watches the project directory and decides for itself which events
|
|
1912
|
-
* matter, using the same `include` glob as `generate`.
|
|
1913
|
-
*/
|
|
1914
|
-
function startGenerateWatcher(root, options, regenerate, delay = 100, log = (message) => logger.info(message)) {
|
|
1915
|
-
const dictionaryFiles = () => new Set(renderGenerated(root, options).files);
|
|
1916
|
-
let known = dictionaryFiles();
|
|
1917
|
-
let outputs = /* @__PURE__ */ new Set();
|
|
1918
|
-
let timer;
|
|
1919
|
-
const trigger = () => {
|
|
1920
|
-
if (timer) clearTimeout(timer);
|
|
1921
|
-
timer = setTimeout(() => {
|
|
1922
|
-
regenerate();
|
|
1923
|
-
try {
|
|
1924
|
-
known = dictionaryFiles();
|
|
1925
|
-
const rendered = renderGenerated(root, options);
|
|
1926
|
-
outputs = /* @__PURE__ */ new Set([rendered.outputPath, rendered.dtsPath]);
|
|
1927
|
-
} catch {}
|
|
1928
|
-
}, delay);
|
|
1929
|
-
};
|
|
1930
|
-
const gitIgnored = createGitignoreFilter(root);
|
|
1931
|
-
const watcher = chokidar.watch(root, {
|
|
1932
|
-
ignoreInitial: true,
|
|
1933
|
-
ignored: (file) => WATCH_IGNORED.test(file) || gitIgnored(path.resolve(root, file))
|
|
1934
|
-
});
|
|
1935
|
-
watcher.on("all", (event, filePath) => {
|
|
1936
|
-
const file = path.resolve(root, filePath);
|
|
1937
|
-
if (outputs.has(file)) return;
|
|
1938
|
-
const isConfig = CONFIG_FILE.test(file);
|
|
1939
|
-
if (event === "change") {
|
|
1940
|
-
if (!known.has(file) && !isConfig) return;
|
|
1941
|
-
} else if (event === "add" || event === "unlink") {
|
|
1942
|
-
let now;
|
|
1943
|
-
try {
|
|
1944
|
-
now = dictionaryFiles();
|
|
1945
|
-
} catch {
|
|
1946
|
-
return;
|
|
1947
|
-
}
|
|
1948
|
-
if (!(now.size !== known.size || [...now].some((f) => !known.has(f))) && !isConfig) return;
|
|
1949
|
-
} else return;
|
|
1950
|
-
log(`[${event}] ${path.relative(root, file)} changed, regenerating...`);
|
|
1951
|
-
trigger();
|
|
1952
|
-
});
|
|
1953
|
-
return watcher;
|
|
1954
|
-
}
|
|
1955
1979
|
//#endregion
|
|
1956
1980
|
//#region src/utils/project.ts
|
|
1957
1981
|
async function loadDictionaries(root, config) {
|
|
@@ -2006,6 +2030,6 @@ async function loadStaticProject(root, configFile) {
|
|
|
2006
2030
|
/** `home` + `["nav", "about"]` -> `home.nav.about` */
|
|
2007
2031
|
const toKey = (dictionary, keyPath) => [dictionary, ...keyPath].join(".");
|
|
2008
2032
|
//#endregion
|
|
2009
|
-
export {
|
|
2033
|
+
export { scanFiles as A, listLeaves as B, lockPath as C, BUILTIN_IGNORE as D, writeLock as E, locationOf as F, saveDictionaryFile as G, loadDictionaryFile as H, copyLeaf as I, TODO_PREFIX as K, setString as L, createAnalysisCache as M, dictionaryLocation as N, SOURCE_BUILTIN_IGNORE as O, getLeafSource as P, getString as R, lockFor as S, record as T, parseDictionaryText as U, listLocales as V, renderDictionaryFile as W, scanReferences as _, generateDictionaries as a, hashSource as b, DEFAULT_STATIC_CONFIG as c, resolveDialexConfig as d, logger as f, DEFAULT_SOURCE_GLOB as g, relativeFile as h, runGenerate as i, clearGitignore as j, createGitignoreFilter as k, parseStaticConfig as l, analyzeProjectSync as m, loadStaticProject as n, startGenerateWatcher as o, analyzeProject as p, toKey as r, renderGenerated as s, loadProject as t, readStaticConfig as u, scanText as v, readLock as w, isStale as x, LOCK_FILE as y, hasPath as z };
|
|
2010
2034
|
|
|
2011
|
-
//# sourceMappingURL=project-
|
|
2035
|
+
//# sourceMappingURL=project-BTpZGNjh.mjs.map
|