Skip to content

Commit

Permalink
feat(core): faster CSS minimizer - `siteConfig.future.experimental_fa…
Browse files Browse the repository at this point in the history
…ster.lightningCssMinimizer` (#10522)
  • Loading branch information
slorber authored Sep 27, 2024
1 parent 3b7c828 commit cba1e02
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 38 deletions.
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
74 changes: 74 additions & 0 deletions packages/docusaurus/src/server/__tests__/configValidation.test.ts
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

0 comments on commit cba1e02

Please sign in to comment.