From 562368683a96d00a02b079fa3cfc580908ba18b7 Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:37:21 -0700 Subject: [PATCH] web: build system had some legacy stuff that I found confusing while working on the CSS ordering (#20698) * . * Did I miss something? * That was a stupid spelling error. * This was an unpopular move. --- web/package.json | 2 +- web/paths/node.js | 2 +- web/scripts/build-web.mjs | 180 ++++++++++++++++++-------------------- 3 files changed, 88 insertions(+), 96 deletions(-) diff --git a/web/package.json b/web/package.json index b2212c88fc..90ec2ebadc 100644 --- a/web/package.json +++ b/web/package.json @@ -248,7 +248,7 @@ } }, "build-proxy": { - "command": "node scripts/build-web.mjs --proxy", + "command": "node scripts/build-web.mjs --styles-only", "dependencies": [ "build-locales" ] diff --git a/web/paths/node.js b/web/paths/node.js index a07fa7e8e1..143c8cdb0b 100644 --- a/web/paths/node.js +++ b/web/paths/node.js @@ -90,7 +90,7 @@ export const EntryPoint = /** @type {const} */ ({ in: resolve(PackageRoot, "src", "styles", "authentik", "static.global.css"), out: resolve(DistDirectory, "styles", "static"), }, - FlowsStyles: { + FlowStyles: { in: resolve(PackageRoot, "src", "styles", "authentik", "flows.global.css"), out: resolve(DistDirectory, "styles", "flow"), }, diff --git a/web/scripts/build-web.mjs b/web/scripts/build-web.mjs index 4d673bab4e..b43e4b40ba 100644 --- a/web/scripts/build-web.mjs +++ b/web/scripts/build-web.mjs @@ -10,7 +10,7 @@ import * as path from "node:path"; /** * @file ESBuild script for building the authentik web UI. * - * @import { BuildOptions } from "esbuild"; + * @import { BuildOptions, Plugin } from "esbuild"; */ import { mdxPlugin } from "#bundler/mdx-plugin/node"; import { styleLoaderPlugin } from "#bundler/style-loader-plugin/node"; @@ -22,7 +22,6 @@ import { NodeEnvironment } from "@goauthentik/core/environment/node"; import { MonoRepoRoot } from "@goauthentik/core/paths/node"; import { BuildIdentifier } from "@goauthentik/core/version/node"; -import { deepmerge } from "deepmerge-ts"; import esbuild from "esbuild"; /// @@ -52,8 +51,59 @@ const assets = [ [path.resolve(PackageRoot, "icons"), "./assets/icons"], ]; +const entryPointNames = Object.keys(EntryPoint); +const entryPoints = Object.values(EntryPoint); +const entryPointsDescription = entryPointNames.join("\n\t"); + /** - * @type {Readonly} + * @type {Plugin[]} + */ +const BASE_ESBUILD_PLUGINS = [ + { + name: "copy", + setup(build) { + build.onEnd(async () => { + /** + * @type {import('esbuild').PartialMessage[]} + */ + const errors = []; + + /** + * @param {SourceDestinationPair} pair + */ + const copy = ([from, to]) => { + const resolvedDestination = path.resolve(DistDirectory, to); + + logger.debug(`📋 Copying assets from ${from} to ${to}`); + + return fs + .cp(from, resolvedDestination, { + recursive: true, + }) + .catch((error) => { + errors.push({ + text: `Failed to copy assets from ${from} to ${to}: ${error}`, + location: { + file: from, + }, + }); + }); + }; + + await Promise.all(assets.map(copy)); + + return { errors }; + }); + }, + }, + + mdxPlugin({ + root: MonoRepoRoot, + }), +]; + +/** + * @type {BuildOptions} */ const BASE_ESBUILD_OPTIONS = { entryNames: `[dir]/[name]-${BuildIdentifier}`, @@ -84,60 +134,28 @@ const BASE_ESBUILD_OPTIONS = { * @see https://nodejs.org/api/packages.html#packages_conditional_exports */ conditions: NodeEnvironment === "production" ? ["production"] : ["development", "production"], - plugins: [ - { - name: "copy", - setup(build) { - build.onEnd(async () => { - /** - * @type {import('esbuild').PartialMessage[]} - */ - const errors = []; - - /** - * @param {SourceDestinationPair} pair - */ - const copy = ([from, to]) => { - const resolvedDestination = path.resolve(DistDirectory, to); - - logger.debug(`📋 Copying assets from ${from} to ${to}`); - - return fs - .cp(from, resolvedDestination, { recursive: true }) - .catch((error) => { - errors.push({ - text: `Failed to copy assets from ${from} to ${to}: ${error}`, - location: { - file: from, - }, - }); - }); - }; - - await Promise.all(assets.map(copy)); - - return { errors }; - }); - }, - }, - - mdxPlugin({ - root: MonoRepoRoot, - }), - ], + plugins: BASE_ESBUILD_PLUGINS, define: bundleDefinitions, format: "esm", - logOverride: { - /** - * HACK: Silences issue originating in ESBuild. - * - * @see {@link https://github.com/evanw/esbuild/blob/b914dd30294346aa15fcc04278f4b4b51b8b43b5/internal/logger/msg_ids.go#L211 ESBuild source} - * @expires 2025-08-11 - */ - "invalid-source-url": "silent", - }, }; +/** + * Creates an ESBuild options, extending the base options with the given overrides. + * + * @param {BuildOptions["entryPoints"]} entryPoints + * @param {Plugin[]} plugIns + * @returns {BuildOptions} + */ +export function createESBuildOptions(entryPoints, plugIns = []) { + const plugins = [...BASE_ESBUILD_PLUGINS, ...plugIns]; + + return { + ...BASE_ESBUILD_OPTIONS, + entryPoints, + plugins, + }; +} + async function cleanDistDirectory() { logger.info(`♻️ Cleaning previous builds...`); @@ -153,29 +171,12 @@ async function cleanDistDirectory() { logger.info(`♻️ Done!`); } -/** - * Creates an ESBuild options, extending the base options with the given overrides. - * - * @param {BuildOptions} overrides - * @returns {BuildOptions} - */ -export function createESBuildOptions(overrides) { - /** - * @type {BuildOptions} - */ - const mergedOptions = deepmerge(BASE_ESBUILD_OPTIONS, overrides); - - return mergedOptions; -} - function doHelp() { logger.info(`Build the authentik UI options: -w, --watch: Build all interfaces - -p, --proxy: Build only the polyfills and the loading application - -h, --help: This help message -`); + -s, --styles-only: Build the static CSS`); process.exit(0); } @@ -185,23 +186,23 @@ function doHelp() { * @returns {Promise<() => Promise>} dispose */ async function doWatch() { - logger.info(`🤖 Watching entry points:\n\t${Object.keys(EntryPoint).join("\n\t")}`); - - const entryPoints = Object.values(EntryPoint); + logger.info(`🤖 Watching entry points:\n\t${entryPointsDescription}`); const developmentPlugins = await import("@goauthentik/esbuild-plugin-live-reload/plugin") .then(({ liveReloadPlugin }) => [ liveReloadPlugin({ relativeRoot: PackageRoot, - logger: logger.child({ name: "Live Reload" }), + logger: logger.child({ + name: "Live Reload", + }), }), ]) .catch(() => []); - const buildOptions = createESBuildOptions({ - entryPoints, - plugins: [...developmentPlugins, styleLoaderPlugin({ logger, watch: true })], - }); + const buildOptions = createESBuildOptions(entryPoints, [ + ...developmentPlugins, + styleLoaderPlugin({ logger, watch: true }), + ]); const buildContext = await esbuild.context(buildOptions); @@ -228,14 +229,9 @@ async function doWatch() { } async function doBuild() { - logger.info(`🤖 Building entry points:\n\t${Object.keys(EntryPoint).join("\n\t")}`); + logger.info(`🤖 Building entry points:\n\t${entryPointsDescription}`); - const entryPoints = Object.values(EntryPoint); - - const buildOptions = createESBuildOptions({ - entryPoints, - plugins: [styleLoaderPlugin({ logger })], - }); + const buildOptions = createESBuildOptions(entryPoints, [styleLoaderPlugin({ logger })]); await esbuild.build(buildOptions); @@ -246,13 +242,9 @@ async function doProxy() { const entryPoints = [ EntryPoint.InterfaceStyles, EntryPoint.StaticStyles, - EntryPoint.FlowsStyles, + EntryPoint.FlowStyles, ]; - - const buildOptions = createESBuildOptions({ - entryPoints, - plugins: [styleLoaderPlugin({ logger })], - }); + const buildOptions = createESBuildOptions(entryPoints, [styleLoaderPlugin({ logger })]); await esbuild.build(buildOptions); logger.info("Proxy build complete"); @@ -269,8 +261,8 @@ async function delegateCommand() { case "--watch": return doWatch(); // There's no watch-for-proxy, sorry. - case "-p": - case "--proxy": + case "-s": + case "--styles-only": return doProxy(); default: return doBuild();