UNPKG

webpack-atoms

Version:

Small atomic bits for crafting webpack configs

607 lines (534 loc) 17.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAtoms = createAtoms; exports.stats = exports.plugins = exports.rules = exports.loaders = exports.makeExtractLoaders = exports.makeInternalOnly = exports.makeExternalOnly = void 0; var _path = _interopRequireDefault(require("path")); var _autoprefixer = _interopRequireDefault(require("autoprefixer")); var _browserslist = require("browserslist"); var _copyWebpackPlugin = _interopRequireDefault(require("copy-webpack-plugin")); var _faviconsWebpackPlugin = _interopRequireDefault(require("favicons-webpack-plugin")); var _htmlWebpackPlugin = _interopRequireDefault(require("html-webpack-plugin")); var _miniCssExtractPlugin = _interopRequireDefault(require("mini-css-extract-plugin")); var _cssMinimizerWebpackPlugin = _interopRequireDefault(require("css-minimizer-webpack-plugin")); var _terserWebpackPlugin = _interopRequireDefault(require("terser-webpack-plugin")); var _webpack = _interopRequireDefault(require("webpack")); var _unusedFilesWebpackPlugin = _interopRequireDefault(require("@4c/unused-files-webpack-plugin")); var _plugins = _interopRequireDefault(require("./plugins")); var _stats = _interopRequireDefault(require("./stats")); const _excluded = ["disable", "fallback"], _excluded2 = ["postcssOptions", "browsers"], _excluded3 = ["browsers", "extract"], _excluded4 = ["modules"], _excluded5 = ["modules", "extract"], _excluded6 = ["modules"], _excluded7 = ["modules", "browsers", "extract"], _excluded8 = ["modules"], _excluded9 = ["browsers", "modules", "extract"], _excluded10 = ["modules"], _excluded11 = ["terserOptions"]; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 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 _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } const VENDOR_MODULE_REGEX = /node_modules/; const DEFAULT_BROWSERS = ['> 1%', 'Firefox ESR', 'not ie < 9']; function createAtoms(options = {}) { let { babelConfig = {}, assetRelativeRoot = '', env = process.env.NODE_ENV, vendorRegex = VENDOR_MODULE_REGEX, disableMiniExtractInDev = true, ignoreBrowserslistConfig = false, browsers: supportedBrowsers } = options; const hasBrowsersListConfig = !!(0, _browserslist.loadConfig)({ path: _path.default.resolve('.') }); if (ignoreBrowserslistConfig || !hasBrowsersListConfig) { supportedBrowsers = supportedBrowsers || DEFAULT_BROWSERS; } const makeExternalOnly = original => (options = {}) => { const rule = original(options); rule.include = vendorRegex; return rule; }; const makeInternalOnly = original => (options = {}) => { const rule = original(options); rule.exclude = vendorRegex; return rule; }; const makeContextual = rule => { return Object.assign(rule, { external: makeExternalOnly(rule), internal: makeInternalOnly(rule) }); }; const makeExtractLoaders = ({ extract } = {}, config) => [// eslint-disable-next-line @typescript-eslint/no-use-before-define loaders.miniCssExtract({ fallback: config.fallback, disable: extract == undefined ? extract : !extract }), ...config.use]; const PRODUCTION = env === 'production'; /** * Loaders */ const loaders = { json: () => ({ loader: require.resolve('json-loader') }), yaml: () => ({ loader: require.resolve('yaml-loader') }), null: () => ({ loader: require.resolve('null-loader') }), raw: () => ({ loader: require.resolve('raw-loader') }), style: () => ({ loader: require.resolve('style-loader') }), miniCssExtract: (opts = {}) => { const _ref = opts, { disable = !PRODUCTION && disableMiniExtractInDev, fallback } = _ref, options = _objectWithoutProperties(_ref, _excluded); return disable ? fallback || loaders.style() : { loader: _miniCssExtractPlugin.default.loader, options }; }, css: (options = {}) => ({ loader: require.resolve('css-loader'), options: _objectSpread(_objectSpread({ sourceMap: !PRODUCTION }, options), {}, { modules: options.modules ? _objectSpread({ // https://github.com/webpack-contrib/css-loader/issues/406 localIdentName: '[name]--[local]--[hash:base64:5]', exportLocalsConvention: 'dashes' }, options.modules) : false }) }), astroturf: options => ({ options: _objectSpread({ extension: '.module.css' }, options), loader: require.resolve('astroturf/loader') }), postcss: (options = {}) => { const { postcssOptions, browsers = supportedBrowsers } = options, rest = _objectWithoutProperties(options, _excluded2); const loader = require.resolve('postcss-loader'); return { loader, options: _objectSpread(_objectSpread({}, rest), {}, { postcssOptions: (...args) => { var _postcssOpts$plugins; const postcssOpts = typeof postcssOptions === `function` ? postcssOptions(...args) : postcssOptions; const plugins = (_postcssOpts$plugins = postcssOpts === null || postcssOpts === void 0 ? void 0 : postcssOpts.plugins) !== null && _postcssOpts$plugins !== void 0 ? _postcssOpts$plugins : []; return _objectSpread(_objectSpread({}, postcssOpts), {}, { plugins: [...plugins, // overrideBrowserslist is only set when browsers is explicit (0, _autoprefixer.default)({ overrideBrowserslist: browsers, flexbox: `no-2009` })] }); } }) }; }, less: (options = {}) => ({ options, loader: require.resolve('less-loader') }), sass: (options = {}) => ({ options, loader: require.resolve('sass-loader') }), file: (options = {}) => ({ loader: require.resolve('file-loader'), options: _objectSpread({ name: `${assetRelativeRoot}[name]-[hash].[ext]` }, options) }), url: (options = {}) => ({ loader: require.resolve('url-loader'), options: _objectSpread({ limit: 10000, name: `${assetRelativeRoot}[name]-[hash].[ext]` }, options) }), js: (options = babelConfig) => ({ options, loader: require.resolve('babel-loader') }), imports: (options = {}) => ({ options, loader: require.resolve('imports-loader') }), exports: (options = {}) => ({ options, loader: require.resolve('exports-loader') }) }; /** * Rules */ const rules = {}; /** * Javascript loader via babel, excludes node_modules */ { const js = (options = {}) => ({ test: /\.(j|t)sx?$/, exclude: vendorRegex, use: [loaders.js(options)] }); rules.js = js; } rules.yaml = () => ({ test: /\.ya?ml/, use: [loaders.json(), loaders.yaml()] }); /** * Font loader */ rules.fonts = () => ({ type: 'asset', test: /\.(eot|otf|ttf|woff(2)?)(\?.*)?$/, parser: { dataUrlCondition: { maxSize: 10000 } }, generator: { filename: `${assetRelativeRoot}[name]-[hash].[ext]` } }); /** * Loads image assets, inlines images via a data URI if they are below * the size threshold */ rules.images = () => ({ type: 'asset', test: /\.(ico|svg|jpg|jpeg|png|gif|webp)(\?.*)?$/, parser: { dataUrlCondition: { maxSize: 10000 } }, generator: { filename: `${assetRelativeRoot}[name]-[hash].[ext]` } }); /** * Loads audio or video assets */ rules.audioVideo = () => ({ type: 'asset/resource', test: /\.(mp4|webm|wav|mp3|m4a|aac|oga|flac)$/, generator: { filename: `${assetRelativeRoot}[name]-[hash].[ext]` } }); /** * A catch-all rule for everything that isn't js, json, or html. * Should only be used in the context of a webpack `oneOf` rule as a fallback * (see rules.assets()) */ rules.files = () => ({ // Exclude `js` files to keep "css" loader working as it injects // it's runtime that would otherwise processed through "file" loader. // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. exclude: [/\.jsx?$/, /\.html$/, /\.json$/], type: 'asset/resource', generator: { filename: `${assetRelativeRoot}[name]-[hash].[ext]` } }); /** * Astroturf loader. */ { const astroturf = (options = {}) => ({ test: /\.(j|t)sx?$/, use: [loaders.astroturf(options)] }); Object.assign(astroturf, { sass: opts => astroturf(_objectSpread({ extension: '.module.scss' }, opts)), less: opts => astroturf(_objectSpread({ extension: '.module.less' }, opts)) }); rules.astroturf = astroturf; } /** * CSS style loader. */ { const css = (_ref2 = {}) => { let { browsers, extract } = _ref2, options = _objectWithoutProperties(_ref2, _excluded3); return { test: /\.css$/, use: makeExtractLoaders({ extract }, { fallback: loaders.style(), use: [loaders.css(_objectSpread(_objectSpread({}, options), {}, { importLoaders: 1 })), loaders.postcss({ browsers })] }) }; }; rules.css = makeContextual((_ref3 = {}) => { let { modules = true } = _ref3, opts = _objectWithoutProperties(_ref3, _excluded4); return { oneOf: [_objectSpread(_objectSpread({}, css(_objectSpread(_objectSpread({}, opts), {}, { modules }))), {}, { test: /\.module\.css$/ }), css(opts)] }; }); } /** * PostCSS loader. */ { const postcss = (_ref4 = {}) => { let { modules, extract } = _ref4, opts = _objectWithoutProperties(_ref4, _excluded5); return { test: /\.css$/, use: makeExtractLoaders({ extract }, { fallback: loaders.style(), use: [loaders.css({ importLoaders: 1, modules }), loaders.postcss(opts)] }) }; }; rules.postcss = makeContextual((_ref5 = {}) => { let { modules = true } = _ref5, opts = _objectWithoutProperties(_ref5, _excluded6); return { oneOf: [_objectSpread(_objectSpread({}, postcss(_objectSpread(_objectSpread({}, opts), {}, { modules }))), {}, { test: /\.module\.css$/ }), postcss(opts)] }; }); } /** * Less style loader. */ { const less = (_ref6 = {}) => { let { modules, browsers, extract } = _ref6, options = _objectWithoutProperties(_ref6, _excluded7); return { test: /\.less$/, use: makeExtractLoaders({ extract }, { fallback: loaders.style(), use: [loaders.css({ importLoaders: 2, modules }), loaders.postcss({ browsers }), loaders.less(options)] }) }; }; rules.less = makeContextual((_ref7 = {}) => { let { modules = true } = _ref7, opts = _objectWithoutProperties(_ref7, _excluded8); return { oneOf: [_objectSpread(_objectSpread({}, less(_objectSpread(_objectSpread({}, opts), {}, { modules }))), {}, { test: /\.module\.less$/ }), less(opts)] }; }); } /** * SASS style loader, excludes node_modules. */ { const sass = (_ref8 = {}) => { let { browsers, modules, extract } = _ref8, options = _objectWithoutProperties(_ref8, _excluded9); return { test: /\.s(a|c)ss$/, use: makeExtractLoaders({ extract }, { fallback: loaders.style(), use: [loaders.css({ importLoaders: 2, modules }), loaders.postcss({ browsers }), loaders.sass(options)] }) }; }; rules.sass = makeContextual((_ref9 = {}) => { let { modules = true } = _ref9, opts = _objectWithoutProperties(_ref9, _excluded10); return { oneOf: [_objectSpread(_objectSpread({}, sass(_objectSpread(_objectSpread({}, opts), {}, { modules }))), {}, { test: /\.module\.s(a|c)ss$/ }), sass(opts)] }; }); } /** * Plugins */ const plugins = _objectSpread(_objectSpread({}, _plugins.default), {}, { /** * https://webpack.js.org/plugins/define-plugin/ * * Replace tokens in code with static values. Defaults to setting NODE_ENV * which is used by React and other libraries to toggle development mode. */ define: (defines = {}) => new _webpack.default.DefinePlugin(_objectSpread({ // eslint-disable-next-line @typescript-eslint/naming-convention 'process.env.NODE_ENV': JSON.stringify(env) }, defines)), /** * Minify javascript code without regard for IE8. Attempts * to parallelize the work to save time. Generally only add in Production */ /** * Minify javascript code without regard for IE8. Attempts * to parallelize the work to save time. Generally only add in Production */ minifyJs: (_ref10 = {}) => { let { terserOptions } = _ref10, options = _objectWithoutProperties(_ref10, _excluded11); return new _terserWebpackPlugin.default(_objectSpread({ parallel: true, exclude: /\.min\.js/, terserOptions: _objectSpread({ ecma: 8, ie8: false }, terserOptions) }, options)); }, /** * Extracts css requires into a single file; * includes some reasonable defaults */ extractCss: options => new _miniCssExtractPlugin.default(_objectSpread({ filename: '[name]-[contenthash].css' }, options)), minifyCss: (options = {}) => new _cssMinimizerWebpackPlugin.default(options), /** * Generates an html file that includes the output bundles. * Sepecify a `title` option to set the page title. */ html: options => new _htmlWebpackPlugin.default(_objectSpread({ inject: true, template: _path.default.join(__dirname, '../assets/index.html') }, options)), moment: () => new _webpack.default.IgnorePlugin({ contextRegExp: /^\.\/locale$/, resourceRegExp: /moment$/ }), copy: (...args) => new _copyWebpackPlugin.default(...args), unusedFiles: (...args) => new _unusedFilesWebpackPlugin.default(...args), favicons: args => new _faviconsWebpackPlugin.default(args) }); const stats = { none: _stats.default, minimal: _objectSpread(_objectSpread({}, _stats.default), {}, { errors: true, errorDetails: true, assets: true, chunks: true, colors: true, performance: true, timings: true, warnings: true }) }; return { loaders, rules: rules, plugins: plugins, stats, makeExternalOnly, makeInternalOnly, makeExtractLoaders }; } const { makeExternalOnly, makeInternalOnly, makeExtractLoaders, stats, loaders, rules, plugins } = createAtoms(); exports.plugins = plugins; exports.rules = rules; exports.loaders = loaders; exports.stats = stats; exports.makeExtractLoaders = makeExtractLoaders; exports.makeInternalOnly = makeInternalOnly; exports.makeExternalOnly = makeExternalOnly;