babel-plugin-react-css-modules
Version:
Transforms styleName to className using compile time CSS module resolution.
223 lines (159 loc) • 8.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _path = require('path');
var _babelPluginSyntaxJsx = require('babel-plugin-syntax-jsx');
var _babelPluginSyntaxJsx2 = _interopRequireDefault(_babelPluginSyntaxJsx);
var _babelTypes = require('babel-types');
var _babelTypes2 = _interopRequireDefault(_babelTypes);
var _ajvKeywords = require('ajv-keywords');
var _ajvKeywords2 = _interopRequireDefault(_ajvKeywords);
var _ajv = require('ajv');
var _ajv2 = _interopRequireDefault(_ajv);
var _optionsSchema = require('./schemas/optionsSchema.json');
var _optionsSchema2 = _interopRequireDefault(_optionsSchema);
var _optionsDefaults = require('./schemas/optionsDefaults');
var _optionsDefaults2 = _interopRequireDefault(_optionsDefaults);
var _createObjectExpression = require('./createObjectExpression');
var _createObjectExpression2 = _interopRequireDefault(_createObjectExpression);
var _requireCssModule = require('./requireCssModule');
var _requireCssModule2 = _interopRequireDefault(_requireCssModule);
var _resolveStringLiteral = require('./resolveStringLiteral');
var _resolveStringLiteral2 = _interopRequireDefault(_resolveStringLiteral);
var _replaceJsxExpressionContainer = require('./replaceJsxExpressionContainer');
var _replaceJsxExpressionContainer2 = _interopRequireDefault(_replaceJsxExpressionContainer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const ajv = new _ajv2.default({
// eslint-disable-next-line id-match
$data: true
});
(0, _ajvKeywords2.default)(ajv);
const validate = ajv.compile(_optionsSchema2.default);
exports.default = (_ref) => {
let t = _ref.types;
const filenameMap = {};
const setupFileForRuntimeResolution = (path, filename) => {
const programPath = path.findParent(parentPath => {
return parentPath.isProgram();
});
filenameMap[filename].importedHelperIndentifier = programPath.scope.generateUidIdentifier('getClassName');
filenameMap[filename].styleModuleImportMapIdentifier = programPath.scope.generateUidIdentifier('styleModuleImportMap');
programPath.unshiftContainer('body', t.importDeclaration([t.importDefaultSpecifier(filenameMap[filename].importedHelperIndentifier)], t.stringLiteral('babel-plugin-react-css-modules/dist/browser/getClassName')));
const firstNonImportDeclarationNode = programPath.get('body').find(node => {
return !t.isImportDeclaration(node);
});
firstNonImportDeclarationNode.insertBefore(t.variableDeclaration('const', [t.variableDeclarator(filenameMap[filename].styleModuleImportMapIdentifier, (0, _createObjectExpression2.default)(t, filenameMap[filename].styleModuleImportMap))]));
// eslint-disable-next-line no-console
// console.log('setting up', filename, util.inspect(filenameMap,{depth: 5}))
};
const addWebpackHotModuleAccept = path => {
const test = t.memberExpression(t.identifier('module'), t.identifier('hot'));
const consequent = t.blockStatement([t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('module'), t.identifier('hot')), t.identifier('accept')), [t.stringLiteral(path.node.source.value), t.functionExpression(null, [], t.blockStatement([t.expressionStatement(t.callExpression(t.identifier('require'), [t.stringLiteral(path.node.source.value)]))]))]))]);
const programPath = path.findParent(parentPath => {
return parentPath.isProgram();
});
const firstNonImportDeclarationNode = programPath.get('body').find(node => {
return !t.isImportDeclaration(node);
});
const hotAcceptStatement = t.ifStatement(test, consequent);
if (firstNonImportDeclarationNode) {
firstNonImportDeclarationNode.insertBefore(hotAcceptStatement);
} else {
programPath.pushContainer('body', hotAcceptStatement);
}
};
const getTargetResourcePath = (path, stats) => {
const targetFileDirectoryPath = (0, _path.dirname)(stats.file.opts.filename);
if (path.node.source.value.startsWith('.')) {
return (0, _path.resolve)(targetFileDirectoryPath, path.node.source.value);
}
return require.resolve(path.node.source.value);
};
const notForPlugin = (path, stats) => {
stats.opts.filetypes = stats.opts.filetypes || {};
const extension = path.node.source.value.lastIndexOf('.') > -1 ? path.node.source.value.substr(path.node.source.value.lastIndexOf('.')) : null;
if (extension !== '.css' && Object.keys(stats.opts.filetypes).indexOf(extension) < 0) {
return true;
}
if (stats.opts.exclude && getTargetResourcePath(path, stats).match(new RegExp(stats.opts.exclude))) {
return true;
}
return false;
};
return {
inherits: _babelPluginSyntaxJsx2.default,
visitor: {
ImportDeclaration(path, stats) {
if (notForPlugin(path, stats)) {
return;
}
const filename = stats.file.opts.filename;
const targetResourcePath = getTargetResourcePath(path, stats);
let styleImportName;
if (path.node.specifiers.length === 0) {
// use imported file path as import name
styleImportName = path.node.source.value;
} else if (path.node.specifiers.length === 1) {
styleImportName = path.node.specifiers[0].local.name;
} else {
// eslint-disable-next-line no-console
console.warn('Please report your use case. https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=Unexpected+use+case.');
throw new Error('Unexpected use case.');
}
filenameMap[filename].styleModuleImportMap[styleImportName] = (0, _requireCssModule2.default)(targetResourcePath, {
context: stats.opts.context,
filetypes: stats.opts.filetypes || {},
generateScopedName: stats.opts.generateScopedName
});
if (stats.opts.webpackHotModuleReloading) {
addWebpackHotModuleAccept(path);
}
if (stats.opts.removeImport) {
path.remove();
}
},
JSXElement(path, stats) {
const filename = stats.file.opts.filename;
let attributeNames = _optionsDefaults2.default.attributeNames;
if (stats.opts && stats.opts.attributeNames) {
attributeNames = Object.assign({}, attributeNames, stats.opts.attributeNames);
}
const attributes = path.node.openingElement.attributes.filter(attribute => {
return typeof attribute.name !== 'undefined' && typeof attributeNames[attribute.name.name] === 'string';
});
if (attributes.length === 0) {
return;
}
const handleMissingStyleName = stats.opts && stats.opts.handleMissingStyleName || _optionsDefaults2.default.handleMissingStyleName;
for (const attribute of attributes) {
const destinationName = attributeNames[attribute.name.name];
if (t.isStringLiteral(attribute.value)) {
(0, _resolveStringLiteral2.default)(path, filenameMap[filename].styleModuleImportMap, attribute, destinationName, {
handleMissingStyleName
});
} else if (t.isJSXExpressionContainer(attribute.value)) {
if (!filenameMap[filename].importedHelperIndentifier) {
setupFileForRuntimeResolution(path, filename);
}
(0, _replaceJsxExpressionContainer2.default)(t, path, attribute, destinationName, filenameMap[filename].importedHelperIndentifier, filenameMap[filename].styleModuleImportMapIdentifier, {
handleMissingStyleName
});
}
}
},
Program(path, stats) {
if (!validate(stats.opts)) {
// eslint-disable-next-line no-console
console.error(validate.errors);
throw new Error('Invalid configuration');
}
const filename = stats.file.opts.filename;
filenameMap[filename] = {
styleModuleImportMap: {}
};
}
}
};
};
//# sourceMappingURL=index.js.map