Fixes issue introduced in https://github.com/angular/angular/pull/33808 for ng_rollup_bundle with `-spawn_strategy=standalone`. Without the sandbox (if --spawn_strategy=standalone is set) rollup can resolve to the non-esm .js file generated by ts_library instead of the desired .mjs. This fixes the problem by prioritizing .mjs. Issue observed is of the flavor: ``` ERROR: modules/benchmarks/src/views/BUILD.bazel:20:1: Bundling JavaScript modules/benchmarks/src/views/bundle.es2015.js [rollup] failed (Exit 1) [!] Error: 'enableProdMode' is not exported by bazel-out/darwin-fastbuild/bin/packages/core/index.js https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module bazel-out/darwin-fastbuild/bin/modules/benchmarks/src/views/index.mjs (12:9) 10: * found in the LICENSE file at https://angular.io/license 11: */ 12: import { enableProdMode } from '@angular/core'; ^ 13: import { platformBrowser } from '@angular/platform-browser'; 14: import { ViewsBenchmarkModuleNgFactory } from './views-benchmark.ngfactory'; Error: 'enableProdMode' is not exported by bazel-out/darwin-fastbuild/bin/packages/core/index.js ``` PR Close #33867
		
			
				
	
	
		
			201 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			7.0 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';
 | |
| 
 | |
| log_verbose(`running with
 | |
|   cwd: ${process.cwd()}
 | |
|   workspaceName: ${workspaceName}
 | |
|   useBuildOptimzier: ${useBuildOptimzier}
 | |
|   rootDir: ${rootDir}
 | |
|   bannerFile: ${bannerFile}
 | |
|   stampData: ${stampData}
 | |
|   moduleMappings: ${JSON.stringify(moduleMappings)}
 | |
|   nodeModulesRoot: ${nodeModulesRoot}
 | |
| `);
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| let plugins = [
 | |
|   {
 | |
|     name: 'resolveBazel',
 | |
|     resolveId: resolveBazel,
 | |
|   },
 | |
|   nodeResolve({
 | |
|     // 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.
 | |
|     mainFields: ['module', 'main'],
 | |
|     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;
 |