web: Fix ESBuild hanging process (#18162)

This commit is contained in:
Teffen Ellis
2025-11-15 02:13:28 +01:00
committed by GitHub
parent 4c92eff521
commit 90d9b2daf9
3 changed files with 110 additions and 60 deletions

View File

@@ -1,12 +1,15 @@
/**
* @file MDX plugin for ESBuild.
*
* @import { Plugin, PluginBuild, BuildContext } from "esbuild"
* @import { Plugin, PluginBuild, BuildContext, BuildOptions } from "esbuild"
* @import { BaseLogger } from "pino"
*/
import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { dirname, join, relative } from "node:path";
import { ConsoleLogger } from "#logger/node";
import { resolvePackage } from "@goauthentik/core/paths/node";
@@ -16,12 +19,23 @@ const CSSNamespace = /** @type {const} */ ({
Bundled: "css-bundled",
});
/**
* @typedef StyleLoaderPluginOptions
*
* @property {boolean} [watch] Whether to watch for file changes.
* @property {BaseLogger} [logger]
*/
/**
* Selectively apply the ESBuild `css` loader.
*
* @param {StyleLoaderPluginOptions} [options]
* @returns {Plugin}
*/
export function styleLoaderPlugin() {
export function styleLoaderPlugin({
watch = false,
logger = ConsoleLogger.child({ name: "style-loader-plugin" }),
} = {}) {
const patternflyPath = resolvePackage("@patternfly/patternfly", import.meta);
const require = createRequire(import.meta.url);
@@ -56,7 +70,8 @@ export function styleLoaderPlugin() {
const disposables = new Map();
build.onDispose(async () => {
for (const ctx of disposables.values()) {
for (const [filePath, ctx] of disposables) {
logger.debug(`Disposing CSS build context for ${filePath}`);
await ctx.dispose();
}
});
@@ -114,33 +129,50 @@ export function styleLoaderPlugin() {
const cssContent = await readFile(args.path, "utf8");
let context = disposables.get(args.path);
if (!context) {
context = await build.esbuild.context({
stdin: {
contents: cssContent,
resolveDir: dirname(args.path),
loader: "css",
},
metafile: true,
bundle: true,
write: false,
minify: build.initialOptions.minify || false,
logLevel: "silent",
loader: { ".woff": "empty", ".woff2": "empty" },
plugins: [
{
name: "font-resolver",
setup(fontBuild) {
fontBuild.onResolve(...fontResolverArgs);
},
/**
* @type {BuildOptions}
*/
const buildOptions = {
stdin: {
contents: cssContent,
resolveDir: dirname(args.path),
loader: "css",
},
metafile: true,
bundle: true,
write: false,
minify: build.initialOptions.minify || false,
logLevel: "silent",
loader: { ".woff": "empty", ".woff2": "empty" },
plugins: [
{
name: "font-resolver",
setup(fontBuild) {
fontBuild.onResolve(...fontResolverArgs);
},
],
});
},
],
};
disposables.set(args.path, context);
if (!watch) {
const result = await build.esbuild.build(buildOptions);
const bundledCSS = result.outputFiles?.[0].text;
return {
contents: bundledCSS,
loader: "text",
};
}
await context.cancel();
if (!context) {
const relativePath = relative(absWorkingDir, args.path);
logger.debug(`Watching ${relativePath}`);
context = await build.esbuild.context(buildOptions);
disposables.set(args.path, context);
} else {
await context.cancel();
}
// Resolve the CSS content by bundling it with ESBuild.
const result = await context.rebuild();