@@ -189,68 +189,70 @@ export class ModuleTSX extends EventTarget implements IModuleTSX {
189189 }
190190 }
191191
192+ private async resolveLocalUrl ( fullUrl : string ) : Promise < string > {
193+ const { pathname } = new URL ( fullUrl ) ;
194+ if ( pathname . endsWith ( ".module.css" ) ) {
195+ return this . transformSourceModule ( "css-module" , fullUrl , await this . fetchText ( fullUrl ) ) ;
196+ }
197+ if ( pathname . endsWith ( ".css" ) ) {
198+ return this . transformSourceModule ( "css" , fullUrl , await this . fetchText ( fullUrl ) ) ;
199+ }
200+ if ( pathname . endsWith ( ".wasm" ) ) {
201+ // wasm will be handled natively by the browser
202+ // so we just return the original full URL
203+ return fullUrl ;
204+ }
205+ // If this URL is already being transformed (circular import), return the raw URL.
206+ // The browser handles circular ESM natively; we just need to avoid deadlocking.
207+ if ( this . sourceTracker . isInFlight ( "esm" , fullUrl ) ) {
208+ return fullUrl ;
209+ }
210+ //! ^ transformSourceModule is recursive ^
211+ return this . transformSourceModule ( "esm" , fullUrl , await this . fetchText ( fullUrl ) ) ;
212+ }
213+
192214 private async resolveSpecifier ( specifier : string , sourceUrl : string ) : Promise < string > {
193215 const resolved = ImportMap . resolve ( specifier , this . importMap , sourceUrl ) ;
194216 if ( resolved ) {
195217 this . resolvedModuleSet . push ( resolved . record ) ;
218+ // CSS resolved via import map still needs to be injected as a <style> tag
219+ const { pathname } = new URL ( resolved . url ) ;
220+ if ( pathname . endsWith ( ".module.css" ) ) {
221+ return this . transformSourceModule ( "css-module" , resolved . url , await this . fetchText ( resolved . url ) ) ;
222+ }
223+ if ( pathname . endsWith ( ".css" ) ) {
224+ return this . transformSourceModule ( "css" , resolved . url , await this . fetchText ( resolved . url ) ) ;
225+ }
196226 return resolved . url ;
197227 }
198- const getCssUrl = async ( fullURL : string ) => {
199- // const code = this.cssStrategy === "link" ? "" : await this.fetchCode(url);
200- return await this . transformSourceModule ( "css" , fullURL , await this . fetchText ( fullURL ) ) ;
201- } ;
202- const toCDNUrl = ( specifier : string ) => {
203- // this avoid we accidentally convert a package named xxx.css to a css file on esm.sh
204- const subpath = specifier . startsWith ( "@" )
205- ? // @scope /pkg/subpath -> /subpath
206- specifier . split ( "/" ) . slice ( 2 ) . join ( "/" )
207- : // pkg/subpath -> /subpath
208- specifier . split ( "/" ) . slice ( 1 ) . join ( "/" ) ;
209228
210- const url = this . resolveBareSpecifier ( specifier ) ;
229+ if ( isRelativeSpecifier ( specifier ) ) {
230+ // local file, we fetch and transform it, then return the blob URL
231+ return this . resolveLocalUrl ( new URL ( specifier , sourceUrl ) . href ) ;
232+ }
233+
234+ if ( specifier . startsWith ( "node:" ) ) {
235+ return `https://raw.esm.sh/@jspm/core/nodelibs/browser/${ specifier . slice ( 5 ) } .js` ;
236+ }
237+
238+ const bareSpecifier = specifier . startsWith ( "npm:" ) ? specifier . slice ( 4 ) : specifier ;
239+ if ( specifier . startsWith ( "npm:" ) || isBareSpecifier ( specifier ) ) {
240+ // this avoid we accidentally convert a package named xxx.css to a css file on esm.sh
241+ const subpath = bareSpecifier . startsWith ( "@" )
242+ ? // @scope /pkg/subpath -> subpath
243+ bareSpecifier . split ( "/" ) . slice ( 2 ) . join ( "/" )
244+ : // pkg/subpath -> subpath
245+ bareSpecifier . split ( "/" ) . slice ( 1 ) . join ( "/" ) ;
246+ const url = this . resolveBareSpecifier ( bareSpecifier ) ;
211247 if ( subpath . endsWith ( ".css" ) ) {
212248 // if the subpath (not the package name) ends with .css, we treat it as a css file
213- return getCssUrl ( url ) ;
249+ return this . transformSourceModule ( "css" , url , await this . fetchText ( url ) ) ;
214250 }
215251 return url ;
216- } ;
217-
218- if ( isRelativeSpecifier ( specifier ) ) {
219- const targetUrl = new URL ( specifier , sourceUrl ) ;
220- // local file, we fetch and transform it, then return the blob URL
221- if ( targetUrl . pathname . endsWith ( ".module.css" ) ) {
222- const blobUrl = await this . transformSourceModule (
223- "css-module" ,
224- targetUrl . href ,
225- await this . fetchText ( targetUrl . href ) ,
226- ) ;
227- return blobUrl ;
228- } else if ( targetUrl . pathname . endsWith ( ".css" ) ) {
229- return getCssUrl ( targetUrl . href ) ;
230- } else if ( targetUrl . pathname . endsWith ( ".wasm" ) ) {
231- // wasm will be handled natively by the browser
232- // so we just return the original full URL
233- return targetUrl . href ;
234- } else {
235- // If this URL is already being transformed (circular import), return the raw URL.
236- // The browser handles circular ESM natively; we just need to avoid deadlocking.
237- if ( this . sourceTracker . isInFlight ( "esm" , targetUrl . href ) ) {
238- return targetUrl . href ;
239- }
240- const blobUrl = await this . transformSourceModule ( "esm" , targetUrl . href , await this . fetchText ( targetUrl . href ) ) ;
241- //! ^ transformSourceModule is recursive ^
242- return blobUrl ;
243- }
244- } else if ( specifier . startsWith ( "node:" ) ) {
245- return `https://raw.esm.sh/@jspm/core/nodelibs/browser/${ specifier . slice ( 5 ) } .js` ;
246- } else if ( specifier . startsWith ( "npm:" ) ) {
247- return toCDNUrl ( specifier . slice ( 4 ) ) ;
248- } else if ( isBareSpecifier ( specifier ) ) {
249- return toCDNUrl ( specifier ) ;
250- } else {
251- // Fallback: return the original specifier
252- return specifier ;
253252 }
253+
254+ // Fallback: return the original specifier
255+ return specifier ;
254256 }
255257
256258 private async resolveSpecifiers ( specifiers : Set < string > , sourceUrl : string ) : Promise < Map < string , string > > {
0 commit comments