UNPKG

babel-plugin-react-css-modules

Version:

Transforms styleName to className using compile time CSS module resolution.

223 lines (159 loc) 8.08 kB
'use strict'; 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