in packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html.ts [54:197]
export async function augmentIndexHtml(
params: AugmentIndexHtmlOptions,
): Promise<{ content: string; warnings: string[]; errors: string[] }> {
const { loadOutputFile, files, entrypoints, sri, deployUrl = '', lang, baseHref, html } = params;
const warnings: string[] = [];
const errors: string[] = [];
let { crossOrigin = 'none' } = params;
if (sri && crossOrigin === 'none') {
crossOrigin = 'anonymous';
}
const stylesheets = new Set<string>();
const scripts = new Map</** file name */ string, /** isModule */ boolean>();
// Sort files in the order we want to insert them by entrypoint
for (const [entrypoint, isModule] of entrypoints) {
for (const { extension, file, name } of files) {
if (name !== entrypoint || scripts.has(file) || stylesheets.has(file)) {
continue;
}
switch (extension) {
case '.js':
// Also, non entrypoints need to be loaded as no module as they can contain problematic code.
scripts.set(file, isModule);
break;
case '.css':
stylesheets.add(file);
break;
}
}
}
let scriptTags: string[] = [];
for (const [src, isModule] of scripts) {
const attrs = [`src="${deployUrl}${src}"`];
// This is also need for non entry-points as they may contain problematic code.
if (isModule) {
attrs.push('type="module"');
} else {
attrs.push('defer');
}
if (crossOrigin !== 'none') {
attrs.push(`crossorigin="${crossOrigin}"`);
}
if (sri) {
const content = await loadOutputFile(src);
attrs.push(generateSriAttributes(content));
}
scriptTags.push(`<script ${attrs.join(' ')}></script>`);
}
let linkTags: string[] = [];
for (const src of stylesheets) {
const attrs = [`rel="stylesheet"`, `href="${deployUrl}${src}"`];
if (crossOrigin !== 'none') {
attrs.push(`crossorigin="${crossOrigin}"`);
}
if (sri) {
const content = await loadOutputFile(src);
attrs.push(generateSriAttributes(content));
}
linkTags.push(`<link ${attrs.join(' ')}>`);
}
const dir = lang ? await getLanguageDirection(lang, warnings) : undefined;
const { rewriter, transformedContent } = await htmlRewritingStream(html);
const baseTagExists = html.includes('<base');
rewriter
.on('startTag', (tag) => {
switch (tag.tagName) {
case 'html':
// Adjust document locale if specified
if (isString(lang)) {
updateAttribute(tag, 'lang', lang);
}
if (dir) {
updateAttribute(tag, 'dir', dir);
}
break;
case 'head':
// Base href should be added before any link, meta tags
if (!baseTagExists && isString(baseHref)) {
rewriter.emitStartTag(tag);
rewriter.emitRaw(`<base href="${baseHref}">`);
return;
}
break;
case 'base':
// Adjust base href if specified
if (isString(baseHref)) {
updateAttribute(tag, 'href', baseHref);
}
break;
}
rewriter.emitStartTag(tag);
})
.on('endTag', (tag) => {
switch (tag.tagName) {
case 'head':
for (const linkTag of linkTags) {
rewriter.emitRaw(linkTag);
}
linkTags = [];
break;
case 'body':
// Add script tags
for (const scriptTag of scriptTags) {
rewriter.emitRaw(scriptTag);
}
scriptTags = [];
break;
}
rewriter.emitEndTag(tag);
});
const content = await transformedContent;
return {
content:
linkTags.length || scriptTags.length
? // In case no body/head tags are not present (dotnet partial templates)
linkTags.join('') + scriptTags.join('') + content
: content,
warnings,
errors,
};
}