Skip to content

Commit

Permalink
feat(@angular-devkit/build-angular): add type=module to all scripts…
Browse files Browse the repository at this point in the history
… tags

With this change we add `type=module` to all script tags. This is now possible since IE is no longer supported.

More information about modules can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
  • Loading branch information
alan-agius4 committed Aug 5, 2021
1 parent 48d4925 commit f53bf9d
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 256 deletions.
Expand Up @@ -172,13 +172,12 @@ export function buildWebpackBrowser(

return {
...(await initialize(options, context, transforms.webpackConfiguration)),
buildBrowserFeatures,
target,
};
}),
switchMap(
// eslint-disable-next-line max-lines-per-function
({ config, projectRoot, projectSourceRoot, i18n, buildBrowserFeatures, target }) => {
({ config, projectRoot, projectSourceRoot, i18n, target }) => {
const normalizedOptimization = normalizeOptimization(options.optimization);

return runWebpack(config, context, {
Expand All @@ -191,7 +190,6 @@ export function buildWebpackBrowser(
}
}),
}).pipe(
// eslint-disable-next-line max-lines-per-function
concatMap(async (buildEvent) => {
const spinner = new Spinner();
spinner.enabled = options.progress !== false;
Expand Down Expand Up @@ -227,8 +225,6 @@ export function buildWebpackBrowser(
} else {
outputPaths = ensureOutputPaths(baseOutputPath, i18n);

let moduleFiles: EmittedFiles[] | undefined;

const scriptsEntryPointName = normalizeExtraEntryPoints(
options.scripts || [],
'scripts',
Expand Down Expand Up @@ -320,8 +316,6 @@ export function buildWebpackBrowser(
lang: locale || undefined,
outputPath,
files: mapEmittedFilesToFileInfo(emittedFiles),
noModuleFiles: [],
moduleFiles: mapEmittedFilesToFileInfo(moduleFiles),
});

if (warnings.length || errors.length) {
Expand Down
Expand Up @@ -39,10 +39,10 @@ describe('Browser Builder crossOrigin', () => {
expect(content).toBe(
`<html><head><base href="/"><link rel="stylesheet" href="styles.css" crossorigin="use-credentials"></head>` +
`<body><app-root></app-root>` +
`<script src="runtime.js" crossorigin="use-credentials" defer></script>` +
`<script src="polyfills.js" crossorigin="use-credentials" defer></script>` +
`<script src="vendor.js" crossorigin="use-credentials" defer></script>` +
`<script src="main.js" crossorigin="use-credentials" defer></script></body></html>`,
`<script src="runtime.js" type="module" crossorigin="use-credentials"></script>` +
`<script src="polyfills.js" type="module" crossorigin="use-credentials"></script>` +
`<script src="vendor.js" type="module" crossorigin="use-credentials"></script>` +
`<script src="main.js" type="module" crossorigin="use-credentials"></script></body></html>`,
);
await run.stop();
});
Expand All @@ -58,10 +58,10 @@ describe('Browser Builder crossOrigin', () => {
`<html><head><base href="/">` +
`<link rel="stylesheet" href="styles.css" crossorigin="anonymous"></head>` +
`<body><app-root></app-root>` +
`<script src="runtime.js" crossorigin="anonymous" defer></script>` +
`<script src="polyfills.js" crossorigin="anonymous" defer></script>` +
`<script src="vendor.js" crossorigin="anonymous" defer></script>` +
`<script src="main.js" crossorigin="anonymous" defer></script></body></html>`,
`<script src="runtime.js" type="module" crossorigin="anonymous"></script>` +
`<script src="polyfills.js" type="module" crossorigin="anonymous"></script>` +
`<script src="vendor.js" type="module" crossorigin="anonymous"></script>` +
`<script src="main.js" type="module" crossorigin="anonymous"></script></body></html>`,
);
await run.stop();
});
Expand All @@ -77,10 +77,10 @@ describe('Browser Builder crossOrigin', () => {
`<html><head><base href="/">` +
`<link rel="stylesheet" href="styles.css"></head>` +
`<body><app-root></app-root>` +
`<script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script>` +
`<script src="main.js" defer></script></body></html>`,
`<script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script>` +
`<script src="main.js" type="module"></script></body></html>`,
);
await run.stop();
});
Expand Down
Expand Up @@ -36,9 +36,9 @@ describe('Browser Builder index HTML processing', () => {
const content = virtualFs.fileBufferToString(await host.read(normalize(fileName)).toPromise());
expect(content).toBe(
`<html><head><base href="/"><link rel="stylesheet" href="styles.css"></head>` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
await run.stop();
});
Expand All @@ -60,9 +60,9 @@ describe('Browser Builder index HTML processing', () => {
expect(content).toBe(
`<html><head><base href="/"><link rel="stylesheet" href="styles.css"></head>` +
`<body><app-root></app-root>` +
`<script src="runtime.js" defer></script><script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script>` +
`<script src="main.js" defer></script></body></html>`,
`<script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script>` +
`<script src="main.js" type="module"></script></body></html>`,
);
await run.stop();
});
Expand All @@ -82,9 +82,9 @@ describe('Browser Builder index HTML processing', () => {
const content = virtualFs.fileBufferToString(await host.read(normalize(fileName)).toPromise());
expect(content).toBe(
`<html><head><title>&iacute;</title><base href="/"><link rel="stylesheet" href="styles.css"></head> ` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
await run.stop();
});
Expand All @@ -104,9 +104,9 @@ describe('Browser Builder index HTML processing', () => {
const content = virtualFs.fileBufferToString(await host.read(normalize(fileName)).toPromise());
expect(content).toBe(
`<html><head><base href="/"><%= csrf_meta_tags %><link rel="stylesheet" href="styles.css"></head> ` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
await run.stop();
});
Expand Down Expand Up @@ -152,9 +152,9 @@ describe('Browser Builder index HTML processing', () => {
const content = await host.read(normalize(outputIndexPath)).toPromise();
expect(virtualFs.fileBufferToString(content)).toBe(
`<html><head><base href="/"><%= csrf_meta_tags %><link rel="stylesheet" href="styles.css"></head> ` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
});

Expand Down Expand Up @@ -198,9 +198,9 @@ describe('Browser Builder index HTML processing', () => {
const content = await host.read(normalize(outputIndexPath)).toPromise();
expect(virtualFs.fileBufferToString(content)).toBe(
`<html><head><base href="/"><link rel="stylesheet" href="styles.css"></head> ` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
});

Expand Down Expand Up @@ -244,9 +244,9 @@ describe('Browser Builder index HTML processing', () => {
const content = await host.read(normalize(outputIndexPath)).toPromise();
expect(virtualFs.fileBufferToString(content)).toBe(
`<html><head><base href="/"><link rel="stylesheet" href="styles.css"></head> ` +
`<body><app-root></app-root><script src="runtime.js" defer></script>` +
`<script src="polyfills.js" defer></script>` +
`<script src="vendor.js" defer></script><script src="main.js" defer></script></body></html>`,
`<body><app-root></app-root><script src="runtime.js" type="module"></script>` +
`<script src="polyfills.js" type="module"></script>` +
`<script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>`,
);
});
});
Expand Up @@ -53,12 +53,12 @@ describe('Browser Builder scripts array', () => {
'renamed-lazy-script.js': 'pre-rename-lazy-script',
'main.js': 'input-script',
'index.html':
'<script src="runtime.js" defer></script>' +
'<script src="polyfills.js" defer></script>' +
'<script src="runtime.js" type="module"></script>' +
'<script src="polyfills.js" type="module"></script>' +
'<script src="scripts.js" defer></script>' +
'<script src="renamed-script.js" defer></script>' +
'<script src="vendor.js" defer></script>' +
'<script src="main.js" defer></script>',
'<script src="vendor.js" type="module"></script>' +
'<script src="main.js" type="module"></script>',
};

host.writeMultipleFiles(scripts);
Expand Down
Expand Up @@ -105,7 +105,7 @@ describe('Browser Builder service worker', () => {
hashTable: {
'/favicon.ico': '84161b857f5c547e3699ddfbffc6d8d737542e01',
'/assets/folder-asset.txt': '617f202968a6a81050aa617c2e28e1dca11ce8d4',
'/index.html': 'f0bea8ced1dfbeeb771a5f48651fbcff52a625eb',
'/index.html': '8964a35a8b850942f8d18ba919f248762ff3154d',
'/spectrum.png': '8d048ece46c0f3af4b598a95fd8e4709b631c3c0',
},
}),
Expand Down Expand Up @@ -222,7 +222,7 @@ describe('Browser Builder service worker', () => {
hashTable: {
'/foo/bar/favicon.ico': '84161b857f5c547e3699ddfbffc6d8d737542e01',
'/foo/bar/assets/folder-asset.txt': '617f202968a6a81050aa617c2e28e1dca11ce8d4',
'/foo/bar/index.html': 'f6650ac91428c6933dfe4c24079b3b15400da1ba',
'/foo/bar/index.html': '5c99755c1e7cfd1c8aba34ad1155afc72a288fec',
},
}),
);
Expand Down
Expand Up @@ -305,17 +305,22 @@ export function serveWebpackBrowser(
switchMap(({ browserOptions, webpackConfig, locale }) => {
if (browserOptions.index) {
const { scripts = [], styles = [], baseHref } = browserOptions;
const entrypoints = generateEntryPoints({ scripts, styles });

webpackConfig.plugins = [...(webpackConfig.plugins || [])];
const entrypoints = generateEntryPoints({
scripts,
styles,
// The below is needed as otherwise HMR for CSS will break.
// styles.js and runtime.js needs to be loaded as a non-module scripts as otherwise `document.currentScript` will be null.
// https://github.com/webpack-contrib/mini-css-extract-plugin/blob/90445dd1d81da0c10b9b0e8a17b417d0651816b8/src/hmr/hotModuleReplacement.js#L39
isHMREnabled: webpackConfig.devServer?.hot,
});

webpackConfig.plugins ??= [];
webpackConfig.plugins.push(
new IndexHtmlWebpackPlugin({
indexPath: path.resolve(workspaceRoot, getIndexInputFile(browserOptions.index)),
outputPath: getIndexOutputFile(browserOptions.index),
baseHref,
entrypoints,
moduleEntrypoints: [],
noModuleEntrypoints: [],
deployUrl: browserOptions.deployUrl,
sri: browserOptions.subresourceIntegrity,
postTransform: transforms.indexHtml,
Expand Down
Expand Up @@ -13,6 +13,8 @@ export type LoadOutputFileFunctionType = (file: string) => Promise<string>;

export type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials';

export type Entrypoint = [name: string, isModule: boolean];

export interface AugmentIndexHtmlOptions {
/* Input contents */
html: string;
Expand All @@ -23,21 +25,16 @@ export interface AugmentIndexHtmlOptions {
crossOrigin?: CrossOriginValue;
/*
* Files emitted by the build.
* Js files will be added without 'nomodule' nor 'module'.
*/
files: FileInfo[];
/** Files that should be added using 'nomodule'. */
noModuleFiles?: FileInfo[];
/** Files that should be added using 'module'. */
moduleFiles?: FileInfo[];
/*
* Function that loads a file used.
* This allows us to use different routines within the IndexHtmlWebpackPlugin and
* when used without this plugin.
*/
loadOutputFile: LoadOutputFileFunctionType;
/** Used to sort the inseration of files in the HTML file */
entrypoints: string[];
entrypoints: Entrypoint[];
/** Used to set the document default locale */
lang?: string;
}
Expand All @@ -47,46 +44,34 @@ export interface FileInfo {
name: string;
extension: string;
}

/*
* Helper function used by the IndexHtmlWebpackPlugin.
* Can also be directly used by builder, e. g. in order to generate an index.html
* after processing several configurations in order to build different sets of
* bundles for differential serving.
*/
export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise<string> {
const {
loadOutputFile,
files,
noModuleFiles = [],
moduleFiles = [],
entrypoints,
sri,
deployUrl = '',
lang,
baseHref,
html,
} = params;
const { loadOutputFile, files, entrypoints, sri, deployUrl = '', lang, baseHref, html } = params;

let { crossOrigin = 'none' } = params;
if (sri && crossOrigin === 'none') {
crossOrigin = 'anonymous';
}

const stylesheets = new Set<string>();
const scripts = new Set<string>();
const scripts = new Map</** file name */ string, /** isModule */ boolean>();

// Sort files in the order we want to insert them by entrypoint and dedupes duplicates
const mergedFiles = [...moduleFiles, ...noModuleFiles, ...files];
for (const entrypoint of entrypoints) {
for (const { extension, file, name } of mergedFiles) {
if (name !== entrypoint) {
// 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':
scripts.add(file);
// 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);
Expand All @@ -96,52 +81,38 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise
}

let scriptTags: string[] = [];
for (const script of scripts) {
const attrs = [`src="${deployUrl}${script}"`];

if (crossOrigin !== 'none') {
attrs.push(`crossorigin="${crossOrigin}"`);
}
for (const [src, isModule] of scripts) {
const attrs = [`src="${deployUrl}${src}"`];

// We want to include nomodule or module when a file is not common amongs all
// such as runtime.js
const scriptPredictor = ({ file }: FileInfo): boolean => file === script;
if (!files.some(scriptPredictor)) {
// in some cases for differential loading file with the same name is available in both
// nomodule and module such as scripts.js
// we shall not add these attributes if that's the case
const isNoModuleType = noModuleFiles.some(scriptPredictor);
const isModuleType = moduleFiles.some(scriptPredictor);

if (isNoModuleType && !isModuleType) {
attrs.push('nomodule', 'defer');
} else if (isModuleType && !isNoModuleType) {
attrs.push('type="module"');
} else {
attrs.push('defer');
}
// 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(script);
const content = await loadOutputFile(src);
attrs.push(generateSriAttributes(content));
}

scriptTags.push(`<script ${attrs.join(' ')}></script>`);
}

let linkTags: string[] = [];
for (const stylesheet of stylesheets) {
const attrs = [`rel="stylesheet"`, `href="${deployUrl}${stylesheet}"`];
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(stylesheet);
const content = await loadOutputFile(src);
attrs.push(generateSriAttributes(content));
}

Expand Down

0 comments on commit f53bf9d

Please sign in to comment.