@@ -4,9 +4,10 @@ import { tmpdir } from "node:os";
44import { join , resolve } from "node:path" ;
55import { rspack } from "@rspack/core" ;
66import { build as esbuildBuild } from "esbuild" ;
7+ import type { NormalizedOutputOptions , OutputBundle } from "rollup" ;
78import { rollup as createRollupBundle } from "rollup" ;
89import { build as viteBuild } from "vite" ;
9- import { afterEach , beforeEach , describe , expect , it } from "vitest" ;
10+ import { afterEach , beforeEach , describe , expect , it , vi } from "vitest" ;
1011import webpack from "webpack" ;
1112import bunPlugin from "../src/bun" ;
1213import esbuildPlugin from "../src/esbuild" ;
@@ -273,6 +274,166 @@ describe("sourcemaps bundler plugin", () => {
273274 await rm ( cwd , { recursive : true , force : true } ) ;
274275 } ) ;
275276
277+ it ( "batches uploads when payload exceeds maxUploadBodyBytes" , async ( ) => {
278+ const postedPayloads : UploadPayload [ ] = [ ] ;
279+ const fetchImpl = ( async (
280+ _input : Parameters < typeof fetch > [ 0 ] ,
281+ init ?: Parameters < typeof fetch > [ 1 ] ,
282+ ) => {
283+ const body = init ?. body ;
284+ if ( ! body || typeof body !== "string" ) {
285+ throw new Error ( "Expected JSON string body" ) ;
286+ }
287+ postedPayloads . push ( JSON . parse ( body ) as UploadPayload ) ;
288+ return new Response ( JSON . stringify ( { ok : true } ) , { status : 200 } ) ;
289+ } ) as typeof fetch ;
290+ const plugin = firstPlugin (
291+ rollupPlugin ( {
292+ endpoint,
293+ buildId : "batch-build-id" ,
294+ maxUploadBodyBytes : 600 ,
295+ fetchImpl,
296+ } ) ,
297+ ) as {
298+ writeBundle ?: (
299+ outputOptions : NormalizedOutputOptions ,
300+ bundle : OutputBundle ,
301+ ) => Promise < void > ;
302+ } ;
303+
304+ if ( ! plugin . writeBundle ) {
305+ throw new Error ( "Expected rollup writeBundle hook" ) ;
306+ }
307+
308+ const bundle = {
309+ "first.js.map" : {
310+ type : "asset" ,
311+ fileName : "first.js.map" ,
312+ source : JSON . stringify ( { version : 3 , mappings : "AAAA" . repeat ( 35 ) } ) ,
313+ } ,
314+ "second.js.map" : {
315+ type : "asset" ,
316+ fileName : "second.js.map" ,
317+ source : JSON . stringify ( { version : 3 , mappings : "BBBB" . repeat ( 35 ) } ) ,
318+ } ,
319+ "third.js.map" : {
320+ type : "asset" ,
321+ fileName : "third.js.map" ,
322+ source : JSON . stringify ( { version : 3 , mappings : "CCCC" . repeat ( 35 ) } ) ,
323+ } ,
324+ } as unknown as OutputBundle ;
325+
326+ await plugin . writeBundle (
327+ { dir : process . cwd ( ) } as NormalizedOutputOptions ,
328+ bundle ,
329+ ) ;
330+
331+ expect ( postedPayloads . length ) . toBeGreaterThan ( 1 ) ;
332+ expect (
333+ postedPayloads . every ( ( payload ) => payload . buildId === "batch-build-id" ) ,
334+ ) . toBe ( true ) ;
335+ expect (
336+ postedPayloads . every ( ( payload ) => payload . bundler === "rollup" ) ,
337+ ) . toBe ( true ) ;
338+ expect (
339+ postedPayloads . every ( ( payload ) => payload . sourcemaps . length > 0 ) ,
340+ ) . toBe ( true ) ;
341+ expect (
342+ postedPayloads . flatMap ( ( payload ) =>
343+ payload . sourcemaps . map ( ( sourcemap ) => sourcemap . fileName ) ,
344+ ) ,
345+ ) . toEqual ( [ "first.js.map" , "second.js.map" , "third.js.map" ] ) ;
346+ } ) ;
347+
348+ it ( "throws on upload error by default" , async ( ) => {
349+ const onUploadError = vi . fn ( ) ;
350+ const fetchImpl = ( async (
351+ _input : Parameters < typeof fetch > [ 0 ] ,
352+ _init ?: Parameters < typeof fetch > [ 1 ] ,
353+ ) =>
354+ new Response ( JSON . stringify ( { ok : false } ) , {
355+ status : 500 ,
356+ } ) ) as unknown as typeof fetch ;
357+ const plugin = firstPlugin (
358+ rollupPlugin ( {
359+ endpoint,
360+ buildId : "fail-default-build-id" ,
361+ fetchImpl,
362+ onUploadError,
363+ } ) ,
364+ ) as {
365+ writeBundle ?: (
366+ outputOptions : NormalizedOutputOptions ,
367+ bundle : OutputBundle ,
368+ ) => Promise < void > ;
369+ } ;
370+
371+ if ( ! plugin . writeBundle ) {
372+ throw new Error ( "Expected rollup writeBundle hook" ) ;
373+ }
374+
375+ const bundle = {
376+ "bundle.js.map" : {
377+ type : "asset" ,
378+ fileName : "bundle.js.map" ,
379+ source : JSON . stringify ( { version : 3 , mappings : "AAAA" } ) ,
380+ } ,
381+ } as unknown as OutputBundle ;
382+
383+ await expect (
384+ plugin . writeBundle (
385+ { dir : process . cwd ( ) } as NormalizedOutputOptions ,
386+ bundle ,
387+ ) ,
388+ ) . rejects . toThrow ( "Sourcemap upload failed with status 500" ) ;
389+ expect ( onUploadError ) . toHaveBeenCalledTimes ( 1 ) ;
390+ } ) ;
391+
392+ it ( "does not throw on upload error when failOnError is false" , async ( ) => {
393+ const onUploadError = vi . fn ( ) ;
394+ const fetchImpl = ( async (
395+ _input : Parameters < typeof fetch > [ 0 ] ,
396+ _init ?: Parameters < typeof fetch > [ 1 ] ,
397+ ) =>
398+ new Response ( JSON . stringify ( { ok : false } ) , {
399+ status : 500 ,
400+ } ) ) as unknown as typeof fetch ;
401+ const plugin = firstPlugin (
402+ rollupPlugin ( {
403+ endpoint,
404+ buildId : "fail-soft-build-id" ,
405+ fetchImpl,
406+ failOnError : false ,
407+ onUploadError,
408+ } ) ,
409+ ) as {
410+ writeBundle ?: (
411+ outputOptions : NormalizedOutputOptions ,
412+ bundle : OutputBundle ,
413+ ) => Promise < void > ;
414+ } ;
415+
416+ if ( ! plugin . writeBundle ) {
417+ throw new Error ( "Expected rollup writeBundle hook" ) ;
418+ }
419+
420+ const bundle = {
421+ "bundle.js.map" : {
422+ type : "asset" ,
423+ fileName : "bundle.js.map" ,
424+ source : JSON . stringify ( { version : 3 , mappings : "AAAA" } ) ,
425+ } ,
426+ } as unknown as OutputBundle ;
427+
428+ await expect (
429+ plugin . writeBundle (
430+ { dir : process . cwd ( ) } as NormalizedOutputOptions ,
431+ bundle ,
432+ ) ,
433+ ) . resolves . toBeUndefined ( ) ;
434+ expect ( onUploadError ) . toHaveBeenCalledTimes ( 1 ) ;
435+ } ) ;
436+
276437 it ( "prefers webpack native build hash when buildId is not provided" , async ( ) => {
277438 const cwd = await mkdtemp ( join ( tmpdir ( ) , "sourcemaps-webpack-native-id-" ) ) ;
278439 await cp ( join ( fixturesDir , "webpack" ) , cwd , { recursive : true } ) ;
0 commit comments