@lingui/macro 3.14.0 → 3.16.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/CHANGELOG.md +406 -0
- package/build/LICENSE +21 -0
- package/{constants.js → build/constants.js} +5 -5
- package/{global.d.ts → build/global.d.ts} +0 -0
- package/build/icu.js +113 -0
- package/{index.d.ts → build/index.d.ts} +1 -0
- package/build/index.js +158 -0
- package/build/macroJs.js +345 -0
- package/build/macroJsx.js +328 -0
- package/build/utils.js +25 -0
- package/package.json +11 -9
- package/icu.js +0 -118
- package/index.js +0 -168
- package/macroJs.js +0 -384
- package/macroJsx.js +0 -360
- package/utils.js +0 -30
package/build/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _babelPluginMacros = require("babel-plugin-macros");
|
|
9
|
+
|
|
10
|
+
var _conf = require("@lingui/conf");
|
|
11
|
+
|
|
12
|
+
var _macroJs = _interopRequireDefault(require("./macroJs"));
|
|
13
|
+
|
|
14
|
+
var _macroJsx = _interopRequireDefault(require("./macroJsx"));
|
|
15
|
+
|
|
16
|
+
var _types = require("@babel/types");
|
|
17
|
+
|
|
18
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
+
|
|
20
|
+
const config = (0, _conf.getConfig)({
|
|
21
|
+
configPath: process.env.LINGUI_CONFIG
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const getSymbolSource = name => {
|
|
25
|
+
if (Array.isArray(config.runtimeConfigModule)) {
|
|
26
|
+
if (name === "i18n") {
|
|
27
|
+
return config.runtimeConfigModule;
|
|
28
|
+
} else {
|
|
29
|
+
return ["@lingui/react", name];
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
if (config.runtimeConfigModule[name]) {
|
|
33
|
+
return config.runtimeConfigModule[name];
|
|
34
|
+
} else {
|
|
35
|
+
return ["@lingui/react", name];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const [i18nImportModule, i18nImportName = "i18n"] = getSymbolSource("i18n");
|
|
41
|
+
const [TransImportModule, TransImportName = "Trans"] = getSymbolSource("Trans");
|
|
42
|
+
|
|
43
|
+
function macro({
|
|
44
|
+
references,
|
|
45
|
+
state,
|
|
46
|
+
babel
|
|
47
|
+
}) {
|
|
48
|
+
const jsxNodes = [];
|
|
49
|
+
const jsNodes = [];
|
|
50
|
+
let needsI18nImport = false;
|
|
51
|
+
Object.keys(references).forEach(tagName => {
|
|
52
|
+
const nodes = references[tagName];
|
|
53
|
+
const macroType = getMacroType(tagName);
|
|
54
|
+
|
|
55
|
+
if (macroType == null) {
|
|
56
|
+
throw nodes[0].buildCodeFrameError(`Unknown macro ${tagName}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (macroType === "js") {
|
|
60
|
+
nodes.forEach(node => {
|
|
61
|
+
jsNodes.push(node.parentPath);
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
nodes.forEach(node => {
|
|
65
|
+
// identifier.openingElement.jsxElement
|
|
66
|
+
jsxNodes.push(node.parentPath.parentPath);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
jsNodes.filter(isRootPath(jsNodes)).forEach(path => {
|
|
71
|
+
if (alreadyVisited(path)) return;
|
|
72
|
+
const macro = new _macroJs.default(babel, {
|
|
73
|
+
i18nImportName
|
|
74
|
+
});
|
|
75
|
+
if (macro.replacePath(path)) needsI18nImport = true;
|
|
76
|
+
});
|
|
77
|
+
jsxNodes.filter(isRootPath(jsxNodes)).forEach(path => {
|
|
78
|
+
if (alreadyVisited(path)) return;
|
|
79
|
+
const macro = new _macroJsx.default(babel);
|
|
80
|
+
macro.replacePath(path);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (needsI18nImport) {
|
|
84
|
+
addImport(babel, state, i18nImportModule, i18nImportName);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (jsxNodes.length) {
|
|
88
|
+
addImport(babel, state, TransImportModule, TransImportName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (process.env.LINGUI_EXTRACT === "1") {
|
|
92
|
+
return {
|
|
93
|
+
keepImports: true
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function addImport(babel, state, module, importName) {
|
|
99
|
+
const {
|
|
100
|
+
types: t
|
|
101
|
+
} = babel;
|
|
102
|
+
const linguiImport = state.file.path.node.body.find(importNode => t.isImportDeclaration(importNode) && importNode.source.value === module && // https://github.com/lingui/js-lingui/issues/777
|
|
103
|
+
importNode.importKind !== "type");
|
|
104
|
+
const tIdentifier = t.identifier(importName); // Handle adding the import or altering the existing import
|
|
105
|
+
|
|
106
|
+
if (linguiImport) {
|
|
107
|
+
if (linguiImport.specifiers.findIndex(specifier => (0, _types.isImportSpecifier)(specifier) && (0, _types.isIdentifier)(specifier.imported, {
|
|
108
|
+
name: importName
|
|
109
|
+
})) === -1) {
|
|
110
|
+
linguiImport.specifiers.push(t.importSpecifier(tIdentifier, tIdentifier));
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
state.file.path.node.body.unshift(t.importDeclaration([t.importSpecifier(tIdentifier, tIdentifier)], t.stringLiteral(module)));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isRootPath(allPath) {
|
|
118
|
+
return node => function traverse(path) {
|
|
119
|
+
if (!path.parentPath) {
|
|
120
|
+
return true;
|
|
121
|
+
} else {
|
|
122
|
+
return !allPath.includes(path.parentPath) && traverse(path.parentPath);
|
|
123
|
+
}
|
|
124
|
+
}(node);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const alreadyVisitedCache = new WeakSet();
|
|
128
|
+
|
|
129
|
+
const alreadyVisited = path => {
|
|
130
|
+
if (alreadyVisitedCache.has(path)) {
|
|
131
|
+
return true;
|
|
132
|
+
} else {
|
|
133
|
+
alreadyVisitedCache.add(path);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
function getMacroType(tagName) {
|
|
139
|
+
switch (tagName) {
|
|
140
|
+
case "defineMessage":
|
|
141
|
+
case "arg":
|
|
142
|
+
case "t":
|
|
143
|
+
case "plural":
|
|
144
|
+
case "select":
|
|
145
|
+
case "selectOrdinal":
|
|
146
|
+
return "js";
|
|
147
|
+
|
|
148
|
+
case "Trans":
|
|
149
|
+
case "Plural":
|
|
150
|
+
case "Select":
|
|
151
|
+
case "SelectOrdinal":
|
|
152
|
+
return "jsx";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
var _default = (0, _babelPluginMacros.createMacro)(macro);
|
|
157
|
+
|
|
158
|
+
exports.default = _default;
|
package/build/macroJs.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
9
|
+
|
|
10
|
+
var _types = require("@babel/types");
|
|
11
|
+
|
|
12
|
+
var _icu = _interopRequireDefault(require("./icu"));
|
|
13
|
+
|
|
14
|
+
var _utils = require("./utils");
|
|
15
|
+
|
|
16
|
+
var _constants = require("./constants");
|
|
17
|
+
|
|
18
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
+
|
|
20
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
21
|
+
|
|
22
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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; }
|
|
23
|
+
|
|
24
|
+
const keepSpaceRe = /(?:\\(?:\r\n|\r|\n))+\s+/g;
|
|
25
|
+
const keepNewLineRe = /(?:\r\n|\r|\n)+\s+/g;
|
|
26
|
+
|
|
27
|
+
function normalizeWhitespace(text) {
|
|
28
|
+
return text.replace(keepSpaceRe, " ").replace(keepNewLineRe, "\n").trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class MacroJs {
|
|
32
|
+
// Babel Types
|
|
33
|
+
// Identifier of i18n object
|
|
34
|
+
// Positional expressions counter (e.g. for placeholders `Hello {0}, today is {1}`)
|
|
35
|
+
_expressionIndex = (0, _utils.makeCounter)();
|
|
36
|
+
|
|
37
|
+
constructor({
|
|
38
|
+
types
|
|
39
|
+
}, {
|
|
40
|
+
i18nImportName
|
|
41
|
+
}) {
|
|
42
|
+
this.types = types;
|
|
43
|
+
this.i18nImportName = i18nImportName;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
replacePathWithMessage = (path, {
|
|
47
|
+
message,
|
|
48
|
+
values
|
|
49
|
+
}, linguiInstance) => {
|
|
50
|
+
const args = [];
|
|
51
|
+
args.push(isString(message) ? this.types.stringLiteral(message) : message);
|
|
52
|
+
|
|
53
|
+
if (Object.keys(values).length) {
|
|
54
|
+
const valuesObject = Object.keys(values).map(key => this.types.objectProperty(this.types.identifier(key), values[key]));
|
|
55
|
+
args.push(this.types.objectExpression(valuesObject));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const newNode = this.types.callExpression(this.types.memberExpression(linguiInstance ?? this.types.identifier(this.i18nImportName), this.types.identifier("_")), args); // preserve line number
|
|
59
|
+
|
|
60
|
+
newNode.loc = path.node.loc;
|
|
61
|
+
path.addComment("leading", _constants.EXTRACT_MARK);
|
|
62
|
+
path.replaceWith(newNode);
|
|
63
|
+
}; // Returns a boolean indicating if the replacement requires i18n import
|
|
64
|
+
|
|
65
|
+
replacePath = path => {
|
|
66
|
+
// reset the expression counter
|
|
67
|
+
this._expressionIndex = (0, _utils.makeCounter)();
|
|
68
|
+
|
|
69
|
+
if (this.isDefineMessage(path.node)) {
|
|
70
|
+
this.replaceDefineMessage(path);
|
|
71
|
+
return true;
|
|
72
|
+
} // t(i18nInstance)`Message` -> i18nInstance._('Message')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
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")) {
|
|
76
|
+
// Use the first argument as i18n instance instead of the default i18n instance
|
|
77
|
+
const i18nInstance = path.node.arguments[0];
|
|
78
|
+
const tokens = this.tokenizeNode(path.parentPath.node);
|
|
79
|
+
const messageFormat = new _icu.default();
|
|
80
|
+
const {
|
|
81
|
+
message: messageRaw,
|
|
82
|
+
values
|
|
83
|
+
} = messageFormat.fromTokens(tokens);
|
|
84
|
+
const message = normalizeWhitespace(messageRaw);
|
|
85
|
+
this.replacePathWithMessage(path.parentPath, {
|
|
86
|
+
message,
|
|
87
|
+
values
|
|
88
|
+
}, i18nInstance);
|
|
89
|
+
return false;
|
|
90
|
+
} // t(i18nInstance)(messageDescriptor) -> i18nInstance._(messageDescriptor)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
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")) {
|
|
94
|
+
const i18nInstance = path.node.arguments[0];
|
|
95
|
+
this.replaceTAsFunction(path.parentPath, i18nInstance);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.types.isCallExpression(path.node) && this.isIdentifier(path.node.callee, "t")) {
|
|
100
|
+
this.replaceTAsFunction(path);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const tokens = this.tokenizeNode(path.node);
|
|
105
|
+
const messageFormat = new _icu.default();
|
|
106
|
+
const {
|
|
107
|
+
message: messageRaw,
|
|
108
|
+
values
|
|
109
|
+
} = messageFormat.fromTokens(tokens);
|
|
110
|
+
const message = normalizeWhitespace(messageRaw);
|
|
111
|
+
this.replacePathWithMessage(path, {
|
|
112
|
+
message,
|
|
113
|
+
values
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* macro `defineMessage` is called with MessageDescriptor. The only
|
|
119
|
+
* thing that happens is that any macros used in `message` property
|
|
120
|
+
* are replaced with formatted message.
|
|
121
|
+
*
|
|
122
|
+
* import { defineMessage, plural } from '@lingui/macro';
|
|
123
|
+
* const message = defineMessage({
|
|
124
|
+
* id: "msg.id",
|
|
125
|
+
* comment: "Description",
|
|
126
|
+
* message: plural(value, { one: "book", other: "books" })
|
|
127
|
+
* })
|
|
128
|
+
*
|
|
129
|
+
* ↓ ↓ ↓ ↓ ↓ ↓
|
|
130
|
+
*
|
|
131
|
+
* const message = {
|
|
132
|
+
* id: "msg.id",
|
|
133
|
+
* comment: "Description",
|
|
134
|
+
* message: "{value, plural, one {book} other {books}}"
|
|
135
|
+
* }
|
|
136
|
+
*
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
replaceDefineMessage = path => {
|
|
140
|
+
// reset the expression counter
|
|
141
|
+
this._expressionIndex = (0, _utils.makeCounter)();
|
|
142
|
+
const descriptor = this.processDescriptor(path.node.arguments[0]);
|
|
143
|
+
path.replaceWith(descriptor);
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* macro `t` is called with MessageDescriptor, after that
|
|
147
|
+
* we create a new node to append it to i18n._
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
replaceTAsFunction = (path, linguiInstance) => {
|
|
151
|
+
const descriptor = this.processDescriptor(path.node.arguments[0]);
|
|
152
|
+
const newNode = this.types.callExpression(this.types.memberExpression(linguiInstance ?? this.types.identifier(this.i18nImportName), this.types.identifier("_")), [descriptor]);
|
|
153
|
+
path.replaceWith(newNode);
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* `processDescriptor` expand macros inside message descriptor.
|
|
157
|
+
* Message descriptor is used in `defineMessage`.
|
|
158
|
+
*
|
|
159
|
+
* {
|
|
160
|
+
* comment: "Description",
|
|
161
|
+
* message: plural("value", { one: "book", other: "books" })
|
|
162
|
+
* }
|
|
163
|
+
*
|
|
164
|
+
* ↓ ↓ ↓ ↓ ↓ ↓
|
|
165
|
+
*
|
|
166
|
+
* {
|
|
167
|
+
* comment: "Description",
|
|
168
|
+
* id: "{value, plural, one {book} other {books}}"
|
|
169
|
+
* }
|
|
170
|
+
*
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
processDescriptor = descriptor_ => {
|
|
174
|
+
const descriptor = descriptor_;
|
|
175
|
+
this.types.addComment(descriptor, "leading", _constants.EXTRACT_MARK);
|
|
176
|
+
const messageIndex = descriptor.properties.findIndex(property => (0, _types.isObjectProperty)(property) && this.isIdentifier(property.key, _constants.MESSAGE));
|
|
177
|
+
|
|
178
|
+
if (messageIndex === -1) {
|
|
179
|
+
return descriptor;
|
|
180
|
+
} // if there's `message` property, replace macros with formatted message
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
const node = descriptor.properties[messageIndex]; // Inside message descriptor the `t` macro in `message` prop is optional.
|
|
184
|
+
// Template strings are always processed as if they were wrapped by `t`.
|
|
185
|
+
|
|
186
|
+
const tokens = this.types.isTemplateLiteral(node.value) ? this.tokenizeTemplateLiteral(node.value) : this.tokenizeNode(node.value, true);
|
|
187
|
+
let messageNode = node.value;
|
|
188
|
+
|
|
189
|
+
if (tokens != null) {
|
|
190
|
+
const messageFormat = new _icu.default();
|
|
191
|
+
const {
|
|
192
|
+
message: messageRaw,
|
|
193
|
+
values
|
|
194
|
+
} = messageFormat.fromTokens(tokens);
|
|
195
|
+
const message = normalizeWhitespace(messageRaw);
|
|
196
|
+
messageNode = this.types.stringLiteral(message);
|
|
197
|
+
this.addValues(descriptor.properties, values);
|
|
198
|
+
} // Don't override custom ID
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
const hasId = descriptor.properties.findIndex(property => (0, _types.isObjectProperty)(property) && this.isIdentifier(property.key, _constants.ID)) !== -1;
|
|
202
|
+
descriptor.properties[messageIndex] = this.types.objectProperty(this.types.identifier(hasId ? _constants.MESSAGE : _constants.ID), messageNode);
|
|
203
|
+
return descriptor;
|
|
204
|
+
};
|
|
205
|
+
addValues = (obj, values) => {
|
|
206
|
+
const valuesObject = Object.keys(values).map(key => this.types.objectProperty(this.types.identifier(key), values[key]));
|
|
207
|
+
if (!valuesObject.length) return;
|
|
208
|
+
obj.push(this.types.objectProperty(this.types.identifier("values"), this.types.objectExpression(valuesObject)));
|
|
209
|
+
};
|
|
210
|
+
tokenizeNode = (node, ignoreExpression = false) => {
|
|
211
|
+
if (this.isI18nMethod(node)) {
|
|
212
|
+
// t
|
|
213
|
+
return this.tokenizeTemplateLiteral(node);
|
|
214
|
+
} else if (this.isChoiceMethod(node)) {
|
|
215
|
+
// plural, select and selectOrdinal
|
|
216
|
+
return [this.tokenizeChoiceComponent(node)]; // } else if (isFormatMethod(node.callee)) {
|
|
217
|
+
// // date, number
|
|
218
|
+
// return transformFormatMethod(node, file, props, root)
|
|
219
|
+
} else if (!ignoreExpression) {
|
|
220
|
+
return this.tokenizeExpression(node);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* `node` is a TemplateLiteral. node.quasi contains
|
|
225
|
+
* text chunks and node.expressions contains expressions.
|
|
226
|
+
* Both arrays must be zipped together to get the final list of tokens.
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
tokenizeTemplateLiteral = node => {
|
|
230
|
+
const tokenize = R.pipe(R.evolve({
|
|
231
|
+
quasis: R.map(text => {
|
|
232
|
+
// Don't output tokens without text.
|
|
233
|
+
// if it's an unicode we keep the cooked value because it's the parsed value by babel (without unicode chars)
|
|
234
|
+
// This regex will detect if a string contains unicode chars, when they're we should interpolate them
|
|
235
|
+
// why? because platforms like react native doesn't parse them, just doing a JSON.parse makes them UTF-8 friendly
|
|
236
|
+
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;
|
|
237
|
+
if (value === "") return null;
|
|
238
|
+
return {
|
|
239
|
+
type: "text",
|
|
240
|
+
value: this.clearBackslashes(value)
|
|
241
|
+
};
|
|
242
|
+
}),
|
|
243
|
+
expressions: R.map(exp => this.types.isCallExpression(exp) ? this.tokenizeNode(exp) : this.tokenizeExpression(exp))
|
|
244
|
+
}), exp => (0, _utils.zip)(exp.quasis, exp.expressions), R.flatten, R.filter(Boolean));
|
|
245
|
+
return tokenize(this.types.isTaggedTemplateExpression(node) ? node.quasi : node);
|
|
246
|
+
};
|
|
247
|
+
tokenizeChoiceComponent = node => {
|
|
248
|
+
const format = node.callee.name.toLowerCase();
|
|
249
|
+
const token = { ...this.tokenizeExpression(node.arguments[0]),
|
|
250
|
+
format,
|
|
251
|
+
options: {
|
|
252
|
+
offset: undefined
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const props = node.arguments[1].properties;
|
|
256
|
+
|
|
257
|
+
for (const attr of props) {
|
|
258
|
+
const {
|
|
259
|
+
key,
|
|
260
|
+
value: attrValue
|
|
261
|
+
} = attr; // name is either:
|
|
262
|
+
// NumericLiteral => convert to `={number}`
|
|
263
|
+
// StringLiteral => key.value
|
|
264
|
+
// Identifier => key.name
|
|
265
|
+
|
|
266
|
+
const name = this.types.isNumericLiteral(key) ? `=${key.value}` : key.name || key.value;
|
|
267
|
+
|
|
268
|
+
if (format !== "select" && name === "offset") {
|
|
269
|
+
token.options.offset = attrValue.value;
|
|
270
|
+
} else {
|
|
271
|
+
let value;
|
|
272
|
+
|
|
273
|
+
if (this.types.isTemplateLiteral(attrValue)) {
|
|
274
|
+
value = this.tokenizeTemplateLiteral(attrValue);
|
|
275
|
+
} else if (this.types.isCallExpression(attrValue)) {
|
|
276
|
+
value = this.tokenizeNode(attrValue);
|
|
277
|
+
} else {
|
|
278
|
+
value = attrValue.value;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
token.options[name] = value;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return token;
|
|
286
|
+
};
|
|
287
|
+
tokenizeExpression = node => {
|
|
288
|
+
if (this.isArg(node) && this.types.isCallExpression(node)) {
|
|
289
|
+
return {
|
|
290
|
+
type: "arg",
|
|
291
|
+
name: node.arguments[0].value,
|
|
292
|
+
value: undefined
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
type: "arg",
|
|
298
|
+
name: this.expressionToArgument(node),
|
|
299
|
+
value: node
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
expressionToArgument = exp => {
|
|
303
|
+
if (this.types.isIdentifier(exp)) {
|
|
304
|
+
return exp.name;
|
|
305
|
+
} else if (this.types.isStringLiteral(exp)) {
|
|
306
|
+
return exp.value;
|
|
307
|
+
} else {
|
|
308
|
+
return String(this._expressionIndex());
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* We clean '//\` ' to just '`'
|
|
313
|
+
*/
|
|
314
|
+
|
|
315
|
+
clearBackslashes(value) {
|
|
316
|
+
// if not we replace the extra scaped literals
|
|
317
|
+
return value.replace(/\\`/g, "`");
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Custom matchers
|
|
321
|
+
*/
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
isIdentifier = (node, name) => {
|
|
325
|
+
return this.types.isIdentifier(node, {
|
|
326
|
+
name
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
isDefineMessage = node => {
|
|
330
|
+
return this.types.isCallExpression(node) && this.isIdentifier(node.callee, "defineMessage");
|
|
331
|
+
};
|
|
332
|
+
isArg = node => {
|
|
333
|
+
return this.types.isCallExpression(node) && this.isIdentifier(node.callee, "arg");
|
|
334
|
+
};
|
|
335
|
+
isI18nMethod = node => {
|
|
336
|
+
return this.types.isTaggedTemplateExpression(node) && (this.isIdentifier(node.tag, "t") || this.types.isCallExpression(node.tag) && this.isIdentifier(node.tag.callee, "t"));
|
|
337
|
+
};
|
|
338
|
+
isChoiceMethod = node => {
|
|
339
|
+
return this.types.isCallExpression(node) && (this.isIdentifier(node.callee, "plural") || this.isIdentifier(node.callee, "select") || this.isIdentifier(node.callee, "selectOrdinal"));
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
exports.default = MacroJs;
|
|
344
|
+
|
|
345
|
+
const isString = s => typeof s === "string";
|