UNPKG

rollup-plugin-css-module

Version:
269 lines (237 loc) 9.63 kB
import fs from 'fs'; import path from 'path'; import { createFilter } from 'rollup-pluginutils'; import Core from 'css-modules-loader-core'; import postcssRemoveClasses from 'postcss-remove-classes'; import stringHash from 'string-hash'; import postcss from 'postcss'; import colors from 'colors'; var pluginName = 'rollup-plugin-css-module'; var defaultTreeshakeOpts = { error: false, // prevent build from continuing warn: true, // display a warning remove: false // remove from compiled css }; var jsReservedNames = ['break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export', 'import']; var logErrs = { namingWarn: 'NamingWarning: export name {{oldName}} has been changed to {{newName}}. Either because it\'s a Javascript reserved word or it has a \'-\' in it. To turn off this warning set suppressNamingWarning: true', unusedWarn: 'UnusedWarning: export name {{export}} is not used ({{file}})', unusedErr: 'UnusedError: stopping build due to unused css exports. If you would like to continue past this error in the future set treeshake.error: false' }; var REPLACE_WITH_CSS = '{{ROLLUP_PLUGIN_CSS_MODULE_CSS}}'; var cssModuleClassName = '{{ROLLUP_PLUGIN_CSS_MODULE_CLASS_NAME}}'; function log(id) { var ctx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var c = Object.keys(ctx).reduce(function (acc, key) { return acc.replace('{{' + key + '}}', colors.red(ctx[key])); }, logErrs[id]); var msg = colors.yellow(colors.white(pluginName) + ' ' + c); console.log(msg); return msg; } function absPath(relPath) { return path.join(process.cwd(), relPath); } function getContentsOfFile(filePath) { return new Promise(function (resolve, reject) { fs.readFile(filePath, function (err, data) { if (err) return reject(err); return resolve(data); }); }); } function stringify(css) { var a = JSON.stringify(css); return a.substr(1, a.length - 2); } function postcssForceAfter(css, plugins) { if (plugins.length === 0) return css; return postcss(plugins).process(css).css; } function makeLegitExportTokens(result, shouldNotWarn) { return Object.keys(result.exportTokens).reduce(function (acc, key) { var str = key; if (jsReservedNames.indexOf(str) > -1) str = 'reserved_' + str; str = str.replace(/-(.{1})/gi, function (a, b) { return b.toUpperCase ? b.toUpperCase() : b; }) // camelCase dashes (-) .replace('\'', ''); // replace quotes, postcss adds to vars with a - if (!shouldNotWarn && str !== key) log('namingWarn', { oldName: key, newName: str }); acc.exportTokens[str] = result.exportTokens[key]; return acc; }, { injectableSource: result.injectableSource, exportTokens: {} }); } function processCssModule(instance, code, id, shouldNotWarn) { var imported = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; return instance.load(code, id, null, function (file) { var rPath = file.split('"').join(''); var aPath = absPath(rPath); return getContentsOfFile(rPath).then(function (c) { return processCssModule(instance, c, aPath, shouldNotWarn, imported); }).then(function (r) { imported[aPath] = r.local; return r.local.exportTokens; }); }).then(function (r) { return { local: makeLegitExportTokens(r, shouldNotWarn), imported: imported }; }); } function concatCss(accumulators) { var g = Object.keys(accumulators.global).reduce(function (acc, key) { return acc + accumulators.global[key].injectableSource; }, ''); var i = Object.keys(accumulators.imported).reduce(function (acc, key) { return acc + accumulators.imported[key].injectableSource; }, ''); var l = Object.keys(accumulators.local).reduce(function (acc, key) { return acc + accumulators.local[key].injectableSource; }, ''); return g + i + l; } function getUnusedCss(source, accumulators) { var all = Object.assign({}, accumulators.global, accumulators.imported, accumulators.local); var unused = []; Object.keys(all).forEach(function (key) { Object.keys(all[key].exportTokens).forEach(function (key1) { if (!source.includes(all[key].exportTokens[key1])) { unused.push({ file: key, export: key1, scopedName: all[key].exportTokens[key1] }); } }); }); return unused; } function logUnused(unused, opts) { if (opts.error === true || opts.warning === true) { unused.map(function (e) { return log('unusedWarn', e); }); if (opts.error === true) throw Error(log('unusedErr')); } } /* istanbul ignore next */ // Credit: https://github.com/substack/insert-css/blob/master/index.js // Heavily modified function insert(css, className) { var styleElement = document.createElement('style'); styleElement.className = className; styleElement.setAttribute('type', 'text/css'); document.querySelector('head').appendChild(styleElement); if (styleElement.styleSheet) { styleElement.styleSheet.cssText = css; } else { styleElement.textContent = css; } } function iife(css, className) { return '\n (' + insert.toString() + ')(\'' + css + '\', \'' + className + '\');\n '; } function generateDependableShortName(ignore, name, filename) { if (ignore.indexOf(name) > -1) return name; var sanitisedPath = filename.replace(process.cwd(), '').replace(/\.[^./\\]+$/, '').replace(/[\W_]+/g, '_').replace(/^_|_$/g, ''); var hash = stringHash('' + sanitisedPath + name).toString(36).substr(0, 5); return '_' + hash; } function generateShortName(ignore, name, filename, css) { if (ignore.indexOf(name) > -1) return name; var i = css.indexOf('.' + name); var numLines = css.substr(0, i).split(';').length; var hash = stringHash('' + name + stringHash(css) + numLines).toString(36).substr(0, 5); return '_' + hash; } function generateLongName(ignore, name, filename, css) { if (ignore.indexOf(name) > -1) return name; var sanitisedPath = filename.replace(process.cwd(), '').replace(/\.[^./\\]+$/, '').replace(/[\W_]+/g, '_').replace(/^_|_$/g, ''); var i = css.indexOf('.' + name); return '_' + sanitisedPath + '__' + i + '__' + name; } function cssModule() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // options var filter = createFilter(options.include, options.exclude); var extensions = options.extensions || ['.css']; var insertMethod = options.insertMethod || 'iife'; // one of: iife, css-module var fileName = options.fileName || null; // only if insertMethod === 'file' var insertedClassName = options.insertedClassName || pluginName; var before = options.before || []; var after = options.after || []; var afterForced = options.afterForced || []; var ignore = options.ignore || []; var treeshake = options.treeshake || defaultTreeshakeOpts; var suppressNamingWarning = options.suppressNamingWarning || false; var globals = options.globals ? options.globals.map(absPath) : []; // private // css accumulators var accumulators = { global: {}, imported: {}, local: {} }; // setup core Core.scope.generateScopedName = options.generateScopedName.bind(null, ignore) || generateShortName.bind(null, ignore); var plugins = before.concat([Core.values, Core.localByDefault, Core.extractImports, Core.scope]).concat(after); var coreInstance = new Core(plugins); // rollup plugin exports function intro() { if (insertMethod === 'iife') return REPLACE_WITH_CSS; return null; } function transform(code, id) { if (!filter(id)) return null; if (extensions.indexOf(path.extname(id)) === -1) return null; return processCssModule(coreInstance, code, id, suppressNamingWarning).then(function (result) { accumulators.imported = Object.assign({}, accumulators.imported, result.imported); if (globals.indexOf(id) > -1) { accumulators.global[id] = result.local; } else { accumulators.local[id] = result.local; } return { code: Object.keys(result.local.exportTokens).reduce(function (acc, key) { acc += 'export var ' + key + ' = ' + JSON.stringify(result.local.exportTokens[key]) + ';'; return acc; }, '') }; }); } function transformBundle(source) { if (treeshake.remove || treeshake.error || treeshake.warn) { var unusedArr = getUnusedCss(source, accumulators); if (treeshake.error || treeshake.warn) { logUnused(unusedArr, treeshake); } if (treeshake.remove) { afterForced.push(postcssRemoveClasses(unusedArr)); } } var finalCss = postcssForceAfter(concatCss(accumulators), afterForced); if (insertMethod === 'iife') { var iife$$1 = iife(stringify(finalCss), insertedClassName); source = source.replace(REPLACE_WITH_CSS, iife$$1); } if (insertMethod === 'file') { fs.writeFileSync(absPath(fileName), finalCss); } if (insertMethod === 'css-module') { source = source.replace(cssModuleClassName, insertedClassName); source = source.replace(REPLACE_WITH_CSS, stringify(finalCss)); } return source; } return { name: pluginName, intro: intro, transform: transform, transformBundle: transformBundle }; } export { generateDependableShortName, generateShortName, generateLongName };export default cssModule;