UNPKG

angular-server-side-configuration

Version:
221 lines (216 loc) 8.2 kB
import { existsSync, readFileSync, writeFileSync, readdirSync, lstatSync } from 'fs'; import { dirname, join } from 'path'; // Source: https://github.com/fitzgen/glob-to-regexp/blob/master/index.js function globToRegExp(glob, opts) { if (typeof glob !== 'string') { throw new TypeError('Expected a string'); } var str = String(glob); // The regexp we are building, as a string. var reStr = ''; // Whether we are matching so called "extended" globs (like bash) and should // support single character matching, matching ranges of characters, group // matching, etc. var extended = opts ? !!opts.extended : false; // When globstar is _false_ (default), '/foo/*' is translated a regexp like // '^\/foo\/.*$' which will match any string beginning with '/foo/' // When globstar is _true_, '/foo/*' is translated to regexp like // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT // which does not have a '/' to the right of it. // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but // these will not '/foo/bar/baz', '/foo/bar/baz.txt' // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when // globstar is _false_ var globstar = opts ? !!opts.globstar : false; // If we are doing extended matching, this boolean is true when we are inside // a group (eg {*.html,*.js}), and false otherwise. var inGroup = false; // RegExp flags (eg "i" ) to pass in to RegExp constructor. var flags = opts && typeof opts.flags === 'string' ? opts.flags : ''; var c; for (var i = 0, len = str.length; i < len; i++) { c = str[i]; switch (c) { case '/': case '$': case '^': case '+': case '.': case '(': case ')': case '=': case '!': case '|': reStr += '\\' + c; break; // @ts-ignore case '?': if (extended) { reStr += '.'; break; } case '[': // @ts-ignore case ']': if (extended) { reStr += c; break; } // @ts-ignore case '{': if (extended) { inGroup = true; reStr += '('; break; } // @ts-ignore case '}': if (extended) { inGroup = false; reStr += ')'; break; } case ',': if (inGroup) { reStr += '|'; break; } reStr += '\\' + c; break; case '*': // Move over all consecutive "*"'s. // Also store the previous and next characters var prevChar = str[i - 1]; var starCount = 1; while (str[i + 1] === '*') { starCount++; i++; } var nextChar = str[i + 1]; if (!globstar) { // globstar is disabled, so treat any number of "*" as one reStr += '.*'; } else { // globstar is enabled, so determine if this is a globstar segment var isGlobstar = starCount > 1 && // multiple "*"'s (prevChar === '/' || prevChar === undefined) && // from the start of the segment (nextChar === '/' || nextChar === undefined); // to the end of the segment if (isGlobstar) { // it's a globstar, so match zero or more path segments reStr += '((?:[^/]*(?:/|$))*)'; i++; // move over the "/" } else { // it's not a globstar, so only match one path segment reStr += '([^/]*)'; } } break; default: reStr += c; } } // When regexp 'g' flag is specified don't // constrain the regular expression with ^ & $ if (!flags || !~flags.indexOf('g')) { reStr = '^' + reStr + '$'; } return new RegExp(reStr, flags); } function insert(options = {}) { const directory = options.directory || process.cwd(); if (options.recursive) { walk(directory, '**/ngssc.json') .map((f) => dirname(f)) .forEach((d) => insertWithNgssc(d, !!options.dryRun)); } else { insertWithNgssc(directory, !!options.dryRun); } } function insertWithNgssc(directory, dryRun) { const ngsscPath = join(directory, 'ngssc.json'); if (!existsSync(ngsscPath)) { throw new Error(`${ngsscPath} does not exist!`); } const ngssc = JSON.parse(readFileSync(ngsscPath, 'utf8')); const populatedVariables = populateVariables(ngssc.environmentVariables); log(`Populated environment variables (Variant: ${ngssc.variant}, ${ngsscPath})`); Object.keys(populatedVariables).forEach((k) => ` ${k}: ${populatedVariables[k]}`); const iife = generateIife(ngssc.variant, populatedVariables); const htmlPattern = ngssc.filePattern || 'index.html'; const htmlFiles = walk(directory, htmlPattern); if (!htmlFiles.length) { log(`No files found with pattern ${htmlPattern} in ${directory}`); return; } log(`Configuration will be inserted into ${htmlFiles.join(', ')}`); if (dryRun) { log('Dry run. Nothing will be inserted.'); } else { htmlFiles.forEach((f) => insertIntoHtml(f, iife)); } } function populateVariables(variables) { const populatedVariables = {}; variables.forEach((v) => (populatedVariables[v] = v in process.env ? process.env[v] || '' : null)); return populatedVariables; } function generateIife(variant, populatedVariables) { let iife; if (variant === 'NG_ENV') { iife = `(function(self){self.NG_ENV=${JSON.stringify(populatedVariables)};})(window)`; } else if (variant === 'global') { iife = `(function(self){Object.assign(self,${JSON.stringify(populatedVariables)});})(window)`; } else { iife = `(function(self){self.process=${JSON.stringify({ env: populatedVariables })};})(window)`; } return `<!--ngssc--><script>${iife}</script><!--/ngssc-->`; } function insertIntoHtml(file, iife) { const fileContent = readFileSync(file, 'utf8'); if (/<!--ngssc-->[\w\W]*<!--\/ngssc-->/.test(fileContent)) { writeFileSync(file, fileContent.replace(/<!--ngssc-->[\w\W]*<!--\/ngssc-->/, iife), 'utf8'); } else if (/<!--\s*CONFIG\s*-->/.test(fileContent)) { writeFileSync(file, fileContent.replace(/<!--\s*CONFIG\s*-->/, iife), 'utf8'); } else if (fileContent.includes('</title>')) { writeFileSync(file, fileContent.replace('</title>', `</title>${iife}`), 'utf8'); } else { writeFileSync(file, fileContent.replace('</head>', `${iife}</head>`), 'utf8'); } } function walk(root, filePattern) { const fileRegex = globToRegExp(filePattern, { extended: true, globstar: true, flags: 'ig' }); const directory = root.replace(/\\/g, '/'); return readdirSync(directory) .map((f) => `${directory}/${f}`) .map((f) => { const stat = lstatSync(f); if (stat.isDirectory()) { return walk(f, filePattern); } else if (stat.isFile() && fileRegex.test(f)) { return [f]; } else { return []; } }) .reduce((current, next) => current.concat(next), []); } function log(message) { // tslint:disable-next-line: no-console console.log(message); } /** * Generated bundle index. Do not edit. */ export { insert }; //# sourceMappingURL=angular-server-side-configuration.mjs.map