mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 10:17:55 +02:00
fix(core): configurePostCss()
should run after configureWebpack()
(#10132)
This commit is contained in:
parent
29b7a4ddbb
commit
ff5039f413
7 changed files with 620 additions and 405 deletions
|
@ -13,7 +13,7 @@ import {isMatch} from 'picomatch';
|
||||||
import commander from 'commander';
|
import commander from 'commander';
|
||||||
import webpack from 'webpack';
|
import webpack from 'webpack';
|
||||||
import {loadContext} from '@docusaurus/core/src/server/site';
|
import {loadContext} from '@docusaurus/core/src/server/site';
|
||||||
import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/utils';
|
import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/configure';
|
||||||
import {sortRoutes} from '@docusaurus/core/src/server/plugins/routeConfig';
|
import {sortRoutes} from '@docusaurus/core/src/server/plugins/routeConfig';
|
||||||
import {posixPath} from '@docusaurus/utils';
|
import {posixPath} from '@docusaurus/utils';
|
||||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||||
|
|
|
@ -15,11 +15,8 @@ import {handleBrokenLinks} from '../server/brokenLinks';
|
||||||
|
|
||||||
import {createBuildClientConfig} from '../webpack/client';
|
import {createBuildClientConfig} from '../webpack/client';
|
||||||
import createServerConfig from '../webpack/server';
|
import createServerConfig from '../webpack/server';
|
||||||
import {
|
import {executePluginsConfigureWebpack} from '../webpack/configure';
|
||||||
executePluginsConfigurePostCss,
|
import {compile} from '../webpack/utils';
|
||||||
executePluginsConfigureWebpack,
|
|
||||||
compile,
|
|
||||||
} from '../webpack/utils';
|
|
||||||
import {PerfLogger} from '../utils';
|
import {PerfLogger} from '../utils';
|
||||||
|
|
||||||
import {loadI18n} from '../server/i18n';
|
import {loadI18n} from '../server/i18n';
|
||||||
|
@ -325,10 +322,6 @@ async function getBuildClientConfig({
|
||||||
bundleAnalyzer: cliOptions.bundleAnalyzer ?? false,
|
bundleAnalyzer: cliOptions.bundleAnalyzer ?? false,
|
||||||
});
|
});
|
||||||
let {config} = result;
|
let {config} = result;
|
||||||
config = executePluginsConfigurePostCss({
|
|
||||||
plugins,
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
config = executePluginsConfigureWebpack({
|
config = executePluginsConfigureWebpack({
|
||||||
plugins,
|
plugins,
|
||||||
config,
|
config,
|
||||||
|
|
|
@ -13,12 +13,11 @@ import WebpackDevServer from 'webpack-dev-server';
|
||||||
import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
|
import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
|
||||||
import {createPollingOptions} from './watcher';
|
import {createPollingOptions} from './watcher';
|
||||||
import {
|
import {
|
||||||
executePluginsConfigurePostCss,
|
|
||||||
executePluginsConfigureWebpack,
|
|
||||||
formatStatsErrorMessage,
|
formatStatsErrorMessage,
|
||||||
getHttpsConfig,
|
getHttpsConfig,
|
||||||
printStatsWarnings,
|
printStatsWarnings,
|
||||||
} from '../../webpack/utils';
|
} from '../../webpack/utils';
|
||||||
|
import {executePluginsConfigureWebpack} from '../../webpack/configure';
|
||||||
import {createStartClientConfig} from '../../webpack/client';
|
import {createStartClientConfig} from '../../webpack/client';
|
||||||
import type {StartCLIOptions} from './start';
|
import type {StartCLIOptions} from './start';
|
||||||
import type {Props} from '@docusaurus/types';
|
import type {Props} from '@docusaurus/types';
|
||||||
|
@ -135,7 +134,6 @@ async function getStartClientConfig({
|
||||||
minify,
|
minify,
|
||||||
poll,
|
poll,
|
||||||
});
|
});
|
||||||
config = executePluginsConfigurePostCss({plugins, config});
|
|
||||||
config = executePluginsConfigureWebpack({
|
config = executePluginsConfigureWebpack({
|
||||||
plugins,
|
plugins,
|
||||||
config,
|
config,
|
||||||
|
|
458
packages/docusaurus/src/webpack/__tests__/configure.test.ts
Normal file
458
packages/docusaurus/src/webpack/__tests__/configure.test.ts
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as webpack from 'webpack';
|
||||||
|
import {fromPartial} from '@total-typescript/shoehorn';
|
||||||
|
import {
|
||||||
|
applyConfigureWebpack,
|
||||||
|
applyConfigurePostCss,
|
||||||
|
executePluginsConfigureWebpack,
|
||||||
|
} from '../configure';
|
||||||
|
import type {Configuration} from 'webpack';
|
||||||
|
import type {LoadedPlugin, Plugin} from '@docusaurus/types';
|
||||||
|
|
||||||
|
describe('extending generated webpack config', () => {
|
||||||
|
it('direct mutation on generated webpack config object', async () => {
|
||||||
|
// Fake generated webpack config
|
||||||
|
let config: Configuration = {
|
||||||
|
output: {
|
||||||
|
path: __dirname,
|
||||||
|
filename: 'bundle.js',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error: Testing an edge-case that we did not write types for
|
||||||
|
const configureWebpack: NonNullable<Plugin['configureWebpack']> = (
|
||||||
|
generatedConfig,
|
||||||
|
isServer,
|
||||||
|
) => {
|
||||||
|
if (!isServer) {
|
||||||
|
generatedConfig.entry = 'entry.js';
|
||||||
|
generatedConfig.output = {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: 'new.bundle.js',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Implicitly returning undefined to test null-safety
|
||||||
|
};
|
||||||
|
|
||||||
|
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||||
|
content: 42,
|
||||||
|
});
|
||||||
|
expect(config).toEqual({
|
||||||
|
entry: 'entry.js',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: 'new.bundle.js',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const errors = webpack.validate(config);
|
||||||
|
expect(errors).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('webpack-merge with user webpack config object', async () => {
|
||||||
|
let config: Configuration = {
|
||||||
|
output: {
|
||||||
|
path: __dirname,
|
||||||
|
filename: 'bundle.js',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const configureWebpack: Plugin['configureWebpack'] = () => ({
|
||||||
|
entry: 'entry.js',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: 'new.bundle.js',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||||
|
content: 42,
|
||||||
|
});
|
||||||
|
expect(config).toEqual({
|
||||||
|
entry: 'entry.js',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: 'new.bundle.js',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const errors = webpack.validate(config);
|
||||||
|
expect(errors).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('webpack-merge with custom strategy', async () => {
|
||||||
|
const config: Configuration = {
|
||||||
|
module: {
|
||||||
|
rules: [{use: 'xxx'}, {use: 'yyy'}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createConfigureWebpack =
|
||||||
|
(mergeStrategy?: {
|
||||||
|
[key: string]: 'prepend' | 'append';
|
||||||
|
}): NonNullable<Plugin['configureWebpack']> =>
|
||||||
|
() => ({
|
||||||
|
module: {
|
||||||
|
rules: [{use: 'zzz'}],
|
||||||
|
},
|
||||||
|
mergeStrategy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultStrategyMergeConfig = applyConfigureWebpack(
|
||||||
|
createConfigureWebpack(),
|
||||||
|
config,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
{content: 42},
|
||||||
|
);
|
||||||
|
expect(defaultStrategyMergeConfig).toEqual({
|
||||||
|
module: {
|
||||||
|
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const prependRulesStrategyConfig = applyConfigureWebpack(
|
||||||
|
createConfigureWebpack({'module.rules': 'prepend'}),
|
||||||
|
config,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
{content: 42},
|
||||||
|
);
|
||||||
|
expect(prependRulesStrategyConfig).toEqual({
|
||||||
|
module: {
|
||||||
|
rules: [{use: 'zzz'}, {use: 'xxx'}, {use: 'yyy'}],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const uselessMergeStrategyConfig = applyConfigureWebpack(
|
||||||
|
createConfigureWebpack({uselessAttributeName: 'append'}),
|
||||||
|
config,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
{content: 42},
|
||||||
|
);
|
||||||
|
expect(uselessMergeStrategyConfig).toEqual({
|
||||||
|
module: {
|
||||||
|
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('extending PostCSS', () => {
|
||||||
|
it('user plugin should be appended in PostCSS loader', () => {
|
||||||
|
let webpackConfig: Configuration = {
|
||||||
|
output: {
|
||||||
|
path: __dirname,
|
||||||
|
filename: 'bundle.js',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: 'any',
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'some-loader-1',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'some-loader-2',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader-1',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: [['default-postcss-loader-1-plugin']],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'some-loader-3',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: '2nd-test',
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader-2',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: [['default-postcss-loader-2-plugin']],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createFakePlugin(name: string) {
|
||||||
|
return [name, {}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run multiple times: ensure last run does not override previous runs
|
||||||
|
webpackConfig = applyConfigurePostCss(
|
||||||
|
(postCssOptions) => ({
|
||||||
|
...postCssOptions,
|
||||||
|
plugins: [
|
||||||
|
...postCssOptions.plugins,
|
||||||
|
createFakePlugin('postcss-plugin-1'),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
webpackConfig = applyConfigurePostCss(
|
||||||
|
(postCssOptions) => ({
|
||||||
|
...postCssOptions,
|
||||||
|
plugins: [
|
||||||
|
createFakePlugin('postcss-plugin-2'),
|
||||||
|
...postCssOptions.plugins,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
webpackConfig = applyConfigurePostCss(
|
||||||
|
(postCssOptions) => ({
|
||||||
|
...postCssOptions,
|
||||||
|
plugins: [
|
||||||
|
...postCssOptions.plugins,
|
||||||
|
createFakePlugin('postcss-plugin-3'),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error: relax type
|
||||||
|
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];
|
||||||
|
expect(postCssLoader1.loader).toBe('postcss-loader-1');
|
||||||
|
|
||||||
|
const pluginNames1 = postCssLoader1.options.postcssOptions.plugins.map(
|
||||||
|
(p: unknown[]) => p[0],
|
||||||
|
);
|
||||||
|
expect(pluginNames1).toHaveLength(4);
|
||||||
|
expect(pluginNames1).toEqual([
|
||||||
|
'postcss-plugin-2',
|
||||||
|
'default-postcss-loader-1-plugin',
|
||||||
|
'postcss-plugin-1',
|
||||||
|
'postcss-plugin-3',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// @ts-expect-error: relax type
|
||||||
|
const postCssLoader2 = webpackConfig.module?.rules[1].use[0];
|
||||||
|
expect(postCssLoader2.loader).toBe('postcss-loader-2');
|
||||||
|
|
||||||
|
const pluginNames2 = postCssLoader2.options.postcssOptions.plugins.map(
|
||||||
|
(p: unknown[]) => p[0],
|
||||||
|
);
|
||||||
|
expect(pluginNames2).toHaveLength(4);
|
||||||
|
expect(pluginNames2).toEqual([
|
||||||
|
'postcss-plugin-2',
|
||||||
|
'default-postcss-loader-2-plugin',
|
||||||
|
'postcss-plugin-1',
|
||||||
|
'postcss-plugin-3',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('executePluginsConfigureWebpack', () => {
|
||||||
|
function fakePlugin(partialPlugin: Partial<LoadedPlugin>): LoadedPlugin {
|
||||||
|
return fromPartial({
|
||||||
|
...partialPlugin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('can merge Webpack aliases of 2 plugins into base config', () => {
|
||||||
|
const config = executePluginsConfigureWebpack({
|
||||||
|
config: {resolve: {alias: {'initial-alias': 'initial-alias-value'}}},
|
||||||
|
isServer: false,
|
||||||
|
jsLoader: 'babel',
|
||||||
|
plugins: [
|
||||||
|
fakePlugin({
|
||||||
|
configureWebpack: () => {
|
||||||
|
return {resolve: {alias: {'p1-alias': 'p1-alias-value'}}};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fakePlugin({
|
||||||
|
configureWebpack: () => {
|
||||||
|
return {resolve: {alias: {'p2-alias': 'p2-alias-value'}}};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(config).toMatchInlineSnapshot(
|
||||||
|
{},
|
||||||
|
`
|
||||||
|
{
|
||||||
|
"resolve": {
|
||||||
|
"alias": {
|
||||||
|
"initial-alias": "initial-alias-value",
|
||||||
|
"p1-alias": "p1-alias-value",
|
||||||
|
"p2-alias": "p2-alias-value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can configurePostCSS() for all loaders added through configureWebpack()', () => {
|
||||||
|
const config = executePluginsConfigureWebpack({
|
||||||
|
config: {},
|
||||||
|
isServer: false,
|
||||||
|
jsLoader: 'babel',
|
||||||
|
plugins: [
|
||||||
|
fakePlugin({
|
||||||
|
configurePostCss: (postCssOptions) => {
|
||||||
|
// Imperative mutation should work
|
||||||
|
postCssOptions.plugins.push('p1-added-postcss-plugin');
|
||||||
|
return postCssOptions;
|
||||||
|
},
|
||||||
|
configureWebpack: () => {
|
||||||
|
return {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.module.scss$/,
|
||||||
|
use: 'some-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: ['p1-initial-postcss-plugin'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fakePlugin({
|
||||||
|
configurePostCss: (postCssOptions) => {
|
||||||
|
postCssOptions.plugins.push('p2-added-postcss-plugin');
|
||||||
|
return postCssOptions;
|
||||||
|
},
|
||||||
|
configureWebpack: () => {
|
||||||
|
return {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.module.scss$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: ['p2-initial-postcss-plugin'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fakePlugin({
|
||||||
|
configurePostCss: (postCssOptions) => {
|
||||||
|
// Functional/immutable copy mutation should work
|
||||||
|
return {
|
||||||
|
...postCssOptions,
|
||||||
|
plugins: [...postCssOptions.plugins, 'p3-added-postcss-plugin'],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
configureWebpack: () => {
|
||||||
|
return {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.module.scss$/,
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
use: 'some-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: ['p3-initial-postcss-plugin'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(config.module.rules).toHaveLength(3);
|
||||||
|
expect(config.module.rules[0]).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"postcssOptions": {
|
||||||
|
"plugins": [
|
||||||
|
"p1-initial-postcss-plugin",
|
||||||
|
"p1-added-postcss-plugin",
|
||||||
|
"p2-added-postcss-plugin",
|
||||||
|
"p3-added-postcss-plugin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test": /\\\\\\.module\\.scss\\$/,
|
||||||
|
"use": "some-loader",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(config.module.rules[1]).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"test": /\\\\\\.module\\.scss\\$/,
|
||||||
|
"use": [
|
||||||
|
{
|
||||||
|
"loader": "postcss-loader",
|
||||||
|
"options": {
|
||||||
|
"postcssOptions": {
|
||||||
|
"plugins": [
|
||||||
|
"p2-initial-postcss-plugin",
|
||||||
|
"p1-added-postcss-plugin",
|
||||||
|
"p2-added-postcss-plugin",
|
||||||
|
"p3-added-postcss-plugin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(config.module.rules[2]).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"postcssOptions": {
|
||||||
|
"plugins": [
|
||||||
|
"p3-initial-postcss-plugin",
|
||||||
|
"p1-added-postcss-plugin",
|
||||||
|
"p2-added-postcss-plugin",
|
||||||
|
"p3-added-postcss-plugin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"use": "some-loader",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"test": /\\\\\\.module\\.scss\\$/,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,15 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import webpack, {type Configuration, type RuleSetRule} from 'webpack';
|
import {getCustomizableJSLoader, getHttpsConfig} from '../utils';
|
||||||
|
import type {RuleSetRule} from 'webpack';
|
||||||
import {
|
|
||||||
getCustomizableJSLoader,
|
|
||||||
applyConfigureWebpack,
|
|
||||||
applyConfigurePostCss,
|
|
||||||
getHttpsConfig,
|
|
||||||
} from '../utils';
|
|
||||||
import type {Plugin} from '@docusaurus/types';
|
|
||||||
|
|
||||||
describe('customize JS loader', () => {
|
describe('customize JS loader', () => {
|
||||||
it('getCustomizableJSLoader defaults to babel loader', () => {
|
it('getCustomizableJSLoader defaults to babel loader', () => {
|
||||||
|
@ -50,255 +43,6 @@ describe('customize JS loader', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('extending generated webpack config', () => {
|
|
||||||
it('direct mutation on generated webpack config object', async () => {
|
|
||||||
// Fake generated webpack config
|
|
||||||
let config: Configuration = {
|
|
||||||
output: {
|
|
||||||
path: __dirname,
|
|
||||||
filename: 'bundle.js',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-expect-error: Testing an edge-case that we did not write types for
|
|
||||||
const configureWebpack: NonNullable<Plugin['configureWebpack']> = (
|
|
||||||
generatedConfig,
|
|
||||||
isServer,
|
|
||||||
) => {
|
|
||||||
if (!isServer) {
|
|
||||||
generatedConfig.entry = 'entry.js';
|
|
||||||
generatedConfig.output = {
|
|
||||||
path: path.join(__dirname, 'dist'),
|
|
||||||
filename: 'new.bundle.js',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Implicitly returning undefined to test null-safety
|
|
||||||
};
|
|
||||||
|
|
||||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
|
||||||
content: 42,
|
|
||||||
});
|
|
||||||
expect(config).toEqual({
|
|
||||||
entry: 'entry.js',
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'dist'),
|
|
||||||
filename: 'new.bundle.js',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const errors = webpack.validate(config);
|
|
||||||
expect(errors).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('webpack-merge with user webpack config object', async () => {
|
|
||||||
let config: Configuration = {
|
|
||||||
output: {
|
|
||||||
path: __dirname,
|
|
||||||
filename: 'bundle.js',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const configureWebpack: Plugin['configureWebpack'] = () => ({
|
|
||||||
entry: 'entry.js',
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'dist'),
|
|
||||||
filename: 'new.bundle.js',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
|
||||||
content: 42,
|
|
||||||
});
|
|
||||||
expect(config).toEqual({
|
|
||||||
entry: 'entry.js',
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'dist'),
|
|
||||||
filename: 'new.bundle.js',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const errors = webpack.validate(config);
|
|
||||||
expect(errors).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('webpack-merge with custom strategy', async () => {
|
|
||||||
const config: Configuration = {
|
|
||||||
module: {
|
|
||||||
rules: [{use: 'xxx'}, {use: 'yyy'}],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createConfigureWebpack =
|
|
||||||
(mergeStrategy?: {
|
|
||||||
[key: string]: 'prepend' | 'append';
|
|
||||||
}): NonNullable<Plugin['configureWebpack']> =>
|
|
||||||
() => ({
|
|
||||||
module: {
|
|
||||||
rules: [{use: 'zzz'}],
|
|
||||||
},
|
|
||||||
mergeStrategy,
|
|
||||||
});
|
|
||||||
|
|
||||||
const defaultStrategyMergeConfig = applyConfigureWebpack(
|
|
||||||
createConfigureWebpack(),
|
|
||||||
config,
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
{content: 42},
|
|
||||||
);
|
|
||||||
expect(defaultStrategyMergeConfig).toEqual({
|
|
||||||
module: {
|
|
||||||
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const prependRulesStrategyConfig = applyConfigureWebpack(
|
|
||||||
createConfigureWebpack({'module.rules': 'prepend'}),
|
|
||||||
config,
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
{content: 42},
|
|
||||||
);
|
|
||||||
expect(prependRulesStrategyConfig).toEqual({
|
|
||||||
module: {
|
|
||||||
rules: [{use: 'zzz'}, {use: 'xxx'}, {use: 'yyy'}],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const uselessMergeStrategyConfig = applyConfigureWebpack(
|
|
||||||
createConfigureWebpack({uselessAttributeName: 'append'}),
|
|
||||||
config,
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
{content: 42},
|
|
||||||
);
|
|
||||||
expect(uselessMergeStrategyConfig).toEqual({
|
|
||||||
module: {
|
|
||||||
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('extending PostCSS', () => {
|
|
||||||
it('user plugin should be appended in PostCSS loader', () => {
|
|
||||||
let webpackConfig: Configuration = {
|
|
||||||
output: {
|
|
||||||
path: __dirname,
|
|
||||||
filename: 'bundle.js',
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: 'any',
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'some-loader-1',
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'some-loader-2',
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'postcss-loader-1',
|
|
||||||
options: {
|
|
||||||
postcssOptions: {
|
|
||||||
plugins: [['default-postcss-loader-1-plugin']],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'some-loader-3',
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: '2nd-test',
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'postcss-loader-2',
|
|
||||||
options: {
|
|
||||||
postcssOptions: {
|
|
||||||
plugins: [['default-postcss-loader-2-plugin']],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function createFakePlugin(name: string) {
|
|
||||||
return [name, {}];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run multiple times: ensure last run does not override previous runs
|
|
||||||
webpackConfig = applyConfigurePostCss(
|
|
||||||
(postCssOptions) => ({
|
|
||||||
...postCssOptions,
|
|
||||||
plugins: [
|
|
||||||
...postCssOptions.plugins,
|
|
||||||
createFakePlugin('postcss-plugin-1'),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
webpackConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
webpackConfig = applyConfigurePostCss(
|
|
||||||
(postCssOptions) => ({
|
|
||||||
...postCssOptions,
|
|
||||||
plugins: [
|
|
||||||
createFakePlugin('postcss-plugin-2'),
|
|
||||||
...postCssOptions.plugins,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
webpackConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
webpackConfig = applyConfigurePostCss(
|
|
||||||
(postCssOptions) => ({
|
|
||||||
...postCssOptions,
|
|
||||||
plugins: [
|
|
||||||
...postCssOptions.plugins,
|
|
||||||
createFakePlugin('postcss-plugin-3'),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
webpackConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
// @ts-expect-error: relax type
|
|
||||||
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];
|
|
||||||
expect(postCssLoader1.loader).toBe('postcss-loader-1');
|
|
||||||
|
|
||||||
const pluginNames1 = postCssLoader1.options.postcssOptions.plugins.map(
|
|
||||||
(p: unknown[]) => p[0],
|
|
||||||
);
|
|
||||||
expect(pluginNames1).toHaveLength(4);
|
|
||||||
expect(pluginNames1).toEqual([
|
|
||||||
'postcss-plugin-2',
|
|
||||||
'default-postcss-loader-1-plugin',
|
|
||||||
'postcss-plugin-1',
|
|
||||||
'postcss-plugin-3',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// @ts-expect-error: relax type
|
|
||||||
const postCssLoader2 = webpackConfig.module?.rules[1].use[0];
|
|
||||||
expect(postCssLoader2.loader).toBe('postcss-loader-2');
|
|
||||||
|
|
||||||
const pluginNames2 = postCssLoader2.options.postcssOptions.plugins.map(
|
|
||||||
(p: unknown[]) => p[0],
|
|
||||||
);
|
|
||||||
expect(pluginNames2).toHaveLength(4);
|
|
||||||
expect(pluginNames2).toEqual([
|
|
||||||
'postcss-plugin-2',
|
|
||||||
'default-postcss-loader-2-plugin',
|
|
||||||
'postcss-plugin-1',
|
|
||||||
'postcss-plugin-3',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getHttpsConfig', () => {
|
describe('getHttpsConfig', () => {
|
||||||
const originalEnv = process.env;
|
const originalEnv = process.env;
|
||||||
|
|
||||||
|
|
156
packages/docusaurus/src/webpack/configure.ts
Normal file
156
packages/docusaurus/src/webpack/configure.ts
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
mergeWithCustomize,
|
||||||
|
customizeArray,
|
||||||
|
customizeObject,
|
||||||
|
} from 'webpack-merge';
|
||||||
|
import {getCustomizableJSLoader, getStyleLoaders} from './utils';
|
||||||
|
|
||||||
|
import type {Configuration, RuleSetRule} from 'webpack';
|
||||||
|
import type {
|
||||||
|
Plugin,
|
||||||
|
PostCssOptions,
|
||||||
|
ConfigureWebpackUtils,
|
||||||
|
LoadedPlugin,
|
||||||
|
} from '@docusaurus/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to modify webpack config
|
||||||
|
* @param configureWebpack a webpack config or a function to modify config
|
||||||
|
* @param config initial webpack config
|
||||||
|
* @param isServer indicates if this is a server webpack configuration
|
||||||
|
* @param jsLoader custom js loader config
|
||||||
|
* @param content content loaded by the plugin
|
||||||
|
* @returns final/ modified webpack config
|
||||||
|
*/
|
||||||
|
export function applyConfigureWebpack(
|
||||||
|
configureWebpack: NonNullable<Plugin['configureWebpack']>,
|
||||||
|
config: Configuration,
|
||||||
|
isServer: boolean,
|
||||||
|
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined,
|
||||||
|
content: unknown,
|
||||||
|
): Configuration {
|
||||||
|
// Export some utility functions
|
||||||
|
const utils: ConfigureWebpackUtils = {
|
||||||
|
getStyleLoaders,
|
||||||
|
getJSLoader: getCustomizableJSLoader(jsLoader),
|
||||||
|
};
|
||||||
|
if (typeof configureWebpack === 'function') {
|
||||||
|
const {mergeStrategy, ...res} =
|
||||||
|
configureWebpack(config, isServer, utils, content) ?? {};
|
||||||
|
const customizeRules = mergeStrategy ?? {};
|
||||||
|
return mergeWithCustomize({
|
||||||
|
customizeArray: customizeArray(customizeRules),
|
||||||
|
customizeObject: customizeObject(customizeRules),
|
||||||
|
})(config, res);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyConfigurePostCss(
|
||||||
|
configurePostCss: NonNullable<Plugin['configurePostCss']>,
|
||||||
|
config: Configuration,
|
||||||
|
): Configuration {
|
||||||
|
type LocalPostCSSLoader = object & {
|
||||||
|
options: {postcssOptions: PostCssOptions};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Not ideal heuristic but good enough for our use-case?
|
||||||
|
function isPostCssLoader(loader: unknown): loader is LocalPostCSSLoader {
|
||||||
|
return !!(loader as LocalPostCSSLoader)?.options?.postcssOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does not handle all edge cases, but good enough for now
|
||||||
|
function overridePostCssOptions(entry: RuleSetRule) {
|
||||||
|
if (isPostCssLoader(entry)) {
|
||||||
|
entry.options.postcssOptions = configurePostCss(
|
||||||
|
entry.options.postcssOptions,
|
||||||
|
);
|
||||||
|
} else if (Array.isArray(entry.oneOf)) {
|
||||||
|
entry.oneOf.forEach((r) => {
|
||||||
|
if (r) {
|
||||||
|
overridePostCssOptions(r);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (Array.isArray(entry.use)) {
|
||||||
|
entry.use
|
||||||
|
.filter((u) => typeof u === 'object')
|
||||||
|
.forEach((rule) => overridePostCssOptions(rule as RuleSetRule));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.module?.rules?.forEach((rule) =>
|
||||||
|
overridePostCssOptions(rule as RuleSetRule),
|
||||||
|
);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin Lifecycle - configurePostCss()
|
||||||
|
function executePluginsConfigurePostCss({
|
||||||
|
plugins,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
plugins: LoadedPlugin[];
|
||||||
|
config: Configuration;
|
||||||
|
}): Configuration {
|
||||||
|
let resultConfig = config;
|
||||||
|
plugins.forEach((plugin) => {
|
||||||
|
const {configurePostCss} = plugin;
|
||||||
|
if (configurePostCss) {
|
||||||
|
resultConfig = applyConfigurePostCss(
|
||||||
|
configurePostCss.bind(plugin),
|
||||||
|
resultConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin Lifecycle - configureWebpack()
|
||||||
|
export function executePluginsConfigureWebpack({
|
||||||
|
plugins,
|
||||||
|
config,
|
||||||
|
isServer,
|
||||||
|
jsLoader,
|
||||||
|
}: {
|
||||||
|
plugins: LoadedPlugin[];
|
||||||
|
config: Configuration;
|
||||||
|
isServer: boolean;
|
||||||
|
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined;
|
||||||
|
}): Configuration {
|
||||||
|
// Step1 - Configure Webpack
|
||||||
|
let resultConfig = config;
|
||||||
|
plugins.forEach((plugin) => {
|
||||||
|
const {configureWebpack} = plugin;
|
||||||
|
if (configureWebpack) {
|
||||||
|
resultConfig = applyConfigureWebpack(
|
||||||
|
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||||
|
resultConfig,
|
||||||
|
isServer,
|
||||||
|
jsLoader,
|
||||||
|
plugin.content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step2 - For client code, configure PostCSS
|
||||||
|
// The order matters! We want to configure PostCSS on loaders
|
||||||
|
// that were potentially added by configureWebpack
|
||||||
|
// See https://github.com/facebook/docusaurus/issues/10106
|
||||||
|
// Note: it's useless to configure postCSS for the server
|
||||||
|
if (!isServer) {
|
||||||
|
resultConfig = executePluginsConfigurePostCss({
|
||||||
|
plugins,
|
||||||
|
config: resultConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultConfig;
|
||||||
|
}
|
|
@ -11,20 +11,9 @@ import crypto from 'crypto';
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils';
|
import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils';
|
||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||||
import {
|
|
||||||
mergeWithCustomize,
|
|
||||||
customizeArray,
|
|
||||||
customizeObject,
|
|
||||||
} from 'webpack-merge';
|
|
||||||
import webpack, {type Configuration, type RuleSetRule} from 'webpack';
|
import webpack, {type Configuration, type RuleSetRule} from 'webpack';
|
||||||
import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages';
|
import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages';
|
||||||
import type {TransformOptions} from '@babel/core';
|
import type {TransformOptions} from '@babel/core';
|
||||||
import type {
|
|
||||||
Plugin,
|
|
||||||
PostCssOptions,
|
|
||||||
ConfigureWebpackUtils,
|
|
||||||
LoadedPlugin,
|
|
||||||
} from '@docusaurus/types';
|
|
||||||
|
|
||||||
export function formatStatsErrorMessage(
|
export function formatStatsErrorMessage(
|
||||||
statsJson: ReturnType<webpack.Stats['toJson']> | undefined,
|
statsJson: ReturnType<webpack.Stats['toJson']> | undefined,
|
||||||
|
@ -181,129 +170,6 @@ export const getCustomizableJSLoader =
|
||||||
? getDefaultBabelLoader({isServer, babelOptions})
|
? getDefaultBabelLoader({isServer, babelOptions})
|
||||||
: jsLoader(isServer);
|
: jsLoader(isServer);
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to modify webpack config
|
|
||||||
* @param configureWebpack a webpack config or a function to modify config
|
|
||||||
* @param config initial webpack config
|
|
||||||
* @param isServer indicates if this is a server webpack configuration
|
|
||||||
* @param jsLoader custom js loader config
|
|
||||||
* @param content content loaded by the plugin
|
|
||||||
* @returns final/ modified webpack config
|
|
||||||
*/
|
|
||||||
export function applyConfigureWebpack(
|
|
||||||
configureWebpack: NonNullable<Plugin['configureWebpack']>,
|
|
||||||
config: Configuration,
|
|
||||||
isServer: boolean,
|
|
||||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined,
|
|
||||||
content: unknown,
|
|
||||||
): Configuration {
|
|
||||||
// Export some utility functions
|
|
||||||
const utils: ConfigureWebpackUtils = {
|
|
||||||
getStyleLoaders,
|
|
||||||
getJSLoader: getCustomizableJSLoader(jsLoader),
|
|
||||||
};
|
|
||||||
if (typeof configureWebpack === 'function') {
|
|
||||||
const {mergeStrategy, ...res} =
|
|
||||||
configureWebpack(config, isServer, utils, content) ?? {};
|
|
||||||
const customizeRules = mergeStrategy ?? {};
|
|
||||||
return mergeWithCustomize({
|
|
||||||
customizeArray: customizeArray(customizeRules),
|
|
||||||
customizeObject: customizeObject(customizeRules),
|
|
||||||
})(config, res);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyConfigurePostCss(
|
|
||||||
configurePostCss: NonNullable<Plugin['configurePostCss']>,
|
|
||||||
config: Configuration,
|
|
||||||
): Configuration {
|
|
||||||
type LocalPostCSSLoader = object & {
|
|
||||||
options: {postcssOptions: PostCssOptions};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Not ideal heuristic but good enough for our use-case?
|
|
||||||
function isPostCssLoader(loader: unknown): loader is LocalPostCSSLoader {
|
|
||||||
return !!(loader as LocalPostCSSLoader)?.options?.postcssOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not handle all edge cases, but good enough for now
|
|
||||||
function overridePostCssOptions(entry: RuleSetRule) {
|
|
||||||
if (isPostCssLoader(entry)) {
|
|
||||||
entry.options.postcssOptions = configurePostCss(
|
|
||||||
entry.options.postcssOptions,
|
|
||||||
);
|
|
||||||
} else if (Array.isArray(entry.oneOf)) {
|
|
||||||
entry.oneOf.forEach((r) => {
|
|
||||||
if (r) {
|
|
||||||
overridePostCssOptions(r);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (Array.isArray(entry.use)) {
|
|
||||||
entry.use
|
|
||||||
.filter((u) => typeof u === 'object')
|
|
||||||
.forEach((rule) => overridePostCssOptions(rule as RuleSetRule));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.module?.rules?.forEach((rule) =>
|
|
||||||
overridePostCssOptions(rule as RuleSetRule),
|
|
||||||
);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugin Lifecycle - configurePostCss()
|
|
||||||
export function executePluginsConfigurePostCss({
|
|
||||||
plugins,
|
|
||||||
config,
|
|
||||||
}: {
|
|
||||||
plugins: LoadedPlugin[];
|
|
||||||
config: Configuration;
|
|
||||||
}): Configuration {
|
|
||||||
let resultConfig = config;
|
|
||||||
plugins.forEach((plugin) => {
|
|
||||||
const {configurePostCss} = plugin;
|
|
||||||
if (configurePostCss) {
|
|
||||||
resultConfig = applyConfigurePostCss(
|
|
||||||
configurePostCss.bind(plugin),
|
|
||||||
resultConfig,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return resultConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugin Lifecycle - configureWebpack()
|
|
||||||
export function executePluginsConfigureWebpack({
|
|
||||||
plugins,
|
|
||||||
config,
|
|
||||||
isServer,
|
|
||||||
jsLoader,
|
|
||||||
}: {
|
|
||||||
plugins: LoadedPlugin[];
|
|
||||||
config: Configuration;
|
|
||||||
isServer: boolean;
|
|
||||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined;
|
|
||||||
}): Configuration {
|
|
||||||
let resultConfig = config;
|
|
||||||
|
|
||||||
plugins.forEach((plugin) => {
|
|
||||||
const {configureWebpack} = plugin;
|
|
||||||
if (configureWebpack) {
|
|
||||||
resultConfig = applyConfigureWebpack(
|
|
||||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
|
||||||
resultConfig,
|
|
||||||
isServer,
|
|
||||||
jsLoader,
|
|
||||||
plugin.content,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return resultConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Error {
|
interface Error {
|
||||||
/** @see https://webpack.js.org/api/node/#error-handling */
|
/** @see https://webpack.js.org/api/node/#error-handling */
|
||||||
|
|
Loading…
Add table
Reference in a new issue