Repository: alexeagle/angular-bazel-example Branch: master Commit: 2f1abb9b0279 Files: 336 Total size: 580.1 KB Directory structure: gitextract_yf405e50/ ├── .bazelignore ├── .bazelrc ├── .circleci/ │ ├── bazel.rc │ └── config.yml ├── .clang-format ├── .firebaserc ├── .gitignore ├── BUILD.bazel ├── LICENSE ├── README.md ├── WORKSPACE ├── angular-metadata.tsconfig.json ├── angular.json ├── deployment.yaml ├── e2e/ │ ├── BUILD.bazel │ ├── protractor.on-prepare.js │ └── src/ │ ├── app.e2e-spec.ts │ └── app.po.ts ├── firebase.json ├── package.json ├── renovate.json ├── src/ │ ├── BUILD.bazel │ ├── app/ │ │ ├── BUILD.bazel │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── billing/ │ │ │ ├── BUILD.bazel │ │ │ ├── billing.module.ts │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp0/ │ │ │ │ │ ├── cmp0.component.html │ │ │ │ │ ├── cmp0.component.scss │ │ │ │ │ ├── cmp0.component.spec.ts │ │ │ │ │ └── cmp0.component.ts │ │ │ │ ├── cmp1/ │ │ │ │ │ ├── cmp1.component.html │ │ │ │ │ ├── cmp1.component.scss │ │ │ │ │ ├── cmp1.component.spec.ts │ │ │ │ │ └── cmp1.component.ts │ │ │ │ └── module0.module.ts │ │ │ └── module1/ │ │ │ ├── BUILD.bazel │ │ │ ├── cmp2/ │ │ │ │ ├── cmp2.component.html │ │ │ │ ├── cmp2.component.scss │ │ │ │ ├── cmp2.component.spec.ts │ │ │ │ └── cmp2.component.ts │ │ │ ├── cmp3/ │ │ │ │ ├── cmp3.component.html │ │ │ │ ├── cmp3.component.scss │ │ │ │ ├── cmp3.component.spec.ts │ │ │ │ └── cmp3.component.ts │ │ │ └── module1.module.ts │ │ ├── compute/ │ │ │ ├── BUILD.bazel │ │ │ ├── compute.module.ts │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp4/ │ │ │ │ │ ├── cmp4.component.html │ │ │ │ │ ├── cmp4.component.scss │ │ │ │ │ ├── cmp4.component.spec.ts │ │ │ │ │ └── cmp4.component.ts │ │ │ │ ├── cmp5/ │ │ │ │ │ ├── cmp5.component.html │ │ │ │ │ ├── cmp5.component.scss │ │ │ │ │ ├── cmp5.component.spec.ts │ │ │ │ │ └── cmp5.component.ts │ │ │ │ └── module0.module.ts │ │ │ └── module1/ │ │ │ ├── BUILD.bazel │ │ │ ├── cmp6/ │ │ │ │ ├── cmp6.component.html │ │ │ │ ├── cmp6.component.scss │ │ │ │ ├── cmp6.component.spec.ts │ │ │ │ └── cmp6.component.ts │ │ │ ├── cmp7/ │ │ │ │ ├── cmp7.component.html │ │ │ │ ├── cmp7.component.scss │ │ │ │ ├── cmp7.component.spec.ts │ │ │ │ └── cmp7.component.ts │ │ │ └── module1.module.ts │ │ ├── datastore/ │ │ │ ├── BUILD.bazel │ │ │ ├── datastore.module.ts │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp8/ │ │ │ │ │ ├── cmp8.component.html │ │ │ │ │ ├── cmp8.component.scss │ │ │ │ │ ├── cmp8.component.spec.ts │ │ │ │ │ └── cmp8.component.ts │ │ │ │ ├── cmp9/ │ │ │ │ │ ├── cmp9.component.html │ │ │ │ │ ├── cmp9.component.scss │ │ │ │ │ ├── cmp9.component.spec.ts │ │ │ │ │ └── cmp9.component.ts │ │ │ │ └── module0.module.ts │ │ │ └── module1/ │ │ │ ├── BUILD.bazel │ │ │ ├── cmp10/ │ │ │ │ ├── cmp10.component.html │ │ │ │ ├── cmp10.component.scss │ │ │ │ ├── cmp10.component.spec.ts │ │ │ │ └── cmp10.component.ts │ │ │ ├── cmp11/ │ │ │ │ ├── cmp11.component.html │ │ │ │ ├── cmp11.component.scss │ │ │ │ ├── cmp11.component.spec.ts │ │ │ │ └── cmp11.component.ts │ │ │ └── module1.module.ts │ │ ├── functions/ │ │ │ ├── BUILD.bazel │ │ │ ├── functions.module.ts │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp12/ │ │ │ │ │ ├── cmp12.component.html │ │ │ │ │ ├── cmp12.component.scss │ │ │ │ │ ├── cmp12.component.spec.ts │ │ │ │ │ └── cmp12.component.ts │ │ │ │ ├── cmp13/ │ │ │ │ │ ├── cmp13.component.html │ │ │ │ │ ├── cmp13.component.scss │ │ │ │ │ ├── cmp13.component.spec.ts │ │ │ │ │ └── cmp13.component.ts │ │ │ │ └── module0.module.ts │ │ │ └── module1/ │ │ │ ├── BUILD.bazel │ │ │ ├── cmp14/ │ │ │ │ ├── cmp14.component.html │ │ │ │ ├── cmp14.component.scss │ │ │ │ ├── cmp14.component.spec.ts │ │ │ │ └── cmp14.component.ts │ │ │ ├── cmp15/ │ │ │ │ ├── cmp15.component.html │ │ │ │ ├── cmp15.component.scss │ │ │ │ ├── cmp15.component.spec.ts │ │ │ │ └── cmp15.component.ts │ │ │ └── module1.module.ts │ │ ├── hello-world/ │ │ │ ├── BUILD.bazel │ │ │ ├── hello-world.component.html │ │ │ ├── hello-world.component.scss │ │ │ ├── hello-world.component.spec.ts │ │ │ ├── hello-world.component.ts │ │ │ └── hello-world.module.ts │ │ ├── home/ │ │ │ ├── BUILD.bazel │ │ │ ├── home.html │ │ │ └── home.ts │ │ ├── logging/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── logging.module.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp16/ │ │ │ │ │ ├── cmp16.component.html │ │ │ │ │ ├── cmp16.component.scss │ │ │ │ │ ├── cmp16.component.spec.ts │ │ │ │ │ └── cmp16.component.ts │ │ │ │ ├── cmp17/ │ │ │ │ │ ├── cmp17.component.html │ │ │ │ │ ├── cmp17.component.scss │ │ │ │ │ ├── cmp17.component.spec.ts │ │ │ │ │ └── cmp17.component.ts │ │ │ │ └── module0.module.ts │ │ │ └── module1/ │ │ │ ├── BUILD.bazel │ │ │ ├── cmp18/ │ │ │ │ ├── cmp18.component.html │ │ │ │ ├── cmp18.component.scss │ │ │ │ ├── cmp18.component.spec.ts │ │ │ │ └── cmp18.component.ts │ │ │ ├── cmp19/ │ │ │ │ ├── cmp19.component.html │ │ │ │ ├── cmp19.component.scss │ │ │ │ ├── cmp19.component.spec.ts │ │ │ │ └── cmp19.component.ts │ │ │ └── module1.module.ts │ │ ├── monitoring/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp20/ │ │ │ │ │ ├── cmp20.component.html │ │ │ │ │ ├── cmp20.component.scss │ │ │ │ │ ├── cmp20.component.spec.ts │ │ │ │ │ └── cmp20.component.ts │ │ │ │ ├── cmp21/ │ │ │ │ │ ├── cmp21.component.html │ │ │ │ │ ├── cmp21.component.scss │ │ │ │ │ ├── cmp21.component.spec.ts │ │ │ │ │ └── cmp21.component.ts │ │ │ │ └── module0.module.ts │ │ │ ├── module1/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp22/ │ │ │ │ │ ├── cmp22.component.html │ │ │ │ │ ├── cmp22.component.scss │ │ │ │ │ ├── cmp22.component.spec.ts │ │ │ │ │ └── cmp22.component.ts │ │ │ │ ├── cmp23/ │ │ │ │ │ ├── cmp23.component.html │ │ │ │ │ ├── cmp23.component.scss │ │ │ │ │ ├── cmp23.component.spec.ts │ │ │ │ │ └── cmp23.component.ts │ │ │ │ └── module1.module.ts │ │ │ └── monitoring.module.ts │ │ ├── networking/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp24/ │ │ │ │ │ ├── cmp24.component.html │ │ │ │ │ ├── cmp24.component.scss │ │ │ │ │ ├── cmp24.component.spec.ts │ │ │ │ │ └── cmp24.component.ts │ │ │ │ ├── cmp25/ │ │ │ │ │ ├── cmp25.component.html │ │ │ │ │ ├── cmp25.component.scss │ │ │ │ │ ├── cmp25.component.spec.ts │ │ │ │ │ └── cmp25.component.ts │ │ │ │ └── module0.module.ts │ │ │ ├── module1/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp26/ │ │ │ │ │ ├── cmp26.component.html │ │ │ │ │ ├── cmp26.component.scss │ │ │ │ │ ├── cmp26.component.spec.ts │ │ │ │ │ └── cmp26.component.ts │ │ │ │ ├── cmp27/ │ │ │ │ │ ├── cmp27.component.html │ │ │ │ │ ├── cmp27.component.scss │ │ │ │ │ ├── cmp27.component.spec.ts │ │ │ │ │ └── cmp27.component.ts │ │ │ │ └── module1.module.ts │ │ │ └── networking.module.ts │ │ ├── registry/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp28/ │ │ │ │ │ ├── cmp28.component.html │ │ │ │ │ ├── cmp28.component.scss │ │ │ │ │ ├── cmp28.component.spec.ts │ │ │ │ │ └── cmp28.component.ts │ │ │ │ ├── cmp29/ │ │ │ │ │ ├── cmp29.component.html │ │ │ │ │ ├── cmp29.component.scss │ │ │ │ │ ├── cmp29.component.spec.ts │ │ │ │ │ └── cmp29.component.ts │ │ │ │ └── module0.module.ts │ │ │ ├── module1/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp30/ │ │ │ │ │ ├── cmp30.component.html │ │ │ │ │ ├── cmp30.component.scss │ │ │ │ │ ├── cmp30.component.spec.ts │ │ │ │ │ └── cmp30.component.ts │ │ │ │ ├── cmp31/ │ │ │ │ │ ├── cmp31.component.html │ │ │ │ │ ├── cmp31.component.scss │ │ │ │ │ ├── cmp31.component.spec.ts │ │ │ │ │ └── cmp31.component.ts │ │ │ │ └── module1.module.ts │ │ │ └── registry.module.ts │ │ ├── storage/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp32/ │ │ │ │ │ ├── cmp32.component.html │ │ │ │ │ ├── cmp32.component.scss │ │ │ │ │ ├── cmp32.component.spec.ts │ │ │ │ │ └── cmp32.component.ts │ │ │ │ ├── cmp33/ │ │ │ │ │ ├── cmp33.component.html │ │ │ │ │ ├── cmp33.component.scss │ │ │ │ │ ├── cmp33.component.spec.ts │ │ │ │ │ └── cmp33.component.ts │ │ │ │ └── module0.module.ts │ │ │ ├── module1/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp34/ │ │ │ │ │ ├── cmp34.component.html │ │ │ │ │ ├── cmp34.component.scss │ │ │ │ │ ├── cmp34.component.spec.ts │ │ │ │ │ └── cmp34.component.ts │ │ │ │ ├── cmp35/ │ │ │ │ │ ├── cmp35.component.html │ │ │ │ │ ├── cmp35.component.scss │ │ │ │ │ ├── cmp35.component.spec.ts │ │ │ │ │ └── cmp35.component.ts │ │ │ │ └── module1.module.ts │ │ │ └── storage.module.ts │ │ ├── support/ │ │ │ ├── BUILD.bazel │ │ │ ├── index/ │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.spec.ts │ │ │ │ └── index.component.ts │ │ │ ├── module0/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp36/ │ │ │ │ │ ├── cmp36.component.html │ │ │ │ │ ├── cmp36.component.scss │ │ │ │ │ ├── cmp36.component.spec.ts │ │ │ │ │ └── cmp36.component.ts │ │ │ │ ├── cmp37/ │ │ │ │ │ ├── cmp37.component.html │ │ │ │ │ ├── cmp37.component.scss │ │ │ │ │ ├── cmp37.component.spec.ts │ │ │ │ │ └── cmp37.component.ts │ │ │ │ └── module0.module.ts │ │ │ ├── module1/ │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── cmp38/ │ │ │ │ │ ├── cmp38.component.html │ │ │ │ │ ├── cmp38.component.scss │ │ │ │ │ ├── cmp38.component.spec.ts │ │ │ │ │ └── cmp38.component.ts │ │ │ │ ├── cmp39/ │ │ │ │ │ ├── cmp39.component.html │ │ │ │ │ ├── cmp39.component.scss │ │ │ │ │ ├── cmp39.component.spec.ts │ │ │ │ │ └── cmp39.component.ts │ │ │ │ └── module1.module.ts │ │ │ └── support.module.ts │ │ └── todos/ │ │ ├── BUILD.bazel │ │ ├── reducers/ │ │ │ ├── BUILD.bazel │ │ │ └── reducers.ts │ │ ├── todos.component.html │ │ ├── todos.component.scss │ │ ├── todos.component.ts │ │ └── todos.module.ts │ ├── assets/ │ │ ├── BUILD.bazel │ │ └── landing.css │ ├── example/ │ │ ├── BUILD.bazel │ │ └── index.html │ ├── index.html │ ├── initialize_testbed.ts │ ├── lib/ │ │ └── shorten/ │ │ ├── BUILD.bazel │ │ ├── README.md │ │ └── shorten.ts │ ├── main.dev.ts │ ├── main.prod.ts │ ├── main.ts │ ├── rxjs_shims.js │ ├── shared/ │ │ └── material/ │ │ ├── BUILD.bazel │ │ └── material.module.ts │ ├── styles.scss │ ├── tsconfig-test.json │ └── tsconfig.json ├── third_party/ │ └── github.com/ │ └── bazelbuild/ │ └── bazel-toolchains/ │ └── bazelrc/ │ └── bazel-0.24.0.bazelrc └── tools/ ├── BUILD.bazel ├── defaults.bzl └── generator/ ├── index.js ├── src/ │ ├── build-file.js │ ├── clean.js │ ├── create-feature-module.js │ ├── feature-names.js │ ├── generate.js │ ├── ng-module.js │ ├── ng.js │ ├── reference-components.js │ └── utils.js └── templates/ ├── dashboard/ │ ├── component.html │ ├── component.scss │ ├── component.spec.ts │ └── component.ts ├── form/ │ ├── component.html │ ├── component.scss │ ├── component.spec.ts │ └── component.ts └── table/ ├── component.html ├── component.scss ├── component.spec.ts └── component.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bazelignore ================================================ node_modules ================================================ FILE: .bazelrc ================================================ # Make TypeScript and Angular compilation fast, by keeping a few copies of the # compiler running as daemons, and cache SourceFile AST's to reduce parse time. build --strategy=TypeScriptCompile=worker build --strategy=AngularTemplateCompile=worker # Don't create bazel-* symlinks in the WORKSPACE directory. # These require .gitignore and may scare users. # Also, it's a workaround for https://github.com/bazelbuild/rules_typescript/issues/12 # which affects the common case of having `tsconfig.json` in the WORKSPACE directory. # # Instead, you should run `bazel info bazel-bin` to find out where the outputs went. build --symlink_prefix=dist/ test --test_output=errors # Use the Angular 6 compiler build --define=compile=legacy # Turn off legacy external runfiles run --nolegacy_external_runfiles test --nolegacy_external_runfiles # Turn on --incompatible_strict_action_env which was on by default # in Bazel 0.21.0 but turned off again in 0.22.0. Follow # https://github.com/bazelbuild/bazel/issues/7026 for more details. # This flag is needed to so that the bazel cache is not invalidated # when running bazel via `yarn bazel`. # See https://github.com/angular/angular/issues/27514. build --incompatible_strict_action_env run --incompatible_strict_action_env test --incompatible_strict_action_env # Turn on managed directories feature in Bazel # This allows us to avoid installing a second copy of node_modules build --experimental_allow_incremental_repository_updates query --experimental_allow_incremental_repository_updates ############################### # Remote Build Execution support # Turn on these settings with # --config=remote ############################### # Load default settings for Remote Build Execution # When updating, the URLs of bazel_toolchains in packages/bazel/package.bzl # may also need to be updated (see https://github.com/angular/angular/pull/27935) import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/bazel-0.24.0.bazelrc # Point to our custom execution platform; see tools/BUILD.bazel build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular build:remote --host_platform=//tools:rbe_ubuntu1604-angular build:remote --platforms=//tools:rbe_ubuntu1604-angular # Remote instance. # rbe-shared-demo has 500 2-core workers # rbe-shared-demo2 has 100 8-core workers build:remote --remote_instance_name=projects/rbe-shared-demo2/instances/default_instance # Allow Bazel to consume all the workers build:remote --jobs=100 # Schedule actions both locally and remote, take the faster one # See https://blog.bazel.build/2019/02/01/dynamic-spawn-scheduler.html # NB: this setting is experimental and can cause obscure build failures. # The Bazel team does not recommend using this in all projects. # We can observe failures like Remote connection/protocol failed # build:remote --experimental_spawn_scheduler ################################################## # Load any settings specific to the current user. ################################################## # Needs to be last statement in this # config, as the user configuration should be able to overwrite flags from this file. # To use Remote Build Execution, this file should contain a line like # build --google_credentials=[path to the .json credentials file] try-import .bazelrc.user ================================================ FILE: .circleci/bazel.rc ================================================ # These options are enabled when running on CI # We do this by copying this file to /etc/bazel.bazelrc at the start of the build. # See remote cache documentation in /docs/BAZEL.md # Don't be spammy in the logs # TODO: re-enable after we deal with 10m timeout on circleci #build --noshow_progress # Don't run tests that are not meant to be run on circleci test --test_tag_filters=-no-circleci # Print all the options that apply to the build. # This helps us diagnose which options override others # (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) build --announce_rc # Prevent unstable environment variables from tainting cache keys build --experimental_strict_action_env # Save downloaded repositories such as the go toolchain # This directory can then be included in the CircleCI cache # It should save time running the first build build --experimental_repository_cache=/home/circleci/bazel_repository_cache # Workaround https://github.com/bazelbuild/bazel/issues/3645 # Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class # https://circleci.com/docs/2.0/configuration-reference/#resource_class build --local_resources=14336,8.0,1.0 # Use fixed chunk names for code-split bundles in CI # so that GitHub buildsize bot reports are accurate build --define=ROLLUP_BUNDLE_FIXED_CHUNK_NAMES=1 ================================================ FILE: .circleci/config.yml ================================================ # This file configures the build at https://circleci.com/gh/alexeagle/angular-bazel-example # Complete documentation is at https://circleci.com/docs/2.0/ # CircleCI lets us pick the key for storing one or more caches, to speed up subsequent builds. # We can use this to avoid re-fetching our dependencies from npm on every build. # To ensure we don't load a stale cache, we invalidate it based on the entries in the key: # - the checksum of Yarn's lock file # - the branch we are on, which really shouldn't be needed since the yarn lock file should be hermetic # - the docker image tag, working around an issue we saw where changing docker images causes permission # errors when restoring the cache, like when the user we run as changes var_1: &default_docker_image circleci/node:10.16 var_2: &cache_key node-0.16-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.8.0 # Each job will inherit these defaults var_3: &job_defaults working_directory: ~/angular-bazel-example docker: - image: *default_docker_image # After checkout, rebase on top of master, because we want to test the proposed merge of a # onto the target branch, not just test what's on the user's fork. # Similar to travis behavior, but not quite the same. # See https://discuss.circleci.com/t/1662 var_4: &post_checkout post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge" var_5: &restore_cache restore_cache: keys: - *cache_key var_6: &init_environment run: name: Initializing environment command: | sudo apt-get update # Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2) # and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1) # which are dependendies of chrome & needed for karma & protractor headless chrome tests. # This is a very small install with the whole init_environment step taking less than 8 seconds. # TODO(gregmagolan): switch rules_webtesting to use a chrome headless_shell binary which does # not depend on any dynamically linked libs sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1 # Also install libraries required for firefox sudo apt-get -y install libdbus-glib-1-2 # Setup /etc/bazel.bazelrc sudo cp .circleci/bazel.rc /etc/bazel.bazelrc var_7: &yarn_install run: name: Run yarn install command: yarn install var_8: &init_bazel run: name: Initializing Bazel command: | # Symlink fetched bazel to /usr/local/bin/bazel pathToBazel=$(ls $(realpath ./node_modules/@bazel/bazel-linux_x64)/bazel-*) sudo ln -fs $pathToBazel /usr/local/bin/bazel echo "Bazel version:" bazel version var_9: &attach_workspace attach_workspace: at: ~/ version: 2 # These jobs will run in parallel, and report separate statuses to GitHub PRs jobs: setup: <<: *job_defaults steps: - checkout: <<: *post_checkout - *restore_cache - *init_environment - *yarn_install - *init_bazel # Save all node_modules to the cache - save_cache: key: *cache_key paths: - "node_modules" # Persist any changes at this point to be reused by further jobs. - persist_to_workspace: root: ~/ paths: - ./angular-bazel-example lint: <<: *job_defaults steps: - *attach_workspace - *init_environment - *init_bazel # Run the Buildifier to check our Bazel rules for format issues. - run: 'yarn bazel:format --mode=check || (echo "BUILD files not formatted. Please run ''yarn bazel:format --mode=fix''" ; exit 1)' # Run the Buildifier to check our Bazel rules for lint issues. # Note: The `--lint=warn` will auto fixe (re-write) the affected files. - run: 'yarn bazel:format --lint=warn || (echo "BUILD files contain unresolved lint errors. Please fix manually the remaining errors." ; exit 1)' build_and_test: <<: *job_defaults resource_class: xlarge steps: - *attach_workspace - *init_environment - *init_bazel # Build and Test - run: yarn ng test - run: yarn ng e2e # Also run prodserver test which is not covered by `ng e2e` - run: bazel test //e2e:prodserver_test - store_artifacts: path: dist/bin/src/bundle.min.js destination: bundle.min.js - store_artifacts: path: dist/bin/src/bundle.cs.min destination: bundle.cs.min benchmark: <<: *job_defaults resource_class: xlarge steps: - *attach_workspace - *init_environment - *init_bazel # Run `bazel build` first as a temporary workaround to unexpected # benchmark failure when entire build runs withing ibazel-benchmark-runner # ``` # Error running Bazel unexpected EOF # [ibazel-benchmark-runner] iBazel process exited unexpectedly 4 null # error Command failed with exit code 1. # ``` # TODO(gregmagolan): remove this once issue is resolved - run: bazel build ... # Run the benchmark - run: yarn benchmark workflows: version: 2 default_workflow: jobs: - setup - lint: requires: - setup - build_and_test: requires: - setup - benchmark: requires: - setup ================================================ FILE: .clang-format ================================================ Language: JavaScript BasedOnStyle: Google ColumnLimit: 100 ================================================ FILE: .firebaserc ================================================ { "projects": { "default": "bazel-angular-io" } } ================================================ FILE: .gitignore ================================================ node_modules bazel-out dist .firebase .DS_Store .bazelrc.user ================================================ FILE: BUILD.bazel ================================================ load("@k8s_deploy//:defaults.bzl", "k8s_deploy") package(default_visibility = ["//:__subpackages__"]) # ts_library and ng_module use the `//:tsconfig.json` target # by default. This alias allows omitting explicit tsconfig # attribute. alias( name = "tsconfig.json", actual = "//src:tsconfig.json", ) k8s_deploy( name = "deploy", images = { "gcr.io/internal-200822/src:nodejs_image": "//src:image", }, template = ":deployment.yaml", ) ================================================ FILE: LICENSE ================================================ The MIT License Copyright (c) 2014-2017 Google, Inc. http://angular.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![CircleCI](https://circleci.com/gh/angular/angular-bazel-example.svg?style=svg)](https://circleci.com/gh/angular/angular-bazel-example) # Moved to rules_nodejs monorepo: https://github.com/bazelbuild/rules_nodejs/tree/master/examples/angular **Readme content is preserved below to avoid breaking links** **This is experimental, as part of Angular Labs! There may be breaking changes.** This is part of the ABC project. The overall goal is to make it possible to develop Angular applications the same way we do at Google. Learn more about Bazel and Angular at https://bazel.angular.io This example is deployed at https://bazel.angular.io/example ## Guide to the example This example is a monorepo, meant to show many different features and integrations that we expect are generally useful for enterprise use cases. - **Angular CLI**: you can use the `ng` command to run build, serve, test, and e2e - **Angular Libraries**: to maximize build incrementality, each Angular module is compiled as a separate step. This lets us re-use Angular libraries without having to publish them as npm packages. See `src/todos` for a typical `NgModule` compiled as a library for use in the application, using the `ng_module` rule in the `BUILD.bazel` file. - **TypeScript Libraries**: see `src/lib` for a trivial example of a pure-TS library that's consumed in the application, using the `ts_library` rule in the `BUILD.bazel` file. - **Sass**: we use Sass for all styling. Angular components import Sass files, and these are built by Bazel as independent processes calling the modern Sass compiler (written in Dart). - **Material design**: see `src/material` where we collect the material modules we use. - **Redux-style state management**: see `src/reducers` where we use the [NgRx Store](https://ngrx.io/guide/store). - **Lazy loading**: in production mode, the application is served in chunks. Run `ng serve --prod` - **Differential loading**: in production mode, we load a pair of ` ================================================ FILE: src/index.html ================================================ ABC: Angular Buildtools Convergence

Angular and Bazel

Build and test Angular applications at scale like Google

Incremental re-builds

A small change to your app should require a small re-build.

cli

Works with Angular CLI

Use the same ng commands you're used to.

Cloud Ready

Expand your build and test to run in parallel on a cluster of workers.

labs

Part of Angular Labs

Not a stable, supported API, so use Bazel with caution in production applications. We expect to promote it to stable for Angular version 9.

📚 Resources

Projects

Talks

  • Building Apps Like Google with Angular, Bazel, and GCP Video (Google Cloud Next, April 2019)
  • Layering in JS tooling Slides Video (ModernWeb Meetup, March 2019)
  • Angular, Bazel, and CLI Slides (AngularSF Meetup, January 2019)
  • Bazel in Angular CLI Video Slides (AngularNYC Meetup, January 2019)
  • The CLI Roadmap Video (AngularConnect, November 2018)
  • Develop Angular like Google Does Slides (AngularMIX, October 2018)
  • Building large Angular apps with Bazel Slides Video (BazelCon, October 2018)
  • How I love being ejected Slides Video (ng-conf, April 2018)
  • Angular Libraries in Bazel Slides (AngularSF Meetup, April 2018)
  • Bazel for building Angular Applications Video (St. Louis Angular Lunch, February 2018)
  • Building Angular Applications like Google Slides Video (AngularConnect, November 2017)
  • Bazel for Web Frontends Slides Video (BazelCon November 2017)
  • Angular with Bazel and Closure Slides Video (AngularMIX October 2017)

Articles

Trainings

  • LiveCoding a Bazel rule for Stylus Video (July 2019)
  • Bazel Training for Angular Team Slides (January 2019)
  • Full Stack development with Nx and Bazel Slides Video (ng-conf, April 2018)
  • ABC Deep Dive tech talk Slides Video (February 2018)

Examples

================================================ FILE: src/initialize_testbed.ts ================================================ /** * @fileoverview Provides a script to initialize TestBed before tests are run. * This file should be included in the "runtime_deps" of a "ts_web_test_suite" * rule. */ import {TestBed} from '@angular/core/testing'; import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); ================================================ FILE: src/lib/shorten/BUILD.bazel ================================================ load("@npm_bazel_typescript//:defs.bzl", "ts_library") package(default_visibility = ["//:__subpackages__"]) ts_library( name = "shorten", srcs = ["shorten.ts"], module_name = "@bazel/shorten", module_root = "shorten", ) # TODO(alexeagle): show how it can be deployed to npm ================================================ FILE: src/lib/shorten/README.md ================================================ # A simple example TS-only library This shows how a TypeScript library looks in a Bazel monorepo. ================================================ FILE: src/lib/shorten/shorten.ts ================================================ export function shorten(s: string, length: number) { if (s.length < length) return s; return s.substr(0, length - 3) + '...'; } ================================================ FILE: src/main.dev.ts ================================================ /** * Used to launch the application under Bazel development mode. */ import {platformBrowser} from '@angular/platform-browser'; import {AppModuleNgFactory} from './app/app.module.ngfactory'; platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); ================================================ FILE: src/main.prod.ts ================================================ /** * Used to launch the application under Bazel production mode. */ import {enableProdMode} from '@angular/core'; import {platformBrowser} from '@angular/platform-browser'; import {AppModuleNgFactory} from './app/app.module.ngfactory'; enableProdMode(); platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); ================================================ FILE: src/main.ts ================================================ /** * This main entry point is used to launch the app under the * @angular-devkit/build-angular, which is the default CLI * builder. Note that for AOT, the CLI will magically replace * the bootstrap by switching platform-browser-dynamic with * platform-browser. * This file is completely unused in the Bazel build. */ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.log(err)); ================================================ FILE: src/rxjs_shims.js ================================================ /** * @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 */ /** * @fileoverview these provide named UMD modules so that we can bundle * the application along with rxjs using the concatjs bundler. */ // rxjs/operators (function(factory) { if (typeof module === 'object' && typeof module.exports === 'object') { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === 'function' && define.amd) { define('rxjs/operators', ['exports', 'rxjs'], factory); } })(function(exports, rxjs) { 'use strict'; Object.keys(rxjs.operators).forEach(function(key) { exports[key] = rxjs.operators[key]; }); Object.defineProperty(exports, '__esModule', {value: true}); }); // rxjs/testing (function(factory) { if (typeof module === 'object' && typeof module.exports === 'object') { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === 'function' && define.amd) { define('rxjs/testing', ['exports', 'rxjs'], factory); } })(function(exports, rxjs) { 'use strict'; Object.keys(rxjs.testing).forEach(function(key) { exports[key] = rxjs.testing[key]; }); Object.defineProperty(exports, '__esModule', {value: true}); }); ================================================ FILE: src/shared/material/BUILD.bazel ================================================ load("@npm_angular_bazel//:index.bzl", "ng_module") package(default_visibility = ["//:__subpackages__"]) # We don't import from these, but the generated ngfactory code will NG_FACTORY_ADDED_IMPORTS = [ "@npm//@angular/forms", "@npm//@angular/platform-browser", ] ng_module( name = "material", srcs = glob(["*.ts"]), tsconfig = "//src:tsconfig.json", deps = NG_FACTORY_ADDED_IMPORTS + [ "@npm//@angular/core", "@npm//@angular/material", ], ) ================================================ FILE: src/shared/material/material.module.ts ================================================ import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { MatGridListModule } from '@angular/material/grid-list'; import { MatMenuModule } from '@angular/material/menu'; import { MatTableModule } from '@angular/material/table'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatTooltipModule } from '@angular/material/tooltip'; const matModules = [ MatButtonModule, MatCardModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatToolbarModule, MatSidenavModule, MatRadioModule, MatSelectModule, MatGridListModule, MatMenuModule, MatTableModule, MatPaginatorModule, MatTooltipModule ]; @NgModule({ imports: matModules, exports: matModules, }) export class MaterialModule { } ================================================ FILE: src/styles.scss ================================================ $theme-primary: #3e236e; $theme-secondary: #43a047; $default-font-stack: Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", serif; $modern-font-stack: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif; html, body { font-family: $default-font-stack; padding: 0; margin: 0; .content { margin: 2rem; } } app-component { display: flex; flex-direction: column; position: absolute; top: 0; right: 0; left: 0; bottom: 0; } mat-drawer-container { flex: 1; } nav > .mat-toolbar { background: $theme-secondary; color: #fff; .nav-logo { height: 28px; } .flex-spacer { flex: 1 1 auto; } a { text-decoration: none; } } .mobile-nav > a { flex: 1 1 auto; } .hide-small { @media screen and (max-width: 720px) { display: none !important; } } .show-small { @media screen and (min-width: 720px) { display: none !important; } } .mat-card { margin: 1em; } mat-drawer mat-nav-list .mat-icon { margin-right: 0.5em; } h1 { font-family: $modern-font-stack; color: $theme-primary; } ================================================ FILE: src/tsconfig-test.json ================================================ { "extends": "./tsconfig.json", "compilerOptions": { "types": ["jasmine"] } } ================================================ FILE: src/tsconfig.json ================================================ { "compilerOptions": { // Allow uses of these JS APIs "lib": [ "dom", "es5", "es2015.collection", "es2015.iterable", "es2015.promise" ], // Allow type-checking to succeed when we import from generated files // This lets us write out Angular AOT bootstrap by importing the // ngfactory file. // Under Angular Ivy, this is unneeded. "rootDirs": [ ".", "../dist/bin/src", ], // Permit decorator syntax "experimentalDecorators": true, // Don't scan the node_modules/@types folder for ambient types. // This would force us to have all the types in the dependencies of // each library. // Instead we'll be explicit about declaring ambient type dependencies // using the /// syntax. "types": [] } } ================================================ FILE: third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/bazel-0.24.0.bazelrc ================================================ # Copyright 2016 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This file is auto-generated from release/bazelrc.tpl and should not be # modified directly. # This .bazelrc file contains all of the flags required for the provided # toolchain with Remote Build Execution. # # This .bazelrc file also contains all of the flags required for the local # docker sandboxing. # Depending on how many machines are in the remote execution instance, setting # this higher can make builds faster by allowing more jobs to run in parallel. # Setting it too high can result in jobs that timeout, however, while waiting # for a remote machine to execute them. build:remote --jobs=50 # Set several flags related to specifying the platform, toolchain and java # properties. # These flags are duplicated rather than imported from (for example) # %workspace%/configs/ubuntu16_04_clang/1.2/toolchain.bazelrc to make this # bazelrc a standalone file that can be copied more easily. # These flags should only be used as is for the rbe-ubuntu16-04 container # and need to be adapted to work with other toolchain containers. build:remote --host_javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:jdk8 build:remote --javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:jdk8 build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8 build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8 build:remote --crosstool_top=@bazel_toolchains//configs/ubuntu16_04_clang/1.2/bazel_0.24.0/default:toolchain build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 # Platform flags: # The toolchain container used for execution is defined in the target indicated # by "extra_execution_platforms", "host_platform" and "platforms". # If you are using your own toolchain container, you need to create a platform # target with "constraint_values" that allow for the toolchain specified with # "extra_toolchains" to be selected (given constraints defined in # "exec_compatible_with"). # More about platforms: https://docs.bazel.build/versions/master/platforms.html build:remote --extra_toolchains=@bazel_toolchains//configs/ubuntu16_04_clang/1.2/bazel_0.24.0/cpp:cc-toolchain-clang-x86_64-default build:remote --extra_execution_platforms=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:rbe_ubuntu1604 build:remote --host_platform=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:rbe_ubuntu1604 build:remote --platforms=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:rbe_ubuntu1604 # Set various strategies so that all actions execute remotely. Mixing remote # and local execution will lead to errors unless the toolchain and remote # machine exactly match the host machine. build:remote --spawn_strategy=remote build:remote --strategy=Javac=remote build:remote --strategy=Closure=remote build:remote --strategy=Genrule=remote build:remote --define=EXECUTOR=remote # Enable the remote cache so action results can be shared across machines, # developers, and workspaces. build:remote --remote_cache=remotebuildexecution.googleapis.com # Enable remote execution so actions are performed on the remote systems. build:remote --remote_executor=remotebuildexecution.googleapis.com # Enable encryption. build:remote --tls_enabled=true # Set a higher timeout value, just in case. build:remote --remote_timeout=3600 # Enable authentication. This will pick up application default credentials by # default. You can use --auth_credentials=some_file.json to use a service # account credential instead. build:remote --auth_enabled=true # The following flags are only necessary for local docker sandboxing # with the rbe-ubuntu16-04 container. Use of these flags is still experimental. build:docker-sandbox --host_javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:jdk8 build:docker-sandbox --javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.2:jdk8 build:docker-sandbox --crosstool_top=@bazel_toolchains//configs/ubuntu16_04_clang/1.2/bazel_0.24.0/default:toolchain build:docker-sandbox --experimental_docker_image=gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:da0f21c71abce3bbb92c3a0c44c3737f007a82b60f8bd2930abc55fe64fc2729 build:docker-sandbox --spawn_strategy=docker build:docker-sandbox --strategy=Javac=docker build:docker-sandbox --strategy=Closure=docker build:docker-sandbox --strategy=Genrule=docker build:docker-sandbox --define=EXECUTOR=remote build:docker-sandbox --experimental_docker_verbose build:docker-sandbox --experimental_enable_docker_sandbox # The following flags enable the remote cache so action results can be shared # across machines, developers, and workspaces. build:remote-cache --remote_cache=remotebuildexecution.googleapis.com build:remote-cache --tls_enabled=true build:remote-cache --remote_timeout=3600 build:remote-cache --auth_enabled=true build:remote-cache --spawn_strategy=standalone build:remote-cache --strategy=Javac=standalone build:remote-cache --strategy=Closure=standalone build:remote-cache --strategy=Genrule=standalone ================================================ FILE: tools/BUILD.bazel ================================================ # The toolchain container used for execution is defined in the target indicated # by "extra_execution_platforms", "host_platform" and "platforms". # If you are using your own toolchain container, you need to create a platform # target with "constraint_values" that allow for the toolchain specified with # "extra_toolchains" to be selected (given constraints defined in # "exec_compatible_with"). # More about platforms: https://docs.bazel.build/versions/master/platforms.html # Note: we are working to remove the need for this custom platform and image platform( name = "rbe_ubuntu1604-angular", constraint_values = [ "@bazel_tools//platforms:x86_64", "@bazel_tools//platforms:linux", "@bazel_tools//tools/cpp:clang", "@bazel_toolchains//constraints:xenial", ], # RBE team maintains this Docker image for the angular/angular project. remote_execution_properties = """ properties: { name: "container-image" value:"docker://gcr.io/asci-toolchain/nosla-ubuntu16_04-webtest@sha256:e874885f5e3d9ac0c0d3176e5369cb5969467dbf9ad8d42b862829cec8d84b9b" } properties: { name: "dockerAddCapabilities" value: "SYS_ADMIN" } """, ) ================================================ FILE: tools/defaults.bzl ================================================ load("@npm_bazel_karma//:defs.bzl", _ts_web_test_suite = "ts_web_test_suite") def ts_web_test_suite(name, browsers = [], tags = [], **kwargs): _ts_web_test_suite( name = name, tags = tags + ["native", "no-bazelci"], browsers = browsers, **kwargs ) # BazelCI docker images are missing shares libs to run a subset browser tests: # mac: firefox does not work, chrome works # ubuntu: firefox and chrome do not work --- there are 0 tests to run _ts_web_test_suite( name = "bazelci_" + name, tags = tags + ["native", "no-circleci"], browsers = [ "@io_bazel_rules_webtesting//browsers:chromium-local", ], **kwargs ) ================================================ FILE: tools/generator/index.js ================================================ if (process.argv[2] === '--clean') { require('./src/clean')(); } else { require('./src/generate')(process.argv); } ================================================ FILE: tools/generator/src/build-file.js ================================================ const fs = require('fs'); module.exports.writeModuleBuildFile = function writeModuleBuildFile(file, {modIdx, scssFileAcc, tsFileAcc, htmlFileAcc}) { fs.writeFileSync( file, // Make Buildifier happy, don't format line below. `# Generated BUILD file, see /tools/generator load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") load("@npm_angular_bazel//:index.bzl", "ng_module") load("@npm_bazel_typescript//:defs.bzl", "ts_library") load("//tools:defaults.bzl", "ts_web_test_suite") package(default_visibility = ["//:__subpackages__"]) ${ scssFileAcc .map( (s, idx) => // Make Buildifier happy, don't format line below. `sass_binary( name = "module${idx}_styles", src = "${s}", )`).join('\n\n')} # We don't import from these, but the generated ngfactory code will NG_FACTORY_ADDED_IMPORTS = [ "@npm//@angular/cdk", "@npm//@angular/material", "@npm//@angular/forms", ] ng_module( name = "module${modIdx}", srcs = [ ${tsFileAcc.map(s => `"${s}"`).join(',\n ')}, "module${modIdx}.module.ts", ], assets = [ ${scssFileAcc.map((_, idx) => `":module${idx}_styles"`).join(`,\n `)}, ${htmlFileAcc.map(s => `"${s}"`).join(',\n ')}, ], tsconfig = "//src:tsconfig.json", deps = [ "@npm//@angular/common", "@npm//@angular/core", "//src/shared/material", ] + NG_FACTORY_ADDED_IMPORTS, ) ts_library( name = "test_lib", testonly = 1, srcs = glob(["**/*.spec.ts"]), tsconfig = "//src:tsconfig-test", deps = [ ":module${modIdx}", "@npm//@angular/core", "@npm//@angular/platform-browser", "@npm//@angular/platform-browser-dynamic", "@npm//@types/jasmine", "@npm//@types/node", ] + NG_FACTORY_ADDED_IMPORTS, ) ts_web_test_suite( name = "test", # do not sort bootstrap = [ "@npm//:node_modules/zone.js/dist/zone-testing-bundle.js", "@npm//:node_modules/reflect-metadata/Reflect.js", ], browsers = [ "@io_bazel_rules_webtesting//browsers:chromium-local", "@io_bazel_rules_webtesting//browsers:firefox-local", ], runtime_deps = [ "//src:initialize_testbed", ], deps = [ ":test_lib", "//src:rxjs_umd_modules", ], ) `); } module.exports.writeFeatureModuleBuildFile = function writeFeatureModuleBuildFile( file, {name, featureModuleDeps}) { fs.writeFileSync( file, // Make Buildifier happy, don't format line below. `# Generated BUILD file, see /tools/generator load("@npm_angular_bazel//:index.bzl", "ng_module") package(default_visibility = ["//:__subpackages__"]) # We don't import from these, but the generated ngfactory code will NG_FACTORY_ADDED_IMPORTS = [ "@npm//@angular/cdk", "@npm//@angular/material", "@npm//@angular/forms", ] ng_module( name = "${name}", srcs = [ "${name}.module.ts", "index/index.component.ts", ], assets = [ "index/index.component.html", ], tsconfig = "//src:tsconfig.json", deps = NG_FACTORY_ADDED_IMPORTS + [ "@npm//@angular/common", "@npm//@angular/core", "@npm//@angular/router", ${featureModuleDeps.map(s => `"${s}"`).join(',\n ')}, ], ) `); } ================================================ FILE: tools/generator/src/clean.js ================================================ const rimraf = require('rimraf'); const { FEATURES } = require('./feature-names'); const { updateNgModule, removeRoutesFromNgModule, removeRoutesFromAppComponentHtml } = require('./ng-module'); module.exports = function() { FEATURES.map(feature => `src/app/${feature.path}`).forEach(featPath => { console.log(`DELETE ${featPath}`); rimraf.sync(featPath); }); updateNgModule('src/app/app.module.ts'); removeRoutesFromNgModule('src/app/app-routing.module.ts'); removeRoutesFromAppComponentHtml('src/app/app.component.html'); } ================================================ FILE: tools/generator/src/create-feature-module.js ================================================ /** * Generate lots of "feature" code to bulk out this example to a given size. * By default we generate 40 components and check that in. * You can generate more by passing arguments, for example * yarn generate 10 10 * will make 1000 components total: for each of the ten "features", it will have 10 modules, each * has 10 components. */ const { ng, ngFromTemplate } = require('./ng'); const { writeModuleBuildFile, writeFeatureModuleBuildFile } = require('./build-file'); const { referenceComponents } = require('./reference-components'); const { updateNgModuleWithExtraDeps, updateRoutesInFeatureModule } = require('./ng-module'); let globalCmpIdx = 0; module.exports.makeFeatureModule = function makeFeatureModule(argv) { return function (feature) { const modulesPerFeature = argv[2] || 2; const componentsPerModule = argv[3] || 2; console.log('INFO ', `Feature: ${feature.path}, Modules: ${modulesPerFeature}, Components: ${componentsPerModule}`); ng('generate', 'module', feature.path, '--module', 'app'); ng('generate', 'component', `${feature.path}/index`, '--module', `${feature.path}`, '--inlineStyle=true'); const featureModuleDeps = []; const featureRootModuleDeps = []; const selectorAcc = []; for (let modIdx = 0; modIdx < modulesPerFeature; modIdx++) { ng('generate', 'module', `${feature.path}/module${modIdx}`, '--module', feature.path); featureRootModuleDeps.push(`//src/app/${feature.path}`); featureModuleDeps.push(`//src/app/${feature.path}/module${modIdx}`); const tsFileAcc = []; const scssFileAcc = []; const htmlFileAcc = []; for (let cmpIdx = 0; cmpIdx < componentsPerModule; cmpIdx++) { ngFromTemplate('generate', 'component', `${feature.path}/module${modIdx}/cmp${globalCmpIdx}`, '--module', `${feature.path}/module${modIdx}`, '--export=true', { componentName: `cmp${globalCmpIdx}`, featureName: feature.path}); tsFileAcc.push(`cmp${globalCmpIdx}/cmp${globalCmpIdx}.component.ts`); scssFileAcc.push(`cmp${globalCmpIdx}/cmp${globalCmpIdx}.component.scss`); htmlFileAcc.push(`cmp${globalCmpIdx}/cmp${globalCmpIdx}.component.html`); selectorAcc.push(`app-cmp${globalCmpIdx}`); globalCmpIdx++; } // Write a BUILD file to build the module writeModuleBuildFile(`src/app/${feature.path}/module${modIdx}/BUILD.bazel`, { modIdx, scssFileAcc, tsFileAcc, htmlFileAcc }); // Update feature modules with extra dependencies ie, MaterialModule, FormsModule... updateNgModuleWithExtraDeps(`src/app/${feature.path}/module${modIdx}/module${modIdx}.module.ts`, { className: `Module${modIdx}Module` }); } // Reference all the component selectors in the feature module's index component referenceComponents(`src/app/${feature.path}/index/index.component.html`, { selectorAcc }); // Wire up routing for the feature module updateRoutesInFeatureModule(`src/app/${feature.path}/${feature.path}.module.ts`); // Write a BUILD file to build the feature module writeFeatureModuleBuildFile(`src/app/${feature.path}/BUILD.bazel`, { name: feature.path, featureModuleDeps }); } } ================================================ FILE: tools/generator/src/feature-names.js ================================================ module.exports.FEATURES = [ { path: 'billing', icon: 'credit_card' }, { path: 'compute', icon: 'memory' }, { path: 'datastore', icon: 'view_agenda' }, { path: 'functions', icon: 'functions' }, { path: 'logging', icon: 'format_align_left' }, { path: 'monitoring', icon: 'device_unknown' }, { path: 'networking', icon: 'settings_ethernet' }, { path: 'registry', icon: 'ballot' }, { path: 'storage', icon: 'folder' }, { path: 'support', icon: 'headset_mic' } ]; ================================================ FILE: tools/generator/src/generate.js ================================================ const { makeFeatureModule } = require('./create-feature-module'); const { updateRoutesInAppComponentHtml, updateRoutesInNgModule } = require('./ng-module'); const { FEATURES } = require('./feature-names'); module.exports = function(argv) { // Create all feature modules and update BUILD files FEATURES.forEach(makeFeatureModule(argv)); // Update routing module with routes definition updateRoutesInNgModule( `src/app/app-routing.module.ts`, {routes: FEATURES.map(_feature => _feature.path)}); // Update src/app/app.component.html with links to the new generated modules updateRoutesInAppComponentHtml('src/app/app.component.html', {features: FEATURES}); } ================================================ FILE: tools/generator/src/ng-module.js ================================================ const fs = require('fs'); const { Project, ts } = require('ts-morph'); const { FEATURES } = require('./feature-names'); const { humanize, routeLinkRegex } = require('./utils'); module.exports.updateNgModule = function updateNgModule(ngAppModulePath = 'src/app/app.module.ts') { // 0-setup const project = new Project(); project.addExistingSourceFiles(ngAppModulePath); const importedFeautreModules = []; // 1-clean import statements in app.module.ts const ngAppModuleFile = project.getSourceFile(ngAppModulePath); ngAppModuleFile.getImportDeclarations().forEach(importDeclaration => { // get the import statment: // eg: import {LoggingModule} from './logging/logging.module'; const imp = importDeclaration.print(); FEATURES.forEach(feature => { if (imp.includes(feature.path)) { // we assume there is only one import, eg: {LoggingModule} // not many: {LoggingModule, BillingModule, UserModule} const moduleName = importDeclaration.getNamedImports().pop(); importedFeautreModules.push(moduleName.print()); importDeclaration.remove(); } }); }); // 2-clean the @NgModule.imports array in app.module.ts const ngAppModuleClass = ngAppModuleFile.getClass('AppModule'); const ngModuleDecorator = ngAppModuleClass.getDecorator('NgModule'); const ngModuleDecoratorArgs = ngModuleDecorator.getArguments().pop(); const cleanImportedFeatureModules = ngModuleDecoratorArgs.getProperty('imports').getDescendantsOfKind(ts.SyntaxKind.ArrayLiteralExpression).pop(); importedFeautreModules.forEach(featureName => { cleanImportedFeatureModules.getElements().forEach(element => { if (featureName === element.getText()) { cleanImportedFeatureModules.removeElement(element); } }); }); console.log(`UPDATE ${ngAppModulePath}`); project.saveSync(); }; module.exports.removeRoutesFromNgModule = function removeRoutesFromNgModule( ngAppRoutingModulePath = 'src/app/app-routing.module.ts') { // 0-setup const project = new Project(); project.addExistingSourceFiles(ngAppRoutingModulePath); // 1- clean the route statements in app-routing.module.ts const ngAppRoutingModuleFile = project.getSourceFile(ngAppRoutingModulePath); try { var routesArray = ngAppRoutingModuleFile.getVariableDeclaration('routes').getDescendantsOfKind(ts.SyntaxKind.ArrayLiteralExpression).pop(); } catch (error) { console.error('ERROR', `couldn't find declaration 'routes' in '${ngAppRoutingModulePath}'.`); process.exit(1); } const featureRoutePaths = FEATURES.map(feature => feature.path); routesArray.getElements().forEach(route => { // route is: // { // path: 'billing', // pathMatch: 'full', // loadChildren: () => import('./billing/billing.module.ngfactory').then(m => m.BillingModuleNgFactory) // } const path = route.getProperty('path').getInitializer().getLiteralValue(); if (featureRoutePaths.includes(path)) { routesArray.removeElement(route); } }); console.log(`UPDATE ${ngAppRoutingModulePath}`); project.saveSync(); }; module.exports.removeRoutesFromAppComponentHtml = function removeRoutesFromAppComponentHtml( file = 'src/app/app.component.html') { const appComponent = fs.readFileSync(file, { encoding: 'utf-8' }); fs.writeFileSync(file, appComponent.replace(routeLinkRegex, '')); console.log(`UPDATE ${file}`); }; module.exports.updateRoutesInNgModule = function updateRoutesInNgModule( ngAppRoutingModulePath = 'src/app/app-routing.module.ts', {routes}) { // 0-setup const project = new Project(); project.addExistingSourceFiles(ngAppRoutingModulePath); // 1- add route statements in app-routing.module.ts const ngAppRoutingModuleFile = project.getSourceFile(ngAppRoutingModulePath); try { var routesArray = ngAppRoutingModuleFile.getVariableDeclaration('routes').getDescendantsOfKind(ts.SyntaxKind.ArrayLiteralExpression).pop(); } catch (error) { console.error('ERROR', `couldn't find declaration 'routes' in '${ngAppRoutingModulePath}'.`); process.exit(1); } routes.forEach(route => { // write a route definition, eg: // { // path: '${route}', // pathMatch: 'full', // loadChildren: () => // import('./${route}/${route}.module.ngfactory').then(m => m.HelloWorldModuleNgFactory) // } if (routesArray.print().includes(route)) { console.log(`SKIP ${ngAppRoutingModulePath} (${route})`); } else { routesArray.addElement(writer => writer.writeLine('{') .writeLine(`path: '${route}',`) .writeLine(`pathMatch: 'full',`) .writeLine(`loadChildren: () => import('./${route}/${route}.module.ngfactory').then(m => m.${humanize(route, true)}ModuleNgFactory)`) .writeLine('}') ); } }); console.log(`UPDATE ${ngAppRoutingModulePath}`); project.saveSync(); }; module.exports.updateRoutesInFeatureModule = function updateRoutesInFeatureModule(file) { const originalFeatureModuleContent = fs.readFileSync(file, { encoding: 'utf-8' }); if (originalFeatureModuleContent.includes('RouterModule')) { console.log(`SKIP ${file} (RouterModule)`); return true; } fs.writeFileSync( file, originalFeatureModuleContent .replace('CommonModule,', `CommonModule, RouterModule.forChild([{path: '', component: IndexComponent}]),`) .replace( `from '@angular/common';`, `from '@angular/common';\nimport { RouterModule } from '@angular/router';`)); }; module.exports.updateRoutesInAppComponentHtml = function updateRoutesInAppComponentHtml( file = 'src/app/app.component.html', {features}) { let appComponent = fs.readFileSync(file, { encoding: 'utf-8' }); const linkTemplate = (feature) => ` ${feature.icon} ${humanize(feature.path)} `; features.forEach(feature => { if (!appComponent.includes(`routerLink="/${feature.path}"`)) { appComponent = appComponent.replace('', `${linkTemplate(feature)}\n `); } }); fs.writeFileSync(file, appComponent); console.log(`UPDATE ${file}`); }; module.exports.updateNgModuleWithExtraDeps = function updateNgModuleWithExtraDeps( featureNgModule, {className}) { // 0-setup const project = new Project(); project.addExistingSourceFiles(featureNgModule); // 1- add extra imports required by this module // eg: import { MaterialModule } from '../../../shared/material/material.module'; const featureNgModuleFile = project.getSourceFile(featureNgModule); if (featureNgModuleFile.print().includes('MaterialModule')) { console.log(`SKIP ${featureNgModule} (${className})`); return true; } featureNgModuleFile.addImportDeclarations([{ defaultImport: '{ ReactiveFormsModule }', moduleSpecifier: '@angular/forms' }, { defaultImport: '{ MaterialModule }', // TODO(manekinekko): use TS path aliases moduleSpecifier: '../../../shared/material/material.module' }]); // 2-add MaterialModule to @NgModule.imports const ngAppModuleClass = featureNgModuleFile.getClass(className); const ngModuleDecorator = ngAppModuleClass.getDecorator('NgModule'); const ngModuleDecoratorArgs = ngModuleDecorator.getArguments().pop(); const importedModules = ngModuleDecoratorArgs.getProperty('imports').getDescendantsOfKind(ts.SyntaxKind.ArrayLiteralExpression).pop(); importedModules.addElements(['ReactiveFormsModule', 'MaterialModule']); console.log(`UPDATE ${featureNgModule}`); project.saveSync(); }; ================================================ FILE: tools/generator/src/ng.js ================================================ const { spawnSync } = require('child_process'); const fs = require('fs'); const { humanize } = require('./utils'); module.exports.ng = function ng(...args) { spawnSync('ng', args, {stdio: 'inherit'}); }; const cmpIdx = 0; module.exports.ngFromTemplate = function ngFromTemplate(...args) { const {featureName, componentName} = args.pop(); // reconstruct the final component filepath // NOTE: args[2] = billing/module0/cmp0 const componentDestinationFile = `src/app/${args[2]}/${componentName}.component`; const templateDir = 'tools/generator/templates'; const templateTypes = ['form', 'dashboard', 'table']; const copyFromTemplate = (destinationFile, templateName, fileType) => { destinationFile = `${destinationFile}.${fileType}`; let content = fs.readFileSync( `${templateDir}/${templateName}/component.${fileType}`, {encoding: 'utf-8'}); content = content.replace(/__name__/g, componentName) .replace(/__Feature__/g, humanize(featureName, true)) .replace(/__Name__/g, humanize(componentName, true)); fs.writeFileSync(destinationFile, content); console.log('UPDATE', destinationFile, `(${templateName})`); }; // run ng and generate the component spawnSync('ng', args, {stdio: 'inherit'}); // cycle through template directories (so the generation is stable) const templateName = templateTypes[cmpIdx % templateTypes.length]; // copy template contents copyFromTemplate(componentDestinationFile, templateName, 'ts'); copyFromTemplate(componentDestinationFile, templateName, 'spec.ts'); copyFromTemplate(componentDestinationFile, templateName, 'scss'); copyFromTemplate(componentDestinationFile, templateName, 'html'); }; ================================================ FILE: tools/generator/src/reference-components.js ================================================ const fs = require('fs'); module.exports.referenceComponents = function referenceComponents(file, {selectorAcc}) { fs.writeFileSync( file, selectorAcc.map(s => `<${s}>`).join('\n')); }; ================================================ FILE: tools/generator/src/utils.js ================================================ const { FEATURES } = require('./feature-names'); // This will match" // folder Storage module.exports.routeLinkRegex = new RegExp(` feature.path).join('|')})">[\\w\\W\\s'"<>]+?<\/a>`, 'mg'); // Transforms "word-word" to "Word Word" or "WordWord" module.exports.humanize = function humanize(str, removeSpaces = false) { str = str.replace(/\-/g, ' ') .trim() // lowercase all word blocks .replace(/\b[A-Za-z]+\b/g, (word) => word.toLowerCase()) // uppercase first letter of each block .replace(/(^[a-z]|\s[a-z])/g, (first) => first.toUpperCase()) if (removeSpaces) { str = str.replace(/\s*/g, ''); } return str; }; ================================================ FILE: tools/generator/templates/dashboard/component.html ================================================

__Feature__ – Dashboard

{{card.title}}
Card Content Here
================================================ FILE: tools/generator/templates/dashboard/component.scss ================================================ .grid-container { margin: 20px; } .dashboard-card { position: absolute; top: 15px; left: 15px; right: 15px; bottom: 15px; } .more-button { position: absolute; top: 5px; right: 10px; } .dashboard-card-content { text-align: center; } ================================================ FILE: tools/generator/templates/dashboard/component.spec.ts ================================================ import {LayoutModule} from '@angular/cdk/layout'; import {NO_ERRORS_SCHEMA} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {MatButtonModule, MatCardModule, MatGridListModule, MatIconModule, MatMenuModule,} from '@angular/material'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {__Name__Component} from './__name__.component'; describe('__Name__Component', () => { let component: __Name__Component; let fixture: ComponentFixture<__Name__Component>; beforeEach(async(() => { TestBed .configureTestingModule({ declarations: [__Name__Component], imports: [ NoopAnimationsModule, LayoutModule, MatButtonModule, MatCardModule, MatGridListModule, MatIconModule, MatMenuModule, ], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(__Name__Component); component = fixture.componentInstance; fixture.detectChanges(); }); // Make the test more realistic by doing lots of assertions for (let i = 0; i < 50; i++) { it(`should compile ${i}`, () => { expect(component).toBeTruthy(); }); } }); ================================================ FILE: tools/generator/templates/dashboard/component.ts ================================================ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; import {Component} from '@angular/core'; import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; @Component({ selector: 'app-__name__', templateUrl: './__name__.component.html', styleUrls: ['./__name__.component.css'] }) export class __Name__Component { /** Based on the screen size, switch from standard to one column per row */ cards: Observable<{title: string; cols: number; rows: number;}[]> = this.breakpointObserver.observe(Breakpoints.Handset).pipe(map(({matches}) => { if (matches) { return [ {title: 'Card 1', cols: 1, rows: 1}, {title: 'Card 2', cols: 1, rows: 1}, {title: 'Card 3', cols: 1, rows: 1}, {title: 'Card 4', cols: 1, rows: 1} ]; } return [ {title: 'Card 1', cols: 2, rows: 1}, {title: 'Card 2', cols: 1, rows: 1}, {title: 'Card 3', cols: 1, rows: 2}, {title: 'Card 4', cols: 1, rows: 1} ]; })); constructor(private breakpointObserver: BreakpointObserver) {} } ================================================ FILE: tools/generator/templates/form/component.html ================================================
Billing
First name is required
Last name is required
Address is required
City is required
{{ state.name }} State is required
{{postalCode.value.length}} / 5
Free Shipping Priority Shipping Next Day Shipping
================================================ FILE: tools/generator/templates/form/component.scss ================================================ .full-width { width: 100%; } .shipping-card { min-width: 120px; margin: 20px auto; } .mat-radio-button { display: block; margin: 5px 0; } .row { display: flex; flex-direction: row; } .col { flex: 1; margin-right: 20px; } .col:last-child { margin-right: 0; } ================================================ FILE: tools/generator/templates/form/component.spec.ts ================================================ import {NO_ERRORS_SCHEMA} from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import {ReactiveFormsModule} from '@angular/forms'; import {MatButtonModule, MatCardModule, MatInputModule, MatRadioModule, MatSelectModule,} from '@angular/material'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import { __Name__Component } from './__name__.component'; describe('__Name__Component', () => { let component: __Name__Component; let fixture: ComponentFixture<__Name__Component>; beforeEach(async(() => { TestBed .configureTestingModule({ declarations: [__Name__Component], imports: [ NoopAnimationsModule, ReactiveFormsModule, MatButtonModule, MatCardModule, MatInputModule, MatRadioModule, MatSelectModule, ], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(__Name__Component); component = fixture.componentInstance; fixture.detectChanges(); }); // Make the test more realistic by doing lots of assertions for (let i = 0; i < 50; i++) { it('should compile', () => { expect(component).toBeTruthy(); }); } }); ================================================ FILE: tools/generator/templates/form/component.ts ================================================ import {Component} from '@angular/core'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; @Component({ selector: 'app-__name__', templateUrl: './__name__.component.html', styleUrls: ['./__name__.component.scss'] }) export class __Name__Component { addressForm: FormGroup = this.fb.group({ company: null, firstName: [null, Validators.required], lastName: [null, Validators.required], address: [null, Validators.required], address2: null, city: [null, Validators.required], state: [null, Validators.required], postalCode: [ null, Validators.compose( [Validators.required, Validators.minLength(5), Validators.maxLength(5)]) ], shipping: ['free', Validators.required] }); hasUnitNumber = false; states = [ {name: 'Alabama', abbreviation: 'AL'}, {name: 'Alaska', abbreviation: 'AK'}, {name: 'American Samoa', abbreviation: 'AS'}, {name: 'Arizona', abbreviation: 'AZ'}, {name: 'Arkansas', abbreviation: 'AR'}, {name: 'California', abbreviation: 'CA'}, {name: 'Colorado', abbreviation: 'CO'}, {name: 'Connecticut', abbreviation: 'CT'}, {name: 'Delaware', abbreviation: 'DE'}, {name: 'District Of Columbia', abbreviation: 'DC'}, {name: 'Federated States Of Micronesia', abbreviation: 'FM'}, {name: 'Florida', abbreviation: 'FL'}, {name: 'Georgia', abbreviation: 'GA'}, {name: 'Guam', abbreviation: 'GU'}, {name: 'Hawaii', abbreviation: 'HI'}, {name: 'Idaho', abbreviation: 'ID'}, {name: 'Illinois', abbreviation: 'IL'}, {name: 'Indiana', abbreviation: 'IN'}, {name: 'Iowa', abbreviation: 'IA'}, {name: 'Kansas', abbreviation: 'KS'}, {name: 'Kentucky', abbreviation: 'KY'}, {name: 'Louisiana', abbreviation: 'LA'}, {name: 'Maine', abbreviation: 'ME'}, {name: 'Marshall Islands', abbreviation: 'MH'}, {name: 'Maryland', abbreviation: 'MD'}, {name: 'Massachusetts', abbreviation: 'MA'}, {name: 'Michigan', abbreviation: 'MI'}, {name: 'Minnesota', abbreviation: 'MN'}, {name: 'Mississippi', abbreviation: 'MS'}, {name: 'Missouri', abbreviation: 'MO'}, {name: 'Montana', abbreviation: 'MT'}, {name: 'Nebraska', abbreviation: 'NE'}, {name: 'Nevada', abbreviation: 'NV'}, {name: 'New Hampshire', abbreviation: 'NH'}, {name: 'New Jersey', abbreviation: 'NJ'}, {name: 'New Mexico', abbreviation: 'NM'}, {name: 'New York', abbreviation: 'NY'}, {name: 'North Carolina', abbreviation: 'NC'}, {name: 'North Dakota', abbreviation: 'ND'}, {name: 'Northern Mariana Islands', abbreviation: 'MP'}, {name: 'Ohio', abbreviation: 'OH'}, {name: 'Oklahoma', abbreviation: 'OK'}, {name: 'Oregon', abbreviation: 'OR'}, {name: 'Palau', abbreviation: 'PW'}, {name: 'Pennsylvania', abbreviation: 'PA'}, {name: 'Puerto Rico', abbreviation: 'PR'}, {name: 'Rhode Island', abbreviation: 'RI'}, {name: 'South Carolina', abbreviation: 'SC'}, {name: 'South Dakota', abbreviation: 'SD'}, {name: 'Tennessee', abbreviation: 'TN'}, {name: 'Texas', abbreviation: 'TX'}, {name: 'Utah', abbreviation: 'UT'}, {name: 'Vermont', abbreviation: 'VT'}, {name: 'Virgin Islands', abbreviation: 'VI'}, {name: 'Virginia', abbreviation: 'VA'}, {name: 'Washington', abbreviation: 'WA'}, {name: 'West Virginia', abbreviation: 'WV'}, {name: 'Wisconsin', abbreviation: 'WI'}, {name: 'Wyoming', abbreviation: 'WY'} ]; constructor(private fb: FormBuilder) {} onSubmit() { alert('Thanks!'); } } ================================================ FILE: tools/generator/templates/table/component.html ================================================
ID {{row.id}} Progress {{row.progress}}% Name {{row.name}} Color {{row.color}}
================================================ FILE: tools/generator/templates/table/component.scss ================================================ table { width: 100%; } .mat-form-field { font-size: 14px; width: 100%; } td, th { width: 25%; } ================================================ FILE: tools/generator/templates/table/component.spec.ts ================================================ import {NO_ERRORS_SCHEMA} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {ReactiveFormsModule} from '@angular/forms'; import {MatPaginatorModule, MatSortModule, MatTableModule} from '@angular/material'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {__Name__Component} from './__name__.component'; describe('__Name__Component', () => { let component: __Name__Component; let fixture: ComponentFixture<__Name__Component>; beforeEach(async(() => { TestBed .configureTestingModule({ declarations: [__Name__Component], imports: [ NoopAnimationsModule, ReactiveFormsModule, MatTableModule, MatPaginatorModule, MatSortModule, ], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(__Name__Component); component = fixture.componentInstance; fixture.detectChanges(); }); // Make the test more realistic by doing lots of assertions for (let i = 0; i < 50; i++) { it('should compile', () => { expect(component).toBeTruthy(); }); } }); ================================================ FILE: tools/generator/templates/table/component.ts ================================================ import {Component, OnInit, ViewChild} from '@angular/core'; import {MatPaginator} from '@angular/material/paginator'; import {MatSort} from '@angular/material/sort'; import {MatTableDataSource} from '@angular/material/table'; export interface UserData { id: string; name: string; progress: string; color: string; } /** Constants used to fill up our data base. */ const COLORS: string[] = [ 'maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple', 'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray' ]; const NAMES: string[] = [ 'Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack', 'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper', 'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth' ]; /** * @title Data table with sorting, pagination, and filtering. */ @Component({ selector: 'app-__name__', styleUrls: ['__name__.component.scss'], templateUrl: '__name__.component.html', }) export class __Name__Component implements OnInit { displayedColumns: string[] = ['id', 'name', 'progress', 'color']; dataSource: MatTableDataSource; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; constructor() { // Create 1000 users const users = Array.from({length: 1000}, (_, k) => createNewUser(k + 1)); // Assign the data to the data source for the table to render this.dataSource = new MatTableDataSource(users); } ngOnInit() { this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; } applyFilter(filterValue: string) { this.dataSource.filter = filterValue.trim().toLowerCase(); if (this.dataSource.paginator) { this.dataSource.paginator.firstPage(); } } } /** Builds and returns a new User. */ function createNewUser(id: number): UserData { const name = NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' + NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.'; return { id: id.toString(), name: name, progress: Math.round(Math.random() * 100).toString(), color: COLORS[Math.round(Math.random() * (COLORS.length - 1))] }; }