Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): faster CSS minimizer - siteConfig.future.experimental_faster.lightningCssMinimizer #10522

Merged
merged 10 commits into from
Sep 27, 2024
19 changes: 15 additions & 4 deletions packages/docusaurus-bundler/src/importFaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
*/

import type {ConfigureWebpackUtils} from '@docusaurus/types';
import type {MinimizerOptions, CustomOptions} from 'terser-webpack-plugin';
import type {
MinimizerOptions as JsMinimizerOptions,
CustomOptions,
} from 'terser-webpack-plugin';
import type {MinimizerOptions as CssMinimizerOptions} from 'css-minimizer-webpack-plugin';

async function importFaster() {
return import('@docusaurus/faster');
Expand All @@ -30,9 +34,16 @@ export async function importSwcJsLoaderFactory(): Promise<
return faster.getSwcJsLoaderFactory;
}

export async function importSwcJsMinifierOptions(): Promise<
MinimizerOptions<CustomOptions>
export async function importSwcJsMinimizerOptions(): Promise<
JsMinimizerOptions<CustomOptions>
> {
const faster = await ensureFaster();
return faster.getSwcJsMinifierOptions() as MinimizerOptions<CustomOptions>;
return faster.getSwcJsMinimizerOptions() as JsMinimizerOptions<CustomOptions>;
}

export async function importLightningCssMinimizerOptions(): Promise<
CssMinimizerOptions<CustomOptions>
> {
const faster = await ensureFaster();
return faster.getLightningCssMinimizerOptions() as CssMinimizerOptions<CustomOptions>;
}
46 changes: 31 additions & 15 deletions packages/docusaurus-bundler/src/minification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@

import TerserPlugin from 'terser-webpack-plugin';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import {importSwcJsMinifierOptions} from './importFaster';
import {
importSwcJsMinimizerOptions,
importLightningCssMinimizerOptions,
} from './importFaster';
import type {CustomOptions, CssNanoOptions} from 'css-minimizer-webpack-plugin';
import type {WebpackPluginInstance} from 'webpack';
import type {CurrentBundler, FasterConfig} from '@docusaurus/types';

export type MinimizersConfig = {
faster: Pick<FasterConfig, 'swcJsMinimizer'>;
faster: Pick<FasterConfig, 'swcJsMinimizer' | 'lightningCssMinimizer'>;
currentBundler: CurrentBundler;
};

Expand All @@ -31,9 +34,11 @@ function getTerserParallel() {
return terserParallel;
}

async function getJsMinimizer({faster}: MinimizersConfig) {
async function getJsMinimizer({
faster,
}: MinimizersConfig): Promise<WebpackPluginInstance> {
if (faster.swcJsMinimizer) {
const terserOptions = await importSwcJsMinifierOptions();
const terserOptions = await importSwcJsMinimizerOptions();
return new TerserPlugin({
parallel: getTerserParallel(),
minify: TerserPlugin.swcMinify,
Expand Down Expand Up @@ -69,7 +74,21 @@ async function getJsMinimizer({faster}: MinimizersConfig) {
});
}

function getAdvancedCssMinifier() {
async function getLightningCssMinimizer(): Promise<WebpackPluginInstance> {
return new CssMinimizerPlugin({
minify: CssMinimizerPlugin.lightningCssMinify,
minimizerOptions: await importLightningCssMinimizerOptions(),
});
}

async function getCssNanoMinimizer(): Promise<WebpackPluginInstance> {
// This is an historical env variable to opt-out of the advanced minimizer
// Sometimes there's a bug in it and people are happy to disable it
const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
if (useSimpleCssMinifier) {
return new CssMinimizerPlugin();
}

// Using the array syntax to add 2 minimizers
// see https://github.com/webpack-contrib/css-minimizer-webpack-plugin#array
return new CssMinimizerPlugin<[CssNanoOptions, CustomOptions]>({
Expand Down Expand Up @@ -101,21 +120,18 @@ function getAdvancedCssMinifier() {
});
}

function getCssMinimizer(): WebpackPluginInstance {
// This is an historical env variable to opt-out of the advanced minifier
// Sometimes there's a bug in it and people are happy to disable it
const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
if (useSimpleCssMinifier) {
return new CssMinimizerPlugin();
} else {
return getAdvancedCssMinifier();
}
async function getCssMinimizer(
params: MinimizersConfig,
): Promise<WebpackPluginInstance> {
return params.faster.lightningCssMinimizer
? getLightningCssMinimizer()
: getCssNanoMinimizer();
}

async function getWebpackMinimizers(
params: MinimizersConfig,
): Promise<WebpackPluginInstance[]> {
return Promise.all([getJsMinimizer(params), getCssMinimizer()]);
return Promise.all([getJsMinimizer(params), getCssMinimizer(params)]);
}

async function getRspackMinimizers({
Expand Down
6 changes: 4 additions & 2 deletions packages/docusaurus-faster/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
},
"license": "MIT",
"dependencies": {
"webpack": "^5.88.1",
"@swc/core": "^1.7.14",
"swc-loader": "^0.2.6"
"browserslist": "^4.24.0",
"lightningcss": "^1.27.0",
"swc-loader": "^0.2.6",
"webpack": "^5.88.1"
},
"engines": {
"node": ">=18.0"
Expand Down
17 changes: 16 additions & 1 deletion packages/docusaurus-faster/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/

import * as lightningcss from 'lightningcss';
import browserslist from 'browserslist';
import type {RuleSetRule} from 'webpack';
import type {JsMinifyOptions} from '@swc/core';

Expand Down Expand Up @@ -39,7 +41,7 @@ export function getSwcJsLoaderFactory({
// They should rather be kept in sync for now to avoid any unexpected behavior
// The goal of faster minifier is not to fine-tune options but only to be faster
// See core minification.ts
export function getSwcJsMinifierOptions(): JsMinifyOptions {
export function getSwcJsMinimizerOptions(): JsMinifyOptions {
return {
ecma: 2020,
compress: {
Expand All @@ -55,3 +57,16 @@ export function getSwcJsMinifierOptions(): JsMinifyOptions {
},
};
}

// LightningCSS doesn't expose any type for css-minimizer-webpack-plugin setup
// So we derive it ourselves
// see https://lightningcss.dev/docs.html#with-webpack
type LightningCssMinimizerOptions = Omit<
lightningcss.TransformOptions<never>,
'filename' | 'code'
>;

export function getLightningCssMinimizerOptions(): LightningCssMinimizerOptions {
const queries = browserslist();
return {targets: lightningcss.browserslistToTargets(queries)};
}
1 change: 1 addition & 0 deletions packages/docusaurus-types/src/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export type StorageConfig = {
export type FasterConfig = {
swcJsLoader: boolean;
swcJsMinimizer: boolean;
lightningCssMinimizer: boolean;
mdxCrossCompilerCache: boolean;
rspackBundler: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -76,6 +77,7 @@ exports[`loadSiteConfig website with ts + js config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -143,6 +145,7 @@ exports[`loadSiteConfig website with valid JS CJS config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -210,6 +213,7 @@ exports[`loadSiteConfig website with valid JS ESM config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -277,6 +281,7 @@ exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -344,6 +349,7 @@ exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -411,6 +417,7 @@ exports[`loadSiteConfig website with valid async config 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -480,6 +487,7 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -549,6 +557,7 @@ exports[`loadSiteConfig website with valid config creator function 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down Expand Up @@ -621,6 +630,7 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = `
"favicon": "img/docusaurus.ico",
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ exports[`load loads props for site with custom i18n path 1`] = `
"customFields": {},
"future": {
"experimental_faster": {
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"swcJsLoader": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('normalizeConfig', () => {
experimental_faster: {
swcJsLoader: true,
swcJsMinimizer: true,
lightningCssMinimizer: true,
mdxCrossCompilerCache: true,
rspackBundler: true,
},
Expand Down Expand Up @@ -745,6 +746,7 @@ describe('future', () => {
experimental_faster: {
swcJsLoader: true,
swcJsMinimizer: true,
lightningCssMinimizer: true,
mdxCrossCompilerCache: true,
rspackBundler: true,
},
Expand Down Expand Up @@ -1096,6 +1098,7 @@ describe('future', () => {
const faster: FasterConfig = {
swcJsLoader: true,
swcJsMinimizer: true,
lightningCssMinimizer: true,
mdxCrossCompilerCache: true,
rspackBundler: true,
};
Expand Down Expand Up @@ -1281,6 +1284,77 @@ describe('future', () => {
});
});

describe('lightningCssMinimizer', () => {
it('accepts - undefined', () => {
const faster: Partial<FasterConfig> = {
lightningCssMinimizer: undefined,
};
expect(
normalizeConfig({
future: {
experimental_faster: faster,
},
}),
).toEqual(fasterContaining({lightningCssMinimizer: false}));
});

it('accepts - true', () => {
const faster: Partial<FasterConfig> = {
lightningCssMinimizer: true,
};
expect(
normalizeConfig({
future: {
experimental_faster: faster,
},
}),
).toEqual(fasterContaining({lightningCssMinimizer: true}));
});

it('accepts - false', () => {
const faster: Partial<FasterConfig> = {
lightningCssMinimizer: false,
};
expect(
normalizeConfig({
future: {
experimental_faster: faster,
},
}),
).toEqual(fasterContaining({lightningCssMinimizer: false}));
});

it('rejects - null', () => {
// @ts-expect-error: invalid
const faster: Partial<FasterConfig> = {lightningCssMinimizer: 42};
expect(() =>
normalizeConfig({
future: {
experimental_faster: faster,
},
}),
).toThrowErrorMatchingInlineSnapshot(`
""future.experimental_faster.lightningCssMinimizer" must be a boolean
"
`);
});

it('rejects - number', () => {
// @ts-expect-error: invalid
const faster: Partial<FasterConfig> = {lightningCssMinimizer: 42};
expect(() =>
normalizeConfig({
future: {
experimental_faster: faster,
},
}),
).toThrowErrorMatchingInlineSnapshot(`
""future.experimental_faster.lightningCssMinimizer" must be a boolean
"
`);
});
});

describe('mdxCrossCompilerCache', () => {
it('accepts - undefined', () => {
const faster: Partial<FasterConfig> = {
Expand Down
5 changes: 5 additions & 0 deletions packages/docusaurus/src/server/configValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const DEFAULT_STORAGE_CONFIG: StorageConfig = {
export const DEFAULT_FASTER_CONFIG: FasterConfig = {
swcJsLoader: false,
swcJsMinimizer: false,
lightningCssMinimizer: false,
mdxCrossCompilerCache: false,
rspackBundler: false,
};
Expand All @@ -52,6 +53,7 @@ export const DEFAULT_FASTER_CONFIG: FasterConfig = {
export const DEFAULT_FASTER_CONFIG_TRUE: FasterConfig = {
swcJsLoader: true,
swcJsMinimizer: true,
lightningCssMinimizer: true,
mdxCrossCompilerCache: true,
rspackBundler: true,
};
Expand Down Expand Up @@ -221,6 +223,9 @@ const FASTER_CONFIG_SCHEMA = Joi.alternatives()
swcJsMinimizer: Joi.boolean().default(
DEFAULT_FASTER_CONFIG.swcJsMinimizer,
),
lightningCssMinimizer: Joi.boolean().default(
DEFAULT_FASTER_CONFIG.lightningCssMinimizer,
),
mdxCrossCompilerCache: Joi.boolean().default(
DEFAULT_FASTER_CONFIG.mdxCrossCompilerCache,
),
Expand Down
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ lastmod
Lastmod
lifecycles
Lifecycles
lightningcss
linkify
Linkify
Localizable
Expand Down
Loading