UNPKG

themes-switch

Version:

Toolset for switch multiple themes in application based on webpack

482 lines (399 loc) 17.7 kB
"use strict"; function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } var path = require('path'); var fs = require('fs-extra'); var webpack = require('webpack'); var EntryPlugin = require('webpack/lib/SingleEntryPlugin'); var MiniCssExtractPlugin = require('mini-css-extract-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var _require = require('./utils'), collectFiles = _require.collectFiles, randomNum = _require.randomNum, recursiveIssuer = _require.recursiveIssuer; var MiniCssExtractPluginOptions; try { MiniCssExtractPluginOptions = require('mini-css-extract-plugin/dist/plugin-options.json'); } catch (e) { console.log('Can\'t find plugin-options.json in dist of mini-css-extract-plugin'); } var isMiniCssOldVer = typeof MiniCssExtractPluginOptions === 'undefined' || typeof MiniCssExtractPluginOptions.properties.moduleFilename !== 'undefined'; var DEFAULT_INGORED_LIST = ['.DS_Store', '.git']; var TEMP_DIR = path.resolve(process.cwd(), 'temp'); var TEMP_THEMES_DIR_NAME = 'themes'; var TEMP_THEMES_DIR = path.resolve(TEMP_DIR, TEMP_THEMES_DIR_NAME); var DEFAULT_STYLE_NAME = 'default'; var pluginInfo = { name: 'ThemesGeneratorPlugin' }; var DEFAULT_THEME_OUTPUT_DIR = 'static/theme/'; var DEFAULT_CSS_OUTPUT_NAME = '[name]-[contenthash].css'; var ThemesGeneratorPlugin = /*#__PURE__*/function () { function ThemesGeneratorPlugin(options) { _classCallCheck(this, ThemesGeneratorPlugin); this.options = options; } _createClass(ThemesGeneratorPlugin, [{ key: "apply", value: function apply(compiler) { var _this = this; var _this$options = this.options, _this$options$clearTe = _this$options.clearTemp, clearTemp = _this$options$clearTe === void 0 ? true : _this$options$clearTe, disable = _this$options.disable, srcDir = _this$options.srcDir, themesDir = _this$options.themesDir, outputDir = _this$options.outputDir, _this$options$enableH = _this$options.enableHotReload, enableHotReload = _this$options$enableH === void 0 ? false : _this$options$enableH; if (disable) { return; } if (!srcDir) { console.log('themes-switch will not work, srcDir can not be empty'); return; } if (!themesDir) { console.log('themes-switch will not work, themesDir can not be empty'); return; } if (!outputDir) { console.log('themes-switch will not work, outputDir can not be empty'); return; } ThemesGeneratorPlugin.clearTemp(); console.log('Themes generating started...'); var themeList = this.getThemes(); hookThemesOptions({ compiler: compiler, themeList: themeList, outputDir: outputDir }); hookOnBeforeRun({ compiler: compiler, themeList: themeList }); var shouldUseProcessAssets = typeof compiler.webpack !== 'undefined' && typeof compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL !== 'undefined'; if (!shouldUseProcessAssets) { hookOnEmit({ compiler: compiler, themeList: themeList }); } else { compiler.hooks.thisCompilation.tap(pluginInfo, function (compilation) { if (shouldUseProcessAssets) { hookOnProcessAssets({ compiler: compiler, themeList: themeList, compilation: compilation }); } }); } if (!enableHotReload || !process.env.WEBPACK_DEV_SERVER) { this.generateThemes(themeList); } else { compiler.hooks.watchRun.tap(pluginInfo, function (comp) { var changedTimes = comp.watchFileSystem.watcher.mtimes; if (Object.keys(changedTimes).length > 0) { var filename = Object.keys(changedTimes)[0]; // eslint-disable-line prefer-destructuring if (filename && filename.startsWith(TEMP_THEMES_DIR)) { return; } } _this.generateThemes(themeList); }); } var onDone = function onDone() { if (clearTemp) { if (enableHotReload && process.env.WEBPACK_DEV_SERVER) { return; } ThemesGeneratorPlugin.clearTemp(); } }; compiler.hooks.done.tap(pluginInfo, onDone); } }, { key: "getThemes", value: function getThemes() { var useStaticThemeName = this.options.useStaticThemeName; var themeFileNames = this.collectOrgThemeFiles(); var themeList = []; themeFileNames.forEach(function (fileName) { var index = fileName.lastIndexOf('.'); var key = index > -1 ? fileName.substring(0, index) : fileName; themeList.push({ key: "theme-".concat(key), path: path.resolve(TEMP_THEMES_DIR, fileName), outputName: useStaticThemeName ? "theme-".concat(key, ".css") : "theme-".concat(key, "-").concat(randomNum(10000000, 99999999), ".css"), orgFile: fileName }); }); return themeList; } }, { key: "collectOrgThemeFiles", value: function collectOrgThemeFiles() { var _this$options2 = this.options, themesDir = _this$options2.themesDir, defaultStyleName = _this$options2.defaultStyleName, _this$options2$ignore = _this$options2.ignoredFilesInThemesDir, ignoredFilesInThemesDir = _this$options2$ignore === void 0 ? [] : _this$options2$ignore; var defaultStyle = defaultStyleName || DEFAULT_STYLE_NAME; var ignoredList = DEFAULT_INGORED_LIST.concat(ignoredFilesInThemesDir); var orgFiles = fs.readdirSync(themesDir); if (!orgFiles || orgFiles.length === 0) { console.warn('No themes found'); return []; } return orgFiles.filter(function (file) { return defaultStyle !== file && ignoredList.indexOf(file) < 0; }); } }, { key: "generateThemes", value: function generateThemes(themeList) { var _this$options3 = this.options, srcDir = _this$options3.srcDir, themesDir = _this$options3.themesDir, defaultStyleName = _this$options3.defaultStyleName, _this$options3$usePur = _this$options3.usePureCSS, usePureCSS = _this$options3$usePur === void 0 ? false : _this$options3$usePur; var defaultStyle = defaultStyleName || DEFAULT_STYLE_NAME; ThemesGeneratorPlugin.clearTemp(); fs.ensureDirSync(TEMP_THEMES_DIR); var themesDependencies = []; if (usePureCSS) { themeList.forEach(function (theme) { var fileContent = fs.readFileSync(path.join(themesDir, theme.orgFile)).toString(); fs.writeFileSync(path.join(TEMP_THEMES_DIR, theme.orgFile), fileContent); }); return; } var importPattern = new RegExp("@import (.+)".concat(defaultStyle, "(.+)")); var urlFilePattern = new RegExp(/(.+):(.*)url\((.+)\);/g); collectFiles(srcDir, themesDependencies, function (file) { var fileContent = fs.readFileSync(file).toString(); return importPattern.test(fileContent); }); if (themesDependencies.length < 1) { console.warn('No theme files are used'); return; } var importContent = ''; themesDependencies.forEach(function (d) { var newFile = path.join(TEMP_THEMES_DIR, d); fs.ensureFileSync(newFile); fs.copyFileSync(path.join(process.cwd(), d), newFile); var fileContent = fs.readFileSync(newFile).toString(); var newContent = fileContent.replace(importPattern, ''); var urlFiles = newContent.match(urlFilePattern); if (urlFiles && urlFiles.length > 0) { urlFiles.forEach(function (file) { var urlInfo = file.substring(file.indexOf('url(') + 4, file.lastIndexOf(')')); var styleFile = path.resolve(process.cwd(), d); var urlFile = path.join(styleFile.substring(0, styleFile.lastIndexOf('/')), urlInfo); fs.copySync(urlFile, urlFile.replace(process.cwd(), TEMP_THEMES_DIR)); }); } fs.writeFileSync(newFile, newContent); importContent += "@import '".concat(path.posix.join('./', d), "';\n"); }); themeList.forEach(function (theme) { var fileContent = fs.readFileSync(path.join(themesDir, theme.orgFile)).toString(); fs.writeFileSync(path.join(TEMP_THEMES_DIR, theme.orgFile), importAfterVariables(theme.orgFile) ? "".concat(fileContent, "\n").concat(importContent) : "".concat(importContent).concat(fileContent)); }); } }], [{ key: "clearTemp", value: function clearTemp() { fs.removeSync(TEMP_DIR); } }]); return ThemesGeneratorPlugin; }(); function hookThemesOptions(_ref) { var compiler = _ref.compiler, themeList = _ref.themeList, outputDir = _ref.outputDir; var onEntryOption = function onEntryOption(context) { var themesUrl = {}; var themesFilename = {}; var publicPath = compiler.options.output && compiler.options.output.publicPath ? compiler.options.output.publicPath : ''; if (themeList && themeList.length > 0) { if (!compiler.options.optimization) { compiler.options.optimization = {}; } if (!compiler.options.optimization.splitChunks) { compiler.options.optimization.splitChunks = {}; } if (!compiler.options.optimization.splitChunks.cacheGroups) { compiler.options.optimization.splitChunks.cacheGroups = {}; } themeList.forEach(function (theme) { var entryPlugin = new EntryPlugin(context, theme.path, theme.key); entryPlugin.apply(compiler); compiler.options.optimization.splitChunks.cacheGroups[theme.key] = { test: function test(m, c) { var entry = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : theme.key; return m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry; }, chunks: 'all', enforce: true }; var finalOutputDir = outputDir || DEFAULT_THEME_OUTPUT_DIR; finalOutputDir = finalOutputDir.endsWith('/') ? finalOutputDir : "".concat(finalOutputDir, "/"); themesFilename[theme.key] = "".concat(finalOutputDir).concat(theme.outputName); if (publicPath === '') { themesUrl[theme.key] = "".concat(finalOutputDir).concat(theme.outputName); } else { themesUrl[theme.key] = "".concat(publicPath.endsWith('/') ? publicPath : "".concat(publicPath, "/")).concat(finalOutputDir).concat(theme.outputName); } }); } var definePlugin = new webpack.DefinePlugin({ process: { themes: JSON.stringify(themesUrl) } }); definePlugin.apply(compiler); var orgMiniCssExtractPlugin; var orgFilename; if (compiler.options.plugins && compiler.options.plugins.length > 0) { compiler.options.plugins.forEach(function (plugin) { if (plugin instanceof MiniCssExtractPlugin) { orgMiniCssExtractPlugin = plugin; } }); } var filenameField = isMiniCssOldVer ? 'moduleFilename' : 'filename'; if (orgMiniCssExtractPlugin) { orgFilename = orgMiniCssExtractPlugin.options[filenameField]; } var moduleFilenameFunc = function moduleFilenameFunc(pathData) { var name = isMiniCssOldVer ? pathData.name : pathData.chunk.name; if (themesFilename[name]) { return themesFilename[name]; } var fileName = DEFAULT_CSS_OUTPUT_NAME; if (orgMiniCssExtractPlugin && orgFilename) { if (typeof orgFilename !== 'function') { fileName = orgFilename; } else { fileName = orgFilename(pathData); } } return fileName; }; if (orgMiniCssExtractPlugin) { orgMiniCssExtractPlugin.options[filenameField] = moduleFilenameFunc; } else { var miniCssExtractPlugin = new MiniCssExtractPlugin(_defineProperty({}, filenameField, moduleFilenameFunc)); miniCssExtractPlugin.apply(compiler); } }; compiler.hooks.entryOption.tap(pluginInfo, onEntryOption); } function hookOnBeforeRun(_ref2) { var compiler = _ref2.compiler, themeList = _ref2.themeList; var onBeforeRun = function onBeforeRun(c) { if (!c.options.plugins && c.options.plugins.length < 1) { return; } var excludeThemeChunks = themeList.map(function (theme) { return theme.key; }); c.options.plugins.forEach(function (plugin) { if (plugin instanceof HtmlWebpackPlugin) { if (plugin.options.excludeChunks) { plugin.options.excludeChunks = [].concat(_toConsumableArray(excludeThemeChunks), _toConsumableArray(plugin.options.excludeChunks)); } else { plugin.options.excludeChunks = excludeThemeChunks; } } }); }; compiler.hooks.beforeRun.tap(pluginInfo, onBeforeRun); } function hookOnEmit(_ref3) { var compiler = _ref3.compiler, themeList = _ref3.themeList; var onEmit = function onEmit(compilation, callback) { var stats = compilation.getStats().toJson(); if (themeList && themeList.length > 0) { themeList.forEach(function (theme) { var outputByThemes = stats.assetsByChunkName[theme.key]; var pattern = getUnusedFilePattern({ key: theme.key, compiler: compiler }); if (outputByThemes) { if (Array.isArray(outputByThemes)) { outputByThemes.forEach(function (fileName) { if (pattern.test(fileName) && compilation.assets[fileName]) { delete compilation.assets[fileName]; } }); } else if (typeof outputByThemes === 'string') { if (pattern.test(outputByThemes) && compilation.assets[outputByThemes]) { delete compilation.assets[outputByThemes]; } } } }); } if (callback && typeof callback === 'function') { callback(); } }; compiler.hooks.emit.tapAsync(pluginInfo, onEmit); } function hookOnProcessAssets(_ref4) { var compiler = _ref4.compiler, themeList = _ref4.themeList, compilation = _ref4.compilation; var onProcessAssets = function onProcessAssets(assets) { var keys = Object.keys(assets); if (themeList && themeList.length > 0) { themeList.forEach(function (theme) { var pattern = getUnusedFilePattern({ key: theme.key, compiler: compiler }); keys.forEach(function (key) { if (pattern.test(key)) { compilation.deleteAsset(key); } }); }); } }; compilation.hooks.processAssets.tap({ name: pluginInfo.name, stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, onProcessAssets); } function getUnusedFilePattern(_ref5) { var key = _ref5.key, compiler = _ref5.compiler; var outputFilename = compiler.options.output && compiler.options.output.filename ? compiler.options.output.filename : ''; var outputDir = outputFilename.substring(0, outputFilename.lastIndexOf('/')); if (!outputDir) { return new RegExp("^".concat(key, "(.*).js")); } return new RegExp("^".concat(outputDir, "/").concat(key, "(.*).js")); } function importAfterVariables(file) { return file.lastIndexOf('.scss') === file.length - 5 || file.lastIndexOf('.sass') === file.length - 5; } module.exports = ThemesGeneratorPlugin;