Skip to content

Instantly share code, notes, and snippets.

@gund
Last active June 20, 2017 08:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gund/651bdbe7339a40b94fab3c0e6a3b3fb2 to your computer and use it in GitHub Desktop.
Save gund/651bdbe7339a40b94fab3c0e6a3b3fb2 to your computer and use it in GitHub Desktop.
Generate routes manifest for @angular/service-worker

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)
    ];
  }, []);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment