@web/polyfills-loader
Version:
Generate loader for loading browser polyfills based on feature detection
96 lines • 4.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.injectPolyfillsLoader = void 0;
const parse5_1 = require("parse5");
const parse5_utils_1 = require("@web/parse5-utils");
const createPolyfillsLoader_js_1 = require("./createPolyfillsLoader.js");
const utils_js_1 = require("./utils.js");
function injectImportMapPolyfill(headAst, originalScript, type) {
const systemJsScript = (0, parse5_utils_1.createScript)({ type }, (0, parse5_utils_1.getTextContent)(originalScript));
(0, parse5_utils_1.insertBefore)(headAst, systemJsScript, originalScript);
}
function findImportMapScripts(document) {
const scripts = (0, parse5_utils_1.findElements)(document, script => (0, parse5_utils_1.getAttribute)(script, 'type') === 'importmap');
const inline = [];
const external = [];
for (const script of scripts) {
if ((0, parse5_utils_1.getAttribute)(script, 'src')) {
external.push(script);
}
else {
inline.push(script);
}
}
return { inline, external };
}
function injectImportMapPolyfills(documentAst, headAst, cfg) {
const importMapScripts = findImportMapScripts(documentAst);
if (importMapScripts.external.length === 0 && importMapScripts.inline.length === 0) {
return;
}
const polyfillSystemJs = (0, utils_js_1.hasFileOfType)(cfg, utils_js_1.fileTypes.SYSTEMJS);
const importMaps = [...importMapScripts.external, ...importMapScripts.inline];
importMaps.forEach(originalScript => {
if (polyfillSystemJs) {
injectImportMapPolyfill(headAst, originalScript, 'systemjs-importmap');
}
});
}
function injectLoaderScript(bodyAst, polyfillsLoader, cfg) {
let loaderScript;
if (cfg.externalLoaderScript) {
const loaderScriptFile = polyfillsLoader.polyfillFiles.find(f => f.path.endsWith('loader.js'));
if (!loaderScriptFile) {
throw new Error('Missing polyfills loader script file');
}
loaderScript = (0, parse5_utils_1.createScript)({ src: loaderScriptFile.path });
}
else {
loaderScript = (0, parse5_utils_1.createScript)({}, polyfillsLoader.code);
}
(0, parse5_utils_1.appendChild)(bodyAst, loaderScript);
}
function injectPrefetchLinks(headAst, cfg) {
for (const file of cfg.modern.files) {
const { path } = file;
const href = path.startsWith('.') || path.startsWith('/') ? path : `./${path}`;
if (file.type === utils_js_1.fileTypes.MODULE) {
(0, parse5_utils_1.appendChild)(headAst, (0, parse5_utils_1.createElement)('link', {
rel: 'preload',
href,
as: 'script',
crossorigin: 'anonymous',
}));
}
else {
(0, parse5_utils_1.appendChild)(headAst, (0, parse5_utils_1.createElement)('link', { rel: 'preload', href, as: 'script' }));
}
}
}
/**
* Transforms an index.html file, injecting a polyfills loader for
* compatibility with older browsers.
*/
async function injectPolyfillsLoader(htmlString, cfg) {
const documentAst = (0, parse5_1.parse)(htmlString);
const headAst = (0, parse5_utils_1.findElement)(documentAst, e => (0, parse5_utils_1.getTagName)(e) === 'head');
const bodyAst = (0, parse5_utils_1.findElement)(documentAst, e => (0, parse5_utils_1.getTagName)(e) === 'body');
if (!headAst || !bodyAst) {
throw new Error(`Invalid index.html: missing <head> or <body>`);
}
const polyfillsLoader = await (0, createPolyfillsLoader_js_1.createPolyfillsLoader)(cfg);
if (polyfillsLoader === null) {
return { htmlString, polyfillFiles: [] };
}
if (cfg.preload) {
injectPrefetchLinks(headAst, cfg);
}
injectImportMapPolyfills(documentAst, headAst, cfg);
injectLoaderScript(bodyAst, polyfillsLoader, cfg);
return {
htmlString: (0, parse5_1.serialize)(documentAst),
polyfillFiles: polyfillsLoader.polyfillFiles,
};
}
exports.injectPolyfillsLoader = injectPolyfillsLoader;
//# sourceMappingURL=injectPolyfillsLoader.js.map