@@ -17,6 +17,9 @@ vi.mock('path', async () => {
1717 if ( lastArg === 'moduleMetadataInjectionLoader.js' ) {
1818 return '/mocked/path/to/moduleMetadataInjectionLoader.js' ;
1919 }
20+ if ( lastArg === 'componentAnnotationLoader.js' ) {
21+ return '/mocked/path/to/componentAnnotationLoader.js' ;
22+ }
2023 return '/mocked/path/to/valueInjectionLoader.js' ;
2124 } ) ,
2225 } ;
@@ -1080,6 +1083,182 @@ describe('moduleMetadataInjection with applicationKey', () => {
10801083 } ) ;
10811084} ) ;
10821085
1086+ describe ( 'componentAnnotation with turbopackReactComponentAnnotation' , ( ) => {
1087+ it ( 'should add component annotation loader rule when enabled and Next.js >= 16' , ( ) => {
1088+ const pathResolveSpy = vi . spyOn ( path , 'resolve' ) ;
1089+ pathResolveSpy . mockImplementation ( ( ...args : string [ ] ) => {
1090+ const lastArg = args [ args . length - 1 ] ;
1091+ if ( lastArg === 'componentAnnotationLoader.js' ) {
1092+ return '/mocked/path/to/componentAnnotationLoader.js' ;
1093+ }
1094+ if ( lastArg === 'moduleMetadataInjectionLoader.js' ) {
1095+ return '/mocked/path/to/moduleMetadataInjectionLoader.js' ;
1096+ }
1097+ return '/mocked/path/to/valueInjectionLoader.js' ;
1098+ } ) ;
1099+
1100+ const userNextConfig : NextConfigObject = { } ;
1101+
1102+ const result = constructTurbopackConfig ( {
1103+ userNextConfig,
1104+ userSentryOptions : {
1105+ _experimental : {
1106+ turbopackReactComponentAnnotation : { enabled : true } ,
1107+ } ,
1108+ } ,
1109+ nextJsVersion : '16.0.0' ,
1110+ } ) ;
1111+
1112+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toEqual ( {
1113+ condition : { not : 'foreign' } ,
1114+ loaders : [
1115+ {
1116+ loader : '/mocked/path/to/componentAnnotationLoader.js' ,
1117+ options : {
1118+ ignoredComponents : [ ] ,
1119+ } ,
1120+ } ,
1121+ ] ,
1122+ } ) ;
1123+ } ) ;
1124+
1125+ it ( 'should NOT add component annotation rule when enabled is false' , ( ) => {
1126+ const userNextConfig : NextConfigObject = { } ;
1127+
1128+ const result = constructTurbopackConfig ( {
1129+ userNextConfig,
1130+ userSentryOptions : {
1131+ _experimental : {
1132+ turbopackReactComponentAnnotation : { enabled : false } ,
1133+ } ,
1134+ } ,
1135+ nextJsVersion : '16.0.0' ,
1136+ } ) ;
1137+
1138+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toBeUndefined ( ) ;
1139+ } ) ;
1140+
1141+ it ( 'should NOT add component annotation rule when not set' , ( ) => {
1142+ const userNextConfig : NextConfigObject = { } ;
1143+
1144+ const result = constructTurbopackConfig ( {
1145+ userNextConfig,
1146+ userSentryOptions : { } ,
1147+ nextJsVersion : '16.0.0' ,
1148+ } ) ;
1149+
1150+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toBeUndefined ( ) ;
1151+ } ) ;
1152+
1153+ it ( 'should NOT add component annotation rule when Next.js < 16' , ( ) => {
1154+ const userNextConfig : NextConfigObject = { } ;
1155+
1156+ const result = constructTurbopackConfig ( {
1157+ userNextConfig,
1158+ userSentryOptions : {
1159+ _experimental : {
1160+ turbopackReactComponentAnnotation : { enabled : true } ,
1161+ } ,
1162+ } ,
1163+ nextJsVersion : '15.4.1' ,
1164+ } ) ;
1165+
1166+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toBeUndefined ( ) ;
1167+ } ) ;
1168+
1169+ it ( 'should NOT add component annotation rule when nextJsVersion is undefined' , ( ) => {
1170+ const userNextConfig : NextConfigObject = { } ;
1171+
1172+ const result = constructTurbopackConfig ( {
1173+ userNextConfig,
1174+ userSentryOptions : {
1175+ _experimental : {
1176+ turbopackReactComponentAnnotation : { enabled : true } ,
1177+ } ,
1178+ } ,
1179+ nextJsVersion : undefined ,
1180+ } ) ;
1181+
1182+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toBeUndefined ( ) ;
1183+ } ) ;
1184+
1185+ it ( 'should pass ignoredComponents to loader options' , ( ) => {
1186+ const pathResolveSpy = vi . spyOn ( path , 'resolve' ) ;
1187+ pathResolveSpy . mockImplementation ( ( ...args : string [ ] ) => {
1188+ const lastArg = args [ args . length - 1 ] ;
1189+ if ( lastArg === 'componentAnnotationLoader.js' ) {
1190+ return '/mocked/path/to/componentAnnotationLoader.js' ;
1191+ }
1192+ if ( lastArg === 'moduleMetadataInjectionLoader.js' ) {
1193+ return '/mocked/path/to/moduleMetadataInjectionLoader.js' ;
1194+ }
1195+ return '/mocked/path/to/valueInjectionLoader.js' ;
1196+ } ) ;
1197+
1198+ const userNextConfig : NextConfigObject = { } ;
1199+
1200+ const result = constructTurbopackConfig ( {
1201+ userNextConfig,
1202+ userSentryOptions : {
1203+ _experimental : {
1204+ turbopackReactComponentAnnotation : {
1205+ enabled : true ,
1206+ ignoredComponents : [ 'Header' , 'Footer' ] ,
1207+ } ,
1208+ } ,
1209+ } ,
1210+ nextJsVersion : '16.0.0' ,
1211+ } ) ;
1212+
1213+ const rule = result . rules ! [ '*.{tsx,jsx}' ] as {
1214+ condition : unknown ;
1215+ loaders : Array < { loader : string ; options : { ignoredComponents : string [ ] } } > ;
1216+ } ;
1217+ expect ( rule . loaders [ 0 ] ! . options . ignoredComponents ) . toEqual ( [ 'Header' , 'Footer' ] ) ;
1218+ } ) ;
1219+
1220+ it ( 'should coexist with value injection and module metadata rules' , ( ) => {
1221+ const pathResolveSpy = vi . spyOn ( path , 'resolve' ) ;
1222+ pathResolveSpy . mockImplementation ( ( ...args : string [ ] ) => {
1223+ const lastArg = args [ args . length - 1 ] ;
1224+ if ( lastArg === 'componentAnnotationLoader.js' ) {
1225+ return '/mocked/path/to/componentAnnotationLoader.js' ;
1226+ }
1227+ if ( lastArg === 'moduleMetadataInjectionLoader.js' ) {
1228+ return '/mocked/path/to/moduleMetadataInjectionLoader.js' ;
1229+ }
1230+ return '/mocked/path/to/valueInjectionLoader.js' ;
1231+ } ) ;
1232+
1233+ const userNextConfig : NextConfigObject = { } ;
1234+ const mockRouteManifest : RouteManifest = {
1235+ dynamicRoutes : [ ] ,
1236+ staticRoutes : [ { path : '/' , regex : '/' } ] ,
1237+ isrRoutes : [ ] ,
1238+ } ;
1239+
1240+ const result = constructTurbopackConfig ( {
1241+ userNextConfig,
1242+ userSentryOptions : {
1243+ _experimental : {
1244+ turbopackApplicationKey : 'my-app' ,
1245+ turbopackReactComponentAnnotation : { enabled : true } ,
1246+ } ,
1247+ } ,
1248+ routeManifest : mockRouteManifest ,
1249+ nextJsVersion : '16.0.0' ,
1250+ } ) ;
1251+
1252+ // Value injection rules should be present
1253+ expect ( result . rules ! [ '**/instrumentation-client.*' ] ) . toBeDefined ( ) ;
1254+ expect ( result . rules ! [ '**/instrumentation.*' ] ) . toBeDefined ( ) ;
1255+ // Module metadata loader should be present
1256+ expect ( result . rules ! [ '*.{ts,tsx,js,jsx,mjs,cjs}' ] ) . toBeDefined ( ) ;
1257+ // Component annotation loader should be present
1258+ expect ( result . rules ! [ '*.{tsx,jsx}' ] ) . toBeDefined ( ) ;
1259+ } ) ;
1260+ } ) ;
1261+
10831262describe ( 'safelyAddTurbopackRule' , ( ) => {
10841263 const mockRule = {
10851264 loaders : [
0 commit comments