babel-plugin-transform-jsx-stylesheet
Version:
Transform stylesheet selector to style in JSX Elements.
220 lines • 11.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var path = require("path");
var camelcase = require("camelcase");
var constants_1 = require("./constants");
function default_1(_a, opts) {
var t = _a.types, template = _a.template;
if (opts === void 0) { opts = {}; }
var injectedStyleName = opts.injectedStyleName;
if (typeof injectedStyleName === 'string') {
(0, constants_1.setStyleSheetName)(injectedStyleName);
}
var mergeStylesFunctionTemplate = template((0, constants_1.mergeStylesFunctionString)());
var getClassNameFunctionTemplate = template((0, constants_1.getClassNameFunctionString)());
var getStyleFunctionTemplete = template((0, constants_1.getStyleFunctionString)());
var getClassNameFunctionAst = getClassNameFunctionTemplate();
var mergeStylesFunctionAst = mergeStylesFunctionTemplate();
var getStyleFunctionAst = getStyleFunctionTemplete();
function getArrayExpression(value) {
var str;
if (!value || value.value === '') {
// className
// className=""
return [];
}
else if (value.type === 'JSXExpressionContainer' &&
value.expression &&
typeof value.expression.value !== 'string') {
// className={{ container: true }}
// className={['container wrapper', { scroll: false }]}
if (value.expression.type === 'MemberExpression') {
return [value.expression];
}
return [t.callExpression(t.identifier(constants_1.GET_STYLE_FUNC_NAME), [value.expression])];
}
else {
// className="container"
// className={'container'}
str = (value.expression ? value.expression.value : value.value).trim();
}
return str === '' ? [] : str.split(/\s+/).map(function (className) {
return template("".concat(constants_1.styleSheetName, "[\"").concat(className, "\"]"))().expression;
});
}
function findLastImportIndex(body) {
var bodyReverse = body.slice(0).reverse();
var _index = 0;
bodyReverse.some(function (node, index) {
if (node.type === 'ImportDeclaration') {
_index = body.length - index - 1;
return true;
}
return false;
});
return _index;
}
return {
pre: function (file) {
file.set('shouldSkipConvert', false);
},
visitor: {
Program: {
exit: function (_a, _b) {
var node = _a.node;
var file = _b.file;
var cssFileCount = file.get('cssFileCount');
var injectGetStyle = file.get('injectGetStyle');
var lastImportIndex = findLastImportIndex(node.body);
var cssParamIdentifiers = file.get('cssParamIdentifiers');
var callExpression;
if (cssParamIdentifiers) {
// only one css file
if (cssParamIdentifiers.length === 1) {
callExpression = t.variableDeclaration('var', [t.variableDeclarator(t.identifier(constants_1.styleSheetName), cssParamIdentifiers[0])]);
}
else if (cssParamIdentifiers.length > 1) {
var objectAssignExpression = t.callExpression(t.identifier(constants_1.MERGE_STYLES_FUNC_NAME), cssParamIdentifiers);
callExpression = t.variableDeclaration('var', [t.variableDeclarator(t.identifier(constants_1.styleSheetName), objectAssignExpression)]);
}
node.body.splice(lastImportIndex + 1, 0, callExpression);
if (injectGetStyle) {
node.body.splice(lastImportIndex + 2, 0, getClassNameFunctionAst);
node.body.splice(lastImportIndex + 3, 0, getStyleFunctionAst);
}
}
if (cssFileCount > 1) {
node.body.unshift(mergeStylesFunctionAst);
}
},
},
// eslint-disable-next-line @typescript-eslint/no-shadow
JSXOpeningElement: function (_a, _b) {
var container = _a.container;
var file = _b.file, opts = _b.opts;
var _c = opts.retainClassName, retainClassName = _c === void 0 ? false : _c, _d = opts.convertImport, convertImport = _d === void 0 ? true : _d;
// skip attribute convert when use css module
// `import styles from '*.module.(c|le|sa|sc)ss'`
// should not convert attribute `className` to `style`
if (file.get('shouldSkipConvert'))
return;
// Check if has "style"
var hasStyleAttribute = false;
var styleAttribute;
var hasClassName = false;
var classNameAttribute;
var attributes = container.openingElement.attributes;
for (var i = 0; i < attributes.length; i++) {
var name_1 = attributes[i].name;
if (name_1) {
if (!hasStyleAttribute) {
hasStyleAttribute = name_1.name === 'style';
styleAttribute = hasStyleAttribute && attributes[i];
}
if (!hasClassName) {
hasClassName = name_1.name === 'className';
classNameAttribute = hasClassName && attributes[i];
}
}
}
// like className={ x.xxx }
var isCssModule = hasClassName && classNameAttribute.value &&
classNameAttribute.value.type === 'JSXExpressionContainer' &&
classNameAttribute.value.expression.type === 'MemberExpression';
var cssFileCount = file.get('cssFileCount') || 0;
if (!isCssModule && cssFileCount < 1 && convertImport !== false) {
return;
}
if (hasClassName) {
// Dont remove className
if (!retainClassName) {
// development env: change className to __class
if (process.env.NODE_ENV === 'development' && classNameAttribute.name) {
classNameAttribute.name.name = '__class';
}
else {
// Remove origin className
attributes.splice(attributes.indexOf(classNameAttribute), 1);
}
}
if (classNameAttribute.value &&
classNameAttribute.value.type === 'JSXExpressionContainer' &&
typeof classNameAttribute.value.expression.value !== 'string' // not like className={'container'}
) {
file.set('injectGetStyle', true);
}
if (isCssModule) {
attributes.splice(attributes.indexOf(classNameAttribute), 1);
file.set('injectGetStyle', false);
}
var arrayExpression = getArrayExpression(classNameAttribute.value);
if (arrayExpression.length === 0) {
return;
}
if (hasStyleAttribute && styleAttribute.value) {
var expression = styleAttribute.value.expression;
var expressionType = expression.type;
// style={[styles.a, styles.b]} ArrayExpression
if (expressionType === 'ArrayExpression') {
expression.elements = arrayExpression.concat(expression.elements);
// style={styles.a} MemberExpression
// style={{ height: 100 }} ObjectExpression
// style={{ ...custom }} ObjectExpression
// style={custom} Identifier
// style={getStyle()} CallExpression
// style={this.props.useCustom ? custom : null} ConditionalExpression
// style={custom || other} LogicalExpression
}
else {
var mergeArrayExpression = arrayExpression.concat(expression);
mergeArrayExpression.unshift(t.objectExpression([]));
styleAttribute.value.expression = t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), mergeArrayExpression);
}
}
else {
if (arrayExpression.length > 1) {
// Object.assign({}, ...)
arrayExpression.unshift(t.objectExpression([]));
}
var expression = arrayExpression.length === 1 ?
arrayExpression[0] :
t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), arrayExpression);
attributes.push(t.jSXAttribute(t.jSXIdentifier('style'), t.jSXExpressionContainer(expression)));
}
}
},
// eslint-disable-next-line @typescript-eslint/no-shadow
ImportDeclaration: function (_a, _b) {
var node = _a.node;
var file = _b.file, opts = _b.opts;
// Convert style import is disabled.
var _c = opts.convertImport, convertImport = _c === void 0 ? true : _c, _d = opts.forceEnableCSS, forceEnableCSS = _d === void 0 ? false : _d;
if (!convertImport)
return;
var sourceValue = node.source.value;
var extname = path.extname(sourceValue);
var cssIndex = constants_1.cssSuffixs.indexOf(extname);
// Do not convert `import styles from './foo.css'` kind
if (node.specifiers.length === 0 && cssIndex > -1) {
var cssFileCount = file.get('cssFileCount') || 0;
var cssParamIdentifiers = file.get('cssParamIdentifiers') || [];
var cssFileBaseName = camelcase(path.basename(sourceValue, extname));
var styleSheetIdentifier = t.identifier("".concat(cssFileBaseName + constants_1.NAME_SUFFIX));
node.specifiers = [t.importDefaultSpecifier(styleSheetIdentifier)];
cssParamIdentifiers.push(styleSheetIdentifier);
cssFileCount++;
file.set('cssParamIdentifiers', cssParamIdentifiers);
file.set('cssFileCount', cssFileCount);
}
// Set skip flag when `import styles from '*.module.(c|le|sa|sc)ss'`
if (node.specifiers.length && cssIndex > -1) {
var cssModuleReg = /\.module\.(c|le|sa|sc)ss$/;
var shouldSkipConvert = (forceEnableCSS && cssModuleReg.test(sourceValue)) || file.get('shouldSkipConvert');
file.set('shouldSkipConvert', shouldSkipConvert);
}
},
},
};
}
exports.default = default_1;
//# sourceMappingURL=index.js.map