@lingui/cli 5.4.1 → 5.5.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.
Files changed (41) hide show
  1. package/dist/api/ProgramExit.d.ts +3 -0
  2. package/dist/api/ProgramExit.js +10 -0
  3. package/dist/api/catalog/extractFromFiles.d.ts +4 -1
  4. package/dist/api/catalog/extractFromFiles.js +56 -29
  5. package/dist/api/catalog/getCatalogs.d.ts +1 -1
  6. package/dist/api/catalog/getCatalogs.js +6 -9
  7. package/dist/api/catalog/getTranslationsForCatalog.d.ts +4 -2
  8. package/dist/api/catalog/getTranslationsForCatalog.js +18 -6
  9. package/dist/api/catalog.d.ts +16 -9
  10. package/dist/api/catalog.js +29 -29
  11. package/dist/api/compile/compileLocale.d.ts +5 -0
  12. package/dist/api/compile/compileLocale.js +84 -0
  13. package/dist/api/extractWorkerPool.d.ts +1 -0
  14. package/dist/api/extractWorkerPool.js +8 -0
  15. package/dist/api/extractors/babel.js +1 -0
  16. package/dist/api/extractors/index.d.ts +2 -6
  17. package/dist/api/extractors/index.js +2 -2
  18. package/dist/api/logger.d.ts +3 -0
  19. package/dist/api/logger.js +2 -0
  20. package/dist/api/resolveWorkersOptions.d.ts +6 -0
  21. package/dist/api/resolveWorkersOptions.js +21 -0
  22. package/dist/api/stats.js +9 -1
  23. package/dist/api/workerLogger.d.ts +9 -0
  24. package/dist/api/workerLogger.js +19 -0
  25. package/dist/extract-experimental/extractFromBundleAndWrite.d.ts +19 -0
  26. package/dist/extract-experimental/extractFromBundleAndWrite.js +63 -0
  27. package/dist/extract-experimental/workers/extractWorker.d.ts +6 -0
  28. package/dist/extract-experimental/workers/extractWorker.js +32 -0
  29. package/dist/lingui-compile.d.ts +2 -0
  30. package/dist/lingui-compile.js +48 -84
  31. package/dist/lingui-extract-experimental.d.ts +4 -2
  32. package/dist/lingui-extract-experimental.js +48 -46
  33. package/dist/lingui-extract-template.d.ts +2 -0
  34. package/dist/lingui-extract-template.js +23 -6
  35. package/dist/lingui-extract.d.ts +2 -0
  36. package/dist/lingui-extract.js +32 -8
  37. package/dist/workers/compileWorker.d.ts +11 -0
  38. package/dist/workers/compileWorker.js +39 -0
  39. package/dist/workers/extractWorker.d.ts +7 -0
  40. package/dist/workers/extractWorker.js +24 -0
  41. package/package.json +14 -10
@@ -0,0 +1,3 @@
1
+ export declare class ProgramExit extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProgramExit = void 0;
4
+ class ProgramExit extends Error {
5
+ constructor() {
6
+ super();
7
+ this.name = "ProgramExit";
8
+ }
9
+ }
10
+ exports.ProgramExit = ProgramExit;
@@ -1,3 +1,6 @@
1
- import type { LinguiConfigNormalized } from "@lingui/conf";
1
+ import type { ExtractedMessage, LinguiConfigNormalized } from "@lingui/conf";
2
2
  import { ExtractedCatalogType } from "../types";
3
+ import { ExtractWorkerPool } from "../extractWorkerPool";
3
4
  export declare function extractFromFiles(paths: string[], config: LinguiConfigNormalized): Promise<ExtractedCatalogType>;
5
+ export declare function mergeExtractedMessage(next: ExtractedMessage, messages: ExtractedCatalogType, config: LinguiConfigNormalized): void;
6
+ export declare function extractFromFilesWithWorkerPool(workerPool: ExtractWorkerPool, paths: string[], config: LinguiConfigNormalized): Promise<ExtractedCatalogType>;
@@ -4,12 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.extractFromFiles = extractFromFiles;
7
+ exports.mergeExtractedMessage = mergeExtractedMessage;
8
+ exports.extractFromFilesWithWorkerPool = extractFromFilesWithWorkerPool;
7
9
  const picocolors_1 = __importDefault(require("picocolors"));
8
10
  const path_1 = __importDefault(require("path"));
9
11
  const extractors_1 = __importDefault(require("../extractors"));
10
12
  const utils_1 = require("../utils");
11
13
  function mergePlaceholders(prev, next) {
12
14
  const res = Object.assign({}, prev);
15
+ // Handle case where next is null or undefined
16
+ if (!next)
17
+ return res;
13
18
  Object.entries(next).forEach(([key, value]) => {
14
19
  if (!res[key]) {
15
20
  res[key] = [];
@@ -24,37 +29,59 @@ function mergePlaceholders(prev, next) {
24
29
  async function extractFromFiles(paths, config) {
25
30
  const messages = {};
26
31
  let catalogSuccess = true;
27
- await Promise.all(paths.map(async (filename) => {
32
+ for (const filename of paths) {
28
33
  const fileSuccess = await (0, extractors_1.default)(filename, (next) => {
29
- var _a;
30
- if (!messages[next.id]) {
31
- messages[next.id] = {
32
- message: next.message,
33
- context: next.context,
34
- placeholders: {},
35
- comments: [],
36
- origin: [],
37
- };
38
- }
39
- const prev = messages[next.id];
40
- // there might be a case when filename was not mapped from sourcemaps
41
- const filename = next.origin[0]
42
- ? path_1.default.relative(config.rootDir, next.origin[0]).replace(/\\/g, "/")
43
- : "";
44
- const origin = [filename, next.origin[1]];
45
- if (prev.message && next.message && prev.message !== next.message) {
46
- throw new Error(`Encountered different default translations for message ${picocolors_1.default.yellow(next.id)}` +
47
- `\n${picocolors_1.default.yellow((0, utils_1.prettyOrigin)(prev.origin))} ${prev.message}` +
48
- `\n${picocolors_1.default.yellow((0, utils_1.prettyOrigin)([origin]))} ${next.message}`);
49
- }
50
- messages[next.id] = Object.assign(Object.assign({}, prev), { message: (_a = prev.message) !== null && _a !== void 0 ? _a : next.message, comments: next.comment
51
- ? [...prev.comments, next.comment].sort()
52
- : prev.comments, origin: [...prev.origin, [filename, next.origin[1]]].sort((a, b) => a[0].localeCompare(b[0])), placeholders: mergePlaceholders(prev.placeholders, next.placeholders) });
53
- }, config, {
54
- extractors: config.extractors,
55
- });
34
+ mergeExtractedMessage(next, messages, config);
35
+ }, config);
56
36
  catalogSuccess && (catalogSuccess = fileSuccess);
57
- }));
37
+ }
38
+ if (!catalogSuccess)
39
+ return undefined;
40
+ return messages;
41
+ }
42
+ function mergeExtractedMessage(next, messages, config) {
43
+ var _a;
44
+ if (!messages[next.id]) {
45
+ messages[next.id] = {
46
+ message: next.message,
47
+ context: next.context,
48
+ placeholders: {},
49
+ comments: [],
50
+ origin: [],
51
+ };
52
+ }
53
+ const prev = messages[next.id];
54
+ // there might be a case when filename was not mapped from sourcemaps
55
+ const filename = next.origin[0]
56
+ ? path_1.default.relative(config.rootDir, next.origin[0]).replace(/\\/g, "/")
57
+ : "";
58
+ const origin = [filename, next.origin[1]];
59
+ if (prev.message && next.message && prev.message !== next.message) {
60
+ throw new Error(`Encountered different default translations for message ${picocolors_1.default.yellow(next.id)}` +
61
+ `\n${picocolors_1.default.yellow((0, utils_1.prettyOrigin)(prev.origin))} ${prev.message}` +
62
+ `\n${picocolors_1.default.yellow((0, utils_1.prettyOrigin)([origin]))} ${next.message}`);
63
+ }
64
+ messages[next.id] = Object.assign(Object.assign({}, prev), { message: (_a = prev.message) !== null && _a !== void 0 ? _a : next.message, comments: next.comment
65
+ ? [...prev.comments, next.comment].sort()
66
+ : prev.comments, origin: [...prev.origin, [filename, next.origin[1]]].sort((a, b) => a[0].localeCompare(b[0])), placeholders: mergePlaceholders(prev.placeholders, next.placeholders) });
67
+ }
68
+ async function extractFromFilesWithWorkerPool(workerPool, paths, config) {
69
+ const messages = {};
70
+ let catalogSuccess = true;
71
+ if (!config.resolvedConfigPath) {
72
+ throw new Error("Multithreading is only supported when lingui config loaded from file system, not passed by API");
73
+ }
74
+ await Promise.all(paths.map((filename) => workerPool.queue(async (worker) => {
75
+ const result = await worker(filename, config.resolvedConfigPath);
76
+ if (!result.success) {
77
+ catalogSuccess = false;
78
+ }
79
+ else {
80
+ result.messages.forEach((message) => {
81
+ mergeExtractedMessage(message, messages, config);
82
+ });
83
+ }
84
+ })));
58
85
  if (!catalogSuccess)
59
86
  return undefined;
60
87
  return messages;
@@ -7,7 +7,7 @@ export declare function getCatalogs(config: LinguiConfigNormalized): Promise<Cat
7
7
  /**
8
8
  * Create catalog for merged messages.
9
9
  */
10
- export declare function getCatalogForMerge(config: LinguiConfigNormalized): Promise<Catalog>;
10
+ export declare function getMergedCatalogPath(config: LinguiConfigNormalized): Promise<string>;
11
11
  export declare function getCatalogForFile(file: string, catalogs: Catalog[]): {
12
12
  locale: string;
13
13
  catalog: Catalog;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getCatalogs = getCatalogs;
7
- exports.getCatalogForMerge = getCatalogForMerge;
7
+ exports.getMergedCatalogPath = getMergedCatalogPath;
8
8
  exports.getCatalogForFile = getCatalogForFile;
9
9
  const glob_1 = require("glob");
10
10
  const path_1 = __importDefault(require("path"));
@@ -78,16 +78,10 @@ const ensureArray = (value) => {
78
78
  /**
79
79
  * Create catalog for merged messages.
80
80
  */
81
- async function getCatalogForMerge(config) {
81
+ async function getMergedCatalogPath(config) {
82
82
  const format = await (0, formats_1.getFormat)(config.format, config.formatOptions, config.sourceLocale);
83
83
  validateCatalogPath(config.catalogsMergePath, format.getCatalogExtension());
84
- return new catalog_1.Catalog({
85
- name: getCatalogName(config.catalogsMergePath),
86
- path: (0, utils_1.normalizeRelativePath)(config.catalogsMergePath),
87
- include: [],
88
- exclude: [],
89
- format,
90
- }, config);
84
+ return (0, utils_1.normalizeRelativePath)(config.catalogsMergePath);
91
85
  }
92
86
  function getCatalogForFile(file, catalogs) {
93
87
  for (const catalog of catalogs) {
@@ -108,6 +102,9 @@ function getCatalogForFile(file, catalogs) {
108
102
  * Validate that `catalogPath` doesn't end with trailing slash
109
103
  */
110
104
  function validateCatalogPath(path, extension) {
105
+ if (!path.includes(LOCALE_PH)) {
106
+ throw new Error(`Invalid catalog path: ${LOCALE_PH} variable is missing`);
107
+ }
111
108
  if (!path.endsWith(utils_1.PATHSEP)) {
112
109
  return;
113
110
  }
@@ -7,8 +7,10 @@ export type TranslationMissingEvent = {
7
7
  export type GetTranslationsOptions = {
8
8
  sourceLocale: string;
9
9
  fallbackLocales: FallbackLocales;
10
- onMissing?: (message: TranslationMissingEvent) => void;
11
10
  };
12
11
  export declare function getTranslationsForCatalog(catalog: Catalog, locale: string, options: GetTranslationsOptions): Promise<{
13
- [id: string]: string;
12
+ missing: TranslationMissingEvent[];
13
+ messages: {
14
+ [id: string]: string;
15
+ };
14
16
  }>;
@@ -3,16 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getTranslationsForCatalog = getTranslationsForCatalog;
4
4
  const getFallbackListForLocale_1 = require("./getFallbackListForLocale");
5
5
  async function getTranslationsForCatalog(catalog, locale, options) {
6
+ const locales = new Set([
7
+ locale,
8
+ options.sourceLocale,
9
+ ...(0, getFallbackListForLocale_1.getFallbackListForLocale)(options.fallbackLocales, locale),
10
+ ]);
6
11
  const [catalogs, template] = await Promise.all([
7
- catalog.readAll(),
12
+ catalog.readAll(Array.from(locales)),
8
13
  catalog.readTemplate(),
9
14
  ]);
10
15
  const sourceLocaleCatalog = catalogs[options.sourceLocale] || {};
11
16
  const input = Object.assign(Object.assign(Object.assign({}, template), sourceLocaleCatalog), catalogs[locale]);
12
- return Object.keys(input).reduce((acc, key) => {
13
- acc[key] = getTranslation(catalogs, input[key], locale, key, options);
17
+ const missing = [];
18
+ const messages = Object.keys(input).reduce((acc, key) => {
19
+ acc[key] = getTranslation(catalogs, input[key], locale, key, (event) => {
20
+ missing.push(event);
21
+ }, options);
14
22
  return acc;
15
23
  }, {});
24
+ return {
25
+ missing,
26
+ messages,
27
+ };
16
28
  }
17
29
  function sourceLocaleFallback(catalog, key) {
18
30
  if (!(catalog === null || catalog === void 0 ? void 0 : catalog[key])) {
@@ -20,8 +32,8 @@ function sourceLocaleFallback(catalog, key) {
20
32
  }
21
33
  return catalog[key].translation || catalog[key].message;
22
34
  }
23
- function getTranslation(catalogs, msg, locale, key, options) {
24
- const { fallbackLocales, sourceLocale, onMissing } = options;
35
+ function getTranslation(catalogs, msg, locale, key, onMissing, options) {
36
+ const { fallbackLocales, sourceLocale } = options;
25
37
  const getTranslation = (_locale) => {
26
38
  var _a;
27
39
  const localeCatalog = catalogs[_locale];
@@ -52,7 +64,7 @@ function getTranslation(catalogs, msg, locale, key, options) {
52
64
  sourceLocale === locale &&
53
65
  sourceLocaleFallback(catalogs[sourceLocale], key));
54
66
  if (!translation) {
55
- onMissing === null || onMissing === void 0 ? void 0 : onMissing({
67
+ onMissing({
56
68
  id: key,
57
69
  source: msg.message || sourceLocaleFallback(catalogs[sourceLocale], key),
58
70
  });
@@ -1,15 +1,21 @@
1
1
  import { LinguiConfigNormalized, OrderBy } from "@lingui/conf";
2
2
  import { FormatterWrapper } from "./formats";
3
- import { CliExtractOptions } from "../lingui-extract";
4
- import { CliExtractTemplateOptions } from "../lingui-extract-template";
5
3
  import { CompiledCatalogNamespace } from "./compile";
6
- import { GetTranslationsOptions, TranslationMissingEvent } from "./catalog/getTranslationsForCatalog";
4
+ import { GetTranslationsOptions } from "./catalog/getTranslationsForCatalog";
7
5
  import { AllCatalogsType, CatalogType, ExtractedCatalogType } from "./types";
8
- export type MakeOptions = CliExtractOptions & {
6
+ import { ExtractWorkerPool } from "./extractWorkerPool";
7
+ export type MakeOptions = {
8
+ files?: string[];
9
+ clean: boolean;
10
+ overwrite: boolean;
11
+ locale: string[];
9
12
  orderBy?: OrderBy;
13
+ workerPool?: ExtractWorkerPool;
10
14
  };
11
- export type MakeTemplateOptions = CliExtractTemplateOptions & {
15
+ export type MakeTemplateOptions = {
16
+ files?: string[];
12
17
  orderBy?: OrderBy;
18
+ workerPool?: ExtractWorkerPool;
13
19
  };
14
20
  export type MergeOptions = {
15
21
  overwrite?: boolean;
@@ -40,21 +46,21 @@ export declare class Catalog {
40
46
  */
41
47
  collect(options?: {
42
48
  files?: string[];
49
+ workerPool?: ExtractWorkerPool;
43
50
  }): Promise<ExtractedCatalogType | undefined>;
44
51
  merge(prevCatalogs: AllCatalogsType, nextCatalog: ExtractedCatalogType, options: MergeOptions): {
45
52
  [k: string]: CatalogType;
46
53
  };
47
- getTranslations(locale: string, options: Omit<GetTranslationsOptions, "onMissing">): Promise<{
48
- missing: TranslationMissingEvent[];
54
+ getTranslations(locale: string, options: GetTranslationsOptions): Promise<{
55
+ missing: import("./catalog/getTranslationsForCatalog").TranslationMissingEvent[];
49
56
  messages: {
50
57
  [id: string]: string;
51
58
  };
52
59
  }>;
53
60
  write(locale: string, messages: CatalogType): Promise<[created: boolean, filename: string]>;
54
61
  writeTemplate(messages: CatalogType): Promise<void>;
55
- writeCompiled(locale: string, compiledCatalog: string, namespace?: CompiledCatalogNamespace): Promise<string>;
56
62
  read(locale: string): Promise<CatalogType>;
57
- readAll(): Promise<AllCatalogsType>;
63
+ readAll(locales?: string[]): Promise<AllCatalogsType>;
58
64
  readTemplate(): Promise<CatalogType>;
59
65
  get sourcePaths(): string[];
60
66
  get localeDir(): string;
@@ -62,4 +68,5 @@ export declare class Catalog {
62
68
  }
63
69
  export declare function cleanObsolete<T extends ExtractedCatalogType>(messages: T): T;
64
70
  export declare function order<T extends ExtractedCatalogType>(by: OrderBy, catalog: T): T;
71
+ export declare function writeCompiled(path: string, locale: string, compiledCatalog: string, namespace?: CompiledCatalogNamespace): Promise<string>;
65
72
  export declare function orderByMessage<T extends ExtractedCatalogType>(messages: T): T;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Catalog = void 0;
7
7
  exports.cleanObsolete = cleanObsolete;
8
8
  exports.order = order;
9
+ exports.writeCompiled = writeCompiled;
9
10
  exports.orderByMessage = orderByMessage;
10
11
  const fs_1 = __importDefault(require("fs"));
11
12
  const path_1 = __importDefault(require("path"));
@@ -35,7 +36,7 @@ class Catalog {
35
36
  }
36
37
  async make(options) {
37
38
  const [nextCatalog, prevCatalogs] = await Promise.all([
38
- this.collect({ files: options.files }),
39
+ this.collect({ files: options.files, workerPool: options.workerPool }),
39
40
  this.readAll(),
40
41
  ]);
41
42
  if (!nextCatalog)
@@ -57,7 +58,10 @@ class Catalog {
57
58
  return sortedCatalogs;
58
59
  }
59
60
  async makeTemplate(options) {
60
- const catalog = await this.collect({ files: options.files });
61
+ const catalog = await this.collect({
62
+ files: options.files,
63
+ workerPool: options.workerPool,
64
+ });
61
65
  if (!catalog)
62
66
  return false;
63
67
  const sorted = order(options.orderBy, catalog);
@@ -74,6 +78,9 @@ class Catalog {
74
78
  const regex = new RegExp(options.files.join("|"), "i");
75
79
  paths = paths.filter((path) => regex.test((0, normalize_path_1.default)(path)));
76
80
  }
81
+ if (options.workerPool) {
82
+ return await (0, extractFromFiles_1.extractFromFilesWithWorkerPool)(options.workerPool, paths, this.config);
83
+ }
77
84
  return await (0, extractFromFiles_1.extractFromFiles)(paths, this.config);
78
85
  }
79
86
  /*
@@ -103,14 +110,7 @@ class Catalog {
103
110
  ]));
104
111
  }
105
112
  async getTranslations(locale, options) {
106
- const missing = [];
107
- const messages = await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(this, locale, Object.assign(Object.assign({}, options), { onMissing: (event) => {
108
- missing.push(event);
109
- } }));
110
- return {
111
- missing,
112
- messages,
113
- };
113
+ return await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(this, locale, options);
114
114
  }
115
115
  async write(locale, messages) {
116
116
  const filename = this.getFilename(locale);
@@ -122,29 +122,12 @@ class Catalog {
122
122
  const filename = this.templateFile;
123
123
  await this.format.write(filename, messages, undefined);
124
124
  }
125
- async writeCompiled(locale, compiledCatalog, namespace) {
126
- let ext;
127
- switch (namespace) {
128
- case "es":
129
- ext = "mjs";
130
- break;
131
- case "ts":
132
- case "json":
133
- ext = namespace;
134
- break;
135
- default:
136
- ext = "js";
137
- }
138
- const filename = `${(0, utils_1.replacePlaceholders)(this.path, { locale })}.${ext}`;
139
- await (0, utils_1.writeFile)(filename, compiledCatalog);
140
- return filename;
141
- }
142
125
  async read(locale) {
143
126
  return await this.format.read(this.getFilename(locale), locale);
144
127
  }
145
- async readAll() {
128
+ async readAll(locales = this.locales) {
146
129
  const res = {};
147
- await Promise.all(this.locales.map(async (locale) => (res[locale] = await this.read(locale))));
130
+ await Promise.all(locales.map(async (locale) => (res[locale] = await this.read(locale))));
148
131
  // statement above will save locales in object in undetermined order
149
132
  // resort here to have keys order the same as in locales definition
150
133
  return this.locales.reduce((acc, locale) => {
@@ -240,6 +223,23 @@ function orderByOrigin(messages) {
240
223
  return acc;
241
224
  }, {});
242
225
  }
226
+ async function writeCompiled(path, locale, compiledCatalog, namespace) {
227
+ let ext;
228
+ switch (namespace) {
229
+ case "es":
230
+ ext = "mjs";
231
+ break;
232
+ case "ts":
233
+ case "json":
234
+ ext = namespace;
235
+ break;
236
+ default:
237
+ ext = "js";
238
+ }
239
+ const filename = `${(0, utils_1.replacePlaceholders)(path, { locale })}.${ext}`;
240
+ await (0, utils_1.writeFile)(filename, compiledCatalog);
241
+ return filename;
242
+ }
243
243
  function orderByMessage(messages) {
244
244
  // hardcoded en-US locale to have consistent sorting
245
245
  // @see https://github.com/lingui/js-lingui/pull/1808
@@ -0,0 +1,5 @@
1
+ import { Catalog } from "../catalog";
2
+ import { LinguiConfigNormalized } from "@lingui/conf";
3
+ import { CliCompileOptions } from "../../lingui-compile";
4
+ import { Logger } from "../logger";
5
+ export declare function compileLocale(catalogs: Catalog[], locale: string, options: CliCompileOptions, config: LinguiConfigNormalized, doMerge: boolean, logger: Logger): Promise<void>;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.compileLocale = compileLocale;
7
+ const catalog_1 = require("../catalog");
8
+ const picocolors_1 = __importDefault(require("picocolors"));
9
+ const getCatalogs_1 = require("../catalog/getCatalogs");
10
+ const ProgramExit_1 = require("../ProgramExit");
11
+ const compile_1 = require("../compile");
12
+ const normalize_path_1 = __importDefault(require("normalize-path"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const messages_1 = require("../messages");
15
+ const getTranslationsForCatalog_1 = require("../catalog/getTranslationsForCatalog");
16
+ async function compileLocale(catalogs, locale, options, config, doMerge, logger) {
17
+ let mergedCatalogs = {};
18
+ for (const catalog of catalogs) {
19
+ const { messages, missing: missingMessages } = await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(catalog, locale, {
20
+ fallbackLocales: config.fallbackLocales,
21
+ sourceLocale: config.sourceLocale,
22
+ });
23
+ if (!options.allowEmpty &&
24
+ locale !== config.pseudoLocale &&
25
+ missingMessages.length > 0) {
26
+ logger.error(picocolors_1.default.red(`Error: Failed to compile catalog for locale ${picocolors_1.default.bold(locale)}!`));
27
+ if (options.verbose) {
28
+ logger.error(picocolors_1.default.red("Missing translations:"));
29
+ missingMessages.forEach((missing) => {
30
+ const source = missing.source || missing.source === missing.id
31
+ ? `: (${missing.source})`
32
+ : "";
33
+ logger.error(`${missing.id}${source}`);
34
+ });
35
+ }
36
+ else {
37
+ logger.error(picocolors_1.default.red(`Missing ${missingMessages.length} translation(s)`));
38
+ }
39
+ logger.error();
40
+ throw new ProgramExit_1.ProgramExit();
41
+ }
42
+ if (doMerge) {
43
+ mergedCatalogs = Object.assign(Object.assign({}, mergedCatalogs), messages);
44
+ }
45
+ else {
46
+ if (!(await compileAndWrite(locale, config, options, catalog.path, messages, logger))) {
47
+ throw new ProgramExit_1.ProgramExit();
48
+ }
49
+ }
50
+ }
51
+ if (doMerge) {
52
+ const result = await compileAndWrite(locale, config, options, await (0, getCatalogs_1.getMergedCatalogPath)(config), mergedCatalogs, logger);
53
+ if (!result) {
54
+ throw new ProgramExit_1.ProgramExit();
55
+ }
56
+ }
57
+ }
58
+ async function compileAndWrite(locale, config, options, writePath, messages, logger) {
59
+ const namespace = options.typescript
60
+ ? "ts"
61
+ : options.namespace || config.compileNamespace;
62
+ const { source: compiledCatalog, errors } = (0, compile_1.createCompiledCatalog)(locale, messages, {
63
+ strict: false,
64
+ namespace,
65
+ pseudoLocale: config.pseudoLocale,
66
+ compilerBabelOptions: config.compilerBabelOptions,
67
+ });
68
+ if (errors.length) {
69
+ let message = (0, messages_1.createCompilationErrorMessage)(locale, errors);
70
+ if (options.failOnCompileError) {
71
+ message += `These errors fail command execution because \`--strict\` option passed`;
72
+ logger.error(picocolors_1.default.red(message));
73
+ return false;
74
+ }
75
+ else {
76
+ message += `You can fail command execution on these errors by passing \`--strict\` option`;
77
+ logger.error(picocolors_1.default.red(message));
78
+ }
79
+ }
80
+ let compiledPath = await (0, catalog_1.writeCompiled)(writePath, locale, compiledCatalog, namespace);
81
+ compiledPath = (0, normalize_path_1.default)(path_1.default.relative(config.rootDir, compiledPath));
82
+ options.verbose && logger.error(picocolors_1.default.green(`${locale} ⇒ ${compiledPath}`));
83
+ return true;
84
+ }
@@ -0,0 +1 @@
1
+ export type ExtractWorkerPool = ReturnType<typeof createExtractWorkerPool>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createExtractWorkerPool = createExtractWorkerPool;
4
+ const threads_1 = require("threads");
5
+ /** @internal */
6
+ function createExtractWorkerPool(opts) {
7
+ return (0, threads_1.Pool)(() => (0, threads_1.spawn)(new threads_1.Worker("../workers/extractWorker")), { size: opts.poolSize });
8
+ }
@@ -120,6 +120,7 @@ function getBabelParserOptions(filename, parserOptions) {
120
120
  "importAttributes", // stage3
121
121
  "explicitResourceManagement", // stage3,
122
122
  "decoratorAutoAccessors", // stage3,
123
+ "deferredImportEvaluation", // stage3
123
124
  ];
124
125
  if ([/\.ts$/, /\.mts$/, /\.cts$/, /\.tsx$/].some((r) => filename.match(r))) {
125
126
  parserPlugins.push("typescript");
@@ -1,6 +1,2 @@
1
- import { ExtractedMessage, ExtractorType, LinguiConfigNormalized } from "@lingui/conf";
2
- type ExtractOptions = {
3
- extractors?: ExtractorType[];
4
- };
5
- export default function extract(filename: string, onMessageExtracted: (msg: ExtractedMessage) => void, linguiConfig: LinguiConfigNormalized, options: ExtractOptions): Promise<boolean>;
6
- export {};
1
+ import { ExtractedMessage, LinguiConfigNormalized } from "@lingui/conf";
2
+ export default function extract(filename: string, onMessageExtracted: (msg: ExtractedMessage) => void, linguiConfig: LinguiConfigNormalized): Promise<boolean>;
@@ -7,9 +7,9 @@ exports.default = extract;
7
7
  const promises_1 = __importDefault(require("fs/promises"));
8
8
  const babel_1 = __importDefault(require("./babel"));
9
9
  const DEFAULT_EXTRACTORS = [babel_1.default];
10
- async function extract(filename, onMessageExtracted, linguiConfig, options) {
10
+ async function extract(filename, onMessageExtracted, linguiConfig) {
11
11
  var _a;
12
- const extractorsToExtract = (_a = options.extractors) !== null && _a !== void 0 ? _a : DEFAULT_EXTRACTORS;
12
+ const extractorsToExtract = (_a = linguiConfig.extractors) !== null && _a !== void 0 ? _a : DEFAULT_EXTRACTORS;
13
13
  for (const ext of extractorsToExtract) {
14
14
  if (!ext.match(filename))
15
15
  continue;
@@ -0,0 +1,3 @@
1
+ export type Logger = {
2
+ error: (msg?: string) => void;
3
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export type WorkersOptions = {
2
+ poolSize: number;
3
+ };
4
+ export declare function resolveWorkersOptions(opts: {
5
+ workers?: number | string;
6
+ }): WorkersOptions;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveWorkersOptions = resolveWorkersOptions;
7
+ const node_os_1 = __importDefault(require("node:os"));
8
+ function resolveWorkersOptions(opts) {
9
+ const cores = node_os_1.default.availableParallelism();
10
+ if (Number(opts.workers) <= 1 || cores === 1) {
11
+ return { poolSize: 0 };
12
+ }
13
+ if (!opts.workers) {
14
+ if (cores <= 2) {
15
+ return { poolSize: cores }; // on tiny machines, use all
16
+ }
17
+ // on big machines cap to 8, to avoid trashing
18
+ return { poolSize: Math.min(cores - 1, 8) };
19
+ }
20
+ return { poolSize: Number(opts.workers) };
21
+ }
package/dist/api/stats.js CHANGED
@@ -25,7 +25,15 @@ function printStats(config, catalogs) {
25
25
  compact: true,
26
26
  },
27
27
  });
28
- Object.keys(catalogs).forEach((locale) => {
28
+ Object.keys(catalogs)
29
+ .sort((a, b) => {
30
+ if (a === config.sourceLocale && b !== config.sourceLocale)
31
+ return -1;
32
+ if (b === config.sourceLocale && a !== config.sourceLocale)
33
+ return 1;
34
+ return a.localeCompare(b);
35
+ })
36
+ .forEach((locale) => {
29
37
  if (locale === config.pseudoLocale)
30
38
  return; // skip pseudo locale
31
39
  const catalog = catalogs[locale];
@@ -0,0 +1,9 @@
1
+ import { Logger } from "./logger";
2
+ export type SerializedLogs = {
3
+ errors: string;
4
+ };
5
+ export declare class WorkerLogger implements Logger {
6
+ private errors;
7
+ error(msg?: string): void;
8
+ flush(): SerializedLogs;
9
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkerLogger = void 0;
4
+ class WorkerLogger {
5
+ constructor() {
6
+ this.errors = [];
7
+ }
8
+ error(msg) {
9
+ this.errors.push(msg);
10
+ }
11
+ flush() {
12
+ const errors = this.errors.join("\n");
13
+ this.errors = [];
14
+ return {
15
+ errors,
16
+ };
17
+ }
18
+ }
19
+ exports.WorkerLogger = WorkerLogger;