@lingui/cli 3.17.1 → 4.0.0-next.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.
@@ -4,50 +4,44 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.cleanObsolete = exports.Catalog = void 0;
7
- exports.getCatalogForFile = getCatalogForFile;
8
- exports.getCatalogForMerge = getCatalogForMerge;
9
- exports.getCatalogs = getCatalogs;
10
- exports.normalizeRelativePath = normalizeRelativePath;
11
7
  exports.order = order;
12
8
  exports.orderByMessageId = orderByMessageId;
13
9
  exports.orderByOrigin = orderByOrigin;
14
- var _os = _interopRequireDefault(require("os"));
15
- var _fsExtra = _interopRequireDefault(require("fs-extra"));
10
+ var _fs = _interopRequireDefault(require("fs"));
16
11
  var _path = _interopRequireDefault(require("path"));
17
12
  var R = _interopRequireWildcard(require("ramda"));
18
- var _chalk = _interopRequireDefault(require("chalk"));
19
13
  var _glob = _interopRequireDefault(require("glob"));
20
- var _micromatch = _interopRequireDefault(require("micromatch"));
21
14
  var _normalizePath = _interopRequireDefault(require("normalize-path"));
22
- var _formats = _interopRequireDefault(require("./formats"));
23
- var _extractors = _interopRequireDefault(require("./extractors"));
15
+ var _formats = require("./formats");
16
+ var _getTranslationsForCatalog = require("./catalog/getTranslationsForCatalog");
17
+ var _mergeCatalog = require("./catalog/mergeCatalog");
18
+ var _extractFromFiles = require("./catalog/extractFromFiles");
24
19
  var _utils = require("./utils");
25
20
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
26
21
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
27
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
- const NAME = "{name}";
29
- const NAME_REPLACE_RE = /{name}/g;
30
23
  const LOCALE = "{locale}";
31
- const LOCALE_REPLACE_RE = /{locale}/g;
32
24
  const LOCALE_SUFFIX_RE = /\{locale\}.*$/;
33
- const PATHSEP = "/"; // force posix everywhere
34
-
35
25
  class Catalog {
36
26
  constructor({
37
27
  name,
38
28
  path,
39
29
  include,
30
+ templatePath,
40
31
  exclude = []
41
32
  }, config) {
42
- this.name = name;
43
- this.path = normalizeRelativePath(path);
44
- this.include = include.map(normalizeRelativePath);
45
- this.exclude = [this.localeDir, ...exclude.map(normalizeRelativePath)];
46
33
  this.config = config;
47
- this.format = (0, _formats.default)(config.format);
34
+ this.name = name;
35
+ this.path = (0, _utils.normalizeRelativePath)(path);
36
+ this.include = include.map(_utils.normalizeRelativePath);
37
+ this.exclude = [this.localeDir, ...exclude.map(_utils.normalizeRelativePath)];
38
+ this.format = (0, _formats.getFormat)(config.format);
39
+ this.templateFile = templatePath || getTemplatePath(this.format, this.path);
48
40
  }
49
41
  async make(options) {
50
- const nextCatalog = await this.collect(options);
42
+ const nextCatalog = await this.collect({
43
+ files: options.files
44
+ });
51
45
  if (!nextCatalog) return false;
52
46
  const prevCatalogs = this.readAll();
53
47
  const catalogs = this.merge(prevCatalogs, nextCatalog, {
@@ -67,171 +61,64 @@ class Catalog {
67
61
  } else {
68
62
  this.writeAll(sortedCatalogs);
69
63
  }
70
- return true;
64
+ return sortedCatalogs;
71
65
  }
72
66
  async makeTemplate(options) {
73
- const catalog = await this.collect(options);
67
+ const catalog = await this.collect({
68
+ files: options.files
69
+ });
74
70
  if (!catalog) return false;
75
- const sort = order(options.orderBy);
76
- this.writeTemplate(sort(catalog));
77
- return true;
71
+ const sorted = order(options.orderBy)(catalog);
72
+ this.writeTemplate(sorted);
73
+ return sorted;
78
74
  }
79
75
 
80
76
  /**
81
77
  * Collect messages from source paths. Return a raw message catalog as JSON.
82
78
  */
83
- async collect(options) {
84
- const tmpDir = _path.default.join(_os.default.tmpdir(), `lingui-${process.pid}`);
85
- if (_fsExtra.default.existsSync(tmpDir)) {
86
- (0, _utils.removeDirectory)(tmpDir, true);
87
- } else {
88
- _fsExtra.default.mkdirSync(tmpDir);
79
+ async collect(options = {}) {
80
+ let paths = this.sourcePaths;
81
+ if (options.files) {
82
+ options.files = options.files.map(p => (0, _normalizePath.default)(p, false));
83
+ const regex = new RegExp(options.files.join("|"), "i");
84
+ paths = paths.filter(path => regex.test(path));
89
85
  }
90
- try {
91
- let paths = this.sourcePaths;
92
- if (options.files) {
93
- options.files = options.files.map(p => (0, _normalizePath.default)(p, false));
94
- const regex = new RegExp(options.files.join("|"), "i");
95
- paths = paths.filter(path => regex.test(path));
96
- }
97
- let catalogSuccess = true;
98
- for (let filename of paths) {
99
- const fileSuccess = await (0, _extractors.default)(filename, tmpDir, {
100
- verbose: options.verbose,
101
- configPath: options.configPath,
102
- babelOptions: this.config.extractBabelOptions,
103
- extractors: options.extractors,
104
- projectType: options.projectType
105
- });
106
- catalogSuccess && (catalogSuccess = fileSuccess);
107
- }
108
- if (!catalogSuccess) return undefined;
109
- return function traverse(directory) {
110
- return _fsExtra.default.readdirSync(directory).map(filename => {
111
- const filepath = _path.default.join(directory, filename);
112
- if (_fsExtra.default.lstatSync(filepath).isDirectory()) {
113
- return traverse(filepath);
114
- }
115
- if (!filename.endsWith(".json")) return;
116
- try {
117
- return JSON.parse(_fsExtra.default.readFileSync(filepath).toString());
118
- } catch (e) {}
119
- }).filter(Boolean).reduce((catalog, messages) => R.mergeWithKey(mergeOriginsAndExtractedComments, catalog, messages), {});
120
- }(tmpDir);
121
- } catch (e) {
122
- throw e;
123
- } finally {
124
- (0, _utils.removeDirectory)(tmpDir);
125
- }
126
- }
86
+ return await (0, _extractFromFiles.extractFromFiles)(paths, this.config);
87
+ }
88
+
89
+ /*
90
+ *
91
+ * prevCatalogs - map of message catalogs in all available languages with translations
92
+ * nextCatalog - language-agnostic catalog with collected messages
93
+ *
94
+ * Note: if a catalog in prevCatalogs is null it means the language is available, but
95
+ * no previous catalog was generated (usually first run).
96
+ *
97
+ * Orthogonal use-cases
98
+ * --------------------
99
+ *
100
+ * Message IDs:
101
+ * - auto-generated IDs: message is used as a key, `defaults` is not set
102
+ * - custom IDs: message is used as `defaults`, custom ID as a key
103
+ *
104
+ * Source locale (defined by `sourceLocale` in config):
105
+ * - catalog for `sourceLocale`: initially, `translation` is prefilled with `defaults`
106
+ * (for custom IDs) or `key` (for auto-generated IDs)
107
+ * - all other languages: translation is kept empty
108
+ */
127
109
  merge(prevCatalogs, nextCatalog, options) {
128
- const nextKeys = R.keys(nextCatalog).map(String);
129
110
  return R.mapObjIndexed((prevCatalog, locale) => {
130
- const prevKeys = R.keys(prevCatalog).map(String);
131
- const newKeys = R.difference(nextKeys, prevKeys);
132
- const mergeKeys = R.intersection(nextKeys, prevKeys);
133
- const obsoleteKeys = R.difference(prevKeys, nextKeys);
134
-
135
- // Initialize new catalog with new keys
136
- const newMessages = R.mapObjIndexed((message, key) => ({
137
- translation: this.config.sourceLocale === locale ? message.message || key : "",
138
- ...message
139
- }), R.pick(newKeys, nextCatalog));
140
-
141
- // Merge translations from previous catalog
142
- const mergedMessages = mergeKeys.map(key => {
143
- const updateFromDefaults = this.config.sourceLocale === locale && (prevCatalog[key].translation === prevCatalog[key].message || options.overwrite);
144
- const translation = updateFromDefaults ? nextCatalog[key].message || key : prevCatalog[key].translation;
145
- return {
146
- [key]: {
147
- translation,
148
- ...R.omit(["obsolete, translation"], nextCatalog[key])
149
- }
150
- };
151
- });
152
-
153
- // Mark all remaining translations as obsolete
154
- // Only if *options.files* is not provided
155
- const obsoleteMessages = obsoleteKeys.map(key => ({
156
- [key]: {
157
- ...prevCatalog[key],
158
- obsolete: options.files ? false : true
159
- }
160
- }));
161
- return R.mergeAll([newMessages, ...mergedMessages, ...obsoleteMessages]);
111
+ return (0, _mergeCatalog.mergeCatalog)(prevCatalog, nextCatalog, this.config.sourceLocale === locale, options);
162
112
  }, prevCatalogs);
163
113
  }
164
114
  getTranslations(locale, options) {
165
- const catalogs = this.readAll();
166
- const template = this.readTemplate() || {};
167
- return R.mapObjIndexed((_value, key) => this.getTranslation(catalogs, locale, key, options), {
168
- ...template,
169
- ...catalogs[locale]
170
- });
171
- }
172
- getTranslation(catalogs, locale, key, {
173
- fallbackLocales,
174
- sourceLocale
175
- }) {
176
- var _catalog$key;
177
- const catalog = catalogs[locale] || {};
178
- const getTranslation = _locale => {
179
- const configLocales = this.config.locales.join('", "');
180
- const localeCatalog = catalogs[_locale] || {};
181
- if (!localeCatalog) {
182
- console.warn(`
183
- Catalog "${_locale}" isn't present in locales config parameter
184
- Add "${_locale}" to your lingui.config.js:
185
- {
186
- locales: ["${configLocales}", "${_locale}"]
187
- }
188
- `);
189
- return null;
190
- }
191
- if (!localeCatalog.hasOwnProperty(key)) {
192
- return null;
193
- }
194
- if (catalogs[_locale]) {
195
- return catalogs[_locale][key].translation;
196
- }
197
- return null;
198
- };
199
- const getMultipleFallbacks = _locale => {
200
- const fL = fallbackLocales && fallbackLocales[_locale];
201
-
202
- // some probably the fallback will be undefined, so just search by locale
203
- if (!fL) return null;
204
- if (Array.isArray(fL)) {
205
- for (const fallbackLocale of fL) {
206
- if (catalogs[fallbackLocale]) {
207
- return getTranslation(fallbackLocale);
208
- }
209
- }
210
- } else {
211
- return getTranslation(fL);
212
- }
213
- };
214
- return (
215
- // Get translation in target locale
216
- getTranslation(locale) ||
217
- // We search in fallbackLocales as dependent of each locale
218
- getMultipleFallbacks(locale) ||
219
- // Get translation in fallbackLocales.default (if any)
220
- (fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales.default) && getTranslation(fallbackLocales.default) || ( // Get message default
221
- (_catalog$key = catalog[key]) === null || _catalog$key === void 0 ? void 0 : _catalog$key.defaults) ||
222
- // If sourceLocale is either target locale of fallback one, use key
223
- sourceLocale && sourceLocale === locale && key || sourceLocale && (fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales.default) && sourceLocale === fallbackLocales.default && key ||
224
- // Otherwise no translation is available
225
- undefined
226
- );
115
+ return (0, _getTranslationsForCatalog.getTranslationsForCatalog)(this, locale, options);
227
116
  }
228
117
  write(locale, messages) {
229
- const filename = this.path.replace(LOCALE_REPLACE_RE, locale) + this.format.catalogExtension;
230
- const created = !_fsExtra.default.existsSync(filename);
231
- const basedir = _path.default.dirname(filename);
232
- if (!_fsExtra.default.existsSync(basedir)) {
233
- _fsExtra.default.mkdirpSync(basedir);
234
- }
118
+ const filename = (0, _utils.replacePlaceholders)(this.path, {
119
+ locale
120
+ }) + this.format.catalogExtension;
121
+ const created = !_fs.default.existsSync(filename);
235
122
  const options = {
236
123
  ...this.config.formatOptions,
237
124
  locale
@@ -244,10 +131,6 @@ class Catalog {
244
131
  }
245
132
  writeTemplate(messages) {
246
133
  const filename = this.templateFile;
247
- const basedir = _path.default.dirname(filename);
248
- if (!_fsExtra.default.existsSync(basedir)) {
249
- _fsExtra.default.mkdirpSync(basedir);
250
- }
251
134
  const options = {
252
135
  ...this.config.formatOptions,
253
136
  locale: undefined
@@ -263,17 +146,16 @@ class Catalog {
263
146
  } else {
264
147
  ext = "js";
265
148
  }
266
- const filename = `${this.path.replace(LOCALE_REPLACE_RE, locale)}.${ext}`;
267
- const basedir = _path.default.dirname(filename);
268
- if (!_fsExtra.default.existsSync(basedir)) {
269
- _fsExtra.default.mkdirpSync(basedir);
270
- }
271
- _fsExtra.default.writeFileSync(filename, compiledCatalog);
149
+ const filename = `${(0, _utils.replacePlaceholders)(this.path, {
150
+ locale
151
+ })}.${ext}`;
152
+ (0, _utils.writeFile)(filename, compiledCatalog);
272
153
  return filename;
273
154
  }
274
155
  read(locale) {
275
- const filename = this.path.replace(LOCALE_REPLACE_RE, locale) + this.format.catalogExtension;
276
- if (!_fsExtra.default.existsSync(filename)) return null;
156
+ const filename = (0, _utils.replacePlaceholders)(this.path, {
157
+ locale
158
+ }) + this.format.catalogExtension;
277
159
  return this.format.read(filename);
278
160
  }
279
161
  readAll() {
@@ -283,12 +165,11 @@ class Catalog {
283
165
  }
284
166
  readTemplate() {
285
167
  const filename = this.templateFile;
286
- if (!_fsExtra.default.existsSync(filename)) return null;
287
168
  return this.format.read(filename);
288
169
  }
289
170
  get sourcePaths() {
290
171
  const includeGlobs = this.include.map(includePath => {
291
- const isDir = _fsExtra.default.existsSync(includePath) && _fsExtra.default.lstatSync(includePath).isDirectory();
172
+ const isDir = (0, _utils.isDirectory)(includePath);
292
173
  /**
293
174
  * glob library results from absolute patterns such as /foo/* are mounted onto the root setting using path.join.
294
175
  * On windows, this will by default result in /foo/* matching C:\foo\bar.txt.
@@ -301,9 +182,6 @@ class Catalog {
301
182
  mark: true
302
183
  });
303
184
  }
304
- get templateFile() {
305
- return this.path.replace(LOCALE_SUFFIX_RE, "messages.pot");
306
- }
307
185
  get localeDir() {
308
186
  const localePatternIndex = this.path.indexOf(LOCALE);
309
187
  if (localePatternIndex === -1) {
@@ -315,150 +193,10 @@ class Catalog {
315
193
  return this.config.locales;
316
194
  }
317
195
  }
318
-
319
- /**
320
- * Parse `config.catalogs` and return a list of configured Catalog instances.
321
- */
322
196
  exports.Catalog = Catalog;
323
- function getCatalogs(config) {
324
- const catalogsConfig = config.catalogs;
325
- const catalogs = [];
326
- catalogsConfig.forEach(catalog => {
327
- // Validate that `catalogPath` doesn't end with trailing slash
328
- if (catalog.path.endsWith(PATHSEP)) {
329
- const extension = (0, _formats.default)(config.format).catalogExtension;
330
- const correctPath = catalog.path.slice(0, -1);
331
- const examplePath = correctPath.replace(LOCALE_REPLACE_RE,
332
- // Show example using one of configured locales (if any)
333
- (config.locales || [])[0] || "en") + extension;
334
- throw new Error(
335
- // prettier-ignore
336
- `Remove trailing slash from "${catalog.path}". Catalog path isn't a directory,` + ` but translation file without extension. For example, catalog path "${correctPath}"` + ` results in translation file "${examplePath}".`);
337
- }
338
- const include = ensureArray(catalog.include).map(normalizeRelativePath);
339
- const exclude = ensureArray(catalog.exclude).map(normalizeRelativePath);
340
-
341
- // catalog.path without {name} pattern -> always refers to a single catalog
342
- if (!catalog.path.includes(NAME)) {
343
- // Validate that sourcePaths doesn't use {name} pattern either
344
- const invalidSource = include.find(path => path.includes(NAME));
345
- if (invalidSource !== undefined) {
346
- throw new Error(`Catalog with path "${catalog.path}" doesn't have a {name} pattern` + ` in it, but one of source directories uses it: "${invalidSource}".` + ` Either add {name} pattern to "${catalog.path}" or remove it` + ` from all source directories.`);
347
- }
348
-
349
- // catalog name is the last directory of catalog.path.
350
- // If the last part is {locale}, then catalog doesn't have an explicit name
351
- const name = function () {
352
- const _name = catalog.path.split(PATHSEP).slice(-1)[0];
353
- return _name !== LOCALE ? _name : null;
354
- }();
355
- catalogs.push(new Catalog({
356
- name,
357
- path: normalizeRelativePath(catalog.path),
358
- include,
359
- exclude
360
- }, config));
361
- return;
362
- }
363
- const patterns = include.map(path => path.replace(NAME_REPLACE_RE, "*"));
364
- const candidates = _glob.default.sync(patterns.length > 1 ? `{${patterns.join(",")}}` : patterns[0], {
365
- ignore: exclude,
366
- mark: true
367
- });
368
- candidates.forEach(catalogDir => {
369
- const name = _path.default.basename(catalogDir);
370
- catalogs.push(new Catalog({
371
- name,
372
- path: normalizeRelativePath(catalog.path.replace(NAME_REPLACE_RE, name)),
373
- include: include.map(path => path.replace(NAME_REPLACE_RE, name)),
374
- exclude: exclude.map(path => path.replace(NAME_REPLACE_RE, name))
375
- }, config));
376
- });
377
- });
378
- return catalogs;
379
- }
380
- function getCatalogForFile(file, catalogs) {
381
- for (const catalog of catalogs) {
382
- const catalogFile = `${catalog.path}${catalog.format.catalogExtension}`;
383
- const catalogGlob = catalogFile.replace(LOCALE_REPLACE_RE, "*");
384
- const match = _micromatch.default.capture(normalizeRelativePath(_path.default.relative(catalog.config.rootDir, catalogGlob)), normalizeRelativePath(file));
385
- if (match) {
386
- return {
387
- locale: match[0],
388
- catalog
389
- };
390
- }
391
- }
392
- return null;
393
- }
394
-
395
- /**
396
- * Create catalog for merged messages.
397
- */
398
- function getCatalogForMerge(config) {
399
- const catalogConfig = config;
400
- if (catalogConfig.catalogsMergePath.endsWith(PATHSEP)) {
401
- const extension = (0, _formats.default)(config.format).catalogExtension;
402
- const correctPath = catalogConfig.catalogsMergePath.slice(0, -1);
403
- const examplePath = correctPath.replace(LOCALE_REPLACE_RE,
404
- // Show example using one of configured locales (if any)
405
- (config.locales || [])[0] || "en") + extension;
406
- throw new Error(
407
- // prettier-ignore
408
- `Remove trailing slash from "${catalogConfig.catalogsMergePath}". Catalog path isn't a directory,` + ` but translation file without extension. For example, catalog path "${correctPath}"` + ` results in translation file "${examplePath}".`);
409
- }
410
-
411
- // catalog name is the last directory of catalogPath.
412
- // If the last part is {locale}, then catalog doesn't have an explicit name
413
- const name = function () {
414
- const _name = _path.default.basename(normalizeRelativePath(catalogConfig.catalogsMergePath));
415
- return _name !== LOCALE ? _name : null;
416
- }();
417
- const catalog = new Catalog({
418
- name,
419
- path: normalizeRelativePath(catalogConfig.catalogsMergePath),
420
- include: [],
421
- exclude: []
422
- }, config);
423
- return catalog;
424
- }
425
-
426
- /**
427
- * Merge origins and extractedComments for messages found in different places. All other attributes
428
- * should be the same (raise an error if defaults are different).
429
- */
430
- function mergeOriginsAndExtractedComments(msgId, prev, next) {
431
- if (prev.defaults !== next.defaults) {
432
- throw new Error(`Encountered different defaults for message ${_chalk.default.yellow(msgId)}` + `\n${_chalk.default.yellow((0, _utils.prettyOrigin)(prev.origin))} ${prev.defaults}` + `\n${_chalk.default.yellow((0, _utils.prettyOrigin)(next.origin))} ${next.defaults}`);
433
- }
434
- return {
435
- ...next,
436
- extractedComments: R.concat(prev.extractedComments, next.extractedComments),
437
- origin: R.concat(prev.origin, next.origin)
438
- };
439
- }
440
-
441
- /**
442
- * Ensure that value is always array. If not, turn it into an array of one element.
443
- */
444
- const ensureArray = value => {
445
- if (value == null) return [];
446
- return Array.isArray(value) ? value : [value];
447
- };
448
-
449
- /**
450
- * Remove ./ at the beginning: ./relative => relative
451
- * relative => relative
452
- * Preserve directories: ./relative/ => relative/
453
- * Preserve absolute paths: /absolute/path => /absolute/path
454
- */
455
- function normalizeRelativePath(sourcePath) {
456
- if (_path.default.isAbsolute(sourcePath)) {
457
- // absolute path
458
- return (0, _normalizePath.default)(sourcePath, false);
459
- }
460
- const isDir = _fsExtra.default.existsSync(sourcePath) && _fsExtra.default.lstatSync(sourcePath).isDirectory();
461
- return (0, _normalizePath.default)(_path.default.relative(process.cwd(), sourcePath), false) + (isDir ? "/" : "");
197
+ function getTemplatePath(format, path) {
198
+ const ext = format.templateExtension || format.catalogExtension;
199
+ return path.replace(LOCALE_SUFFIX_RE, "messages" + ext);
462
200
  }
463
201
  const cleanObsolete = R.filter(message => !message.obsolete);
464
202
  exports.cleanObsolete = cleanObsolete;
@@ -474,11 +212,11 @@ function order(by) {
474
212
  * https://stackoverflow.com/a/31102605/1535540
475
213
  */
476
214
  function orderByMessageId(messages) {
477
- const orderedMessages = {};
478
- Object.keys(messages).sort().forEach(function (key) {
479
- orderedMessages[key] = messages[key];
480
- });
481
- return orderedMessages;
215
+ return Object.keys(messages).sort().reduce((acc, key) => {
216
+ ;
217
+ acc[key] = messages[key];
218
+ return acc;
219
+ }, {});
482
220
  }
483
221
  function orderByOrigin(messages) {
484
222
  function getFirstOrigin(messageKey) {
@@ -489,7 +227,7 @@ function orderByOrigin(messages) {
489
227
  });
490
228
  return sortedOrigins[0];
491
229
  }
492
- return Object.keys(messages).sort(function (a, b) {
230
+ return Object.keys(messages).sort((a, b) => {
493
231
  const [aFile, aLineNumber] = getFirstOrigin(a);
494
232
  const [bFile, bLineNumber] = getFirstOrigin(b);
495
233
  if (aFile < bFile) return -1;
@@ -498,6 +236,7 @@ function orderByOrigin(messages) {
498
236
  if (aLineNumber > bLineNumber) return 1;
499
237
  return 0;
500
238
  }).reduce((acc, key) => {
239
+ ;
501
240
  acc[key] = messages[key];
502
241
  return acc;
503
242
  }, {});
@@ -21,18 +21,6 @@ function createCompiledCatalog(locale, messages, options) {
21
21
  } = options;
22
22
  const shouldPseudolocalize = locale === pseudoLocale;
23
23
  const compiledMessages = Object.keys(messages).reduce((obj, key) => {
24
- const value = messages[key];
25
-
26
- // If the current ID's value is a context object, create a nested
27
- // expression, and assign the current ID to that expression
28
- if (typeof value === "object") {
29
- obj[key] = Object.keys(value).reduce((obj, contextKey) => {
30
- obj[contextKey] = compile(value[contextKey], shouldPseudolocalize);
31
- return obj;
32
- }, {});
33
- return obj;
34
- }
35
-
36
24
  // Don't use `key` as a fallback translation in strict mode.
37
25
  const translation = messages[key] || (!strict ? key : "");
38
26
  obj[key] = compile(translation, shouldPseudolocalize);
@@ -6,37 +6,80 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _core = require("@babel/core");
8
8
  var _babelPluginExtractMessages = _interopRequireDefault(require("@lingui/babel-plugin-extract-messages"));
9
- var _detect = require("../detect");
9
+ var _sourceMap = require("source-map");
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
- const babelRe = new RegExp("\\.(" + [..._core.DEFAULT_EXTENSIONS, ".ts", ".tsx"].map(ext => ext.slice(1)).join("|") + ")$", "i");
11
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
+ const babelRe = new RegExp("\\.(" + [..._core.DEFAULT_EXTENSIONS, ".ts", ".mts", ".cts", ".tsx"].map(ext => ext.slice(1)).join("|") + ")$", "i");
14
+ const inlineSourceMapsRE = new RegExp(/\/[\/\*][#@]\s+sourceMappingURL=data:application\/json;(?:charset:utf-8;)?base64,/i);
12
15
  const extractor = {
13
16
  match(filename) {
14
17
  return babelRe.test(filename);
15
18
  },
16
- extract(filename, localeDir, options = {}) {
17
- const {
18
- babelOptions: _babelOptions = {},
19
- configPath
20
- } = options;
21
- const {
22
- plugins = [],
23
- ...babelOptions
24
- } = _babelOptions;
25
- const frameworkOptions = {};
26
- if (options.projectType === _detect.projectType.CRA) {
27
- frameworkOptions.presets = ["react-app"];
19
+ async extract(filename, code, onMessageExtracted, linguiConfig, ctx) {
20
+ const parserOptions = linguiConfig.extractorParserOptions;
21
+ const parserPlugins = [
22
+ // https://babeljs.io/docs/en/babel-parser#latest-ecmascript-features
23
+ ["decorators", {
24
+ decoratorsBeforeExport: (parserOptions === null || parserOptions === void 0 ? void 0 : parserOptions.decoratorsBeforeExport) || true
25
+ }]];
26
+ if ([/\.ts$/, /\.mts$/, /\.cts$/, /\.tsx$/].some(r => filename.match(r))) {
27
+ parserPlugins.push("typescript");
28
+ } else if (parserOptions !== null && parserOptions !== void 0 && parserOptions.flow) {
29
+ parserPlugins.push("flow");
28
30
  }
29
- (0, _core.transformFileSync)(filename, {
30
- ...babelOptions,
31
- ...frameworkOptions,
32
- // we override envName to avoid issues with NODE_ENV=production
33
- // https://github.com/lingui/js-lingui/issues/952
34
- envName: "development",
35
- plugins: ["macros", [_babelPluginExtractMessages.default, {
36
- localeDir,
37
- configPath
38
- }], ...plugins]
31
+ if ([/\.jsx$/, /\.tsx$/].some(r => filename.match(r))) {
32
+ parserPlugins.push("jsx");
33
+ }
34
+ let sourceMapsConsumer;
35
+ if (ctx !== null && ctx !== void 0 && ctx.sourceMaps) {
36
+ sourceMapsConsumer = await new _sourceMap.SourceMapConsumer(ctx === null || ctx === void 0 ? void 0 : ctx.sourceMaps);
37
+ } else if (code.search(inlineSourceMapsRE) != -1) {
38
+ const {
39
+ fromSource
40
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require("convert-source-map")));
41
+ sourceMapsConsumer = await new _sourceMap.SourceMapConsumer(fromSource(code).toObject());
42
+ }
43
+ await (0, _core.transformAsync)(code, {
44
+ // don't generate code
45
+ code: false,
46
+ babelrc: false,
47
+ configFile: false,
48
+ filename: filename,
49
+ inputSourceMap: ctx === null || ctx === void 0 ? void 0 : ctx.sourceMaps,
50
+ parserOpts: {
51
+ plugins: parserPlugins
52
+ },
53
+ plugins: [["macros", {
54
+ // macro plugin uses package `resolve` to find a path of macro file
55
+ // this will not follow jest pathMapping and will resolve path from ./build
56
+ // instead of ./src which makes testing & developing hard.
57
+ // here we override resolve and provide correct path for testing
58
+ resolvePath: source => require.resolve(source),
59
+ lingui: {
60
+ extract: true,
61
+ linguiConfig
62
+ }
63
+ }], [_babelPluginExtractMessages.default, {
64
+ onMessageExtracted: msg => {
65
+ if (!sourceMapsConsumer) {
66
+ return onMessageExtracted(msg);
67
+ }
68
+ const [_, line, column] = msg.origin;
69
+ const mappedPosition = sourceMapsConsumer.originalPositionFor({
70
+ line,
71
+ column
72
+ });
73
+ return onMessageExtracted({
74
+ ...msg,
75
+ origin: [mappedPosition.source, mappedPosition.line, mappedPosition.column]
76
+ });
77
+ }
78
+ }]]
39
79
  });
80
+ if (sourceMapsConsumer) {
81
+ sourceMapsConsumer.destroy();
82
+ }
40
83
  }
41
84
  };
42
85
  var _default = extractor;