@@ -7,6 +7,7 @@ import type {
7
7
BuildContext ,
8
8
BuildOptions ,
9
9
Loader ,
10
+ OnLoadArgs ,
10
11
OnLoadResult ,
11
12
Plugin ,
12
13
} from 'esbuild'
@@ -382,116 +383,127 @@ function esbuildScanPlugin(
382
383
}
383
384
} )
384
385
385
- // extract scripts inside HTML-like files and treat it as a js module
386
- build . onLoad (
387
- { filter : htmlTypesRE , namespace : 'html' } ,
388
- async ( { path } ) => {
389
- let raw = await fsp . readFile ( path , 'utf-8' )
390
- // Avoid matching the content of the comment
391
- raw = raw . replace ( commentRE , '<!---->' )
392
- const isHtml = path . endsWith ( '.html' )
393
- scriptRE . lastIndex = 0
394
- let js = ''
395
- let scriptId = 0
396
- let match : RegExpExecArray | null
397
- while ( ( match = scriptRE . exec ( raw ) ) ) {
398
- const [ , openTag , content ] = match
399
- const typeMatch = openTag . match ( typeRE )
400
- const type =
401
- typeMatch && ( typeMatch [ 1 ] || typeMatch [ 2 ] || typeMatch [ 3 ] )
402
- const langMatch = openTag . match ( langRE )
403
- const lang =
404
- langMatch && ( langMatch [ 1 ] || langMatch [ 2 ] || langMatch [ 3 ] )
405
- // skip non type module script
406
- if ( isHtml && type !== 'module' ) {
407
- continue
408
- }
409
- // skip type="application/ld+json" and other non-JS types
410
- if (
411
- type &&
412
- ! (
413
- type . includes ( 'javascript' ) ||
414
- type . includes ( 'ecmascript' ) ||
415
- type === 'module'
416
- )
417
- ) {
418
- continue
419
- }
420
- let loader : Loader = 'js'
421
- if ( lang === 'ts' || lang === 'tsx' || lang === 'jsx' ) {
422
- loader = lang
423
- } else if ( path . endsWith ( '.astro' ) ) {
424
- loader = 'ts'
425
- }
426
- const srcMatch = openTag . match ( srcRE )
427
- if ( srcMatch ) {
428
- const src = srcMatch [ 1 ] || srcMatch [ 2 ] || srcMatch [ 3 ]
429
- js += `import ${ JSON . stringify ( src ) } \n`
430
- } else if ( content . trim ( ) ) {
431
- // The reason why virtual modules are needed:
432
- // 1. There can be module scripts (`<script context="module">` in Svelte and `<script>` in Vue)
433
- // or local scripts (`<script>` in Svelte and `<script setup>` in Vue)
434
- // 2. There can be multiple module scripts in html
435
- // We need to handle these separately in case variable names are reused between them
436
-
437
- // append imports in TS to prevent esbuild from removing them
438
- // since they may be used in the template
439
- const contents =
440
- content +
441
- ( loader . startsWith ( 'ts' ) ? extractImportPaths ( content ) : '' )
442
-
443
- const key = `${ path } ?id=${ scriptId ++ } `
444
- if ( contents . includes ( 'import.meta.glob' ) ) {
445
- scripts [ key ] = {
446
- loader : 'js' , // since it is transpiled
447
- contents : await doTransformGlobImport ( contents , path , loader ) ,
448
- pluginData : {
449
- htmlType : { loader } ,
450
- } ,
451
- }
452
- } else {
453
- scripts [ key ] = {
454
- loader,
455
- contents,
456
- pluginData : {
457
- htmlType : { loader } ,
458
- } ,
459
- }
386
+ const htmlTypeOnLoadCallback : (
387
+ args : OnLoadArgs ,
388
+ ) => Promise < OnLoadResult | null | undefined > = async ( { path : p } ) => {
389
+ let raw = await fsp . readFile ( p , 'utf-8' )
390
+ // Avoid matching the content of the comment
391
+ raw = raw . replace ( commentRE , '<!---->' )
392
+ const isHtml = p . endsWith ( '.html' )
393
+ scriptRE . lastIndex = 0
394
+ let js = ''
395
+ let scriptId = 0
396
+ let match : RegExpExecArray | null
397
+ while ( ( match = scriptRE . exec ( raw ) ) ) {
398
+ const [ , openTag , content ] = match
399
+ const typeMatch = openTag . match ( typeRE )
400
+ const type =
401
+ typeMatch && ( typeMatch [ 1 ] || typeMatch [ 2 ] || typeMatch [ 3 ] )
402
+ const langMatch = openTag . match ( langRE )
403
+ const lang =
404
+ langMatch && ( langMatch [ 1 ] || langMatch [ 2 ] || langMatch [ 3 ] )
405
+ // skip non type module script
406
+ if ( isHtml && type !== 'module' ) {
407
+ continue
408
+ }
409
+ // skip type="application/ld+json" and other non-JS types
410
+ if (
411
+ type &&
412
+ ! (
413
+ type . includes ( 'javascript' ) ||
414
+ type . includes ( 'ecmascript' ) ||
415
+ type === 'module'
416
+ )
417
+ ) {
418
+ continue
419
+ }
420
+ let loader : Loader = 'js'
421
+ if ( lang === 'ts' || lang === 'tsx' || lang === 'jsx' ) {
422
+ loader = lang
423
+ } else if ( p . endsWith ( '.astro' ) ) {
424
+ loader = 'ts'
425
+ }
426
+ const srcMatch = openTag . match ( srcRE )
427
+ if ( srcMatch ) {
428
+ const src = srcMatch [ 1 ] || srcMatch [ 2 ] || srcMatch [ 3 ]
429
+ js += `import ${ JSON . stringify ( src ) } \n`
430
+ } else if ( content . trim ( ) ) {
431
+ // The reason why virtual modules are needed:
432
+ // 1. There can be module scripts (`<script context="module">` in Svelte and `<script>` in Vue)
433
+ // or local scripts (`<script>` in Svelte and `<script setup>` in Vue)
434
+ // 2. There can be multiple module scripts in html
435
+ // We need to handle these separately in case variable names are reused between them
436
+
437
+ // append imports in TS to prevent esbuild from removing them
438
+ // since they may be used in the template
439
+ const contents =
440
+ content +
441
+ ( loader . startsWith ( 'ts' ) ? extractImportPaths ( content ) : '' )
442
+
443
+ const key = `${ p } ?id=${ scriptId ++ } `
444
+ if ( contents . includes ( 'import.meta.glob' ) ) {
445
+ scripts [ key ] = {
446
+ loader : 'js' , // since it is transpiled
447
+ contents : await doTransformGlobImport ( contents , p , loader ) ,
448
+ resolveDir : normalizePath ( path . dirname ( p ) ) ,
449
+ pluginData : {
450
+ htmlType : { loader } ,
451
+ } ,
460
452
}
461
-
462
- const virtualModulePath = JSON . stringify (
463
- virtualModulePrefix + key ,
464
- )
465
-
466
- const contextMatch = openTag . match ( contextRE )
467
- const context =
468
- contextMatch &&
469
- ( contextMatch [ 1 ] || contextMatch [ 2 ] || contextMatch [ 3 ] )
470
-
471
- // Especially for Svelte files, exports in <script context="module"> means module exports,
472
- // exports in <script> means component props. To avoid having two same export name from the
473
- // star exports, we need to ignore exports in <script>
474
- if ( path . endsWith ( '.svelte' ) && context !== 'module' ) {
475
- js += `import ${ virtualModulePath } \n`
476
- } else {
477
- js += `export * from ${ virtualModulePath } \n`
453
+ } else {
454
+ scripts [ key ] = {
455
+ loader,
456
+ contents,
457
+ resolveDir : normalizePath ( path . dirname ( p ) ) ,
458
+ pluginData : {
459
+ htmlType : { loader } ,
460
+ } ,
478
461
}
479
462
}
480
- }
481
463
482
- // This will trigger incorrectly if `export default` is contained
483
- // anywhere in a string. Svelte and Astro files can't have
484
- // `export default` as code so we know if it's encountered it's a
485
- // false positive (e.g. contained in a string)
486
- if ( ! path . endsWith ( '.vue' ) || ! js . includes ( 'export default' ) ) {
487
- js += '\nexport default {}'
488
- }
464
+ const virtualModulePath = JSON . stringify ( virtualModulePrefix + key )
489
465
490
- return {
491
- loader : 'js' ,
492
- contents : js ,
466
+ const contextMatch = openTag . match ( contextRE )
467
+ const context =
468
+ contextMatch &&
469
+ ( contextMatch [ 1 ] || contextMatch [ 2 ] || contextMatch [ 3 ] )
470
+
471
+ // Especially for Svelte files, exports in <script context="module"> means module exports,
472
+ // exports in <script> means component props. To avoid having two same export name from the
473
+ // star exports, we need to ignore exports in <script>
474
+ if ( p . endsWith ( '.svelte' ) && context !== 'module' ) {
475
+ js += `import ${ virtualModulePath } \n`
476
+ } else {
477
+ js += `export * from ${ virtualModulePath } \n`
478
+ }
493
479
}
494
- } ,
480
+ }
481
+
482
+ // This will trigger incorrectly if `export default` is contained
483
+ // anywhere in a string. Svelte and Astro files can't have
484
+ // `export default` as code so we know if it's encountered it's a
485
+ // false positive (e.g. contained in a string)
486
+ if ( ! p . endsWith ( '.vue' ) || ! js . includes ( 'export default' ) ) {
487
+ js += '\nexport default {}'
488
+ }
489
+
490
+ return {
491
+ loader : 'js' ,
492
+ contents : js ,
493
+ }
494
+ }
495
+
496
+ // extract scripts inside HTML-like files and treat it as a js module
497
+ build . onLoad (
498
+ { filter : htmlTypesRE , namespace : 'html' } ,
499
+ htmlTypeOnLoadCallback ,
500
+ )
501
+ // the onResolve above will use namespace=html but esbuild doesn't
502
+ // call onResolve for glob imports and those will use namespace=file
503
+ // https://github.com/evanw/esbuild/issues/3317
504
+ build . onLoad (
505
+ { filter : htmlTypesRE , namespace : 'file' } ,
506
+ htmlTypeOnLoadCallback ,
495
507
)
496
508
497
509
// bare imports: record and externalize ----------------------------------
0 commit comments