babel-helper-decorate-react
Version:
Babel Helper for custom decorator for React Component
308 lines (307 loc) • 14.6 kB
JavaScript
;
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultReactFunctionCallTokens = exports.defaultReactClassSuperTokens = exports.defaultReactClassCallTokens = exports.defaultReactClassMemberTokens = exports.defaultReactClassMethodsTokens = void 0;
const createDecorateVisitor_1 = require("./createDecorateVisitor");
const utils_1 = require("./utils");
const t = require("@babel/types");
const isMemberExpression = (path, name) => {
if (typeof name === 'string') {
return String(path) === name;
}
if (typeof name === 'function') {
return name(path);
}
return name.test(String(path));
};
exports.defaultReactClassMethodsTokens = [
'componentDidUpdate',
'componentDidCatch',
'componentDidMount',
'componentWillMount',
'componentWillReceiveProps',
'componentWillUnmount',
'componentWillUpdate',
'UNSAFE_componentWillMount',
'UNSAFE_componentWillReceiveProps',
'UNSAFE_componentWillUpdate',
'getSnapshotBeforeUpdate',
'shouldComponentUpdate',
'render'
];
exports.defaultReactClassMemberTokens = [];
exports.defaultReactClassCallTokens = [];
exports.defaultReactClassSuperTokens = [];
['React.Profiler', 'React.Suspense', 'React.StrictMode', 'React.Fragment'].forEach((name) => {
exports.defaultReactClassMemberTokens.push(name);
exports.defaultReactClassMemberTokens.push(name.split('.')[1]);
});
['React.Component', 'React.PureComponent'].forEach((name) => {
exports.defaultReactClassSuperTokens.push(name);
exports.defaultReactClassSuperTokens.push(name.split('.')[1]);
});
['React.createRef', 'React.createFactory', 'React.createElement', 'React.cloneElement'].forEach((name) => {
exports.defaultReactClassCallTokens.push(name);
exports.defaultReactClassCallTokens.push(name.split('.')[1]);
});
exports.defaultReactFunctionCallTokens = exports.defaultReactClassCallTokens.slice();
[
'React.useCallback',
'React.useEffect',
'React.useMemo',
'React.useImperativeHandle',
'React.useLayoutEffect',
'React.useReducer',
'React.useContext',
'React.useState',
'React.useDebugValue',
'React.useRef'
].forEach((name) => {
exports.defaultReactFunctionCallTokens.push(name);
exports.defaultReactFunctionCallTokens.push(name.split('.')[1]);
});
const detectIsValidName = (path) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
if (t.isFunctionExpression(path.node) || t.isArrowFunctionExpression(path.node)) {
const variableDeclartorPath = path.findParent((path) => t.isVariableDeclarator(path.node));
if (variableDeclartorPath && ((_b = (_a =
// @ts-ignore
variableDeclartorPath.node) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.name) &&
// @ts-ignore
/^[^a-zA-Z]*?[A-Z]/.test((_d = (_c = variableDeclartorPath.node) === null || _c === void 0 ? void 0 : _c.id) === null || _d === void 0 ? void 0 : _d.name)) {
return true;
}
}
if (t.isFunctionDeclaration(path.node) &&
(!((_e = path.node) === null || _e === void 0 ? void 0 : _e.id) || // @ts-ignore
(((_g = (_f = path.node) === null || _f === void 0 ? void 0 : _f.id) === null || _g === void 0 ? void 0 : _g.name) &&
// @ts-ignore
/^[^a-zA-Z]*?[A-Z]/.test((_j = (_h = path.node) === null || _h === void 0 ? void 0 : _h.id) === null || _j === void 0 ? void 0 : _j.name)))) {
return true;
}
return false;
};
function createDecorateReactVisitor(_a) {
var { reactClassSuperTokens = exports.defaultReactClassSuperTokens, reactClassMethodsTokens = exports.defaultReactClassMethodsTokens, reactClassCallTokens = exports.defaultReactClassCallTokens, reactFunctionCallTokens = exports.defaultReactFunctionCallTokens, reactClassMemberTokens = exports.defaultReactClassMemberTokens, wrapFunctionComponentDecorateTokens = ['React.forwardRef', 'forwardRef'], detectClassComponent = true, detectFunctionComponent = true, detectComponentName = true, condition } = _a, options = __rest(_a, ["reactClassSuperTokens", "reactClassMethodsTokens", "reactClassCallTokens", "reactFunctionCallTokens", "reactClassMemberTokens", "wrapFunctionComponentDecorateTokens", "detectClassComponent", "detectFunctionComponent", "detectComponentName", "condition"]);
const mergedOptions = Object.assign({ detectScopeDepth: 1 }, options);
const isReactInner = (path) => {
let isMatched = false;
path.traverse({
CallExpression(path) {
if (utils_1.isScopeDepthPassed(path, mergedOptions.detectScopeDepth) &&
reactFunctionCallTokens.some((token) => isMemberExpression(path.get('callee'), token))) {
isMatched = true;
path.stop();
}
},
// @ts-ignore
['MemberExpression|Identifier'](path) {
if (utils_1.isScopeDepthPassed(path, mergedOptions.detectScopeDepth) &&
reactClassMemberTokens.some((token) => isMemberExpression(path, token))) {
isMatched = true;
path.stop();
}
path.skip();
},
JSXElement(path) {
if (utils_1.isScopeDepthPassed(path, mergedOptions.detectScopeDepth)) {
isMatched = true;
path.stop();
}
},
JSXFragment(path) {
if (utils_1.isScopeDepthPassed(path, mergedOptions.detectScopeDepth)) {
isMatched = true;
path.stop();
}
}
});
return isMatched;
};
const vTypes = [
detectFunctionComponent && 'FunctionExpression|ArrowFunctionExpression',
detectFunctionComponent && 'FunctionDeclaration',
detectClassComponent && 'ClassExpression|ClassDeclaration'
]
.filter(Boolean)
.map((name) => ({
type: name,
condition: (path, a, b, api) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
if (condition) {
if (false === condition(path, a, b, api)) {
return false;
}
}
let isMatched = false;
if (name === 'ClassExpression|ClassDeclaration') {
if (!((_a = path.get('superClass')) === null || _a === void 0 ? void 0 : _a.node)) {
return false;
}
if (utils_1.isScopeDepthPassed(path, mergedOptions.detectScopeDepth) &&
reactClassSuperTokens.some((token) => isMemberExpression(path.get('superClass'), token))) {
path.stop();
return true;
}
const deltaDepth = (delta) => {
if (delta != null) {
return mergedOptions.detectScopeDepth != null
? mergedOptions.detectScopeDepth < 0
? mergedOptions.detectScopeDepth
: mergedOptions.detectScopeDepth + delta
: mergedOptions.detectScopeDepth;
}
return mergedOptions.detectScopeDepth;
};
path.traverse({
ClassMethod(path) {
// console.log(String(path.get('key')), reactClassMethodsTokens.some((token) => isMemberExpression(path.get('key') as any, token)))
if (utils_1.isScopeDepthPassed(path, deltaDepth(1)) &&
reactClassMethodsTokens.some((token) => isMemberExpression(path.get('key'), token))) {
isMatched = true;
path.stop();
}
},
CallExpression(path) {
if (utils_1.isScopeDepthPassed(path, deltaDepth(2)) &&
reactClassCallTokens.some((token) => isMemberExpression(path.get('callee'), token))) {
isMatched = true;
path.stop();
}
},
// @ts-ignore
['MemberExpression|Identifier'](path) {
if (utils_1.isScopeDepthPassed(path, deltaDepth(2)) &&
reactClassMemberTokens.some((token) => isMemberExpression(path, token))) {
isMatched = true;
path.stop();
}
path.skip();
},
JSXElement(path) {
if (utils_1.isScopeDepthPassed(path, deltaDepth(2))) {
isMatched = true;
path.stop();
}
},
JSXFragment(path) {
if (utils_1.isScopeDepthPassed(path, deltaDepth(2))) {
isMatched = true;
path.stop();
}
}
});
return isMatched;
}
else {
// @ts-ignore
if (((_b = path.node) === null || _b === void 0 ? void 0 : _b.async) || ((_c = path.node) === null || _c === void 0 ? void 0 : _c.generator)) {
return false;
}
/**
* function Button() {}
*/
if (path.node.type === 'FunctionDeclaration' &&
isReactInner(path) &&
(!detectComponentName || detectIsValidName(path))) {
const getVariableDeclarator = () => {
var _a, _b;
// path.opts
return t.variableDeclarator(
// @ts-ignore
t.identifier(((_b = (_a = path.node) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.name) || '__unknow'), t.functionExpression(
// @ts-ignore
path.node.id,
// @ts-ignore
path.node.params,
// @ts-ignore
path.node.body,
// @ts-ignore
path.node.generator,
// @ts-ignore
path.node.async));
};
const getVariableDeclaration = () => {
return t.variableDeclaration('const', [getVariableDeclarator()]);
};
/**
* function X() {
* return <div></div>
* }
* =>
* const X = function X() {
* return <div></div>
* }
*/
if (((_d = path.parent) === null || _d === void 0 ? void 0 : _d.type) === 'Program') {
// @ts-ignore
utils_1.replaceAdvancedWith(path, getVariableDeclaration());
api.noSkip();
return false;
}
/**
* export function X() {
* return <div></div>
* }
* =>
* export const X = function X() {
* return <div></div>
* }
*/
if (((_e = path.parent) === null || _e === void 0 ? void 0 : _e.type) === 'ExportNamedDeclaration') {
utils_1.replaceAdvancedWith(path, getVariableDeclaration());
api.noSkip();
return false;
}
/**
* export default function X() {
* return <div></div>
* }
* =>
* const X = function X() {
* return <div></div>
* }
* export default X;
*/
if (((_f = path.parent) === null || _f === void 0 ? void 0 : _f.type) === 'ExportDefaultDeclaration') {
// @ts-ignore
path.parentPath.insertBefore(getVariableDeclaration());
utils_1.replaceAdvancedWith(path, t.identifier(path.node.id.name));
api.noSkip();
return false;
}
return false;
}
// // @ts-ignore
// if (path.parent.node?.type === 'CallExpression') {
// debugger
// }
if (detectComponentName && !detectIsValidName(path)) {
return false;
}
if (isReactInner(path)) {
const parentPath = path.parentPath;
if (((_g = parentPath.node) === null || _g === void 0 ? void 0 : _g.type) === 'CallExpression' && ((_j = (_h = parentPath.node) === null || _h === void 0 ? void 0 : _h.callee) === null || _j === void 0 ? void 0 : _j.name) &&
wrapFunctionComponentDecorateTokens.some((t) => isMemberExpression(parentPath.get('callee'), t))) {
api.wrap();
return true;
}
return true;
}
return false;
}
}
}));
return createDecorateVisitor_1.default(Object.assign({ deepVisitorTypes: vTypes, visitorTypes: vTypes }, mergedOptions));
}
exports.default = createDecorateReactVisitor;