This is small script that can help generating routes manifest for @angular/service-worker
.
It is alternative way for ng-pwa-tools
to generate manifest without ever running angular application on the server.
It requires you to have installed ng-pwa-tools
and ng-router-resolver
libraries.
It is a drop-in replacement for command ngu-sw-manifest
.
NOTE: --out
argument should point to existing manifest json file.
routes.js
:
//@ts-check
const { NgRouterResolver } = require('ng-router-resolver');
const { writeFileSync, readFileSync } = require('fs');
const { genRoutingManifest } = require('./routes-2-sw-conf');
const modulePath = getArgument('--module');
const manifestPath = getArgument('--out');
const index = getArgument('--index', 'index.html');
const baseUrl = getArgument('--base-url', '/');
console.log(`Resolving routes from ${modulePath}...`);
const routes = NgRouterResolver.fromModule(modulePath);
console.log(`OK! Collected ${recursiveRouterLength(routes)} route rules`);
console.log('Generating manifest for routes');
const routesManifest = genRoutingManifest(index, routes, baseUrl);
console.log(`OK! Merging with manifest ${manifestPath}`);
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
mergeConfig(manifest, { routing: routesManifest });
console.log(`OK! Saving to ${manifestPath}`);
writeFileSync(manifestPath, JSON.stringify(manifest, null, ' '));
console.log('OK');
function recursiveRouterLength(arr) {
if (!Array.isArray(arr)) return 0;
return arr.length + arr.reduce((l, n) =>
l + recursiveRouterLength(n.children) + recursiveRouterLength(n.loadChildren), 0);
}
function getArgument(name, defaultValue, requiredMsg) {
const idx = process.argv.indexOf(name);
const found = idx !== -1;
if (!found && defaultValue === void 0) {
console.log(requiredMsg || `Argument ${name} is required`);
process.exit(1);
}
return found ? process.argv[idx + 1] : defaultValue;
}
function mergeConfig(mergeTo, mergeFrom) {
Object.keys(mergeFrom).forEach(function (key) {
var value = mergeFrom[key];
if (mergeTo[key]) {
if (Array.isArray(mergeTo[value])) {
if (Array.isArray(value)) {
mergeTo[key] = mergeTo[key].concat(value);
}
else {
mergeTo[key].push(value);
}
}
else if (typeof value === 'object') {
mergeTo[key] = mergeConfig(mergeTo[key], value);
}
else {
mergeTo[key] = value;
}
}
else {
mergeTo[key] = value;
}
});
}
routes-2-sw-conf.js
:
// @ts-check
const { coalesceToTerminals, matcherForTerminal } = require('ng-pwa-tools/lib/ls-routes/lib');
/**
* Convert routes json file to SW config
* @param {String} index
* @param {Array} routes
* @param {String=} baseUrl
*/
function genRoutingManifest(index, routes, baseUrl = '/') {
if (baseUrl.endsWith('/')) {
baseUrl = baseUrl.substr(0, baseUrl.length - 1);
}
const routesConfig = coalesceToTerminals(flattenRoutes(routes))
.map(terminal => matcherForTerminal(terminal, baseUrl))
.reduce(
(routes, matcher) => (routes[matcher.pattern] = { match: matcher.match }, routes),
{}
);
return ({ index: index, routes: routesConfig });
}
exports.genRoutingManifest = genRoutingManifest;
/**
* @param {Array} routes
* @param {String=} routes
*/
function flattenRoutes(routes, path = '') {
if (!routes) {
return [];
}
if (path.endsWith('/')) {
path = path.substr(0, path.length - 1);
}
return routes.reduce((acc, route) => {
const { children, loadChildren } = route;
delete route.children;
delete route.loadChildren;
if (path) {
route.path = path + '/' + route.path;
}
return [
...acc,
route,
...flattenRoutes(children, route.path),
...flattenRoutes(loadChildren, route.path)
];
}, []);
}