Skip to content

Commit 54e21e1

Browse files
gwansikksxzz
andauthored
feat: add banner & footer options (#338)
Co-authored-by: 三咲智子 Kevin Deng <[email protected]>
1 parent 2ee682f commit 54e21e1

File tree

8 files changed

+108
-12
lines changed

8 files changed

+108
-12
lines changed

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ catalogs:
33
'@arethetypeswrong/core': ^0.18.2
44
'@sxzz/eslint-config': ^7.0.6
55
'@sxzz/prettier-config': ^2.2.3
6-
'@sxzz/test-utils': ^0.5.6
6+
'@sxzz/test-utils': ^0.5.7
77
'@types/debug': ^4.1.12
88
'@types/node': ^24.0.14
99
'@types/semver': ^7.7.0

src/features/output.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import { RE_CSS, RE_DTS, RE_JS } from 'rolldown-plugin-dts/filename'
12
import { getPackageType, type PackageType } from '../utils/package'
2-
import type { NormalizedFormat, ResolvedOptions } from '../options'
3-
import type { InputOptions, PreRenderedChunk } from 'rolldown'
3+
import type { Format, NormalizedFormat, ResolvedOptions } from '../options'
4+
import type {
5+
AddonFunction,
6+
InputOptions,
7+
PreRenderedChunk,
8+
RenderedChunk,
9+
} from 'rolldown'
410

511
export interface OutExtensionContext {
612
options: InputOptions
@@ -76,3 +82,37 @@ function createChunkFilename(
7682
return `${basename}${chunk.name.endsWith('.d') ? dtsExtension : jsExtension}`
7783
}
7884
}
85+
86+
export interface ChunkAddonObject {
87+
js?: string
88+
css?: string
89+
dts?: string
90+
}
91+
export type ChunkAddonFunction = (ctx: {
92+
format: Format
93+
}) => ChunkAddonObject | undefined
94+
export type ChunkAddon = ChunkAddonObject | ChunkAddonFunction
95+
96+
export function resolveChunkAddon(
97+
chunkAddon: ChunkAddon | undefined,
98+
format: NormalizedFormat,
99+
): AddonFunction | undefined {
100+
if (!chunkAddon) return
101+
102+
return (chunk: RenderedChunk) => {
103+
if (typeof chunkAddon === 'function') {
104+
chunkAddon = chunkAddon({ format })
105+
}
106+
107+
switch (true) {
108+
case RE_JS.test(chunk.fileName):
109+
return chunkAddon?.js || ''
110+
case RE_CSS.test(chunk.fileName):
111+
return chunkAddon?.css || ''
112+
case RE_DTS.test(chunk.fileName):
113+
return chunkAddon?.dts || ''
114+
default:
115+
return ''
116+
}
117+
}
118+
}

src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { ExternalPlugin } from './features/external'
1919
import { createHooks } from './features/hooks'
2020
import { LightningCSSPlugin } from './features/lightningcss'
2121
import { NodeProtocolPlugin } from './features/node-protocol'
22-
import { resolveChunkFilename } from './features/output'
22+
import { resolveChunkAddon, resolveChunkFilename } from './features/output'
2323
import { publint } from './features/publint'
2424
import { ReportPlugin } from './features/report'
2525
import { getShimsInject } from './features/shims'
@@ -215,6 +215,8 @@ async function getBuildOptions(
215215
loader,
216216
name,
217217
unbundle,
218+
banner,
219+
footer,
218220
} = config
219221

220222
const plugins: RolldownPluginOption = []
@@ -312,6 +314,8 @@ async function getBuildOptions(
312314
preserveModulesRoot: unbundle
313315
? lowestCommonAncestor(...Object.values(entry))
314316
: undefined,
317+
banner: resolveChunkAddon(banner, format),
318+
footer: resolveChunkAddon(footer, format),
315319
},
316320
config.outputOptions,
317321
[format],

src/options/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { AttwOptions } from '../features/attw'
22
import type { CopyOptions, CopyOptionsFn } from '../features/copy'
33
import type { ExportsOptions } from '../features/exports'
44
import type { TsdownHooks } from '../features/hooks'
5-
import type { OutExtensionFactory } from '../features/output'
5+
import type { ChunkAddon, OutExtensionFactory } from '../features/output'
66
import type { ReportOptions } from '../features/report'
77
import type {
88
Arrayable,
@@ -359,6 +359,8 @@ export interface Options {
359359
* Filter workspace packages. This option is only available in workspace mode.
360360
*/
361361
filter?: RegExp | string | string[]
362+
footer?: ChunkAddon
363+
banner?: ChunkAddon
362364
}
363365

364366
/**
@@ -389,6 +391,8 @@ export type ResolvedOptions = Omit<
389391
| 'loader'
390392
| 'name'
391393
| 'bundle'
394+
| 'banner'
395+
| 'footer'
392396
>,
393397
{
394398
format: NormalizedFormat[]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## index.d.ts
2+
3+
```ts
4+
export { };
5+
```
6+
## index.js
7+
8+
```js
9+
// js banner
10+
//#region index.ts
11+
console.log("Hello, world!");
12+
13+
//#endregion
14+
// js footer
15+
```

tests/index.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,30 @@ test('workspace option', async (context) => {
408408
expectPattern: '**/dist',
409409
})
410410
})
411+
412+
test('banner and footer option', async (context) => {
413+
const content = `console.log("Hello, world!")`
414+
const { fileMap } = await testBuild({
415+
context,
416+
files: {
417+
'index.ts': content,
418+
},
419+
options: {
420+
dts: true,
421+
banner: {
422+
js: '// js banner',
423+
dts: '// dts banner',
424+
},
425+
footer: {
426+
js: '// js footer',
427+
dts: '// dts footer',
428+
},
429+
},
430+
})
431+
432+
expect(fileMap['index.js']).toContain('// js banner')
433+
expect(fileMap['index.js']).toContain('// js footer')
434+
435+
// expect(fileMap['index.d.ts']).toContain('// dts banner')
436+
// expect(fileMap['index.d.ts']).toContain('// dts footer')
437+
})

tests/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export async function testBuild({
122122
outputFiles: string[]
123123
outputDir: string
124124
snapshot: string
125+
fileMap: Record<string, string>
125126
}> {
126127
const { expect } = context
127128
const { testName, testDir } = await writeFixtures(context, files, fixture)
@@ -141,7 +142,11 @@ export async function testBuild({
141142
restoreCwd()
142143

143144
const outputDir = path.resolve(workingDir, resolvedOptions.outDir!)
144-
const { files: outputFiles, snapshot } = await expectFilesSnapshot(
145+
const {
146+
files: outputFiles,
147+
snapshot,
148+
fileMap,
149+
} = await expectFilesSnapshot(
145150
path.resolve(outputDir, expectDir),
146151
path.resolve(snapshotsDir, `${testName}.snap.md`),
147152
{ pattern: expectPattern, expect },
@@ -153,6 +158,7 @@ export async function testBuild({
153158
outputFiles,
154159
outputDir,
155160
snapshot,
161+
fileMap,
156162
}
157163
}
158164

0 commit comments

Comments
 (0)