From 64c76f2c4dc18c180912366a55137617eea1a90c Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 23 Oct 2026 21:01:08 +0200 Subject: [PATCH] refactor(bundler): target the current Node.js version for server builds, remove the hardcoded `26.0.0` clamp (AI-assisted) SWC no longer falls back to ES5 for Node.js versions newer than its Browserslist data: it now skips all transforms for them, like Babel (swc-project/swc#11457, shipped in every Rspack 2.x). The server target is the current Node.js version without its pre-release tag, because SWC panics on versions like "27.0.0-rc.1". Co-Authored-By: Claude Opus 5.5 --- .../src/__tests__/browserslist.test.ts | 39 +++++++++++++++ .../docusaurus-bundler/src/browserslist.ts | 48 ++++--------------- .../src/loaders/jsLoader.ts | 2 +- packages/docusaurus-bundler/src/swc.ts | 5 +- packages/docusaurus/src/webpack/base.ts | 2 +- 5 files changed, 50 insertions(+), 46 deletions(-) create mode 100644 packages/docusaurus-bundler/src/__tests__/browserslist.test.ts diff --git a/packages/docusaurus-bundler/src/__tests__/browserslist.test.ts b/packages/docusaurus-bundler/src/__tests__/browserslist.test.ts new file mode 100644 index 000000000000..c09ee72e542f --- /dev/null +++ b/packages/docusaurus-bundler/src/__tests__/browserslist.test.ts @@ -0,0 +1,39 @@ +/** + * 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 {afterEach, describe, expect, it, vi} from 'vitest'; +import {getTargets} from '../browserslist'; + +const realNodeVersion = process.versions.node; + +function stubNodeVersion(version: string) { + Object.defineProperty(process.versions, 'node', {value: version}); +} + +describe('getTargets', () => { + describe('server', () => { + afterEach(() => { + stubNodeVersion(realNodeVersion); + vi.unstubAllEnvs(); + }); + + it('targets the current Node.js version', () => { + stubNodeVersion('26.8.1'); + expect(getTargets({isServer: true})).toEqual(['node 26.8.1']); + }); + + it('removes the pre-release tag of the current Node.js version', () => { + stubNodeVersion('27.0.0-rc.1'); + expect(getTargets({isServer: true})).toEqual(['node 27.0.0']); + }); + + it('targets the DOCUSAURUS_SERVER_NODE_TARGET env variable', () => { + vi.stubEnv('DOCUSAURUS_SERVER_NODE_TARGET', '24.18.0'); + expect(getTargets({isServer: true})).toEqual(['node 24.18.0']); + }); + }); +}); diff --git a/packages/docusaurus-bundler/src/browserslist.ts b/packages/docusaurus-bundler/src/browserslist.ts index 725cca50a033..fd27abc7f7e2 100644 --- a/packages/docusaurus-bundler/src/browserslist.ts +++ b/packages/docusaurus-bundler/src/browserslist.ts @@ -8,7 +8,6 @@ import browserslist from 'browserslist'; import semver from 'semver'; import {browserslistToTargets, type TransformOptions} from 'lightningcss'; -import type {CurrentBundler} from '@docusaurus/types'; // Used when the site doesn't provide its own Browserslist config // See https://web.dev/baseline @@ -18,14 +17,8 @@ const DefaultBrowserslistQueries = ['baseline widely available']; * The targets the client or server bundle is compiled for, like * `['chrome 120', 'firefox 115']` or `['node 24.11.1']` */ -export function getTargets({ - isServer, - bundlerName, -}: { - isServer: boolean; - bundlerName: CurrentBundler['name']; -}): string[] { - return isServer ? getServerTargets({bundlerName}) : getClientTargets(); +export function getTargets({isServer}: {isServer: boolean}): string[] { + return isServer ? getServerTargets() : getClientTargets(); } export function getClientQueries(): string[] { @@ -42,41 +35,16 @@ function getClientTargets(): string[] { return browserslist(getClientQueries()); } -export function getServerTargets({ - bundlerName, -}: { - bundlerName: CurrentBundler['name']; -}): string[] { +// SWC skips all transforms for Node versions newer than its Browserslist data +// See https://github.com/swc-project/swc/pull/11457 +export function getServerTargets(): string[] { // Escape hatch env variable if (process.env.DOCUSAURUS_SERVER_NODE_TARGET) { return [`node ${process.env.DOCUSAURUS_SERVER_NODE_TARGET}`]; } - // For server builds, we want to use the current Node version as target - // But we can't pass a target that Browserslist doesn't know about yet - const nodeTarget = getMinVersion( - process.versions.node, - getLastBrowserslistKnownNodeVersion(bundlerName), - ); - return [`node ${nodeTarget}`]; -} - -// TODO this is not accurate -// for Rspack we should read from the built-in browserslist data -// see https://github.com/facebook/docusaurus/pull/11496 -function getLastBrowserslistKnownNodeVersion( - bundlerName: CurrentBundler['name'], -): string { - if (bundlerName === 'rspack') { - // TODO hardcoded value until Rspack exposes its Browserslist data - // see https://github.com/facebook/docusaurus/pull/11496 - return '26.0.0'; - } - // browserslist('last 1 node versions')[0]!.replace('node ', '') - return browserslist.nodeVersions.at(-1)!; -} - -function getMinVersion(v1: string, v2: string): string { - return semver.lt(v1, v2) ? v1 : v2; + // SWC panics on pre-release versions like "27.0.0-rc.1" + const nodeVersion = semver.coerce(process.versions.node)!.version; + return [`node ${nodeVersion}`]; } // LightningCSS doesn't expose any type for minimizer-webpack-plugin setup diff --git a/packages/docusaurus-bundler/src/loaders/jsLoader.ts b/packages/docusaurus-bundler/src/loaders/jsLoader.ts index e510c31cad4c..f3691646af57 100644 --- a/packages/docusaurus-bundler/src/loaders/jsLoader.ts +++ b/packages/docusaurus-bundler/src/loaders/jsLoader.ts @@ -29,7 +29,7 @@ function createRspackSwcJsLoaderFactory(): ConfigureWebpackUtils['getJSLoader'] return ({isServer}) => { return { loader, - options: getSwcLoaderOptions({isServer, bundlerName: 'rspack'}), + options: getSwcLoaderOptions({isServer}), }; }; } diff --git a/packages/docusaurus-bundler/src/swc.ts b/packages/docusaurus-bundler/src/swc.ts index 939bc33d35d9..3763a05bba25 100644 --- a/packages/docusaurus-bundler/src/swc.ts +++ b/packages/docusaurus-bundler/src/swc.ts @@ -7,19 +7,16 @@ import {getClientQueries, getServerTargets} from './browserslist'; import type {SwcLoaderOptions} from '@rspack/core'; -import type {CurrentBundler} from '@docusaurus/types'; export function getSwcLoaderOptions({ isServer, - bundlerName, }: { isServer: boolean; - bundlerName: CurrentBundler['name']; }): SwcLoaderOptions { return { env: { // Rspack resolves the client queries with its own Browserslist data - targets: isServer ? getServerTargets({bundlerName}) : getClientQueries(), + targets: isServer ? getServerTargets() : getClientQueries(), }, jsc: { parser: { diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 2d73953fb460..7d3aedbb2170 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -118,7 +118,7 @@ export async function createBaseConfig({ // Changing the target browsers or Node.js version must evict the cache // The same Browserslist queries can match other browsers over time and // after data upgrades, for example "baseline widely available" - getTargets({isServer, bundlerName: props.currentBundler.name}), + getTargets({isServer}), // The plugin code computing loader options is not a build dependency // Upgrading a third-party plugin must evict the cache siteMetadata.pluginVersions,