refactor(router): Warn when provideRoutes is used without provideRouter (#47896)

Due to being only 1 letter away from `provideRouter`, it is quite
possible that developers may accidentally use `provideRoutes` rather
than `provideRouter` in the `boostrapApplication` function. This change
will warn developers when `provideRoutes` is used without the `Router`.

PR Close #47896
This commit is contained in:
Andrew Scott
2022-10-27 11:38:46 -07:00
committed by Andrew Kushnir
parent 6b659d08ef
commit bc0fc02016
5 changed files with 43 additions and 7 deletions
@@ -1577,9 +1577,6 @@
{
"name": "promise"
},
{
"name": "provideRoutes"
},
{
"name": "redirectIfUrlTree"
},
@@ -1640,6 +1637,9 @@
{
"name": "rootRoute"
},
{
"name": "routes"
},
{
"name": "rxSubscriber"
},
+24
View File
@@ -61,6 +61,7 @@ const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode;
export function provideRouter(routes: Routes, ...features: RouterFeatures[]): EnvironmentProviders {
return makeEnvironmentProviders([
{provide: ROUTES, multi: true, useValue: routes},
NG_DEV_MODE ? {provide: ROUTER_IS_PROVIDED, useValue: true} : [],
{provide: ActivatedRoute, useFactory: rootRoute, deps: [Router]},
{provide: APP_BOOTSTRAP_LISTENER, multi: true, useFactory: getBootstrapListener},
features.map(feature => feature.ɵproviders),
@@ -93,6 +94,28 @@ 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>('', {providedIn: 'root', 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](guide/glossary#provider) for a set of routes.
* @param routes The route configuration to provide.
@@ -113,6 +136,7 @@ function routerFeature<FeatureKind extends RouterFeatureKind>(
export function provideRoutes(routes: Routes): Provider[] {
return [
{provide: ROUTES, multi: true, useValue: routes},
NG_DEV_MODE ? routerIsProvidedDevModeCheck : [],
];
}
+4 -1
View File
@@ -15,7 +15,7 @@ import {RouterLinkActive} from './directives/router_link_active';
import {RouterOutlet} from './directives/router_outlet';
import {RuntimeErrorCode} from './errors';
import {Routes} from './models';
import {getBootstrapListener, rootRoute, withDebugTracing, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation, withPreloading} from './provide_router';
import {getBootstrapListener, rootRoute, ROUTER_IS_PROVIDED, withDebugTracing, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation, withPreloading} from './provide_router';
import {Router, setupRouter} from './router';
import {ExtraOptions, ROUTER_CONFIGURATION} from './router_config';
import {RouterConfigLoader, ROUTES} from './router_config_loader';
@@ -48,6 +48,9 @@ export const ROUTER_PROVIDERS: Provider[] = [
ChildrenOutletContexts,
{provide: ActivatedRoute, useFactory: rootRoute, deps: [Router]},
RouterConfigLoader,
// Only used to warn when `provideRoutes` is used without `RouterModule` or `provideRouter`. Can
// be removed when `provideRoutes` is removed.
NG_DEV_MODE ? {provide: ROUTER_IS_PROVIDED, useValue: true} : [],
];
export function routerNgProbeToken() {
+11 -2
View File
@@ -9,9 +9,8 @@
import {Component, Injectable, NgModule} from '@angular/core';
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {NavigationEnd, Router, RouterModule} from '@angular/router';
import {provideRoutes, Router, RouterModule, ROUTES} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
import {filter, first} from 'rxjs/operators';
@Component({template: '<div>simple standalone</div>', standalone: true})
export class SimpleStandaloneComponent {
@@ -372,6 +371,16 @@ describe('standalone in Router API', () => {
});
});
describe('provideRoutes', () => {
it('warns if provideRoutes is used without provideRouter, RouterTestingModule, or RouterModule.forRoot',
() => {
spyOn(console, 'warn');
TestBed.configureTestingModule({providers: [provideRoutes([])]});
TestBed.inject(ROUTES);
expect(console.warn).toHaveBeenCalled();
});
});
function advance(fixture: ComponentFixture<unknown>) {
tick();
fixture.detectChanges();