@@ -197,15 +197,10 @@ export class BootstrapOptionsMigration extends TsurgeFunnelMigration<
197197 if ( ts . isObjectLiteralExpression ( optionsNode ) ) {
198198 optionLiteral = optionsNode ;
199199 addProvidersToBootstrapOption ( optionProjectFile , optionLiteral , providerFn , replacements ) ;
200+ } else if ( this . isServerConfigZoneless ( optionsNode , typeChecker ) ) {
201+ // Nothing to migrate for the SSR bootstrap
202+ return ;
200203 } else if ( ts . isIdentifier ( optionsNode ) ) {
201- // This case handled both `bootstrapApplication(App, appConfig)` and the server () => bootstrapApplication(App, appConfig)
202- // where appConfig is the result of a `mergeApplicationConfig` call.
203-
204- // This is tricky case to handle, in G3 we're might not be able to resolve the identifier's value
205- // Our best alternative is to assume there is not CD providers set and add the ZoneChangeDetection provider
206- // In the cases where it is, we'll just override the zone provider we just set by re-used inthe appConfig providers
207-
208- // TODO: Should we insert a TODO to clean this up ?
209204 const text = `{...${ optionsNode . getText ( ) } , providers: [${ providerFn } , ...${ optionsNode . getText ( ) } .providers]}` ;
210205 replacements . push (
211206 new Replacement (
@@ -239,6 +234,63 @@ export class BootstrapOptionsMigration extends TsurgeFunnelMigration<
239234 } ) ;
240235 }
241236
237+ /**
238+ * The optionsNode can be a appConfig built with mergeApplicationConfig
239+ * In this case we need to analyze if the base config uses provideZonelessChangeDetection
240+ */
241+ private isServerConfigZoneless ( optionsNode : ts . Expression , typeChecker : ts . TypeChecker ) : boolean {
242+ // Check if optionsNode is a result of mergeApplicationConfig
243+ let symbol = typeChecker . getSymbolAtLocation ( optionsNode ) ;
244+ if ( symbol && ( symbol . flags & ts . SymbolFlags . Alias ) !== 0 ) {
245+ symbol = typeChecker . getAliasedSymbol ( symbol ) ;
246+ }
247+ const optionDeclaration = symbol ?. getDeclarations ( ) ?. [ 0 ] ;
248+ if ( ! optionDeclaration ) {
249+ return false ;
250+ }
251+
252+ if (
253+ ! ts . isVariableDeclaration ( optionDeclaration ) ||
254+ ! optionDeclaration . initializer ||
255+ ! ts . isCallExpression ( optionDeclaration . initializer ) ||
256+ ! ts . isIdentifier ( optionDeclaration . initializer . expression ) ||
257+ optionDeclaration . initializer . expression . text !== 'mergeApplicationConfig'
258+ ) {
259+ // We didn't find a mergeApplicationConfig call, this isn't a server config
260+ return false ;
261+ }
262+
263+ let maybeAppConfig = optionDeclaration . initializer . arguments [ 0 ] ;
264+ if ( ts . isIdentifier ( maybeAppConfig ) ) {
265+ const resolved = getObjectLiteralFromIdentifier ( maybeAppConfig , typeChecker ) ;
266+ if ( resolved ) {
267+ maybeAppConfig = resolved ;
268+ }
269+ }
270+
271+ if ( maybeAppConfig && ts . isObjectLiteralExpression ( maybeAppConfig ) ) {
272+ for ( const prop of maybeAppConfig . properties ) {
273+ if (
274+ ts . isPropertyAssignment ( prop ) &&
275+ ts . isIdentifier ( prop . name ) &&
276+ prop . name . text === 'providers' &&
277+ ts . isArrayLiteralExpression ( prop . initializer )
278+ ) {
279+ for ( const el of prop . initializer . elements ) {
280+ if (
281+ ts . isCallExpression ( el ) &&
282+ ts . isIdentifier ( el . expression ) &&
283+ el . expression . text === 'provideZonelessChangeDetection'
284+ ) {
285+ return true ;
286+ }
287+ }
288+ }
289+ }
290+ }
291+ return false ;
292+ }
293+
242294 private analyzeCreateApplication (
243295 node : ts . CallExpression ,
244296 sourceFile : ts . SourceFile ,
0 commit comments