@@ -2,9 +2,13 @@ import { readFile, writeFile } from 'node:fs/promises'
22import path from 'node:path'
33import { RE_DTS } from 'rolldown-plugin-dts/filename'
44import { detectIndentation } from '../../utils/format.ts'
5- import { slash } from '../../utils/general.ts'
5+ import { matchPattern , slash } from '../../utils/general.ts'
66import type { NormalizedFormat , ResolvedConfig } from '../../config/types.ts'
7- import type { ChunksByFormat , RolldownChunk } from '../../utils/chunks.ts'
7+ import type {
8+ ChunksByFormat ,
9+ RolldownChunk ,
10+ RolldownCodeChunk ,
11+ } from '../../utils/chunks.ts'
812import type { Awaitable } from '../../utils/types.ts'
913import type { PackageJson } from 'pkg-types'
1014
@@ -21,6 +25,18 @@ export interface ExportsOptions {
2125 */
2226 all ?: boolean
2327
28+ /**
29+ * Define filenames or RegExp patterns to exclude files from exports.
30+ * This is useful for excluding files that should not be part of the package exports,
31+ * such as bin files or internal utilities.
32+ *
33+ * @example
34+ * ```js
35+ * exclude: ['foo.ts', /\.spec\.ts$/, /internal/]
36+ * ```
37+ */
38+ exclude ?: ( RegExp | string ) [ ]
39+
2440 customExports ?: (
2541 exports : Record < string , any > ,
2642 context : {
@@ -65,10 +81,18 @@ export async function writeExports(
6581
6682type SubExport = Partial < Record < 'cjs' | 'es' | 'src' , string > >
6783
84+ function shouldExclude (
85+ fileName : string ,
86+ exclude ?: ( RegExp | string ) [ ] ,
87+ ) : boolean {
88+ if ( ! exclude ?. length ) return false
89+ return matchPattern ( fileName , exclude )
90+ }
91+
6892export async function generateExports (
6993 pkg : PackageJson ,
7094 chunks : ChunksByFormat ,
71- { devExports, all, customExports } : ExportsOptions ,
95+ { devExports, all, exclude , customExports } : ExportsOptions ,
7296) : Promise < {
7397 main : string | undefined
7498 module : string | undefined
@@ -90,16 +114,19 @@ export async function generateExports(
90114 ] [ ] ) {
91115 if ( format !== 'es' && format !== 'cjs' ) continue
92116
117+ // Filter out non-entry chunks and excluded files
118+ const filteredChunks = chunksByFormat . filter (
119+ ( chunk ) : chunk is RolldownCodeChunk =>
120+ chunk . type === 'chunk' &&
121+ chunk . isEntry &&
122+ ! shouldExclude ( chunk . fileName , exclude ) ,
123+ )
124+
93125 const onlyOneEntry =
94- chunksByFormat . filter (
95- ( chunk ) =>
96- chunk . type === 'chunk' &&
97- chunk . isEntry &&
98- ! RE_DTS . test ( chunk . fileName ) ,
99- ) . length === 1
100- for ( const chunk of chunksByFormat ) {
101- if ( chunk . type !== 'chunk' || ! chunk . isEntry ) continue
126+ filteredChunks . filter ( ( chunk ) => ! RE_DTS . test ( chunk . fileName ) ) . length ===
127+ 1
102128
129+ for ( const chunk of filteredChunks ) {
103130 const normalizedName = slash ( chunk . fileName )
104131 const ext = path . extname ( chunk . fileName )
105132 let name = normalizedName . slice ( 0 , - ext . length )
0 commit comments