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

View file

@ -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.

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' | '';

View file

@ -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.

View file

@ -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;

View file

@ -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,
) {

View file

@ -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 {}
`),

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,

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`.
*

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}
: [],
];
/**

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();