@lingui/babel-plugin-extract-messages 3.17.0 → 3.17.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/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.17.2](https://github.com/lingui/js-lingui/compare/v3.17.1...v3.17.2) (2023-02-24)
7
+
8
+ **Note:** Version bump only for package @lingui/babel-plugin-extract-messages
9
+
10
+
11
+
12
+
13
+
14
+ ## [3.17.1](https://github.com/lingui/js-lingui/compare/v3.17.0...v3.17.1) (2023-02-07)
15
+
16
+ **Note:** Version bump only for package @lingui/babel-plugin-extract-messages
17
+
18
+
19
+
20
+
21
+
6
22
  # [3.17.0](https://github.com/lingui/js-lingui/compare/v3.16.1...v3.17.0) (2023-02-01)
7
23
 
8
24
  **Note:** Version bump only for package @lingui/babel-plugin-extract-messages
package/build/index.js CHANGED
@@ -4,27 +4,21 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = _default;
7
-
8
7
  var _fs = _interopRequireDefault(require("fs"));
9
-
10
8
  var _path = _interopRequireDefault(require("path"));
11
-
12
9
  var _mkdirp = _interopRequireDefault(require("mkdirp"));
13
-
14
10
  var _generator = _interopRequireDefault(require("@babel/generator"));
15
-
16
11
  var _conf = require("@lingui/conf");
17
-
18
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ const CONFIG = Symbol("I18nConfig");
19
14
 
20
- const CONFIG = Symbol("I18nConfig"); // Map of messages
15
+ // Map of messages
16
+ const MESSAGES = Symbol("I18nMessages");
21
17
 
22
- const MESSAGES = Symbol("I18nMessages"); // We need to remember all processed nodes. When JSX expressions are
18
+ // We need to remember all processed nodes. When JSX expressions are
23
19
  // replaced with CallExpressions, all children are traversed for each CallExpression.
24
20
  // Then, i18n._ methods are visited multiple times for each parent CallExpression.
25
-
26
21
  const VISITED = Symbol("I18nVisited");
27
-
28
22
  function addMessage(path, messages, {
29
23
  id,
30
24
  message: newDefault,
@@ -36,31 +30,27 @@ function addMessage(path, messages, {
36
30
  // prevent from adding undefined msgid
37
31
  if (id === undefined) return;
38
32
  const extractedComments = comment ? [comment] : [];
39
-
40
33
  if (context) {
41
34
  if (messages.has(context)) {
42
35
  const existingContext = messages.get(context);
43
-
44
36
  if (existingContext.has(id)) {
45
- const message = messages.get(id); // only set/check default language when it's defined.
46
-
37
+ const message = messages.get(id);
38
+ // only set/check default language when it's defined.
47
39
  if (message.message && newDefault && message.message !== newDefault) {
48
40
  throw path.buildCodeFrameError("Different defaults for the same message ID.");
49
41
  }
50
-
51
42
  if (newDefault) {
52
43
  message.message = newDefault;
53
44
  }
54
-
55
45
  ;
56
46
  [].push.apply(message.origin, origin);
57
-
58
47
  if (comment) {
59
48
  ;
60
49
  [].push.apply(message.extractedComments, [comment]);
61
50
  }
62
51
  } else {
63
- existingContext.set(id, { ...props,
52
+ existingContext.set(id, {
53
+ ...props,
64
54
  message: newDefault,
65
55
  origin,
66
56
  extractedComments
@@ -69,7 +59,8 @@ function addMessage(path, messages, {
69
59
  }
70
60
  } else {
71
61
  const newContext = new Map();
72
- newContext.set(id, { ...props,
62
+ newContext.set(id, {
63
+ ...props,
73
64
  message: newDefault,
74
65
  origin,
75
66
  extractedComments
@@ -78,25 +69,24 @@ function addMessage(path, messages, {
78
69
  }
79
70
  } else {
80
71
  if (messages.has(id)) {
81
- const message = messages.get(id); // only set/check default language when it's defined.
72
+ const message = messages.get(id);
82
73
 
74
+ // only set/check default language when it's defined.
83
75
  if (message.message && newDefault && message.message !== newDefault) {
84
76
  throw path.buildCodeFrameError("Different defaults for the same message ID.");
85
77
  }
86
-
87
78
  if (newDefault) {
88
79
  message.message = newDefault;
89
80
  }
90
-
91
81
  ;
92
82
  [].push.apply(message.origin, origin);
93
-
94
83
  if (comment) {
95
84
  ;
96
85
  [].push.apply(message.extractedComments, [comment]);
97
86
  }
98
87
  } else {
99
- messages.set(id, { ...props,
88
+ messages.set(id, {
89
+ ...props,
100
90
  message: newDefault,
101
91
  origin,
102
92
  extractedComments
@@ -104,14 +94,13 @@ function addMessage(path, messages, {
104
94
  }
105
95
  }
106
96
  }
97
+
107
98
  /**
108
99
  * An ES6 Map type is not possible to encode with JSON.stringify,
109
- * so we can instead use a replacer function as an argument to
100
+ * so we can instead use a replacer function as an argument to
110
101
  * tell the JSON parser how to serialize / deserialize the Maps
111
102
  * it encounters.
112
103
  */
113
-
114
-
115
104
  function mapReplacer(key, value) {
116
105
  if (value instanceof Map) {
117
106
  const object = {};
@@ -120,10 +109,8 @@ function mapReplacer(key, value) {
120
109
  });
121
110
  return object;
122
111
  }
123
-
124
112
  return value;
125
113
  }
126
-
127
114
  function extractStringContatentation(t, node, error) {
128
115
  if (t.isStringLiteral(node)) {
129
116
  return node.value;
@@ -133,51 +120,42 @@ function extractStringContatentation(t, node, error) {
133
120
  throw error;
134
121
  }
135
122
  }
136
-
137
123
  function extractCommentString(t, path, valuePath, valueObj) {
138
124
  if (t.isStringLiteral(valueObj)) {
139
125
  // Comment is a single line string
140
126
  return valueObj.value;
141
- } // Comment is a multi-line string.
142
-
127
+ }
143
128
 
129
+ // Comment is a multi-line string.
144
130
  const errorIfNotAString = path.get(valuePath).buildCodeFrameError("Only strings are supported as comments.");
145
-
146
131
  if (t.isBinaryExpression(valueObj)) {
147
132
  return extractStringContatentation(t, valueObj, errorIfNotAString);
148
133
  } else {
149
134
  throw errorIfNotAString;
150
135
  }
151
136
  }
152
-
153
137
  function _default({
154
138
  types: t
155
139
  }) {
156
140
  let localTransComponentName;
157
-
158
141
  function isTransComponent(node) {
159
142
  return t.isJSXElement(node) && t.isJSXIdentifier(node.openingElement.name, {
160
143
  name: localTransComponentName
161
144
  });
162
145
  }
163
-
164
146
  const isI18nMethod = node => t.isMemberExpression(node) && t.isIdentifier(node.object, {
165
147
  name: "i18n"
166
148
  }) && t.isIdentifier(node.property, {
167
149
  name: "_"
168
150
  });
169
-
170
151
  function collectMessage(path, file, props) {
171
152
  const messages = file.get(MESSAGES);
172
153
  const rootDir = file.get(CONFIG).rootDir;
173
-
174
154
  const filename = _path.default.relative(rootDir, file.opts.filename).replace(/\\/g, "/");
175
-
176
155
  const line = path.node.loc ? path.node.loc.start.line : null;
177
156
  props.origin = [[filename, line]];
178
157
  addMessage(path, messages, props);
179
158
  }
180
-
181
159
  return {
182
160
  visitor: {
183
161
  // Get the local name of Trans component. Usually it's just `Trans`, but
@@ -190,21 +168,19 @@ function _default({
190
168
  const moduleName = node.source.value;
191
169
  if (!["@lingui/react", "@lingui/macro", "@lingui/core"].includes(moduleName)) return;
192
170
  const importDeclarations = {};
193
-
194
171
  if (moduleName === "@lingui/react" || moduleName === "@lingui/macro") {
195
172
  node.specifiers.forEach(specifier => {
196
173
  importDeclarations[specifier.imported.name] = specifier.local.name;
197
- }); // Trans import might be missing if there's just Plural or similar macro.
198
- // If there's no alias, consider it was imported as Trans.
174
+ });
199
175
 
176
+ // Trans import might be missing if there's just Plural or similar macro.
177
+ // If there's no alias, consider it was imported as Trans.
200
178
  localTransComponentName = importDeclarations["Trans"] || "Trans";
201
179
  }
202
-
203
180
  if (!node.specifiers.length) {
204
181
  path.remove();
205
182
  }
206
183
  },
207
-
208
184
  // Extract translation from <Trans /> component.
209
185
  JSXElement(path, {
210
186
  file
@@ -216,7 +192,6 @@ function _default({
216
192
  const attrs = node.openingElement.attributes || [];
217
193
  const props = attrs.reduce((acc, item) => {
218
194
  const key = item.name.name;
219
-
220
195
  if (key === "id" || key === "message" || key === "comment" || key === "context") {
221
196
  if (item.value.value) {
222
197
  acc[key] = item.value.value;
@@ -224,25 +199,19 @@ function _default({
224
199
  acc[key] = item.value.expression.value;
225
200
  }
226
201
  }
227
-
228
202
  return acc;
229
203
  }, {});
230
-
231
204
  if (!props.id) {
232
205
  // <Trans id={message} /> is valid, don't raise warning
233
206
  const idProp = attrs.filter(item => item.name.name === "id")[0];
234
-
235
207
  if (idProp === undefined || t.isLiteral(props.id)) {
236
208
  console.warn("Missing message ID, skipping.");
237
209
  console.warn((0, _generator.default)(node).code);
238
210
  }
239
-
240
211
  return;
241
212
  }
242
-
243
213
  collectMessage(path, file, props);
244
214
  },
245
-
246
215
  CallExpression(path, {
247
216
  file
248
217
  }) {
@@ -258,15 +227,12 @@ function _default({
258
227
  const props = {
259
228
  id: path.node.arguments[0].value
260
229
  };
261
-
262
230
  if (!props.id) {
263
231
  console.warn("Missing message ID, skipping.");
264
232
  console.warn((0, _generator.default)(path.node).code);
265
233
  return;
266
234
  }
267
-
268
235
  const copyOptions = ["message", "comment", "context"];
269
-
270
236
  if (t.isObjectExpression(path.node.arguments[2])) {
271
237
  path.node.arguments[2].properties.forEach(({
272
238
  key,
@@ -274,54 +240,43 @@ function _default({
274
240
  }, i) => {
275
241
  if (!copyOptions.includes(key.name)) return;
276
242
  let valueToExtract = value.value;
277
-
278
243
  if (key.name === "comment") {
279
244
  valueToExtract = extractCommentString(t, path, `arguments.2.properties.${i}.value`, value);
280
245
  }
281
-
282
246
  props[key.name] = valueToExtract;
283
247
  });
284
248
  }
285
-
286
249
  visited.add(path.node);
287
250
  collectMessage(path, file, props);
288
251
  },
289
-
290
252
  StringLiteral(path, {
291
253
  file
292
254
  }) {
293
255
  const visited = file.get(VISITED);
294
256
  const comment = path.node.leadingComments && path.node.leadingComments.filter(node => node.value.match(/^\s*i18n/))[0];
295
-
296
257
  if (!comment || visited.has(path.node)) {
297
258
  return;
298
259
  }
299
-
300
260
  visited.add(path.node);
301
261
  const props = {
302
262
  id: path.node.value
303
263
  };
304
-
305
264
  if (!props.id) {
306
265
  console.warn("Missing message ID, skipping.");
307
266
  console.warn((0, _generator.default)(path.node).code);
308
267
  return;
309
268
  }
310
-
311
269
  collectMessage(path, file, props);
312
270
  },
313
-
314
271
  // Extract message descriptors
315
272
  ObjectExpression(path, {
316
273
  file
317
274
  }) {
318
275
  const visited = file.get(VISITED);
319
276
  const comment = path.node.leadingComments && path.node.leadingComments.filter(node => node.value.match(/^\s*i18n/))[0];
320
-
321
277
  if (!comment || visited.has(path.node)) {
322
278
  return;
323
279
  }
324
-
325
280
  visited.add(path.node);
326
281
  const props = {};
327
282
  const copyProps = ["id", "message", "comment", "context"];
@@ -333,43 +288,39 @@ function _default({
333
288
  }, i) => {
334
289
  // By default, the value is just the string value of the object property.
335
290
  let valueToExtract = value.value;
336
-
337
291
  if (key.name === "comment") {
338
292
  valueToExtract = extractCommentString(t, path, `properties.${i}.value`, value);
339
293
  } else if (key.name === "id") {
340
294
  const isIdLiteral = !value.value && t.isTemplateLiteral(value);
341
-
342
295
  if (isIdLiteral) {
343
- valueToExtract = value?.quasis[0]?.value?.cooked;
296
+ var _value$quasis$, _value$quasis$$value;
297
+ valueToExtract = value === null || value === void 0 ? void 0 : (_value$quasis$ = value.quasis[0]) === null || _value$quasis$ === void 0 ? void 0 : (_value$quasis$$value = _value$quasis$.value) === null || _value$quasis$$value === void 0 ? void 0 : _value$quasis$$value.cooked;
344
298
  }
345
299
  }
346
-
347
300
  props[key.name] = valueToExtract;
348
301
  });
349
302
  collectMessage(path, file, props);
350
303
  }
351
-
352
304
  },
353
-
354
305
  pre(file) {
355
- localTransComponentName = null; // Skip validation because config is loaded for each file.
356
- // Config was already validated in CLI.
306
+ localTransComponentName = null;
357
307
 
308
+ // Skip validation because config is loaded for each file.
309
+ // Config was already validated in CLI.
358
310
  file.set(CONFIG, (0, _conf.getConfig)({
359
311
  cwd: file.opts.filename,
360
312
  skipValidation: true,
361
313
  configPath: this.opts.configPath
362
- })); // Ignore else path for now. Collision is possible if other plugin is
314
+ }));
315
+
316
+ // Ignore else path for now. Collision is possible if other plugin is
363
317
  // using the same Symbol('I18nMessages').
364
318
  // istanbul ignore else
365
-
366
319
  if (!file.has(MESSAGES)) {
367
320
  file.set(MESSAGES, new Map());
368
321
  }
369
-
370
322
  file.set(VISITED, new WeakSet());
371
323
  },
372
-
373
324
  post(file) {
374
325
  /* Write catalog to directory `localeDir`/_build/`path.to.file`/`filename`.json
375
326
  * e.g: if file is src/components/App.js (relative to package.json), then
@@ -381,36 +332,26 @@ function _default({
381
332
  filename
382
333
  } = file.opts;
383
334
  const rootDir = config.rootDir;
384
-
385
335
  const baseDir = _path.default.dirname(_path.default.relative(rootDir, filename));
386
-
387
336
  const targetDir = _path.default.join(localeDir, "_build", baseDir);
388
-
389
337
  const messages = file.get(MESSAGES);
390
338
  const catalog = {};
391
-
392
339
  const baseName = _path.default.basename(filename);
393
-
394
340
  const catalogFilename = _path.default.join(targetDir, `${baseName}.json`);
341
+ _mkdirp.default.sync(targetDir);
395
342
 
396
- _mkdirp.default.sync(targetDir); // no messages, skip file
397
-
398
-
343
+ // no messages, skip file
399
344
  if (!messages.size) {
400
345
  // clean any existing catalog
401
346
  if (_fs.default.existsSync(catalogFilename)) {
402
347
  _fs.default.writeFileSync(catalogFilename, JSON.stringify({}));
403
348
  }
404
-
405
349
  return;
406
350
  }
407
-
408
351
  messages.forEach((value, key) => {
409
352
  catalog[key] = value;
410
353
  });
411
-
412
354
  _fs.default.writeFileSync(catalogFilename, JSON.stringify(catalog, mapReplacer, 2));
413
355
  }
414
-
415
356
  };
416
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingui/babel-plugin-extract-messages",
3
- "version": "3.17.0",
3
+ "version": "3.17.2",
4
4
  "description": "Babel plugin for collecting messages from source code for internationalization",
5
5
  "main": "./build/index.js",
6
6
  "author": {
@@ -33,10 +33,10 @@
33
33
  "node": ">=14.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@babel/generator": "^7.11.6",
37
- "@babel/runtime": "^7.11.2",
38
- "@lingui/conf": "3.17.0",
36
+ "@babel/generator": "^7.20.14",
37
+ "@babel/runtime": "^7.20.13",
38
+ "@lingui/conf": "3.17.2",
39
39
  "mkdirp": "^1.0.4"
40
40
  },
41
- "gitHead": "1c8bc46213b35b25da8fe7a80ddcf6f6a5d9d539"
41
+ "gitHead": "31dcab5a9a8f88bfa8b3a2c7cd12aa2d908a1d80"
42
42
  }