refactor(router): remove deprecated provideRoutes function.

`provideRoutes` was deprecated in v15.

BREAKING CHANGE: `provideRoutes()` has been removed. Use `provideRouter()` or `ROUTES` as multi token if necessary.
This commit is contained in:
Matthieu Riegler
2026-02-24 09:33:46 +01:00
committed by Jessica Janiuk
parent c57ba8c6e6
commit bdb6ae9dbc
10 changed files with 28 additions and 162 deletions
@@ -25,7 +25,6 @@ The schematic will attempt to find all the places where the application routes a
- `RouterModule.forRoot` and `RouterModule.forChild`
- `Router.resetConfig`
- `provideRouter`
- `provideRoutes`
- variables of type `Routes` or `Route[]` (e.g. `const routes: Routes = [{...}]`)
The migration will check all the components in the routes, check if they are standalone and eagerly loaded, and if so, it will convert them to lazy loaded routes.
-3
View File
@@ -589,9 +589,6 @@ export const PRIMARY_OUTLET = "primary";
// @public
export function provideRouter(routes: Routes, ...features: RouterFeatures[]): EnvironmentProviders;
// @public @deprecated
export function provideRoutes(routes: Routes): Provider[];
// @public
export type QueryParamsHandling = 'merge' | 'preserve' | 'replace' | '';
@@ -27,7 +27,6 @@ The schematic will attempt to find all the places where the application routes a
- `RouterModule.forRoot` and `RouterModule.forChild`
- `Router.resetConfig`
- `provideRouter`
- `provideRoutes`
- variables of type `Routes` or `Route[]` (e.g. `const routes: Routes = [{...}]`)
The migration will check all the components in the routes, check if they are standalone and eagerly loaded, and if so, it will convert them to lazy loaded routes.
@@ -15,10 +15,9 @@ import {findClassDeclaration} from '../../utils/typescript/class_declaration';
import {findLiteralProperty} from '../../utils/typescript/property_name';
import {
isAngularRoutesArray,
isProvideRoutesCallExpression,
isProvideRouterCallExpression,
isRouterCallExpression,
isRouterModuleCallExpression,
isRouterProviderCallExpression,
isStandaloneComponent,
} from './util';
@@ -79,9 +78,8 @@ function findRoutesArrayToMigrate(sourceFile: ts.SourceFile, typeChecker: ts.Typ
if (ts.isCallExpression(node)) {
if (
isRouterModuleCallExpression(node, typeChecker) ||
isRouterProviderCallExpression(node, typeChecker) ||
isRouterCallExpression(node, typeChecker) ||
isProvideRoutesCallExpression(node, typeChecker)
isProvideRouterCallExpression(node, typeChecker)
) {
const arg = node.arguments[0]; // ex: RouterModule.forRoot(routes) or provideRouter(routes)
const routeFileImports = sourceFile.statements.filter(ts.isImportDeclaration);
@@ -95,7 +93,7 @@ function findRoutesArrayToMigrate(sourceFile: ts.SourceFile, typeChecker: ts.Typ
});
} else if (ts.isIdentifier(arg)) {
// ex: reference to routes array: RouterModule.forRoot(routes)
// RouterModule.forRoot(routes), provideRouter(routes), provideRoutes(routes)
// RouterModule.forRoot(routes), provideRouter(routes)
const symbol = typeChecker.getSymbolAtLocation(arg);
if (!symbol?.declarations) return;
@@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
import ts from 'typescript';
import {findAngularDecorator, ReflectionHost} from '@angular/compiler-cli/private/migrations';
import ts from 'typescript';
import {findLiteralProperty} from '../../utils/typescript/property_name';
/**
@@ -103,26 +103,11 @@ export function isRouterCallExpression(node: ts.CallExpression, typeChecker: ts.
return false;
}
/**
* Checks whether a node is a call expression to router provide function.
* Example: provideRoutes(routes)
*/
export function isRouterProviderCallExpression(
node: ts.CallExpression,
typeChecker: ts.TypeChecker,
) {
if (ts.isIdentifier(node.expression)) {
const moduleSymbol = typeChecker.getSymbolAtLocation(node.expression);
return moduleSymbol && moduleSymbol.name === 'provideRoutes';
}
return false;
}
/**
* Checks whether a node is a call expression to provideRouter function.
* Example: provideRouter(routes)
*/
export function isProvideRoutesCallExpression(
export function isProvideRouterCallExpression(
node: ts.CallExpression,
typeChecker: ts.TypeChecker,
) {
@@ -10,8 +10,8 @@ import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core';
import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
import {HostTree} from '@angular-devkit/schematics';
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing/index.js';
import {resolve} from 'path';
import {rmSync} from 'node:fs';
import {resolve} from 'path';
describe('route lazy loading migration', () => {
let runner: SchematicTestRunner;
@@ -285,55 +285,12 @@ describe('route lazy loading migration', () => {
);
});
it('should support provideRoutes', async () => {
writeFile(
'app.module.ts',
`
import {NgModule} from '@angular/core';
import {provideRoutes} from '@angular/router';
import {TestComponent} from './test';
const routes = [{path: 'test', component: TestComponent}];
@NgModule({
providers: [provideRoutes(routes)],
})
export class AppModule {}
`,
);
writeFile(
'test.ts',
`
import {Component} from '@angular/core';
@Component({template: 'hello', standalone: true})
export class TestComponent {}
`,
);
await runMigration('route-lazy-loading');
expect(stripWhitespace(tree.readContent('app.module.ts'))).toContain(
stripWhitespace(`
import {NgModule} from '@angular/core';
import {provideRoutes} from '@angular/router';
const routes = [{path: 'test', loadComponent: () => import('./test').then(m => m.TestComponent)}];
@NgModule({
providers: [provideRoutes(routes)],
})
export class AppModule {}
`),
);
});
it('should skip not standalone components', async () => {
writeFile(
'app.module.ts',
`
import {NgModule} from '@angular/core';
import {provideRoutes} from '@angular/router';
import {provideRouter} from '@angular/router';
import {TestComponent} from './test';
import {StandaloneByDefaultComponent} from './standalone-by-default';
import {NotStandaloneComponent} from './not-standalone';
@@ -345,7 +302,7 @@ describe('route lazy loading migration', () => {
];
@NgModule({
providers: [provideRoutes(routes)],
providers: [provideRouter(routes)],
})
export class AppModule {}
`,
@@ -386,7 +343,7 @@ describe('route lazy loading migration', () => {
expect(stripWhitespace(tree.readContent('app.module.ts'))).toContain(
stripWhitespace(`
import {NgModule} from '@angular/core';
import {provideRoutes} from '@angular/router';
import {provideRouter} from '@angular/router';
import {NotStandaloneComponent} from './not-standalone';
const routes = [
@@ -396,7 +353,7 @@ describe('route lazy loading migration', () => {
];
@NgModule({
providers: [provideRoutes(routes)],
providers: [provideRouter(routes)],
})
export class AppModule {}
`),
+17 -18
View File
@@ -9,7 +9,7 @@
export {createUrlTreeFromSnapshot} from './create_url_tree';
export {RouterLink, RouterLinkWithHref} from './directives/router_link';
export {RouterLinkActive} from './directives/router_link_active';
export {RouterOutlet, ROUTER_OUTLET_DATA, RouterOutletContract} from './directives/router_outlet';
export {ROUTER_OUTLET_DATA, RouterOutlet, RouterOutletContract} from './directives/router_outlet';
export {
ActivationEnd,
ActivationStart,
@@ -35,21 +35,29 @@ export {
Scroll,
} from './events';
export {
CanActivate,
CanActivateChild,
CanActivateChildFn,
MaybeAsync,
GuardResult,
CanActivateFn,
CanDeactivate,
CanDeactivateFn,
CanLoad,
CanLoadFn,
CanMatch,
CanMatchFn,
Data,
DefaultExport,
GuardResult,
LoadChildren,
LoadChildrenCallback,
MaybeAsync,
NavigationBehaviorOptions,
OnSameUrlNavigation,
PartialMatchRouteSnapshot,
QueryParamsHandling,
RedirectCommand,
RedirectFunction,
Resolve,
ResolveData,
ResolveFn,
Route,
@@ -57,14 +65,6 @@ export {
RunGuardsAndResolvers,
UrlMatcher,
UrlMatchResult,
RedirectCommand,
CanActivate,
CanActivateChild,
CanDeactivate,
CanLoad,
CanMatch,
Resolve,
PartialMatchRouteSnapshot,
} from './models';
export {ViewTransitionInfo, ViewTransitionsFeatureOptions} from './utils/view_transition';
@@ -75,36 +75,35 @@ export {
ComponentInputBindingFeature,
DebugTracingFeature,
DisabledInitialNavigationFeature,
withViewTransitions,
ViewTransitionsFeature,
EnabledBlockingInitialNavigationFeature,
InitialNavigationFeature,
InMemoryScrollingFeature,
NavigationErrorHandlerFeature,
PreloadingFeature,
provideRouter,
withExperimentalPlatformNavigation,
provideRoutes,
RouterConfigurationFeature,
RouterFeature,
RouterFeatures,
RouterHashLocationFeature,
ViewTransitionsFeature,
withComponentInputBinding,
withDebugTracing,
withDisabledInitialNavigation,
withEnabledBlockingInitialNavigation,
withExperimentalAutoCleanupInjectors,
withExperimentalPlatformNavigation,
withHashLocation,
withInMemoryScrolling,
withNavigationErrorHandler,
withPreloading,
withRouterConfig,
withExperimentalAutoCleanupInjectors,
withViewTransitions,
} from './provide_router';
export {
BaseRouteReuseStrategy,
DetachedRouteHandle,
destroyDetachedRouteHandle,
DetachedRouteHandle,
RouteReuseStrategy,
} from './route_reuse_strategy';
export {Router} from './router';
@@ -134,12 +133,12 @@ export {convertToParamMap, defaultUrlMatcher, ParamMap, Params, PRIMARY_OUTLET}
export {UrlHandlingStrategy} from './url_handling_strategy';
export {
DefaultUrlSerializer,
isActive,
IsActiveMatchOptions,
UrlSegment,
UrlSegmentGroup,
UrlSerializer,
UrlTree,
isActive,
} from './url_tree';
export {
mapToCanActivate,
-53
View File
@@ -106,9 +106,6 @@ export function provideRouter(routes: Routes, ...features: RouterFeatures[]): En
return makeEnvironmentProviders([
{provide: ROUTES, multi: true, useValue: routes},
typeof ngDevMode === 'undefined' || ngDevMode
? {provide: ROUTER_IS_PROVIDED, useValue: true}
: [],
{provide: ActivatedRoute, useFactory: rootRoute},
{provide: APP_BOOTSTRAP_LISTENER, multi: true, useFactory: getBootstrapListener},
features.map((feature) => feature.ɵproviders),
@@ -139,56 +136,6 @@ function routerFeature<FeatureKind extends RouterFeatureKind>(
return {ɵkind: kind, ɵproviders: providers};
}
/**
* An Injection token used to indicate whether `provideRouter` or `RouterModule.forRoot` was ever
* called.
*/
export const ROUTER_IS_PROVIDED = new InjectionToken<boolean>(
typeof ngDevMode !== 'undefined' && ngDevMode ? 'Router is provided' : '',
{
factory: () => false,
},
);
const routerIsProvidedDevModeCheck = {
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useFactory() {
return () => {
if (!inject(ROUTER_IS_PROVIDED)) {
console.warn(
'`provideRoutes` was called without `provideRouter` or `RouterModule.forRoot`. ' +
'This is likely a mistake.',
);
}
};
},
};
/**
* Registers a DI provider for a set of routes.
* @param routes The route configuration to provide.
*
* @usageNotes
*
* ```ts
* @NgModule({
* providers: [provideRoutes(ROUTES)]
* })
* class LazyLoadedChildModule {}
* ```
*
* @deprecated If necessary, provide routes using the `ROUTES` `InjectionToken`.
* @see {@link ROUTES}
* @publicApi
*/
export function provideRoutes(routes: Routes): Provider[] {
return [
{provide: ROUTES, multi: true, useValue: routes},
typeof ngDevMode === 'undefined' || ngDevMode ? routerIsProvidedDevModeCheck : [],
];
}
/**
* A type alias for providers returned by `withInMemoryScrolling` for use with `provideRouter`.
*
-6
View File
@@ -34,7 +34,6 @@ import {NAVIGATION_ERROR_HANDLER} from './navigation_transition';
import {
getBootstrapListener,
rootRoute,
ROUTER_IS_PROVIDED,
withComponentInputBinding,
withDebugTracing,
withDisabledInitialNavigation,
@@ -73,11 +72,6 @@ export const ROUTER_PROVIDERS: Provider[] = [
ChildrenOutletContexts,
{provide: ActivatedRoute, useFactory: rootRoute},
RouterConfigLoader,
// Only used to warn when `provideRoutes` is used without `RouterModule` or `provideRouter`. Can
// be removed when `provideRoutes` is removed.
typeof ngDevMode === 'undefined' || ngDevMode
? {provide: ROUTER_IS_PROVIDED, useValue: true}
: [],
];
/**
+1 -10
View File
@@ -9,8 +9,8 @@
import {Component, inject, Injectable, InjectionToken, NgModule} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {provideRoutes, Router, RouterModule, ROUTES} from '../index';
import {timeout} from '@angular/private/testing';
import {Router, RouterModule} from '../index';
@Component({template: '<div>simple standalone</div>'})
export class SimpleStandaloneComponent {}
@@ -546,15 +546,6 @@ describe('standalone in Router API', () => {
});
});
describe('provideRoutes', () => {
it('warns if provideRoutes is used without provideRouter, RouterModule, or RouterModule.forRoot', () => {
spyOn(console, 'warn');
TestBed.configureTestingModule({providers: [provideRoutes([])]});
TestBed.inject(ROUTES);
expect(console.warn).toHaveBeenCalled();
});
});
async function advanceAsync(fixture: ComponentFixture<unknown>) {
await timeout();
fixture.detectChanges();