diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index ac109619a..c9c0ceeda 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -327,8 +327,8 @@ export function composeBannerFooterConfig( banner: BannerAndFooter, footer: BannerAndFooter, ): RsbuildConfig { - const bannerConfig = pick(banner, ['js', 'css']); - const footerConfig = pick(footer, ['js', 'css']); + const bannerConfig = pick(banner, ['js', 'css', 'raw']); + const footerConfig = pick(footer, ['js', 'css', 'raw']); if (isEmptyObject(bannerConfig) && isEmptyObject(footerConfig)) { return {}; @@ -342,7 +342,7 @@ export function composeBannerFooterConfig( new rspack.BannerPlugin({ banner: bannerConfig.js, stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1, - raw: true, + raw: bannerConfig?.raw ?? true, include: /\.(js|mjs|cjs)$/, }), ); @@ -352,7 +352,7 @@ export function composeBannerFooterConfig( new rspack.BannerPlugin({ banner: bannerConfig.css, stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1, - raw: true, + raw: bannerConfig?.raw ?? true, include: /\.(css)$/, }), ); @@ -365,7 +365,7 @@ export function composeBannerFooterConfig( new rspack.BannerPlugin({ banner: footerConfig.js, stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1, - raw: true, + raw: footerConfig?.raw ?? true, footer: true, include: /\.(js|mjs|cjs)$/, }), @@ -376,7 +376,7 @@ export function composeBannerFooterConfig( new rspack.BannerPlugin({ banner: footerConfig.css, stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1, - raw: true, + raw: footerConfig?.raw ?? true, footer: true, include: /\.(css)$/, }), @@ -934,8 +934,8 @@ const composeDtsConfig = async ( abortOnError: dts?.abortOnError ?? true, dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts', autoExternal, - banner: banner?.dts, - footer: footer?.dts, + banner: { content: banner?.dts, raw: banner?.raw ?? true }, + footer: { content: footer?.dts, raw: footer?.raw ?? true }, }), ], }; diff --git a/packages/core/src/types/config/index.ts b/packages/core/src/types/config/index.ts index 7b159240d..b82e9c43c 100644 --- a/packages/core/src/types/config/index.ts +++ b/packages/core/src/types/config/index.ts @@ -45,11 +45,13 @@ export type AutoExternal = peerDependencies?: boolean; }; +export type BannerAndFooterOptions = { raw?: boolean }; + export type BannerAndFooter = { js?: string; css?: string; dts?: string; -}; +} & BannerAndFooterOptions; export type Shims = { cjs?: { diff --git a/packages/plugin-dts/src/apiExtractor.ts b/packages/plugin-dts/src/apiExtractor.ts index 95568eb61..e67b619e7 100644 --- a/packages/plugin-dts/src/apiExtractor.ts +++ b/packages/plugin-dts/src/apiExtractor.ts @@ -6,7 +6,7 @@ import { } from '@microsoft/api-extractor'; import { logger } from '@rsbuild/core'; import color from 'picocolors'; -import type { DtsEntry } from './index'; +import type { BannerAndFooter, DtsEntry } from './index'; import { addBannerAndFooter, getTimeCost } from './utils'; export type BundleOptions = { @@ -14,8 +14,8 @@ export type BundleOptions = { cwd: string; outDir: string; dtsExtension: string; - banner?: string; - footer?: string; + banner: BannerAndFooter; + footer: BannerAndFooter; dtsEntry: DtsEntry; tsconfigPath?: string; bundledPackages?: string[]; diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 3767dd915..1bc989c2b 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -20,10 +20,12 @@ export type PluginDtsOptions = { devDependencies?: boolean; peerDependencies?: boolean; }; - banner?: string; - footer?: string; + banner: BannerAndFooter; + footer: BannerAndFooter; }; +export type BannerAndFooter = { content?: string; raw?: boolean }; + export type DtsEntry = { name?: string; path?: string; diff --git a/packages/plugin-dts/src/tsc.ts b/packages/plugin-dts/src/tsc.ts index 712d9cfd4..d07477b32 100644 --- a/packages/plugin-dts/src/tsc.ts +++ b/packages/plugin-dts/src/tsc.ts @@ -1,5 +1,6 @@ import { logger } from '@rsbuild/core'; import color from 'picocolors'; +import type { BannerAndFooter } from 'src'; import ts from 'typescript'; import { getFileLoc, @@ -14,8 +15,8 @@ export type EmitDtsOptions = { configPath: string; declarationDir: string; dtsExtension: string; - banner?: string; - footer?: string; + banner: BannerAndFooter; + footer: BannerAndFooter; }; export async function emitDts( diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index b85c1ece4..585209b8c 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -7,7 +7,7 @@ import MagicString from 'magic-string'; import color from 'picocolors'; import { convertPathToPattern, glob } from 'tinyglobby'; import ts from 'typescript'; -import type { DtsEntry } from './index'; +import type { BannerAndFooter, DtsEntry } from './index'; export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine { const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile); @@ -85,22 +85,34 @@ export function getTimeCost(start: number): string { export async function addBannerAndFooter( file: string, - banner?: string, - footer?: string, + banner: BannerAndFooter, + footer: BannerAndFooter, ): Promise { - if (!banner && !footer) { + if (!banner.content && !footer.content) { return; } const content = await fsP.readFile(file, 'utf-8'); const code = new MagicString(content); - if (banner && !content.trimStart().startsWith(banner.trim())) { - code.prepend(`${banner}\n`); + if ( + banner.content && + !content.trimStart().startsWith(banner.content.trim()) && + !content.trimStart().startsWith(`/*! ${banner.content.trim()} */`) + ) { + code.prepend( + banner?.raw ? `${banner.content}\n` : `/*! ${banner.content} */\n`, + ); } - if (footer && !content.trimEnd().endsWith(footer.trim())) { - code.append(`\n${footer}\n`); + if ( + footer.content && + !content.trimEnd().endsWith(footer.content.trim()) && + !content.trimEnd().endsWith(`/*! ${footer.content.trim()} */`) + ) { + code.append( + footer?.raw ? `\n${footer.content}\n` : `\n/*! ${footer.content} */\n`, + ); } if (code.hasChanged()) { @@ -112,8 +124,8 @@ export async function processDtsFiles( bundle: boolean, dir: string, dtsExtension: string, - banner?: string, - footer?: string, + banner: BannerAndFooter, + footer: BannerAndFooter, ): Promise { if (bundle) { return; diff --git a/tests/integration/banner-footer/index.test.ts b/tests/integration/banner-footer/index.test.ts index 4c9f7cd07..8d9c3e541 100644 --- a/tests/integration/banner-footer/index.test.ts +++ b/tests/integration/banner-footer/index.test.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { buildAndGetResults } from 'test-helper'; import { expect, test } from 'vitest'; @@ -10,8 +11,7 @@ enum BannerFooter { DTS_FOOTER = '/*! hello footer dts */', } -test('banner and footer should work in js, css and dts', async () => { - const fixturePath = __dirname; +const testBannerAndFooter = async (fixturePath: string) => { const { js, css, dts } = await buildAndGetResults({ fixturePath, type: 'all', @@ -57,4 +57,16 @@ test('banner and footer should work in js, css and dts', async () => { checkBannerAndFooter(jsContents, 'js'); checkBannerAndFooter(cssContents, 'css'); checkBannerAndFooter(dtsContents, 'dts'); +}; + +test('banner and footer should work in js, css and dts', async () => { + const fixturePath = path.join(__dirname, 'raw'); + + testBannerAndFooter(fixturePath); +}); + +test('banner and footer should work in js, css, and dts with raw option set to false', async () => { + const fixturePath = path.join(__dirname, 'raw-false'); + + testBannerAndFooter(fixturePath); }); diff --git a/tests/integration/banner-footer/raw-false/package.json b/tests/integration/banner-footer/raw-false/package.json new file mode 100644 index 000000000..7a308fc59 --- /dev/null +++ b/tests/integration/banner-footer/raw-false/package.json @@ -0,0 +1,6 @@ +{ + "name": "banner-footer-raw-false-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/banner-footer/raw-false/rslib.config.ts b/tests/integration/banner-footer/raw-false/rslib.config.ts new file mode 100644 index 000000000..4f97652cc --- /dev/null +++ b/tests/integration/banner-footer/raw-false/rslib.config.ts @@ -0,0 +1,102 @@ +import { type LibConfig, defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +const bannerFooterConfig: LibConfig = { + banner: { + js: 'hello banner js', + css: 'hello banner css', + dts: 'hello banner dts', + raw: false, + }, + footer: { + js: 'hello footer js', + css: 'hello footer css', + dts: 'hello footer dts', + raw: false, + }, +}; + +export default defineConfig({ + lib: [ + // bundle esm + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/bundle', + }, + }, + dts: { + bundle: true, + }, + ...bannerFooterConfig, + }), + // bundle cjs + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundle', + }, + }, + dts: { + bundle: true, + }, + ...bannerFooterConfig, + }), + // bundleless esm + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/bundleless', + }, + }, + bundle: false, + dts: { + bundle: false, + }, + // TODO: bundleless css + source: { + entry: { + index: ['./src/**/*.ts'], + }, + }, + ...bannerFooterConfig, + }), + // bundleless cjs + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundleless', + }, + }, + bundle: false, + dts: { + bundle: false, + }, + // TODO: bundleless css + source: { + entry: { + index: ['./src/**/*.ts'], + }, + }, + ...bannerFooterConfig, + }), + // bundle esm with minify enabled + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/bundle-minify', + }, + minify: true, + }, + dts: { + bundle: true, + }, + ...bannerFooterConfig, + }), + ], + source: { + entry: { + index: './src/index.ts', + }, + }, +}); diff --git a/tests/integration/banner-footer/src/foo.ts b/tests/integration/banner-footer/raw-false/src/foo.ts similarity index 100% rename from tests/integration/banner-footer/src/foo.ts rename to tests/integration/banner-footer/raw-false/src/foo.ts diff --git a/tests/integration/banner-footer/src/index.css b/tests/integration/banner-footer/raw-false/src/index.css similarity index 100% rename from tests/integration/banner-footer/src/index.css rename to tests/integration/banner-footer/raw-false/src/index.css diff --git a/tests/integration/banner-footer/src/index.ts b/tests/integration/banner-footer/raw-false/src/index.ts similarity index 100% rename from tests/integration/banner-footer/src/index.ts rename to tests/integration/banner-footer/raw-false/src/index.ts diff --git a/tests/integration/banner-footer/tsconfig.json b/tests/integration/banner-footer/raw-false/tsconfig.json similarity index 100% rename from tests/integration/banner-footer/tsconfig.json rename to tests/integration/banner-footer/raw-false/tsconfig.json diff --git a/tests/integration/banner-footer/package.json b/tests/integration/banner-footer/raw/package.json similarity index 64% rename from tests/integration/banner-footer/package.json rename to tests/integration/banner-footer/raw/package.json index 315c5894a..57ef1fad5 100644 --- a/tests/integration/banner-footer/package.json +++ b/tests/integration/banner-footer/raw/package.json @@ -1,5 +1,5 @@ { - "name": "banner-footer-test", + "name": "banner-footer-raw-test", "version": "1.0.0", "private": true, "type": "module" diff --git a/tests/integration/banner-footer/rslib.config.ts b/tests/integration/banner-footer/raw/rslib.config.ts similarity index 100% rename from tests/integration/banner-footer/rslib.config.ts rename to tests/integration/banner-footer/raw/rslib.config.ts diff --git a/tests/integration/banner-footer/raw/src/foo.ts b/tests/integration/banner-footer/raw/src/foo.ts new file mode 100644 index 000000000..3329a7d97 --- /dev/null +++ b/tests/integration/banner-footer/raw/src/foo.ts @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/tests/integration/banner-footer/raw/src/index.css b/tests/integration/banner-footer/raw/src/index.css new file mode 100644 index 000000000..0916ff25a --- /dev/null +++ b/tests/integration/banner-footer/raw/src/index.css @@ -0,0 +1,3 @@ +.a { + color: black; +} diff --git a/tests/integration/banner-footer/raw/src/index.ts b/tests/integration/banner-footer/raw/src/index.ts new file mode 100644 index 000000000..212e13057 --- /dev/null +++ b/tests/integration/banner-footer/raw/src/index.ts @@ -0,0 +1,4 @@ +// import './index.css'; +import { foo } from './foo'; + +export const text = foo; diff --git a/tests/integration/banner-footer/raw/tsconfig.json b/tests/integration/banner-footer/raw/tsconfig.json new file mode 100644 index 000000000..888d3e460 --- /dev/null +++ b/tests/integration/banner-footer/raw/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["src"] +}