babel-plugin-clsx
Version:
Add clsx() automatically to className in React and support Typescript.
91 lines (90 loc) • 3.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@babel/core");
const plugin_syntax_jsx_1 = require("@babel/plugin-syntax-jsx");
const CLSX_IGNORE_GLOBAL_TOKEN = '@clsx-ignore-global';
const CLSX_IGNORE_TOKEN = '@clsx-ignore';
const CLASS_NAME_STRICT_RE = /^className$/;
const CLASS_NAME_RE = /^(className|\w+ClassName)$/;
const IMPORT_SOURCE = 'clsx';
const IMPORT_NAME = 'default';
const IMPORT_NAMESPACE = '_clsx';
exports.default = (_, opts = {}) => {
var _a, _b, _c, _d;
(_a = opts.static) !== null && _a !== void 0 ? _a : (opts.static = true);
(_b = opts.strict) !== null && _b !== void 0 ? _b : (opts.strict = true);
(_c = opts.importSource) !== null && _c !== void 0 ? _c : (opts.importSource = IMPORT_SOURCE);
(_d = opts.importName) !== null && _d !== void 0 ? _d : (opts.importName = IMPORT_NAME);
const callId = core_1.types.identifier(IMPORT_NAMESPACE);
const importDecl = core_1.types.importDeclaration([
opts.importName === IMPORT_NAME
? core_1.types.importDefaultSpecifier(callId)
: core_1.types.importSpecifier(callId, core_1.types.identifier(opts.importName)),
], core_1.types.stringLiteral(opts.importSource));
const classNameRE = opts.strict ? CLASS_NAME_STRICT_RE : CLASS_NAME_RE;
function isDynamicClassName(node) {
return classNameRE.test(node.name.name) && core_1.types.isJSXExpressionContainer(node.value);
}
function isIgnoredGlobal(nodes) {
for (const node of nodes) {
if (core_1.types.isImportDeclaration(node)) {
if (isIgnored(node, CLSX_IGNORE_GLOBAL_TOKEN))
return true;
}
else {
return isIgnored(node, CLSX_IGNORE_GLOBAL_TOKEN);
}
}
return false;
}
function isIgnored(node, token = CLSX_IGNORE_TOKEN) {
return node.leadingComments
? node.leadingComments.some((comment) => {
const ignored = comment.value.trim() === token;
if (ignored)
comment.ignore = ignored;
return ignored;
})
: false;
}
function isNeedTransform(jsxExpr) {
if (opts.static) {
return core_1.types.isArrayExpression(jsxExpr) || core_1.types.isObjectExpression(jsxExpr);
}
else {
return !core_1.types.isCallExpression(jsxExpr) && !core_1.types.isStringLiteral(jsxExpr);
}
}
function replaceNode(path) {
const { node } = path;
const args = (core_1.types.isArrayExpression(node) ? node.elements : [node]);
const callExpr = core_1.types.callExpression(callId, args);
path.replaceWith(callExpr);
}
return {
name: 'clsx',
inherits: plugin_syntax_jsx_1.default,
visitor: {
Program: {
enter(path, state) {
state.clsxIgnoreGlobal = isIgnoredGlobal(path.node.body);
},
exit(path, state) {
if (state.clsxImport) {
path.node.body.unshift(importDecl);
}
},
},
JSXAttribute(path, state) {
const { node } = path;
if (isDynamicClassName(node) &&
!isIgnored(node) &&
!state.clsxIgnoreGlobal &&
isNeedTransform(node.value.expression)) {
state.clsxImport = true;
replaceNode(path.get('value').get('expression'));
}
},
},
};
};