From 9df676a79071cee01dba318e80e7e7658e7a2376 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 00:01:36 +0200 Subject: [PATCH 1/7] test SWC html minifier --- packages/docusaurus/package.json | 1 + packages/docusaurus/src/ssg.ts | 17 ++++---- yarn.lock | 68 ++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 9f4fd580b59f..47c6e82d8218 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -40,6 +40,7 @@ "@docusaurus/utils": "3.5.2", "@docusaurus/utils-common": "3.5.2", "@docusaurus/utils-validation": "3.5.2", + "@swc/html": "^1.7.28", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", diff --git a/packages/docusaurus/src/ssg.ts b/packages/docusaurus/src/ssg.ts index a444905165c2..04424a94ea54 100644 --- a/packages/docusaurus/src/ssg.ts +++ b/packages/docusaurus/src/ssg.ts @@ -11,7 +11,8 @@ import path from 'path'; import _ from 'lodash'; import evaluate from 'eval'; import pMap from 'p-map'; -import {minify} from 'html-minifier-terser'; +// import {minify} from 'html-minifier-terser'; +import {minify} from '@swc/html'; import logger, {PerfLogger} from '@docusaurus/logger'; import {renderSSRTemplate} from './templates/templates'; import type {AppRenderer, AppRenderResult, SiteCollectedData} from './common'; @@ -269,15 +270,17 @@ async function minifyHtml(html: string): Promise { return html; } // Minify html with https://github.com/DanielRuf/html-minifier-terser - return await minify(html, { + const result = await minify(Buffer.from(html), { + preserveComments: [], removeComments: false, - removeRedundantAttributes: true, + // @ts-expect-error: type + removeRedundantAttributes: 'all', removeEmptyAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true, - minifyJS: true, + minifyJs: true, + minifyJson: true, + minifyCss: true, }); + return result.code; } catch (err) { throw new Error('HTML minification failed', {cause: err as Error}); } diff --git a/yarn.lock b/yarn.lock index 5492346d4245..8fee9926f60e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3116,6 +3116,74 @@ resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== +"@swc/html-darwin-arm64@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-darwin-arm64/-/html-darwin-arm64-1.7.28.tgz#19a6b8d50620bccd75e357b3409c17dacb951a27" + integrity sha512-vWxqVn8pUW/TG6NCnqNTGblJ70Rvet93rCIklo7AqAmEYnuSEyxmCo40CgAYLmpQS9Ql9OlEalH36ymPaQBiIA== + +"@swc/html-darwin-x64@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-darwin-x64/-/html-darwin-x64-1.7.28.tgz#babe74ab7427bce9f09bf4c2f85a170a5c2b9fac" + integrity sha512-p+4vsRWUqSkvpqil9YItYRWwQ+DAD1553t6LMbyYPzT02LOw8pHxdvt7ZMIfoGn7mjgWhkl07/VeMsXfUtiWaA== + +"@swc/html-linux-arm-gnueabihf@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.7.28.tgz#3369d6f905c44a721a71bfde271c393308a2d770" + integrity sha512-267layBwsXW0MsAiO085dckbLki2e4ZHlDsrPnCX4qLyzw6JykKplWeqWkg+Py2jNKw+YafYlHhlsKR/hN+olQ== + +"@swc/html-linux-arm64-gnu@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.7.28.tgz#84eaefe7460169228f7f28b6f967beadb9e5ae04" + integrity sha512-3xEEepFXOZ7ZPcAbFzWhUqkYK3qUIhKjdr+VUut0fXeKRY8Y1qc79CvAAWxT1rQKuK4LLTUYCOJUskUbYWHAXg== + +"@swc/html-linux-arm64-musl@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.7.28.tgz#e2ef51295f761074153ce5a32c1e754aa0eb97f8" + integrity sha512-P3wraHA2/vU8aXreNtIyYcqn0dj4o4gpT60s3Kj79L0G8dUdnTaXxiqX/7WRyB25asuGn608iwrzlMXQnX5c8A== + +"@swc/html-linux-x64-gnu@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.7.28.tgz#96c72c5f76df444b46d6a4587d3e81f5c117ee70" + integrity sha512-VW98fYTgU6Bz6++paBMe4gaNbxKjN+hO/Jb+8Cfh1ln8fvjMTk/FxzqU1gHJMh4R2xJo4NbUxSkzBFiKN0Dq6w== + +"@swc/html-linux-x64-musl@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.7.28.tgz#dbae82d638b656d916c2af25c653729e5ca78063" + integrity sha512-/GHYvo6SJr6vfkvhreXc+9va20isZle252GtrcyWGxKz+9t20T6fsbtX07U2XYTIVS1KXMCwaFooHBVu1zT7Pg== + +"@swc/html-win32-arm64-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.7.28.tgz#4dab56c4671ef18b91620baf2cad360cd50de834" + integrity sha512-WZu4xje+tSZvDu09jwdmuwP78GmNhWBUzFUV/xulmJtcWRZOAHZyByPNV7Sp4fpcewSPiPoHjFdyhVR1MvGl1A== + +"@swc/html-win32-ia32-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.7.28.tgz#9520d1d50d422a833c685afa9b9a2812d82e7216" + integrity sha512-qzBNJUZ5zBOJGmcsejJyNi4WBcYFPGnmtDCMzRP5uF1eMbQC+ye3nL8AP5BKCV8+i7kq8y+IGh6l6ieRnxwx0w== + +"@swc/html-win32-x64-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.7.28.tgz#0e9234614e883209e2513be94ae3114d14ee813d" + integrity sha512-9/wQWVwYJLPmeztj6Pg5Q7/Man96hwDTh5mRcSGNpAdaXyWMDYJWk0JTbZvPJUSrTBX0lKv9F7jhHcsOBKGErQ== + +"@swc/html@^1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/html/-/html-1.7.28.tgz#413a14c1351da81a99903beac8cb6f461e3c7607" + integrity sha512-eVPIz/goSyoNjDobwoB16dxX8RDu4gE1sNjFCpR/ZwrmqUTDvGhnLkQ7tz7rAdLdCIEW/iIO2hlrMyUHZzJW+A== + dependencies: + "@swc/counter" "^0.1.3" + optionalDependencies: + "@swc/html-darwin-arm64" "1.7.28" + "@swc/html-darwin-x64" "1.7.28" + "@swc/html-linux-arm-gnueabihf" "1.7.28" + "@swc/html-linux-arm64-gnu" "1.7.28" + "@swc/html-linux-arm64-musl" "1.7.28" + "@swc/html-linux-x64-gnu" "1.7.28" + "@swc/html-linux-x64-musl" "1.7.28" + "@swc/html-win32-arm64-msvc" "1.7.28" + "@swc/html-win32-ia32-msvc" "1.7.28" + "@swc/html-win32-x64-msvc" "1.7.28" + "@swc/jest@^0.2.36": version "0.2.36" resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.36.tgz#2797450a30d28b471997a17e901ccad946fe693e" From 1b9cae894bcc74aebe13e02c1a8814cd7d8e672b Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 00:12:49 +0200 Subject: [PATCH 2/7] empty From 0466544ac584cb4cbe3b5e9e48fb78f804c09587 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 12:08:25 +0200 Subject: [PATCH 3/7] fix React hydration errors --- packages/docusaurus/src/ssg.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus/src/ssg.ts b/packages/docusaurus/src/ssg.ts index 04424a94ea54..49eba8e7a55e 100644 --- a/packages/docusaurus/src/ssg.ts +++ b/packages/docusaurus/src/ssg.ts @@ -271,8 +271,15 @@ async function minifyHtml(html: string): Promise { } // Minify html with https://github.com/DanielRuf/html-minifier-terser const result = await minify(Buffer.from(html), { - preserveComments: [], + // Removing comments can lead to React hydration errors + // See https://x.com/sebastienlorber/status/1841966927440478577 removeComments: false, + // TODO maybe it's fine to only keep React comments? + preserveComments: [], + + // Sorting space attributes like class can lead to React hydration errors + sortSpaceSeparatedAttributeValues: false, + // @ts-expect-error: type removeRedundantAttributes: 'all', removeEmptyAttributes: true, From 5168f8b3639fe061711eb351f78f25e63e886126 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 14:12:36 +0200 Subject: [PATCH 4/7] add comment + explicit sortAttributes: false --- packages/docusaurus/src/ssg.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus/src/ssg.ts b/packages/docusaurus/src/ssg.ts index 49eba8e7a55e..91e610f7ca6e 100644 --- a/packages/docusaurus/src/ssg.ts +++ b/packages/docusaurus/src/ssg.ts @@ -279,8 +279,9 @@ async function minifyHtml(html: string): Promise { // Sorting space attributes like class can lead to React hydration errors sortSpaceSeparatedAttributeValues: false, + sortAttributes: false, - // @ts-expect-error: type + // @ts-expect-error: bad type https://github.com/swc-project/swc/pull/9615 removeRedundantAttributes: 'all', removeEmptyAttributes: true, minifyJs: true, From 95613c2a927359cbe60eeff32c0f1d8dc6a4d21c Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 15:15:11 +0200 Subject: [PATCH 5/7] Add proper wiring for swcHtmlMinimizer --- packages/docusaurus-bundler/package.json | 1 + .../docusaurus-bundler/src/importFaster.ts | 13 +- packages/docusaurus-bundler/src/index.ts | 1 + packages/docusaurus-bundler/src/minifyHtml.ts | 100 +++++++++++++ packages/docusaurus-faster/package.json | 3 +- packages/docusaurus-faster/src/index.ts | 5 + packages/docusaurus-types/src/config.d.ts | 1 + packages/docusaurus/package.json | 4 +- packages/docusaurus/src/commands/build.ts | 18 ++- .../__snapshots__/config.test.ts.snap | 10 ++ .../__tests__/__snapshots__/site.test.ts.snap | 1 + .../server/__tests__/configValidation.test.ts | 74 ++++++++++ .../docusaurus/src/server/configValidation.ts | 5 + packages/docusaurus/src/ssg.ts | 40 +----- yarn.lock | 136 +++++++++--------- 15 files changed, 299 insertions(+), 113 deletions(-) create mode 100644 packages/docusaurus-bundler/src/minifyHtml.ts diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 643ef0fbe951..771d42ab6792 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -35,6 +35,7 @@ "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "file-loader": "^6.2.0", + "html-minifier-terser": "^7.2.0", "mini-css-extract-plugin": "^2.9.1", "null-loader": "^4.0.1", "react-dev-utils": "^12.0.1", diff --git a/packages/docusaurus-bundler/src/importFaster.ts b/packages/docusaurus-bundler/src/importFaster.ts index a880d2275b1e..a7549ef96a9b 100644 --- a/packages/docusaurus-bundler/src/importFaster.ts +++ b/packages/docusaurus-bundler/src/importFaster.ts @@ -12,11 +12,13 @@ import type { } from 'terser-webpack-plugin'; import type {MinimizerOptions as CssMinimizerOptions} from 'css-minimizer-webpack-plugin'; -async function importFaster() { +type FasterModule = Awaited; + +async function importFaster(): Promise { return import('@docusaurus/faster'); } -async function ensureFaster() { +async function ensureFaster(): Promise { try { return await importFaster(); } catch (error) { @@ -41,6 +43,13 @@ export async function importSwcJsMinimizerOptions(): Promise< return faster.getSwcJsMinimizerOptions() as JsMinimizerOptions; } +export async function importSwcHtmlMinifier(): Promise< + ReturnType +> { + const faster = await ensureFaster(); + return faster.getSwcHtmlMinifier(); +} + export async function importLightningCssMinimizerOptions(): Promise< CssMinimizerOptions > { diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index 457cdd9d2516..10995162d761 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -15,5 +15,6 @@ export { } from './currentBundler'; export {getMinimizers} from './minification'; +export {getHtmlMinifier, type HtmlMinifier} from './minifyHtml'; export {createJsLoaderFactory} from './loaders/jsLoader'; export {createStyleLoadersFactory} from './loaders/styleLoader'; diff --git a/packages/docusaurus-bundler/src/minifyHtml.ts b/packages/docusaurus-bundler/src/minifyHtml.ts new file mode 100644 index 000000000000..957794ad7d58 --- /dev/null +++ b/packages/docusaurus-bundler/src/minifyHtml.ts @@ -0,0 +1,100 @@ +/** + * 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 {minify as terserHtmlMinifier} from 'html-minifier-terser'; +import {importSwcHtmlMinifier} from './importFaster'; +import type {DocusaurusConfig} from '@docusaurus/types'; + +// Historical env variable +const SkipHtmlMinification = process.env.SKIP_HTML_MINIFICATION === 'true'; + +export type HtmlMinifier = { + minify: (html: string) => Promise; +}; + +const NoopMinifier: HtmlMinifier = { + minify: async (html: string) => html, +}; + +type SiteConfigSlice = { + future: { + experimental_faster: Pick< + DocusaurusConfig['future']['experimental_faster'], + 'swcHtmlMinimizer' + >; + }; +}; + +export async function getHtmlMinifier({ + siteConfig, +}: { + siteConfig: SiteConfigSlice; +}): Promise { + if (SkipHtmlMinification) { + return NoopMinifier; + } + if (siteConfig.future.experimental_faster.swcHtmlMinimizer) { + return getSwcMinifier(); + } else { + return getTerserMinifier(); + } +} + +// Minify html with https://github.com/DanielRuf/html-minifier-terser +async function getTerserMinifier(): Promise { + return { + minify: async function minifyHtmlWithTerser(html) { + try { + return await terserHtmlMinifier(html, { + removeComments: false, + removeRedundantAttributes: true, + removeEmptyAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + minifyJS: true, + }); + } catch (err) { + throw new Error('HTML minification failed', {cause: err as Error}); + } + }, + }; +} + +// Minify html with @swc/html +// Not well-documented but fast! +// See https://github.com/swc-project/swc/discussions/9616 +async function getSwcMinifier(): Promise { + const swcHtmlMinifier = await importSwcHtmlMinifier(); + return { + minify: async function minifyHtmlWithSwc(html) { + try { + const result = await swcHtmlMinifier(Buffer.from(html), { + // Removing comments can lead to React hydration errors + // See https://x.com/sebastienlorber/status/1841966927440478577 + removeComments: false, + // TODO maybe it's fine to only keep React comments? + preserveComments: [], + + // Sorting these attributes (class) can lead to React hydration errors + sortSpaceSeparatedAttributeValues: false, + sortAttributes: false, + + // @ts-expect-error: bad type https://github.com/swc-project/swc/pull/9615 + removeRedundantAttributes: 'all', + removeEmptyAttributes: true, + minifyJs: true, + minifyJson: true, + minifyCss: true, + }); + return result.code; + } catch (err) { + throw new Error('HTML minification failed', {cause: err as Error}); + } + }, + }; +} diff --git a/packages/docusaurus-faster/package.json b/packages/docusaurus-faster/package.json index a38f4984cebb..34e5b284c3c8 100644 --- a/packages/docusaurus-faster/package.json +++ b/packages/docusaurus-faster/package.json @@ -18,7 +18,8 @@ }, "license": "MIT", "dependencies": { - "@swc/core": "^1.7.14", + "@swc/core": "^1.7.28", + "@swc/html": "^1.7.28", "browserslist": "^4.24.0", "lightningcss": "^1.27.0", "swc-loader": "^0.2.6", diff --git a/packages/docusaurus-faster/src/index.ts b/packages/docusaurus-faster/src/index.ts index e54ed6979b48..42ab8413b4cb 100644 --- a/packages/docusaurus-faster/src/index.ts +++ b/packages/docusaurus-faster/src/index.ts @@ -7,9 +7,14 @@ import * as lightningcss from 'lightningcss'; import browserslist from 'browserslist'; +import {minify as swcHtmlMinifier} from '@swc/html'; import type {RuleSetRule} from 'webpack'; import type {JsMinifyOptions} from '@swc/core'; +export function getSwcHtmlMinifier(): typeof swcHtmlMinifier { + return swcHtmlMinifier; +} + export function getSwcJsLoaderFactory({ isServer, }: { diff --git a/packages/docusaurus-types/src/config.d.ts b/packages/docusaurus-types/src/config.d.ts index b7e4221beed0..e8e4a4611547 100644 --- a/packages/docusaurus-types/src/config.d.ts +++ b/packages/docusaurus-types/src/config.d.ts @@ -126,6 +126,7 @@ export type StorageConfig = { export type FasterConfig = { swcJsLoader: boolean; swcJsMinimizer: boolean; + swcHtmlMinimizer: boolean; lightningCssMinimizer: boolean; mdxCrossCompilerCache: boolean; rspackBundler: boolean; diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 47c6e82d8218..21411f0baf28 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -40,7 +40,6 @@ "@docusaurus/utils": "3.5.2", "@docusaurus/utils-common": "3.5.2", "@docusaurus/utils-validation": "3.5.2", - "@swc/html": "^1.7.28", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -54,9 +53,8 @@ "eta": "^2.2.0", "eval": "^0.1.8", "fs-extra": "^11.1.1", - "html-minifier-terser": "^7.2.0", "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.5.3", + "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", "p-map": "^4.0.0", diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 6cc6e73b751f..ec0f5f3ae73c 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -8,7 +8,7 @@ import fs from 'fs-extra'; import path from 'path'; import _ from 'lodash'; -import {compile} from '@docusaurus/bundler'; +import {compile, getHtmlMinifier} from '@docusaurus/bundler'; import logger, {PerfLogger} from '@docusaurus/logger'; import {DOCUSAURUS_VERSION, mapAsyncSequential} from '@docusaurus/utils'; import {loadSite, loadContext, type LoadContextParams} from '../server/site'; @@ -271,17 +271,23 @@ async function executeSSG({ return {collectedData: {}}; } - const renderer = await PerfLogger.async('Load App renderer', () => - loadAppRenderer({ - serverBundlePath, - }), - ); + const [renderer, htmlMinifier] = await Promise.all([ + PerfLogger.async('Load App renderer', () => + loadAppRenderer({ + serverBundlePath, + }), + ), + PerfLogger.async('Load HTML minifier', () => + getHtmlMinifier({siteConfig: props.siteConfig}), + ), + ]); const ssgResult = await PerfLogger.async('Generate static files', () => generateStaticFiles({ pathnames: props.routesPaths, renderer, params, + htmlMinifier, }), ); diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap index ffceecd12de3..0e0a790f0e04 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap @@ -12,6 +12,7 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -80,6 +81,7 @@ exports[`loadSiteConfig website with ts + js config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -148,6 +150,7 @@ exports[`loadSiteConfig website with valid JS CJS config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -216,6 +219,7 @@ exports[`loadSiteConfig website with valid JS ESM config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -284,6 +288,7 @@ exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -352,6 +357,7 @@ exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -420,6 +426,7 @@ exports[`loadSiteConfig website with valid async config 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -490,6 +497,7 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -560,6 +568,7 @@ exports[`loadSiteConfig website with valid config creator function 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, @@ -633,6 +642,7 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap index 3bc788d98fa3..af2a47aa5ce5 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap @@ -86,6 +86,7 @@ exports[`load loads props for site with custom i18n path 1`] = ` "lightningCssMinimizer": false, "mdxCrossCompilerCache": false, "rspackBundler": false, + "swcHtmlMinimizer": false, "swcJsLoader": false, "swcJsMinimizer": false, }, diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index 59dfaca37ae4..c8784efc2cc8 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -48,6 +48,7 @@ describe('normalizeConfig', () => { experimental_faster: { swcJsLoader: true, swcJsMinimizer: true, + swcHtmlMinimizer: true, lightningCssMinimizer: true, mdxCrossCompilerCache: true, rspackBundler: true, @@ -746,6 +747,7 @@ describe('future', () => { experimental_faster: { swcJsLoader: true, swcJsMinimizer: true, + swcHtmlMinimizer: true, lightningCssMinimizer: true, mdxCrossCompilerCache: true, rspackBundler: true, @@ -1098,6 +1100,7 @@ describe('future', () => { const faster: FasterConfig = { swcJsLoader: true, swcJsMinimizer: true, + swcHtmlMinimizer: true, lightningCssMinimizer: true, mdxCrossCompilerCache: true, rspackBundler: true, @@ -1284,6 +1287,77 @@ describe('future', () => { }); }); + describe('swcHtmlMinimizer', () => { + it('accepts - undefined', () => { + const faster: Partial = { + swcHtmlMinimizer: undefined, + }; + expect( + normalizeConfig({ + future: { + experimental_faster: faster, + }, + }), + ).toEqual(fasterContaining({swcHtmlMinimizer: false})); + }); + + it('accepts - true', () => { + const faster: Partial = { + swcHtmlMinimizer: true, + }; + expect( + normalizeConfig({ + future: { + experimental_faster: faster, + }, + }), + ).toEqual(fasterContaining({swcHtmlMinimizer: true})); + }); + + it('accepts - false', () => { + const faster: Partial = { + swcHtmlMinimizer: false, + }; + expect( + normalizeConfig({ + future: { + experimental_faster: faster, + }, + }), + ).toEqual(fasterContaining({swcHtmlMinimizer: false})); + }); + + it('rejects - null', () => { + // @ts-expect-error: invalid + const faster: Partial = {swcHtmlMinimizer: 42}; + expect(() => + normalizeConfig({ + future: { + experimental_faster: faster, + }, + }), + ).toThrowErrorMatchingInlineSnapshot(` + ""future.experimental_faster.swcHtmlMinimizer" must be a boolean + " + `); + }); + + it('rejects - number', () => { + // @ts-expect-error: invalid + const faster: Partial = {swcHtmlMinimizer: 42}; + expect(() => + normalizeConfig({ + future: { + experimental_faster: faster, + }, + }), + ).toThrowErrorMatchingInlineSnapshot(` + ""future.experimental_faster.swcHtmlMinimizer" must be a boolean + " + `); + }); + }); + describe('lightningCssMinimizer', () => { it('accepts - undefined', () => { const faster: Partial = { diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index e81c54034cd7..0b001db8d204 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -44,6 +44,7 @@ export const DEFAULT_STORAGE_CONFIG: StorageConfig = { export const DEFAULT_FASTER_CONFIG: FasterConfig = { swcJsLoader: false, swcJsMinimizer: false, + swcHtmlMinimizer: false, lightningCssMinimizer: false, mdxCrossCompilerCache: false, rspackBundler: false, @@ -53,6 +54,7 @@ export const DEFAULT_FASTER_CONFIG: FasterConfig = { export const DEFAULT_FASTER_CONFIG_TRUE: FasterConfig = { swcJsLoader: true, swcJsMinimizer: true, + swcHtmlMinimizer: true, lightningCssMinimizer: true, mdxCrossCompilerCache: true, rspackBundler: true, @@ -223,6 +225,9 @@ const FASTER_CONFIG_SCHEMA = Joi.alternatives() swcJsMinimizer: Joi.boolean().default( DEFAULT_FASTER_CONFIG.swcJsMinimizer, ), + swcHtmlMinimizer: Joi.boolean().default( + DEFAULT_FASTER_CONFIG.swcHtmlMinimizer, + ), lightningCssMinimizer: Joi.boolean().default( DEFAULT_FASTER_CONFIG.lightningCssMinimizer, ), diff --git a/packages/docusaurus/src/ssg.ts b/packages/docusaurus/src/ssg.ts index 91e610f7ca6e..77f6a1cf5d7d 100644 --- a/packages/docusaurus/src/ssg.ts +++ b/packages/docusaurus/src/ssg.ts @@ -11,11 +11,10 @@ import path from 'path'; import _ from 'lodash'; import evaluate from 'eval'; import pMap from 'p-map'; -// import {minify} from 'html-minifier-terser'; -import {minify} from '@swc/html'; import logger, {PerfLogger} from '@docusaurus/logger'; import {renderSSRTemplate} from './templates/templates'; import type {AppRenderer, AppRenderResult, SiteCollectedData} from './common'; +import type {HtmlMinifier} from '@docusaurus/bundler'; import type {Manifest} from 'react-loadable-ssr-addon-v5-slorber'; import type {SSRTemplateCompiled} from './templates/templates'; @@ -115,10 +114,12 @@ export async function generateStaticFiles({ pathnames, renderer, params, + htmlMinifier, }: { pathnames: string[]; renderer: AppRenderer; params: SSGParams; + htmlMinifier: HtmlMinifier; }): Promise<{collectedData: SiteCollectedData}> { type SSGSuccess = {pathname: string; error: null; result: AppRenderResult}; type SSGError = {pathname: string; error: Error; result: null}; @@ -133,6 +134,7 @@ export async function generateStaticFiles({ pathname, renderer, params, + htmlMinifier, }).then( (result) => ({pathname, result, error: null}), (error) => ({pathname, result: null, error: error as Error}), @@ -171,10 +173,12 @@ async function generateStaticFile({ pathname, renderer, params, + htmlMinifier, }: { pathname: string; renderer: AppRenderer; params: SSGParams; + htmlMinifier: HtmlMinifier; }) { try { // This only renders the app HTML @@ -186,7 +190,7 @@ async function generateStaticFile({ params, result, }); - const content = await minifyHtml(fullPageHtml); + const content = await htmlMinifier.minify(fullPageHtml); await writeStaticFile({ pathname, content, @@ -263,33 +267,3 @@ async function writeStaticFile({ await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, content); } - -async function minifyHtml(html: string): Promise { - try { - if (process.env.SKIP_HTML_MINIFICATION === 'true') { - return html; - } - // Minify html with https://github.com/DanielRuf/html-minifier-terser - const result = await minify(Buffer.from(html), { - // Removing comments can lead to React hydration errors - // See https://x.com/sebastienlorber/status/1841966927440478577 - removeComments: false, - // TODO maybe it's fine to only keep React comments? - preserveComments: [], - - // Sorting space attributes like class can lead to React hydration errors - sortSpaceSeparatedAttributeValues: false, - sortAttributes: false, - - // @ts-expect-error: bad type https://github.com/swc-project/swc/pull/9615 - removeRedundantAttributes: 'all', - removeEmptyAttributes: true, - minifyJs: true, - minifyJson: true, - minifyCss: true, - }); - return result.code; - } catch (err) { - throw new Error('HTML minification failed', {cause: err as Error}); - } -} diff --git a/yarn.lock b/yarn.lock index 8fee9926f60e..a14fba51a995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3042,74 +3042,74 @@ "@svgr/plugin-jsx" "8.1.0" "@svgr/plugin-svgo" "8.1.0" -"@swc/core-darwin-arm64@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz#a4530ec755ea183802cc9dfe4900ab5f6a327fea" - integrity sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ== - -"@swc/core-darwin-x64@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.7.14.tgz#2c9c717fd28dd1dde9c21cf58b01f1cda7976b1a" - integrity sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ== - -"@swc/core-linux-arm-gnueabihf@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.14.tgz#fed055c9c65347177c8df88720f8a51793a4df06" - integrity sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA== - -"@swc/core-linux-arm64-gnu@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.14.tgz#ca740c8ea26f041b2dc43ba87facec452052814f" - integrity sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g== - -"@swc/core-linux-arm64-musl@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.14.tgz#fbc6fed24f5ad58b948e5b7abe6cd1f07112bef1" - integrity sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ== - -"@swc/core-linux-x64-gnu@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.14.tgz#509a37833e4fbf89506b9291d9bd131fa2017fca" - integrity sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og== - -"@swc/core-linux-x64-musl@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.14.tgz#81156cc6ff814ad4b8fcf6eb6658d3f247db0b57" - integrity sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ== - -"@swc/core-win32-arm64-msvc@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.14.tgz#c605fa783b5fbe1fff784ace4c4bb074b8d6026d" - integrity sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A== - -"@swc/core-win32-ia32-msvc@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.14.tgz#3e15dc3b662c9fab851a38b3e271c8e2da4ba03a" - integrity sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA== - -"@swc/core-win32-x64-msvc@1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz#83958d92e9f07865ec9365212111fbc295660f0d" - integrity sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q== - -"@swc/core@^1.7.14": - version "1.7.14" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.7.14.tgz#d10492b5a4168cb1e73cf561a315e8b0f62255ed" - integrity sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow== +"@swc/core-darwin-arm64@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.28.tgz#f4ff1c09443a0040a29c7e1e7615f5f5642b6945" + integrity sha512-BNkj6enHo2pdzOpCtQGKZbXT2A/qWIr0CVtbTM4WkJ3MCK/glbFsyO6X59p1r8+gfaZG4bWYnTTu+RuUAcsL5g== + +"@swc/core-darwin-x64@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.7.28.tgz#ce0a6d559084a794517a81457cdadbf61a55c55d" + integrity sha512-96zQ+X5Fd6P/RNPkOyikTJgEc2M4TzznfYvjRd2hye5h22jhxCLL/csoauDgN7lYfd7mwsZ/sVXwJTMKl+vZSA== + +"@swc/core-linux-arm-gnueabihf@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.28.tgz#501375ac84c61dc718ed07239c7e44972f6c44e0" + integrity sha512-l2100Wx6LdXMOmOW3+KoHhBhyZrGdz8ylkygcVOC0QHp6YIATfuG+rRHksfyEWCSOdL3anM9MJZJX26KT/s+XQ== + +"@swc/core-linux-arm64-gnu@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.28.tgz#75e99da625939627f5b45d3004a6cfd8d6cbf46e" + integrity sha512-03m6iQ5Bv9u2VPnNRyaBmE8eHi056eE39L0gXcqGoo46GAGuoqYHt9pDz8wS6EgoN4t85iBMUZrkCNqFKkN6ZQ== + +"@swc/core-linux-arm64-musl@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.28.tgz#c737def355c0bf8db7d8e7bd87a3ae8bb3f9f8fc" + integrity sha512-vqVOpG/jc8mvTKQjaPBLhr7tnWyzuztOHsPnJqMWmg7zGcMeQC/2c5pU4uzRAfXMTp25iId6s4Y4wWfPS1EeDw== + +"@swc/core-linux-x64-gnu@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.28.tgz#eb612272ceb1331310eb79ef6094c5a6cc085d23" + integrity sha512-HGwpWuB83Kr+V0E+zT5UwIIY9OxiS8aLd0UVMRVWuO8SrQyKm9HKJ46+zoAb8tfJrpZftfxvbn2ayZWR7gqosA== + +"@swc/core-linux-x64-musl@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.28.tgz#a39749a71e690685aabeb7fd60141ccca2e62411" + integrity sha512-q2Y2T8y8EgFtIiRyInnAXNe94aaHX74F0ha1Bl9VdRxE0u1/So+3VLbPvtp4V3Z6pj5pOePfCQJKifnllgAQ9A== + +"@swc/core-win32-arm64-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.28.tgz#93b22667b027e0a5060c91df7e0cc7406d27b01f" + integrity sha512-bCqh4uBT/59h3dWK1v91In6qzz8rKoWoFRxCtNQLIK4jP55K0U231ZK9oN7neZD6bzcOUeFvOGgcyMAgDfFWfA== + +"@swc/core-win32-ia32-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.28.tgz#4d7dbc43a1de79ac0c7cccf35bebf9fe887b2e24" + integrity sha512-XTHbHrksnrqK3JSJ2sbuMWvdJ6/G0roRpgyVTmNDfhTYPOwcVaL/mSrPGLwbksYUbq7ckwoKzrobhdxvQzPsDA== + +"@swc/core-win32-x64-msvc@1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.28.tgz#d00acea3339a90768279096e6e5f1540c599e6ce" + integrity sha512-jyXeoq6nX8abiCy2EpporsC5ywNENs4ocYuvxo1LSxDktWN1E2MTXq3cdJcEWB2Vydxq0rDcsGyzkRPMzFhkZw== + +"@swc/core@^1.7.14", "@swc/core@^1.7.28": + version "1.7.28" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.7.28.tgz#74aec7a31344da7cfd305a09f14f22420351d495" + integrity sha512-XapcMgsOS0cKh01AFEj+qXOk6KM4NZhp7a5vPicdhkRR8RzvjrCa7DTtijMxfotU8bqaEHguxmiIag2HUlT8QQ== dependencies: "@swc/counter" "^0.1.3" "@swc/types" "^0.1.12" optionalDependencies: - "@swc/core-darwin-arm64" "1.7.14" - "@swc/core-darwin-x64" "1.7.14" - "@swc/core-linux-arm-gnueabihf" "1.7.14" - "@swc/core-linux-arm64-gnu" "1.7.14" - "@swc/core-linux-arm64-musl" "1.7.14" - "@swc/core-linux-x64-gnu" "1.7.14" - "@swc/core-linux-x64-musl" "1.7.14" - "@swc/core-win32-arm64-msvc" "1.7.14" - "@swc/core-win32-ia32-msvc" "1.7.14" - "@swc/core-win32-x64-msvc" "1.7.14" + "@swc/core-darwin-arm64" "1.7.28" + "@swc/core-darwin-x64" "1.7.28" + "@swc/core-linux-arm-gnueabihf" "1.7.28" + "@swc/core-linux-arm64-gnu" "1.7.28" + "@swc/core-linux-arm64-musl" "1.7.28" + "@swc/core-linux-x64-gnu" "1.7.28" + "@swc/core-linux-x64-musl" "1.7.28" + "@swc/core-win32-arm64-msvc" "1.7.28" + "@swc/core-win32-ia32-msvc" "1.7.28" + "@swc/core-win32-x64-msvc" "1.7.28" "@swc/counter@^0.1.3": version "0.1.3" @@ -9132,10 +9132,10 @@ html-void-elements@^3.0.0: resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== -html-webpack-plugin@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz#72270f4a78e222b5825b296e5e3e1328ad525a3e" - integrity sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg== +html-webpack-plugin@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" From 72a6583833b59006892f8ebb57f5af9db36064f9 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 17:42:16 +0200 Subject: [PATCH 6/7] fix unclosed span, invalid html reported by minifier --- website/_dogfooding/docs-tests-sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_dogfooding/docs-tests-sidebars.js b/website/_dogfooding/docs-tests-sidebars.js index 1e37a44d7c90..67d4b8030002 100644 --- a/website/_dogfooding/docs-tests-sidebars.js +++ b/website/_dogfooding/docs-tests-sidebars.js @@ -157,7 +157,7 @@ const sidebars = { { type: 'html', value: - '', + '', }, // Image { From fdecad5634f73fdc7caa65b68b1fcce519f0e543 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 4 Oct 2024 18:06:09 +0200 Subject: [PATCH 7/7] Add awkward swc minifier error handling --- packages/docusaurus-bundler/src/minifyHtml.ts | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-bundler/src/minifyHtml.ts b/packages/docusaurus-bundler/src/minifyHtml.ts index 957794ad7d58..e84b03bede8d 100644 --- a/packages/docusaurus-bundler/src/minifyHtml.ts +++ b/packages/docusaurus-bundler/src/minifyHtml.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import logger from '@docusaurus/logger'; import {minify as terserHtmlMinifier} from 'html-minifier-terser'; import {importSwcHtmlMinifier} from './importFaster'; import type {DocusaurusConfig} from '@docusaurus/types'; @@ -59,7 +60,9 @@ async function getTerserMinifier(): Promise { minifyJS: true, }); } catch (err) { - throw new Error('HTML minification failed', {cause: err as Error}); + throw new Error(`HTML minification failed (Terser)`, { + cause: err as Error, + }); } }, }; @@ -91,9 +94,54 @@ async function getSwcMinifier(): Promise { minifyJson: true, minifyCss: true, }); + + // Escape hatch because SWC is quite aggressive to report errors + // TODO figure out what to do with these errors: throw or swallow? + // See https://github.com/facebook/docusaurus/pull/10554 + // See https://github.com/swc-project/swc/discussions/9616#discussioncomment-10846201 + const ignoreSwcMinifierErrors = + process.env.DOCUSAURUS_IGNORE_SWC_HTML_MINIFIER_ERRORS === 'true'; + if (!ignoreSwcMinifierErrors && result.errors) { + const ignoredErrors: string[] = [ + // TODO Docusaurus seems to emit NULL chars, and minifier detects it + // see https://github.com/facebook/docusaurus/issues/9985 + 'Unexpected null character', + ]; + result.errors = result.errors.filter( + (diagnostic) => !ignoredErrors.includes(diagnostic.message), + ); + if (result.errors.length) { + throw new Error( + `HTML minification diagnostic errors: +- ${result.errors + .map( + (diagnostic) => + `[${diagnostic.level}] ${ + diagnostic.message + } - ${JSON.stringify(diagnostic.span)}`, + ) + .join('\n- ')} +Note: please report the problem to the Docusaurus team +In the meantime, you can skip this error with ${logger.code( + 'DOCUSAURUS_IGNORE_SWC_HTML_MINIFIER_ERRORS=true', + )}`, + ); + } + /* + if (result.errors.length) { + throw new AggregateError( + result.errors.map( + (diagnostic) => new Error(JSON.stringify(diagnostic, null, 2)), + ), + ); + } + */ + } return result.code; } catch (err) { - throw new Error('HTML minification failed', {cause: err as Error}); + throw new Error(`HTML minification failed (SWC)`, { + cause: err as Error, + }); } }, };