Skip to content
This repository has been archived by the owner on Nov 22, 2024. It is now read-only.

Commit

Permalink
feat(ls-routes): introduce ls-routes
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabian Wiles committed Aug 15, 2017
1 parent f7a4a49 commit 1eb506b
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 0 deletions.
5 changes: 5 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ npm run build:ng-module-map-ngfactory-loader

cp modules/ng-module-map-ngfactory-loader/package.json dist/ng-module-map-ngfactory-loader/package.json
cp modules/ng-module-map-ngfactory-loader/README.md dist/ng-module-map-ngfactory-loader/README.md

npm run build:ng-ls-routes

cp modules/ng-ls-routes/package.json dist/ng-ls-routes/package.json
cp modules/ng-ls-routes/README.md dist/ng-ls-routes/README.md
23 changes: 23 additions & 0 deletions modules/ng-ls-routes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
This is a tool which will gather the routes from a build factory bundle and return them ready to be used with stamping out prerendered index.html files

```js
import { lsRoutes } from '@nguniversal/ls-routes';

const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./main.a5d2e81ce51e0c3ba3c8.bundle.js')

lsRoutes(
'flatPaths',
{serverFactory: AppServerModuleNgFactory, lazyModuleMap: LAZY_MODULE_MAP}
).then(paths => {
paths.filter(path => !path.includes(':'))
.forEach(path => {
renderModuleFactory(AppServerModuleNgFactory, {
document: index,
url: path,
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP)
]
})
.then(html => fs.writeFileSync(`dist/${path.replace(/\//g, '-')}.index.html`, html))
})
})
1 change: 1 addition & 0 deletions modules/ng-ls-routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src';
26 changes: 26 additions & 0 deletions modules/ng-ls-routes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@nguniversal/ls-routes",
"main": "index.js",
"types": "index.d.ts",
"version": "1.0.0-beta.0",
"description": "A tool for retrieving routes from a factory",
"homepage": "https://github.com/angular/universal",
"license": "MIT",
"contributors": [
"Toxicable"
],
"repository": {
"type": "git",
"url": "https://github.com/angular/universal"
},
"bugs": {
"url": "https://github.com/angular/universal/issues"
},
"peerDependencies": {
"@angular/core": "^4.0.0",
"@angular/platform-server": "^4.0.0",
"@angular/router": "^4.0.0",
"@nguniversal/module-map-ngfactory-loader": "^1.0.0",
"zone.js": "^0.8.4"
}
}
1 change: 1 addition & 0 deletions modules/ng-ls-routes/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ls-routes';
109 changes: 109 additions & 0 deletions modules/ng-ls-routes/src/ls-routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import 'zone.js/dist/zone-node';
import { ReflectiveInjector, NgModuleFactoryLoader, NgModuleFactory, Injector, NgZone } from '@angular/core';
import { platformServer } from '@angular/platform-server';
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
import { ROUTES, Route } from '@angular/router';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/toPromise';

let loader: NgModuleFactoryLoader;

export function lsRoutes<T>(
returnType: 'flatPaths' | 'nestedPaths' | 'full',
factory: { serverFactory: NgModuleFactory<T>, lazyModuleMap?: any }
) {
const ngZone = new NgZone({ enableLongStackTrace: false });
const rootInjector = ReflectiveInjector.resolveAndCreate(
[
{ provide: NgZone, useValue: ngZone },
provideModuleMap(factory.lazyModuleMap)
],
platformServer().injector
);
const moduleRef = factory.serverFactory.create(rootInjector);
loader = moduleRef.injector.get(NgModuleFactoryLoader);
return Promise.all(createModule(factory.serverFactory, rootInjector))
.then(routes => {
if (returnType === 'full') {
return routes;
}
if (returnType === 'nestedPaths') {
return flattenRouteToPath(routes);
}
if (returnType === 'flatPaths') {
return flattenArray(flattenRouteToPath(routes));
}
throw new Error('you must provide a supported returnType');
});
}

function flattenArray<T, V>(array: T[] | T): V[] {
return !Array.isArray(array) ? array : [].concat.apply([], array.map(r => flattenArray(r)));
}

function flattenRouteToPath(routes: Route[]): (string[] | string)[] {
return routes.map(route => {
if (!route.children) {
return route.path ? '/' + route.path : '/';
} else {
return flattenRouteToPath(route.children)
.map((childRoute: string) => (!route.path ? '' : '/' + route.path) + childRoute);
}
});
}

function cocerceIntoPromise<T>(mightBePromise: Observable<T> | Promise<T> | T): Promise<T> {
if (mightBePromise instanceof Observable) {
return mightBePromise.toPromise();
}
return Promise.resolve(mightBePromise);
}

function extractRoute(route: Route, injector: Injector): Promise<Route> {
if (route.loadChildren) {
return resolveLazyChildren(route, injector);
}
if (route.children) {
return Promise.all(route.children.map(r => extractRoute(r, injector)))
.then(routes => {
route.children = routes;
return route;
});
}
return Promise.resolve(route);

}

function resolveLazyChildren(route: Route, injector: Injector): Promise<Route> {
let nextFactory: Promise<NgModuleFactory<any>>;
if (typeof route.loadChildren === 'function') {
nextFactory = cocerceIntoPromise<NgModuleFactory<any>>(
< NgModuleFactory<any> | Promise<NgModuleFactory<any>> >route.loadChildren()
)
} else {
nextFactory = loader.load(<string>route.loadChildren)
}
return nextFactory
.then(factory => Promise.all(createModule(factory, injector)))
.then(children => {
route.children = children;
delete route.loadChildren;
return route;
});
}

function createModule<T>(factory: NgModuleFactory<T>, parentInjector: Injector): Promise<Route>[] {

const moduleRef = factory.create(parentInjector);
const routes = moduleRef.injector.get(ROUTES);

return flattenArray<Route[][], Route>(routes)
.map(route => {
if (!route.loadChildren) {
//no lazy loaded paths so we can return the routes directly
return extractRoute(route, parentInjector);
} else {
return resolveLazyChildren(route, moduleRef.injector);
}
});
}
12 changes: 12 additions & 0 deletions modules/ng-ls-routes/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/ng-ls-routes"
},
"angularCompilerOptions": {
"genDir": "ngfactory"
},
"files": [
"index.ts"
]
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"build:ng-express-engine": "ngc -p modules/ng-express-engine/tsconfig.json",
"build:ng-aspnetcore-engine": "ngc -p modules/ng-aspnetcore-engine/tsconfig.json",
"build:ng-module-map-ngfactory-loader": "ngc -p modules/ng-module-map-ngfactory-loader/tsconfig.json",
"build:ng-ls-routes": "ngc -p modules/ng-ls-routes/tsconfig.json",
"build": "./build.sh",
"test": "exit 0"
},
Expand All @@ -71,6 +72,7 @@
"@angular/common": "^4.0.0",
"@angular/compiler": "^4.0.0",
"@angular/compiler-cli": "^4.0.0",
"@angular/router": "^4.0.0",
"@angular/core": "^4.0.0",
"@angular/http": "^4.0.0",
"@angular/platform-browser": "^4.0.0",
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"skipLibCheck": true,
"lib": [
"dom",
"es6"
Expand Down

0 comments on commit 1eb506b

Please sign in to comment.