diff --git a/packages/webpack-config-composer/lib/constants.js b/packages/webpack-config-composer/lib/constants.js new file mode 100644 index 000000000..feb14f554 --- /dev/null +++ b/packages/webpack-config-composer/lib/constants.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + PARTIALS: Symbol("partials"), + PROFILES: Symbol("profiles") +}; diff --git a/packages/webpack-config-composer/lib/index.js b/packages/webpack-config-composer/lib/index.js index 23e379c49..7457612e2 100644 --- a/packages/webpack-config-composer/lib/index.js +++ b/packages/webpack-config-composer/lib/index.js @@ -7,11 +7,13 @@ const Partial = require("./partial"); const Profile = require("./profile"); const { getConcatMethod } = require("./concat-method"); +const { PARTIALS, PROFILES } = require("./constants"); + class WebpackConfigComposer { constructor(options) { options = options || {}; - this._profiles = {}; - this._partials = {}; + this[PROFILES] = {}; + this[PARTIALS] = {}; if (options.profiles) { this.addProfiles(options.profiles); } @@ -22,11 +24,11 @@ class WebpackConfigComposer { } get profiles() { - return this._profiles; + return this[PROFILES]; } get partials() { - return this._partials; + return this[PARTIALS]; } addProfiles(profiles) { @@ -53,7 +55,7 @@ class WebpackConfigComposer { profile = new Profile(name, partials); } - this._profiles[name] = profile; + this[PROFILES][name] = profile; return profile; } @@ -82,10 +84,10 @@ class WebpackConfigComposer { } _addPartial(name, data, addOpt) { - const exist = this._partials[name]; + const exist = this[PARTIALS][name]; if (!exist || _.get(addOpt, "method") === "replace") { - this._partials[name] = new Partial(name, data); + this[PARTIALS][name] = data instanceof Partial ? data : new Partial(name, data); } else { exist.merge(data, _.get(addOpt, "concatArray")); } @@ -102,11 +104,18 @@ class WebpackConfigComposer { } getPartial(name) { - return this._partials[name]; + return this[PARTIALS][name]; + } + + enablePartial(name, flag) { + const partial = this.getPartial(name); + if (partial) { + partial.enable = flag; + } } getProfile(name) { - return this._profiles[name]; + return this[PROFILES][name]; } compose(options, ...profiles) { diff --git a/packages/webpack-config-composer/lib/partial.js b/packages/webpack-config-composer/lib/partial.js index 32d318a5a..19eaa1b56 100644 --- a/packages/webpack-config-composer/lib/partial.js +++ b/packages/webpack-config-composer/lib/partial.js @@ -2,34 +2,49 @@ const _ = require("lodash"); const { getConcatMethod } = require("./concat-method"); +const assert = require("assert"); + +const OVERRIDE = Symbol("override webpack config partial"); +const DATA = Symbol("webpack partial data"); class Partial { constructor(name, data) { this._name = name; - this._data = Object.assign({ config: {}, options: {} }, data); + if (typeof data === "function") { + this[DATA] = { config: data }; + } else { + this[DATA] = Object.assign({ config: {}, options: {} }, data); + } + this.setOverride(); } set config(config) { - this._data.config = config; + this[DATA].config = config; } get config() { - return this._data.config; + return this[DATA].config; } set options(options) { - this._data.options = Object.assign({}, options); + this[DATA].options = Object.assign({}, options); } get options() { - return this._data.options; + return this[DATA].options; } merge(data, concatArray) { - _.mergeWith(this._data, data, getConcatMethod(concatArray)); + _.mergeWith(this[DATA], data, getConcatMethod(concatArray)); + } + + setOverride(fn) { + this[OVERRIDE] = fn || _.identity; } compose(options) { + options = Object.assign({}, this.options, options); + const config = this.config; const configType = typeof config; @@ -38,7 +53,6 @@ class Partial { if (configType === "object") { ret = config; } else if (configType === "function") { - options = Object.assign({}, this.options, options); ret = config(options); if (typeof ret === "function") { ret = ret(options); @@ -47,7 +61,9 @@ class Partial { throw new Error(`can't process config from Partial ${this._name}`); } - return ret; + const override = this[OVERRIDE](ret, options); + + return override || ret; } } diff --git a/packages/webpack-config-composer/lib/profile.js b/packages/webpack-config-composer/lib/profile.js index b0fa14372..3b5fa4d62 100644 --- a/packages/webpack-config-composer/lib/profile.js +++ b/packages/webpack-config-composer/lib/profile.js @@ -1,9 +1,11 @@ "use strict"; +const { PARTIALS } = require("./constants"); + class Profile { constructor(name, partials) { this._name = name; - this._partials = partials || {}; + this[PARTIALS] = partials || {}; } get name() { @@ -11,19 +13,19 @@ class Profile { } get partials() { - return this._partials; + return this[PARTIALS]; } setPartial(name, options) { - this._partials[name] = options || {}; + this[PARTIALS][name] = options || {}; } getPartial(name) { - return this._partials[name]; + return this[PARTIALS][name]; } delPartial(name) { - delete this._partials[name]; + delete this[PARTIALS][name]; } } diff --git a/packages/xarc-app-dev/config/webpack/index.js b/packages/xarc-app-dev/config/webpack/index.js index b7c07c685..9b6a15164 100644 --- a/packages/xarc-app-dev/config/webpack/index.js +++ b/packages/xarc-app-dev/config/webpack/index.js @@ -17,5 +17,6 @@ module.exports = { initWebpackConfigComposer, compose: generateConfig, env: profile, - options + options, + partials: require("./partials") }; diff --git a/packages/xarc-app-dev/config/webpack/partials/dll-entry.js b/packages/xarc-app-dev/config/webpack/partials/dll-entry.js index 3c4b5b01e..b04d870f3 100644 --- a/packages/xarc-app-dev/config/webpack/partials/dll-entry.js +++ b/packages/xarc-app-dev/config/webpack/partials/dll-entry.js @@ -5,6 +5,6 @@ const AppMode = archetype.AppMode; const Path = require("path"); const clientDllConfig = require(Path.resolve(AppMode.src.client, "dll.config.js")); -module.exports = { +module.exports = () => ({ entry: clientDllConfig -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/dll-output.js b/packages/xarc-app-dev/config/webpack/partials/dll-output.js index f3ae1f566..208c6f520 100644 --- a/packages/xarc-app-dev/config/webpack/partials/dll-output.js +++ b/packages/xarc-app-dev/config/webpack/partials/dll-output.js @@ -2,10 +2,10 @@ const Path = require("path"); -module.exports = { +module.exports = () => ({ output: { path: Path.resolve("dll/js"), filename: "[name].bundle.[hash].js", library: "[name]_[hash]" } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/entry.js b/packages/xarc-app-dev/config/webpack/partials/entry.js index cfa65cdd3..1799ef9d5 100644 --- a/packages/xarc-app-dev/config/webpack/partials/entry.js +++ b/packages/xarc-app-dev/config/webpack/partials/entry.js @@ -211,4 +211,4 @@ if (module.hot) { return partial; } -module.exports = makeEntryPartial(); +module.exports = makeEntryPartial; diff --git a/packages/xarc-app-dev/config/webpack/partials/index.js b/packages/xarc-app-dev/config/webpack/partials/index.js index 71f9bd8a3..73266d6e5 100644 --- a/packages/xarc-app-dev/config/webpack/partials/index.js +++ b/packages/xarc-app-dev/config/webpack/partials/index.js @@ -2,6 +2,7 @@ const Fs = require("fs"); const assert = require("assert"); +const Partial = require("webpack-config-composer/lib/partial"); // // This specifies a general order of partials to be applied. @@ -50,12 +51,11 @@ const files = Fs.readdirSync(__dirname) .filter(x => x !== "index.js") .map(x => x.substr(0, x.length - 3)); -module.exports = { - orders, - partials: files.reduce((a, p) => { - const k = `_${p}`; - assert(orders.indexOf(k) >= 0, `No default order specified for partial ${p}`); - a[k] = { config: () => require(`./${p}`) }; - return a; - }, {}) -}; +const partials = files.reduce((a, p) => { + const k = `_${p}`; + assert(orders.indexOf(k) >= 0, `No default order specified for partial ${p}`); + a[k] = new Partial(k, { config: () => require(`./${p}`) }); + return a; +}, {}); + +module.exports = partials; diff --git a/packages/xarc-app-dev/config/webpack/partials/output.js b/packages/xarc-app-dev/config/webpack/partials/output.js index 7acf08b28..f222ef2e0 100644 --- a/packages/xarc-app-dev/config/webpack/partials/output.js +++ b/packages/xarc-app-dev/config/webpack/partials/output.js @@ -26,7 +26,7 @@ const getOutputPath = () => { } }; -module.exports = { +module.exports = () => ({ output: { path: getOutputPath(), pathinfo: inspectpack, // Enable path information for inspectpack @@ -36,4 +36,4 @@ module.exports = { : "[contenthash].[name].js", filename: getOutputFilename() } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/resolve-loader.js b/packages/xarc-app-dev/config/webpack/partials/resolve-loader.js index f838bbff9..6d6bc0817 100644 --- a/packages/xarc-app-dev/config/webpack/partials/resolve-loader.js +++ b/packages/xarc-app-dev/config/webpack/partials/resolve-loader.js @@ -5,7 +5,7 @@ const Path = require("path"); const ModuleResolver = require("electrode-node-resolver/lib/webpack-plugin"); const archetype = require("@xarc/app/config/archetype"); -module.exports = { +module.exports = () => ({ resolveLoader: { symlinks: !archetype.webpack.preserveSymlinks, modules: [Path.resolve("lib"), process.cwd()] @@ -13,4 +13,4 @@ module.exports = { .filter(identity), plugins: [new ModuleResolver("module", "resolve", archetype.devDir, undefined)] } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/resolve.js b/packages/xarc-app-dev/config/webpack/partials/resolve.js index 82452b148..692806677 100644 --- a/packages/xarc-app-dev/config/webpack/partials/resolve.js +++ b/packages/xarc-app-dev/config/webpack/partials/resolve.js @@ -53,4 +53,4 @@ if (AppMode.hasSubApps) { resolve.mainFields = ["module", "browser", "main"]; } -module.exports = { resolve }; +module.exports = () => ({ resolve }); diff --git a/packages/xarc-app-dev/config/webpack/partials/subapp-chunks.js b/packages/xarc-app-dev/config/webpack/partials/subapp-chunks.js index b659a68eb..49f0cff5c 100644 --- a/packages/xarc-app-dev/config/webpack/partials/subapp-chunks.js +++ b/packages/xarc-app-dev/config/webpack/partials/subapp-chunks.js @@ -1,9 +1,6 @@ "use strict"; const Crypto = require("crypto"); -const { AppMode, webpack } = require("@xarc/app/config/archetype"); - -const config = {}; const splitMap = {}; @@ -20,53 +17,65 @@ function hashChunks(mod, chunks, key) { return `${key}~${digest}`; } -if (AppMode.hasSubApps && webpack.minimizeSubappChunks) { - config.optimization = { - runtimeChunk: "single", - splitChunks: { - cacheGroups: { - common: { - chunks: "all", - minChunks: 2, - enforce: true, - name: "common" +function makeConfig() { + const { AppMode, webpack } = require("@xarc/app/config/archetype"); + + const config = {}; + + if (!AppMode.hasSubApps) { + return config; + } + + if (webpack.minimizeSubappChunks) { + config.optimization = { + runtimeChunk: "single", + splitChunks: { + cacheGroups: { + common: { + chunks: "all", + minChunks: 2, + enforce: true, + name: "common" + } } } - } - }; -} else if (AppMode.hasSubApps) { - // use webpack splitChunks optimization to automatically put modules - // shared by subapps into common bundles. - // The common bundles will be determined by the splitChunks parameters. - // The filename has the pattern of hex-sum.bundle1~bundle2~bundle#.js - // https://webpack.js.org/plugins/split-chunks-plugin/ - config.optimization = { - runtimeChunk: "single", - splitChunks: { - chunks: "all", - minSize: 30 * 1024, - maxSize: 0, - minChunks: 2, - maxAsyncRequests: 500, - maxInitialRequests: 500, - automaticNameDelimiter: "~", - automaticNameMaxLength: 250, - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - // https://webpack.js.org/plugins/split-chunks-plugin/#splitchunksname - name: hashChunks, - priority: -10, - reuseExistingChunk: true - }, - shared: { - name: hashChunks, - priority: -20, - reuseExistingChunk: true + }; + } else { + // use webpack splitChunks optimization to automatically put modules + // shared by subapps into common bundles. + // The common bundles will be determined by the splitChunks parameters. + // The filename has the pattern of hex-sum.bundle1~bundle2~bundle#.js + // https://webpack.js.org/plugins/split-chunks-plugin/ + config.optimization = { + runtimeChunk: "single", + splitChunks: { + chunks: "all", + minSize: 30 * 1024, + maxSize: 0, + minChunks: 2, + maxAsyncRequests: 500, + maxInitialRequests: 500, + automaticNameDelimiter: "~", + automaticNameMaxLength: 250, + cacheGroups: { + vendors: { + test: /[\\/]node_modules[\\/]/, + // https://webpack.js.org/plugins/split-chunks-plugin/#splitchunksname + name: hashChunks, + priority: -10, + reuseExistingChunk: true + }, + shared: { + name: hashChunks, + priority: -20, + reuseExistingChunk: true + } } } - } - }; + }; + } + + return config; } -module.exports = config; +module.exports = makeConfig; diff --git a/packages/xarc-app-dev/config/webpack/partials/test-base.js b/packages/xarc-app-dev/config/webpack/partials/test-base.js index 6e2732057..6124fd015 100644 --- a/packages/xarc-app-dev/config/webpack/partials/test-base.js +++ b/packages/xarc-app-dev/config/webpack/partials/test-base.js @@ -2,7 +2,7 @@ const Path = require("path"); -module.exports = { +module.exports = () => ({ module: { /* * This prevents webpack from running its parsers on any sinon files. Sinon breaks when it is @@ -34,4 +34,4 @@ module.exports = { "react/lib/ExecutionEnvironment": true, "react/lib/ReactContext": true } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/test-output.js b/packages/xarc-app-dev/config/webpack/partials/test-output.js index d90813081..9fdd7f21c 100644 --- a/packages/xarc-app-dev/config/webpack/partials/test-output.js +++ b/packages/xarc-app-dev/config/webpack/partials/test-output.js @@ -1,9 +1,9 @@ "use strict"; -module.exports = { +module.exports = () => ({ output: { path: process.cwd(), filename: "[name].bundle.js", publicPath: "/assets/" } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/partials/test-resolve.js b/packages/xarc-app-dev/config/webpack/partials/test-resolve.js index e743ed720..f11063ad3 100644 --- a/packages/xarc-app-dev/config/webpack/partials/test-resolve.js +++ b/packages/xarc-app-dev/config/webpack/partials/test-resolve.js @@ -1,7 +1,7 @@ "use strict"; const optionalRequire = require("optional-require")(require); -module.exports = { +module.exports = () => ({ resolve: { alias: { // Allow root import of `src/FOO` from ROOT/src. @@ -9,4 +9,4 @@ module.exports = { sinon: optionalRequire.resolve("sinon/pkg/sinon") || "" } } -}; +}); diff --git a/packages/xarc-app-dev/config/webpack/util/generate-config.js b/packages/xarc-app-dev/config/webpack/util/generate-config.js index 733cff110..f685aaac8 100644 --- a/packages/xarc-app-dev/config/webpack/util/generate-config.js +++ b/packages/xarc-app-dev/config/webpack/util/generate-config.js @@ -3,7 +3,7 @@ /* eslint-disable no-unused-expressions */ const xsh = require("xsh"); -const partialConfigs = require("../partials"); +const partials = require("../partials"); const WebpackConfigComposer = require("webpack-config-composer"); const optionalRequire = require("optional-require")(require); const Path = require("path"); @@ -69,7 +69,7 @@ function initWebpackConfigComposer(options) { options.profiles && composer.addProfiles(options.profiles); composer.addProfile("user", {}); - composer.addPartials(partialConfigs.partials); + composer.addPartials(partials); options.partials && composer.addPartials(options.partials); }