diff --git a/devtools/packages.bzl b/devtools/packages.bzl deleted file mode 100644 index f29d6e79bf3..00000000000 --- a/devtools/packages.bzl +++ /dev/null @@ -1,51 +0,0 @@ -_CDK_ENTRY_POINTS = [ - "scrolling", - "tree", - "keycodes", - "collections", - "overlay", - "table", - "text-field", - "accordion", - "drag-drop", - "a11y", - "platform", -] - -_MATERIAL_ENTRY_POINTS = [ - "dialog", - "menu", - "slide-toggle", - "grid-list", - "tree", - "expansion", - "checkbox", - "select", - "input", - "button", - "core", - "progress-bar", - "snack-bar", - "icon", - "progress-spinner", - "tabs", - "card", - "form-field", - "tooltip", - "toolbar", -] - -ANGULAR_PACKAGES_CONFIG = [ - ("@angular/cdk", struct(entry_points = _CDK_ENTRY_POINTS)), - ("@angular/material", struct(entry_points = _MATERIAL_ENTRY_POINTS)), -] - -ANGULAR_PACKAGES = [ - struct( - name = name[len("@angular/"):], - entry_points = config.entry_points, - platform = config.platform if hasattr(config, "platform") else "browser", - module_name = name, - ) - for name, config in ANGULAR_PACKAGES_CONFIG -] diff --git a/devtools/tools/BUILD.bazel b/devtools/tools/BUILD.bazel index 644319d90c4..364cfcbef57 100644 --- a/devtools/tools/BUILD.bazel +++ b/devtools/tools/BUILD.bazel @@ -1,3 +1,19 @@ +load("//devtools/tools/linking:index.bzl", "link_package") + +package(default_visibility = ["//devtools:__subpackages__"]) + exports_files([ "bazel-karma-local-config.js", ]) + +link_package( + name = "angular_cdk", + package_name = "@angular/cdk", + npm_package = "@npm//@angular/cdk", +) + +link_package( + name = "angular_material", + package_name = "@angular/material", + npm_package = "@npm//@angular/material", +) diff --git a/devtools/tools/esbuild/BUILD.bazel b/devtools/tools/esbuild/BUILD.bazel index b770354e3e3..28fdb38a133 100644 --- a/devtools/tools/esbuild/BUILD.bazel +++ b/devtools/tools/esbuild/BUILD.bazel @@ -1,6 +1,5 @@ load("@build_bazel_rules_nodejs//:index.bzl", "js_library") load("//tools:defaults.bzl", "esbuild_config") -load(":index.bzl", "create_angular_bundle_targets") package(default_visibility = ["//visibility:public"]) @@ -46,5 +45,3 @@ esbuild_config( ":esbuild_base", ], ) - -create_angular_bundle_targets() diff --git a/devtools/tools/esbuild/index.bzl b/devtools/tools/esbuild/index.bzl index be4d7880b8b..d10c455c894 100644 --- a/devtools/tools/esbuild/index.bzl +++ b/devtools/tools/esbuild/index.bzl @@ -1,84 +1,4 @@ -load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "JSModuleInfo") -load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "LinkerPackageMappingInfo") -load("@npm//@angular/build-tooling/bazel/esbuild:index.bzl", "esbuild") -load("//devtools:packages.bzl", "ANGULAR_PACKAGES") - -""" - Starlark file exposing a definition for generating Angular linker-processed ESM bundles - for all entry-points the Angular framework packages expose. - - These linker-processed ESM bundles are useful as they can be integrated into the - spec bundling, or dev-app to avoid unnecessary re-linking of framework entry-points - every time the bundler executes. This helps with the overall development turnaround and - is more idiomatic as it allows caching of the Angular framework packages. -""" - -def _linker_mapping_impl(ctx): - return [ - # Pass through the `ExternalNpmPackageInfo` which is needed for the linker - # resolving dependencies which might be external. e.g. `rxjs` for `@angular/core`. - ctx.attr.package[ExternalNpmPackageInfo], - JSModuleInfo( - direct_sources = depset(ctx.files.srcs), - sources = depset(ctx.files.srcs), - ), - LinkerPackageMappingInfo( - mappings = depset([ - struct( - package_name = ctx.attr.module_name, - package_path = "", - link_path = "%s/%s" % (ctx.label.package, ctx.attr.subpath), - ), - ]), - node_modules_roots = depset([]), - ), - ] - -_linker_mapping = rule( - implementation = _linker_mapping_impl, - attrs = { - "package": attr.label(), - "srcs": attr.label_list(allow_files = False), - "subpath": attr.string(), - "module_name": attr.string(), - }, -) - -def _get_target_name_base(pkg, entry_point): - return "%s%s" % (pkg.name, "_%s" % entry_point if entry_point else "") - -def _create_bundle_targets(pkg, entry_point, module_name): - target_name_base = _get_target_name_base(pkg, entry_point) - fesm_bundle_path = "fesm2022/%s.mjs" % (entry_point if entry_point else pkg.name) - - esbuild( - name = "%s_linked_bundle" % target_name_base, - output = "%s/index.mjs" % target_name_base, - platform = pkg.platform, - entry_point = "@npm//:node_modules/@angular/%s/%s" % (pkg.name, fesm_bundle_path), - deps = ["@npm//@angular/%s" % pkg.name], - config = "//devtools/tools/esbuild:esbuild_config_esm", - # List of dependencies which should never be bundled into these linker-processed bundles. - external = ["rxjs", "@angular", "domino", "xhr2", "@material"], - ) - - _linker_mapping( - name = "%s_linked" % target_name_base, - srcs = [":%s_linked_bundle" % target_name_base], - package = "@npm//@angular/%s" % pkg.name, - module_name = module_name, - subpath = target_name_base, - ) - -def create_angular_bundle_targets(): - for pkg in ANGULAR_PACKAGES: - _create_bundle_targets(pkg, None, pkg.module_name) - - for entry_point in pkg.entry_points: - _create_bundle_targets(pkg, entry_point, "%s/%s" % (pkg.module_name, entry_point)) - LINKER_PROCESSED_FW_PACKAGES = [ - "//devtools/tools/esbuild:%s_linked" % _get_target_name_base(pkg, entry_point) - for pkg in ANGULAR_PACKAGES - for entry_point in [None] + pkg.entry_points + "//devtools/tools:angular_cdk", + "//devtools/tools:angular_material", ] diff --git a/devtools/tools/linking/BUILD.bazel b/devtools/tools/linking/BUILD.bazel new file mode 100644 index 00000000000..24e4d37b7b1 --- /dev/null +++ b/devtools/tools/linking/BUILD.bazel @@ -0,0 +1,19 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin") +load("//tools:defaults.bzl", "nodejs_binary") + +copy_to_bin( + name = "linker_srcs", + srcs = ["index.mjs"], +) + +nodejs_binary( + name = "linker_bin", + data = [ + ":linker_srcs", + "//packages/compiler-cli/linker/babel", + "@npm//@babel/core", + "@npm//tinyglobby", + ], + entry_point = ":index.mjs", + visibility = ["//devtools/tools:__subpackages__"], +) diff --git a/devtools/tools/linking/index.bzl b/devtools/tools/linking/index.bzl new file mode 100644 index 00000000000..d181590f1f3 --- /dev/null +++ b/devtools/tools/linking/index.bzl @@ -0,0 +1,19 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin") +load("//devtools/tools/linking:linker_mapping.bzl", "linker_mapping") + +def link_package(name, package_name, npm_package): + npm_package_bin( + name = "%s_package_out" % name, + data = [npm_package], + args = ["./external/npm/node_modules/%s" % package_name, "$(@D)"], + output_dir = True, + tool = "//devtools/tools/linking:linker_bin", + ) + + linker_mapping( + name = name, + srcs = [":%s_package_out" % name], + package = npm_package, + module_name = package_name, + subpath = "./%s_package_out" % name, + ) diff --git a/devtools/tools/linking/index.mjs b/devtools/tools/linking/index.mjs new file mode 100644 index 00000000000..f7ec77f25e5 --- /dev/null +++ b/devtools/tools/linking/index.mjs @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Google LLC 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.dev/license + */ + +/** + * @fileoverview + * + * Script that copies the npm package contents of e.g. `@angular/cdk` over into + * a new output directory while performing Angular linking using the local + * Angular compiler-cli version. + * + * This is necessary for the devtools as we don't want to rely on JIT compilation, + * and consumed libraries like Angular CDK, or Angular Material are only shipping + * partial compilation output to npm. + */ + +import linkerBabelPlugin from '../../../packages/compiler-cli/linker/babel/index.mjs'; +import {transformAsync} from '@babel/core'; +import {readFile, writeFile, mkdir} from 'node:fs/promises'; +import {globSync} from 'tinyglobby'; +import path from 'path'; + +async function main() { + const [packageDir, outDir] = process.argv.slice(2); + + // Copy without preserving readonly permissions from Bazel. + await Promise.all( + globSync('**/*', {cwd: packageDir}).map(async (filePath) => { + await mkdir(path.dirname(path.join(outDir, filePath)), {recursive: true}); + await writeFile(path.join(outDir, filePath), await readFile(path.join(packageDir, filePath))); + }), + ); + + const fesmBundles = globSync('fesm2022/**/*.mjs', {cwd: outDir}); + const tasks = []; + const babelOptions = { + plugins: [ + [ + linkerBabelPlugin, + { + // We compile with an unstamped version of the compiler, so ignore. + unknownDeclarationVersionHandling: 'ignore', + }, + ], + ], + }; + + for (const bundleFile of fesmBundles) { + tasks.push( + (async () => { + const filePath = path.join(outDir, bundleFile); + const content = await readFile(filePath, 'utf8'); + const result = await transformAsync(content, {...babelOptions, filename: filePath}); + + await writeFile(path.join(outDir, bundleFile), result.code); + })(), + ); + } + + await Promise.all(tasks); +} + +main().catch((e) => { + console.error(e, e.stack); + process.exitCode = 1; +}); diff --git a/devtools/tools/linking/linker_mapping.bzl b/devtools/tools/linking/linker_mapping.bzl new file mode 100644 index 00000000000..72455d29b50 --- /dev/null +++ b/devtools/tools/linking/linker_mapping.bzl @@ -0,0 +1,33 @@ +load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "JSModuleInfo") +load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "LinkerPackageMappingInfo") + +def _linker_mapping_impl(ctx): + return [ + # Pass through the `ExternalNpmPackageInfo` which is needed for the linker + # resolving dependencies which might be external. e.g. `rxjs` for `@angular/core`. + ctx.attr.package[ExternalNpmPackageInfo], + JSModuleInfo( + direct_sources = depset(ctx.files.srcs), + sources = depset(ctx.files.srcs), + ), + LinkerPackageMappingInfo( + mappings = depset([ + struct( + package_name = ctx.attr.module_name, + package_path = "", + link_path = "%s/%s" % (ctx.label.package, ctx.attr.subpath), + ), + ]), + node_modules_roots = depset([]), + ), + ] + +linker_mapping = rule( + implementation = _linker_mapping_impl, + attrs = { + "package": attr.label(), + "srcs": attr.label_list(allow_files = False), + "subpath": attr.string(), + "module_name": attr.string(), + }, +)