1
1
/* eslint-disable jest/no-export */
2
+ // eslint-disable @typescript-eslint/no-unsafe-call
2
3
import path from "path" ;
3
4
import fs from "fs-extra" ;
4
- import { InputOptions , OutputOptions } from "rollup " ;
5
+ import { InputOptions , OutputOptions , rolldown } from "rolldown " ;
5
6
6
7
import { Options } from "../../src/types" ;
7
8
8
- import { rolldown } from ' rolldown'
9
- import nodePolyfills from '@ rolldown/plugin-node-polyfills'
9
+ import nodePolyfills from "@ rolldown/plugin-node-polyfills" ;
10
+ import type { OutputChunk , OutputAsset } from " rolldown" ;
10
11
11
12
export interface WriteData {
12
13
input : string | string [ ] ;
@@ -17,77 +18,140 @@ export interface WriteData {
17
18
outputOpts ?: OutputOptions ;
18
19
}
19
20
20
- export interface WriteResult {
21
+ interface WriteResult {
21
22
js : ( ) => Promise < string [ ] > ;
22
23
css : ( ) => Promise < string [ ] > ;
23
- isCss : ( ) => Promise < boolean > ;
24
+ isCss : ( ) => Promise < boolean [ ] > ;
24
25
map : ( ) => Promise < string [ ] > ;
25
- isMap : ( ) => Promise < boolean > ;
26
+ isMap : ( ) => Promise < boolean [ ] > ;
26
27
isFile : ( file : string ) => Promise < boolean > ;
27
28
}
28
29
29
- async function pathExistsAll ( files : string [ ] ) : Promise < boolean > {
30
- if ( files . length === 0 ) return false ;
31
- for await ( const file of files ) {
32
- const exists = await fs . pathExists ( file ) ;
33
- if ( ! exists ) return false ;
34
- }
35
- return true ;
30
+ async function pathExistsAll ( paths : string [ ] ) : Promise < boolean [ ] > {
31
+ return Promise . all (
32
+ paths . map ( async p =>
33
+ fs
34
+ . access ( p )
35
+ . then ( ( ) => true )
36
+ . catch ( ( ) => false ) ,
37
+ ) ,
38
+ ) ;
39
+ }
40
+ // Process output files
41
+ function isChunk ( f : OutputChunk | OutputAsset ) : f is OutputChunk {
42
+ return f . type === "chunk" ;
36
43
}
37
44
38
- export const fixture = ( ...args : string [ ] ) : string =>
39
- path . normalize ( path . join ( __dirname , ".." , "fixtures" , ...args ) ) ;
45
+ function isCssAsset ( f : OutputAsset ) : boolean {
46
+ return f . fileName . endsWith ( ".css" ) ;
47
+ }
40
48
41
- export async function write ( data : WriteData ) : Promise < WriteResult > {
42
- const outDir = fixture ( "dist" , data . outDir ?? data . title ?? " ") ;
43
- const input = Array . isArray ( data . input ) ? data . input . map ( i => fixture ( i ) ) : fixture ( data . input ) ;
49
+ function isSourceMap ( f : OutputAsset ) : boolean {
50
+ return f . fileName . endsWith ( ".css.map ") ;
51
+ }
44
52
53
+ export async function write ( data : {
54
+ input : string | string [ ] ;
55
+ outDir ?: string ;
56
+ title ?: string ;
57
+ inputOpts ?: Partial < InputOptions > ;
58
+ outputOpts ?: Partial < OutputOptions > ;
59
+ } ) : Promise < WriteResult > {
60
+ const outDir = path . join ( "dist" , data . outDir ?? data . title ?? "" ) ;
61
+ const input = Array . isArray ( data . input )
62
+ ? data . input . map ( i => path . join ( i ) )
63
+ : path . join ( data . input ) ;
64
+
65
+ // Rolldown-specific configuration
45
66
const bundle = await rolldown ( {
46
- // ... other config
47
67
input,
68
+ platform : "node" ,
69
+ // Merged configuration
70
+ ...data . inputOpts ,
71
+ // Rolldown-native options
72
+ resolve : {
73
+ mainFields : [ "module" , "main" ] ,
74
+ extensions : [ ".mjs" , ".js" , ".ts" , ".json" , ".node" ] ,
75
+ } ,
48
76
plugins : [
49
- nodePolyfills ( {
50
- // Optional configuration
51
- include : [ 'fs' , 'path' ] , // Only polyfill specific modules
52
- exclude : [ 'crypto' ] , // Exclude modules from polyfilling
53
- } ) ,
54
- // ... other plugins
77
+ {
78
+ name : "json-handler" ,
79
+ transform ( code : string , id : string ) {
80
+ if ( id . endsWith ( ".json" ) ) {
81
+ return `export default ${ code } ` ;
82
+ }
83
+ } ,
84
+ } ,
85
+ {
86
+ name : "node-resolve" ,
87
+ resolveId ( source : string ) {
88
+ if ( source === "vue" || source . startsWith ( "@vue/" ) ) {
89
+ return { id : source , external : true } ;
90
+ }
91
+ } ,
92
+ } ,
93
+ {
94
+ name : "css-extract" ,
95
+ transform ( code : string , id : string ) {
96
+ if ( id . endsWith ( ".css" ) ) {
97
+ return `export default ${ JSON . stringify ( code ) } ` ;
98
+ }
99
+ } ,
100
+ } ,
101
+ nodePolyfills ( ) ,
55
102
] ,
56
- platform : 'node' // Important for Node.js polyfilling
57
- } )
58
- const { output } = await bundle . write ( {
59
- ...data . outputOpts ,
60
- dir : data . outputOpts ?. file ? undefined : outDir ,
61
- file : data . outputOpts ?. file && path . join ( outDir , data . outputOpts . file ) ,
103
+
104
+ treeshake : {
105
+ moduleSideEffects : ( id : string ) =>
106
+ id . includes ( ".css" ) || id . includes ( ".vue" ) || id . includes ( ".json" ) ,
107
+ } ,
62
108
} ) ;
63
109
64
- const js = output
65
- . filter ( f => f . type === "chunk" )
110
+ // Output handling
111
+ const outputConfig = {
112
+ dir : outDir ,
113
+ format : "es" as const ,
114
+ ...( data . outputOpts ?? { } ) ,
115
+ } ;
116
+
117
+ if ( data . outputOpts ?. file ) {
118
+ const { ...rest } = outputConfig ;
119
+ outputConfig . file = path . join ( outDir , data . outputOpts . file ) ;
120
+ Object . assign ( outputConfig , rest ) ;
121
+ }
122
+
123
+ const { output } = await bundle . write ( outputConfig ) ;
124
+
125
+ const jsFiles = output
126
+ . filter ( ( f ) : f is OutputChunk => isChunk ( f ) )
66
127
. map ( f => path . join ( outDir , f . fileName ) )
67
128
. sort ( ) ;
68
129
69
- const css = output
70
- . filter ( f => f . type === "asset" && f . fileName . endsWith ( ".css" ) )
130
+ const cssFiles = output
131
+ . filter ( ( f ) : f is OutputAsset => ! isChunk ( f ) && isCssAsset ( f ) )
71
132
. map ( f => path . join ( outDir , f . fileName ) )
72
133
. sort ( ) ;
73
134
74
- const map = output
75
- . filter ( f => f . type === "asset" && f . fileName . endsWith ( ".css.map" ) )
135
+ const mapFiles = output
136
+ . filter ( ( f ) : f is OutputAsset => ! isChunk ( f ) && isSourceMap ( f ) )
76
137
. map ( f => path . join ( outDir , f . fileName ) )
77
138
. sort ( ) ;
78
139
79
- const res : WriteResult = {
80
- js : async ( ) => Promise . all ( js . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
81
- css : async ( ) => Promise . all ( css . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
82
- isCss : async ( ) => pathExistsAll ( css ) ,
83
- map : async ( ) => Promise . all ( map . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
84
- isMap : async ( ) => pathExistsAll ( map ) ,
85
- isFile : async file => fs . pathExists ( path . join ( outDir , file ) ) ,
140
+ return {
141
+ js : async ( ) => Promise . all ( jsFiles . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
142
+ css : async ( ) => Promise . all ( cssFiles . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
143
+ isCss : async ( ) => pathExistsAll ( cssFiles ) ,
144
+ map : async ( ) => Promise . all ( mapFiles . map ( async f => fs . readFile ( f , "utf8" ) ) ) ,
145
+ isMap : async ( ) => pathExistsAll ( mapFiles ) ,
146
+ isFile : async ( file : string ) =>
147
+ fs
148
+ . access ( path . join ( outDir , file ) )
149
+ . then ( ( ) => true )
150
+ . catch ( ( ) => false ) ,
86
151
} ;
87
-
88
- return res ;
89
152
}
90
153
154
+ /////
91
155
export interface TestData extends WriteData {
92
156
title : string ;
93
157
files ?: string [ ] ;
@@ -99,11 +163,25 @@ export function validate(data: TestData): void {
99
163
test ( data . title , async ( ) => {
100
164
if ( data . shouldFail ) {
101
165
// eslint-disable-next-line jest/no-conditional-expect -- helper utility
102
- await expect ( write ( data ) ) . rejects . toThrowErrorMatchingSnapshot ( ) ;
166
+ await expect (
167
+ write ( {
168
+ input : data . input ,
169
+ outDir : data . outDir ,
170
+ title : data . title ,
171
+ inputOpts : data . inputOpts as Partial < InputOptions > ,
172
+ outputOpts : data . outputOpts as Partial < OutputOptions > ,
173
+ } ) ,
174
+ ) . rejects . toThrowErrorMatchingSnapshot ( ) ;
103
175
return ;
104
176
}
105
177
106
- const res = await write ( data ) ;
178
+ const res = await write ( {
179
+ input : data . input ,
180
+ outDir : data . outDir ,
181
+ title : data . title ,
182
+ inputOpts : data . inputOpts as Partial < InputOptions > ,
183
+ outputOpts : data . outputOpts as Partial < OutputOptions > ,
184
+ } ) ;
107
185
108
186
for ( const f of await res . js ( ) ) expect ( f ) . toMatchSnapshot ( "js" ) ;
109
187
0 commit comments