gatsby
Version:
Blazing fast modern site generator for React
631 lines (548 loc) • 19.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.reactHasJsxRuntime = reactHasJsxRuntime;
exports.createWebpackUtils = void 0;
var path = _interopRequireWildcard(require("path"));
var _autoprefixer = _interopRequireDefault(require("autoprefixer"));
var _postcssFlexbugsFixes = _interopRequireDefault(require("postcss-flexbugs-fixes"));
var _terserWebpackPlugin = _interopRequireDefault(require("terser-webpack-plugin"));
var _miniCssExtractPlugin = _interopRequireDefault(require("mini-css-extract-plugin"));
var _cssMinimizerWebpackPlugin = _interopRequireDefault(require("css-minimizer-webpack-plugin"));
var _reactRefreshWebpackPlugin = _interopRequireDefault(require("@pmmmwh/react-refresh-webpack-plugin"));
var _browserslist = require("./browserslist");
var _eslintWebpackPlugin = _interopRequireDefault(require("eslint-webpack-plugin"));
var _gatsbyCoreUtils = require("gatsby-core-utils");
var _gatsbyWebpackStatsExtractor = require("./gatsby-webpack-stats-extractor");
var _gatsbyWebpackEslintGraphqlSchemaReloadPlugin = require("./gatsby-webpack-eslint-graphql-schema-reload-plugin");
var _gatsbyWebpackVirtualModules = require("./gatsby-webpack-virtual-modules");
var _webpackPlugins = require("./webpack-plugins");
var _eslintConfig = require("./eslint-config");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const vendorRegex = /(node_modules|bower_components)/;
/**
* A factory method that produces an atoms namespace
*/
const createWebpackUtils = (stage, program) => {
const assetRelativeRoot = `static/`;
const supportedBrowsers = (0, _browserslist.getBrowsersList)(program.directory);
const PRODUCTION = !stage.includes(`develop`);
const isSSR = stage.includes(`html`);
const jsxRuntimeExists = reactHasJsxRuntime();
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 loaders = {
json: (options = {}) => {
return {
options,
loader: require.resolve(`json-loader`)
};
},
yaml: (options = {}) => {
return {
options,
loader: require.resolve(`yaml-loader`)
};
},
null: (options = {}) => {
return {
options,
loader: require.resolve(`null-loader`)
};
},
raw: (options = {}) => {
return {
options,
loader: require.resolve(`raw-loader`)
};
},
style: (options = {}) => {
return {
options,
loader: require.resolve(`style-loader`)
};
},
miniCssExtract: (options = {}) => {
let moduleOptions = undefined;
const {
modules,
...restOptions
} = options;
if (typeof modules === `boolean` && options.modules) {
moduleOptions = {
namedExport: true
};
} else {
moduleOptions = modules;
}
return {
loader: _miniCssExtractPlugin.default.loader,
options: {
modules: moduleOptions,
...restOptions
}
};
},
css: (options = {}) => {
let modulesOptions = false;
if (options.modules) {
modulesOptions = {
auto: undefined,
namedExport: true,
localIdentName: `[name]--[local]--[hash:base64:5]`,
exportLocalsConvention: `dashesOnly`,
exportOnlyLocals: isSSR
};
if (typeof options.modules === `object`) {
modulesOptions = { ...modulesOptions,
...options.modules
};
}
}
return {
loader: require.resolve(`css-loader`),
options: {
// Absolute urls (https or //) are not send to this function. Only resolvable paths absolute or relative ones.
url: function (url) {
// When an url starts with /
if (url.startsWith(`/`)) {
return false;
}
return true;
},
sourceMap: !PRODUCTION,
modules: modulesOptions
}
};
},
postcss: (options = {}) => {
const {
plugins,
overrideBrowserslist = supportedBrowsers,
...postcssOpts
} = options;
return {
loader: require.resolve(`postcss-loader`),
options: {
execute: false,
sourceMap: !PRODUCTION,
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
postcssOptions: loaderContext => {
var _options, _postCSSPlugins$find;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let postCSSPlugins = [];
if (plugins) {
postCSSPlugins = typeof plugins === `function` ? plugins(loaderContext) : plugins;
}
const autoprefixerPlugin = (0, _autoprefixer.default)({
overrideBrowserslist,
flexbox: `no-2009`,
...((_options = (_postCSSPlugins$find = postCSSPlugins.find(plugin => plugin.postcssPlugin === `autoprefixer`)) === null || _postCSSPlugins$find === void 0 ? void 0 : _postCSSPlugins$find.options) !== null && _options !== void 0 ? _options : {})
});
postCSSPlugins.unshift(autoprefixerPlugin);
postCSSPlugins.unshift(_postcssFlexbugsFixes.default);
return {
plugins: postCSSPlugins,
...postcssOpts
};
}
}
};
},
file: (options = {}) => {
return {
loader: require.resolve(`file-loader`),
options: {
name: `${assetRelativeRoot}[name]-[hash].[ext]`,
...options
}
};
},
url: (options = {}) => {
return {
loader: require.resolve(`url-loader`),
options: {
limit: 10000,
name: `${assetRelativeRoot}[name]-[hash].[ext]`,
fallback: require.resolve(`file-loader`),
...options
}
};
},
js: options => {
return {
options: {
stage,
reactRuntime: jsxRuntimeExists ? `automatic` : `classic`,
cacheDirectory: path.join(program.directory, `.cache`, `webpack`, `babel`),
...options,
rootDir: program.directory
},
loader: require.resolve(`./babel-loader`)
};
},
dependencies: options => {
return {
options: {
cacheDirectory: path.join(program.directory, `.cache`, `webpack`, `babel`),
...options
},
loader: require.resolve(`babel-loader`)
};
}
};
/**
* Rules
*/
const rules = {};
/**
* JavaScript loader via babel, includes userland code
* and packages that depend on `gatsby`
*/
{
const js = ({
modulesThatUseGatsby = [],
...options
} = {}) => {
return {
test: /\.(js|mjs|jsx)$/,
include: modulePath => {
// when it's not coming from node_modules we treat it as a source file.
if (!vendorRegex.test(modulePath)) {
return true;
} // If the module uses Gatsby as a dependency
// we want to treat it as src so we can extract queries
return modulesThatUseGatsby.some(module => modulePath.includes(module.path));
},
type: `javascript/auto`,
use: [loaders.js({ ...options,
configFile: true,
compact: PRODUCTION
})]
};
};
rules.js = js;
}
/**
* Node_modules JavaScript loader via babel
* Excludes core-js & babel-runtime to speedup babel transpilation
* Excludes modules that use Gatsby since the `rules.js` already transpiles those
*/
{
const dependencies = ({
modulesThatUseGatsby = []
} = {}) => {
const jsOptions = {
babelrc: false,
configFile: false,
compact: false,
presets: [[require.resolve(`babel-preset-gatsby/dependencies`), {
stage
}]],
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
cacheIdentifier: JSON.stringify({
browerslist: supportedBrowsers,
gatsbyPreset: require(`babel-preset-gatsby/package.json`).version
})
}; // TODO REMOVE IN V3
// a list of vendors we know we shouldn't polyfill (we should have set core-js to entry but we didn't so we have to do this)
const VENDORS_TO_NOT_POLYFILL = [`[\\\\/]runtime`, `[\\\\/]domready`, `[\\\\/]router`, `babel-preset-gatsby`, `core-js`, `dom-helpers`, `gatsby-legacy-polyfills`, `gatsby-link`, `gatsby-react-router-scroll`, `invariant`, `lodash`, `mitt`, `prop-types`, `react-dom`, `react`, `regenerator-runtime`, `scheduler`, `scroll-behavior`, `shallow-compare`, `warning`, `webpack`];
const doNotPolyfillRegex = new RegExp(`[\\\\/]node_modules[\\\\/](${VENDORS_TO_NOT_POLYFILL.join(`|`)})[\\\\/]`);
return {
test: /\.(js|mjs)$/,
exclude: modulePath => {
// If dep is user land code, exclude
if (!vendorRegex.test(modulePath)) {
return true;
} // If dep uses Gatsby, exclude
if (modulesThatUseGatsby.some(module => modulePath.includes(module.path))) {
return true;
}
return doNotPolyfillRegex.test(modulePath);
},
type: `javascript/auto`,
use: [loaders.dependencies(jsOptions)]
};
};
rules.dependencies = dependencies;
}
rules.yaml = () => {
return {
test: /\.ya?ml$/,
type: `json`,
use: [loaders.yaml()]
};
};
/**
* Font loader
*/
rules.fonts = () => {
return {
use: [loaders.url()],
test: /\.(eot|otf|ttf|woff(2)?)(\?.*)?$/
};
};
/**
* Loads image assets, inlines images via a data URI if they are below
* the size threshold
*/
rules.images = () => {
return {
use: [loaders.url()],
test: /\.(ico|svg|jpg|jpeg|png|gif|webp|avif)(\?.*)?$/
};
};
/**
* Loads audio and video and inlines them via a data URI if they are below
* the size threshold
*/
rules.media = () => {
return {
use: [loaders.url()],
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/
};
};
/**
* Loads assets without inlining
*/
rules.miscAssets = () => {
return {
use: [loaders.file()],
test: /\.pdf$/
};
};
/**
* CSS style loader.
*/
{
const css = (options = {}) => {
const {
browsers,
...restOptions
} = options;
const use = [!isSSR && loaders.miniCssExtract(restOptions), loaders.css({ ...restOptions,
importLoaders: 1
}), loaders.postcss({
browsers
})].filter(Boolean);
return {
use,
test: /\.css$/
};
};
/**
* CSS style loader, _excludes_ node_modules.
*/
css.internal = makeInternalOnly(css);
css.external = makeExternalOnly(css);
const cssModules = options => {
const rule = css({ ...options,
modules: true
});
delete rule.exclude;
rule.test = /\.module\.css$/;
return rule;
};
rules.css = css;
rules.cssModules = cssModules;
}
/**
* PostCSS loader.
*/
{
const postcss = options => {
return {
test: /\.css$/,
use: [loaders.css({
importLoaders: 1
}), loaders.postcss(options)]
};
};
/**
* PostCSS loader, _excludes_ node_modules.
*/
postcss.internal = makeInternalOnly(postcss);
postcss.external = makeExternalOnly(postcss);
rules.postcss = postcss;
}
/**
* cast rules to IRuleUtils
*/
/**
* Plugins
*/
const plugins = { ..._webpackPlugins.builtinPlugins
};
plugins.minifyJs = ({
terserOptions,
...options
} = {}) => new _terserWebpackPlugin.default({
exclude: /\.min\.js/,
terserOptions: {
ie8: false,
mangle: {
safari10: true
},
parse: {
ecma: 8
},
compress: {
ecma: 5
},
output: {
ecma: 5
},
...terserOptions
},
parallel: Math.max(1, (0, _gatsbyCoreUtils.cpuCoreCount)() - 1),
...options
});
plugins.minifyCss = (options = {
minimizerOptions: {
preset: [`default`, {
svgo: {
full: true,
plugins: [{
// potentially destructive plugins removed - see https://github.com/gatsbyjs/gatsby/issues/15629
// convertShapeToPath: true,
// removeViewBox: true,
removeUselessDefs: true,
addAttributesToSVGElement: true,
addClassesToSVGElement: true,
cleanupAttrs: true,
cleanupEnableBackground: true,
cleanupIDs: true,
cleanupListOfValues: true,
cleanupNumericValues: true,
collapseGroups: true,
convertColors: true,
convertPathData: true,
convertStyleToAttrs: true,
convertTransform: true,
inlineStyles: true,
mergePaths: true,
minifyStyles: true,
moveElemsAttrsToGroup: true,
moveGroupAttrsToElems: true,
prefixIds: true,
removeAttributesBySelector: true,
removeAttrs: true,
removeComments: true,
removeDesc: true,
removeDimensions: true,
removeDoctype: true,
removeEditorsNSData: true,
removeElementsByAttr: true,
removeEmptyAttrs: true,
removeEmptyContainers: true,
removeEmptyText: true,
removeHiddenElems: true,
removeMetadata: true,
removeNonInheritableGroupAttrs: true,
removeOffCanvasPaths: true,
removeRasterImages: true,
removeScriptElement: true,
removeStyleElement: true,
removeTitle: true,
removeUnknownsAndDefaults: true,
removeUnusedNS: true,
removeUselessStrokeAndFill: true,
removeXMLNS: true,
removeXMLProcInst: true,
reusePaths: true,
sortAttrs: true
}]
}
}]
}
}) => new _cssMinimizerWebpackPlugin.default({
parallel: Math.max(1, (0, _gatsbyCoreUtils.cpuCoreCount)() - 1),
...options
});
plugins.fastRefresh = ({
modulesThatUseGatsby
}) => {
const regExpToHack = /node_modules/;
regExpToHack.test = modulePath => {
// when it's not coming from node_modules we treat it as a source file.
if (!vendorRegex.test(modulePath)) {
return false;
} // If the module uses Gatsby as a dependency
// we want to treat it as src because of shadowing
return !modulesThatUseGatsby.some(module => modulePath.includes(module.path));
};
return new _reactRefreshWebpackPlugin.default({
overlay: {
sockIntegration: `whm`,
module: path.join(__dirname, `fast-refresh-module`)
},
// this is a bit hacky - exclude expect string or regexp or array of those
// so this is tricking ReactRefreshWebpackPlugin with providing regexp with
// overwritten .test method
exclude: regExpToHack
});
};
plugins.extractText = options => new _miniCssExtractPlugin.default({ ...options
});
plugins.moment = () => plugins.ignore(/^\.\/locale$/, /moment$/);
plugins.extractStats = () => new _gatsbyWebpackStatsExtractor.GatsbyWebpackStatsExtractor();
plugins.eslintGraphqlSchemaReload = () => new _gatsbyWebpackEslintGraphqlSchemaReloadPlugin.GatsbyWebpackEslintGraphqlSchemaReload();
plugins.virtualModules = () => new _gatsbyWebpackVirtualModules.GatsbyWebpackVirtualModules();
plugins.eslint = schema => {
const options = {
extensions: [`js`, `jsx`],
exclude: [`/node_modules/`, `/bower_components/`, _gatsbyWebpackVirtualModules.VIRTUAL_MODULES_BASE_PATH],
...(0, _eslintConfig.eslintConfig)(schema, jsxRuntimeExists)
}; // @ts-ignore
return new _eslintWebpackPlugin.default(options);
};
plugins.eslintRequired = () => {
const options = {
extensions: [`js`, `jsx`],
exclude: [`/node_modules/`, `/bower_components/`, _gatsbyWebpackVirtualModules.VIRTUAL_MODULES_BASE_PATH],
..._eslintConfig.eslintRequiredConfig
}; // @ts-ignore
return new _eslintWebpackPlugin.default(options);
};
return {
loaders,
rules,
plugins
};
};
exports.createWebpackUtils = createWebpackUtils;
function reactHasJsxRuntime() {
// We've got some complains about the ecosystem not being ready for automatic so we disable it by default.
// People can use a custom babelrc file to support it
// try {
// // React is shipping a new jsx runtime that is to be used with
// // an option on @babel/preset-react called `runtime: automatic`
// // Not every version of React has this jsx-runtime yet. Eventually,
// // it will be backported to older versions of react and this check
// // will become unnecessary.
// // for now we also do the semver check until react 17 is more widely used
// // const react = require(`react/package.json`)
// // return (
// // !!require.resolve(`react/jsx-runtime.js`) &&
// // semver.major(react.version) >= 17
// // )
// } catch (e) {
// // If the require.resolve throws, that means this version of React
// // does not support the jsx runtime.
// }
return false;
}
//# sourceMappingURL=webpack-utils.js.map