Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/docusaurus-bundler/src/browserslist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ import type {CurrentBundler} from '@docusaurus/types';
// See https://web.dev/baseline
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 getClientQueries(): string[] {
return (
browserslist.loadConfig({path: process.cwd()}) ?? DefaultBrowserslistQueries
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus-bundler/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export {
registerBundlerTracing,
} from './currentBundler';

export {getClientQueries} from './browserslist';
export {getClientQueries, getTargets} from './browserslist';
export {getMinimizers} from './minification';
export {getHtmlMinifier, type HtmlMinifier} from './minifyHtml';
export {createJsLoaderFactory} from './loaders/jsLoader';
Expand Down
94 changes: 83 additions & 11 deletions packages/docusaurus/src/webpack/__tests__/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {getCurrentBundler} from '@docusaurus/bundler';
import {excludeJS, clientDir, createBaseConfig} from '../base';
import {BundlerNames, createTestConfigureWebpackUtils} from './testUtils';
import {DEFAULT_FUTURE_CONFIG} from '../../server/configValidation';
import type {CurrentBundler, Props} from '@docusaurus/types';
import type {CurrentBundler, Props, SiteMetadata} from '@docusaurus/types';

describe('babel transpilation exclude logic', () => {
it('always transpiles client dir files', () => {
Expand Down Expand Up @@ -77,6 +77,7 @@ describe('base webpack config', () => {
},
siteMetadata: {
docusaurusVersion: '2.0.0-alpha.70',
pluginVersions: {},
},
plugins: [
{
Expand Down Expand Up @@ -160,24 +161,95 @@ describe('base webpack config', () => {
vi.unstubAllEnvs();
});

async function getCacheVersion(browserslistQuery: string) {
// Browserslist reads this env variable before any config file
vi.stubEnv('BROWSERSLIST', browserslistQuery);
async function getCacheVersion({
isServer = false,
siteMetadata = props.siteMetadata,
}: {
isServer?: boolean;
siteMetadata?: SiteMetadata;
} = {}) {
const config = await createBaseConfig({
props: await createProps(),
isServer: false,
props: {...(await createProps()), siteMetadata},
isServer,
minify: true,
configureWebpackUtils: await createTestConfigureWebpackUtils(),
});
return (config.cache as {version: string}).version;
}

it('is evicted when the Browserslist queries change', async () => {
const oldBrowsersVersion = await getCacheVersion('chrome 90');
const baselineVersion = await getCacheVersion(
'baseline widely available',
);
// Browserslist reads this env variable before any config file
function stubBrowserslistQuery(query: string) {
vi.stubEnv('BROWSERSLIST', query);
}

function stubServerNodeTarget(nodeVersion: string) {
vi.stubEnv('DOCUSAURUS_SERVER_NODE_TARGET', nodeVersion);
}

function createSiteMetadata({
siteVersion,
pluginVersion = '1.0.0',
}: {
siteVersion?: string;
pluginVersion?: string;
}): SiteMetadata {
return {
...props.siteMetadata,
siteVersion,
pluginVersions: {
'third-party-plugin': {
type: 'package',
name: 'third-party-plugin',
version: pluginVersion,
},
},
};
}

it('is evicted when the Browserslist queries match other browsers', async () => {
stubBrowserslistQuery('chrome 90');
const oldBrowsersVersion = await getCacheVersion();
stubBrowserslistQuery('baseline widely available');
const baselineVersion = await getCacheVersion();
expect(oldBrowsersVersion).not.toBe(baselineVersion);
});

it('is not evicted when other Browserslist queries match the same browsers', async () => {
stubBrowserslistQuery('chrome 90');
const versionQueryVersion = await getCacheVersion();
stubBrowserslistQuery('chrome >= 90 and chrome <= 90');
const rangeQueryVersion = await getCacheVersion();
expect(versionQueryVersion).toBe(rangeQueryVersion);
});

// Server targets aren't Browserslist queries: Browserslist doesn't know
// these Node.js patch releases
it('is evicted when the server Node.js target changes', async () => {
stubServerNodeTarget('24.11.1');
const oldNodeVersion = await getCacheVersion({isServer: true});
stubServerNodeTarget('24.11.2');
const newNodeVersion = await getCacheVersion({isServer: true});
expect(oldNodeVersion).not.toBe(newNodeVersion);
});

it('is evicted when a plugin package version changes', async () => {
const oldPluginVersion = await getCacheVersion({
siteMetadata: createSiteMetadata({pluginVersion: '1.0.0'}),
});
const newPluginVersion = await getCacheVersion({
siteMetadata: createSiteMetadata({pluginVersion: '1.1.0'}),
});
expect(oldPluginVersion).not.toBe(newPluginVersion);
});

it('is not evicted when the site version changes', async () => {
const oldSiteVersion = await getCacheVersion({
siteMetadata: createSiteMetadata({siteVersion: '1.0.0'}),
});
const newSiteVersion = await getCacheVersion({
siteMetadata: createSiteMetadata({siteVersion: '2.0.0'}),
});
expect(oldSiteVersion).toBe(newSiteVersion);
});
});
});
29 changes: 19 additions & 10 deletions packages/docusaurus/src/webpack/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import path from 'node:path';
import {
createJsLoaderFactory,
getClientQueries,
getCSSExtractPlugin,
getMinimizers,
getTargets,
} from '@docusaurus/bundler';
import {
ASSET_INLINE_MAX_SIZE,
Expand Down Expand Up @@ -109,15 +109,24 @@ export async function createBaseConfig({

// When the version string changes, the cache is evicted
function getCacheVersion() {
// Because Webpack does not evict the cache on alias/swizzle changes,
// See https://github.com/webpack/webpack/issues/13627
const themeAliasesHash = md5Hash(JSON.stringify(themeAliases));
// Loader and minimizer options are not part of the cache key
// Changing the Browserslist config must evict the cache
const browserslistHash = md5Hash(JSON.stringify(getClientQueries()));
// Asset module parser options are not part of the cache key
// Changing the asset inline max size must evict the cache
return `${siteMetadata.docusaurusVersion}-${themeAliasesHash}-${browserslistHash}-${ASSET_INLINE_MAX_SIZE}`;
const versionParts: unknown[] = [
siteMetadata.docusaurusVersion,
// Because Webpack does not evict the cache on alias/swizzle changes,
// See https://github.com/webpack/webpack/issues/13627
themeAliases,
// Loader and minimizer options are not part of the cache key
// 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}),
// The plugin code computing loader options is not a build dependency
// Upgrading a third-party plugin must evict the cache
siteMetadata.pluginVersions,
// Asset module parser options are not part of the cache key
// Changing the asset inline max size must evict the cache
ASSET_INLINE_MAX_SIZE,
];
return md5Hash(JSON.stringify(versionParts));
}

// When one of those modules/dependencies change (including transitive
Expand Down
Loading