@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
33import {
44 transformNextjsConfig ,
55 transformNuxtConfig ,
6+ transformRsbuildConfig ,
67 transformViteConfig ,
78} from "./bootstrappers" ;
89
@@ -324,4 +325,203 @@ describe("bootstrappers", () => {
324325 expect ( result . code ?. endsWith ( "\n" ) ) . toBe ( true ) ;
325326 } ) ;
326327 } ) ;
328+
329+ describe ( "transformRsbuildConfig" , ( ) => {
330+ it ( "injects plugin into a standard rsbuild.config.ts" , async ( ) => {
331+ const initialContent = dedent `
332+ import { defineConfig } from "@rsbuild/core"
333+ export default defineConfig({
334+ plugins: []
335+ })
336+ ` ;
337+
338+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
339+ expect ( result . success ) . toBe ( true ) ;
340+
341+ expect ( result . code ) . toContain (
342+ 'import { arkenvRsbuildPlugin } from "@arkenv/rsbuild-plugin"' ,
343+ ) ;
344+ expect ( result . code ) . toContain ( "arkenvRsbuildPlugin()" ) ;
345+ } ) ;
346+
347+ it ( "injects plugin into a simple object export" , async ( ) => {
348+ const initialContent = dedent `
349+ export default {
350+ plugins: []
351+ }
352+ ` ;
353+
354+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
355+ expect ( result . success ) . toBe ( true ) ;
356+ expect ( result . code ) . toContain ( "arkenvRsbuildPlugin()" ) ;
357+ } ) ;
358+
359+ it ( "handles missing plugins array" , async ( ) => {
360+ const initialContent = dedent `
361+ export default {
362+ server: {}
363+ }
364+ ` ;
365+
366+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
367+ expect ( result . success ) . toBe ( true ) ;
368+ expect ( result . code ) . toContain ( "plugins: [" ) ;
369+ expect ( result . code ) . toContain ( "arkenvRsbuildPlugin()" ) ;
370+ } ) ;
371+
372+ it ( "preserves space indentation format" , ( ) => {
373+ const initialContent =
374+ 'import { defineConfig } from "@rsbuild/core";\n\nexport default defineConfig({\n plugins: [],\n});\n' ;
375+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
376+ expect ( result . success ) . toBe ( true ) ;
377+ expect ( result . code ) . toContain ( " plugins: [arkenvRsbuildPlugin()]" ) ;
378+ expect ( result . code ?. endsWith ( "\n" ) ) . toBe ( true ) ;
379+ } ) ;
380+
381+ it ( "preserves tab indentation format" , ( ) => {
382+ const initialContent =
383+ 'import { defineConfig } from "@rsbuild/core";\n\nexport default defineConfig({\n\tplugins: [],\n});\n' ;
384+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
385+ expect ( result . success ) . toBe ( true ) ;
386+ expect ( result . code ) . toContain ( "\tplugins: [arkenvRsbuildPlugin()]" ) ;
387+ } ) ;
388+
389+ it ( "preserves absence of trailing newline" , ( ) => {
390+ const initialContent = "export default { plugins: [] }" ;
391+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
392+ expect ( result . success ) . toBe ( true ) ;
393+ expect ( result . code ?. endsWith ( "\n" ) ) . toBe ( false ) ;
394+ } ) ;
395+
396+ it ( "refuses defineConfig callback form with an actionable message" , ( ) => {
397+ const initialContent =
398+ 'import { defineConfig } from "@rsbuild/core";\nexport default defineConfig((env) => ({\n plugins: [],\n}));' ;
399+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
400+ expect ( result . success ) . toBe ( false ) ;
401+ if ( ! result . success ) {
402+ expect ( result . error ) . toContain (
403+ "The 'defineConfig' callback form is currently not supported" ,
404+ ) ;
405+ }
406+ } ) ;
407+
408+ it ( "fails when default export is not an object" , ( ) => {
409+ const initialContent = "export default 123;" ;
410+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
411+ expect ( result . success ) . toBe ( false ) ;
412+ if ( ! result . success ) {
413+ expect ( result . error ) . toContain (
414+ "Could not find default export object in Rsbuild config" ,
415+ ) ;
416+ }
417+ } ) ;
418+
419+ it ( "fails when plugins property is not an array" , ( ) => {
420+ const initialContent = "export default { plugins: 123 };" ;
421+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
422+ expect ( result . success ) . toBe ( false ) ;
423+ if ( ! result . success ) {
424+ expect ( result . error ) . toContain (
425+ "The 'plugins' property in your Rsbuild config is not an array" ,
426+ ) ;
427+ }
428+ } ) ;
429+
430+ it ( "does not duplicate plugin if already exists and returns updated: false" , async ( ) => {
431+ const initialContent = dedent `
432+ import { arkenvRsbuildPlugin } from "@arkenv/rsbuild-plugin"
433+ export default {
434+ plugins: [arkenvRsbuildPlugin()]
435+ }
436+ ` ;
437+
438+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
439+ expect ( result . success ) . toBe ( true ) ;
440+ expect ( result . updated ) . toBe ( false ) ;
441+ } ) ;
442+
443+ it ( "is idempotent when arkenvRsbuildPlugin is aliased in the import" , async ( ) => {
444+ const initialContent = dedent `
445+ import { arkenvRsbuildPlugin as myPlugin } from "@arkenv/rsbuild-plugin"
446+ export default {
447+ plugins: [myPlugin()]
448+ }
449+ ` ;
450+
451+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
452+ expect ( result . success ) . toBe ( true ) ;
453+ expect ( result . updated ) . toBe ( false ) ;
454+ } ) ;
455+
456+ it ( "returns updated: true when plugin is injected" , async ( ) => {
457+ const initialContent = dedent `
458+ export default {
459+ plugins: []
460+ }
461+ ` ;
462+
463+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
464+ expect ( result . success ) . toBe ( true ) ;
465+ expect ( result . updated ) . toBe ( true ) ;
466+ } ) ;
467+
468+ it ( "injects plugin into plugins array even if import already exists but is unregistered" , async ( ) => {
469+ const initialContent = dedent `
470+ import { arkenvRsbuildPlugin } from "@arkenv/rsbuild-plugin"
471+ export default {
472+ plugins: []
473+ }
474+ ` ;
475+
476+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
477+ expect ( result . success ) . toBe ( true ) ;
478+ expect ( result . updated ) . toBe ( true ) ;
479+ expect ( result . code ) . toContain ( "plugins: [arkenvRsbuildPlugin()]" ) ;
480+ } ) ;
481+
482+ it ( "injects aliased plugin call when an unregistered aliased import exists" , async ( ) => {
483+ const initialContent = dedent `
484+ import { arkenvRsbuildPlugin as customPlugin } from "@arkenv/rsbuild-plugin"
485+ export default {
486+ plugins: []
487+ }
488+ ` ;
489+
490+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
491+ expect ( result . success ) . toBe ( true ) ;
492+ expect ( result . updated ) . toBe ( true ) ;
493+ expect ( result . code ) . toContain ( "plugins: [customPlugin()]" ) ;
494+ expect ( result . code ) . not . toContain ( "arkenvRsbuildPlugin()" ) ;
495+ } ) ;
496+
497+ it ( "prefers the plugin function when options type is imported before it" , async ( ) => {
498+ const initialContent = dedent `
499+ import { type RsbuildTransformOptions, arkenvRsbuildPlugin } from "@arkenv/rsbuild-plugin"
500+ export default {
501+ plugins: []
502+ }
503+ ` ;
504+
505+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
506+ expect ( result . success ) . toBe ( true ) ;
507+ expect ( result . updated ) . toBe ( true ) ;
508+ expect ( result . code ) . toContain ( "plugins: [arkenvRsbuildPlugin()]" ) ;
509+ expect ( result . code ) . not . toContain ( "RsbuildTransformOptions()" ) ;
510+ } ) ;
511+
512+ it ( "injects plugin into a non-empty plugins array preserving existing entries" , async ( ) => {
513+ const initialContent = dedent `
514+ import { pluginReact } from "@rsbuild/plugin-react"
515+ export default {
516+ plugins: [pluginReact()]
517+ }
518+ ` ;
519+
520+ const result = transformRsbuildConfig ( { code : initialContent } ) ;
521+ expect ( result . success ) . toBe ( true ) ;
522+ expect ( result . updated ) . toBe ( true ) ;
523+ expect ( result . code ) . toContain ( "pluginReact()" ) ;
524+ expect ( result . code ) . toContain ( "arkenvRsbuildPlugin()" ) ;
525+ } ) ;
526+ } ) ;
327527} ) ;
0 commit comments