@lingui/macro 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.
package/build/macroJs.js CHANGED
@@ -4,14 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var R = _interopRequireWildcard(require("ramda"));
8
7
  var _types = require("@babel/types");
9
8
  var _icu = _interopRequireDefault(require("./icu"));
10
9
  var _utils = require("./utils");
11
10
  var _constants = require("./constants");
11
+ var _api = require("@lingui/cli/api");
12
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
- 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); }
14
- 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; }
15
13
  const keepSpaceRe = /(?:\\(?:\r\n|\r|\n))+\s+/g;
16
14
  const keepNewLineRe = /(?:\r\n|\r|\n)+\s+/g;
17
15
  function normalizeWhitespace(text) {
@@ -26,27 +24,21 @@ class MacroJs {
26
24
  _expressionIndex = (0, _utils.makeCounter)();
27
25
  constructor({
28
26
  types
29
- }, {
30
- i18nImportName
31
- }) {
27
+ }, opts) {
32
28
  this.types = types;
33
- this.i18nImportName = i18nImportName;
29
+ this.i18nImportName = opts.i18nImportName;
30
+ this.stripNonEssentialProps = opts.stripNonEssentialProps;
31
+ this.nameMap = opts.nameMap;
32
+ this.nameMapReversed = Array.from(opts.nameMap.entries()).reduce((map, [key, value]) => map.set(value, key), new Map());
34
33
  }
35
34
  replacePathWithMessage = (path, {
36
35
  message,
37
36
  values
38
37
  }, linguiInstance) => {
39
- const args = [];
40
- args.push(isString(message) ? this.types.stringLiteral(message) : message);
41
- if (Object.keys(values).length) {
42
- const valuesObject = Object.keys(values).map(key => this.types.objectProperty(this.types.identifier(key), values[key]));
43
- args.push(this.types.objectExpression(valuesObject));
44
- }
45
- const newNode = this.types.callExpression(this.types.memberExpression(linguiInstance ?? this.types.identifier(this.i18nImportName), this.types.identifier("_")), args);
46
-
47
- // preserve line number
48
- newNode.loc = path.node.loc;
49
- path.addComment("leading", _constants.EXTRACT_MARK);
38
+ const properties = [this.createIdProperty(message), this.createObjectProperty(_constants.MESSAGE, this.types.stringLiteral(message)), this.createValuesProperty(values)];
39
+ const newNode = this.createI18nCall(this.createMessageDescriptor(properties,
40
+ // preserve line numbers for extractor
41
+ path.node.loc), linguiInstance);
50
42
  path.replaceWith(newNode);
51
43
  };
52
44
 
@@ -59,8 +51,8 @@ class MacroJs {
59
51
  return true;
60
52
  }
61
53
 
62
- // t(i18nInstance)`Message` -> i18nInstance._('Message')
63
- if (this.types.isCallExpression(path.node) && this.types.isTaggedTemplateExpression(path.parentPath.node) && this.types.isIdentifier(path.node.arguments[0]) && this.isIdentifier(path.node.callee, "t")) {
54
+ // t(i18nInstance)`Message` -> i18nInstance._(messageDescriptor)
55
+ if (this.types.isCallExpression(path.node) && this.types.isTaggedTemplateExpression(path.parentPath.node) && this.types.isExpression(path.node.arguments[0]) && this.isLinguiIdentifier(path.node.callee, "t")) {
64
56
  // Use the first argument as i18n instance instead of the default i18n instance
65
57
  const i18nInstance = path.node.arguments[0];
66
58
  const tokens = this.tokenizeNode(path.parentPath.node);
@@ -78,12 +70,12 @@ class MacroJs {
78
70
  }
79
71
 
80
72
  // t(i18nInstance)(messageDescriptor) -> i18nInstance._(messageDescriptor)
81
- if (this.types.isCallExpression(path.node) && this.types.isCallExpression(path.parentPath.node) && this.types.isIdentifier(path.node.arguments[0]) && this.isIdentifier(path.node.callee, "t")) {
73
+ if (this.types.isCallExpression(path.node) && this.types.isCallExpression(path.parentPath.node) && this.types.isExpression(path.node.arguments[0]) && this.isLinguiIdentifier(path.node.callee, "t")) {
82
74
  const i18nInstance = path.node.arguments[0];
83
75
  this.replaceTAsFunction(path.parentPath, i18nInstance);
84
76
  return false;
85
77
  }
86
- if (this.types.isCallExpression(path.node) && this.isIdentifier(path.node.callee, "t")) {
78
+ if (this.types.isCallExpression(path.node) && this.isLinguiIdentifier(path.node.callee, "t")) {
87
79
  this.replaceTAsFunction(path);
88
80
  return true;
89
81
  }
@@ -134,9 +126,8 @@ class MacroJs {
134
126
  * we create a new node to append it to i18n._
135
127
  */
136
128
  replaceTAsFunction = (path, linguiInstance) => {
137
- let descriptor = this.processDescriptor(path.node.arguments[0]);
138
- const newNode = this.types.callExpression(this.types.memberExpression(linguiInstance ?? this.types.identifier(this.i18nImportName), this.types.identifier("_")), [descriptor]);
139
- path.replaceWith(newNode);
129
+ const descriptor = this.processDescriptor(path.node.arguments[0]);
130
+ path.replaceWith(this.createI18nCall(descriptor, linguiInstance));
140
131
  };
141
132
 
142
133
  /**
@@ -158,44 +149,52 @@ class MacroJs {
158
149
  */
159
150
  processDescriptor = descriptor_ => {
160
151
  const descriptor = descriptor_;
161
- this.types.addComment(descriptor, "leading", _constants.EXTRACT_MARK);
162
- const messageIndex = descriptor.properties.findIndex(property => (0, _types.isObjectProperty)(property) && this.isIdentifier(property.key, _constants.MESSAGE));
163
- if (messageIndex === -1) {
164
- return descriptor;
152
+ const messageProperty = this.getObjectPropertyByKey(descriptor, _constants.MESSAGE);
153
+ const idProperty = this.getObjectPropertyByKey(descriptor, _constants.ID);
154
+ const contextProperty = this.getObjectPropertyByKey(descriptor, _constants.CONTEXT);
155
+ const properties = [idProperty];
156
+ if (!this.stripNonEssentialProps) {
157
+ properties.push(contextProperty);
165
158
  }
166
159
 
167
160
  // if there's `message` property, replace macros with formatted message
168
- const node = descriptor.properties[messageIndex];
169
-
170
- // Inside message descriptor the `t` macro in `message` prop is optional.
171
- // Template strings are always processed as if they were wrapped by `t`.
172
- const tokens = this.types.isTemplateLiteral(node.value) ? this.tokenizeTemplateLiteral(node.value) : this.tokenizeNode(node.value, true);
173
- let messageNode = node.value;
174
- if (tokens != null) {
175
- const messageFormat = new _icu.default();
176
- const {
177
- message: messageRaw,
178
- values
179
- } = messageFormat.fromTokens(tokens);
180
- const message = normalizeWhitespace(messageRaw);
181
- messageNode = this.types.stringLiteral(message);
182
- this.addValues(descriptor.properties, values);
161
+ if (messageProperty) {
162
+ // Inside message descriptor the `t` macro in `message` prop is optional.
163
+ // Template strings are always processed as if they were wrapped by `t`.
164
+ const tokens = this.types.isTemplateLiteral(messageProperty.value) ? this.tokenizeTemplateLiteral(messageProperty.value) : this.tokenizeNode(messageProperty.value, true);
165
+ let messageNode = messageProperty.value;
166
+ if (tokens) {
167
+ const messageFormat = new _icu.default();
168
+ const {
169
+ message: messageRaw,
170
+ values
171
+ } = messageFormat.fromTokens(tokens);
172
+ const message = normalizeWhitespace(messageRaw);
173
+ messageNode = this.types.stringLiteral(message);
174
+ properties.push(this.createValuesProperty(values));
175
+ }
176
+ if (!this.stripNonEssentialProps) {
177
+ properties.push(this.createObjectProperty(_constants.MESSAGE, messageNode));
178
+ }
179
+ if (!idProperty && this.types.isStringLiteral(messageNode)) {
180
+ const context = contextProperty && this.getTextFromExpression(contextProperty.value);
181
+ properties.push(this.createIdProperty(messageNode.value, context));
182
+ }
183
183
  }
184
-
185
- // Don't override custom ID
186
- const hasId = descriptor.properties.findIndex(property => (0, _types.isObjectProperty)(property) && this.isIdentifier(property.key, _constants.ID)) !== -1;
187
- descriptor.properties[messageIndex] = this.types.objectProperty(this.types.identifier(hasId ? _constants.MESSAGE : _constants.ID), messageNode);
188
- if (process.env.NODE_ENV === "production") {
189
- descriptor.properties = descriptor.properties.filter(property => (0, _types.isObjectProperty)(property) && !this.isIdentifier(property.key, _constants.MESSAGE) && (0, _types.isObjectProperty)(property) && !this.isIdentifier(property.key, _constants.COMMENT));
184
+ if (!this.stripNonEssentialProps) {
185
+ properties.push(this.getObjectPropertyByKey(descriptor, _constants.COMMENT));
190
186
  }
191
- return descriptor;
187
+ return this.createMessageDescriptor(properties, descriptor.loc);
192
188
  };
193
- addValues = (obj, values) => {
189
+ createIdProperty(message, context) {
190
+ return this.createObjectProperty(_constants.ID, this.types.stringLiteral((0, _api.generateMessageId)(message, context)));
191
+ }
192
+ createValuesProperty(values) {
194
193
  const valuesObject = Object.keys(values).map(key => this.types.objectProperty(this.types.identifier(key), values[key]));
195
194
  if (!valuesObject.length) return;
196
- obj.push(this.types.objectProperty(this.types.identifier("values"), this.types.objectExpression(valuesObject)));
197
- };
198
- tokenizeNode = (node, ignoreExpression = false) => {
195
+ return this.types.objectProperty(this.types.identifier("values"), this.types.objectExpression(valuesObject));
196
+ }
197
+ tokenizeNode(node, ignoreExpression = false) {
199
198
  if (this.isI18nMethod(node)) {
200
199
  // t
201
200
  return this.tokenizeTemplateLiteral(node);
@@ -206,35 +205,37 @@ class MacroJs {
206
205
  // // date, number
207
206
  // return transformFormatMethod(node, file, props, root)
208
207
  } else if (!ignoreExpression) {
209
- return this.tokenizeExpression(node);
208
+ return [this.tokenizeExpression(node)];
210
209
  }
211
- };
210
+ }
212
211
 
213
212
  /**
214
213
  * `node` is a TemplateLiteral. node.quasi contains
215
214
  * text chunks and node.expressions contains expressions.
216
215
  * Both arrays must be zipped together to get the final list of tokens.
217
216
  */
218
- tokenizeTemplateLiteral = node => {
219
- const tokenize = R.pipe(R.evolve({
220
- quasis: R.map(text => {
221
- // Don't output tokens without text.
222
- // if it's an unicode we keep the cooked value because it's the parsed value by babel (without unicode chars)
223
- // This regex will detect if a string contains unicode chars, when they're we should interpolate them
224
- // why? because platforms like react native doesn't parse them, just doing a JSON.parse makes them UTF-8 friendly
225
- const value = /\\u[a-fA-F0-9]{4}|\\x[a-fA-F0-9]{2}/g.test(text.value.raw) ? text.value.cooked : text.value.raw;
226
- if (value === "") return null;
227
- return {
228
- type: "text",
229
- value: this.clearBackslashes(value)
230
- };
231
- }),
232
- expressions: R.map(exp => this.types.isCallExpression(exp) ? this.tokenizeNode(exp) : this.tokenizeExpression(exp))
233
- }), exp => (0, _utils.zip)(exp.quasis, exp.expressions), R.flatten, R.filter(Boolean));
234
- return tokenize(this.types.isTaggedTemplateExpression(node) ? node.quasi : node);
235
- };
236
- tokenizeChoiceComponent = node => {
237
- const format = node.callee.name.toLowerCase();
217
+ tokenizeTemplateLiteral(node) {
218
+ const tpl = this.types.isTaggedTemplateExpression(node) ? node.quasi : node;
219
+ const expressions = tpl.expressions;
220
+ return tpl.quasis.flatMap((text, i) => {
221
+ // if it's an unicode we keep the cooked value because it's the parsed value by babel (without unicode chars)
222
+ // This regex will detect if a string contains unicode chars, when they're we should interpolate them
223
+ // why? because platforms like react native doesn't parse them, just doing a JSON.parse makes them UTF-8 friendly
224
+ const value = /\\u[a-fA-F0-9]{4}|\\x[a-fA-F0-9]{2}/g.test(text.value.raw) ? text.value.cooked : text.value.raw;
225
+ let argTokens = [];
226
+ const currExp = expressions[i];
227
+ if (currExp) {
228
+ argTokens = this.types.isCallExpression(currExp) ? this.tokenizeNode(currExp) : [this.tokenizeExpression(currExp)];
229
+ }
230
+ return [...(value ? [{
231
+ type: "text",
232
+ value: this.clearBackslashes(value)
233
+ }] : []), ...argTokens];
234
+ });
235
+ }
236
+ tokenizeChoiceComponent(node) {
237
+ const name = node.callee.name;
238
+ const format = (this.nameMapReversed.get(name) || name).toLowerCase();
238
239
  const token = {
239
240
  ...this.tokenizeExpression(node.arguments[0]),
240
241
  format,
@@ -269,8 +270,8 @@ class MacroJs {
269
270
  }
270
271
  }
271
272
  return token;
272
- };
273
- tokenizeExpression = node => {
273
+ }
274
+ tokenizeExpression(node) {
274
275
  if (this.isArg(node) && this.types.isCallExpression(node)) {
275
276
  return {
276
277
  type: "arg",
@@ -283,8 +284,8 @@ class MacroJs {
283
284
  name: this.expressionToArgument(node),
284
285
  value: node
285
286
  };
286
- };
287
- expressionToArgument = exp => {
287
+ }
288
+ expressionToArgument(exp) {
288
289
  if (this.types.isIdentifier(exp)) {
289
290
  return exp.name;
290
291
  } else if (this.types.isStringLiteral(exp)) {
@@ -292,7 +293,7 @@ class MacroJs {
292
293
  } else {
293
294
  return String(this._expressionIndex());
294
295
  }
295
- };
296
+ }
296
297
 
297
298
  /**
298
299
  * We clean '//\` ' to just '`'
@@ -301,27 +302,54 @@ class MacroJs {
301
302
  // if not we replace the extra scaped literals
302
303
  return value.replace(/\\`/g, "`");
303
304
  }
305
+ createI18nCall(messageDescriptor, linguiInstance) {
306
+ return this.types.callExpression(this.types.memberExpression(linguiInstance ?? this.types.identifier(this.i18nImportName), this.types.identifier("_")), [messageDescriptor]);
307
+ }
308
+ createMessageDescriptor(properties, oldLoc) {
309
+ const newDescriptor = this.types.objectExpression(properties.filter(Boolean));
310
+ this.types.addComment(newDescriptor, "leading", _constants.EXTRACT_MARK);
311
+ if (oldLoc) {
312
+ newDescriptor.loc = oldLoc;
313
+ }
314
+ return newDescriptor;
315
+ }
316
+ createObjectProperty(key, value) {
317
+ return this.types.objectProperty(this.types.identifier(key), value);
318
+ }
319
+ getObjectPropertyByKey(objectExp, key) {
320
+ return objectExp.properties.find(property => (0, _types.isObjectProperty)(property) && this.isLinguiIdentifier(property.key, key));
321
+ }
304
322
 
305
323
  /**
306
324
  * Custom matchers
307
325
  */
308
- isIdentifier = (node, name) => {
326
+ isLinguiIdentifier(node, name) {
309
327
  return this.types.isIdentifier(node, {
310
- name
328
+ name: this.nameMap.get(name) || name
311
329
  });
312
- };
313
- isDefineMessage = node => {
314
- return this.types.isCallExpression(node) && this.isIdentifier(node.callee, "defineMessage");
315
- };
316
- isArg = node => {
317
- return this.types.isCallExpression(node) && this.isIdentifier(node.callee, "arg");
318
- };
319
- isI18nMethod = node => {
320
- return this.types.isTaggedTemplateExpression(node) && (this.isIdentifier(node.tag, "t") || this.types.isCallExpression(node.tag) && this.isIdentifier(node.tag.callee, "t"));
321
- };
322
- isChoiceMethod = node => {
323
- return this.types.isCallExpression(node) && (this.isIdentifier(node.callee, "plural") || this.isIdentifier(node.callee, "select") || this.isIdentifier(node.callee, "selectOrdinal"));
324
- };
330
+ }
331
+ isDefineMessage(node) {
332
+ return this.types.isCallExpression(node) && this.isLinguiIdentifier(node.callee, "defineMessage");
333
+ }
334
+ isArg(node) {
335
+ return this.types.isCallExpression(node) && this.isLinguiIdentifier(node.callee, "arg");
336
+ }
337
+ isI18nMethod(node) {
338
+ return this.types.isTaggedTemplateExpression(node) && (this.isLinguiIdentifier(node.tag, "t") || this.types.isCallExpression(node.tag) && this.isLinguiIdentifier(node.tag.callee, "t"));
339
+ }
340
+ isChoiceMethod(node) {
341
+ return this.types.isCallExpression(node) && (this.isLinguiIdentifier(node.callee, "plural") || this.isLinguiIdentifier(node.callee, "select") || this.isLinguiIdentifier(node.callee, "selectOrdinal"));
342
+ }
343
+ getTextFromExpression(exp) {
344
+ if (this.types.isStringLiteral(exp)) {
345
+ return exp.value;
346
+ }
347
+ if (this.types.isTemplateLiteral(exp)) {
348
+ if ((exp === null || exp === void 0 ? void 0 : exp.quasis.length) === 1) {
349
+ var _exp$quasis$, _exp$quasis$$value;
350
+ return (_exp$quasis$ = exp.quasis[0]) === null || _exp$quasis$ === void 0 ? void 0 : (_exp$quasis$$value = _exp$quasis$.value) === null || _exp$quasis$$value === void 0 ? void 0 : _exp$quasis$$value.cooked;
351
+ }
352
+ }
353
+ }
325
354
  }
326
- exports.default = MacroJs;
327
- const isString = s => typeof s === "string";
355
+ exports.default = MacroJs;