UNPKG

9.44 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _path = require("path");
9
10var _helperModuleImports = require("@babel/helper-module-imports");
11
12function transCamel(_str, symbol) {
13 const str = _str[0].toLowerCase() + _str.substr(1);
14
15 return str.replace(/([A-Z])/g, $1 => `${symbol}${$1.toLowerCase()}`);
16}
17
18function winPath(path) {
19 return path.replace(/\\/g, '/');
20}
21
22function normalizeCustomName(originCustomName) {
23 // If set to a string, treat it as a JavaScript source file path.
24 if (typeof originCustomName === 'string') {
25 const customNameExports = require(originCustomName);
26
27 return typeof customNameExports === 'function' ? customNameExports : customNameExports.default;
28 }
29
30 return originCustomName;
31}
32
33class Plugin {
34 constructor(libraryName, libraryDirectory, style, styleLibraryDirectory, customStyleName, camel2DashComponentName, camel2UnderlineComponentName, fileName, customName, transformToDefaultImport, types, index = 0) {
35 this.libraryName = libraryName;
36 this.libraryDirectory = typeof libraryDirectory === 'undefined' ? 'lib' : libraryDirectory;
37 this.camel2DashComponentName = typeof camel2DashComponentName === 'undefined' ? true : camel2DashComponentName;
38 this.camel2UnderlineComponentName = camel2UnderlineComponentName;
39 this.style = style || false;
40 this.styleLibraryDirectory = styleLibraryDirectory;
41 this.customStyleName = normalizeCustomName(customStyleName);
42 this.fileName = fileName || '';
43 this.customName = normalizeCustomName(customName);
44 this.transformToDefaultImport = typeof transformToDefaultImport === 'undefined' ? true : transformToDefaultImport;
45 this.types = types;
46 this.pluginStateKey = `importPluginState${index}`;
47 }
48
49 getPluginState(state) {
50 if (!state[this.pluginStateKey]) {
51 state[this.pluginStateKey] = {}; // eslint-disable-line
52 }
53
54 return state[this.pluginStateKey];
55 }
56
57 importMethod(methodName, file, pluginState) {
58 if (!pluginState.selectedMethods[methodName]) {
59 const libraryDirectory = this.libraryDirectory;
60 const style = this.style;
61 const transformedMethodName = this.camel2UnderlineComponentName // eslint-disable-line
62 ? transCamel(methodName, '_') : this.camel2DashComponentName ? transCamel(methodName, '-') : methodName;
63 const path = winPath(this.customName ? this.customName(transformedMethodName) : (0, _path.join)(this.libraryName, libraryDirectory, transformedMethodName, this.fileName) // eslint-disable-line
64 );
65 pluginState.selectedMethods[methodName] = this.transformToDefaultImport // eslint-disable-line
66 ? (0, _helperModuleImports.addDefault)(file.path, path, {
67 nameHint: methodName
68 }) : (0, _helperModuleImports.addNamed)(file.path, methodName, path);
69
70 if (this.customStyleName) {
71 const stylePath = winPath(this.customStyleName(transformedMethodName));
72 (0, _helperModuleImports.addSideEffect)(file.path, `${stylePath}`);
73 } else if (this.styleLibraryDirectory) {
74 const stylePath = winPath((0, _path.join)(this.libraryName, this.styleLibraryDirectory, transformedMethodName, this.fileName));
75 (0, _helperModuleImports.addSideEffect)(file.path, `${stylePath}`);
76 } else if (style === true) {
77 (0, _helperModuleImports.addSideEffect)(file.path, `${path}/style`);
78 } else if (style === 'css') {
79 (0, _helperModuleImports.addSideEffect)(file.path, `${path}/style/css`);
80 } else if (typeof style === 'function') {
81 const stylePath = style(path, file);
82
83 if (stylePath) {
84 (0, _helperModuleImports.addSideEffect)(file.path, stylePath);
85 }
86 }
87 }
88
89 return Object.assign({}, pluginState.selectedMethods[methodName]);
90 }
91
92 buildExpressionHandler(node, props, path, state) {
93 const file = path && path.hub && path.hub.file || state && state.file;
94 const types = this.types;
95 const pluginState = this.getPluginState(state);
96 props.forEach(prop => {
97 if (!types.isIdentifier(node[prop])) return;
98
99 if (pluginState.specified[node[prop].name] && types.isImportSpecifier(path.scope.getBinding(node[prop].name).path)) {
100 node[prop] = this.importMethod(pluginState.specified[node[prop].name], file, pluginState); // eslint-disable-line
101 }
102 });
103 }
104
105 buildDeclaratorHandler(node, prop, path, state) {
106 const file = path && path.hub && path.hub.file || state && state.file;
107 const types = this.types;
108 const pluginState = this.getPluginState(state);
109 if (!types.isIdentifier(node[prop])) return;
110
111 if (pluginState.specified[node[prop].name] && path.scope.hasBinding(node[prop].name) && path.scope.getBinding(node[prop].name).path.type === 'ImportSpecifier') {
112 node[prop] = this.importMethod(pluginState.specified[node[prop].name], file, pluginState); // eslint-disable-line
113 }
114 }
115
116 ProgramEnter(path, state) {
117 const pluginState = this.getPluginState(state);
118 pluginState.specified = Object.create(null);
119 pluginState.libraryObjs = Object.create(null);
120 pluginState.selectedMethods = Object.create(null);
121 pluginState.pathsToRemove = [];
122 }
123
124 ProgramExit(path, state) {
125 this.getPluginState(state).pathsToRemove.forEach(p => !p.removed && p.remove());
126 }
127
128 ImportDeclaration(path, state) {
129 const node = path.node; // path maybe removed by prev instances.
130
131 if (!node) return;
132 const value = node.source.value;
133 const libraryName = this.libraryName;
134 const types = this.types;
135 const pluginState = this.getPluginState(state);
136
137 if (value === libraryName) {
138 node.specifiers.forEach(spec => {
139 if (types.isImportSpecifier(spec)) {
140 pluginState.specified[spec.local.name] = spec.imported.name;
141 } else {
142 pluginState.libraryObjs[spec.local.name] = true;
143 }
144 });
145 pluginState.pathsToRemove.push(path);
146 }
147 }
148
149 CallExpression(path, state) {
150 const node = path.node;
151 const file = path && path.hub && path.hub.file || state && state.file;
152 const name = node.callee.name;
153 const types = this.types;
154 const pluginState = this.getPluginState(state);
155
156 if (types.isIdentifier(node.callee)) {
157 if (pluginState.specified[name]) {
158 node.callee = this.importMethod(pluginState.specified[name], file, pluginState);
159 }
160 }
161
162 node.arguments = node.arguments.map(arg => {
163 const argName = arg.name;
164
165 if (pluginState.specified[argName] && path.scope.hasBinding(argName) && path.scope.getBinding(argName).path.type === 'ImportSpecifier') {
166 return this.importMethod(pluginState.specified[argName], file, pluginState);
167 }
168
169 return arg;
170 });
171 }
172
173 MemberExpression(path, state) {
174 const node = path.node;
175 const file = path && path.hub && path.hub.file || state && state.file;
176 const pluginState = this.getPluginState(state); // multiple instance check.
177
178 if (!node.object || !node.object.name) return;
179
180 if (pluginState.libraryObjs[node.object.name]) {
181 // antd.Button -> _Button
182 path.replaceWith(this.importMethod(node.property.name, file, pluginState));
183 } else if (pluginState.specified[node.object.name] && path.scope.hasBinding(node.object.name)) {
184 const scope = path.scope.getBinding(node.object.name).scope; // global variable in file scope
185
186 if (scope.path.parent.type === 'File') {
187 node.object = this.importMethod(pluginState.specified[node.object.name], file, pluginState);
188 }
189 }
190 }
191
192 Property(path, state) {
193 const node = path.node;
194 this.buildDeclaratorHandler(node, 'value', path, state);
195 }
196
197 VariableDeclarator(path, state) {
198 const node = path.node;
199 this.buildDeclaratorHandler(node, 'init', path, state);
200 }
201
202 ArrayExpression(path, state) {
203 const node = path.node;
204 const props = node.elements.map((_, index) => index);
205 this.buildExpressionHandler(node.elements, props, path, state);
206 }
207
208 LogicalExpression(path, state) {
209 const node = path.node;
210 this.buildExpressionHandler(node, ['left', 'right'], path, state);
211 }
212
213 ConditionalExpression(path, state) {
214 const node = path.node;
215 this.buildExpressionHandler(node, ['test', 'consequent', 'alternate'], path, state);
216 }
217
218 IfStatement(path, state) {
219 const node = path.node;
220 this.buildExpressionHandler(node, ['test'], path, state);
221 this.buildExpressionHandler(node.test, ['left', 'right'], path, state);
222 }
223
224 ExpressionStatement(path, state) {
225 const node = path.node;
226 const types = this.types;
227
228 if (types.isAssignmentExpression(node.expression)) {
229 this.buildExpressionHandler(node.expression, ['right'], path, state);
230 }
231 }
232
233 ReturnStatement(path, state) {
234 const node = path.node;
235 this.buildExpressionHandler(node, ['argument'], path, state);
236 }
237
238 ExportDefaultDeclaration(path, state) {
239 const node = path.node;
240 this.buildExpressionHandler(node, ['declaration'], path, state);
241 }
242
243 BinaryExpression(path, state) {
244 const node = path.node;
245 this.buildExpressionHandler(node, ['left', 'right'], path, state);
246 }
247
248 NewExpression(path, state) {
249 const node = path.node;
250 this.buildExpressionHandler(node, ['callee', 'arguments'], path, state);
251 }
252
253 ClassDeclaration(path, state) {
254 const node = path.node;
255 this.buildExpressionHandler(node, ['superClass'], path, state);
256 }
257
258}
259
260exports.default = Plugin;
\No newline at end of file