1 | const { dirname } = require('path');
|
2 | const readPkgUp = require('read-pkg-up');
|
3 |
|
4 | const DEFINE_MESSAGES = 'defineMessages';
|
5 |
|
6 | const COMPONENT_NAMES = ['FormattedMessage', 'FormattedHTMLMessage'];
|
7 |
|
8 | function getPrefix(state) {
|
9 | let { prefix } = state.opts;
|
10 | if (prefix && !prefix.endsWith(':')) prefix = `${prefix}:`;
|
11 | return prefix;
|
12 | }
|
13 |
|
14 | function referencesImport(path) {
|
15 | if (!(path.isIdentifier() || path.isJSXIdentifier())) return false;
|
16 | return COMPONENT_NAMES.some(name =>
|
17 | path.referencesImport('react-intl', name),
|
18 | );
|
19 | }
|
20 |
|
21 | const PREFIXES = new Map();
|
22 |
|
23 | const PREFIX = Symbol('namespace prefix');
|
24 |
|
25 | function getPrefixFromPackage(filename) {
|
26 | for (const [root, prefix] of PREFIXES.entries()) {
|
27 | if (filename.startsWith(root)) {
|
28 | return prefix;
|
29 | }
|
30 | }
|
31 |
|
32 | const pkgUpResult = readPkgUp.sync({ cwd: dirname(filename) });
|
33 | if (!pkgUpResult) return '';
|
34 |
|
35 | const prefix = `${pkgUpResult.packageJson.name}:`;
|
36 | PREFIXES.set(dirname(pkgUpResult.path), prefix);
|
37 | return prefix;
|
38 | }
|
39 |
|
40 | function getMessagesObjectFromExpression(nodePath) {
|
41 | let currentPath = nodePath;
|
42 | while (
|
43 | currentPath.isTSAsExpression() ||
|
44 | currentPath.isTSTypeAssertion() ||
|
45 | currentPath.isTypeCastExpression()
|
46 | ) {
|
47 | currentPath = currentPath.get('expression');
|
48 | }
|
49 | return currentPath;
|
50 | }
|
51 |
|
52 | function isFormatMessageCall(callee) {
|
53 | if (!callee.isMemberExpression()) {
|
54 | return false;
|
55 | }
|
56 | const object = callee.get('object');
|
57 | const property = callee.get('property');
|
58 |
|
59 | return (
|
60 | property.isIdentifier() &&
|
61 | property.node.name === 'formatMessage' &&
|
62 |
|
63 | ((object.isIdentifier() && object.node.name === 'intl') ||
|
64 |
|
65 | (object.isMemberExpression() &&
|
66 | object.get('property').node.name === 'intl'))
|
67 | );
|
68 | }
|
69 |
|
70 | module.exports = function namespacePlugin({ types: t }) {
|
71 | return {
|
72 | pre(file) {
|
73 | const prefix =
|
74 | getPrefix(this) || getPrefixFromPackage(file.opts.filename);
|
75 |
|
76 | file.set(PREFIX, prefix);
|
77 | },
|
78 | visitor: {
|
79 | JSXOpeningElement(path, state) {
|
80 | const name = path.get('name');
|
81 | if (!referencesImport(name)) return;
|
82 |
|
83 | const prefix = state.file.get(PREFIX);
|
84 |
|
85 | const idAttr = path
|
86 | .get('attributes')
|
87 | .find(attr => attr.isJSXAttribute() && attr.node.name.name === 'id');
|
88 |
|
89 | if (idAttr && !idAttr.node.value.value.startsWith(prefix)) {
|
90 | idAttr
|
91 | .get('value')
|
92 | .replaceWith(
|
93 | t.StringLiteral(`${prefix}${idAttr.node.value.value}`),
|
94 | );
|
95 | }
|
96 | },
|
97 |
|
98 | CallExpression(path, state) {
|
99 | const prefix = state.file.get(PREFIX);
|
100 | const callee = path.get('callee');
|
101 |
|
102 | function processMessageObject(messageObj) {
|
103 | if (!messageObj || !messageObj.isObjectExpression()) {
|
104 | return;
|
105 | }
|
106 |
|
107 | const idProp = messageObj.get('properties').find(p => {
|
108 |
|
109 |
|
110 | const keyNode = p.get('key').node;
|
111 | return keyNode.name === 'id' || keyNode.value === 'id';
|
112 | });
|
113 |
|
114 | const value = idProp && idProp.get('value');
|
115 |
|
116 | if (value && !value.node.value.startsWith(prefix)) {
|
117 | value.replaceWith(
|
118 | t.StringLiteral(`${prefix}${value.node.value}`),
|
119 | );
|
120 | }
|
121 | }
|
122 |
|
123 | if (callee.isIdentifier() && callee.node.name === DEFINE_MESSAGES) {
|
124 | getMessagesObjectFromExpression(path.get('arguments')[0])
|
125 | .get('properties')
|
126 | .map(prop => prop.get('value'))
|
127 | .forEach(processMessageObject);
|
128 | } else if (isFormatMessageCall(callee)) {
|
129 | const messageDescriptor = getMessagesObjectFromExpression(
|
130 | path.get('arguments')[0],
|
131 | );
|
132 |
|
133 | if (messageDescriptor.isObjectExpression()) {
|
134 | processMessageObject(messageDescriptor);
|
135 | }
|
136 | }
|
137 | },
|
138 | },
|
139 | };
|
140 | };
|