@docusaurus/bundler 3.5.2 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/lib/compiler.d.ts +22 -0
- package/lib/compiler.d.ts.map +1 -0
- package/lib/compiler.js +66 -0
- package/lib/compiler.js.map +1 -0
- package/lib/currentBundler.d.ts +33 -0
- package/lib/currentBundler.d.ts.map +1 -0
- package/lib/currentBundler.js +73 -0
- package/lib/currentBundler.js.map +1 -0
- package/lib/importFaster.d.ts +17 -0
- package/lib/importFaster.d.ts.map +1 -0
- package/lib/importFaster.js +57 -0
- package/lib/importFaster.js.map +1 -0
- package/lib/index.d.ts +6 -22
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +18 -52
- package/lib/index.js.map +1 -1
- package/lib/loaders/jsLoader.d.ts +16 -0
- package/lib/loaders/jsLoader.d.ts.map +1 -0
- package/lib/loaders/jsLoader.js +67 -0
- package/lib/loaders/jsLoader.js.map +1 -0
- package/lib/loaders/styleLoader.d.ts +11 -0
- package/lib/loaders/styleLoader.d.ts.map +1 -0
- package/lib/loaders/styleLoader.js +68 -0
- package/lib/loaders/styleLoader.js.map +1 -0
- package/lib/minification.d.ts +14 -0
- package/lib/minification.d.ts.map +1 -0
- package/lib/minification.js +147 -0
- package/lib/minification.js.map +1 -0
- package/lib/minifyHtml.d.ts +18 -0
- package/lib/minifyHtml.d.ts.map +1 -0
- package/lib/minifyHtml.js +91 -0
- package/lib/minifyHtml.js.map +1 -0
- package/package.json +35 -5
- package/src/compiler.ts +87 -0
- package/src/currentBundler.ts +106 -0
- package/src/importFaster.ts +77 -0
- package/src/index.ts +17 -82
- package/src/loaders/jsLoader.ts +89 -0
- package/src/loaders/styleLoader.ts +80 -0
- package/src/minification.ts +176 -0
- package/src/minifyHtml.ts +111 -0
package/src/index.ts
CHANGED
|
@@ -5,85 +5,20 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function isRspack(siteConfig: SiteConfigSlice): boolean {
|
|
26
|
-
return siteConfig.future.experimental_faster.rspackBundler;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function getCurrentBundler({
|
|
30
|
-
siteConfig,
|
|
31
|
-
}: {
|
|
32
|
-
siteConfig: SiteConfigSlice;
|
|
33
|
-
}): Promise<CurrentBundler> {
|
|
34
|
-
if (isRspack(siteConfig)) {
|
|
35
|
-
// TODO add support for Rspack
|
|
36
|
-
logger.error(
|
|
37
|
-
'Rspack bundler is not supported yet, will use Webpack instead',
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
name: 'webpack',
|
|
42
|
-
instance: webpack,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function getCSSExtractPlugin({
|
|
47
|
-
currentBundler,
|
|
48
|
-
}: {
|
|
49
|
-
currentBundler: CurrentBundler;
|
|
50
|
-
}): Promise<typeof MiniCssExtractPlugin> {
|
|
51
|
-
if (currentBundler.name === 'rspack') {
|
|
52
|
-
throw new Error('Rspack bundler is not supported yet');
|
|
53
|
-
}
|
|
54
|
-
return MiniCssExtractPlugin;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function getCopyPlugin({
|
|
58
|
-
currentBundler,
|
|
59
|
-
}: {
|
|
60
|
-
currentBundler: CurrentBundler;
|
|
61
|
-
}): Promise<typeof CopyWebpackPlugin> {
|
|
62
|
-
if (currentBundler.name === 'rspack') {
|
|
63
|
-
throw new Error('Rspack bundler is not supported yet');
|
|
64
|
-
}
|
|
65
|
-
// https://github.com/webpack-contrib/copy-webpack-plugin
|
|
66
|
-
return CopyWebpackPlugin;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function getProgressBarPlugin({
|
|
70
|
-
currentBundler,
|
|
71
|
-
}: {
|
|
72
|
-
currentBundler: CurrentBundler;
|
|
73
|
-
}): Promise<typeof WebpackBar> {
|
|
74
|
-
if (currentBundler.name === 'rspack') {
|
|
75
|
-
class CustomRspackProgressPlugin extends currentBundler.instance
|
|
76
|
-
.ProgressPlugin {
|
|
77
|
-
constructor({name}: {name: string}) {
|
|
78
|
-
// TODO add support for color
|
|
79
|
-
// Unfortunately the rspack.ProgressPlugin does not have a name option
|
|
80
|
-
// See https://rspack.dev/plugins/webpack/progress-plugin
|
|
81
|
-
// @ts-expect-error: adapt Rspack ProgressPlugin constructor
|
|
82
|
-
super({prefix: name});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return CustomRspackProgressPlugin as typeof WebpackBar;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return WebpackBar;
|
|
89
|
-
}
|
|
8
|
+
export {printStatsWarnings, formatStatsErrorMessage, compile} from './compiler';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
getCurrentBundler,
|
|
12
|
+
getCSSExtractPlugin,
|
|
13
|
+
getCopyPlugin,
|
|
14
|
+
getProgressBarPlugin,
|
|
15
|
+
} from './currentBundler';
|
|
16
|
+
|
|
17
|
+
export {getMinimizers} from './minification';
|
|
18
|
+
export {
|
|
19
|
+
getHtmlMinifier,
|
|
20
|
+
type HtmlMinifier,
|
|
21
|
+
type HtmlMinifierType,
|
|
22
|
+
} from './minifyHtml';
|
|
23
|
+
export {createJsLoaderFactory} from './loaders/jsLoader';
|
|
24
|
+
export {createStyleLoadersFactory} from './loaders/styleLoader';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {getBabelOptions} from '@docusaurus/babel';
|
|
9
|
+
import {importSwcLoader, importGetSwcLoaderOptions} from '../importFaster';
|
|
10
|
+
import {getCurrentBundler} from '../currentBundler';
|
|
11
|
+
import type {ConfigureWebpackUtils, DocusaurusConfig} from '@docusaurus/types';
|
|
12
|
+
|
|
13
|
+
const BabelJsLoaderFactory: ConfigureWebpackUtils['getJSLoader'] = ({
|
|
14
|
+
isServer,
|
|
15
|
+
babelOptions,
|
|
16
|
+
}) => {
|
|
17
|
+
return {
|
|
18
|
+
loader: require.resolve('babel-loader'),
|
|
19
|
+
options: getBabelOptions({isServer, babelOptions}),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async function createSwcJsLoaderFactory(): Promise<
|
|
24
|
+
ConfigureWebpackUtils['getJSLoader']
|
|
25
|
+
> {
|
|
26
|
+
const loader = await importSwcLoader();
|
|
27
|
+
const getOptions = await importGetSwcLoaderOptions();
|
|
28
|
+
return ({isServer}) => {
|
|
29
|
+
return {
|
|
30
|
+
loader,
|
|
31
|
+
options: getOptions({isServer}),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Same as swcLoader, except we use the built-in SWC loader
|
|
37
|
+
async function createRspackSwcJsLoaderFactory(): Promise<
|
|
38
|
+
ConfigureWebpackUtils['getJSLoader']
|
|
39
|
+
> {
|
|
40
|
+
const loader = 'builtin:swc-loader';
|
|
41
|
+
const getOptions = await importGetSwcLoaderOptions();
|
|
42
|
+
return ({isServer}) => {
|
|
43
|
+
return {
|
|
44
|
+
loader,
|
|
45
|
+
options: getOptions({isServer}),
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Confusing: function that creates a function that creates actual js loaders
|
|
51
|
+
// This is done on purpose because the js loader factory is a public API
|
|
52
|
+
// It is injected in configureWebpack plugin lifecycle for plugin authors
|
|
53
|
+
export async function createJsLoaderFactory({
|
|
54
|
+
siteConfig,
|
|
55
|
+
}: {
|
|
56
|
+
siteConfig: {
|
|
57
|
+
webpack?: DocusaurusConfig['webpack'];
|
|
58
|
+
future: {
|
|
59
|
+
experimental_faster: DocusaurusConfig['future']['experimental_faster'];
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
}): Promise<ConfigureWebpackUtils['getJSLoader']> {
|
|
63
|
+
const currentBundler = await getCurrentBundler({siteConfig});
|
|
64
|
+
const isSWCLoader = siteConfig.future.experimental_faster.swcJsLoader;
|
|
65
|
+
if (currentBundler.name === 'rspack') {
|
|
66
|
+
return isSWCLoader
|
|
67
|
+
? createRspackSwcJsLoaderFactory()
|
|
68
|
+
: BabelJsLoaderFactory;
|
|
69
|
+
}
|
|
70
|
+
const jsLoader = siteConfig.webpack?.jsLoader ?? 'babel';
|
|
71
|
+
if (
|
|
72
|
+
jsLoader instanceof Function &&
|
|
73
|
+
siteConfig.future?.experimental_faster.swcJsLoader
|
|
74
|
+
) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"You can't use a custom webpack.jsLoader and experimental_faster.swcJsLoader at the same time",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (jsLoader instanceof Function) {
|
|
80
|
+
return ({isServer}) => jsLoader(isServer);
|
|
81
|
+
}
|
|
82
|
+
if (siteConfig.future?.experimental_faster.swcJsLoader) {
|
|
83
|
+
return createSwcJsLoaderFactory();
|
|
84
|
+
}
|
|
85
|
+
if (jsLoader === 'babel') {
|
|
86
|
+
return BabelJsLoaderFactory;
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Docusaurus bug: unexpected jsLoader value${jsLoader}`);
|
|
89
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {getCSSExtractPlugin} from '../currentBundler';
|
|
9
|
+
import type {ConfigureWebpackUtils, CurrentBundler} from '@docusaurus/types';
|
|
10
|
+
|
|
11
|
+
export async function createStyleLoadersFactory({
|
|
12
|
+
currentBundler,
|
|
13
|
+
}: {
|
|
14
|
+
currentBundler: CurrentBundler;
|
|
15
|
+
}): Promise<ConfigureWebpackUtils['getStyleLoaders']> {
|
|
16
|
+
const CssExtractPlugin = await getCSSExtractPlugin({currentBundler});
|
|
17
|
+
|
|
18
|
+
return function getStyleLoaders(
|
|
19
|
+
isServer: boolean,
|
|
20
|
+
cssOptionsArg: {
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
} = {},
|
|
23
|
+
) {
|
|
24
|
+
const cssOptions: {[key: string]: unknown} = {
|
|
25
|
+
// TODO turn esModule on later, see https://github.com/facebook/docusaurus/pull/6424
|
|
26
|
+
esModule: false,
|
|
27
|
+
...cssOptionsArg,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// On the server we don't really need to extract/emit CSS
|
|
31
|
+
// We only need to transform CSS module imports to a styles object
|
|
32
|
+
if (isServer) {
|
|
33
|
+
return cssOptions.modules
|
|
34
|
+
? [
|
|
35
|
+
{
|
|
36
|
+
loader: require.resolve('css-loader'),
|
|
37
|
+
options: cssOptions,
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
: // Ignore regular CSS files
|
|
41
|
+
[{loader: require.resolve('null-loader')}];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
loader: CssExtractPlugin.loader,
|
|
47
|
+
options: {
|
|
48
|
+
esModule: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
loader: require.resolve('css-loader'),
|
|
53
|
+
options: cssOptions,
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// TODO apart for configurePostCss(), do we really need this loader?
|
|
57
|
+
// Note: using postcss here looks inefficient/duplicate
|
|
58
|
+
// But in practice, it's not a big deal because css-loader also uses postcss
|
|
59
|
+
// and is able to reuse the parsed AST from postcss-loader
|
|
60
|
+
// See https://github.com/webpack-contrib/css-loader/blob/master/src/index.js#L159
|
|
61
|
+
{
|
|
62
|
+
// Options for PostCSS as we reference these options twice
|
|
63
|
+
// Adds vendor prefixing based on your specified browser support in
|
|
64
|
+
// package.json
|
|
65
|
+
loader: require.resolve('postcss-loader'),
|
|
66
|
+
options: {
|
|
67
|
+
postcssOptions: {
|
|
68
|
+
// Necessary for external CSS imports to work
|
|
69
|
+
// https://github.com/facebook/create-react-app/issues/2677
|
|
70
|
+
ident: 'postcss',
|
|
71
|
+
plugins: [
|
|
72
|
+
// eslint-disable-next-line global-require
|
|
73
|
+
require('autoprefixer'),
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import TerserPlugin from 'terser-webpack-plugin';
|
|
9
|
+
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
|
10
|
+
import {
|
|
11
|
+
importSwcJsMinimizerOptions,
|
|
12
|
+
importLightningCssMinimizerOptions,
|
|
13
|
+
importGetBrowserslistQueries,
|
|
14
|
+
} from './importFaster';
|
|
15
|
+
import {getCurrentBundlerAsRspack} from './currentBundler';
|
|
16
|
+
import type {CustomOptions, CssNanoOptions} from 'css-minimizer-webpack-plugin';
|
|
17
|
+
import type {WebpackPluginInstance} from 'webpack';
|
|
18
|
+
import type {CurrentBundler, FasterConfig} from '@docusaurus/types';
|
|
19
|
+
|
|
20
|
+
export type MinimizersConfig = {
|
|
21
|
+
faster: Pick<FasterConfig, 'swcJsMinimizer' | 'lightningCssMinimizer'>;
|
|
22
|
+
currentBundler: CurrentBundler;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// See https://github.com/webpack-contrib/terser-webpack-plugin#parallel
|
|
26
|
+
function getTerserParallel() {
|
|
27
|
+
let terserParallel: boolean | number = true;
|
|
28
|
+
if (process.env.TERSER_PARALLEL === 'false') {
|
|
29
|
+
terserParallel = false;
|
|
30
|
+
} else if (
|
|
31
|
+
process.env.TERSER_PARALLEL &&
|
|
32
|
+
parseInt(process.env.TERSER_PARALLEL, 10) > 0
|
|
33
|
+
) {
|
|
34
|
+
terserParallel = parseInt(process.env.TERSER_PARALLEL, 10);
|
|
35
|
+
}
|
|
36
|
+
return terserParallel;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function getJsMinimizer({
|
|
40
|
+
faster,
|
|
41
|
+
}: MinimizersConfig): Promise<WebpackPluginInstance> {
|
|
42
|
+
if (faster.swcJsMinimizer) {
|
|
43
|
+
const terserOptions = await importSwcJsMinimizerOptions();
|
|
44
|
+
return new TerserPlugin({
|
|
45
|
+
parallel: getTerserParallel(),
|
|
46
|
+
minify: TerserPlugin.swcMinify,
|
|
47
|
+
terserOptions,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return new TerserPlugin({
|
|
52
|
+
parallel: getTerserParallel(),
|
|
53
|
+
// See https://terser.org/docs/options/
|
|
54
|
+
terserOptions: {
|
|
55
|
+
parse: {
|
|
56
|
+
// We want uglify-js to parse ecma 8 code. However, we don't want it
|
|
57
|
+
// to apply any minification steps that turns valid ecma 5 code
|
|
58
|
+
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
|
59
|
+
// sections only apply transformations that are ecma 5 safe
|
|
60
|
+
// https://github.com/facebook/create-react-app/pull/4234
|
|
61
|
+
ecma: 2020,
|
|
62
|
+
},
|
|
63
|
+
compress: {
|
|
64
|
+
ecma: 5,
|
|
65
|
+
},
|
|
66
|
+
mangle: {
|
|
67
|
+
safari10: true,
|
|
68
|
+
},
|
|
69
|
+
output: {
|
|
70
|
+
ecma: 5,
|
|
71
|
+
comments: false,
|
|
72
|
+
// Turned on because emoji and regex is not minified properly using
|
|
73
|
+
// default. See https://github.com/facebook/create-react-app/issues/2488
|
|
74
|
+
ascii_only: true,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function getLightningCssMinimizer(): Promise<WebpackPluginInstance> {
|
|
81
|
+
return new CssMinimizerPlugin({
|
|
82
|
+
minify: CssMinimizerPlugin.lightningCssMinify,
|
|
83
|
+
minimizerOptions: await importLightningCssMinimizerOptions(),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function getCssNanoMinimizer(): Promise<WebpackPluginInstance> {
|
|
88
|
+
// This is an historical env variable to opt-out of the advanced minimizer
|
|
89
|
+
// Sometimes there's a bug in it and people are happy to disable it
|
|
90
|
+
const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
|
|
91
|
+
if (useSimpleCssMinifier) {
|
|
92
|
+
return new CssMinimizerPlugin();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Using the array syntax to add 2 minimizers
|
|
96
|
+
// see https://github.com/webpack-contrib/css-minimizer-webpack-plugin#array
|
|
97
|
+
return new CssMinimizerPlugin<[CssNanoOptions, CustomOptions]>({
|
|
98
|
+
minimizerOptions: [
|
|
99
|
+
// CssNano options
|
|
100
|
+
{
|
|
101
|
+
preset: require.resolve('@docusaurus/cssnano-preset'),
|
|
102
|
+
},
|
|
103
|
+
// CleanCss options
|
|
104
|
+
{
|
|
105
|
+
inline: false,
|
|
106
|
+
level: {
|
|
107
|
+
1: {
|
|
108
|
+
all: false,
|
|
109
|
+
removeWhitespace: true,
|
|
110
|
+
},
|
|
111
|
+
2: {
|
|
112
|
+
all: true,
|
|
113
|
+
restructureRules: true,
|
|
114
|
+
removeUnusedAtRules: false,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
minify: [
|
|
120
|
+
CssMinimizerPlugin.cssnanoMinify,
|
|
121
|
+
CssMinimizerPlugin.cleanCssMinify,
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function getCssMinimizer(
|
|
127
|
+
params: MinimizersConfig,
|
|
128
|
+
): Promise<WebpackPluginInstance> {
|
|
129
|
+
return params.faster.lightningCssMinimizer
|
|
130
|
+
? getLightningCssMinimizer()
|
|
131
|
+
: getCssNanoMinimizer();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function getWebpackMinimizers(
|
|
135
|
+
params: MinimizersConfig,
|
|
136
|
+
): Promise<WebpackPluginInstance[]> {
|
|
137
|
+
return Promise.all([getJsMinimizer(params), getCssMinimizer(params)]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function getRspackMinimizers({
|
|
141
|
+
currentBundler,
|
|
142
|
+
}: MinimizersConfig): Promise<WebpackPluginInstance[]> {
|
|
143
|
+
const rspack = getCurrentBundlerAsRspack({currentBundler});
|
|
144
|
+
const getBrowserslistQueries = await importGetBrowserslistQueries();
|
|
145
|
+
const browserslistQueries = getBrowserslistQueries({isServer: false});
|
|
146
|
+
const swcJsMinimizerOptions = await importSwcJsMinimizerOptions();
|
|
147
|
+
return [
|
|
148
|
+
// See https://rspack.dev/plugins/rspack/swc-js-minimizer-rspack-plugin
|
|
149
|
+
// See https://swc.rs/docs/configuration/minification
|
|
150
|
+
new rspack.SwcJsMinimizerRspackPlugin({
|
|
151
|
+
minimizerOptions: {
|
|
152
|
+
minify: true,
|
|
153
|
+
...swcJsMinimizerOptions,
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
new rspack.LightningCssMinimizerRspackPlugin({
|
|
157
|
+
minimizerOptions: {
|
|
158
|
+
...(await importLightningCssMinimizerOptions()),
|
|
159
|
+
// Not sure why but Rspack takes browserslist queries directly
|
|
160
|
+
// While LightningCSS targets are normally not browserslist queries
|
|
161
|
+
// We have to override the option to avoid errors
|
|
162
|
+
// See https://rspack.dev/plugins/rspack/lightning-css-minimizer-rspack-plugin#minimizeroptions
|
|
163
|
+
// See https://lightningcss.dev/transpilation.html
|
|
164
|
+
targets: browserslistQueries,
|
|
165
|
+
},
|
|
166
|
+
}),
|
|
167
|
+
] as unknown as WebpackPluginInstance[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function getMinimizers(
|
|
171
|
+
params: MinimizersConfig,
|
|
172
|
+
): Promise<WebpackPluginInstance[]> {
|
|
173
|
+
return params.currentBundler.name === 'rspack'
|
|
174
|
+
? getRspackMinimizers(params)
|
|
175
|
+
: getWebpackMinimizers(params);
|
|
176
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {minify as terserHtmlMinifier} from 'html-minifier-terser';
|
|
9
|
+
import {importSwcHtmlMinifier} from './importFaster';
|
|
10
|
+
|
|
11
|
+
// Historical env variable
|
|
12
|
+
const SkipHtmlMinification = process.env.SKIP_HTML_MINIFICATION === 'true';
|
|
13
|
+
|
|
14
|
+
export type HtmlMinifierType = 'swc' | 'terser';
|
|
15
|
+
|
|
16
|
+
export type HtmlMinifierResult = {
|
|
17
|
+
code: string;
|
|
18
|
+
warnings: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type HtmlMinifier = {
|
|
22
|
+
minify: (html: string) => Promise<HtmlMinifierResult>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const NoopMinifier: HtmlMinifier = {
|
|
26
|
+
minify: async (html: string) => ({code: html, warnings: []}),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export async function getHtmlMinifier({
|
|
30
|
+
type,
|
|
31
|
+
}: {
|
|
32
|
+
type: HtmlMinifierType;
|
|
33
|
+
}): Promise<HtmlMinifier> {
|
|
34
|
+
if (SkipHtmlMinification) {
|
|
35
|
+
return NoopMinifier;
|
|
36
|
+
}
|
|
37
|
+
if (type === 'swc') {
|
|
38
|
+
return getSwcMinifier();
|
|
39
|
+
} else {
|
|
40
|
+
return getTerserMinifier();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Minify html with https://github.com/DanielRuf/html-minifier-terser
|
|
45
|
+
async function getTerserMinifier(): Promise<HtmlMinifier> {
|
|
46
|
+
return {
|
|
47
|
+
minify: async function minifyHtmlWithTerser(html) {
|
|
48
|
+
try {
|
|
49
|
+
const code = await terserHtmlMinifier(html, {
|
|
50
|
+
removeComments: false,
|
|
51
|
+
removeRedundantAttributes: true,
|
|
52
|
+
removeEmptyAttributes: true,
|
|
53
|
+
removeScriptTypeAttributes: true,
|
|
54
|
+
removeStyleLinkTypeAttributes: true,
|
|
55
|
+
useShortDoctype: true,
|
|
56
|
+
minifyJS: true,
|
|
57
|
+
});
|
|
58
|
+
return {code, warnings: []};
|
|
59
|
+
} catch (err) {
|
|
60
|
+
throw new Error(`HTML minification failed (Terser)`, {
|
|
61
|
+
cause: err as Error,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Minify html with @swc/html
|
|
69
|
+
// Not well-documented but fast!
|
|
70
|
+
// See https://github.com/swc-project/swc/discussions/9616
|
|
71
|
+
async function getSwcMinifier(): Promise<HtmlMinifier> {
|
|
72
|
+
const swcHtmlMinifier = await importSwcHtmlMinifier();
|
|
73
|
+
return {
|
|
74
|
+
minify: async function minifyHtmlWithSwc(html) {
|
|
75
|
+
try {
|
|
76
|
+
const result = await swcHtmlMinifier(Buffer.from(html), {
|
|
77
|
+
// Removing comments can lead to React hydration errors
|
|
78
|
+
// See https://x.com/sebastienlorber/status/1841966927440478577
|
|
79
|
+
removeComments: false,
|
|
80
|
+
// TODO maybe it's fine to only keep <!-- --> React comments?
|
|
81
|
+
preserveComments: [],
|
|
82
|
+
|
|
83
|
+
// Sorting these attributes (class) can lead to React hydration errors
|
|
84
|
+
sortSpaceSeparatedAttributeValues: false,
|
|
85
|
+
sortAttributes: false,
|
|
86
|
+
|
|
87
|
+
removeRedundantAttributes: 'all',
|
|
88
|
+
removeEmptyAttributes: true,
|
|
89
|
+
minifyJs: true,
|
|
90
|
+
minifyJson: true,
|
|
91
|
+
minifyCss: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const warnings = (result.errors ?? []).map((diagnostic) => {
|
|
95
|
+
return `[HTML minifier diagnostic - ${diagnostic.level}] ${
|
|
96
|
+
diagnostic.message
|
|
97
|
+
} - ${JSON.stringify(diagnostic.span)}`;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
code: result.code,
|
|
102
|
+
warnings,
|
|
103
|
+
};
|
|
104
|
+
} catch (err) {
|
|
105
|
+
throw new Error(`HTML minification failed (SWC)`, {
|
|
106
|
+
cause: err as Error,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|