feat(webpack): remove support for legacy browsers (#14190)
This commit is contained in:
parent
716ba89b15
commit
fcc02d1932
@ -37,7 +37,7 @@
|
||||
"default": false
|
||||
},
|
||||
"skipHelperLibs": {
|
||||
"description": "Do not install helpers libs (tslib, core-js, regenerator-runtime).",
|
||||
"description": "Do not install tslib.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
|
||||
@ -300,10 +300,6 @@
|
||||
"description": "Extract CSS into a `.css` file.",
|
||||
"default": true
|
||||
},
|
||||
"es2015Polyfills": {
|
||||
"description": "Conditional polyfills loaded in browsers which do not support `ES2015`.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresourceIntegrity": {
|
||||
"type": "boolean",
|
||||
"description": "Enables the use of subresource integrity validation.",
|
||||
|
||||
@ -307,10 +307,6 @@
|
||||
"description": "Extract CSS into a `.css` file.",
|
||||
"default": true
|
||||
},
|
||||
"es2015Polyfills": {
|
||||
"description": "Conditional polyfills loaded in browsers which do not support `ES2015`.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresourceIntegrity": {
|
||||
"type": "boolean",
|
||||
"description": "Enables the use of subresource integrity validation.",
|
||||
|
||||
@ -597,7 +597,6 @@ describe('Nx Affected and Graph Tests', () => {
|
||||
expect(() => checkFilesExist('project-graph.html')).not.toThrow();
|
||||
expect(() => checkFilesExist('static/styles.css')).not.toThrow();
|
||||
expect(() => checkFilesExist('static/runtime.js')).not.toThrow();
|
||||
expect(() => checkFilesExist('static/polyfills.js')).not.toThrow();
|
||||
expect(() => checkFilesExist('static/main.js')).not.toThrow();
|
||||
expect(() => checkFilesExist('static/environment.js')).not.toThrow();
|
||||
|
||||
|
||||
@ -26,10 +26,7 @@ describe('file-server', () => {
|
||||
const p = await runCommandUntil(
|
||||
`serve ${appName} --port=${port}`,
|
||||
(output) => {
|
||||
return (
|
||||
output.indexOf('webpack compiled') > -1 &&
|
||||
output.indexOf(`localhost:${port}`) > -1
|
||||
);
|
||||
return output.indexOf(`localhost:${port}`) > -1;
|
||||
}
|
||||
);
|
||||
|
||||
@ -39,5 +36,5 @@ describe('file-server', () => {
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, 1000000);
|
||||
}, 300_000);
|
||||
});
|
||||
|
||||
@ -112,23 +112,6 @@ describe('Web Components Applications', () => {
|
||||
checkFilesExist(`dist/libs/${libName}/_should_keep.txt`);
|
||||
}, 120000);
|
||||
|
||||
it('should do another build if differential loading is needed', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
updateFile(`apps/${appName}/browserslist`, `IE 9-11`);
|
||||
|
||||
runCLI(`build ${appName} --outputHashing=none`);
|
||||
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/main.js`,
|
||||
`dist/apps/${appName}/main.es5.js`
|
||||
);
|
||||
}, 120000);
|
||||
|
||||
it('should emit decorator metadata when it is enabled in tsconfig', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
|
||||
@ -222,7 +222,6 @@ function buildTargetWebpack(
|
||||
const defaultWebpack = getWebpackConfig(
|
||||
context,
|
||||
options,
|
||||
true,
|
||||
isScriptOptimizeOn,
|
||||
{
|
||||
root: ctProjectConfig.root,
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
import {
|
||||
ExecutorContext,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
ProjectGraph,
|
||||
readJsonFile,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { getBaseWebpackPartial } from '@nrwl/webpack/src/utils/config';
|
||||
@ -17,7 +13,6 @@ import { gte } from 'semver';
|
||||
import { Configuration, DefinePlugin, WebpackPluginInstance } from 'webpack';
|
||||
import * as mergeWebpack from 'webpack-merge';
|
||||
import { mergePlugins } from './merge-plugins';
|
||||
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||
|
||||
const reactWebpackConfig = require('../webpack');
|
||||
|
||||
@ -119,14 +114,12 @@ export const webpack = async (
|
||||
target: 'web',
|
||||
};
|
||||
|
||||
const esm = true;
|
||||
const isScriptOptimizeOn = storybookWebpackConfig.mode !== 'development';
|
||||
const extractCss = storybookWebpackConfig.mode === 'production';
|
||||
|
||||
// ESM build for modern browsers.
|
||||
const baseWebpackConfig = mergeWebpack.merge([
|
||||
getBaseWebpackPartial(builderOptions, {
|
||||
esm,
|
||||
isScriptOptimizeOn,
|
||||
skipTypeCheck: true,
|
||||
}),
|
||||
|
||||
@ -73,7 +73,6 @@ describe('app', () => {
|
||||
await applicationGenerator(appTree, schema);
|
||||
|
||||
expect(appTree.exists('apps/my-app/.babelrc')).toBeTruthy();
|
||||
expect(appTree.exists('apps/my-app/.browserslistrc')).toBeTruthy();
|
||||
expect(appTree.exists('apps/my-app/src/main.tsx')).toBeTruthy();
|
||||
expect(appTree.exists('apps/my-app/src/app/app.tsx')).toBeTruthy();
|
||||
expect(appTree.exists('apps/my-app/src/app/app.spec.tsx')).toBeTruthy();
|
||||
@ -312,7 +311,6 @@ describe('app', () => {
|
||||
main: 'apps/my-app/src/main.tsx',
|
||||
baseHref: '/',
|
||||
outputPath: 'dist/apps/my-app',
|
||||
polyfills: 'apps/my-app/src/polyfills.ts',
|
||||
scripts: [],
|
||||
styles: ['apps/my-app/src/styles.css'],
|
||||
tsConfig: 'apps/my-app/tsconfig.app.json',
|
||||
@ -810,19 +808,6 @@ describe('app', () => {
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add required polyfills for core-js and regenerator', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
});
|
||||
|
||||
const polyfillsSource = appTree
|
||||
.read('apps/my-app/src/polyfills.ts')
|
||||
.toString();
|
||||
|
||||
expect(polyfillsSource).toContain('regenerator');
|
||||
expect(polyfillsSource).toContain('core-js');
|
||||
});
|
||||
|
||||
describe('--skipWorkspaceJson', () => {
|
||||
it('should update workspace with defaults when --skipWorkspaceJson=false', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
# This file is used by:
|
||||
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
||||
# 2. babel preset-env to adjust included polyfills
|
||||
#
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# If you need to support different browsers in production, you may tweak the list below.
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
@ -57,10 +57,6 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
||||
options.appProjectRoot,
|
||||
maybeJs(options, `src/main.tsx`)
|
||||
),
|
||||
polyfills: joinPathFragments(
|
||||
options.appProjectRoot,
|
||||
maybeJs(options, 'src/polyfills.ts')
|
||||
),
|
||||
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||
assets: [
|
||||
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
|
||||
|
||||
@ -52,8 +52,6 @@ function updateDependencies(host: Tree, schema: InitSchema) {
|
||||
};
|
||||
|
||||
if (!schema.skipHelperLibs) {
|
||||
dependencies['core-js'] = '^3.6.5';
|
||||
dependencies['regenerator-runtime'] = '0.13.7';
|
||||
dependencies['tslib'] = tsLibVersion;
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
"default": false
|
||||
},
|
||||
"skipHelperLibs": {
|
||||
"description": "Do not install helpers libs (tslib, core-js, regenerator-runtime).",
|
||||
"description": "Do not install tslib.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
|
||||
@ -43,7 +43,6 @@ module.exports = function (api: any, options: NxWebBabelPresetOptions = {}) {
|
||||
? { targets: { node: 'current' }, loose: true }
|
||||
: {
|
||||
// Allow importing core-js in entrypoint and use browserlist to select polyfills.
|
||||
// This is needed for differential loading as well.
|
||||
useBuiltIns: options.useBuiltIns ?? 'entry',
|
||||
corejs: 3,
|
||||
// Do not transform modules to CJS
|
||||
|
||||
@ -176,10 +176,6 @@
|
||||
"description": "Extract CSS into a `.css` file.",
|
||||
"default": true
|
||||
},
|
||||
"es2015Polyfills": {
|
||||
"description": "Conditional polyfills loaded in browsers which do not support `ES2015`.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresourceIntegrity": {
|
||||
"type": "boolean",
|
||||
"description": "Enables the use of subresource integrity validation.",
|
||||
|
||||
@ -348,7 +348,6 @@ describe('app', () => {
|
||||
baseHref: '/',
|
||||
main: 'apps/my-app/src/main.ts',
|
||||
outputPath: 'dist/apps/my-app',
|
||||
polyfills: 'apps/my-app/src/polyfills.ts',
|
||||
scripts: [],
|
||||
styles: ['apps/my-app/src/styles.css'],
|
||||
tsConfig: 'apps/my-app/tsconfig.app.json',
|
||||
|
||||
@ -91,10 +91,6 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) {
|
||||
'src/index.html'
|
||||
);
|
||||
buildOptions.baseHref = '/';
|
||||
buildOptions.polyfills = joinPathFragments(
|
||||
options.appProjectRoot,
|
||||
'src/polyfills.ts'
|
||||
);
|
||||
buildOptions.styles = [
|
||||
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
|
||||
];
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
@ -7,5 +7,11 @@
|
||||
"factory": "./src/migrations/update-15-0-0/add-babel-inputs"
|
||||
}
|
||||
},
|
||||
"remove-es2015-polyfills-option": {
|
||||
"cli": "nx",
|
||||
"version": "15.4.5-beta.0",
|
||||
"description": "Removes es2015Polyfills option since legacy browsers are no longer supported.",
|
||||
"factory": "./src/migrations/update-15-4-5/remove-es2015-polyfills-option"
|
||||
},
|
||||
"packageJsonUpdates": {}
|
||||
}
|
||||
|
||||
@ -34,8 +34,6 @@
|
||||
"@nrwl/workspace": "file:../workspace",
|
||||
"autoprefixer": "^10.4.9",
|
||||
"babel-loader": "^8.2.2",
|
||||
"browserslist": "^4.21.4",
|
||||
"caniuse-lite": "^1.0.30001394",
|
||||
"chalk": "4.1.0",
|
||||
"chokidar": "^3.5.1",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
|
||||
@ -23,7 +23,6 @@ export function getDevServerConfig(
|
||||
const webpackConfig = getWebpackConfig(
|
||||
context,
|
||||
buildOptions,
|
||||
true,
|
||||
typeof buildOptions.optimization === 'boolean'
|
||||
? buildOptions.optimization
|
||||
: buildOptions.optimization?.scripts
|
||||
@ -53,7 +52,6 @@ export function getDevServerConfig(
|
||||
deployUrl,
|
||||
sri: subresourceIntegrity,
|
||||
moduleEntrypoints: [],
|
||||
noModuleEntrypoints: ['polyfills-es5'],
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import * as path from 'path';
|
||||
import { posix, resolve } from 'path';
|
||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||
import { ScriptTarget } from 'typescript';
|
||||
import { getHashDigest, interpolateName } from 'loader-utils';
|
||||
import type { Configuration } from 'webpack';
|
||||
|
||||
@ -34,7 +33,6 @@ interface GetWebpackConfigOverrides {
|
||||
export function getWebpackConfig(
|
||||
context: ExecutorContext,
|
||||
options: NormalizedWebpackExecutorOptions,
|
||||
esm?: boolean,
|
||||
isScriptOptimizeOn?: boolean,
|
||||
overrides?: GetWebpackConfigOverrides
|
||||
): Configuration {
|
||||
@ -53,19 +51,11 @@ export function getWebpackConfig(
|
||||
sourceRoot = project.sourceRoot;
|
||||
}
|
||||
|
||||
if (isScriptOptimizeOn) {
|
||||
// Angular CLI uses an environment variable (NG_BUILD_DIFFERENTIAL_FULL)
|
||||
// to determine whether to use the scriptTargetOverride
|
||||
// or the tsConfig target
|
||||
// We want to force the target if overridden
|
||||
tsConfig.options.target = ScriptTarget.ES5;
|
||||
}
|
||||
const wco: any = {
|
||||
root: workspaceRoot,
|
||||
projectRoot: resolve(workspaceRoot, projectRoot),
|
||||
sourceRoot: resolve(workspaceRoot, sourceRoot),
|
||||
buildOptions: convertBuildOptions(options),
|
||||
esm,
|
||||
console,
|
||||
tsConfig,
|
||||
tsConfigPath: options.tsConfig,
|
||||
@ -75,18 +65,12 @@ export function getWebpackConfig(
|
||||
_getBaseWebpackPartial(
|
||||
context,
|
||||
options,
|
||||
esm,
|
||||
isScriptOptimizeOn,
|
||||
tsConfig.options.emitDecoratorMetadata,
|
||||
overrides
|
||||
),
|
||||
options.target === 'web'
|
||||
? getPolyfillsPartial(
|
||||
options.polyfills,
|
||||
options.es2015Polyfills,
|
||||
esm,
|
||||
isScriptOptimizeOn
|
||||
)
|
||||
? getPolyfillsPartial(options.polyfills, isScriptOptimizeOn)
|
||||
: {},
|
||||
options.target === 'web'
|
||||
? getStylesPartial(
|
||||
@ -105,7 +89,6 @@ export function getWebpackConfig(
|
||||
function _getBaseWebpackPartial(
|
||||
context: ExecutorContext,
|
||||
options: NormalizedWebpackExecutorOptions,
|
||||
esm: boolean,
|
||||
isScriptOptimizeOn: boolean,
|
||||
emitDecoratorMetadata: boolean,
|
||||
overrides?: GetWebpackConfigOverrides
|
||||
@ -113,7 +96,6 @@ function _getBaseWebpackPartial(
|
||||
let partial = getBaseWebpackPartial(
|
||||
options,
|
||||
{
|
||||
esm,
|
||||
isScriptOptimizeOn,
|
||||
emitDecoratorMetadata,
|
||||
configuration: overrides?.configuration ?? context.configurationName,
|
||||
@ -271,33 +253,23 @@ export function getStylesPartial(
|
||||
|
||||
export function getPolyfillsPartial(
|
||||
polyfills: string,
|
||||
es2015Polyfills: string,
|
||||
esm: boolean,
|
||||
isScriptOptimizeOn: boolean
|
||||
): Configuration {
|
||||
const config = {
|
||||
entry: {} as { [key: string]: string[] },
|
||||
};
|
||||
|
||||
if (polyfills && esm && isScriptOptimizeOn) {
|
||||
if (polyfills && isScriptOptimizeOn) {
|
||||
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
|
||||
// Need to patch it up so the browser doesn't load both sets.
|
||||
config.entry.polyfills = [
|
||||
require.resolve('@nrwl/webpack/src/utils/webpack/safari-nomodule.js'),
|
||||
...(polyfills ? [polyfills] : []),
|
||||
];
|
||||
} else if (es2015Polyfills && !esm && isScriptOptimizeOn) {
|
||||
config.entry.polyfills = [
|
||||
es2015Polyfills,
|
||||
...(polyfills ? [polyfills] : []),
|
||||
];
|
||||
} else {
|
||||
if (polyfills) {
|
||||
config.entry.polyfills = [polyfills];
|
||||
}
|
||||
if (es2015Polyfills) {
|
||||
config.entry['polyfills-es5'] = [es2015Polyfills];
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
@ -5,8 +5,8 @@ import { normalizePath } from '@nrwl/devkit';
|
||||
import type {
|
||||
AssetGlobPattern,
|
||||
FileReplacement,
|
||||
WebpackExecutorOptions,
|
||||
NormalizedWebpackExecutorOptions,
|
||||
WebpackExecutorOptions,
|
||||
} from '../schema';
|
||||
|
||||
export function normalizeOptions(
|
||||
@ -34,11 +34,9 @@ export function normalizeOptions(
|
||||
}
|
||||
: options.optimization,
|
||||
polyfills: options.polyfills ? resolve(root, options.polyfills) : undefined,
|
||||
es2015Polyfills: options.es2015Polyfills
|
||||
? resolve(root, options.es2015Polyfills)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeFileReplacements(
|
||||
root: string,
|
||||
fileReplacements: FileReplacement[]
|
||||
|
||||
@ -47,7 +47,6 @@ export interface WebpackExecutorOptions {
|
||||
crossOrigin?: CrossOriginValue;
|
||||
deleteOutputPath?: boolean;
|
||||
deployUrl?: string;
|
||||
es2015Polyfills?: string;
|
||||
externalDependencies?: 'all' | 'none' | string[];
|
||||
extractCss?: boolean;
|
||||
extractLicenses?: boolean;
|
||||
|
||||
@ -228,10 +228,6 @@
|
||||
"description": "Extract CSS into a `.css` file.",
|
||||
"default": true
|
||||
},
|
||||
"es2015Polyfills": {
|
||||
"description": "Conditional polyfills loaded in browsers which do not support `ES2015`.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresourceIntegrity": {
|
||||
"type": "boolean",
|
||||
"description": "Enables the use of subresource integrity validation.",
|
||||
|
||||
@ -1,26 +1,18 @@
|
||||
import 'dotenv/config';
|
||||
import { ExecutorContext, logger } from '@nrwl/devkit';
|
||||
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
||||
import type { Configuration, Stats } from 'webpack';
|
||||
import { from, of } from 'rxjs';
|
||||
import {
|
||||
bufferCount,
|
||||
mergeMap,
|
||||
mergeScan,
|
||||
switchMap,
|
||||
tap,
|
||||
} from 'rxjs/operators';
|
||||
import type { Configuration } from 'webpack';
|
||||
import { of } from 'rxjs';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import { basename, join, resolve } from 'path';
|
||||
import {
|
||||
calculateProjectDependencies,
|
||||
createTmpTsConfig,
|
||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||
|
||||
import { getWebpackConfig } from './lib/get-webpack-config';
|
||||
import { getEmittedFiles } from './lib/get-emitted-files';
|
||||
import { runWebpack } from './lib/run-webpack';
|
||||
import { BuildBrowserFeatures } from '../../utils/webpack/build-browser-features';
|
||||
import { deleteOutputDir } from '../../utils/fs';
|
||||
import { writeIndexHtml } from '../../utils/webpack/write-index-html';
|
||||
import { resolveCustomWebpackConfig } from '../../utils/webpack/custom-webpack';
|
||||
@ -34,7 +26,7 @@ import { EmittedFile } from '../../utils/models';
|
||||
async function getWebpackConfigs(
|
||||
options: NormalizedWebpackExecutorOptions,
|
||||
context: ExecutorContext
|
||||
): Promise<Configuration[]> {
|
||||
): Promise<Configuration> {
|
||||
const metadata = context.projectsConfigurations.projects[context.projectName];
|
||||
const projectRoot = metadata.root;
|
||||
const isScriptOptimizeOn =
|
||||
@ -43,13 +35,6 @@ async function getWebpackConfigs(
|
||||
: options.optimization && options.optimization.scripts
|
||||
? options.optimization.scripts
|
||||
: false;
|
||||
const tsConfig = readTsConfig(options.tsConfig);
|
||||
const scriptTarget = tsConfig.options.target;
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
projectRoot,
|
||||
scriptTarget
|
||||
);
|
||||
|
||||
let customWebpack = null;
|
||||
|
||||
@ -64,19 +49,8 @@ async function getWebpackConfigs(
|
||||
}
|
||||
}
|
||||
|
||||
return await Promise.all(
|
||||
[
|
||||
// ESM build for modern browsers.
|
||||
getWebpackConfig(context, options, true, isScriptOptimizeOn),
|
||||
// ES5 build for legacy browsers.
|
||||
options.target === 'web' &&
|
||||
isScriptOptimizeOn &&
|
||||
buildBrowserFeatures.isDifferentialLoadingNeeded()
|
||||
? getWebpackConfig(context, options, false, isScriptOptimizeOn)
|
||||
: undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map(async (config) => {
|
||||
const config = getWebpackConfig(context, options, isScriptOptimizeOn);
|
||||
|
||||
if (customWebpack) {
|
||||
return await customWebpack(config, {
|
||||
options,
|
||||
@ -86,8 +60,6 @@ async function getWebpackConfigs(
|
||||
} else {
|
||||
return config;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export type WebpackExecutorEvent =
|
||||
@ -162,40 +134,26 @@ export async function* webpackExecutor(
|
||||
|
||||
const configs = await getWebpackConfigs(options, context);
|
||||
return yield* eachValueFrom(
|
||||
from(configs).pipe(
|
||||
mergeMap((config) => (Array.isArray(config) ? from(config) : of(config))),
|
||||
// Run build sequentially and bail when first one fails.
|
||||
mergeScan(
|
||||
(acc, config) => {
|
||||
if (!acc.hasErrors()) {
|
||||
of(configs).pipe(
|
||||
switchMap((config) => {
|
||||
return runWebpack(config).pipe(
|
||||
tap((stats) => {
|
||||
console.info(stats.toString(config.stats));
|
||||
console.info(stats.toString());
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of();
|
||||
}
|
||||
},
|
||||
{ hasErrors: () => false } as Stats,
|
||||
1
|
||||
),
|
||||
// Collect build results as an array.
|
||||
bufferCount(configs.length),
|
||||
switchMap(async ([result1, result2]) => {
|
||||
const success =
|
||||
result1 && !result1.hasErrors() && (!result2 || !result2.hasErrors());
|
||||
const emittedFiles1 = getEmittedFiles(result1);
|
||||
const emittedFiles2 = result2 ? getEmittedFiles(result2) : [];
|
||||
}),
|
||||
switchMap(async (result) => {
|
||||
const success = result && !result.hasErrors();
|
||||
const emittedFiles = getEmittedFiles(result);
|
||||
if (options.index && options.generateIndexHtml) {
|
||||
await writeIndexHtml({
|
||||
crossOrigin: options.crossOrigin,
|
||||
sri: options.subresourceIntegrity,
|
||||
outputPath: join(options.outputPath, basename(options.index)),
|
||||
indexPath: join(context.root, options.index),
|
||||
files: emittedFiles1.filter((x) => x.extension === '.css'),
|
||||
noModuleFiles: emittedFiles2,
|
||||
moduleFiles: emittedFiles1,
|
||||
files: emittedFiles.filter((x) => x.extension === '.css'),
|
||||
noModuleFiles: [],
|
||||
moduleFiles: emittedFiles,
|
||||
baseHref: options.baseHref,
|
||||
deployUrl: options.deployUrl,
|
||||
scripts: options.scripts,
|
||||
@ -209,7 +167,7 @@ export async function* webpackExecutor(
|
||||
options.outputPath,
|
||||
options.outputFileName
|
||||
),
|
||||
emittedFiles: [...emittedFiles1, ...emittedFiles2],
|
||||
emittedFiles,
|
||||
options,
|
||||
};
|
||||
})
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nrwl/devkit';
|
||||
import update from './remove-es2015-polyfills-option';
|
||||
|
||||
describe('15.4.5 migration (remove es2015-polyfills)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should update all executors using @nrwl/webpack:webpack and es2015Polyfills option', async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'app1',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
options: {
|
||||
es2015Polyfills: 'app1/polyfills.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'app2', {
|
||||
root: 'app2',
|
||||
targets: {
|
||||
custom: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
options: {
|
||||
es2016Polyfills: 'app2/polyfills.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'app3', {
|
||||
root: 'app3',
|
||||
targets: {
|
||||
custom: {
|
||||
executor: '@foo/bar:faz',
|
||||
options: {
|
||||
es2015Polyfills: 'app3/polyfills.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'app1').targets.build.options
|
||||
.es2015Polyfills
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'app2').targets.custom.options
|
||||
.es2015Polyfills
|
||||
).toBeUndefined();
|
||||
// Another executor, left intact.
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'app3').targets.custom.options
|
||||
.es2015Polyfills
|
||||
).toEqual('app3/polyfills.ts');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
import { getProjects, Tree, updateProjectConfiguration } from '@nrwl/devkit';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
projects.forEach((p) => {
|
||||
let shouldUpdate = false;
|
||||
|
||||
Object.entries(p.targets).forEach(([name, config]) => {
|
||||
if (
|
||||
p.targets?.[name]?.executor === '@nrwl/webpack:webpack' &&
|
||||
p.targets?.[name]?.options.es2015Polyfills
|
||||
) {
|
||||
delete p.targets?.[name]?.options.es2015Polyfills;
|
||||
shouldUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldUpdate) {
|
||||
updateProjectConfiguration(tree, p.name, p);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -23,7 +23,6 @@ const IGNORED_WEBPACK_WARNINGS = [
|
||||
];
|
||||
|
||||
export interface InternalBuildOptions {
|
||||
esm?: boolean;
|
||||
isScriptOptimizeOn?: boolean;
|
||||
emitDecoratorMetadata?: boolean;
|
||||
configuration?: string;
|
||||
@ -40,18 +39,13 @@ export function getBaseWebpackPartial(
|
||||
// If the function is called directly and not through `@nrwl/webpack:webpack` then this target may not be set.
|
||||
options.target ??= 'web';
|
||||
|
||||
const mainFields = [
|
||||
...(internalOptions.esm ? ['es2015'] : []),
|
||||
'module',
|
||||
'main',
|
||||
];
|
||||
const mainFields = ['es2015', 'module', 'main'];
|
||||
const hashFormat = getOutputHashFormat(options.outputHashing);
|
||||
const suffixFormat = internalOptions.esm ? '' : '.es5';
|
||||
const filename = internalOptions.isScriptOptimizeOn
|
||||
? `[name]${hashFormat.script}${suffixFormat}.js`
|
||||
? `[name]${hashFormat.script}.js`
|
||||
: '[name].js';
|
||||
const chunkFilename = internalOptions.isScriptOptimizeOn
|
||||
? `[name]${hashFormat.chunk}${suffixFormat}.js`
|
||||
? `[name]${hashFormat.chunk}.js`
|
||||
: '[name].js';
|
||||
const mode = internalOptions.isScriptOptimizeOn
|
||||
? 'production'
|
||||
@ -90,7 +84,7 @@ export function getBaseWebpackPartial(
|
||||
hashFunction: 'xxhash64',
|
||||
// Disabled for performance
|
||||
pathinfo: false,
|
||||
scriptType: internalOptions.esm ? 'module' : undefined,
|
||||
scriptType: 'module',
|
||||
},
|
||||
module: {
|
||||
// Enabled for performance
|
||||
@ -180,14 +174,21 @@ export function getBaseWebpackPartial(
|
||||
webpackConfig.plugins.push(
|
||||
new webpack.DefinePlugin(getClientEnvironment(mode).stringified)
|
||||
);
|
||||
if (options.compiler !== 'swc' && internalOptions.isScriptOptimizeOn) {
|
||||
webpackConfig.optimization ??= {};
|
||||
webpackConfig.optimization.nodeEnv = process.env.NODE_ENV ?? mode;
|
||||
|
||||
if (internalOptions.isScriptOptimizeOn) {
|
||||
// Always check sideEffects field in package.json for tree-shaking to work.
|
||||
webpackConfig.optimization.sideEffects = true;
|
||||
|
||||
if (options.compiler !== 'swc') {
|
||||
webpackConfig.optimization = {
|
||||
sideEffects: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
parallel: true,
|
||||
terserOptions: {
|
||||
ecma: (internalOptions.esm ? 2016 : 5) as TerserPlugin.TerserECMA,
|
||||
ecma: 2020,
|
||||
safari10: true,
|
||||
output: {
|
||||
ascii_only: true,
|
||||
@ -200,13 +201,12 @@ export function getBaseWebpackPartial(
|
||||
runtimeChunk: true,
|
||||
};
|
||||
}
|
||||
webpackConfig.optimization ??= {};
|
||||
webpackConfig.optimization.nodeEnv = process.env.NODE_ENV ?? mode;
|
||||
}
|
||||
}
|
||||
|
||||
const extraPlugins: WebpackPluginInstance[] = [];
|
||||
|
||||
if (!internalOptions.skipTypeCheck && internalOptions.esm) {
|
||||
if (!internalOptions.skipTypeCheck) {
|
||||
extraPlugins.push(
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
@ -412,7 +412,7 @@ export function createLoaderFromCompiler(
|
||||
rootMode: 'upward',
|
||||
cwd: join(options.root, options.sourceRoot),
|
||||
emitDecoratorMetadata: extraOptions.emitDecoratorMetadata,
|
||||
isModern: extraOptions.esm,
|
||||
isModern: true,
|
||||
envName: extraOptions.isScriptOptimizeOn
|
||||
? 'production'
|
||||
: extraOptions.configuration,
|
||||
|
||||
@ -25,5 +25,4 @@ export interface CreateWebpackConfigOptions<T = any> {
|
||||
buildOptions: T;
|
||||
tsConfig: any;
|
||||
tsConfigPath: string;
|
||||
supportES2015: boolean;
|
||||
}
|
||||
|
||||
@ -1,161 +0,0 @@
|
||||
import { fs, vol } from 'memfs';
|
||||
jest.mock('fs', () => fs);
|
||||
import { ScriptTarget } from 'typescript';
|
||||
|
||||
// Disable browserslist cache so that each test resolves a new config.
|
||||
process.env.BROWSERSLIST_DISABLE_CACHE = 'true';
|
||||
|
||||
import { BuildBrowserFeatures } from './build-browser-features';
|
||||
|
||||
describe('BuildBrowserFeatures', () => {
|
||||
beforeEach(async () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'.browserslistrc': '',
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
});
|
||||
|
||||
describe('isDifferentialLoadingNeeded', () => {
|
||||
it('should be true for for IE 9-11 and ES2015', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'IE 9-11');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false for Chrome and ES2015', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'last 1 chrome version');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('detects no need for differential loading for target is ES5', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'last 1 chrome version');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES5
|
||||
);
|
||||
expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false for Safari 10.1 when target is ES2015', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari 10.1');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFeatureSupported', () => {
|
||||
it('should be true for es6-module and Safari 10.1', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari 10.1');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false for es6-module and IE9', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'IE 9');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(false);
|
||||
});
|
||||
|
||||
it('should be true for es6-module and last 1 chrome version', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'last 1 chrome version');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be true for es6-module and Edge 18', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Edge 18');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNoModulePolyfillNeeded', () => {
|
||||
it('should be false for Safari 10.1 when target is ES5', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari 10.1');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES5
|
||||
);
|
||||
expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false for Safari 10.1 when target is ES2015', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari 10.1');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be true for Safari 9+ when target is ES2015', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari >= 9');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false for Safari 9+ when target is ES5', () => {
|
||||
fs.writeFileSync('/root/.browserslistrc', 'Safari >= 9');
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES5
|
||||
);
|
||||
expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false when not supporting Safari 10.1 target is ES2015', () => {
|
||||
fs.writeFileSync(
|
||||
'/root/.browserslistrc',
|
||||
`
|
||||
Edge 18
|
||||
IE 9
|
||||
`
|
||||
);
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
'/root',
|
||||
ScriptTarget.ES2015
|
||||
);
|
||||
expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,78 +0,0 @@
|
||||
import * as browserslist from 'browserslist';
|
||||
import { feature, features } from 'caniuse-lite';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export class BuildBrowserFeatures {
|
||||
private readonly _supportedBrowsers: string[];
|
||||
private readonly _es6TargetOrLater: boolean;
|
||||
|
||||
constructor(
|
||||
private projectRoot: string,
|
||||
private scriptTarget: ts.ScriptTarget
|
||||
) {
|
||||
this._supportedBrowsers = browserslist(undefined, {
|
||||
path: this.projectRoot,
|
||||
});
|
||||
this._es6TargetOrLater = this.scriptTarget > ts.ScriptTarget.ES5;
|
||||
}
|
||||
|
||||
/**
|
||||
* True, when one or more browsers requires ES5
|
||||
* support and the script target is ES2015 or greater.
|
||||
*/
|
||||
isDifferentialLoadingNeeded(): boolean {
|
||||
return this._es6TargetOrLater && this.isEs5SupportNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* True, when one or more browsers requires ES5 support
|
||||
*/
|
||||
isEs5SupportNeeded(): boolean {
|
||||
return !this.isFeatureSupported('es6-module');
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari 10.1 and iOS Safari 10.3 supports modules,
|
||||
* but does not support the `nomodule` attribute.
|
||||
* While return `true`, when support for Safari 10.1 and iOS Safari 10.3
|
||||
* is required and in differential loading is enabled.
|
||||
*/
|
||||
isNoModulePolyfillNeeded(): boolean {
|
||||
if (!this.isDifferentialLoadingNeeded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const safariBrowsers = ['safari 10.1', 'ios_saf 10.3'];
|
||||
|
||||
return this._supportedBrowsers.some((browser) =>
|
||||
safariBrowsers.includes(browser)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* True, when a browser feature is supported partially or fully.
|
||||
*/
|
||||
isFeatureSupported(featureId: string): boolean {
|
||||
// y: feature is fully available
|
||||
// n: feature is unavailable
|
||||
// a: feature is partially supported
|
||||
// x: feature is prefixed
|
||||
const criteria = ['y', 'a'];
|
||||
|
||||
const data = feature(features[featureId]);
|
||||
|
||||
return !this._supportedBrowsers.some((browser) => {
|
||||
const [agentId, version] = browser.split(' ');
|
||||
|
||||
const browserData = data.stats[agentId];
|
||||
const featureStatus = (browserData && browserData[version]) as
|
||||
| string
|
||||
| undefined;
|
||||
|
||||
// We are only interested in the first character
|
||||
// Ex: when 'a #4 #5', we only need to check for 'a'
|
||||
// as for such cases we should polyfill these features as needed
|
||||
return !featureStatus || !criteria.includes(featureStatus.charAt(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -22,9 +22,7 @@ export function generateEntryPoints(appConfig: {
|
||||
};
|
||||
|
||||
const entryPoints = [
|
||||
'polyfills-nomodule-es5',
|
||||
'runtime',
|
||||
'polyfills-es5',
|
||||
'polyfills',
|
||||
'sw-register',
|
||||
...extraEntryPoints(appConfig.styles, 'styles'),
|
||||
|
||||
@ -5,7 +5,7 @@ import { CreateWebpackConfigOptions } from '../../models';
|
||||
import CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
|
||||
export function getBrowserConfig(wco: CreateWebpackConfigOptions) {
|
||||
const { buildOptions, supportES2015 } = wco;
|
||||
const { buildOptions } = wco;
|
||||
const extraPlugins = [];
|
||||
|
||||
const stylesOptimization =
|
||||
@ -41,12 +41,7 @@ export function getBrowserConfig(wco: CreateWebpackConfigOptions) {
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
mainFields: [
|
||||
...(supportES2015 ? ['es2015'] : []),
|
||||
'browser',
|
||||
'module',
|
||||
'main',
|
||||
],
|
||||
mainFields: ['browser', 'module', 'main'],
|
||||
},
|
||||
output: {
|
||||
crossOriginLoading: buildOptions.subresourceIntegrity
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
import { basename, join, resolve } from 'path';
|
||||
import { basename, resolve } from 'path';
|
||||
import type { Compiler, Configuration } from 'webpack';
|
||||
import { ids, ProgressPlugin, sources } from 'webpack';
|
||||
import { ScriptTarget } from 'typescript';
|
||||
import { ProgressPlugin, sources } from 'webpack';
|
||||
|
||||
import { normalizeExtraEntryPoints } from '../normalize-entry';
|
||||
import { ScriptsWebpackPlugin } from '../plugins/scripts-webpack-plugin';
|
||||
import { BuildBrowserFeatures } from '../build-browser-features';
|
||||
import { getOutputHashFormat } from '../../hash-format';
|
||||
import { findAllNodeModules, findUp } from '../../fs';
|
||||
import type { CreateWebpackConfigOptions, ExtraEntryPoint } from '../../models';
|
||||
import type { CreateWebpackConfigOptions } from '../../models';
|
||||
|
||||
export function getCommonConfig(
|
||||
wco: CreateWebpackConfigOptions
|
||||
): Configuration {
|
||||
const { root, projectRoot, sourceRoot, buildOptions, tsConfig } = wco;
|
||||
const { root, projectRoot, sourceRoot, buildOptions } = wco;
|
||||
|
||||
let stylesOptimization: boolean;
|
||||
let scriptsOptimization: boolean;
|
||||
@ -37,48 +35,6 @@ export function getCommonConfig(
|
||||
entryPoints['main'] = [resolve(root, buildOptions.main)];
|
||||
}
|
||||
|
||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||
projectRoot,
|
||||
tsConfig.options.target || ScriptTarget.ES5
|
||||
);
|
||||
|
||||
const differentialLoadingNeeded =
|
||||
buildBrowserFeatures.isDifferentialLoadingNeeded();
|
||||
|
||||
if (tsConfig.options.target === ScriptTarget.ES5) {
|
||||
if (buildBrowserFeatures.isEs5SupportNeeded()) {
|
||||
// The nomodule polyfill needs to be inject prior to any script and be
|
||||
// outside of webpack compilation because otherwise webpack will cause the
|
||||
// script to be wrapped in window["webpackJsonp"] which causes this to fail.
|
||||
if (buildBrowserFeatures.isNoModulePolyfillNeeded()) {
|
||||
const noModuleScript: ExtraEntryPoint = {
|
||||
bundleName: 'polyfills-nomodule-es5',
|
||||
input: join(__dirname, '..', 'safari-nomodule.js'),
|
||||
};
|
||||
buildOptions.scripts = buildOptions.scripts
|
||||
? [...buildOptions.scripts, noModuleScript]
|
||||
: [noModuleScript];
|
||||
}
|
||||
|
||||
// For full build differential loading we don't need to generate a seperate polyfill file
|
||||
// because they will be loaded exclusivly based on module and nomodule
|
||||
const polyfillsChunkName = differentialLoadingNeeded
|
||||
? 'polyfills'
|
||||
: 'polyfills-es5';
|
||||
|
||||
entryPoints[polyfillsChunkName] = [
|
||||
join(__dirname, '..', 'es5-polyfills.js'),
|
||||
];
|
||||
|
||||
// If not performing a full differential build the polyfills need to be added to ES5 bundle
|
||||
if (buildOptions.polyfills) {
|
||||
entryPoints[polyfillsChunkName].push(
|
||||
resolve(root, buildOptions.polyfills)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buildOptions.polyfills) {
|
||||
entryPoints['polyfills'] = [
|
||||
...(entryPoints['polyfills'] || []),
|
||||
@ -179,26 +135,12 @@ export function getCommonConfig(
|
||||
const loaderNodeModules = findAllNodeModules(__dirname, projectRoot);
|
||||
loaderNodeModules.unshift('node_modules');
|
||||
|
||||
// Load rxjs path aliases.
|
||||
// https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#build-and-treeshaking
|
||||
let alias = {};
|
||||
try {
|
||||
const rxjsPathMappingImport = wco.supportES2015
|
||||
? 'rxjs/_esm2015/path-mapping'
|
||||
: 'rxjs/_esm5/path-mapping';
|
||||
const rxPaths = require(require.resolve(rxjsPathMappingImport, {
|
||||
paths: [projectRoot],
|
||||
}));
|
||||
alias = rxPaths(nodeModules);
|
||||
} catch {}
|
||||
|
||||
return {
|
||||
profile: buildOptions.statsJson,
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.mjs', '.js'],
|
||||
symlinks: true,
|
||||
modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'],
|
||||
alias,
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: loaderNodeModules,
|
||||
|
||||
@ -21,7 +21,6 @@ export interface IndexHtmlGeneratorProcessOptions {
|
||||
baseHref?: string | undefined;
|
||||
outputPath: string;
|
||||
files: FileInfo[];
|
||||
noModuleFiles: FileInfo[];
|
||||
moduleFiles: FileInfo[];
|
||||
}
|
||||
|
||||
@ -111,14 +110,7 @@ function augmentIndexHtmlPlugin(
|
||||
} = generator.options;
|
||||
|
||||
return async (html, options) => {
|
||||
const {
|
||||
lang,
|
||||
baseHref,
|
||||
outputPath = '',
|
||||
noModuleFiles,
|
||||
files,
|
||||
moduleFiles,
|
||||
} = options;
|
||||
const { lang, baseHref, outputPath = '', files, moduleFiles } = options;
|
||||
|
||||
return augmentIndexHtml({
|
||||
html,
|
||||
@ -130,7 +122,6 @@ function augmentIndexHtmlPlugin(
|
||||
entrypoints,
|
||||
loadOutputFile: (filePath) =>
|
||||
generator.readAsset(join(outputPath, filePath)),
|
||||
noModuleFiles,
|
||||
moduleFiles,
|
||||
files,
|
||||
});
|
||||
|
||||
@ -13,7 +13,6 @@ export interface IndexHtmlWebpackPluginOptions
|
||||
IndexHtmlGeneratorProcessOptions,
|
||||
'files' | 'noModuleFiles' | 'moduleFiles'
|
||||
> {
|
||||
noModuleEntrypoints: string[];
|
||||
moduleEntrypoints: string[];
|
||||
}
|
||||
|
||||
@ -59,7 +58,6 @@ export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
|
||||
const callback = async (assets: Record<string, unknown>) => {
|
||||
// Get all files for selected entrypoints
|
||||
const files: FileInfo[] = [];
|
||||
const noModuleFiles: FileInfo[] = [];
|
||||
const moduleFiles: FileInfo[] = [];
|
||||
|
||||
try {
|
||||
@ -79,9 +77,7 @@ export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.options.noModuleEntrypoints.includes(entryName)) {
|
||||
noModuleFiles.push(...entryFiles);
|
||||
} else if (this.options.moduleEntrypoints.includes(entryName)) {
|
||||
if (this.options.moduleEntrypoints.includes(entryName)) {
|
||||
moduleFiles.push(...entryFiles);
|
||||
} else {
|
||||
files.push(...entryFiles);
|
||||
@ -90,7 +86,6 @@ export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
|
||||
|
||||
const { content, warnings, errors } = await this.process({
|
||||
files,
|
||||
noModuleFiles,
|
||||
moduleFiles,
|
||||
outputPath: dirname(this.options.outputPath),
|
||||
baseHref: this.options.baseHref,
|
||||
|
||||
@ -59,13 +59,7 @@ export interface FileInfo {
|
||||
* bundles for differential serving.
|
||||
*/
|
||||
export function augmentIndexHtml(params: AugmentIndexHtmlOptions): string {
|
||||
const {
|
||||
loadOutputFile,
|
||||
files,
|
||||
noModuleFiles = [],
|
||||
moduleFiles = [],
|
||||
entrypoints,
|
||||
} = params;
|
||||
const { loadOutputFile, files, moduleFiles = [], entrypoints } = params;
|
||||
|
||||
let { crossOrigin = 'none' } = params;
|
||||
if (params.sri && crossOrigin === 'none') {
|
||||
@ -76,7 +70,7 @@ export function augmentIndexHtml(params: AugmentIndexHtmlOptions): string {
|
||||
const scripts = new Set<string>();
|
||||
|
||||
// Sort files in the order we want to insert them by entrypoint and dedupes duplicates
|
||||
const mergedFiles = [...moduleFiles, ...noModuleFiles, ...files];
|
||||
const mergedFiles = [...moduleFiles, ...files];
|
||||
for (const entrypoint of entrypoints) {
|
||||
for (const { extension, file, name } of mergedFiles) {
|
||||
if (name !== entrypoint) {
|
||||
@ -160,15 +154,9 @@ export function augmentIndexHtml(params: AugmentIndexHtmlOptions): string {
|
||||
// in some cases for differential loading file with the same name is avialable 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({ name: 'nomodule', value: null });
|
||||
if (!script.startsWith('polyfills-nomodule-es5')) {
|
||||
attrs.push({ name: 'defer', value: null });
|
||||
}
|
||||
} else if (isModuleType) {
|
||||
if (isModuleType) {
|
||||
attrs.push({ name: 'type', value: 'module' });
|
||||
} else {
|
||||
attrs.push({ name: 'defer', value: null });
|
||||
@ -293,7 +281,6 @@ export async function writeIndexHtml({
|
||||
outputPath,
|
||||
indexPath,
|
||||
files = [],
|
||||
noModuleFiles = [],
|
||||
moduleFiles = [],
|
||||
baseHref,
|
||||
deployUrl,
|
||||
@ -314,7 +301,6 @@ export async function writeIndexHtml({
|
||||
sri,
|
||||
entrypoints: generateEntryPoints({ scripts, styles }),
|
||||
files: filterAndMapBuildFiles(files, ['.js', '.css']),
|
||||
noModuleFiles: filterAndMapBuildFiles(noModuleFiles, '.js'),
|
||||
moduleFiles: filterAndMapBuildFiles(moduleFiles, '.js'),
|
||||
loadOutputFile: (filePath) =>
|
||||
readFileSync(join(dirname(outputPath), filePath)).toString(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user