@lingui/cli 5.2.0 → 5.3.1

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.
@@ -3,7 +3,7 @@ import { FormatterWrapper } from "./formats";
3
3
  import { CliExtractOptions } from "../lingui-extract";
4
4
  import { CliExtractTemplateOptions } from "../lingui-extract-template";
5
5
  import { CompiledCatalogNamespace } from "./compile";
6
- import { GetTranslationsOptions } from "./catalog/getTranslationsForCatalog";
6
+ import { GetTranslationsOptions, TranslationMissingEvent } from "./catalog/getTranslationsForCatalog";
7
7
  import { AllCatalogsType, CatalogType, ExtractedCatalogType } from "./types";
8
8
  export type MakeOptions = CliExtractOptions & {
9
9
  orderBy?: OrderBy;
@@ -44,8 +44,11 @@ export declare class Catalog {
44
44
  merge(prevCatalogs: AllCatalogsType, nextCatalog: ExtractedCatalogType, options: MergeOptions): {
45
45
  [k: string]: CatalogType;
46
46
  };
47
- getTranslations(locale: string, options: GetTranslationsOptions): Promise<{
48
- [id: string]: string;
47
+ getTranslations(locale: string, options: Omit<GetTranslationsOptions, "onMissing">): Promise<{
48
+ missing: TranslationMissingEvent[];
49
+ messages: {
50
+ [id: string]: string;
51
+ };
49
52
  }>;
50
53
  write(locale: string, messages: CatalogType): Promise<[created: boolean, filename: string]>;
51
54
  writeTemplate(messages: CatalogType): Promise<void>;
@@ -101,7 +101,14 @@ class Catalog {
101
101
  ]));
102
102
  }
103
103
  async getTranslations(locale, options) {
104
- return await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(this, locale, options);
104
+ const missing = [];
105
+ const messages = await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(this, locale, Object.assign(Object.assign({}, options), { onMissing: (event) => {
106
+ missing.push(event);
107
+ } }));
108
+ return {
109
+ missing,
110
+ messages,
111
+ };
105
112
  }
106
113
  async write(locale, messages) {
107
114
  const filename = this.getFilename(locale);
@@ -10,7 +10,24 @@ export type CreateCompileCatalogOptions = {
10
10
  pseudoLocale?: string;
11
11
  compilerBabelOptions?: GeneratorOptions;
12
12
  };
13
- export declare function createCompiledCatalog(locale: string, messages: CompiledCatalogType, options: CreateCompileCatalogOptions): string;
13
+ export type MessageCompilationError = {
14
+ /**
15
+ * ID of the message in the Catalog
16
+ */
17
+ id: string;
18
+ /**
19
+ * Message itself
20
+ */
21
+ source: string;
22
+ /**
23
+ * Error associated with this message
24
+ */
25
+ error: Error;
26
+ };
27
+ export declare function createCompiledCatalog(locale: string, messages: CompiledCatalogType, options: CreateCompileCatalogOptions): {
28
+ source: string;
29
+ errors: MessageCompilationError[];
30
+ };
14
31
  /**
15
32
  * Compile string message into AST tree. Message format is parsed/compiled into
16
33
  * JS arrays, which are handled in client.
@@ -45,14 +45,24 @@ const pseudoLocalize_1 = __importDefault(require("./pseudoLocalize"));
45
45
  function createCompiledCatalog(locale, messages, options) {
46
46
  const { strict = false, namespace = "cjs", pseudoLocale, compilerBabelOptions = {}, } = options;
47
47
  const shouldPseudolocalize = locale === pseudoLocale;
48
+ const errors = [];
48
49
  const compiledMessages = Object.keys(messages).reduce((obj, key) => {
49
50
  // Don't use `key` as a fallback translation in strict mode.
50
51
  const translation = (messages[key] || (!strict ? key : ""));
51
- obj[key] = compile(translation, shouldPseudolocalize);
52
+ try {
53
+ obj[key] = compile(translation, shouldPseudolocalize);
54
+ }
55
+ catch (e) {
56
+ errors.push({
57
+ id: key,
58
+ source: translation,
59
+ error: e,
60
+ });
61
+ }
52
62
  return obj;
53
63
  }, {});
54
64
  if (namespace === "json") {
55
- return JSON.stringify({ messages: compiledMessages });
65
+ return { source: JSON.stringify({ messages: compiledMessages }), errors };
56
66
  }
57
67
  const ast = buildExportStatement(
58
68
  //build JSON.parse(<compiledMessages>) statement
@@ -60,7 +70,7 @@ function createCompiledCatalog(locale, messages, options) {
60
70
  const code = (0, generator_1.default)(ast, Object.assign({ minified: true, jsescOption: {
61
71
  minimal: true,
62
72
  } }, compilerBabelOptions)).code;
63
- return "/*eslint-disable*/" + code;
73
+ return { source: "/*eslint-disable*/" + code, errors };
64
74
  }
65
75
  function buildExportStatement(expression, namespace) {
66
76
  if (namespace === "ts") {
@@ -105,5 +115,5 @@ function buildExportStatement(expression, namespace) {
105
115
  * JS arrays, which are handled in client.
106
116
  */
107
117
  function compile(message, shouldPseudolocalize = false) {
108
- return (0, compileMessage_1.compileMessage)(message, (value) => shouldPseudolocalize ? (0, pseudoLocalize_1.default)(value) : value);
118
+ return (0, compileMessage_1.compileMessageOrThrow)(message, (value) => shouldPseudolocalize ? (0, pseudoLocalize_1.default)(value) : value);
109
119
  }
@@ -3,4 +3,5 @@ export { getCatalogForFile, getCatalogs } from "./catalog/getCatalogs";
3
3
  export { createCompiledCatalog } from "./compile";
4
4
  export { default as extractor, extractFromFileWithBabel, } from "./extractors/babel";
5
5
  export { getCatalogDependentFiles } from "./catalog/getCatalogDependentFiles";
6
+ export { createMissingErrorMessage, createCompilationErrorMessage, } from "./messages";
6
7
  export * from "./types";
package/dist/api/index.js CHANGED
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.getCatalogDependentFiles = exports.extractFromFileWithBabel = exports.extractor = exports.createCompiledCatalog = exports.getCatalogs = exports.getCatalogForFile = exports.getFormat = void 0;
20
+ exports.createCompilationErrorMessage = exports.createMissingErrorMessage = exports.getCatalogDependentFiles = exports.extractFromFileWithBabel = exports.extractor = exports.createCompiledCatalog = exports.getCatalogs = exports.getCatalogForFile = exports.getFormat = void 0;
21
21
  var formats_1 = require("./formats");
22
22
  Object.defineProperty(exports, "getFormat", { enumerable: true, get: function () { return formats_1.getFormat; } });
23
23
  var getCatalogs_1 = require("./catalog/getCatalogs");
@@ -30,4 +30,7 @@ Object.defineProperty(exports, "extractor", { enumerable: true, get: function ()
30
30
  Object.defineProperty(exports, "extractFromFileWithBabel", { enumerable: true, get: function () { return babel_1.extractFromFileWithBabel; } });
31
31
  var getCatalogDependentFiles_1 = require("./catalog/getCatalogDependentFiles");
32
32
  Object.defineProperty(exports, "getCatalogDependentFiles", { enumerable: true, get: function () { return getCatalogDependentFiles_1.getCatalogDependentFiles; } });
33
+ var messages_1 = require("./messages");
34
+ Object.defineProperty(exports, "createMissingErrorMessage", { enumerable: true, get: function () { return messages_1.createMissingErrorMessage; } });
35
+ Object.defineProperty(exports, "createCompilationErrorMessage", { enumerable: true, get: function () { return messages_1.createCompilationErrorMessage; } });
33
36
  __exportStar(require("./types"), exports);
@@ -0,0 +1,4 @@
1
+ import { TranslationMissingEvent } from "./catalog/getTranslationsForCatalog";
2
+ import { MessageCompilationError } from "./compile";
3
+ export declare function createMissingErrorMessage(locale: string, missingMessages: TranslationMissingEvent[], configurationMsg: string): string;
4
+ export declare function createCompilationErrorMessage(locale: string, errors: MessageCompilationError[]): string;
@@ -0,0 +1,32 @@
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.createMissingErrorMessage = createMissingErrorMessage;
7
+ exports.createCompilationErrorMessage = createCompilationErrorMessage;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ function createMissingErrorMessage(locale, missingMessages, configurationMsg) {
10
+ let message = `Failed to compile catalog for locale ${chalk_1.default.bold(locale)}!
11
+
12
+ Missing ${missingMessages.length} translation(s):
13
+ \n`;
14
+ missingMessages.forEach((missing) => {
15
+ const source = missing.source || missing.source === missing.id
16
+ ? `: ${missing.source}`
17
+ : "";
18
+ message += `${missing.id}${source}\n`;
19
+ });
20
+ return message;
21
+ }
22
+ function createCompilationErrorMessage(locale, errors) {
23
+ let message = `Failed to compile catalog for locale ${chalk_1.default.bold(locale)}!
24
+
25
+ Compilation error for ${errors.length} translation(s):
26
+ \n`;
27
+ errors.forEach((error) => {
28
+ const source = error.source || error.source === error.id ? `: ${error.source}` : "";
29
+ message += `${error.id}${source}\nReason: ${error.error.message}\n\n`;
30
+ });
31
+ return message;
32
+ }
@@ -2,6 +2,7 @@ import { LinguiConfigNormalized } from "@lingui/conf";
2
2
  export type CliCompileOptions = {
3
3
  verbose?: boolean;
4
4
  allowEmpty?: boolean;
5
+ failOnCompileError?: boolean;
5
6
  typescript?: boolean;
6
7
  watch?: boolean;
7
8
  namespace?: string;
@@ -22,13 +22,9 @@ async function command(config, options) {
22
22
  console.log("Compiling message catalogs…");
23
23
  for (const locale of config.locales) {
24
24
  for (const catalog of catalogs) {
25
- const missingMessages = [];
26
- const messages = await catalog.getTranslations(locale, {
25
+ const { messages, missing: missingMessages } = await catalog.getTranslations(locale, {
27
26
  fallbackLocales: config.fallbackLocales,
28
27
  sourceLocale: config.sourceLocale,
29
- onMissing: (missing) => {
30
- missingMessages.push(missing);
31
- },
32
28
  });
33
29
  if (!options.allowEmpty &&
34
30
  locale !== config.pseudoLocale &&
@@ -53,35 +49,42 @@ async function command(config, options) {
53
49
  mergedCatalogs = Object.assign(Object.assign({}, mergedCatalogs), messages);
54
50
  }
55
51
  else {
56
- const namespace = options.typescript
57
- ? "ts"
58
- : options.namespace || config.compileNamespace;
59
- const compiledCatalog = (0, compile_1.createCompiledCatalog)(locale, messages, {
60
- strict: false,
61
- namespace,
62
- pseudoLocale: config.pseudoLocale,
63
- compilerBabelOptions: config.compilerBabelOptions,
64
- });
65
- let compiledPath = await catalog.writeCompiled(locale, compiledCatalog, namespace);
66
- compiledPath = (0, normalize_path_1.default)(path_1.default.relative(config.rootDir, compiledPath));
67
- options.verbose &&
68
- console.error(chalk_1.default.green(`${locale} ⇒ ${compiledPath}`));
52
+ if (!(await compileAndWrite(locale, config, options, catalog, messages))) {
53
+ return false;
54
+ }
69
55
  }
70
56
  }
71
57
  if (doMerge) {
72
- const compileCatalog = await (0, getCatalogs_1.getCatalogForMerge)(config);
73
- const namespace = options.namespace || config.compileNamespace;
74
- const compiledCatalog = (0, compile_1.createCompiledCatalog)(locale, mergedCatalogs, {
75
- strict: false,
76
- namespace: namespace,
77
- pseudoLocale: config.pseudoLocale,
78
- compilerBabelOptions: config.compilerBabelOptions,
79
- });
80
- let compiledPath = await compileCatalog.writeCompiled(locale, compiledCatalog, namespace);
81
- compiledPath = (0, normalize_path_1.default)(path_1.default.relative(config.rootDir, compiledPath));
82
- options.verbose && console.log(chalk_1.default.green(`${locale} ⇒ ${compiledPath}`));
58
+ return await compileAndWrite(locale, config, options, await (0, getCatalogs_1.getCatalogForMerge)(config), mergedCatalogs);
59
+ }
60
+ }
61
+ return true;
62
+ }
63
+ async function compileAndWrite(locale, config, options, catalogToWrite, messages) {
64
+ const namespace = options.typescript
65
+ ? "ts"
66
+ : options.namespace || config.compileNamespace;
67
+ const { source: compiledCatalog, errors } = (0, compile_1.createCompiledCatalog)(locale, messages, {
68
+ strict: false,
69
+ namespace,
70
+ pseudoLocale: config.pseudoLocale,
71
+ compilerBabelOptions: config.compilerBabelOptions,
72
+ });
73
+ if (errors.length) {
74
+ let message = (0, api_1.createCompilationErrorMessage)(locale, errors);
75
+ if (options.failOnCompileError) {
76
+ message += `These errors fail command execution because \`--strict\` option passed`;
77
+ console.error(chalk_1.default.red(message));
78
+ return false;
79
+ }
80
+ else {
81
+ message += `You can fail command execution on these errors by passing \`--strict\` option`;
82
+ console.error(chalk_1.default.red(message));
83
83
  }
84
84
  }
85
+ let compiledPath = await catalogToWrite.writeCompiled(locale, compiledCatalog, namespace);
86
+ compiledPath = (0, normalize_path_1.default)(path_1.default.relative(config.rootDir, compiledPath));
87
+ options.verbose && console.error(chalk_1.default.green(`${locale} ⇒ ${compiledPath}`));
85
88
  return true;
86
89
  }
87
90
  if (require.main === module) {
@@ -112,6 +115,7 @@ if (require.main === module) {
112
115
  previousRun = previousRun.then(() => command(config, {
113
116
  verbose: options.watch || options.verbose || false,
114
117
  allowEmpty: !options.strict,
118
+ failOnCompileError: !!options.strict,
115
119
  typescript: options.typescript || config.compileNamespace === "ts" || false,
116
120
  namespace: options.namespace, // we want this to be undefined if user does not specify so default can be used
117
121
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingui/cli",
3
- "version": "5.2.0",
3
+ "version": "5.3.1",
4
4
  "description": "CLI for working wit message catalogs",
5
5
  "keywords": [
6
6
  "cli",
@@ -57,12 +57,12 @@
57
57
  "@babel/parser": "^7.22.0",
58
58
  "@babel/runtime": "^7.21.0",
59
59
  "@babel/types": "^7.21.2",
60
- "@lingui/babel-plugin-extract-messages": "5.2.0",
61
- "@lingui/babel-plugin-lingui-macro": "5.2.0",
62
- "@lingui/conf": "5.2.0",
63
- "@lingui/core": "5.2.0",
64
- "@lingui/format-po": "5.2.0",
65
- "@lingui/message-utils": "5.2.0",
60
+ "@lingui/babel-plugin-extract-messages": "5.3.1",
61
+ "@lingui/babel-plugin-lingui-macro": "5.3.1",
62
+ "@lingui/conf": "5.3.1",
63
+ "@lingui/core": "5.3.1",
64
+ "@lingui/format-po": "5.3.1",
65
+ "@lingui/message-utils": "5.3.1",
66
66
  "babel-plugin-macros": "^3.0.1",
67
67
  "chalk": "^4.1.0",
68
68
  "chokidar": "3.5.1",
@@ -70,7 +70,7 @@
70
70
  "commander": "^10.0.0",
71
71
  "convert-source-map": "^2.0.0",
72
72
  "date-fns": "^3.6.0",
73
- "esbuild": "^0.21.5",
73
+ "esbuild": "^0.25.1",
74
74
  "glob": "^11.0.0",
75
75
  "inquirer": "^7.3.3",
76
76
  "micromatch": "^4.0.7",
@@ -90,5 +90,5 @@
90
90
  "mock-fs": "^5.2.0",
91
91
  "mockdate": "^3.0.5"
92
92
  },
93
- "gitHead": "9c50b4877ca8b134d0d96c09a8055221ca70b095"
93
+ "gitHead": "f2cccd1211178f34aa47f0548a0c5d8caff93f59"
94
94
  }