mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(migrations): migrate RouterModule.forRoot with a config object to use features (#48935)
Previously if the standalone migration saw a `RouterModule.forRoot` with a config object, it wouldn't migrate it. These changes add some logic that convert the config object to a set of features from the new router API. PR Close #48935
This commit is contained in:
committed by
Andrew Scott
parent
2ceff3f0c2
commit
2de6dae16d
@@ -225,7 +225,9 @@ export class ExportedConfigClass {}
|
||||
RouterModule.forRoot([{
|
||||
path: 'shop',
|
||||
loadComponent: () => import('./shop/shop.component').then(m => m.ShopComponent)
|
||||
}])
|
||||
}], {
|
||||
initialNavigation: 'enabledBlocking'
|
||||
})
|
||||
],
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
@@ -302,7 +304,7 @@ export class AppComponent {}
|
||||
// ./main.ts
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {InjectionToken, importProvidersFrom} from '@angular/core';
|
||||
import {provideRouter} from '@angular/router';
|
||||
import {withEnabledBlockingInitialNavigation, provideRouter} from '@angular/router';
|
||||
import {provideAnimations} from '@angular/platform-browser/animations';
|
||||
import {AppModule, ExportedConfigClass} from './app/app.module';
|
||||
import {AppComponent} from './app/app.component';
|
||||
@@ -326,7 +328,7 @@ bootstrapApplication(AppComponent, {
|
||||
provideRouter([{
|
||||
path: 'shop',
|
||||
loadComponent: () => import('./app/shop/shop.component').then(m => m.ShopComponent)
|
||||
}])
|
||||
}], withEnabledBlockingInitialNavigation())
|
||||
]
|
||||
}).catch(e => console.error(e));
|
||||
```
|
||||
|
||||
+125
-12
@@ -16,7 +16,7 @@ import {getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {closestNode} from '../../utils/typescript/nodes';
|
||||
|
||||
import {convertNgModuleDeclarationToStandalone} from './to-standalone';
|
||||
import {ChangeTracker, createLanguageService, findClassDeclaration, findLiteralProperty, getNodeLookup, getRelativeImportPath, NamedClassDeclaration, NodeLookup, offsetsToNodes} from './util';
|
||||
import {ChangeTracker, createLanguageService, findClassDeclaration, findLiteralProperty, getNodeLookup, getRelativeImportPath, NamedClassDeclaration, NodeLookup, offsetsToNodes, UniqueItemTracker} from './util';
|
||||
|
||||
/** Information extracted from a `bootstrapModule` call necessary to migrate it. */
|
||||
interface BootstrapCallAnalysis {
|
||||
@@ -281,19 +281,27 @@ function migrateImportsForBootstrapCall(
|
||||
}
|
||||
|
||||
for (const element of imports.initializer.elements) {
|
||||
// If the reference is to a `RouterModule.forRoot` call with
|
||||
// one argument, we can migrate to the new `provideRouter` API.
|
||||
if (ts.isCallExpression(element) && element.arguments.length === 1 &&
|
||||
ts.isPropertyAccessExpression(element.expression) &&
|
||||
element.expression.name.text === 'forRoot' &&
|
||||
// If the reference is to a `RouterModule.forRoot` call, we can try to migrate it.
|
||||
if (ts.isCallExpression(element) && ts.isPropertyAccessExpression(element.expression) &&
|
||||
element.arguments.length > 0 && element.expression.name.text === 'forRoot' &&
|
||||
isClassReferenceInModule(
|
||||
element.expression.expression, 'RouterModule', '@angular/router', typeChecker)) {
|
||||
providersInNewCall.push(ts.factory.createCallExpression(
|
||||
tracker.addImport(sourceFile, 'provideRouter', '@angular/router'), [],
|
||||
element.arguments));
|
||||
addNodesToCopy(
|
||||
sourceFile, element.arguments[0], nodeLookup, tracker, nodesToCopy, languageService);
|
||||
continue;
|
||||
const options = element.arguments[1] as ts.Expression | undefined;
|
||||
const features = options ? getRouterModuleForRootFeatures(sourceFile, options, tracker) : [];
|
||||
|
||||
// If the features come back as null, it means that the router
|
||||
// has a configuration that can't be migrated automatically.
|
||||
if (features !== null) {
|
||||
providersInNewCall.push(ts.factory.createCallExpression(
|
||||
tracker.addImport(sourceFile, 'provideRouter', '@angular/router'), [],
|
||||
[element.arguments[0], ...features]));
|
||||
addNodesToCopy(
|
||||
sourceFile, element.arguments[0], nodeLookup, tracker, nodesToCopy, languageService);
|
||||
if (options) {
|
||||
addNodesToCopy(sourceFile, options, nodeLookup, tracker, nodesToCopy, languageService);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ts.isIdentifier(element)) {
|
||||
@@ -335,6 +343,111 @@ function migrateImportsForBootstrapCall(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the call expressions that can be used to replace the options
|
||||
* object that is passed into a `RouterModule.forRoot` call.
|
||||
* @param sourceFile File that the `forRoot` call is coming from.
|
||||
* @param options Node that is passed as the second argument to the `forRoot` call.
|
||||
* @param tracker Tracker in which to track imports that need to be inserted.
|
||||
* @returns Null if the options can't be migrated, otherwise an array of call expressions.
|
||||
*/
|
||||
function getRouterModuleForRootFeatures(
|
||||
sourceFile: ts.SourceFile, options: ts.Expression, tracker: ChangeTracker): ts.CallExpression[]|
|
||||
null {
|
||||
// Options that aren't a static object literal can't be migrated.
|
||||
if (!ts.isObjectLiteralExpression(options)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const featureExpressions: ts.CallExpression[] = [];
|
||||
const configOptions: ts.PropertyAssignment[] = [];
|
||||
const inMemoryScrollingOptions: ts.PropertyAssignment[] = [];
|
||||
const features = new UniqueItemTracker<string, ts.Expression|null>();
|
||||
|
||||
for (const prop of options.properties) {
|
||||
// We can't migrate options that we can't easily analyze.
|
||||
if (!ts.isPropertyAssignment(prop) ||
|
||||
(!ts.isIdentifier(prop.name) && !ts.isStringLiteralLike(prop.name))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (prop.name.text) {
|
||||
// `preloadingStrategy` maps to the `withPreloading` function.
|
||||
case 'preloadingStrategy':
|
||||
features.track('withPreloading', prop.initializer);
|
||||
break;
|
||||
|
||||
// `enableTracing: true` maps to the `withDebugTracing` feature.
|
||||
case 'enableTracing':
|
||||
if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
||||
features.track('withDebugTracing', null);
|
||||
}
|
||||
break;
|
||||
|
||||
// `initialNavigation: 'enabled'` and `initialNavigation: 'enabledBlocking'` map to the
|
||||
// `withEnabledBlockingInitialNavigation` feature, while `initialNavigation: 'disabled'` maps
|
||||
// to the `withDisabledInitialNavigation` feature.
|
||||
case 'initialNavigation':
|
||||
if (!ts.isStringLiteralLike(prop.initializer)) {
|
||||
return null;
|
||||
}
|
||||
if (prop.initializer.text === 'enabledBlocking' || prop.initializer.text === 'enabled') {
|
||||
features.track('withEnabledBlockingInitialNavigation', null);
|
||||
} else if (prop.initializer.text === 'disabled') {
|
||||
features.track('withDisabledInitialNavigation', null);
|
||||
}
|
||||
break;
|
||||
|
||||
// `useHash: true` maps to the `withHashLocation` feature.
|
||||
case 'useHash':
|
||||
if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
||||
features.track('withHashLocation', null);
|
||||
}
|
||||
break;
|
||||
|
||||
// `errorHandler` maps to the `withNavigationErrorHandler` feature.
|
||||
case 'errorHandler':
|
||||
features.track('withNavigationErrorHandler', prop.initializer);
|
||||
break;
|
||||
|
||||
// `anchorScrolling` and `scrollPositionRestoration` arguments have to be combined into an
|
||||
// object literal that is passed into the `withInMemoryScrolling` feature.
|
||||
case 'anchorScrolling':
|
||||
case 'scrollPositionRestoration':
|
||||
inMemoryScrollingOptions.push(prop);
|
||||
break;
|
||||
|
||||
// All remaining properties can be passed through the `withRouterConfig` feature.
|
||||
default:
|
||||
configOptions.push(prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inMemoryScrollingOptions.length > 0) {
|
||||
features.track(
|
||||
'withInMemoryScrolling',
|
||||
ts.factory.createObjectLiteralExpression(inMemoryScrollingOptions));
|
||||
}
|
||||
|
||||
if (configOptions.length > 0) {
|
||||
features.track('withRouterConfig', ts.factory.createObjectLiteralExpression(configOptions));
|
||||
}
|
||||
|
||||
for (const [feature, featureArgs] of features.getEntries()) {
|
||||
const callArgs: ts.Expression[] = [];
|
||||
featureArgs.forEach(arg => {
|
||||
if (arg !== null) {
|
||||
callArgs.push(arg);
|
||||
}
|
||||
});
|
||||
featureExpressions.push(ts.factory.createCallExpression(
|
||||
tracker.addImport(sourceFile, feature, '@angular/router'), [], callArgs));
|
||||
}
|
||||
|
||||
return featureExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the nodes that are referenced inside a root node and would need to be copied into a
|
||||
* new file in order for the node to compile, and tracks them.
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('standalone migration', () => {
|
||||
import {ModuleWithProviders} from '@angular/core';
|
||||
|
||||
export declare class RouterModule {
|
||||
static forRoot(routes: any[]): ModuleWithProviders<RouterModule>;
|
||||
static forRoot(routes: any[], config?: any): ModuleWithProviders<RouterModule>;
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -2377,7 +2377,7 @@ describe('standalone migration', () => {
|
||||
`));
|
||||
});
|
||||
|
||||
it('should preserve RouterModule.forRoot calls with multiple arguments', async () => {
|
||||
it('should migrate a RouterModule.forRoot call with an empty config', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
@@ -2406,16 +2406,420 @@ describe('standalone migration', () => {
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {importProvidersFrom} from '@angular/core';
|
||||
import {APP_ROUTES} from './app/routes';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [importProvidersFrom(RouterModule.forRoot(APP_ROUTES, {}))]
|
||||
providers: [provideRouter(APP_ROUTES)]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with a preloadingStrategy to withPreloading', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
preloadingStrategy: () => of(true)
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {of} from 'rxjs';
|
||||
import {withPreloading, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withPreloading(() => of(true)))]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with enableTracing to withDebugTracing', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
enableTracing: true
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withDebugTracing, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withDebugTracing())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with `initialNavigation: "enabledBlocking"` to withEnabledBlockingInitialNavigation',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
initialNavigation: 'enabledBlocking'
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withEnabledBlockingInitialNavigation, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withEnabledBlockingInitialNavigation())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with `initialNavigation: "enabled"` to withEnabledBlockingInitialNavigation',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
initialNavigation: 'enabled'
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withEnabledBlockingInitialNavigation, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withEnabledBlockingInitialNavigation())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with `initialNavigation: "disabled"` to withDisabledInitialNavigation',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
initialNavigation: 'disabled'
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withDisabledInitialNavigation, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withDisabledInitialNavigation())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with useHash to withHashLocation', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
useHash: true
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withHashLocation, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withHashLocation())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should migrate a router config with errorHandler to withNavigationErrorHandler', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
errorHandler: () => {}
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withNavigationErrorHandler, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withNavigationErrorHandler(() => {}))]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should combine the anchorScrolling and scrollPositionRestoration of a router config into withInMemoryScrolling',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
anchorScrolling: 'enabled',
|
||||
scrollPositionRestoration: 'top'
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withInMemoryScrolling, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withInMemoryScrolling({
|
||||
anchorScrolling: 'enabled',
|
||||
scrollPositionRestoration: 'top'
|
||||
}))]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should copy properties that do not map to a feature into withRouterConfig', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
canceledNavigationResolution: 'replace',
|
||||
useHash: true,
|
||||
paramsInheritanceStrategy: 'always'
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withHashLocation, withRouterConfig, provideRouter} from '@angular/router';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter([], withHashLocation(), withRouterConfig({
|
||||
canceledNavigationResolution: 'replace',
|
||||
paramsInheritanceStrategy: 'always'
|
||||
}))]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should preserve a RouterModule.forRoot where the config cannot be statically analyzed',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
const extraOptions = {useHash: true};
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [RouterModule.forRoot([], {
|
||||
enableTracing: true,
|
||||
...extraOptions
|
||||
})]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppModule, AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {importProvidersFrom} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
const extraOptions = {useHash: true};
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [importProvidersFrom(RouterModule.forRoot([], {
|
||||
enableTracing: true,
|
||||
...extraOptions
|
||||
}))]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should convert BrowserAnimationsModule references to provideAnimations', async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
|
||||
Reference in New Issue
Block a user