@@ -564,6 +564,15 @@ export function createSentryBuildPluginManager(
564564 return ;
565565 }
566566
567+ // Early exit if assets is explicitly set to an empty array
568+ const assets = options . sourcemaps ?. assets ;
569+ if ( Array . isArray ( assets ) && assets . length === 0 ) {
570+ logger . debug (
571+ "Empty `sourcemaps.assets` option provided. Will not upload sourcemaps with debug ID."
572+ ) ;
573+ return ;
574+ }
575+
567576 await startSpan (
568577 // This is `forceTransaction`ed because this span is used in dashboards in the form of indexed transactions.
569578 { name : "debug-id-sourcemap-upload" , scope : sentryScope , forceTransaction : true } ,
@@ -578,65 +587,77 @@ export function createSentryBuildPluginManager(
578587 const freeUploadDependencyOnBuildArtifacts = createDependencyOnBuildArtifacts ( ) ;
579588
580589 try {
581- const assets = options . sourcemaps ?. assets ;
582-
583- let globAssets : string | string [ ] ;
584- if ( assets ) {
585- globAssets = assets ;
586- } else {
587- logger . debug (
588- "No `sourcemaps.assets` option provided, falling back to uploading detected build artifacts."
589- ) ;
590- globAssets = buildArtifactPaths ;
591- }
590+ if ( ! shouldPrepare ) {
591+ // Direct CLI upload from existing artifact paths (no globbing, no preparation)
592+ let pathsToUpload : string [ ] ;
592593
593- const globResult = await startSpan (
594- { name : "glob" , scope : sentryScope } ,
595- async ( ) =>
596- await glob ( globAssets , {
597- absolute : true ,
598- // If we do not use a temp folder, we allow directories and files; CLI will traverse as needed when given paths.
599- nodir : shouldPrepare ,
600- ignore : options . sourcemaps ?. ignore ,
601- } )
602- ) ;
594+ if ( assets ) {
595+ pathsToUpload = Array . isArray ( assets ) ? assets : [ assets ] ;
596+ logger . debug (
597+ `Direct upload mode: passing user-provided assets directly to CLI: ${ pathsToUpload . join (
598+ ", "
599+ ) } `
600+ ) ;
601+ } else {
602+ // Use original paths e.g. like ['.next/server'] directly –> preferred way when no globbing is done
603+ pathsToUpload = buildArtifactPaths ;
604+ }
603605
604- const debugIdChunkFilePaths = shouldPrepare
605- ? globResult . filter ( ( debugIdChunkFilePath ) => {
606- return ! ! stripQueryAndHashFromPath ( debugIdChunkFilePath ) . match ( / \. ( j s | m j s | c j s ) $ / ) ;
607- } )
608- : globResult ;
606+ const ignorePaths = options . sourcemaps ?. ignore
607+ ? Array . isArray ( options . sourcemaps ?. ignore )
608+ ? options . sourcemaps ?. ignore
609+ : [ options . sourcemaps ?. ignore ]
610+ : [ ] ;
611+ await startSpan ( { name : "upload" , scope : sentryScope } , async ( ) => {
612+ const cliInstance = createCliInstance ( options ) ;
613+ await cliInstance . releases . uploadSourceMaps ( options . release . name ?? "undefined" , {
614+ include : [
615+ {
616+ paths : pathsToUpload ,
617+ rewrite : true ,
618+ dist : options . release . dist ,
619+ } ,
620+ ] ,
621+ ignore : ignorePaths ,
622+ live : "rejectOnError" ,
623+ } ) ;
624+ } ) ;
609625
610- // The order of the files output by glob() is not deterministic
611- // Ensure order within the files so that {debug-id}-{chunkIndex} coupling is consistent
612- debugIdChunkFilePaths . sort ( ) ;
626+ logger . info ( "Successfully uploaded source maps to Sentry" ) ;
627+ } else {
628+ // Prepare artifacts in temp folder before uploading
629+ let globAssets : string | string [ ] ;
630+ if ( assets ) {
631+ globAssets = assets ;
632+ } else {
633+ logger . debug (
634+ "No `sourcemaps.assets` option provided, falling back to uploading detected build artifacts."
635+ ) ;
636+ globAssets = buildArtifactPaths ;
637+ }
613638
614- if ( Array . isArray ( assets ) && assets . length === 0 ) {
615- logger . debug (
616- "Empty `sourcemaps.assets` option provided. Will not upload sourcemaps with debug ID."
617- ) ;
618- } else if ( debugIdChunkFilePaths . length === 0 ) {
619- logger . warn (
620- "Didn't find any matching sources for debug ID upload. Please check the `sourcemaps.assets` option."
639+ const globResult = await startSpan (
640+ { name : "glob" , scope : sentryScope } ,
641+ async ( ) =>
642+ await glob ( globAssets , {
643+ absolute : true ,
644+ nodir : true , // We need individual files for preparation
645+ ignore : options . sourcemaps ?. ignore ,
646+ } )
621647 ) ;
622- } else {
623- if ( ! shouldPrepare ) {
624- // Direct CLI upload from existing artifact paths (no preparation or temp copies)
625- await startSpan ( { name : "upload" , scope : sentryScope } , async ( ) => {
626- const cliInstance = createCliInstance ( options ) ;
627- await cliInstance . releases . uploadSourceMaps ( options . release . name ?? "undefined" , {
628- include : [
629- {
630- paths : debugIdChunkFilePaths ,
631- rewrite : false ,
632- dist : options . release . dist ,
633- } ,
634- ] ,
635- live : "rejectOnError" ,
636- } ) ;
637- } ) ;
638648
639- logger . info ( "Successfully uploaded source maps to Sentry" ) ;
649+ const debugIdChunkFilePaths = globResult . filter ( ( debugIdChunkFilePath ) => {
650+ return ! ! stripQueryAndHashFromPath ( debugIdChunkFilePath ) . match ( / \. ( j s | m j s | c j s ) $ / ) ;
651+ } ) ;
652+
653+ // The order of the files output by glob() is not deterministic
654+ // Ensure order within the files so that {debug-id}-{chunkIndex} coupling is consistent
655+ debugIdChunkFilePaths . sort ( ) ;
656+
657+ if ( debugIdChunkFilePaths . length === 0 ) {
658+ logger . warn (
659+ "Didn't find any matching sources for debug ID upload. Please check the `sourcemaps.assets` option."
660+ ) ;
640661 } else {
641662 const tmpUploadFolder = await startSpan (
642663 { name : "mkdtemp" , scope : sentryScope } ,
0 commit comments