86 lines
3.4 KiB
JavaScript
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`);
|
||
|
}
|