| load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary", "pkg_web") |
| load("@io_bazel_rules_docker//container:container.bzl", "container_image") |
| load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image") |
| load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") |
| load("@npm//@babel/cli:index.bzl", "babel") |
| load("@npm//@bazel/concatjs:index.bzl", "concatjs_devserver") |
| load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") |
| load("@npm//@bazel/terser:index.bzl", "terser_minified") |
| load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_library") |
| load("@npm//history-server:index.bzl", "history_server") |
| load("@npm//html-insert-assets:index.bzl", "html_insert_assets") |
| load("//tools:angular_prerender.bzl", "ng_prerender", "ng_prerender_test") |
| load("//tools:angular_ts_library.bzl", "ng_ts_library") |
| load("//tools:ngsw_config.bzl", "ngsw_config") |
| |
| package(default_visibility = ["//:__subpackages__"]) |
| |
| ts_config( |
| name = "tsconfig-test", |
| src = "tsconfig-test.json", |
| deps = [":tsconfig.json"], |
| ) |
| |
| ts_config( |
| name = "tsconfig-server", |
| src = "tsconfig.server.json", |
| deps = [":tsconfig.json"], |
| ) |
| |
| # Run the sass compiler to output "styles.css" |
| # TODO(alexeagle): demonstrate the sass_library rule too |
| sass_binary( |
| name = "styles", |
| src = "styles.scss", |
| ) |
| |
| ts_library( |
| name = "initialize_testbed", |
| testonly = 1, |
| srcs = [ |
| "initialize_testbed.ts", |
| ], |
| deps = [ |
| "@npm//@angular/core", |
| "@npm//@angular/platform-browser-dynamic", |
| "@npm//@types", |
| ], |
| ) |
| |
| ng_ts_library( |
| name = "src", |
| srcs = [ |
| "main.dev.ts", |
| "main.prod.ts", |
| ], |
| tsconfig = ":tsconfig.json", |
| deps = [ |
| "//src/app", |
| "@npm//@angular/core", |
| "@npm//@angular/platform-browser", |
| "@npm//@angular/router", |
| "@npm//@ngrx/store", |
| ], |
| ) |
| |
| filegroup( |
| name = "rxjs_umd_modules", |
| srcs = [ |
| ":rxjs_shims.js", |
| "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", |
| ], |
| ) |
| |
| # Files that we serve in both development and production |
| _ASSETS = [ |
| # This label references an output of the "styles" sass_binary above. |
| ":styles.css", |
| ":manifest.webmanifest", |
| "//:favicon.ico", |
| |
| # Directly reference a file that came from @angular/material npm package |
| "@npm//:node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", |
| |
| # We load zone.js outside the bundle. That's because it's a "pollyfill" |
| # which speculates that such features might be available in a browser. |
| # Also it's tricky to configure dead code elimination to understand that |
| # zone.js is used, given that we don't have any import statement that |
| # imports from it. |
| "@npm//:node_modules/zone.js/dist/zone.min.js", |
| ] |
| |
| html_insert_assets( |
| name = "inject_scripts_for_dev", |
| outs = ["index.html"], |
| args = [ |
| "--html=$(execpath //src:example/index.html)", |
| "--out=$@", |
| "--roots=. $(RULEDIR)", |
| "--assets", |
| ] + ["$(execpath %s)" % s for s in _ASSETS] + [ |
| # This file doesn't exist during the build, but will be served by concatjs_devserver |
| "./_/ts_scripts.js", |
| ], |
| data = ["//src:example/index.html"] + _ASSETS, |
| ) |
| |
| # This devserver is written in Go and is super-fast. |
| # It doesn't run any bundler or code splitter. Instead, it concatenates |
| # named UMD and named AMD JavaScript code on-the-fly in-memory. |
| # This scales really well for massive codebases. |
| concatjs_devserver( |
| name = "devserver", |
| # Serve src/example/index.html at /index.html |
| additional_root_paths = ["src/example"], |
| # Run the program from the development version of the main |
| entry_module = "examples_angular/src/main.dev", |
| # These scripts will be included in the JS bundle after require.js |
| # They should have only named UMD modules, or require.js will throw. |
| scripts = [ |
| "@npm//:node_modules/tslib/tslib.js", |
| ":rxjs_umd_modules", |
| # We are manaully adding the bazel generated named-UMD date-fns bundle here as |
| # named-UMD bundles for non-APF npm packages are not yet automatically added. |
| # This file is generated by the npm_umd_bundle @npm//date-fns:date-fns__umd |
| # rule that is setup by yarn_install. |
| "@npm//date-fns:date-fns.umd.js", |
| ], |
| # Serve these files in addition to the JavaScript bundle |
| static_files = _ASSETS + [ |
| ":inject_scripts_for_dev", |
| "//src/assets", |
| ], |
| # Tell Bazel to build the sources first |
| deps = ["//src"], |
| ) |
| |
| rollup_bundle( |
| name = "bundle-es2015", |
| config_file = "rollup.config.js", |
| entry_points = { |
| ":main.prod.ts": "index", |
| }, |
| output_dir = True, |
| deps = [ |
| "//src", |
| "@npm//@rollup/plugin-commonjs", |
| "@npm//@rollup/plugin-node-resolve", |
| ], |
| ) |
| |
| babel( |
| name = "bundle-es5", |
| args = [ |
| "$(execpath :bundle-es2015)", |
| "--no-babelrc", |
| "--source-maps", |
| "--presets=@babel/preset-env", |
| "--plugins=@babel/plugin-transform-modules-systemjs", |
| "--out-dir", |
| "$(@D)", |
| ], |
| data = [ |
| ":bundle-es2015", |
| "@npm//@babel/preset-env", |
| ], |
| output_dir = True, |
| ) |
| |
| terser_minified( |
| name = "bundle-es2015.min", |
| src = ":bundle-es2015", |
| ) |
| |
| terser_minified( |
| name = "bundle-es5.min", |
| src = ":bundle-es5", |
| ) |
| |
| # take the skeleton index.html and use it to render the root, /hello and /todos routes |
| # this generates multiple index.html files, one for each requested route |
| ng_prerender( |
| name = "prerender", |
| index = "//src:example/index.prod.html", |
| # routes added here are rendered out to index files |
| # the root route / is always rendered |
| prerender_roots = [ |
| "hello", |
| "todos", |
| ], |
| ) |
| |
| # smoke test testing the generated root index for key elements |
| ng_prerender_test( |
| name = "prerender_test", |
| expected_elements = ["app-component"], |
| index = ":prerender.root", |
| route = "/", |
| ) |
| |
| html_insert_assets( |
| name = "inject_scripts_for_prod", |
| # we can't output "src/example/index.html" since that collides with the devmode file. |
| # pkg_web rule will re-root paths that start with _{name} by default |
| # so we output "_prodapp/src/example/index.html" so that it is mapped to |
| # `example/index.html` in the web package. |
| outs = ["_prodapp/src/example/index.html"], |
| args = [ |
| "--html=$(execpath //src:prerender.root)", |
| "--out=$@", |
| "--roots=. $(RULEDIR)", |
| "--assets", |
| ] + ["$(execpath %s)" % s for s in _ASSETS], |
| data = ["//src:prerender.root"] + _ASSETS, |
| ) |
| |
| # as there is a index for the /hello and /todos routes, each index needs the scripts and |
| # assets injected in |
| html_insert_assets( |
| name = "inject_scripts_for_prod_hello_route_index", |
| outs = ["_prodapp/src/example/hello/index.html"], |
| args = [ |
| "--html=$(execpath //src:prerender.hello)", |
| "--out=$@", |
| "--roots=. $(RULEDIR)", |
| "--assets", |
| ] + ["$(execpath %s)" % s for s in _ASSETS], |
| data = ["//src:prerender.hello"] + _ASSETS, |
| ) |
| |
| html_insert_assets( |
| name = "inject_scripts_for_prod_todos_route_index", |
| outs = ["_prodapp/src/example/todos/index.html"], |
| args = [ |
| "--html=$(execpath //src:prerender.todos)", |
| "--out=$@", |
| "--roots=. $(RULEDIR)", |
| "--assets", |
| ] + ["$(execpath %s)" % s for s in _ASSETS], |
| data = ["//src:prerender.todos"] + _ASSETS, |
| ) |
| |
| pkg_web( |
| name = "prodapp", |
| srcs = _ASSETS + [ |
| ":bundle-es2015.min", |
| ":bundle-es5.min", |
| # each injected index file should be listed here |
| ":inject_scripts_for_prod", |
| ":inject_scripts_for_prod_hello_route_index", |
| ":inject_scripts_for_prod_todos_route_index", |
| "//src/assets", |
| ":robots.txt", |
| # Service worker |
| "@npm//:node_modules/@angular/service-worker/ngsw-worker.js", |
| # Include polyfills that will be requested by old browsers |
| "@npm//:node_modules/systemjs/dist/system.js", |
| "@npm//:node_modules/core-js/client/core.min.js", |
| ], |
| # In production mode we serve some polyfills with script tags that have hard-coded paths in the index.html |
| # so we must serve them at that path, by stripping a prefix |
| additional_root_paths = [ |
| "npm/node_modules/core-js/client", |
| "npm/node_modules/systemjs/dist", |
| "npm/node_modules/@angular/service-worker", |
| ], |
| ) |
| |
| ngsw_config( |
| name = "pwa", |
| src = ":prodapp", |
| config = "//src:ngsw-config.json", |
| index_html = ":inject_scripts_for_prod", |
| ) |
| |
| history_server( |
| name = "prodserver", |
| data = [":pwa"], |
| templated_args = ["-a $$(rlocation $(rootpath :pwa))"], |
| ) |
| |
| nodejs_image( |
| name = "nodejs_image", |
| binary = ":prodserver", |
| # Actions created by this rule are I/O-bound, |
| # so there is no benefit to running them remotely |
| tags = ["local"], |
| ) |
| |
| container_image( |
| name = "image", |
| base = ":nodejs_image", |
| # Actions created by this rule are I/O-bound, |
| # so there is no benefit to running them remotely |
| tags = ["local"], |
| ) |
| |
| ts_library( |
| name = "universal_server_lib", |
| srcs = ["server.ts"], |
| deps = [ |
| "//src/app:app_server", |
| "@npm//@nguniversal/express-engine", |
| "@npm//@types/node", |
| "@npm//express", |
| ], |
| ) |
| |
| nodejs_binary( |
| name = "universal_server", |
| data = [ |
| ":pwa", |
| ":universal_server_lib", |
| ], |
| entry_point = ":server.ts", |
| templated_args = ["--bazel_patch_module_resolver"], |
| ) |