new-themes-switch
Version:
Toolset for switch multiple themes in application based on webpack
378 lines (308 loc) • 15 kB
JavaScript
;
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 _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(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); return Constructor; }
var path = require('path');
var fs = require('fs-extra');
var webpack = require('webpack');
var sast = require('sast');
var EntryPlugin = require('webpack/lib/SingleEntryPlugin');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var MiniCssExtractPluginOptions = require('mini-css-extract-plugin/dist/plugin-options.json');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var _require = require('./utils'),
collectFiles = _require.collectFiles,
randomNum = _require.randomNum,
recursiveIssuer = _require.recursiveIssuer,
clearRules = _require.clearRules;
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;
if (disable) {
return;
}
if (!srcDir) {
console.log('srcDir can not be empty');
return;
}
if (!themesDir) {
console.log('themesDir can not be empty');
return;
}
if (!outputDir) {
console.log('outputDir can not be empty');
return;
}
var themeList = this.getThemeList();
var webpackNewVer = ('hooks' in compiler);
var onEntryOption = function onEntryOption(context) {
if (_typeof(compiler.options.entry) !== 'object') {
console.log('Entry must be an object if ThemesGeneratorPlugin was used!');
return;
}
console.log('Themes generating started...');
var finalThemes = {};
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(_this.context || context, theme.path, theme.key);
if (webpackNewVer) {
entryPlugin.apply(compiler);
} else {
compiler.apply(entryPlugin);
}
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, "/");
finalThemes[theme.key] = publicPath ? "".concat(publicPath).concat(publicPath.endsWith('/') ? '' : '/').concat(finalOutputDir).concat(theme.outputName) : "".concat(finalOutputDir).concat(theme.outputName);
});
}
var definePlugin = new webpack.DefinePlugin({
process: {
themes: JSON.stringify(finalThemes)
}
});
if (webpackNewVer) {
definePlugin.apply(compiler);
} else {
compiler.apply(definePlugin);
}
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 isMiniCssOldVer = typeof MiniCssExtractPluginOptions.properties.moduleFilename !== 'undefined';
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 (finalThemes[name]) {
return "".concat(finalThemes[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));
if (webpackNewVer) {
miniCssExtractPlugin.apply(compiler);
} else {
compiler.apply(miniCssExtractPlugin);
}
}
if (compiler.options.plugins && compiler.options.plugins.length > 0) {
var excludeThemeChunks = themeList.map(function (theme) {
return theme.key;
});
compiler.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;
}
}
});
}
};
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];
if (outputByThemes) {
var pattern = new RegExp("^".concat(theme.key, "(.*).(js|css)"));
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();
}
};
var onDone = function onDone() {
fs.removeSync(TEMP_DIR);
};
if (webpackNewVer) {
compiler.hooks.entryOption.tap(pluginInfo, onEntryOption);
compiler.hooks.emit.tapAsync(pluginInfo, onEmit);
if (clearTemp) {
compiler.hooks.done.tap(pluginInfo, onDone);
}
} else {
compiler.plugin('entry-option', onEntryOption);
compiler.plugin('emit', onEmit);
if (clearTemp) {
compiler.plugin('done', onDone);
}
}
}
}, {
key: "getThemeList",
value: function getThemeList() {
var useStaticThemeName = this.options.useStaticThemeName;
var themeFileNames = this.generateThemes();
var themeList = [];
themeFileNames.forEach(function (fileName) {
var index = fileName.lastIndexOf('.');
var key = index > -1 ? fileName.substr(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")
});
});
return themeList;
}
}, {
key: "generateThemes",
value: function generateThemes() {
var _this$options2 = this.options,
srcDir = _this$options2.srcDir,
themesDir = _this$options2.themesDir,
defaultStyleName = _this$options2.defaultStyleName,
_this$options2$ignore = _this$options2.ignoredFilesInThemesDir,
ignoredFilesInThemesDir = _this$options2$ignore === void 0 ? [] : _this$options2$ignore,
_this$options2$usePur = _this$options2.usePureCSS,
usePureCSS = _this$options2$usePur === void 0 ? false : _this$options2$usePur;
var ignoredList = DEFAULT_INGORED_LIST.concat(ignoredFilesInThemesDir);
fs.removeSync(TEMP_DIR);
var orgFiles = fs.readdirSync(themesDir);
if (!orgFiles || orgFiles.length === 0) {
console.warn('No themes');
return;
}
fs.ensureDirSync(TEMP_THEMES_DIR);
var themesDependencies = [];
var defaultStyle = defaultStyleName || DEFAULT_STYLE_NAME;
var importPattern = new RegExp("@import (.+)".concat(defaultStyle, "(.+)"));
if (usePureCSS) {
var _themeFileNames = [];
orgFiles.forEach(function (file) {
if (defaultStyle !== file && ignoredList.indexOf(file) < 0) {
_themeFileNames.push(file);
var fileContent = fs.readFileSync(path.join(themesDir, file)).toString();
fs.writeFileSync(path.join(TEMP_THEMES_DIR, file), fileContent);
}
});
return _themeFileNames;
}
collectFiles(srcDir, themesDependencies, function (file) {
var fileContent = fs.readFileSync(file).toString();
return importPattern.test(fileContent);
});
if (themesDependencies.length < 1) {
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 astTree = sast.parse(fileContent, {
syntax: path.extname(newFile).replace('.', '')
});
clearRules(astTree);
var newContent = sast.jsonify(astTree).replace(/\$/g, '@');
var finalContent = newContent.replace(importPattern, '');
fs.writeFileSync(newFile, finalContent);
importContent += "@import '".concat(path.posix.join('./', d), "';\n");
});
var themeFileNames = [];
orgFiles.forEach(function (file) {
if (defaultStyle !== file && ignoredList.indexOf(file) < 0) {
themeFileNames.push(file);
var fileContent = fs.readFileSync(path.join(themesDir, file)).toString();
fs.writeFileSync(path.join(TEMP_THEMES_DIR, file), ThemesGeneratorPlugin.importAfterVariables(file) ? "".concat(fileContent, "\n").concat(importContent) : "".concat(importContent).concat(fileContent));
}
});
return themeFileNames;
}
}], [{
key: "importAfterVariables",
value: function importAfterVariables(file) {
return file.lastIndexOf('.scss') === file.length - 5 || file.lastIndexOf('.sass') === file.length - 5;
}
}, {
key: "clearTemp",
value: function clearTemp() {
fs.removeSync(TEMP_DIR);
}
}]);
return ThemesGeneratorPlugin;
}();
module.exports = ThemesGeneratorPlugin;