@@ -103,53 +103,109 @@ export function show_output(cwd, options = {}) {
103103
104104const svelte_path = fileURLToPath ( new URL ( '..' , import . meta. url ) ) . replace ( / \\ / g, '/' ) ;
105105
106+ const AsyncFunction = /** @type {typeof Function } */ ( async function ( ) { } . constructor ) ;
107+
106108export function create_loader ( compileOptions , cwd ) {
107109 const cache = new Map ( ) ;
108110
109111 async function load ( file ) {
110112 if ( cache . has ( file ) ) return cache . get ( file ) ;
111113
112114 if ( file . endsWith ( '.svelte' ) ) {
115+ const options = {
116+ ...compileOptions ,
117+ filename : file
118+ } ;
119+
113120 const compiled = compile (
114121 // Windows/Linux newline conversion
115122 fs . readFileSync ( file , 'utf-8' ) . replace ( / \r \n / g, '\n' ) ,
116- {
117- ...compileOptions ,
118- filename : file
119- }
123+ options
120124 ) ;
121125
122- const imports = new Map ( ) ;
126+ const __import = ( id ) => {
127+ let resolved = id ;
123128
124- for ( const match of compiled . js . code . matchAll ( / r e q u i r e \( " ( .+ ?) " \) / g) ) {
125- const source = match [ 1 ] ;
126- let resolved = source ;
127-
128- if ( source . startsWith ( '.' ) ) {
129- resolved = path . resolve ( path . dirname ( file ) , source ) ;
129+ if ( id . startsWith ( '.' ) ) {
130+ resolved = path . resolve ( path . dirname ( file ) , id ) ;
130131 }
131132
132- if ( source === 'svelte' ) {
133+ if ( id === 'svelte' ) {
133134 resolved = `${ svelte_path } src/runtime/index.js` ;
134135 }
135136
136- if ( source . startsWith ( 'svelte/' ) ) {
137- resolved = `${ svelte_path } src/runtime/${ source . slice ( 7 ) } /index.js` ;
137+ if ( id . startsWith ( 'svelte/' ) ) {
138+ resolved = `${ svelte_path } src/runtime/${ id . slice ( 7 ) } /index.js` ;
138139 }
139140
140- imports . set ( source , await load ( resolved ) ) ;
141- }
142-
143- function require ( id ) {
144- return imports . get ( id ) ;
141+ return load ( resolved ) ;
142+ } ;
143+
144+ const exports = [ ] ;
145+
146+ // We can't use Node's or Vitest's loaders cause we compile with different options.
147+ // We need to rewrite the imports into function calls that we can intercept to transform
148+ // any imported Svelte components as well. A few edge cases aren't handled but also
149+ // currently unused in the tests, for example `export * from`and live bindings.
150+ let transformed = compiled . js . code
151+ . replace (
152+ / ^ i m p o r t \* a s ( \w + ) f r o m [ ' " ] ( [ ^ ' " ] + ) [ ' " ] ; ? / gm,
153+ 'const $1 = await __import("$2");'
154+ )
155+ . replace (
156+ / ^ i m p o r t ( \w + ) f r o m [ ' " ] ( [ ^ ' " ] + ) [ ' " ] ; ? / gm,
157+ 'const {default: $1} = await __import("$2");'
158+ )
159+ . replace (
160+ / ^ i m p o r t ( \w + , ) ? { ( [ ^ } ] + ) } f r o m [ ' " ] ( .+ ) [ ' " ] ; ? / gm,
161+ ( _ , default_ , names , source ) => {
162+ const d = default_ ? `default: ${ default_ } ` : '' ;
163+ return `const { ${ d } ${ names . replaceAll (
164+ ' as ' ,
165+ ': '
166+ ) } } = await __import("${ source } ");`;
167+ }
168+ )
169+ . replace ( / ^ e x p o r t d e f a u l t / gm, '__exports.default = ' )
170+ . replace (
171+ / ^ e x p o r t ( c o n s t | l e t | v a r | c l a s s | f u n c t i o n | a s y n c \s + f u n c t i o n ) ( \w + ) / gm,
172+ ( _ , type , name ) => {
173+ exports . push ( name ) ;
174+ return `${ type } ${ name } ` ;
175+ }
176+ )
177+ . replace ( / ^ e x p o r t \{ ( [ ^ } ] + ) \} (?: f r o m [ ' " ] ( [ ^ ' " ] + ) [ ' " ] ; ? ) ? / gm, ( _ , names , source ) => {
178+ const entries = names . split ( ',' ) . map ( ( name ) => {
179+ const match = name . trim ( ) . match ( / ^ ( \w + ) ( a s ( \w + ) ) ? $ / ) ;
180+ const i = match [ 1 ] ;
181+ const o = match [ 3 ] || i ;
182+
183+ return [ o , i ] ;
184+ } ) ;
185+ return source
186+ ? `{ const __mod = await __import("${ source } "); ${ entries
187+ . map ( ( [ o , i ] ) => `__exports.${ o } = __mod.${ i } ;` )
188+ . join ( '\n' ) } }`
189+ : `{ ${ entries . map ( ( [ o , i ] ) => `__exports.${ o } = ${ i } ;` ) . join ( '\n' ) } }` ;
190+ } ) ;
191+
192+ exports . forEach ( ( name ) => {
193+ transformed += `\n__exports.${ name } = ${ name } ;` ;
194+ } ) ;
195+
196+ const __exports = {
197+ [ Symbol . toStringTag ] : 'Module'
198+ } ;
199+ try {
200+ const fn = new AsyncFunction ( '__import' , '__exports' , transformed ) ;
201+ await fn ( __import , __exports ) ;
202+ } catch ( err ) {
203+ console . error ( { transformed } ) ; // eslint-disable-line no-console
204+ throw err ;
145205 }
146206
147- const fn = new Function ( 'require' , 'exports' , 'module' , compiled . js . code ) ;
148- const module = { exports : { } } ;
149- fn ( require , module . exports , module ) ;
150-
151- cache . set ( file , module . exports ) ;
152- return module . exports ;
207+ cache . set ( file , __exports ) ;
208+ return __exports ;
153209 } else {
154210 return import ( file ) ;
155211 }
0 commit comments