Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: import order #459

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ coverage
lib
*.log
*.log.*
.vscode
190 changes: 79 additions & 111 deletions src/Plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,33 @@ export default class Plugin {
this.pluginStateKey = `importPluginState${index}`;
}

getPluginState(state) {
if (!state[this.pluginStateKey]) {
state[this.pluginStateKey] = {}; // eslint-disable-line
transformFilename(methodName) {
// eslint-disable-next-line no-nested-ternary
return this.camel2UnderlineComponentName
? transCamel(methodName, '_')
: this.camel2DashComponentName
? transCamel(methodName, '-')
: methodName;
}

getInjectStylePath(path, file, transformedMethodName) {
let stylePath;
const { style } = this;

if (this.customStyleName) {
stylePath = winPath(this.customStyleName(transformedMethodName));
} else if (this.styleLibraryDirectory) {
stylePath = winPath(
join(this.libraryName, this.styleLibraryDirectory, transformedMethodName, this.fileName),
);
} else if (style === true) {
stylePath = `${path}/style`;
} else if (style === 'css') {
stylePath = `${path}/style/css`;
} else if (typeof style === 'function') {
stylePath = style(path, file);
}
return state[this.pluginStateKey];
return stylePath;
}

importMethod(methodName, file, pluginState) {
Expand Down Expand Up @@ -97,6 +119,13 @@ export default class Plugin {
return { ...pluginState.selectedMethods[methodName] };
}

getPluginState(state) {
if (!state[this.pluginStateKey]) {
state[this.pluginStateKey] = {}; // eslint-disable-line
}
return state[this.pluginStateKey];
}

buildExpressionHandler(node, props, path, state) {
const file = (path && path.hub && path.hub.file) || (state && state.file);
const { types } = this;
Expand Down Expand Up @@ -145,45 +174,55 @@ export default class Plugin {
if (!node) return;

const { value } = node.source;
const { libraryName } = this;
const { types } = this;
const { libraryName, libraryDirectory, types } = this;
const pluginState = this.getPluginState(state);
if (value === libraryName) {
node.specifiers.forEach(spec => {
if (types.isImportSpecifier(spec)) {
pluginState.specified[spec.local.name] = spec.imported.name;
} else {
pluginState.libraryObjs[spec.local.name] = true;
}
});
pluginState.pathsToRemove.push(path);
}
}

CallExpression(path, state) {
const { node } = path;
const file = (path && path.hub && path.hub.file) || (state && state.file);
const { name } = node.callee;
const { types } = this;
const pluginState = this.getPluginState(state);

if (types.isIdentifier(node.callee)) {
if (pluginState.specified[name]) {
node.callee = this.importMethod(pluginState.specified[name], file, pluginState);
}
const newImports = node.specifiers
.map(item => {
/**
* LibraryObjs for @MemberExpression shaking, like
* import antd from 'antd'
* <antd.Button />
*
* to
*
* var Button = require("antd/lib/button")
* <Button />
*/
if (!types.isImportSpecifier(item)) {
pluginState.libraryObjs[item.local.name] = true;
return false;
}

const transformedMethodName = this.transformFilename(
types.isImportSpecifier(item) ? item.imported.name : item.local.name,
);

const file = (path && path.hub && path.hub.file) || (state && state.file);
const importPath = winPath(
this.customName
? this.customName(transformedMethodName, file)
: join(this.libraryName, libraryDirectory, transformedMethodName, this.fileName), // eslint-disable-line
);
const stylePath = this.getInjectStylePath(importPath, file, transformedMethodName);
// eslint-disable-next-line new-cap
return [
stylePath ? types.ImportDeclaration([], types.StringLiteral(stylePath)) : false,
types.ImportDeclaration(
[
this.transformToDefaultImport
? types.importDefaultSpecifier(item.local)
: types.ImportNamespaceSpecifier(item.local),
],
// eslint-disable-next-line new-cap
types.StringLiteral(importPath),
),
];
})
.reduce((acc, val) => acc.concat(val), [])
.filter(Boolean);
path.replaceWithMultiple(newImports);
}

node.arguments = node.arguments.map(arg => {
const { name: argName } = arg;
if (
pluginState.specified[argName] &&
path.scope.hasBinding(argName) &&
path.scope.getBinding(argName).path.type === 'ImportSpecifier'
) {
return this.importMethod(pluginState.specified[argName], file, pluginState);
}
return arg;
});
}

MemberExpression(path, state) {
Expand All @@ -197,77 +236,6 @@ export default class Plugin {
if (pluginState.libraryObjs[node.object.name]) {
// antd.Button -> _Button
path.replaceWith(this.importMethod(node.property.name, file, pluginState));
} else if (pluginState.specified[node.object.name] && path.scope.hasBinding(node.object.name)) {
const { scope } = path.scope.getBinding(node.object.name);
// global variable in file scope
if (scope.path.parent.type === 'File') {
node.object = this.importMethod(pluginState.specified[node.object.name], file, pluginState);
}
}
}

Property(path, state) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些为啥删了?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

统一都在 ImportDeclaration 处理掉了

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那就是按 import 来算,而不是按使用来算,那和之前的逻辑不一样了吧,比如 import * as antd from antd, <antd.Button /> 这种逻辑还能覆盖吗?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这种 import 在 ImportDeclaration 确实处理不到, 但是属于 MemberExpression,所以 MemberExpression 保留了

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

只有 MemberExpression 不够,之前加的东西都是有场景用上的。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我觉得解这个问题的思路应该是记录删除的 import 的位置,然后再在相应的位置加回去。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以前的思路是找到使用的地方然后 remove -> unshift,现在 babel 提供了 replace refactor 的工具函数 replaceWithMultiple ,改掉 import 那么作用域的内的都会替换

只有 MemberExpression 不够,之前加的东西都是有场景用上的。

跑完测试感觉是没什么大问题的,可能以前的一些修改没加测试用例就不好说

const { node } = path;
this.buildDeclaratorHandler(node, 'value', path, state);
}

VariableDeclarator(path, state) {
const { node } = path;
this.buildDeclaratorHandler(node, 'init', path, state);
}

ArrayExpression(path, state) {
const { node } = path;
const props = node.elements.map((_, index) => index);
this.buildExpressionHandler(node.elements, props, path, state);
}

LogicalExpression(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['left', 'right'], path, state);
}

ConditionalExpression(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['test', 'consequent', 'alternate'], path, state);
}

IfStatement(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['test'], path, state);
this.buildExpressionHandler(node.test, ['left', 'right'], path, state);
}

ExpressionStatement(path, state) {
const { node } = path;
const { types } = this;
if (types.isAssignmentExpression(node.expression)) {
this.buildExpressionHandler(node.expression, ['right'], path, state);
}
}

ReturnStatement(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['argument'], path, state);
}

ExportDefaultDeclaration(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['declaration'], path, state);
}

BinaryExpression(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['left', 'right'], path, state);
}

NewExpression(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['callee', 'arguments'], path, state);
}

ClassDeclaration(path, state) {
const { node } = path;
this.buildExpressionHandler(node, ['superClass'], path, state);
}
}
8 changes: 4 additions & 4 deletions test/fixtures/custom-style-path-ignore/expected.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"use strict";

var _react = _interopRequireDefault(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

var _animation = _interopRequireDefault(require("antd/lib/animation"));

require("antd/lib/button/style/2x");

var _button = _interopRequireDefault(require("antd/lib/button"));

var _react = _interopRequireDefault(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

ReactDOM.render(_react.default.createElement(_animation.default, null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById("react-container"));
10 changes: 5 additions & 5 deletions test/fixtures/custom-style-path/expected.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use strict";

require("antd/lib/button/style/2x");

var _button = _interopRequireDefault(require("antd/lib/button"));

var _react = _interopRequireDefault(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

require("antd/lib/button/style/2x");

var _button = _interopRequireDefault(require("antd/lib/button"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById('react-container'));
ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById('react-container'));
4 changes: 2 additions & 2 deletions test/fixtures/execute-direct/expected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

var _message2 = _interopRequireDefault(require("antd/lib/message"));
var _message = _interopRequireDefault(require("antd/lib/message"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _message2.default)('xxx');
(0, _message.default)('xxx');
4 changes: 2 additions & 2 deletions test/fixtures/execute-member/expected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

var _message2 = _interopRequireDefault(require("antd/lib/message"));
var _message = _interopRequireDefault(require("antd/lib/message"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

_message2.default.success('xxx');
_message.default.success('xxx');
12 changes: 6 additions & 6 deletions test/fixtures/import-css/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

var _react = _interopRequireDefault(require("react"));

require("antd/lib/button/style");
require("antd/lib/message/style");

var _button = _interopRequireDefault(require("antd/lib/button"));
var _message = _interopRequireDefault(require("antd/lib/message"));

require("antd/lib/message/style");
require("antd/lib/button/style");

var _message2 = _interopRequireDefault(require("antd/lib/message"));
var _button = _interopRequireDefault(require("antd/lib/button"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _message2.default)('xxx');
ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")));
(0, _message.default)('xxx');
ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")));
4 changes: 4 additions & 0 deletions test/fixtures/import-order/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import 'polyfill';
import { Button } from 'antd';

console.log(Button);
9 changes: 9 additions & 0 deletions test/fixtures/import-order/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";

require("polyfill");

var _button = _interopRequireDefault(require("antd/lib/button"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

console.log(_button.default);
12 changes: 8 additions & 4 deletions test/fixtures/keep-named-import/expected.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"use strict";

var _end2 = require("stream/lib/end");
var start = _interopRequireWildcard(require("stream/lib/start"));

var _start2 = require("stream/lib/start");
var end = _interopRequireWildcard(require("stream/lib/end"));

(0, _start2.start)();
(0, _end2.end)();
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }

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; }

start();
end();
4 changes: 2 additions & 2 deletions test/fixtures/material-ui/expected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

var _Toolbar2 = _interopRequireDefault(require("material-ui/Toolbar"));
var _Toolbar = _interopRequireDefault(require("material-ui/Toolbar"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _Toolbar2.default)('xxx');
(0, _Toolbar.default)('xxx');
8 changes: 4 additions & 4 deletions test/fixtures/modules-false/expected.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use strict";

require("antd/lib/button/style");

var _button = _interopRequireDefault(require("antd/lib/button"));

var _react = _interopRequireDefault(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

require("antd/lib/button/style");

var _button = _interopRequireDefault(require("antd/lib/button"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById('react-container'));
6 changes: 3 additions & 3 deletions test/fixtures/multiple-libraries-hilojs/expected.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use strict";

var _select = _interopRequireDefault(require("antd/lib/select"));

var _abc = _interopRequireDefault(require("hilojs/abc"));

var _class = _interopRequireDefault(require("hilojs/core/class"));

var _select = _interopRequireDefault(require("antd/lib/select"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

if (_select.default) {}

if (_class.default && _abc.default) {}
if (_class.default && _abc.default) {}
Loading