-
Notifications
You must be signed in to change notification settings - Fork 24.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(router): auto-unwrap default exports when lazy loading #47586
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Injector, NgModuleFactory} from '@angular/core'; | ||
import {Observable} from 'rxjs'; | ||
|
||
// This file exists to support the legacy `loadChildren: string` behavior being patched back into | ||
// Angular. | ||
|
||
/** | ||
* Deprecated `loadChildren` value types. | ||
* | ||
* @publicApi | ||
* @deprecated represents the deprecated type side of `LoadChildren`. | ||
*/ | ||
export type DeprecatedLoadChildren = never; | ||
|
||
export function deprecatedLoadChildrenString( | ||
injector: Injector, loadChildren: unknown): Observable<NgModuleFactory<any>>|null { | ||
return null; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Component} from '@angular/core'; | ||
|
||
@Component({ | ||
standalone: true, | ||
template: 'default exported', | ||
selector: 'test-route', | ||
}) | ||
export default class TestRoute { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Component} from '@angular/core'; | ||
import {Routes} from '@angular/router'; | ||
|
||
@Component({ | ||
standalone: true, | ||
template: 'default exported', | ||
selector: 'test-route', | ||
}) | ||
export class TestRoute { | ||
} | ||
|
||
|
||
export default [ | ||
{path: '', pathMatch: 'full', component: TestRoute}, | ||
] as Routes; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,10 +9,9 @@ | |
import {Component, Injectable, NgModule} from '@angular/core'; | ||
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; | ||
import {By} from '@angular/platform-browser'; | ||
import {Router} from '@angular/router'; | ||
import {NavigationEnd, Router, RouterModule} from '@angular/router'; | ||
import {RouterTestingModule} from '@angular/router/testing'; | ||
|
||
import {RouterModule} from '../src'; | ||
import {filter, first} from 'rxjs/operators'; | ||
|
||
@Component({template: '<div>simple standalone</div>', standalone: true}) | ||
export class SimpleStandaloneComponent { | ||
|
@@ -340,6 +339,35 @@ describe('standalone in Router API', () => { | |
expect(() => advance(root)).toThrowError(/.*home.*component must be standalone/); | ||
})); | ||
}); | ||
describe('default export unwrapping', () => { | ||
it('should work for loadComponent', async () => { | ||
TestBed.configureTestingModule({ | ||
imports: [RouterTestingModule.withRoutes([{ | ||
path: 'home', | ||
loadComponent: () => import('./default_export_component'), | ||
}])], | ||
}); | ||
|
||
const root = TestBed.createComponent(RootCmp); | ||
await TestBed.inject(Router).navigateByUrl('/home'); | ||
|
||
expect(root.nativeElement.innerHTML).toContain('default exported'); | ||
}); | ||
|
||
it('should work for loadChildren', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering what the DX would look like in case of missing default export? Can we add a test to check that? I was thinking of a couple scenarios:
WDYT? (we can also do that in a followup PR if needed, it's not a blocker) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TypeScript will fail in both cases: If there are no exports, the TypeScript compiler will error with If there is no default export, it would fail the |
||
TestBed.configureTestingModule({ | ||
imports: [RouterTestingModule.withRoutes([{ | ||
path: 'home', | ||
loadChildren: () => import('./default_export_routes'), | ||
}])], | ||
}); | ||
|
||
const root = TestBed.createComponent(RootCmp); | ||
await TestBed.inject(Router).navigateByUrl('/home'); | ||
|
||
expect(root.nativeElement.innerHTML).toContain('default exported'); | ||
}); | ||
}); | ||
}); | ||
|
||
function advance(fixture: ComponentFixture<unknown>) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ɵDeprecatedLoadChildren
now appears in the public API even though it's not exported and is unused externally. What about moving theLoadChildren
type to its own file that we patch in g3 to addɵDeprecatedLoadChildren
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep - that's fine. The goal is to not need to touch the definition of
LoadChildren
at all in the patch file - that's what leads to brittle patch files.