@@ -156,31 +156,6 @@ async function handleFastNpmMeta(
156156 }
157157}
158158
159- /**
160- * Handle esm.sh requests for the standard $fetch (returns data directly).
161- */
162- async function handleEsmShRequest (
163- url : string ,
164- _options : unknown ,
165- storage : ReturnType < typeof useStorage > ,
166- ) : Promise < unknown | null > {
167- const urlObj = new URL ( url )
168- if ( urlObj . hostname !== 'esm.sh' ) return null
169-
170- // For GET requests to .d.ts files, return the file content
171- const typesPath = urlObj . pathname . slice ( 1 ) // Remove leading /
172- if ( typesPath . endsWith ( '.d.ts' ) ) {
173- const fixturePath = `${ FIXTURE_PATHS . esmTypes } :${ typesPath . replace ( / \/ / g, ':' ) } `
174- const content = await storage . getItem < string > ( fixturePath )
175- if ( content ) {
176- if ( VERBOSE ) process . stdout . write ( `[test-fixtures] esm.sh types: ${ typesPath } \n` )
177- return content
178- }
179- }
180-
181- return null
182- }
183-
184159/**
185160 * Handle esm.sh requests for $fetch.raw (returns response object with headers).
186161 */
@@ -348,80 +323,6 @@ export default defineNitroPlugin(nitroApp => {
348323 process . stdout . write ( '[test-fixtures] Test mode active (verbose logging enabled)\n' )
349324 }
350325
351- // Override native fetch globally to intercept esm.sh requests from ofetch
352- // This is needed because server/utils/docs/client.ts imports $fetch from ofetch
353- // directly, bypassing our globalThis.$fetch override
354- const originalNativeFetch = globalThis . fetch
355- globalThis . fetch = async ( input : RequestInfo | URL , init ?: RequestInit ) => {
356- const url =
357- typeof input === 'string' ? input : input instanceof URL ? input . toString ( ) : input . url
358- if ( url . includes ( 'esm.sh' ) ) {
359- const method = init ?. method || 'GET'
360-
361- // HEAD request - return headers with x-typescript-types
362- if ( method === 'HEAD' ) {
363- const urlObj = new URL ( url )
364- const pathname = urlObj . pathname . slice ( 1 )
365- let pkgVersion = pathname
366- const slashIndex = pkgVersion . indexOf (
367- '/' ,
368- pkgVersion . includes ( '@' ) ? pkgVersion . lastIndexOf ( '@' ) + 1 : 0 ,
369- )
370- if ( slashIndex !== - 1 ) {
371- pkgVersion = pkgVersion . slice ( 0 , slashIndex )
372- }
373-
374- const fixturePath = `${ FIXTURE_PATHS . esmHeaders } :${ pkgVersion . replace ( / \/ / g, ':' ) } .json`
375- const headerData = await storage . getItem < { 'x-typescript-types' : string } > ( fixturePath )
376-
377- if ( headerData ) {
378- if ( VERBOSE ) process . stdout . write ( `[test-fixtures] fetch HEAD esm.sh: ${ pkgVersion } \n` )
379- return new Response ( null , {
380- status : 200 ,
381- headers : new Headers ( {
382- 'x-typescript-types' : headerData [ 'x-typescript-types' ] ,
383- 'content-type' : 'application/javascript' ,
384- } ) ,
385- } )
386- }
387-
388- // No fixture - return response without types header
389- if ( VERBOSE )
390- process . stdout . write ( `[test-fixtures] fetch HEAD esm.sh (no fixture): ${ pkgVersion } \n` )
391- return new Response ( null , {
392- status : 200 ,
393- headers : new Headers ( { 'content-type' : 'application/javascript' } ) ,
394- } )
395- }
396-
397- // GET request for .d.ts files
398- if ( method === 'GET' ) {
399- const urlObj = new URL ( url )
400- const pathname = urlObj . pathname . slice ( 1 )
401- const fixturePath = `${ FIXTURE_PATHS . esmTypes } :${ pathname . replace ( / \/ / g, ':' ) } `
402- const content = await storage . getItem < string > ( fixturePath )
403-
404- if ( content ) {
405- if ( VERBOSE ) process . stdout . write ( `[test-fixtures] fetch GET esm.sh: ${ pathname } \n` )
406- return new Response ( content , {
407- status : 200 ,
408- headers : new Headers ( { 'content-type' : 'application/typescript' } ) ,
409- } )
410- }
411-
412- // No fixture - return 404 so the loader skips this file
413- if ( VERBOSE )
414- process . stdout . write ( `[test-fixtures] fetch GET esm.sh (no fixture): ${ pathname } \n` )
415- return new Response ( 'Not Found' , {
416- status : 404 ,
417- headers : new Headers ( { 'content-type' : 'text/plain' } ) ,
418- } )
419- }
420- }
421-
422- return originalNativeFetch ( input , init )
423- }
424-
425326 nitroApp . hooks . hook ( 'request' , event => {
426327 event . context . cachedFetch = async < T = unknown > (
427328 url : string ,
@@ -488,26 +389,86 @@ export default defineNitroPlugin(nitroApp => {
488389 return { data, isStale : false , cachedAt : Date . now ( ) }
489390 }
490391
491- const originalFetch = globalThis . $fetch
392+ const original$Fetch = globalThis . $fetch
393+ const originalFetch = globalThis . fetch
492394
493- // @ts -expect-error invalid global augmentation
494- globalThis . $fetch = async ( url , options ) => {
495- if ( typeof url === 'string' && url . startsWith ( '/' ) ) {
496- return originalFetch ( url , options )
497- }
395+ globalThis . fetch = async ( url : URL | RequestInfo , init ?: RequestInit ) : Promise < Response > => {
396+ const urlStr = typeof url === 'string' ? url : url instanceof URL ? url . toString ( ) : url . url
498397
499398 // Handle esm.sh requests specially (used by docs feature)
500- if ( typeof url === 'string' && url . includes ( 'esm.sh' ) ) {
501- const esmResult = await handleEsmShRequest ( url , options , storage )
502- if ( esmResult !== null ) {
503- return esmResult
399+ if ( urlStr . startsWith ( 'https://esm.sh/' ) ) {
400+ const method = init ?. method ?. toUpperCase ( ) || 'GET'
401+ const urlObj = new URL ( urlStr )
402+ const pathname = urlObj . pathname . slice ( 1 ) // Remove leading /
403+
404+ // HEAD request - return headers with x-typescript-types if fixture exists
405+ if ( method === 'HEAD' ) {
406+ // Extract package@version from pathname
407+ let pkgVersion = pathname
408+ const slashIndex = pkgVersion . indexOf (
409+ '/' ,
410+ pkgVersion . includes ( '@' ) ? pkgVersion . lastIndexOf ( '@' ) + 1 : 0 ,
411+ )
412+ if ( slashIndex !== - 1 ) {
413+ pkgVersion = pkgVersion . slice ( 0 , slashIndex )
414+ }
415+
416+ const fixturePath = `${ FIXTURE_PATHS . esmHeaders } :${ pkgVersion . replace ( / \/ / g, ':' ) } .json`
417+ const headerData = await storage . getItem < { 'x-typescript-types' : string } > ( fixturePath )
418+
419+ if ( headerData ) {
420+ if ( VERBOSE ) process . stdout . write ( `[test-fixtures] fetch HEAD esm.sh: ${ pkgVersion } \n` )
421+ return new Response ( null , {
422+ status : 200 ,
423+ headers : {
424+ 'x-typescript-types' : headerData [ 'x-typescript-types' ] ,
425+ 'content-type' : 'application/javascript' ,
426+ } ,
427+ } )
428+ }
429+
430+ // No fixture - return 200 without x-typescript-types header (types not available)
431+ if ( VERBOSE )
432+ process . stdout . write ( `[test-fixtures] fetch HEAD esm.sh (no fixture): ${ pkgVersion } \n` )
433+ return new Response ( null , {
434+ status : 200 ,
435+ headers : { 'content-type' : 'application/javascript' } ,
436+ } )
504437 }
505- // If no fixture found, throw an error in test mode
506- throw createError ( {
507- statusCode : 404 ,
508- statusMessage : 'No esm.sh fixture available' ,
509- message : `No fixture for esm.sh URL: ${ url } ` ,
510- } )
438+
439+ // GET request - return .d.ts content if fixture exists
440+ if ( method === 'GET' && pathname . endsWith ( '.d.ts' ) ) {
441+ const fixturePath = `${ FIXTURE_PATHS . esmTypes } :${ pathname . replace ( / \/ / g, ':' ) } `
442+ const content = await storage . getItem < string > ( fixturePath )
443+
444+ if ( content ) {
445+ if ( VERBOSE ) process . stdout . write ( `[test-fixtures] fetch GET esm.sh: ${ pathname } \n` )
446+ return new Response ( content , {
447+ status : 200 ,
448+ headers : { 'content-type' : 'application/typescript' } ,
449+ } )
450+ }
451+
452+ // No fixture - return 404 for missing .d.ts files
453+ if ( VERBOSE )
454+ process . stdout . write ( `[test-fixtures] fetch GET esm.sh (no fixture): ${ pathname } \n` )
455+ return new Response ( 'Not Found' , {
456+ status : 404 ,
457+ headers : { 'content-type' : 'text/plain' } ,
458+ } )
459+ }
460+
461+ // Other esm.sh requests - return empty response
462+ return new Response ( null , { status : 200 } )
463+ }
464+
465+ return originalFetch ( url , init )
466+ }
467+
468+ // @ts -expect-error invalid global augmentation
469+ globalThis . $fetch = async ( url , options ) => {
470+ if ( typeof url === 'string' && url . startsWith ( '/' ) ) {
471+ return original$Fetch ( url , options )
511472 }
512473
513474 const { data } = await event . context . cachedFetch ! < any > ( url as string , options )
0 commit comments