mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-02 16:29:47 +02:00
fix(core): fix handling of Swc html minifier warnings (#10581)
This commit is contained in:
parent
97e6c42099
commit
904b53b963
2 changed files with 105 additions and 56 deletions
|
@ -5,7 +5,6 @@
|
|||
* 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';
|
||||
|
@ -13,12 +12,17 @@ import type {DocusaurusConfig} from '@docusaurus/types';
|
|||
// Historical env variable
|
||||
const SkipHtmlMinification = process.env.SKIP_HTML_MINIFICATION === 'true';
|
||||
|
||||
export type HtmlMinifierResult = {
|
||||
code: string;
|
||||
warnings: string[];
|
||||
};
|
||||
|
||||
export type HtmlMinifier = {
|
||||
minify: (html: string) => Promise<string>;
|
||||
minify: (html: string) => Promise<HtmlMinifierResult>;
|
||||
};
|
||||
|
||||
const NoopMinifier: HtmlMinifier = {
|
||||
minify: async (html: string) => html,
|
||||
minify: async (html: string) => ({code: html, warnings: []}),
|
||||
};
|
||||
|
||||
type SiteConfigSlice = {
|
||||
|
@ -50,7 +54,7 @@ async function getTerserMinifier(): Promise<HtmlMinifier> {
|
|||
return {
|
||||
minify: async function minifyHtmlWithTerser(html) {
|
||||
try {
|
||||
return await terserHtmlMinifier(html, {
|
||||
const code = await terserHtmlMinifier(html, {
|
||||
removeComments: false,
|
||||
removeRedundantAttributes: true,
|
||||
removeEmptyAttributes: true,
|
||||
|
@ -59,6 +63,7 @@ async function getTerserMinifier(): Promise<HtmlMinifier> {
|
|||
useShortDoctype: true,
|
||||
minifyJS: true,
|
||||
});
|
||||
return {code, warnings: []};
|
||||
} catch (err) {
|
||||
throw new Error(`HTML minification failed (Terser)`, {
|
||||
cause: err as Error,
|
||||
|
@ -95,49 +100,16 @@ async function getSwcMinifier(): Promise<HtmlMinifier> {
|
|||
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;
|
||||
const warnings = (result.errors ?? []).map((diagnostic) => {
|
||||
return `[HTML minifier diagnostic - ${diagnostic.level}] ${
|
||||
diagnostic.message
|
||||
} - ${JSON.stringify(diagnostic.span)}`;
|
||||
});
|
||||
|
||||
return {
|
||||
code: result.code,
|
||||
warnings,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new Error(`HTML minification failed (SWC)`, {
|
||||
cause: err as Error,
|
||||
|
|
|
@ -110,6 +110,57 @@ function pathnameToFilename({
|
|||
return `${outputFileName}.html`;
|
||||
}
|
||||
|
||||
export function printSSGWarnings(
|
||||
results: {
|
||||
pathname: string;
|
||||
warnings: string[];
|
||||
}[],
|
||||
): void {
|
||||
// Escape hatch because SWC is quite aggressive to report errors
|
||||
// See https://github.com/facebook/docusaurus/pull/10554
|
||||
// See https://github.com/swc-project/swc/discussions/9616#discussioncomment-10846201
|
||||
if (process.env.DOCUSAURUS_IGNORE_SSG_WARNINGS === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
const ignoredWarnings: string[] = [
|
||||
// TODO React/Docusaurus emit NULL chars, and minifier detects it
|
||||
// see https://github.com/facebook/docusaurus/issues/9985
|
||||
'Unexpected null character',
|
||||
];
|
||||
|
||||
const keepWarning = (warning: string) => {
|
||||
return !ignoredWarnings.some((iw) => warning.includes(iw));
|
||||
};
|
||||
|
||||
const resultsWithWarnings = results
|
||||
.map((result) => {
|
||||
return {
|
||||
...result,
|
||||
warnings: result.warnings.filter(keepWarning),
|
||||
};
|
||||
})
|
||||
.filter((result) => result.warnings.length > 0);
|
||||
|
||||
if (resultsWithWarnings.length) {
|
||||
const message = `Docusaurus static site generation process emitted warnings for ${
|
||||
resultsWithWarnings.length
|
||||
} path${resultsWithWarnings.length ? 's' : ''}
|
||||
This is non-critical and can be disabled with DOCUSAURUS_IGNORE_SSG_WARNINGS=true
|
||||
Troubleshooting guide: https://github.com/facebook/docusaurus/discussions/10580
|
||||
|
||||
- ${resultsWithWarnings
|
||||
.map(
|
||||
(result) => `${logger.path(result.pathname)}:
|
||||
- ${result.warnings.join('\n - ')}
|
||||
`,
|
||||
)
|
||||
.join('\n- ')}`;
|
||||
|
||||
logger.warn(message);
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateStaticFiles({
|
||||
pathnames,
|
||||
renderer,
|
||||
|
@ -121,8 +172,18 @@ export async function generateStaticFiles({
|
|||
params: SSGParams;
|
||||
htmlMinifier: HtmlMinifier;
|
||||
}): Promise<{collectedData: SiteCollectedData}> {
|
||||
type SSGSuccess = {pathname: string; error: null; result: AppRenderResult};
|
||||
type SSGError = {pathname: string; error: Error; result: null};
|
||||
type SSGSuccess = {
|
||||
pathname: string;
|
||||
error: null;
|
||||
result: AppRenderResult;
|
||||
warnings: string[];
|
||||
};
|
||||
type SSGError = {
|
||||
pathname: string;
|
||||
error: Error;
|
||||
result: null;
|
||||
warnings: string[];
|
||||
};
|
||||
type SSGResult = SSGSuccess | SSGError;
|
||||
|
||||
// Note that we catch all async errors on purpose
|
||||
|
@ -136,15 +197,27 @@ export async function generateStaticFiles({
|
|||
params,
|
||||
htmlMinifier,
|
||||
}).then(
|
||||
(result) => ({pathname, result, error: null}),
|
||||
(error) => ({pathname, result: null, error: error as Error}),
|
||||
(result) => ({
|
||||
pathname,
|
||||
result,
|
||||
error: null,
|
||||
warnings: result.warnings,
|
||||
}),
|
||||
(error) => ({
|
||||
pathname,
|
||||
result: null,
|
||||
error: error as Error,
|
||||
warnings: [],
|
||||
}),
|
||||
),
|
||||
{concurrency: Concurrency},
|
||||
);
|
||||
|
||||
printSSGWarnings(results);
|
||||
|
||||
const [allSSGErrors, allSSGSuccesses] = _.partition(
|
||||
results,
|
||||
(r): r is SSGError => !!r.error,
|
||||
(result): result is SSGError => !!result.error,
|
||||
);
|
||||
|
||||
if (allSSGErrors.length > 0) {
|
||||
|
@ -179,7 +252,7 @@ async function generateStaticFile({
|
|||
renderer: AppRenderer;
|
||||
params: SSGParams;
|
||||
htmlMinifier: HtmlMinifier;
|
||||
}) {
|
||||
}): Promise<AppRenderResult & {warnings: string[]}> {
|
||||
try {
|
||||
// This only renders the app HTML
|
||||
const result = await renderer({
|
||||
|
@ -190,13 +263,17 @@ async function generateStaticFile({
|
|||
params,
|
||||
result,
|
||||
});
|
||||
const content = await htmlMinifier.minify(fullPageHtml);
|
||||
const minifierResult = await htmlMinifier.minify(fullPageHtml);
|
||||
await writeStaticFile({
|
||||
pathname,
|
||||
content,
|
||||
content: minifierResult.code,
|
||||
params,
|
||||
});
|
||||
return result;
|
||||
return {
|
||||
...result,
|
||||
// As of today, only the html minifier can emit SSG warnings
|
||||
warnings: minifierResult.warnings,
|
||||
};
|
||||
} catch (errorUnknown) {
|
||||
const error = errorUnknown as Error;
|
||||
const tips = getSSGErrorTips(error);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue