99import { DOCUMENT } from '@angular/common' ;
1010import {
1111 APP_ID ,
12+ inject ,
13+ InjectionToken ,
14+ Injector ,
1215 Provider ,
1316 TransferState ,
1417 ɵstartMeasuring as startMeasuring ,
@@ -17,11 +20,19 @@ import {
1720
1821import { BEFORE_APP_SERIALIZED } from './tokens' ;
1922
23+ // Tracks whether the server-side application state for a given app ID has been serialized already.
24+ export const TRANSFER_STATE_SERIALIZED_FOR_APPID = new InjectionToken < Set < string > > (
25+ typeof ngDevMode === 'undefined' || ngDevMode ? 'TRANSFER_STATE_SERIALIZED_FOR_APPID' : '' ,
26+ {
27+ providedIn : 'platform' ,
28+ factory : ( ) => new Set ( ) ,
29+ } ,
30+ ) ;
31+
2032export const TRANSFER_STATE_SERIALIZATION_PROVIDERS : Provider [ ] = [
2133 {
2234 provide : BEFORE_APP_SERIALIZED ,
2335 useFactory : serializeTransferStateFactory ,
24- deps : [ DOCUMENT , APP_ID , TransferState ] ,
2536 multi : true ,
2637 } ,
2738] ;
@@ -41,7 +52,28 @@ export function createScript(
4152 return script ;
4253}
4354
44- function serializeTransferStateFactory ( doc : Document , appId : string , transferStore : TransferState ) {
55+ export function warnIfStateTransferHappened ( injector : Injector ) : void {
56+ const appId = injector . get ( APP_ID ) ;
57+ const appIdsWithTransferStateSerialized = injector . get ( TRANSFER_STATE_SERIALIZED_FOR_APPID ) ;
58+
59+ if ( appIdsWithTransferStateSerialized . has ( appId ) ) {
60+ console . warn (
61+ `Angular detected an incompatible configuration, which causes duplicate serialization of the server-side application state.\n\n` +
62+ `This can happen if the server providers have been provided more than once using different mechanisms. For example:\n\n` +
63+ ` imports: [ServerModule], // Registers server providers\n` +
64+ ` providers: [provideServerRendering()] // Also registers server providers\n\n` +
65+ `To fix this, ensure that the \`provideServerRendering()\` function is the only provider used and remove the other(s).` ,
66+ ) ;
67+ }
68+ appIdsWithTransferStateSerialized . add ( appId ) ;
69+ }
70+
71+ function serializeTransferStateFactory ( ) {
72+ const doc = inject ( DOCUMENT ) ;
73+ const appId = inject ( APP_ID ) ;
74+ const transferStore = inject ( TransferState ) ;
75+ const injector = inject ( Injector ) ;
76+
4577 return ( ) => {
4678 const measuringLabel = 'serializeTransferStateFactory' ;
4779 startMeasuring ( measuringLabel ) ;
@@ -55,6 +87,10 @@ function serializeTransferStateFactory(doc: Document, appId: string, transferSto
5587 return ;
5688 }
5789
90+ if ( typeof ngDevMode !== 'undefined' && ngDevMode ) {
91+ warnIfStateTransferHappened ( injector ) ;
92+ }
93+
5894 const script = createScript (
5995 doc ,
6096 content ,
@@ -68,7 +104,7 @@ function serializeTransferStateFactory(doc: Document, appId: string, transferSto
68104 script . setAttribute ( 'type' , 'application/json' ) ;
69105
70106 // It is intentional that we add the script at the very bottom. Angular CLI script tags for
71- // bundles are always `type="module"`. These are deferred by default and cause the transfer
107+ // bundles are always `type="module"`. These are deferred by default and cause the
72108 // transfer data to be queried only after the browser has finished parsing the DOM.
73109 doc . body . appendChild ( script ) ;
74110 stopMeasuring ( measuringLabel ) ;
0 commit comments