diff --git a/e2e/cases/inline-chunk/index.test.ts b/e2e/cases/inline-chunk/index.test.ts index ad0bc3795fe..b91ce1194d3 100644 --- a/e2e/cases/inline-chunk/index.test.ts +++ b/e2e/cases/inline-chunk/index.test.ts @@ -1,5 +1,5 @@ import path from 'node:path'; -import { build, gotoPage } from '@e2e/helper'; +import { build, dev, gotoPage } from '@e2e/helper'; import { expect, test } from '@playwright/test'; import type { RspackChain } from '@rsbuild/core'; @@ -155,3 +155,204 @@ test('inline styles by filename and file size', async () => { ).length, ).toEqual(0); }); + +test('styles are not inline by default in development mode', async ({ + page, +}) => { + const rsbuild = await dev({ + cwd: __dirname, + rsbuildConfig: { + tools: toolsConfig, + }, + }); + + await gotoPage(page, rsbuild); + + // index.css in page + await expect( + page.evaluate( + `document.querySelectorAll('link[href*="index.css"]').length`, + ), + ).resolves.toEqual(1); +}); + +test('using RegExp to inline styles in development mode', async ({ page }) => { + const rsbuild = await dev({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineStyles: { + enable: true, + test: /\.css$/, + }, + }, + tools: toolsConfig, + }, + }); + + await gotoPage(page, rsbuild); + + // no index.css in page + await expect( + page.evaluate( + `document.querySelectorAll('link[href*="index.css"]').length`, + ), + ).resolves.toEqual(0); +}); + +test('inline styles by filename and file size in development mode', async ({ + page, +}) => { + const rsbuild = await dev({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineStyles: { + enable: true, + test({ size, name }: { size: number; name: string }) { + return name.includes('index') && size < 1000; + }, + }, + }, + tools: toolsConfig, + }, + }); + + await gotoPage(page, rsbuild); + + // no index.css in page + await expect( + page.evaluate( + `document.querySelectorAll('link[href*="index.css"]').length`, + ), + ).resolves.toEqual(0); +}); + +test('inline scripts does not work when enable is false', async () => { + const rsbuild = await build({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineScripts: { + enable: false, + test: /\.js$/, + }, + }, + tools: toolsConfig, + }, + }); + const files = await rsbuild.unwrapOutputJSON(false); + + // all index.js in output + expect( + Object.keys(files).filter( + (fileName) => fileName.endsWith('.js') && fileName.includes('/index.'), + ).length, + ).toEqual(1); + + // all source maps in output + expect( + Object.keys(files).filter((fileName) => fileName.endsWith('.js.map')) + .length, + ).toBeGreaterThanOrEqual(2); +}); + +test('inline styles does not work when enable is false', async () => { + const rsbuild = await build({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineStyles: { + enable: false, + test: /\.css$/, + }, + }, + tools: toolsConfig, + }, + }); + const files = await rsbuild.unwrapOutputJSON(false); + + // all index.css in output + expect( + Object.keys(files).filter( + (fileName) => fileName.endsWith('.css') && fileName.includes('/index.'), + ).length, + ).toEqual(1); +}); + +test('inline chunk works in production mode when enable is auto', async () => { + const rsbuild = await build({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineScripts: { + enable: 'auto', + test: /\.js$/, + }, + inlineStyles: { + enable: 'auto', + test: /\.css$/, + }, + }, + tools: toolsConfig, + }, + }); + const files = await rsbuild.unwrapOutputJSON(false); + + // no index.js in output + expect( + Object.keys(files).filter( + (fileName) => fileName.endsWith('.js') && fileName.includes('/index.'), + ).length, + ).toEqual(0); + + // all source maps in output + expect( + Object.keys(files).filter((fileName) => fileName.endsWith('.js.map')) + .length, + ).toBeGreaterThanOrEqual(2); + + // no index.css in output + expect( + Object.keys(files).filter( + (fileName) => fileName.endsWith('.css') && fileName.includes('/index.'), + ).length, + ).toEqual(0); +}); + +test('inline does not work in development mode when enable is auto', async ({ + page, +}) => { + const rsbuild = await dev({ + cwd: __dirname, + rsbuildConfig: { + output: { + inlineScripts: { + enable: 'auto', + test: /\.js$/, + }, + inlineStyles: { + enable: 'auto', + test: /\.css$/, + }, + }, + tools: toolsConfig, + }, + }); + + await gotoPage(page, rsbuild); + + // all index.js in page + await expect( + page.evaluate( + `document.querySelectorAll('script[src*="index.js"]').length`, + ), + ).resolves.toEqual(1); + + // all index.css in page + await expect( + page.evaluate( + `document.querySelectorAll('link[href*="index.css"]').length`, + ), + ).resolves.toEqual(1); +}); diff --git a/packages/core/src/plugins/inlineChunk.ts b/packages/core/src/plugins/inlineChunk.ts index 7379073a494..0e271ca44e2 100644 --- a/packages/core/src/plugins/inlineChunk.ts +++ b/packages/core/src/plugins/inlineChunk.ts @@ -3,8 +3,8 @@ import { CSS_REGEX, JS_REGEX } from '../constants'; import { addTrailingSlash, getPublicPathFromCompiler, - isDev, isFunction, + isProd, } from '../helpers'; import type { HtmlBasicTag, @@ -228,7 +228,7 @@ export const pluginInlineChunk = (): RsbuildPlugin => ({ ({ headTags, bodyTags }, { compiler, compilation, environment }) => { const { htmlPaths, config } = environment; - if (isDev() || Object.keys(htmlPaths).length === 0) { + if (Object.keys(htmlPaths).length === 0) { return { headTags, bodyTags }; } @@ -238,11 +238,37 @@ export const pluginInlineChunk = (): RsbuildPlugin => ({ const styleTests: InlineChunkTest[] = []; if (inlineScripts) { - scriptTests.push(inlineScripts === true ? JS_REGEX : inlineScripts); + if (inlineScripts === true) { + scriptTests.push(JS_REGEX); + } else if ( + inlineScripts instanceof RegExp || + inlineScripts instanceof Function + ) { + scriptTests.push(inlineScripts); + } else { + const enable = + inlineScripts.enable === 'auto' ? isProd() : inlineScripts.enable; + if (enable) { + scriptTests.push(inlineScripts.test); + } + } } if (inlineStyles) { - styleTests.push(inlineStyles === true ? CSS_REGEX : inlineStyles); + if (inlineStyles === true) { + styleTests.push(CSS_REGEX); + } else if ( + inlineStyles instanceof RegExp || + inlineStyles instanceof Function + ) { + styleTests.push(inlineStyles); + } else { + const enable = + inlineStyles.enable === 'auto' ? isProd() : inlineStyles.enable; + if (enable) { + styleTests.push(inlineStyles.test); + } + } } if (!scriptTests.length && !styleTests.length) { diff --git a/packages/core/src/types/config/output.ts b/packages/core/src/types/config/output.ts index 358d6251242..a9b396377cd 100644 --- a/packages/core/src/types/config/output.ts +++ b/packages/core/src/types/config/output.ts @@ -189,6 +189,11 @@ export type InlineChunkTestFunction = (params: { export type InlineChunkTest = RegExp | InlineChunkTestFunction; +export type InlineChunkConfig = + | boolean + | InlineChunkTest + | { enable?: boolean | 'auto'; test: InlineChunkTest }; + export interface OutputConfig { /** * Specify build target to run in specified environment. @@ -265,11 +270,11 @@ export interface OutputConfig { /** * Whether to inline output scripts files (.js files) into HTML with `