angular-cn/aio/scripts/build-ngsw-config.js

86 lines
3.4 KiB
JavaScript

// Imports
const {basename, dirname, resolve: resolvePath} = require('canonical-path');
const {mkdirSync, readFileSync, writeFileSync} = require('fs');
const {parse: parseJson} = require('json5');
// Constants
const FIREBASE_CONFIG_PATH = resolvePath(__dirname, '../firebase.json');
const NGSW_CONFIG_TEMPLATE_PATH = resolvePath(__dirname, '../ngsw-config.template.json');
const NGSW_CONFIG_OUTPUT_PATH = resolvePath(__dirname, '../src/generated/ngsw-config.json');
// Run
_main();
// Helpers
function _main() {
const firebaseConfig = readJson(FIREBASE_CONFIG_PATH);
const ngswConfig = readJson(NGSW_CONFIG_TEMPLATE_PATH);
const firebaseRedirects = firebaseConfig.hosting.redirects;
// Check that there are no regex-based redirects.
const regexBasedRedirects = firebaseRedirects.filter(({regex}) => regex !== undefined);
if (regexBasedRedirects.length > 0) {
throw new Error(
'The following redirects use `regex`, which is currently not supported by ' +
`${basename(__filename)}:` +
regexBasedRedirects.map(x => `\n - ${JSON.stringify(x)}`).join(''));
}
// Check that there are no unsupported glob characters/patterns.
const redirectsWithUnsupportedGlobs = firebaseRedirects
.filter(({source}) => !/^(?:[/\w\-.*]|:\w+|@\([\w\-.|]+\))+$/.test(source));
if (redirectsWithUnsupportedGlobs.length > 0) {
throw new Error(
'The following redirects use glob characters/patterns that are currently not supported by ' +
`${basename(__filename)}:` +
redirectsWithUnsupportedGlobs.map(x => `\n - ${JSON.stringify(x)}`).join(''));
}
// Compute additional ignore glob patterns to be added to `navigationUrls`.
const additionalNavigationUrls = firebaseRedirects
// Grab the redirect source glob.
.map(({source}) => source)
// Ignore redirects for files (since these are already ignored by the SW).
.filter(glob => /\/[^/.]*$/.test(glob))
// Convert each Firebase-specific glob to a SW-compatible glob.
.map(glob => `!${glob.replace(/:\w+/g, '*').replace(/@(\([\w\-.|]+\))/g, '$1')}`)
// Add optional trailing `/` for globs that don't end with `*`.
.map(glob => /\w$/.test(glob) ? `${glob}\/{0,1}` : glob)
// Remove more specific globs that are covered by more generic ones.
.filter((glob, _i, globs) => !isGlobRedundant(glob, globs))
// Sort the generated globs alphabetically.
.sort();
// Add the additional `navigationUrls` globs and save the config.
ngswConfig.navigationUrls.push(...additionalNavigationUrls);
mkdirSync(dirname(NGSW_CONFIG_OUTPUT_PATH), {recursive: true});
writeJson(NGSW_CONFIG_OUTPUT_PATH, ngswConfig);
}
function isGlobRedundant(glob, globs) {
// Find all globs that could cover other globs.
// For simplicity, we only consider globs ending with `/**`.
const genericGlobs = globs.filter(g => g.endsWith('/**'));
// A glob is redundant if it starts with the path of a potentially generic glob (excluding the
// trailing `**`) followed by a word character or a `*`.
// For example, `/foo/bar/baz` is covered (and thus made redundant) by `/foo/**`, but `/foo/{0,1}`
// is not.
return genericGlobs.some(g => {
const pathPrefix = g.slice(0, -2);
return (glob !== g) && glob.startsWith(pathPrefix) &&
/^[\w*]/.test(glob.slice(pathPrefix.length));
});
}
function readJson(filePath) {
return parseJson(readFileSync(filePath, 'utf8'));
}
function writeJson(filePath, obj) {
return writeFileSync(filePath, `${JSON.stringify(obj, null, 2)}\n`);
}