c0143cb2ab
The `ng_rollup_bundle` rule currently is only consumed in the Angular framework repository. This means that Angular packages are built from source, and ngcc is never needed to build rollup bundles using Ivy. Though, this rule is planned to be shared with other repositories to support common benchmark code. This means that ngcc needs to be handled as these other repositories cannot build Angular from source, but instead consume Angular through NPM (with ngcc enabling Ivy). The `ng_rollup_bundle` rule needs to dynamically prioritize `ngcc` generated main resolution fields if `--define=angular_ivy_enabled=True` is set (or with the alias: `--config=ivy`). ds PR Close #36044
212 lines
7.8 KiB
JavaScript
212 lines
7.8 KiB
JavaScript
/**
|
|
* @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
|
|
*/
|
|
// Rollup configuration
|
|
// GENERATED BY Bazel
|
|
|
|
const buildOptimizer = require(
|
|
'npm/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js');
|
|
const nodeResolve = require('rollup-plugin-node-resolve');
|
|
const sourcemaps = require('rollup-plugin-sourcemaps');
|
|
const commonjs = require('rollup-plugin-commonjs');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
function log_verbose(...m) {
|
|
// This is a template file so we use __filename to output the actual filename
|
|
if (!!process.env['VERBOSE_LOGS']) console.error(`[${path.basename(__filename)}]`, ...m);
|
|
}
|
|
|
|
const workspaceName = 'TMPL_workspace_name';
|
|
const useBuildOptimzier = TMPL_build_optimizer;
|
|
const rootDir = 'TMPL_root_dir';
|
|
const bannerFile = TMPL_banner_file;
|
|
const stampData = TMPL_stamp_data;
|
|
const moduleMappings = TMPL_module_mappings;
|
|
const nodeModulesRoot = 'TMPL_node_modules_root';
|
|
const ivyEnabled = TMPL_ivy_enabled;
|
|
|
|
log_verbose(`running with
|
|
cwd: ${process.cwd()}
|
|
workspaceName: ${workspaceName}
|
|
useBuildOptimzier: ${useBuildOptimzier}
|
|
rootDir: ${rootDir}
|
|
bannerFile: ${bannerFile}
|
|
stampData: ${stampData}
|
|
moduleMappings: ${JSON.stringify(moduleMappings)}
|
|
nodeModulesRoot: ${nodeModulesRoot}
|
|
ivyEnabled: ${ivyEnabled}
|
|
`);
|
|
|
|
function fileExists(filePath) {
|
|
try {
|
|
return fs.statSync(filePath).isFile();
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This resolver mimics the TypeScript Path Mapping feature, which lets us resolve
|
|
// modules based on a mapping of short names to paths.
|
|
function resolveBazel(
|
|
importee, importer, baseDir = process.cwd(), resolve = require.resolve, root = rootDir) {
|
|
log_verbose(`resolving '${importee}' from ${importer}`);
|
|
|
|
function resolveInRootDir(importee) {
|
|
function tryImportee(importee) {
|
|
var candidate = path.join(baseDir, root, importee);
|
|
log_verbose(`try to resolve '${importee}' at '${candidate}'`);
|
|
try {
|
|
var result = resolve(candidate);
|
|
return result;
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
// Attempt in the following order {importee}.mjs, {importee}/index.mjs,
|
|
// {importee}, {importee}.js, {importee}/index.js. If an .mjs file is
|
|
// available it should be resolved as rollup cannot handle the .js files
|
|
// generated by ts_library as they are not esm. When rolling up esm5 files
|
|
// these are re-rooted so it is not an issue.
|
|
// TODO(gregmagolan): clean this up in the future as the .mjs es2015 outputs
|
|
// along side the .js es5 outputs from ts_library creates this unusual situation for
|
|
// which we can't rely on standard node module resolution to do the right thing.
|
|
// In the future ts_library (or equivalent) should only produce a single flavor of
|
|
// output and ng_rollup_bundle should also just use the use the vanilla rollup_bundle
|
|
// rule without the need for a custom bazel resolver.
|
|
return tryImportee(`${importee}.mjs`) || tryImportee(`${importee}/index.mjs`) ||
|
|
tryImportee(importee) || tryImportee(`${importee}.js`) ||
|
|
tryImportee(`${importee}/index.js`);
|
|
}
|
|
|
|
// Since mappings are always in POSIX paths, when comparing the importee to mappings
|
|
// we should normalize the importee.
|
|
// Having it normalized is also useful to determine relative paths.
|
|
const normalizedImportee = importee.replace(/\\/g, '/');
|
|
|
|
// If import is fully qualified then resolve it directly
|
|
if (fileExists(importee)) {
|
|
log_verbose(`resolved fully qualified '${importee}'`);
|
|
return importee;
|
|
}
|
|
|
|
var resolved;
|
|
if (normalizedImportee.startsWith('./') || normalizedImportee.startsWith('../')) {
|
|
// relative import
|
|
if (importer) {
|
|
let importerRootRelative = path.dirname(importer);
|
|
const relative = path.relative(path.join(baseDir, root), importerRootRelative);
|
|
if (!relative.startsWith('.')) {
|
|
importerRootRelative = relative;
|
|
}
|
|
resolved = path.join(importerRootRelative, importee);
|
|
} else {
|
|
throw new Error('cannot resolve relative paths without an importer');
|
|
}
|
|
if (resolved) resolved = resolveInRootDir(resolved);
|
|
}
|
|
|
|
if (!resolved) {
|
|
// possible workspace import or external import if importee matches a module
|
|
// mapping
|
|
for (const k in moduleMappings) {
|
|
if (normalizedImportee == k || normalizedImportee.startsWith(k + '/')) {
|
|
// replace the root module name on a mappings match
|
|
// note that the module_root attribute is intended to be used for type-checking
|
|
// so it uses eg. "index.d.ts". At runtime, we have only index.js, so we strip the
|
|
// .d.ts suffix and let node require.resolve do its thing.
|
|
var v = moduleMappings[k].replace(/\.d\.ts$/, '');
|
|
const mappedImportee = path.join(v, normalizedImportee.slice(k.length + 1));
|
|
log_verbose(`module mapped '${importee}' to '${mappedImportee}'`);
|
|
resolved = resolveInRootDir(mappedImportee);
|
|
if (resolved) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!resolved) {
|
|
// workspace import
|
|
const userWorkspacePath = path.relative(workspaceName, importee);
|
|
resolved = resolveInRootDir(userWorkspacePath.startsWith('..') ? importee : userWorkspacePath);
|
|
}
|
|
|
|
if (resolved) {
|
|
log_verbose(`resolved to ${resolved}`);
|
|
} else {
|
|
log_verbose(`allowing rollup to resolve '${importee}' with node module resolution`);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
// We make mainFields match what was the default for plugin-node-resolve <4.2.0
|
|
// when mainFields was introduced. Resolving to 'browser' or 'es2015' first breaks
|
|
// some of the benchmarks such as `//modules/benchmarks/src/expanding_rows:perf_chromium-local`.
|
|
// See https://app.circleci.com/jobs/github/angular/angular/507444 &&
|
|
// https://app.circleci.com/jobs/github/angular/angular/507442 for affected tests.
|
|
const mainFields = ['module', 'main'];
|
|
const ngccMainFields = mainFields.map(f => `${f}_ivy_ngcc`);
|
|
|
|
let plugins = [
|
|
{
|
|
name: 'resolveBazel',
|
|
resolveId: resolveBazel,
|
|
},
|
|
nodeResolve({
|
|
// If Ivy is enabled, we need to make sure that the module resolution prioritizes ngcc
|
|
// processed entry-point fields. Ngcc adds special fields to `package.json` files of
|
|
// modules that have been processed. Prioritizing these fields matches the Angular CLIs
|
|
// behavior for supporting Ivy. We need to support ngcc because `ng_rollup_bundle` rule is
|
|
// shared with other repositories that consume Angular from NPM (w/ ngcc).
|
|
// https://github.com/angular/angular-cli/blob/1a1ceb609b9a87c4021cce3a6f0fc6d167cd09d2/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L918-L920
|
|
mainFields: ivyEnabled ? [...ngccMainFields, ...mainFields] : mainFields,
|
|
jail: process.cwd(),
|
|
customResolveOptions: {moduleDirectory: nodeModulesRoot}
|
|
}),
|
|
commonjs({ignoreGlobal: true}),
|
|
sourcemaps(),
|
|
];
|
|
|
|
if (useBuildOptimzier) {
|
|
plugins = [
|
|
buildOptimizer.default({
|
|
sideEffectFreeModules: [
|
|
'.esm5/packages/core/src',
|
|
'.esm5/packages/common/src',
|
|
'.esm5/packages/compiler/src',
|
|
'.esm5/packages/platform-browser/src',
|
|
]
|
|
}),
|
|
].concat(plugins);
|
|
}
|
|
|
|
let banner = '';
|
|
if (bannerFile) {
|
|
banner = fs.readFileSync(bannerFile, {encoding: 'utf-8'});
|
|
if (stampData) {
|
|
const versionTag = fs.readFileSync(stampData, {encoding: 'utf-8'})
|
|
.split('\n')
|
|
.find(s => s.startsWith('BUILD_SCM_VERSION'));
|
|
// Don't assume BUILD_SCM_VERSION exists
|
|
if (versionTag) {
|
|
const version = versionTag.split(' ')[1].trim();
|
|
banner = banner.replace(/0.0.0-PLACEHOLDER/, version);
|
|
}
|
|
}
|
|
}
|
|
|
|
const config = {
|
|
plugins,
|
|
external: [TMPL_external],
|
|
output: {
|
|
globals: {TMPL_globals},
|
|
banner,
|
|
}
|
|
};
|
|
|
|
module.exports = config;
|