mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-18 03:23:57 +08:00
138 lines
4.5 KiB
TypeScript
138 lines
4.5 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import { Path, getSystemPath, logging, normalize, resolve, workspaces } from '@angular-devkit/core';
|
|
import { Rule } from '@angular-devkit/schematics';
|
|
import { getWorkspace, updateWorkspace } from '../../utility/workspace';
|
|
import { Builders, ProjectType } from '../../utility/workspace-models';
|
|
|
|
export default function (): Rule {
|
|
return async (host, context) => {
|
|
const workspace = await getWorkspace(host);
|
|
|
|
for (const [projectName, project] of workspace.projects) {
|
|
if (project.extensions.projectType !== ProjectType.Application) {
|
|
// Only interested in application projects
|
|
continue;
|
|
}
|
|
|
|
for (const [, target] of project.targets) {
|
|
// Only interested in Angular Devkit Browser builder
|
|
if (target?.builder !== Builders.Browser) {
|
|
continue;
|
|
}
|
|
|
|
const isES5Needed = await isES5SupportNeeded(
|
|
resolve(
|
|
normalize(host.root.path),
|
|
normalize(project.root),
|
|
),
|
|
);
|
|
|
|
// Check options
|
|
if (target.options) {
|
|
target.options = removeE5BrowserSupportOption(projectName, target.options, isES5Needed, context.logger);
|
|
}
|
|
|
|
// Go through each configuration entry
|
|
if (!target.configurations) {
|
|
continue;
|
|
}
|
|
|
|
for (const [configurationName, options] of Object.entries(target.configurations)) {
|
|
target.configurations[configurationName] = removeE5BrowserSupportOption(
|
|
projectName,
|
|
options,
|
|
isES5Needed,
|
|
context.logger,
|
|
configurationName,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return updateWorkspace(workspace);
|
|
};
|
|
}
|
|
|
|
type TargetOptions = workspaces.TargetDefinition['options'];
|
|
|
|
function removeE5BrowserSupportOption(
|
|
projectName: string,
|
|
options: TargetOptions,
|
|
isES5Needed: boolean | undefined,
|
|
logger: logging.LoggerApi,
|
|
configurationName = '',
|
|
): TargetOptions {
|
|
if (typeof options?.es5BrowserSupport !== 'boolean') {
|
|
return options;
|
|
}
|
|
|
|
const configurationPath = configurationName ? `configurations.${configurationName}.` : '';
|
|
|
|
if (options.es5BrowserSupport && isES5Needed === false) {
|
|
logger.warn(
|
|
`Project '${projectName}' doesn't require ES5 support, but '${configurationPath}es5BrowserSupport' was set to 'true'.\n` +
|
|
`ES5 polyfills will no longer be added when building this project${configurationName ? ` with '${configurationName}' configuration.` : '.'}\n` +
|
|
`If ES5 polyfills are needed, add the supported ES5 browsers in the browserslist configuration.`,
|
|
);
|
|
} else if (!options.es5BrowserSupport && isES5Needed === true) {
|
|
logger.warn(
|
|
`Project '${projectName}' requires ES5 support, but '${configurationPath}es5BrowserSupport' was set to 'false'.\n` +
|
|
`ES5 polyfills will be added when building this project${configurationName ? ` with '${configurationName}' configuration.` : '.'}\n` +
|
|
`If ES5 polyfills are not needed, remove the unsupported ES5 browsers from the browserslist configuration.`,
|
|
);
|
|
}
|
|
|
|
return {
|
|
...options,
|
|
es5BrowserSupport: undefined,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* True, when one or more browsers requires ES5 support
|
|
*/
|
|
async function isES5SupportNeeded(projectRoot: Path): Promise<boolean | undefined> {
|
|
// y: feature is fully available
|
|
// n: feature is unavailable
|
|
// a: feature is partially supported
|
|
// x: feature is prefixed
|
|
const criteria = [
|
|
'y',
|
|
'a',
|
|
];
|
|
|
|
try {
|
|
// tslint:disable-next-line:no-implicit-dependencies
|
|
const browserslist = await import('browserslist');
|
|
const supportedBrowsers = browserslist(undefined, {
|
|
path: getSystemPath(projectRoot),
|
|
});
|
|
|
|
// tslint:disable-next-line:no-implicit-dependencies
|
|
const { feature, features } = await import('caniuse-lite');
|
|
const data = feature(features['es6-module']);
|
|
|
|
return 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));
|
|
});
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|