From 2dd5fcc7daba0a0b15076694652ce3d0d5d9d4a9 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 22 Oct 2019 16:17:32 -0700 Subject: [PATCH] build: remove deps on legacy nodejs rules rollup_bundle internals The legacy nodejs rules rollup_bundle is now deprecated and will be removed in the nodejs rules 1.0 release due in mid-November. This PR brings in the rules_nodejs internal API deps that ng_rollup_bundle, ng_package and ls_rollup_bundle depend on into this repo to break the dependency. In the future these rules should switch to use the new rollup_bundle via a macro as done in https://github.com/angular/angular/pull/33329 but this is not possible right now due to the complication of having esm5 re-rooted ts_library dependencies. --- package.json | 3 + packages/bazel/docs/BUILD.bazel | 1 - packages/bazel/src/BUILD.bazel | 17 - packages/bazel/src/ng_package/BUILD.bazel | 12 +- packages/bazel/src/ng_package/ng_package.bzl | 324 +++++++- .../bazel/src/ng_package/rollup.config.js | 168 ++++ .../src/ng_package/terser_config.default.json | 12 + packages/bazel/src/ng_rollup_bundle.bzl | 229 ------ .../test/ng_package/example_package.golden | 8 +- .../example_with_ts_library_package.golden | 4 +- packages/language-service/bundles/BUILD.bazel | 2 +- packages/language-service/bundles/rollup.bzl | 65 -- tools/defaults.bzl | 6 +- tools/ng_rollup_bundle/BUILD.bazel | 22 + tools/ng_rollup_bundle/ng_rollup_bundle.bzl | 462 +++++++++++ tools/ng_rollup_bundle/rollup.config.js | 193 +++++ yarn.lock | 743 +++++++++++++++++- 17 files changed, 1907 insertions(+), 364 deletions(-) create mode 100644 packages/bazel/src/ng_package/rollup.config.js create mode 100644 packages/bazel/src/ng_package/terser_config.default.json delete mode 100644 packages/bazel/src/ng_rollup_bundle.bzl delete mode 100644 packages/language-service/bundles/rollup.bzl create mode 100644 tools/ng_rollup_bundle/BUILD.bazel create mode 100644 tools/ng_rollup_bundle/ng_rollup_bundle.bzl create mode 100644 tools/ng_rollup_bundle/rollup.config.js diff --git a/package.json b/package.json index ee782f5c6dbeb3..ccf7124db83509 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,13 @@ "@angular-devkit/core": "^9.0.0-next.15", "@angular-devkit/schematics": "^9.0.0-next.15", "@angular/bazel": "file:./tools/npm/@angular_bazel", + "@babel/cli": "^7.6.4", "@babel/core": "^7.6.4", + "@babel/preset-env": "^7.6.3", "@bazel/jasmine": "0.38.3", "@bazel/karma": "0.38.3", "@bazel/protractor": "0.38.3", + "@bazel/terser": "0.38.3", "@bazel/typescript": "0.38.3", "@microsoft/api-extractor": "^7.3.9", "@schematics/angular": "^8.0.0-beta.15", diff --git a/packages/bazel/docs/BUILD.bazel b/packages/bazel/docs/BUILD.bazel index 2e32d61ec17f1c..55b84494d89db8 100644 --- a/packages/bazel/docs/BUILD.bazel +++ b/packages/bazel/docs/BUILD.bazel @@ -4,7 +4,6 @@ skylark_doc( name = "docs", srcs = [ "//packages/bazel/src:ng_module.bzl", - "//packages/bazel/src:ng_rollup_bundle.bzl", "//packages/bazel/src/ng_package:ng_package.bzl", ], format = "html", diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index 16121e38c62863..aca43575aed9ee 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -10,23 +10,6 @@ exports_files(glob(["*.bzl"])) load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") -nodejs_binary( - name = "rollup_with_build_optimizer", - data = [ - "@npm//@angular-devkit/build-optimizer", - "@npm//is-builtin-module", - "@npm//rollup", - "@npm//rollup-plugin-amd", - "@npm//rollup-plugin-commonjs", - "@npm//rollup-plugin-json", - "@npm//rollup-plugin-node-resolve", - "@npm//rollup-plugin-sourcemaps", - ], - entry_point = "@npm//:node_modules/rollup/bin/rollup", - install_source_map_support = False, - visibility = ["//visibility:public"], -) - filegroup( name = "empty_node_modules", srcs = [], diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel index 48a1cc03afd126..535ea3a4bcad8d 100644 --- a/packages/bazel/src/ng_package/BUILD.bazel +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -27,9 +27,17 @@ nodejs_binary( install_source_map_support = False, ) -exports_files(["ng_package.bzl"]) +exports_files([ + "ng_package.bzl", + "rollup.config.js", + "terser_config.default.json", +]) filegroup( name = "package_assets", - srcs = glob(["*.bzl"]) + ["BUILD.bazel"], + srcs = glob(["*.bzl"]) + [ + "BUILD.bazel", + "rollup.config.js", + "terser_config.default.json", + ], ) diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index cfd7790970d1cb..5039da4f6b7fa9 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -13,15 +13,8 @@ It packages your library following the Angular Package Format, see the specification of this format at https://goo.gl/jB3GVv """ -load("@build_bazel_rules_nodejs//internal/common:collect_es6_sources.bzl", "collect_es6_sources") -load("@build_bazel_rules_nodejs//:providers.bzl", "JSNamedModuleInfo", "NpmPackageInfo") -load( - "@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", - "ROLLUP_ATTRS", - "ROLLUP_DEPS_ASPECTS", - "run_terser", - "write_rollup_config", -) +load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "JSNamedModuleInfo", "NpmPackageInfo", "node_modules_aspect") +load("@build_bazel_rules_nodejs//internal/common:module_mappings.bzl", "get_module_mappings") load( "@build_bazel_rules_nodejs//internal/npm_package:npm_package.bzl", "NPM_PACKAGE_ATTRS", @@ -38,6 +31,21 @@ def _debug(vars, *args): print("[ng_package.bzl]", args) _DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager" +_DEFAULT_ROLLUP_CONFIG_TMPL = "@npm_angular_bazel//src/ng_package:rollup.config.js" +_DEFALUT_TERSER_CONFIG_FILE = "@npm_angular_bazel//src/ng_package:terser_config.default.json" +_DEFAULT_ROLLUP = "@npm//rollup/bin:rollup" +_DEFAULT_TERSER = "@npm//terser/bin:terser" + +def _rollup_module_mappings_aspect_impl(target, ctx): + mappings = get_module_mappings(target.label, ctx.rule.attr) + return struct(rollup_module_mappings = mappings) + +rollup_module_mappings_aspect = aspect( + _rollup_module_mappings_aspect_impl, + attr_aspects = ["deps"], +) + +_ROLLUP_MODULE_MAPPINGS_ATTR = "rollup_module_mappings" # Convert from some-dash-case to someCamelCase def _convert_dash_case_to_camel_case(s): @@ -102,6 +110,162 @@ WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [ # TODO(gregmagolan): clean this up _DEPSET_TYPE = "depset" +def _collect_es6_sources(ctx): + """Returns a file tree containing only es6 files. + + Args: + ctx: ctx. + + Returns: + A file tree containing only production files. + """ + + non_rerooted_files = [d for d in ctx.files.deps if d.is_source] + for dep in ctx.attr.deps: + if JSEcmaScriptModuleInfo in dep: + non_rerooted_files += dep[JSEcmaScriptModuleInfo].sources.to_list() + + rerooted_files = [] + for file in non_rerooted_files: + path = file.short_path + if (path.startswith("../")): + path = "external/" + path[3:] + + rerooted_file = ctx.actions.declare_file( + "%s.es6/%s" % ( + ctx.label.name, + path.replace(".mjs", ".js"), + ), + ) + + # Cheap way to create an action that copies a file + ctx.actions.expand_template( + output = rerooted_file, + template = file, + substitutions = {}, + ) + rerooted_files += [rerooted_file] + + return depset(direct = rerooted_files) + +def _terser(ctx, input, output): + """Runs terser on an input file. + + Args: + ctx: Bazel rule execution context + input: input file + output: output file + + Returns: + The sourcemap file + """ + + map_output = ctx.actions.declare_file(output.basename + ".map", sibling = output) + + args = ctx.actions.args() + + args.add(input.path) + args.add_all(["--output", output.path]) + + # Source mapping options are comma-packed into one argv + # see https://github.com/terser-js/terser#command-line-usage + source_map_opts = ["includeSources", "base=" + ctx.bin_dir.path] + + # This option doesn't work in the config file, only on the CLI + args.add_all(["--source-map", ",".join(source_map_opts)]) + + args.add("--comments") + + args.add_all(["--config-file", ctx.file.terser_config_file.path]) + + ctx.actions.run( + progress_message = "Optimizing JavaScript %s [terser]" % output.short_path, + executable = ctx.executable.terser, + inputs = [input, ctx.file.terser_config_file], + outputs = [output, map_output], + arguments = [args], + ) + + return map_output + +def _compute_node_modules_root(ctx): + """Computes the node_modules root from the node_modules and deps attributes. + + Args: + ctx: the skylark execution context + + Returns: + The node_modules root as a string + """ + node_modules_root = None + for d in ctx.attr.deps: + if NpmPackageInfo in d: + possible_root = "/".join(["external", d[NpmPackageInfo].workspace, "node_modules"]) + if not node_modules_root: + node_modules_root = possible_root + elif node_modules_root != possible_root: + fail("All npm dependencies need to come from a single workspace. Found '%s' and '%s'." % (node_modules_root, possible_root)) + if not node_modules_root: + # there are no fine grained deps but we still need a node_modules_root even if its empty + node_modules_root = "external/npm/node_modules" + return node_modules_root + +# Avoid using non-normalized paths (workspace/../other_workspace/path) +def _to_manifest_path(ctx, file): + if file.short_path.startswith("../"): + return file.short_path[3:] + else: + return ctx.workspace_name + "/" + file.short_path + +# Expand entry_point into runfiles and strip the file extension +def _entry_point_path(ctx): + return _to_manifest_path(ctx, ctx.file.entry_point)[:-(len(ctx.file.entry_point.extension) + 1)] + +def _write_rollup_config(ctx, root_dir, filename = "_%s.rollup.conf.js", output_format = "iife"): + """Generate a rollup config file. + + Args: + ctx: Bazel rule execution context + root_dir: root directory for module resolution (defaults to None) + filename: output filename pattern (defaults to `_%s.rollup.conf.js`) + output_format: passed to rollup output.format option, e.g. "umd" + + Returns: + The rollup config file. See https://rollupjs.org/guide/en#configuration-files + """ + config = ctx.actions.declare_file(filename % ctx.label.name) + + # build_file_path includes the BUILD.bazel file, transform here to only include the dirname + build_file_dirname = "/".join(ctx.build_file_path.split("/")[:-1]) + + mappings = dict() + all_deps = ctx.attr.deps + ctx.attr.srcs + for dep in all_deps: + if hasattr(dep, _ROLLUP_MODULE_MAPPINGS_ATTR): + for k, v in getattr(dep, _ROLLUP_MODULE_MAPPINGS_ATTR).items(): + if k in mappings and mappings[k] != v: + fail(("duplicate module mapping at %s: %s maps to both %s and %s" % + (dep.label, k, mappings[k], v)), "deps") + mappings[k] = v + + ctx.actions.expand_template( + output = config, + template = ctx.file.rollup_config_tmpl, + substitutions = { + "TMPL_banner_file": "\"%s\"" % ctx.file.license_banner.path if ctx.file.license_banner else "undefined", + "TMPL_global_name": ctx.attr.global_name if ctx.attr.global_name else ctx.label.name, + "TMPL_entry_point": _entry_point_path(ctx), + "TMPL_module_mappings": str(mappings), + "TMPL_node_modules_root": _compute_node_modules_root(ctx), + "TMPL_output_format": output_format, + "TMPL_root_dir": root_dir, + "TMPL_stamp_data": "\"%s\"" % ctx.version_file.path if ctx.version_file else "undefined", + "TMPL_workspace_name": ctx.workspace_name, + }, + ) + + return config + def _rollup(ctx, bundle_name, rollup_config, entry_point, inputs, js_output, format = "es", module_name = "", include_tslib = False): map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output) @@ -153,8 +317,8 @@ def _rollup(ctx, bundle_name, rollup_config, entry_point, inputs, js_output, for mnemonic = "AngularPackageRollup", inputs = inputs.to_list() + other_inputs, outputs = [js_output, map_output], - executable = ctx.executable._rollup, - tools = [ctx.executable._rollup], + executable = ctx.executable.rollup, + tools = [ctx.executable.rollup], arguments = [args], ) return struct( @@ -206,7 +370,7 @@ def _filter_js_inputs(all_inputs): def _ng_package_impl(ctx): npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name) - esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx), "js") + esm_2015_files = _filter_out_generated_files(_collect_es6_sources(ctx), "js") esm5_sources = _filter_out_generated_files(flatten_esm5(ctx), "js") # These accumulators match the directory names where the files live in the @@ -343,17 +507,16 @@ def _ng_package_impl(ctx): umd_output = ctx.outputs.umd min_output = ctx.outputs.umd_min - node_modules_files = _filter_js_inputs(ctx.files.node_modules) - # Also include files from npm fine grained deps as inputs. # These deps are identified by the NpmPackageInfo provider. + node_modules_files = [] for d in ctx.attr.deps: if NpmPackageInfo in d: node_modules_files += _filter_js_inputs(d.files) esm5_rollup_inputs = depset(node_modules_files, transitive = [esm5_sources]) - esm2015_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename = "_%s.rollup_esm2015.conf.js") - esm5_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename = "_%s.rollup_esm5.conf.js") + esm2015_config = _write_rollup_config(ctx, "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename = "_%s.rollup_esm2015.conf.js") + esm5_config = _write_rollup_config(ctx, "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename = "_%s.rollup_esm5.conf.js") fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, depset(node_modules_files, transitive = [esm_2015_files]), fesm2015_output)) fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_rollup_inputs, fesm5_output)) @@ -371,11 +534,10 @@ def _ng_package_impl(ctx): include_tslib = True, ), ) - terser_sourcemap = run_terser( + terser_sourcemap = _terser( ctx, umd_output, min_output, - config_name = entry_point.replace("/", "_"), ) bundles.append(struct(js = min_output, map = terser_sourcemap)) @@ -468,21 +630,99 @@ def _ng_package_impl(ctx): files = depset([package_dir]), )] -DEPS_ASPECTS = [esm5_outputs_aspect] +_NG_PACKAGE_DEPS_ASPECTS = [esm5_outputs_aspect, rollup_module_mappings_aspect, node_modules_aspect] -# Workaround skydoc bug which assumes ROLLUP_DEPS_ASPECTS is a str type -[DEPS_ASPECTS.append(a) for a in ROLLUP_DEPS_ASPECTS] +_NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **{ + "srcs": attr.label_list( + doc = """JavaScript source files from the workspace. + These can use ES2015 syntax and ES Modules (import/export)""", + allow_files = True, + ), + "entry_point": attr.label( + doc = """The starting point of the application, passed as the `--input` flag to rollup. + + If the entry JavaScript file belongs to the same package (as the BUILD file), + you can simply reference it by its relative name to the package directory: + + ``` + ng_package( + name = "bundle", + entry_point = ":main.js", + ) + ``` + + You can specify the entry point as a typescript file so long as you also include + the ts_library target in deps: + + ``` + ts_library( + name = "main", + srcs = ["main.ts"], + ) + + ng_package( + name = "bundle", + deps = [":main"] + entry_point = ":main.ts", + ) + ``` -NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{ - "srcs": attr.label_list(allow_files = True), - "deps": attr.label_list(aspects = DEPS_ASPECTS), + The rule will use the corresponding `.js` output of the ts_library rule as the entry point. + + If the entry point target is a rule, it should produce a single JavaScript entry file that will be passed to the nodejs_binary rule. + For example: + + ``` + filegroup( + name = "entry_file", + srcs = ["main.js"], + ) + + ng_package( + name = "bundle", + entry_point = ":entry_file", + ) + ``` + """, + mandatory = True, + allow_single_file = True, + ), + "global_name": attr.string( + doc = """A name given to this package when referenced as a global variable. + This name appears in the bundle module incantation at the beginning of the file, + and governs the global symbol added to the global context (e.g. `window`) as a side- + effect of loading the UMD/IIFE JS bundle. + + Rollup doc: "The variable name, representing your iife/umd bundle, by which other scripts on the same page can access it." + + This is passed to the `output.name` setting in Rollup.""", + ), + "globals": attr.string_dict( + doc = """A dict of symbols that reference external scripts. + The keys are variable names that appear in the program, + and the values are the symbol to reference at runtime in a global context (UMD bundles). + For example, a program referencing @angular/core should use ng.core + as the global reference, so Angular users should include the mapping + `"@angular/core":"ng.core"` in the globals.""", + default = {}, + ), + "license_banner": attr.label( + doc = """A .txt file passed to the `banner` config option of rollup. + The contents of the file will be copied to the top of the resulting bundles. + Note that you can replace a version placeholder in the license file, by using + the special version `0.0.0-PLACEHOLDER`. See the section on stamping in the README.""", + allow_single_file = [".txt"], + ), + "deps": attr.label_list( + doc = """Other rules that produce JavaScript outputs, such as `ts_library`.""", + aspects = _NG_PACKAGE_DEPS_ASPECTS, + ), "data": attr.label_list( doc = "Additional, non-Angular files to be added to the package, e.g. global CSS assets.", allow_files = True, ), "include_devmode_srcs": attr.bool(default = False), "readme_md": attr.label(allow_single_file = [".md"]), - "globals": attr.string_dict(default = {}), "entry_point_name": attr.string( doc = "Name to use when generating bundle files for the primary entry-point.", ), @@ -491,16 +731,34 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{ executable = True, cfg = "host", ), - "_rollup": attr.label( - default = Label("@build_bazel_rules_nodejs//internal/rollup"), + "rollup": attr.label( + default = Label(_DEFAULT_ROLLUP), + executable = True, + cfg = "host", + ), + "terser": attr.label( executable = True, cfg = "host", + default = Label(_DEFAULT_TERSER), + ), + "terser_config_file": attr.label( + doc = """A JSON file containing Terser minify() options. + +This is the file you would pass to the --config-file argument in terser's CLI. +https://github.com/terser-js/terser#minify-options documents the content of the file. + +If `config_file` isn't supplied, Bazel will use a default config file. +""", + allow_single_file = True, + # These defaults match how terser was run in the legacy built-in rollup_bundle rule. + # We keep them the same so it's easier for users to migrate. + default = Label(_DEFALUT_TERSER_CONFIG_FILE), ), - "_rollup_config_tmpl": attr.label( - default = Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"), + "rollup_config_tmpl": attr.label( + default = Label(_DEFAULT_ROLLUP_CONFIG_TMPL), allow_single_file = True, ), -})) +}) # Angular wants these named after the entry_point, # eg. for //packages/core it looks like "packages/core/index.js", we want @@ -539,7 +797,7 @@ def primary_entry_point_name(name, entry_point, entry_point_name): # Fall back to the name of the ng_package rule. return name -def ng_package_outputs(name, entry_point, entry_point_name): +def _ng_package_outputs(name, entry_point, entry_point_name): """This is not a public API. This function computes the named outputs for an ng_package rule. @@ -570,8 +828,8 @@ def ng_package_outputs(name, entry_point, entry_point_name): ng_package = rule( implementation = _ng_package_impl, - attrs = NG_PACKAGE_ATTRS, - outputs = ng_package_outputs, + attrs = _NG_PACKAGE_ATTRS, + outputs = _ng_package_outputs, ) """ ng_package produces an npm-ready package from an Angular library. diff --git a/packages/bazel/src/ng_package/rollup.config.js b/packages/bazel/src/ng_package/rollup.config.js new file mode 100644 index 00000000000000..b1862f82bdada4 --- /dev/null +++ b/packages/bazel/src/ng_package/rollup.config.js @@ -0,0 +1,168 @@ +/** + * @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 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 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} + 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) { + 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; + } + } + + // 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; + } + + // process.cwd() is the execroot and ends up looking something like + // `.../2c2a834fcea131eff2d962ffe20e1c87/bazel-sandbox/872535243457386053/execroot/` + // from that path to the es6 output is + // `//.es6` from there, sources + // from the user's workspace are under `` and sources from external + // workspaces are under `external//` + 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 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: [ + { + name: 'resolveBazel', + resolveId: resolveBazel, + }, + nodeResolve({ + mainFields: ['browser', 'es2015', 'module', 'jsnext:main', 'main'], + jail: process.cwd(), + customResolveOptions: {moduleDirectory: nodeModulesRoot} + }), + commonjs({ignoreGlobal: true}), + sourcemaps(), + ], + input: 'TMPL_entry_point', + output: { + banner, + name: 'TMPL_global_name', + format: 'TMPL_output_format', + }, + preserveSymlinks: true, +}; + +module.exports = config; diff --git a/packages/bazel/src/ng_package/terser_config.default.json b/packages/bazel/src/ng_package/terser_config.default.json new file mode 100644 index 00000000000000..7fe029be719efc --- /dev/null +++ b/packages/bazel/src/ng_package/terser_config.default.json @@ -0,0 +1,12 @@ +{ + "compress": { + "global_defs": {"ngDevMode": false, "ngI18nClosureMode": false}, + "keep_fnames": true, + "passes": 3, + "pure_getters": true, + "reduce_funcs": true, + "reduce_vars": true, + "sequences": true + }, + "mangle": true +} \ No newline at end of file diff --git a/packages/bazel/src/ng_rollup_bundle.bzl b/packages/bazel/src/ng_rollup_bundle.bzl deleted file mode 100644 index 46f9641eade3a8..00000000000000 --- a/packages/bazel/src/ng_rollup_bundle.bzl +++ /dev/null @@ -1,229 +0,0 @@ -# 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 with Build Optimizer - - This provides a variant of the [rollup_bundle] rule that works better for Angular apps. - - It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get - better optimization. It also uses ESM5 format inputs, as this is what - build-optimizer is hard-coded to look for and transform. - - [rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html -""" - -load( - "@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", - "ROLLUP_ATTRS", - "ROLLUP_DEPS_ASPECTS", - "run_rollup", - "run_sourcemapexplorer", - "run_terser", - "write_rollup_config", -) -load("@build_bazel_rules_nodejs//internal/common:collect_es6_sources.bzl", collect_es2015_sources = "collect_es6_sources") -load(":esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5") - -ROLLUP_OUTPUTS = { - "build_cjs": "%{name}.cjs.js", - "build_es2015": "%{name}.es2015.js", - "build_es2015_min": "%{name}.min.es2015.js", - "build_es2015_min_debug": "%{name}.min_debug.es2015.js", - "build_es5": "%{name}.js", - "build_es5_min": "%{name}.min.js", - "build_es5_min_debug": "%{name}.min_debug.js", - "build_umd": "%{name}.umd.js", - "explore_html": "%{name}.explore.html", -} - -PACKAGES = [ - # Generated paths when using ng_rollup_bundle outside this monorepo. - "external/angular/packages/core/src", - "external/angular/packages/common/src", - "external/angular/packages/compiler/src", - "external/angular/packages/platform-browser/src", - "external/rxjs", - # Generated paths when using ng_rollup_bundle inside this monorepo. - "packages/core/src", - "packages/common/src", - "packages/compiler/src", - "packages/platform-browser/src", -] -PLUGIN_CONFIG = "{sideEffectFreeModules: [\n%s]}" % ",\n".join( - [" '.esm5/{0}'".format(p) for p in PACKAGES], -) -BO_ROLLUP = "npm/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js" -BO_PLUGIN = "require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG) - -def _use_plain_rollup(ctx): - """Determine whether to use the Angular or upstream versions of the rollup_bundle rule. - - In most modes, the Angular version of rollup is used. This runs build optimizer as part of its - processing, which affects decorators and annotations. - - In JIT modes, an emulation of the upstream rollup_bundle rule is used. This avoids running - build optimizer on code which isn't designed to be optimized by it. - - Args: - ctx: skylark rule execution context - - Returns: - true iff the Angular version of rollup with build optimizer should be used, false otherwise - """ - - if "compile" not in ctx.var: - return False - - strategy = ctx.var["compile"] - return strategy == "jit" - -def run_brotli(ctx, input, output): - """Execute the Brotli compression utility. - - Args: - ctx: Bazel's rule execution context - input: any file - output: the compressed file - """ - ctx.actions.run( - executable = ctx.executable._brotli, - inputs = [input], - outputs = [output], - arguments = ["--output=%s" % output.path, input.path], - ) - -# Borrowed from bazelbuild/rules_nodejs -def _run_tsc(ctx, input, output): - args = ctx.actions.args() - args.add("--target", "es5") - args.add("--allowJS") - args.add(input) - args.add("--outFile", output) - - ctx.actions.run( - executable = ctx.executable._tsc, - inputs = [input], - outputs = [output], - arguments = [args], - ) - -# Borrowed from bazelbuild/rules_nodejs, with the addition of brotli compression output -def _plain_rollup_bundle(ctx): - rollup_config = write_rollup_config(ctx) - run_rollup(ctx, collect_es2015_sources(ctx), rollup_config, ctx.outputs.build_es2015) - run_terser(ctx, ctx.outputs.build_es2015, ctx.outputs.build_es2015_min, config_name = ctx.label.name + "es2015_min") - run_terser(ctx, ctx.outputs.build_es2015, ctx.outputs.build_es2015_min_debug, debug = True, config_name = ctx.label.name + "es2015_min_debug") - _run_tsc(ctx, ctx.outputs.build_es2015, ctx.outputs.build_es5) - source_map = run_terser(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min) - run_terser(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True) - umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd") - run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd) - cjs_rollup_config = write_rollup_config(ctx, filename = "_%s_cjs.rollup.conf.js", output_format = "cjs") - run_rollup(ctx, collect_es2015_sources(ctx), cjs_rollup_config, ctx.outputs.build_cjs) - run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html) - - run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed) - files = [ctx.outputs.build_es5_min, source_map] - return DefaultInfo(files = depset(files), runfiles = ctx.runfiles(files)) - -def _ng_rollup_bundle(ctx): - # Escape and use the plain rollup rule if the compilation strategy requires it - if _use_plain_rollup(ctx): - return _plain_rollup_bundle(ctx) - - # We don't expect anyone to make use of this bundle yet, but it makes this rule - # compatible with rollup_bundle which allows them to be easily swapped back and - # forth. - esm2015_rollup_config = write_rollup_config(ctx, filename = "_%s.rollup_es2015.conf.js") - esm2015_rollup_sourcemap = run_rollup(ctx, collect_es2015_sources(ctx), esm2015_rollup_config, ctx.outputs.build_es2015) - - run_terser( - ctx, - ctx.outputs.build_es2015, - ctx.outputs.build_es2015_min, - config_name = ctx.label.name + "es2015_min", - comments = False, - in_source_map = esm2015_rollup_sourcemap, - ) - run_terser( - ctx, - ctx.outputs.build_es2015, - ctx.outputs.build_es2015_min_debug, - config_name = ctx.label.name + "es2015_min_debug", - debug = True, - comments = False, - ) - - esm5_sources = flatten_esm5(ctx) - - rollup_config = write_rollup_config(ctx, [BO_PLUGIN], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)])) - rollup_sourcemap = run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_es5) - - sourcemap = run_terser( - ctx, - ctx.outputs.build_es5, - ctx.outputs.build_es5_min, - config_name = ctx.label.name + "es5_min", - comments = False, - in_source_map = rollup_sourcemap, - ) - run_terser( - ctx, - ctx.outputs.build_es5, - ctx.outputs.build_es5_min_debug, - config_name = ctx.label.name + "es5_min_debug", - debug = True, - comments = False, - ) - - umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd") - run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd) - cjs_rollup_config = write_rollup_config(ctx, filename = "_%s_cjs.rollup.conf.js", output_format = "cjs") - run_rollup(ctx, collect_es2015_sources(ctx), cjs_rollup_config, ctx.outputs.build_cjs) - - run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed) - - run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, sourcemap, ctx.outputs.explore_html) - - return DefaultInfo(files = depset([ctx.outputs.build_es5_min, sourcemap])) - -DEPS_ASPECTS = [esm5_outputs_aspect] - -# Workaround skydoc bug which assumes ROLLUP_DEPS_ASPECTS is a str type -[DEPS_ASPECTS.append(a) for a in ROLLUP_DEPS_ASPECTS] - -ng_rollup_bundle = rule( - implementation = _ng_rollup_bundle, - attrs = dict(ROLLUP_ATTRS, **{ - "deps": attr.label_list( - doc = """Other targets that provide JavaScript files. - Typically this will be `ts_library` or `ng_module` targets.""", - aspects = DEPS_ASPECTS, - ), - "_brotli": attr.label( - executable = True, - cfg = "host", - default = Label("//tools/brotli-cli"), - ), - "_rollup": attr.label( - executable = True, - cfg = "host", - default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer"), - ), - }), - outputs = dict(ROLLUP_OUTPUTS, **{ - "build_es5_min_compressed": "%{name}.min.js.br", - }), -) -""" -Run [Rollup] with the [Build Optimizer] plugin. - -This rule extends from the [rollup_bundle] rule, so attributes and outputs of -that rule are used here too. - -[Rollup]: https://rollupjs.org/ -[Build Optimizer]: https://www.npmjs.com/package/@angular-devkit/build-optimizer -[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html -""" diff --git a/packages/bazel/test/ng_package/example_package.golden b/packages/bazel/test/ng_package/example_package.golden index d19653a4579191..82641371f16ef7 100644 --- a/packages/bazel/test/ng_package/example_package.golden +++ b/packages/bazel/test/ng_package/example_package.golden @@ -246,7 +246,7 @@ Hello for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; - }; + } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); @@ -281,7 +281,7 @@ Hello function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; - }; + } function __importStar(mod) { if (mod && mod.__esModule) return mod; @@ -520,7 +520,7 @@ e.SecondaryModule=o,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})}); for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; - }; + } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); @@ -555,7 +555,7 @@ e.SecondaryModule=o,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})}); function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; - }; + } function __importStar(mod) { if (mod && mod.__esModule) return mod; diff --git a/packages/bazel/test/ng_package/example_with_ts_library_package.golden b/packages/bazel/test/ng_package/example_with_ts_library_package.golden index 0528d83c5114ce..a6f9eb7aa7a6d8 100644 --- a/packages/bazel/test/ng_package/example_with_ts_library_package.golden +++ b/packages/bazel/test/ng_package/example_with_ts_library_package.golden @@ -234,7 +234,7 @@ License: MIT for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; - }; + } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); @@ -269,7 +269,7 @@ License: MIT function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; - }; + } function __importStar(mod) { if (mod && mod.__esModule) return mod; diff --git a/packages/language-service/bundles/BUILD.bazel b/packages/language-service/bundles/BUILD.bazel index 7ddb1345de1604..cfb20da7b5cd51 100644 --- a/packages/language-service/bundles/BUILD.bazel +++ b/packages/language-service/bundles/BUILD.bazel @@ -1,4 +1,4 @@ -load(":rollup.bzl", "ls_rollup_bundle") +load("//tools/ng_rollup_bundle:ng_rollup_bundle.bzl", "ls_rollup_bundle") ls_rollup_bundle( name = "language-service", diff --git a/packages/language-service/bundles/rollup.bzl b/packages/language-service/bundles/rollup.bzl deleted file mode 100644 index 73f1cc207bfa7c..00000000000000 --- a/packages/language-service/bundles/rollup.bzl +++ /dev/null @@ -1,65 +0,0 @@ -"""Custom rollup_bundle for language service. - -Overrides format to AMD and produces only umd and min, no FESM. - -We do this so that we bundle all of the dependencies into the bundle -except for typescript, fs and path. - -This allows editors and other tools to easily use the language service bundle -without having to provide all of the angular specific peer dependencies. -""" - -load( - "@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", - "ROLLUP_ATTRS", - "ROLLUP_DEPS_ASPECTS", - "run_rollup", - # "run_terser", - "write_rollup_config", -) -load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5") - -# Note: the file is called "umd.js" and "umd.min.js" because of historical -# reasons. The format is actually amd and not umd, but we are afraid to rename -# the file because that would likely break the IDE and other integrations that -# have the path hardcoded in them. -_ROLLUP_OUTPUTS = { - "build_umd": "%{name}.umd.js", - # min bundle is not used at the moment. Disable to speed up build - # "build_umd_min": "%{name}.umd.min.js", -} - -DEPS_ASPECTS = [esm5_outputs_aspect] - -# Workaround skydoc bug which assumes ROLLUP_DEPS_ASPECTS is a str type -[DEPS_ASPECTS.append(a) for a in ROLLUP_DEPS_ASPECTS] - -def _ls_rollup_bundle(ctx): - esm5_sources = flatten_esm5(ctx) - rollup_config = write_rollup_config( - ctx, - root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), - output_format = "amd", - ) - run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_umd) - - # source_map = run_terser(ctx, ctx.outputs.build_umd, ctx.outputs.build_umd_min) - return DefaultInfo( - files = depset([ - ctx.outputs.build_umd, - # ctx.outputs.build_umd_min, - # source_map, - ]), - ) - -ls_rollup_bundle = rule( - implementation = _ls_rollup_bundle, - attrs = dict(ROLLUP_ATTRS, **{ - "deps": attr.label_list( - doc = """Other targets that provide JavaScript files. - Typically this will be `ts_library` or `ng_module` targets.""", - aspects = DEPS_ASPECTS, - ), - }), - outputs = _ROLLUP_OUTPUTS, -) diff --git a/tools/defaults.bzl b/tools/defaults.bzl index 592b8eefe6be23..02133c915b0c2d 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -5,13 +5,15 @@ load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test") load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite", _ts_web_test = "ts_web_test", _ts_web_test_suite = "ts_web_test_suite") load("@npm_bazel_typescript//:index.bzl", _ts_library = "ts_library") load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") -load("//packages/bazel/src:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle") +load("//tools/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle") _DEFAULT_TSCONFIG_TEST = "//packages:tsconfig-test" _INTERNAL_NG_MODULE_API_EXTRACTOR = "//packages/bazel/src/api-extractor:api_extractor" _INTERNAL_NG_MODULE_COMPILER = "//packages/bazel/src/ngc-wrapped" _INTERNAL_NG_MODULE_XI18N = "//packages/bazel/src/ngc-wrapped:xi18n" _INTERNAL_NG_PACKAGER_PACKAGER = "//packages/bazel/src/ng_package:packager" +_INTERNAL_NG_PACKAGER_DEFALUT_TERSER_CONFIG_FILE = "//packages/bazel/src/ng_package:terser_config.default.json" +_INTERNAL_NG_PACKAGER_DEFAULT_ROLLUP_CONFIG_TMPL = "//packages/bazel/src/ng_package:rollup.config.js" # Packages which are versioned together on npm ANGULAR_SCOPED_PACKAGES = ["@angular/%s" % p for p in [ @@ -145,6 +147,8 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg license_banner = license_banner, replacements = PKG_GROUP_REPLACEMENTS, ng_packager = _INTERNAL_NG_PACKAGER_PACKAGER, + terser_config_file = _INTERNAL_NG_PACKAGER_DEFALUT_TERSER_CONFIG_FILE, + rollup_config_tmpl = _INTERNAL_NG_PACKAGER_DEFAULT_ROLLUP_CONFIG_TMPL, **kwargs ) diff --git a/tools/ng_rollup_bundle/BUILD.bazel b/tools/ng_rollup_bundle/BUILD.bazel new file mode 100644 index 00000000000000..eaa0d986789baa --- /dev/null +++ b/tools/ng_rollup_bundle/BUILD.bazel @@ -0,0 +1,22 @@ +package(default_visibility = ["//visibility:public"]) + +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") + +exports_files(["rollup.config.js"]) + +nodejs_binary( + name = "rollup_with_build_optimizer", + data = [ + "@npm//@angular-devkit/build-optimizer", + "@npm//is-builtin-module", + "@npm//rollup", + "@npm//rollup-plugin-amd", + "@npm//rollup-plugin-commonjs", + "@npm//rollup-plugin-json", + "@npm//rollup-plugin-node-resolve", + "@npm//rollup-plugin-sourcemaps", + ], + entry_point = "@npm//:node_modules/rollup/bin/rollup", + install_source_map_support = False, + visibility = ["//visibility:public"], +) diff --git a/tools/ng_rollup_bundle/ng_rollup_bundle.bzl b/tools/ng_rollup_bundle/ng_rollup_bundle.bzl new file mode 100644 index 00000000000000..a824112444724e --- /dev/null +++ b/tools/ng_rollup_bundle/ng_rollup_bundle.bzl @@ -0,0 +1,462 @@ +# 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 with Build Optimizer + + This provides a variant of the [rollup_bundle] rule that works better for Angular apps. + + It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get + better optimization. It also uses ESM5 format inputs, as this is what + build-optimizer is hard-coded to look for and transform. + + [rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html +""" + +load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package_bin") +load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NpmPackageInfo", "node_modules_aspect") +load("@build_bazel_rules_nodejs//internal/common:module_mappings.bzl", "get_module_mappings") +load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5") +load("@npm_bazel_terser//:index.bzl", "terser_minified") +load("@npm//@babel/cli:index.bzl", "babel") + +_NG_ROLLUP_BUNDLE_OUTPUTS = { + "bundle": "%{name}.js", + "sourcemap": "%{name}.js.map", +} + +def _rollup_module_mappings_aspect_impl(target, ctx): + mappings = get_module_mappings(target.label, ctx.rule.attr) + return struct(rollup_module_mappings = mappings) + +rollup_module_mappings_aspect = aspect( + _rollup_module_mappings_aspect_impl, + attr_aspects = ["deps"], +) + +_ROLLUP_MODULE_MAPPINGS_ATTR = "rollup_module_mappings" + +_NG_ROLLUP_BUNDLE_DEPS_ASPECTS = [esm5_outputs_aspect, rollup_module_mappings_aspect, node_modules_aspect] + +_NG_ROLLUP_BUNDLE_ATTRS = { + "build_optimizer": attr.bool( + doc = """Use build optimizer plugin""", + default = True, + ), + "plain_rollup_for_jit": attr.bool( + doc = """If ctx.var["compile"] == "jot" then plain rollup is used with es2015 inputs and without build optimizer""", + default = True, + ), + "srcs": attr.label_list( + doc = """JavaScript source files from the workspace. + These can use ES2015 syntax and ES Modules (import/export)""", + allow_files = True, + ), + "entry_point": attr.label( + doc = """The starting point of the application, passed as the `--input` flag to rollup. + + If the entry JavaScript file belongs to the same package (as the BUILD file), + you can simply reference it by its relative name to the package directory: + + ``` + ng_package( + name = "bundle", + entry_point = ":main.js", + ) + ``` + + You can specify the entry point as a typescript file so long as you also include + the ts_library target in deps: + + ``` + ts_library( + name = "main", + srcs = ["main.ts"], + ) + + ng_package( + name = "bundle", + deps = [":main"] + entry_point = ":main.ts", + ) + ``` + + The rule will use the corresponding `.js` output of the ts_library rule as the entry point. + + If the entry point target is a rule, it should produce a single JavaScript entry file that will be passed to the nodejs_binary rule. + For example: + + ``` + filegroup( + name = "entry_file", + srcs = ["main.js"], + ) + + ng_package( + name = "bundle", + entry_point = ":entry_file", + ) + ``` + """, + mandatory = True, + allow_single_file = True, + ), + "deps": attr.label_list( + doc = """Other targets that provide JavaScript files. + Typically this will be `ts_library` or `ng_module` targets.""", + aspects = _NG_ROLLUP_BUNDLE_DEPS_ASPECTS, + ), + "format": attr.string( + doc = """"Specifies the format of the generated bundle. One of the following: + +- `amd`: Asynchronous Module Definition, used with module loaders like RequireJS +- `cjs`: CommonJS, suitable for Node and other bundlers +- `esm`: Keep the bundle as an ES module file, suitable for other bundlers and inclusion as a `