Compare commits
59 Commits
api-overlo
...
6.0.0-rc.6
Author | SHA1 | Date | |
---|---|---|---|
4008e36e80 | |||
e47bb52084 | |||
ac2b530f4b | |||
64bf6edf00 | |||
adf6235479 | |||
04c18ac1aa | |||
1b26dd8cdb | |||
f721b06bde | |||
0bc8443e12 | |||
db17231597 | |||
540626a3a6 | |||
391bfcede5 | |||
d8de6488dd | |||
151fb66848 | |||
02424ff0d0 | |||
f0925d9705 | |||
212b806eda | |||
b9431e88fb | |||
7790cfa0d0 | |||
41b5149509 | |||
06f865640d | |||
824f74f27b | |||
5301c43eed | |||
f280d1aef1 | |||
5e741d42a6 | |||
0035d41030 | |||
08f447ceec | |||
8953f123e3 | |||
6274007e3b | |||
ee76be7783 | |||
a8c720bc3a | |||
ac47a3cd93 | |||
041458a3d2 | |||
2cb74b748f | |||
cb0bfe7a43 | |||
8ee11aeaa6 | |||
bf89fcb361 | |||
4f70c5b6f7 | |||
ad3ebec2a5 | |||
3f5d61f2dd | |||
11ada7f78b | |||
0b96bc7456 | |||
fefadadff3 | |||
946057ae29 | |||
d4293aaaaa | |||
e3dcc227f6 | |||
c9230dd90e | |||
234af9ba59 | |||
7204028d3e | |||
f7c55952bf | |||
e38e3bd135 | |||
cd20c01ba1 | |||
12a191ef3f | |||
65e67b3c3a | |||
32e57f6197 | |||
7de69ba29b | |||
44193c0b94 | |||
6db8241ffa | |||
33c594516c |
@ -1,19 +0,0 @@
|
|||||||
# Encryption
|
|
||||||
|
|
||||||
Based on https://github.com/circleci/encrypted-files
|
|
||||||
|
|
||||||
In the CircleCI web UI, we have a secret variable called `KEY`
|
|
||||||
https://circleci.com/gh/angular/angular/edit#env-vars
|
|
||||||
which is only exposed to non-fork builds
|
|
||||||
(see "Pass secrets to builds from forked pull requests" under
|
|
||||||
https://circleci.com/gh/angular/angular/edit#advanced-settings)
|
|
||||||
|
|
||||||
We use this as a symmetric AES encryption key to encrypt tokens like
|
|
||||||
a GitHub token that enables publishing snapshots.
|
|
||||||
|
|
||||||
To create the github_token file, we take this approach:
|
|
||||||
- Find the angular-builds:token in http://valentine
|
|
||||||
- Go inside the ngcontainer docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it angular/ngcontainer`
|
|
||||||
- echo "https://[token]:@github.com" > credentials
|
|
||||||
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
|
||||||
- If needed, base64-encode the result so you can copy-paste it out of docker: `base64 github_token`
|
|
@ -12,8 +12,8 @@
|
|||||||
## IMPORTANT
|
## IMPORTANT
|
||||||
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
|
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
|
||||||
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
|
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
|
||||||
var_1: &docker_image angular/ngcontainer:0.3.3
|
var_1: &docker_image angular/ngcontainer:0.2.0
|
||||||
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.3
|
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.2.0
|
||||||
|
|
||||||
# Define common ENV vars
|
# Define common ENV vars
|
||||||
var_3: &define_env_vars
|
var_3: &define_env_vars
|
||||||
@ -63,7 +63,7 @@ jobs:
|
|||||||
- run: yarn install --frozen-lockfile --non-interactive
|
- run: yarn install --frozen-lockfile --non-interactive
|
||||||
- run: ./node_modules/.bin/gulp lint
|
- run: ./node_modules/.bin/gulp lint
|
||||||
|
|
||||||
test:
|
build:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
@ -80,152 +80,37 @@ jobs:
|
|||||||
|
|
||||||
- run: ls /home/circleci/bazel_repository_cache || true
|
- run: ls /home/circleci/bazel_repository_cache || true
|
||||||
- run: bazel info release
|
- run: bazel info release
|
||||||
- run: bazel run @nodejs//:yarn
|
- run: bazel run @yarn//:yarn
|
||||||
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
|
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
|
||||||
# This avoids waiting for the slowest build target to finish before running the first test
|
# This avoids waiting for the slowest build target to finish before running the first test
|
||||||
# See https://github.com/bazelbuild/bazel/issues/4257
|
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||||
# NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/...
|
# NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/...
|
||||||
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only
|
- run: bazel query --output=label //... | xargs bazel test
|
||||||
|
|
||||||
|
# We run the integration tests outside of Bazel for now.
|
||||||
|
# See comments inside this script.
|
||||||
|
- run: xvfb-run --auto-servernum ./integration/run_tests.sh
|
||||||
|
|
||||||
# CircleCI will allow us to go back and view/download these artifacts from past builds.
|
# CircleCI will allow us to go back and view/download these artifacts from past builds.
|
||||||
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
|
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
|
||||||
# The destination keys need be format {projectName}/{context}/{fileName} so that the github-robot can process them for size calculations
|
|
||||||
# projectName should remain consistant to group files
|
|
||||||
# context and fileName can be almost anything (within usual URI rules)
|
|
||||||
# There should only be exactly 2 forward slashes in the path
|
|
||||||
# This is so they're backwards compatiable with the existing data we have on bundle sizes
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
||||||
destination: core/hello_world/bundle
|
destination: packages/core/test/bundling/hello_world/bundle.min.js
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
||||||
destination: core/todo/bundle
|
destination: packages/core/test/bundling/todo/bundle.min.js
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
|
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.brotli
|
||||||
destination: core/hello_world/bundle.br
|
destination: packages/core/test/bundling/hello_world/bundle.min.js.brotli
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.brotli
|
||||||
destination: core/todo/bundle.br
|
destination: packages/core/test/bundling/todo/bundle.min.js.brotli
|
||||||
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: *cache_key
|
key: *cache_key
|
||||||
paths:
|
paths:
|
||||||
- "node_modules"
|
- "node_modules"
|
||||||
- "~/bazel_repository_cache"
|
- "~/bazel_repository_cache"
|
||||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
|
||||||
test_ivy_jit:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *define_env_vars
|
|
||||||
- checkout:
|
|
||||||
<<: *post_checkout
|
|
||||||
# See remote cache documentation in /docs/BAZEL.md
|
|
||||||
- run: .circleci/setup_cache.sh
|
|
||||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
|
||||||
- *setup-bazel-remote-cache
|
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
key: *cache_key
|
|
||||||
|
|
||||||
- run: bazel run @yarn//:yarn
|
|
||||||
- run: bazel query --output=label //... | xargs bazel test --define=compile=jit --build_tag_filters=ivy-jit --test_tag_filters=-manual,ivy-jit
|
|
||||||
|
|
||||||
test_ivy_aot:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *define_env_vars
|
|
||||||
- checkout:
|
|
||||||
<<: *post_checkout
|
|
||||||
# See remote cache documentation in /docs/BAZEL.md
|
|
||||||
- run: .circleci/setup_cache.sh
|
|
||||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
|
||||||
- *setup-bazel-remote-cache
|
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
key: *cache_key
|
|
||||||
|
|
||||||
- run: bazel run @yarn//:yarn
|
|
||||||
- run: bazel query --output=label //... | xargs bazel test --define=compile=local --build_tag_filters=ivy-local --test_tag_filters=-manual,ivy-local
|
|
||||||
|
|
||||||
# This job exists only for backwards-compatibility with old scripts and tests
|
|
||||||
# that rely on the pre-Bazel dist/packages-dist layout.
|
|
||||||
# It duplicates some work with the job above: we build the bazel packages
|
|
||||||
# twice. Even though we have a remote cache, these jobs will typically run in
|
|
||||||
# parallel so up-to-date outputs will not be available at the time the build
|
|
||||||
# starts.
|
|
||||||
# No new jobs should depend on this one.
|
|
||||||
build-packages-dist:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *define_env_vars
|
|
||||||
- checkout:
|
|
||||||
<<: *post_checkout
|
|
||||||
# See remote cache documentation in /docs/BAZEL.md
|
|
||||||
- run: .circleci/setup_cache.sh
|
|
||||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
|
||||||
- *setup-bazel-remote-cache
|
|
||||||
|
|
||||||
- run: bazel run @nodejs//:yarn
|
|
||||||
- run: scripts/build-packages-dist.sh
|
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
|
||||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: dist
|
|
||||||
paths:
|
|
||||||
- packages-dist
|
|
||||||
- packages-dist-ivy-jit
|
|
||||||
- packages-dist-ivy-local
|
|
||||||
|
|
||||||
# We run the integration tests outside of Bazel for now.
|
|
||||||
# They are a separate workflow job so that they can be easily re-run.
|
|
||||||
# When the tests are ported to bazel test targets, they should move to the "test"
|
|
||||||
# job above, as part of the bazel test command. That has flaky_test_attempts so the
|
|
||||||
# need to re-run manually should be alleviated.
|
|
||||||
# See comments inside the integration/run_tests.sh script.
|
|
||||||
integration_test:
|
|
||||||
<<: *job_defaults
|
|
||||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
|
||||||
# of memory. Together with the system under test, this can exhaust the RAM
|
|
||||||
# on a 4G worker so we use a larger machine here too.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *define_env_vars
|
|
||||||
- checkout:
|
|
||||||
<<: *post_checkout
|
|
||||||
- attach_workspace:
|
|
||||||
at: dist
|
|
||||||
- run: xvfb-run --auto-servernum ./integration/run_tests.sh
|
|
||||||
|
|
||||||
# This job updates the content of repos like github.com/angular/core-builds
|
|
||||||
# for every green build on angular/angular.
|
|
||||||
publish_snapshot:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
# See below - ideally this job should not trigger for non-upstream builds.
|
|
||||||
# But since it does, we have to check this condition.
|
|
||||||
- run:
|
|
||||||
name: Skip this job for Pull Requests and Fork builds
|
|
||||||
# Note, `|| true` on the end makes this step always exit 0
|
|
||||||
command: '[[
|
|
||||||
-v CIRCLE_PR_NUMBER
|
|
||||||
|| "$CIRCLE_PROJECT_USERNAME" != "angular"
|
|
||||||
|| "$CIRCLE_PROJECT_REPONAME" != "angular"
|
|
||||||
]] && circleci step halt || true'
|
|
||||||
- checkout:
|
|
||||||
<<: *post_checkout
|
|
||||||
- attach_workspace:
|
|
||||||
at: dist
|
|
||||||
# CircleCI has a config setting to force SSH for all github connections
|
|
||||||
# This is not compatible with our mechanism of using a Personal Access Token
|
|
||||||
# Clear the global setting
|
|
||||||
- run: git config --global --unset "url.ssh://git@github.com.insteadof"
|
|
||||||
- run:
|
|
||||||
name: Decrypt github credentials
|
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -k "${KEY}" -out ~/.git_credentials'
|
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
|
||||||
|
|
||||||
aio_monitoring:
|
aio_monitoring:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
@ -241,28 +126,7 @@ workflows:
|
|||||||
default_workflow:
|
default_workflow:
|
||||||
jobs:
|
jobs:
|
||||||
- lint
|
- lint
|
||||||
- test
|
- build
|
||||||
- test_ivy_jit
|
|
||||||
- test_ivy_aot
|
|
||||||
- build-packages-dist
|
|
||||||
- integration_test:
|
|
||||||
requires:
|
|
||||||
- build-packages-dist
|
|
||||||
- publish_snapshot:
|
|
||||||
# Note: no filters on this job because we want it to run for all upstream branches
|
|
||||||
# We'd really like to filter out pull requests here, but not yet available:
|
|
||||||
# https://discuss.circleci.com/t/workflows-pull-request-filter/14396/4
|
|
||||||
# Instead, the job just exits immediately at the first step.
|
|
||||||
requires:
|
|
||||||
# Only publish if tests and integration tests pass
|
|
||||||
- test
|
|
||||||
- test_ivy_jit
|
|
||||||
- test_ivy_aot
|
|
||||||
- integration_test
|
|
||||||
# Get the artifacts to publish from the build-packages-dist job
|
|
||||||
# since the publishing script expects the legacy outputs layout.
|
|
||||||
- build-packages-dist
|
|
||||||
|
|
||||||
aio_monitoring:
|
aio_monitoring:
|
||||||
jobs:
|
jobs:
|
||||||
- aio_monitoring
|
- aio_monitoring
|
||||||
|
Binary file not shown.
11
.github/angular-robot.yml
vendored
11
.github/angular-robot.yml
vendored
@ -1,14 +1,5 @@
|
|||||||
# Configuration for angular-robot
|
# Configuration for angular-robot
|
||||||
|
|
||||||
#options for the size plugin
|
|
||||||
size:
|
|
||||||
disabled: false
|
|
||||||
maxSizeIncrease: 1000
|
|
||||||
circleCiStatusName: "ci/circleci: build-packages-dist"
|
|
||||||
status:
|
|
||||||
disabled: false
|
|
||||||
context: "ci/angular: size"
|
|
||||||
|
|
||||||
# options for the merge plugin
|
# options for the merge plugin
|
||||||
merge:
|
merge:
|
||||||
# the status will be added to your pull requests
|
# the status will be added to your pull requests
|
||||||
@ -45,12 +36,10 @@ merge:
|
|||||||
- "packages/language-service/**"
|
- "packages/language-service/**"
|
||||||
- "**/.gitignore"
|
- "**/.gitignore"
|
||||||
- "**/.gitkeep"
|
- "**/.gitkeep"
|
||||||
- "**/package.json"
|
|
||||||
- "**/tsconfig-build.json"
|
- "**/tsconfig-build.json"
|
||||||
- "**/tsconfig.json"
|
- "**/tsconfig.json"
|
||||||
- "**/rollup.config.js"
|
- "**/rollup.config.js"
|
||||||
- "**/BUILD.bazel"
|
- "**/BUILD.bazel"
|
||||||
- "packages/**/integrationtest/**"
|
|
||||||
- "packages/**/test/**"
|
- "packages/**/test/**"
|
||||||
|
|
||||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
# alexeagle - Alex Eagle
|
# alexeagle - Alex Eagle
|
||||||
# alxhub - Alex Rickabaugh
|
# alxhub - Alex Rickabaugh
|
||||||
# andrewseguin - Andrew Seguin
|
# andrewseguin - Andrew Seguin
|
||||||
# brandonroberts - Brandon Roberts
|
|
||||||
# brocco - Mike Brocchi
|
# brocco - Mike Brocchi
|
||||||
|
# chuckjaz - Chuck Jazdzewski
|
||||||
# filipesilva - Filipe Silva
|
# filipesilva - Filipe Silva
|
||||||
# gkalpak - George Kalpakas
|
# gkalpak - George Kalpakas
|
||||||
# hansl - Hans Larsen
|
# hansl - Hans Larsen
|
||||||
@ -17,7 +17,6 @@
|
|||||||
# jasonaden - Jason Aden
|
# jasonaden - Jason Aden
|
||||||
# kapunahelewong - Kapunahele Wong
|
# kapunahelewong - Kapunahele Wong
|
||||||
# kara - Kara Erickson
|
# kara - Kara Erickson
|
||||||
# kyliau - Keen Yee Liau
|
|
||||||
# matsko - Matias Niemelä
|
# matsko - Matias Niemelä
|
||||||
# mhevery - Misko Hevery
|
# mhevery - Misko Hevery
|
||||||
# petebacondarwin - Pete Bacon Darwin
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
@ -94,7 +93,7 @@ groups:
|
|||||||
- "tools/bazel.rc"
|
- "tools/bazel.rc"
|
||||||
users:
|
users:
|
||||||
- alexeagle #primary
|
- alexeagle #primary
|
||||||
- kyliau
|
- chuckjaz
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery
|
- mhevery
|
||||||
- vikerman #fallback
|
- vikerman #fallback
|
||||||
@ -133,7 +132,7 @@ groups:
|
|||||||
- "packages/core/*"
|
- "packages/core/*"
|
||||||
users:
|
users:
|
||||||
- mhevery #primary
|
- mhevery #primary
|
||||||
- jasonaden
|
- chuckjaz
|
||||||
- kara
|
- kara
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
@ -163,7 +162,7 @@ groups:
|
|||||||
files:
|
files:
|
||||||
- "packages/compiler/*"
|
- "packages/compiler/*"
|
||||||
users:
|
users:
|
||||||
- alxhub #primary
|
- chuckjaz #primary
|
||||||
- vicb
|
- vicb
|
||||||
- mhevery
|
- mhevery
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
@ -237,7 +236,7 @@ groups:
|
|||||||
files:
|
files:
|
||||||
- "packages/language-service/*"
|
- "packages/language-service/*"
|
||||||
users:
|
users:
|
||||||
- kyliau #primary
|
- chuckjaz #primary
|
||||||
# needs secondary
|
# needs secondary
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
@ -353,7 +352,6 @@ groups:
|
|||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- brandonroberts
|
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
|
||||||
angular.io-marketing:
|
angular.io-marketing:
|
||||||
|
14
BUILD.bazel
14
BUILD.bazel
@ -11,7 +11,7 @@ exports_files([
|
|||||||
# This ensures that package.json in subdirectories get installed as well.
|
# This ensures that package.json in subdirectories get installed as well.
|
||||||
alias(
|
alias(
|
||||||
name = "install",
|
name = "install",
|
||||||
actual = "@nodejs//:yarn",
|
actual = "@yarn//:yarn",
|
||||||
)
|
)
|
||||||
|
|
||||||
node_modules_filegroup(
|
node_modules_filegroup(
|
||||||
@ -22,7 +22,6 @@ node_modules_filegroup(
|
|||||||
"jasmine",
|
"jasmine",
|
||||||
"minimist",
|
"minimist",
|
||||||
"protobufjs",
|
"protobufjs",
|
||||||
"protractor",
|
|
||||||
"reflect-metadata",
|
"reflect-metadata",
|
||||||
"source-map-support",
|
"source-map-support",
|
||||||
"tsickle",
|
"tsickle",
|
||||||
@ -35,9 +34,6 @@ node_modules_filegroup(
|
|||||||
"@types",
|
"@types",
|
||||||
"@webcomponents/custom-elements",
|
"@webcomponents/custom-elements",
|
||||||
],
|
],
|
||||||
patterns = [
|
|
||||||
"node_modules/protractor/**",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
@ -48,16 +44,14 @@ filegroup(
|
|||||||
"//:node_modules/zone.js/dist/zone.js",
|
"//:node_modules/zone.js/dist/zone.js",
|
||||||
"//:node_modules/zone.js/dist/zone-testing.js",
|
"//:node_modules/zone.js/dist/zone-testing.js",
|
||||||
"//:node_modules/zone.js/dist/task-tracking.js",
|
"//:node_modules/zone.js/dist/task-tracking.js",
|
||||||
"//:test-events.js",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs",
|
||||||
|
# do not sort
|
||||||
srcs = [
|
srcs = [
|
||||||
"//:node_modules/angular-1.5/angular.js",
|
|
||||||
"//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
|
||||||
"//:node_modules/angular-mocks/angular-mocks.js",
|
|
||||||
"//:node_modules/angular/angular.js",
|
"//:node_modules/angular/angular.js",
|
||||||
|
"//:node_modules/angular-mocks/angular-mocks.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
935
CHANGELOG.md
935
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -227,15 +227,10 @@ The following is the list of supported scopes:
|
|||||||
|
|
||||||
There are currently a few exceptions to the "use package name" rule:
|
There are currently a few exceptions to the "use package name" rule:
|
||||||
|
|
||||||
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g.
|
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||||
public path changes, package.json changes done to all packages, d.ts file/format changes, changes
|
|
||||||
to bundles, etc.
|
|
||||||
* **changelog**: used for updating the release notes in CHANGELOG.md
|
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
* **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||||
repo
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
|
||||||
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
|
|
||||||
specific package (e.g. `docs: fix typo in tutorial`).
|
|
||||||
|
|
||||||
### Subject
|
### Subject
|
||||||
The subject contains a succinct description of the change:
|
The subject contains a succinct description of the change:
|
||||||
|
17
README.md
17
README.md
@ -5,6 +5,10 @@
|
|||||||
[](https://www.npmjs.com/@angular/core)
|
[](https://www.npmjs.com/@angular/core)
|
||||||
|
|
||||||
|
|
||||||
|
[](https://saucelabs.com/u/angular2-ci)
|
||||||
|
|
||||||
|
*Safari (7+), iOS (7+) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||||
|
|
||||||
# Angular
|
# Angular
|
||||||
|
|
||||||
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
|
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
|
||||||
@ -13,19 +17,12 @@ Angular is a development platform for building mobile and desktop web applicatio
|
|||||||
|
|
||||||
[Get started in 5 minutes][quickstart].
|
[Get started in 5 minutes][quickstart].
|
||||||
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
[Learn about the latest improvements][changelog].
|
|
||||||
|
|
||||||
|
|
||||||
## Want to help?
|
## Want to help?
|
||||||
|
|
||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
[browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
|
[browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
|
||||||
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[quickstart]: https://angular.io/guide/quickstart
|
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
[ng]: http://angular.io
|
||||||
[ng]: https://angular.io
|
|
||||||
|
162
WORKSPACE
162
WORKSPACE
@ -1,106 +1,34 @@
|
|||||||
workspace(name = "angular")
|
workspace(name = "angular")
|
||||||
|
|
||||||
#
|
|
||||||
# Download Bazel toolchain dependencies as needed by build actions
|
|
||||||
#
|
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
|
url = "https://github.com/bazelbuild/rules_nodejs/archive/1931156c232a08356dfda02e9c8b0275c2e63c00.zip",
|
||||||
strip_prefix = "rules_nodejs-0.10.1",
|
strip_prefix = "rules_nodejs-1931156c232a08356dfda02e9c8b0275c2e63c00",
|
||||||
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
|
sha256 = "9cfe33276a6ac0076ee9ee159c4a2576f9851c0f437435b5ac19b2e592493078",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||||
name = "io_bazel_rules_webtesting",
|
|
||||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
|
check_bazel_version("0.11.1")
|
||||||
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
|
node_repositories(package_json = ["//:package.json"])
|
||||||
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
|
|
||||||
|
yarn_install(
|
||||||
|
name = "ts-api-guardian_runtime_deps",
|
||||||
|
package_json = "//tools/ts-api-guardian:package.json",
|
||||||
|
yarn_lock = "//tools/ts-api-guardian:yarn.lock",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_typescript",
|
name = "build_bazel_rules_typescript",
|
||||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.1.zip",
|
url = "https://github.com/bazelbuild/rules_typescript/archive/0.12.1.zip",
|
||||||
strip_prefix = "rules_typescript-0.15.1",
|
strip_prefix = "rules_typescript-0.12.1",
|
||||||
sha256 = "3792cc20ef13bb1d1d8b1760894c3320f02a87843e3a04fed7e8e454a75328b6",
|
sha256 = "24e2c36f60508c6d270ae4265b89b381e3f66d550e70c367ed3755ad8d7ce3b0",
|
||||||
)
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "io_bazel_rules_go",
|
|
||||||
url = "https://github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz",
|
|
||||||
sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a",
|
|
||||||
)
|
|
||||||
|
|
||||||
# This commit matches the version of buildifier in angular/ngcontainer
|
|
||||||
# If you change this, also check if it matches the version in the angular/ngcontainer
|
|
||||||
# version in /.circleci/config.yml
|
|
||||||
BAZEL_BUILDTOOLS_VERSION = "82b21607e00913b16fe1c51bec80232d9d6de31c"
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "com_github_bazelbuild_buildtools",
|
|
||||||
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
|
|
||||||
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
|
||||||
sha256 = "edb24c2f9c55b10a820ec74db0564415c0cf553fa55e9fc709a6332fb6685eff",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fetching the Bazel source code allows us to compile the Skylark linter
|
|
||||||
http_archive(
|
|
||||||
name = "io_bazel",
|
|
||||||
url = "https://github.com/bazelbuild/bazel/archive/968f87900dce45a7af749a965b72dbac51b176b3.zip",
|
|
||||||
strip_prefix = "bazel-968f87900dce45a7af749a965b72dbac51b176b3",
|
|
||||||
sha256 = "e373d2ae24955c1254c495c9c421c009d88966565c35e4e8444c082cb1f0f48f",
|
|
||||||
)
|
|
||||||
|
|
||||||
# We have a source dependency on the Devkit repository, because it's built with
|
|
||||||
# Bazel.
|
|
||||||
# This allows us to edit sources and have the effect appear immediately without
|
|
||||||
# re-packaging or "npm link"ing.
|
|
||||||
# Even better, things like aspects will visit the entire graph including
|
|
||||||
# ts_library rules in the devkit repository.
|
|
||||||
http_archive(
|
|
||||||
name = "angular_cli",
|
|
||||||
url = "https://github.com/angular/angular-cli/archive/v6.1.0-rc.0.zip",
|
|
||||||
strip_prefix = "angular-cli-6.1.0-rc.0",
|
|
||||||
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
|
|
||||||
)
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "org_brotli",
|
|
||||||
url = "https://github.com/google/brotli/archive/f9b8c02673c576a3e807edbf3a9328e9e7af6d7c.zip",
|
|
||||||
strip_prefix = "brotli-f9b8c02673c576a3e807edbf3a9328e9e7af6d7c",
|
|
||||||
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Load and install our dependencies downloaded above.
|
|
||||||
#
|
|
||||||
|
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
|
||||||
|
|
||||||
check_bazel_version("0.15.0")
|
|
||||||
node_repositories(package_json = ["//:package.json"])
|
|
||||||
|
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
|
||||||
|
|
||||||
go_rules_dependencies()
|
|
||||||
go_register_toolchains()
|
|
||||||
|
|
||||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
|
|
||||||
|
|
||||||
web_test_repositories()
|
|
||||||
browser_repositories(
|
|
||||||
chromium = True,
|
|
||||||
firefox = True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||||
|
|
||||||
ts_setup_workspace()
|
ts_setup_workspace()
|
||||||
|
|
||||||
#
|
|
||||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
|
||||||
#
|
|
||||||
|
|
||||||
local_repository(
|
local_repository(
|
||||||
name = "rxjs",
|
name = "rxjs",
|
||||||
path = "node_modules/rxjs/src",
|
path = "node_modules/rxjs/src",
|
||||||
@ -113,20 +41,54 @@ local_repository(
|
|||||||
path = "integration/bazel",
|
path = "integration/bazel",
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
# This commit matches the version of buildifier in angular/ngcontainer
|
||||||
# Ask Bazel to manage these toolchain dependencies for us.
|
# If you change this, also check if it matches the version in the angular/ngcontainer
|
||||||
# Bazel will run `yarn install` when one of these toolchains is requested during
|
# version in /.circleci/config.yml
|
||||||
# a build.
|
BAZEL_BUILDTOOLS_VERSION = "70bc7843bb9950fece2bc014ed16de03419e36e2"
|
||||||
#
|
|
||||||
|
|
||||||
yarn_install(
|
http_archive(
|
||||||
name = "ts-api-guardian_runtime_deps",
|
name = "com_github_bazelbuild_buildtools",
|
||||||
package_json = "//tools/ts-api-guardian:package.json",
|
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
|
||||||
yarn_lock = "//tools/ts-api-guardian:yarn.lock",
|
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
||||||
|
sha256 = "367c23a5fe7fc2a7cb57863d3718b4149f0e57426c48c8ad54c45348a0b53cc1",
|
||||||
)
|
)
|
||||||
|
|
||||||
yarn_install(
|
http_archive(
|
||||||
name = "http-server_runtime_deps",
|
name = "io_bazel_rules_go",
|
||||||
package_json = "//tools/http-server:package.json",
|
url = "https://github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz",
|
||||||
yarn_lock = "//tools/http-server:yarn.lock",
|
sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a",
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||||
|
|
||||||
|
go_rules_dependencies()
|
||||||
|
|
||||||
|
go_register_toolchains()
|
||||||
|
|
||||||
|
# Fetching the Bazel source code allows us to compile the Skylark linter
|
||||||
|
http_archive(
|
||||||
|
name = "io_bazel",
|
||||||
|
url = "https://github.com/bazelbuild/bazel/archive/5a35e72f9e97c06540c479f8c31512fb4656202f.zip",
|
||||||
|
strip_prefix = "bazel-5a35e72f9e97c06540c479f8c31512fb4656202f",
|
||||||
|
sha256 = "ed33a52874c14e3b487fb50f390c541fab9c81a33d986d38fb01766a66dbcd21",
|
||||||
|
)
|
||||||
|
|
||||||
|
# We have a source dependency on the Devkit repository, because it's built with
|
||||||
|
# Bazel.
|
||||||
|
# This allows us to edit sources and have the effect appear immediately without
|
||||||
|
# re-packaging or "npm link"ing.
|
||||||
|
# Even better, things like aspects will visit the entire graph including
|
||||||
|
# ts_library rules in the devkit repository.
|
||||||
|
http_archive(
|
||||||
|
name = "angular_devkit",
|
||||||
|
url = "https://github.com/angular/devkit/archive/v0.3.1.zip",
|
||||||
|
strip_prefix = "devkit-0.3.1",
|
||||||
|
sha256 = "31d4b597fe9336650acf13df053c1c84dcbe9c29c6a833bcac3819cd3fd8cad3",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "org_brotli",
|
||||||
|
url = "https://github.com/google/brotli/archive/c6333e1e79fb62ea088443f192293f964409b04e.zip",
|
||||||
|
strip_prefix = "brotli-c6333e1e79fb62ea088443f192293f964409b04e",
|
||||||
|
sha256 = "3f781988dee7dd3bcce2bf238294663cfaaf3b6433505bdb762e24d0a284d1dc",
|
||||||
)
|
)
|
||||||
|
@ -52,7 +52,8 @@ export class BuildCleaner {
|
|||||||
protected removeDir(dir: string) {
|
protected removeDir(dir: string) {
|
||||||
try {
|
try {
|
||||||
if (shell.test('-d', dir)) {
|
if (shell.test('-d', dir)) {
|
||||||
shell.chmod('-R', 'a+w', dir);
|
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||||
|
(shell as any).chmod('-R', 'a+w', dir);
|
||||||
shell.rm('-rf', dir);
|
shell.rm('-rf', dir);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -106,7 +106,8 @@ export class BuildCreator extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
shell.chmod('-R', 'a-w', outputDir);
|
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||||
|
(shell as any).chmod('-R', 'a-w', outputDir);
|
||||||
shell.rm('-f', inputFile);
|
shell.rm('-f', inputFile);
|
||||||
resolve();
|
resolve();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -98,7 +98,8 @@ class Helper {
|
|||||||
const prDir = this.getPrDir(pr, isPublic);
|
const prDir = this.getPrDir(pr, isPublic);
|
||||||
|
|
||||||
if (fs.existsSync(prDir)) {
|
if (fs.existsSync(prDir)) {
|
||||||
shell.chmod('-R', 'a+w', prDir);
|
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||||
|
(shell as any).chmod('-R', 'a+w', prDir);
|
||||||
shell.rm('-rf', prDir);
|
shell.rm('-rf', prDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "yarn clean-dist",
|
"prebuild": "yarn clean-dist",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"build-watch": "yarn build --watch",
|
"build-watch": "yarn tsc --watch",
|
||||||
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
||||||
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
|
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
|
||||||
"lint": "tslint --project tsconfig.json",
|
"lint": "tslint --project tsconfig.json",
|
||||||
@ -33,7 +33,7 @@
|
|||||||
"@types/jasmine": "^2.6.0",
|
"@types/jasmine": "^2.6.0",
|
||||||
"@types/jsonwebtoken": "^7.2.3",
|
"@types/jsonwebtoken": "^7.2.3",
|
||||||
"@types/node": "^8.0.30",
|
"@types/node": "^8.0.30",
|
||||||
"@types/shelljs": "^0.8.0",
|
"@types/shelljs": "^0.7.4",
|
||||||
"@types/supertest": "^2.0.3",
|
"@types/supertest": "^2.0.3",
|
||||||
"concurrently": "^3.5.0",
|
"concurrently": "^3.5.0",
|
||||||
"nodemon": "^1.12.1",
|
"nodemon": "^1.12.1",
|
||||||
|
@ -69,9 +69,9 @@
|
|||||||
"@types/express-serve-static-core" "*"
|
"@types/express-serve-static-core" "*"
|
||||||
"@types/mime" "*"
|
"@types/mime" "*"
|
||||||
|
|
||||||
"@types/shelljs@^0.8.0":
|
"@types/shelljs@^0.7.4":
|
||||||
version "0.8.0"
|
version "0.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.0.tgz#0caa56b68baae4f68f44e0dd666ab30b098e3632"
|
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.4.tgz#137b5f31306eaff4de120ffe5b9d74b297809cfc"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/glob" "*"
|
"@types/glob" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
set -eux -o pipefail
|
set -eux -o pipefail
|
||||||
exec 3>&1
|
exec 3>&1
|
||||||
|
|
||||||
echo -e "\n\n[`date`] - Updating the preview server..."
|
echo "\n\n[`date`] - Updating the preview server..."
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
readonly HOST_REPO_DIR=$1
|
readonly HOST_REPO_DIR=$1
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
"projects": {
|
"projects": {
|
||||||
"site": {
|
"site": {
|
||||||
"root": "",
|
"root": "",
|
||||||
"sourceRoot": "src",
|
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
@ -30,12 +29,36 @@
|
|||||||
"vendorChunk": false,
|
"vendorChunk": false,
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/assets",
|
{
|
||||||
"src/generated",
|
"glob": "**/*",
|
||||||
"src/app/search/search-worker.js",
|
"input": "src/assets",
|
||||||
"src/favicon.ico",
|
"output": "/assets"
|
||||||
"src/pwa-manifest.json",
|
},
|
||||||
"src/google385281288605d160.html",
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "src/generated",
|
||||||
|
"output": "/generated"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "app/search/search-worker.js",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "favicon.ico",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "pwa-manifest.json",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "google385281288605d160.html",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"glob": "custom-elements.min.js",
|
"glob": "custom-elements.min.js",
|
||||||
"input": "node_modules/@webcomponents/custom-elements",
|
"input": "node_modules/@webcomponents/custom-elements",
|
||||||
@ -48,14 +71,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
{
|
||||||
|
"input": "src/styles.scss"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [],
|
||||||
|
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"fast": {
|
|
||||||
"optimization": false
|
|
||||||
},
|
|
||||||
"next": {
|
"next": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
{
|
{
|
||||||
@ -88,9 +111,6 @@
|
|||||||
"browserTarget": "site:build"
|
"browserTarget": "site:build"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"fast": {
|
|
||||||
"browserTarget": "site:build:fast"
|
|
||||||
},
|
|
||||||
"next": {
|
"next": {
|
||||||
"browserTarget": "site:build:next"
|
"browserTarget": "site:build:next"
|
||||||
},
|
},
|
||||||
@ -117,15 +137,41 @@
|
|||||||
"tsConfig": "src/tsconfig.spec.json",
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
{
|
||||||
|
"input": "src/styles.scss"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/assets",
|
{
|
||||||
"src/generated",
|
"glob": "**/*",
|
||||||
"src/app/search/search-worker.js",
|
"input": "src/assets",
|
||||||
"src/favicon.ico",
|
"output": "/assets"
|
||||||
"src/pwa-manifest.json",
|
},
|
||||||
"src/google385281288605d160.html",
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "src/generated",
|
||||||
|
"output": "/generated"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "app/search/search-worker.js",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "favicon.ico",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "pwa-manifest.json",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "google385281288605d160.html",
|
||||||
|
"input": "src",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"glob": "custom-elements.min.js",
|
"glob": "custom-elements.min.js",
|
||||||
"input": "node_modules/@webcomponents/custom-elements",
|
"input": "node_modules/@webcomponents/custom-elements",
|
||||||
|
BIN
aio/content/examples/.DS_Store
vendored
Normal file
BIN
aio/content/examples/.DS_Store
vendored
Normal file
Binary file not shown.
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -60,8 +60,6 @@ dist/
|
|||||||
!rollup-config.js
|
!rollup-config.js
|
||||||
aot-compiler/**/*.d.ts
|
aot-compiler/**/*.d.ts
|
||||||
aot-compiler/**/*.factory.d.ts
|
aot-compiler/**/*.factory.d.ts
|
||||||
upgrade-phonecat-2-hybrid/aot/**/*
|
|
||||||
!upgrade-phonecat-2-hybrid/aot/index.html
|
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
!i18n/src/systemjs-text-plugin.js
|
!i18n/src/systemjs-text-plugin.js
|
||||||
|
@ -40,7 +40,5 @@ export class HighlightDirective {
|
|||||||
// #docregion color-2
|
// #docregion color-2
|
||||||
@Input() appHighlight: string;
|
@Input() appHighlight: string;
|
||||||
// #enddocregion color-2
|
// #enddocregion color-2
|
||||||
|
|
||||||
// #docregion
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
27
aio/content/examples/cli-quickstart/README.md
Normal file
27
aio/content/examples/cli-quickstart/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# MasterProject
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-rc.0.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||||
|
Before running the tests make sure you are serving the app via `ng serve`.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
@ -3,7 +3,7 @@
|
|||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js",
|
"!**/*.js",
|
||||||
"!**/*.[0-9].*",
|
"!**/*.[0-9].*",
|
||||||
"angular.json",
|
".angular-cli.json",
|
||||||
"protractor.conf.js"
|
"protractor.conf.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,18 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|||||||
selector: 'app-voter',
|
selector: 'app-voter',
|
||||||
template: `
|
template: `
|
||||||
<h4>{{name}}</h4>
|
<h4>{{name}}</h4>
|
||||||
<button (click)="vote(true)" [disabled]="didVote">Agree</button>
|
<button (click)="vote(true)" [disabled]="voted">Agree</button>
|
||||||
<button (click)="vote(false)" [disabled]="didVote">Disagree</button>
|
<button (click)="vote(false)" [disabled]="voted">Disagree</button>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class VoterComponent {
|
export class VoterComponent {
|
||||||
@Input() name: string;
|
@Input() name: string;
|
||||||
@Output() voted = new EventEmitter<boolean>();
|
@Output() onVoted = new EventEmitter<boolean>();
|
||||||
didVote = false;
|
voted = false;
|
||||||
|
|
||||||
vote(agreed: boolean) {
|
vote(agreed: boolean) {
|
||||||
this.voted.emit(agreed);
|
this.onVoted.emit(agreed);
|
||||||
this.didVote = true;
|
this.voted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -8,7 +8,7 @@ import { Component } from '@angular/core';
|
|||||||
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
|
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
|
||||||
<app-voter *ngFor="let voter of voters"
|
<app-voter *ngFor="let voter of voters"
|
||||||
[name]="voter"
|
[name]="voter"
|
||||||
(voted)="onVoted($event)">
|
(onVoted)="onVoted($event)">
|
||||||
</app-voter>
|
</app-voter>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,6 @@ describe('Form Validation Tests', function () {
|
|||||||
|
|
||||||
tests('Template-Driven Form');
|
tests('Template-Driven Form');
|
||||||
bobTests();
|
bobTests();
|
||||||
crossValidationTests();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reactive form', () => {
|
describe('Reactive form', () => {
|
||||||
@ -26,7 +25,6 @@ describe('Form Validation Tests', function () {
|
|||||||
|
|
||||||
tests('Reactive Form');
|
tests('Reactive Form');
|
||||||
bobTests();
|
bobTests();
|
||||||
crossValidationTests();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -44,8 +42,7 @@ let page: {
|
|||||||
powerOption: ElementFinder,
|
powerOption: ElementFinder,
|
||||||
errorMessages: ElementArrayFinder,
|
errorMessages: ElementArrayFinder,
|
||||||
heroFormButtons: ElementArrayFinder,
|
heroFormButtons: ElementArrayFinder,
|
||||||
heroSubmitted: ElementFinder,
|
heroSubmitted: ElementFinder
|
||||||
crossValidationErrorMessage: ElementFinder,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPage(sectionTag: string) {
|
function getPage(sectionTag: string) {
|
||||||
@ -62,8 +59,7 @@ function getPage(sectionTag: string) {
|
|||||||
powerOption: section.element(by.css('#power option')),
|
powerOption: section.element(by.css('#power option')),
|
||||||
errorMessages: section.all(by.css('div.alert')),
|
errorMessages: section.all(by.css('div.alert')),
|
||||||
heroFormButtons: buttons,
|
heroFormButtons: buttons,
|
||||||
heroSubmitted: section.element(by.css('.submitted-message')),
|
heroSubmitted: section.element(by.css('.submitted-message'))
|
||||||
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,29 +172,3 @@ function bobTests() {
|
|||||||
expectFormIsValid();
|
expectFormIsValid();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function crossValidationTests() {
|
|
||||||
const emsg = 'Name cannot match alter ego.';
|
|
||||||
|
|
||||||
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () {
|
|
||||||
page.nameInput.clear();
|
|
||||||
page.nameInput.sendKeys('Batman');
|
|
||||||
|
|
||||||
page.alterEgoInput.clear();
|
|
||||||
page.alterEgoInput.sendKeys('Batman');
|
|
||||||
|
|
||||||
expectFormIsInvalid();
|
|
||||||
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be ok again with different values', function () {
|
|
||||||
page.nameInput.clear();
|
|
||||||
page.nameInput.sendKeys('Batman');
|
|
||||||
|
|
||||||
page.alterEgoInput.clear();
|
|
||||||
page.alterEgoInput.sendKeys('Superman');
|
|
||||||
|
|
||||||
expectFormIsValid();
|
|
||||||
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ import { AppComponent } from './app.component';
|
|||||||
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
||||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||||
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -19,8 +19,7 @@ import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.d
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HeroFormTemplateComponent,
|
HeroFormTemplateComponent,
|
||||||
HeroFormReactiveComponent,
|
HeroFormReactiveComponent,
|
||||||
ForbiddenValidatorDirective,
|
ForbiddenValidatorDirective
|
||||||
IdentityRevealedValidatorDirective
|
|
||||||
],
|
],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
/* tslint:disable: member-ordering forin */
|
|
||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
|
||||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-form-reactive',
|
|
||||||
templateUrl: './hero-form-reactive.component.html',
|
|
||||||
styleUrls: ['./hero-form-reactive.component.css'],
|
|
||||||
})
|
|
||||||
export class HeroFormReactiveComponent implements OnInit {
|
|
||||||
|
|
||||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
|
||||||
|
|
||||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
|
||||||
|
|
||||||
heroForm: FormGroup;
|
|
||||||
|
|
||||||
// #docregion form-group
|
|
||||||
ngOnInit(): void {
|
|
||||||
// #docregion custom-validator
|
|
||||||
this.heroForm = new FormGroup({
|
|
||||||
'name': new FormControl(this.hero.name, [
|
|
||||||
Validators.required,
|
|
||||||
Validators.minLength(4),
|
|
||||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
|
||||||
]),
|
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
|
||||||
});
|
|
||||||
// #enddocregion custom-validator
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() { return this.heroForm.get('name'); }
|
|
||||||
|
|
||||||
get power() { return this.heroForm.get('power'); }
|
|
||||||
// #enddocregion form-group
|
|
||||||
}
|
|
||||||
// #enddocregion
|
|
@ -1,5 +0,0 @@
|
|||||||
/* #docregion cross-validation-error-css */
|
|
||||||
.cross-validation-error input {
|
|
||||||
border-left: 5px solid red;
|
|
||||||
}
|
|
||||||
/* #enddocregion cross-validation-error-css */
|
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
<div [hidden]="formDir.submitted">
|
<div [hidden]="formDir.submitted">
|
||||||
|
|
||||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
||||||
<label for="name">Name</label>
|
<label for="name">Name</label>
|
||||||
@ -37,13 +36,6 @@
|
|||||||
formControlName="alterEgo" >
|
formControlName="alterEgo" >
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- #docregion cross-validation-error-message -->
|
|
||||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
|
||||||
Name cannot match alter ego.
|
|
||||||
</div>
|
|
||||||
<!-- #enddocregion cross-validation-error-message -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="power">Hero Power</label>
|
<label for="power">Hero Power</label>
|
||||||
<select id="power" class="form-control"
|
<select id="power" class="form-control"
|
||||||
|
@ -1,36 +1,40 @@
|
|||||||
/* tslint:disable: member-ordering forin */
|
/* tslint:disable: member-ordering forin */
|
||||||
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||||
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form-reactive',
|
selector: 'app-hero-form-reactive',
|
||||||
templateUrl: './hero-form-reactive.component.html',
|
templateUrl: './hero-form-reactive.component.html'
|
||||||
styleUrls: ['./hero-form-reactive.component.css'],
|
|
||||||
})
|
})
|
||||||
export class HeroFormReactiveComponent implements OnInit {
|
export class HeroFormReactiveComponent implements OnInit {
|
||||||
|
|
||||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||||
|
|
||||||
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
|
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||||
|
|
||||||
heroForm: FormGroup;
|
heroForm: FormGroup;
|
||||||
|
|
||||||
|
// #docregion form-group
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// #docregion custom-validator
|
||||||
this.heroForm = new FormGroup({
|
this.heroForm = new FormGroup({
|
||||||
'name': new FormControl(this.hero.name, [
|
'name': new FormControl(this.hero.name, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(4),
|
Validators.minLength(4),
|
||||||
forbiddenNameValidator(/bob/i)
|
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||||
]),
|
]),
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
'power': new FormControl(this.hero.power, Validators.required)
|
||||||
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
});
|
||||||
|
// #enddocregion custom-validator
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() { return this.heroForm.get('name'); }
|
get name() { return this.heroForm.get('name'); }
|
||||||
|
|
||||||
get power() { return this.heroForm.get('power'); }
|
get power() { return this.heroForm.get('power'); }
|
||||||
|
// #enddocregion form-group
|
||||||
}
|
}
|
||||||
|
// #enddocregion
|
||||||
|
@ -5,7 +5,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } fr
|
|||||||
// #docregion custom-validator
|
// #docregion custom-validator
|
||||||
/** A hero's name can't match the given regular expression */
|
/** A hero's name can't match the given regular expression */
|
||||||
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||||
return (control: AbstractControl): {[key: string]: any} | null => {
|
return (control: AbstractControl): {[key: string]: any} => {
|
||||||
const forbidden = nameRe.test(control.value);
|
const forbidden = nameRe.test(control.value);
|
||||||
return forbidden ? {'forbiddenName': {value: control.value}} : null;
|
return forbidden ? {'forbiddenName': {value: control.value}} : null;
|
||||||
};
|
};
|
||||||
@ -22,7 +22,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
|||||||
export class ForbiddenValidatorDirective implements Validator {
|
export class ForbiddenValidatorDirective implements Validator {
|
||||||
@Input('appForbiddenName') forbiddenName: string;
|
@Input('appForbiddenName') forbiddenName: string;
|
||||||
|
|
||||||
validate(control: AbstractControl): {[key: string]: any} | null {
|
validate(control: AbstractControl): {[key: string]: any} {
|
||||||
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
|
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
// #docregion
|
|
||||||
import { Directive } from '@angular/core';
|
|
||||||
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
|
|
||||||
|
|
||||||
// #docregion cross-validation-validator
|
|
||||||
/** A hero's name can't match the hero's alter ego */
|
|
||||||
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
|
|
||||||
const name = control.get('name');
|
|
||||||
const alterEgo = control.get('alterEgo');
|
|
||||||
|
|
||||||
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
|
|
||||||
};
|
|
||||||
// #enddocregion cross-validation-validator
|
|
||||||
|
|
||||||
// #docregion cross-validation-directive
|
|
||||||
@Directive({
|
|
||||||
selector: '[appIdentityRevealed]',
|
|
||||||
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
|
|
||||||
})
|
|
||||||
export class IdentityRevealedValidatorDirective implements Validator {
|
|
||||||
validate(control: AbstractControl): ValidationErrors {
|
|
||||||
return identityRevealedValidator(control)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #enddocregion cross-validation-directive
|
|
@ -1,4 +0,0 @@
|
|||||||
/* #docregion */
|
|
||||||
.cross-validation-error input {
|
|
||||||
border-left: 5px solid red;
|
|
||||||
}
|
|
@ -2,11 +2,11 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<h1>Template-Driven Form</h1>
|
<h1>Template-Driven Form</h1>
|
||||||
<!-- #docregion cross-validation-register-validator -->
|
<!-- #docregion form-tag-->
|
||||||
<form #heroForm="ngForm" appIdentityRevealed>
|
<form #heroForm="ngForm">
|
||||||
<!-- #enddocregion cross-validation-register-validator -->
|
<!-- #enddocregion form-tag-->
|
||||||
<div [hidden]="heroForm.submitted">
|
<div [hidden]="heroForm.submitted">
|
||||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">Name</label>
|
<label for="name">Name</label>
|
||||||
<!-- #docregion name-with-error-msg -->
|
<!-- #docregion name-with-error-msg -->
|
||||||
@ -39,13 +39,6 @@
|
|||||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- #docregion cross-validation-error-message -->
|
|
||||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
|
||||||
Name cannot match alter ego.
|
|
||||||
</div>
|
|
||||||
<!-- #enddocregion cross-validation-error-message -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="power">Hero Power</label>
|
<label for="power">Hero Power</label>
|
||||||
<select id="power" name="power" class="form-control"
|
<select id="power" name="power" class="form-control"
|
||||||
@ -69,4 +62,5 @@
|
|||||||
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
// #docregion component
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form-template',
|
selector: 'app-hero-form-template',
|
||||||
templateUrl: './hero-form-template.component.html',
|
templateUrl: './hero-form-template.component.html'
|
||||||
styleUrls: ['./hero-form-template.component.css'],
|
|
||||||
})
|
})
|
||||||
export class HeroFormTemplateComponent {
|
export class HeroFormTemplateComponent {
|
||||||
|
|
||||||
@ -16,4 +14,3 @@ export class HeroFormTemplateComponent {
|
|||||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||||
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
"description": "Validation",
|
"description": "Validation",
|
||||||
"files":[
|
"files":[
|
||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js",
|
"!**/*.js"
|
||||||
"!**/*.[1].*"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ describe('HttpClient testing', () => {
|
|||||||
|
|
||||||
// Create mock ErrorEvent, raised when something goes wrong at the network level.
|
// Create mock ErrorEvent, raised when something goes wrong at the network level.
|
||||||
// Connection timeout, DNS error, offline, etc
|
// Connection timeout, DNS error, offline, etc
|
||||||
const mockError = new ErrorEvent('Network error', {
|
const errorEvent = new ErrorEvent('so sad', {
|
||||||
message: emsg,
|
message: emsg,
|
||||||
// #enddocregion network-error
|
// #enddocregion network-error
|
||||||
// The rest of this is optional and not used.
|
// The rest of this is optional and not used.
|
||||||
@ -162,7 +162,7 @@ describe('HttpClient testing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Respond with mock error
|
// Respond with mock error
|
||||||
req.error(mockError);
|
req.error(errorEvent);
|
||||||
});
|
});
|
||||||
// #enddocregion network-error
|
// #enddocregion network-error
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ export class MyCounterComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************/
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'counter-parent',
|
selector: 'counter-parent',
|
||||||
template: `
|
template: `
|
||||||
|
@ -72,6 +72,8 @@ export class DoCheckComponent implements DoCheck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************/
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'do-check-parent',
|
selector: 'do-check-parent',
|
||||||
templateUrl: './do-check-parent.component.html',
|
templateUrl: './do-check-parent.component.html',
|
||||||
|
@ -46,6 +46,8 @@ export class OnChangesComponent implements OnChanges {
|
|||||||
reset() { this.changeLog = []; }
|
reset() { this.changeLog = []; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************/
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'on-changes-parent',
|
selector: 'on-changes-parent',
|
||||||
templateUrl: './on-changes-parent.component.html',
|
templateUrl: './on-changes-parent.component.html',
|
||||||
|
@ -4,8 +4,7 @@ button {
|
|||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
code,
|
code, .code {
|
||||||
.code {
|
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
color: black;
|
color: black;
|
||||||
font-family: Courier, sans-serif;
|
font-family: Courier, sans-serif;
|
||||||
@ -22,18 +21,14 @@ div.code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: 40px 0;
|
margin: 40px 0
|
||||||
}
|
}
|
||||||
|
|
||||||
td,
|
td, th {
|
||||||
th {
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #docregion p-span */
|
/* #docregion p-span */
|
||||||
p span {
|
p span { color: red; font-size: 70%; }
|
||||||
color: red;
|
|
||||||
font-size: 70%;
|
|
||||||
}
|
|
||||||
/* #enddocregion p-span */
|
/* #enddocregion p-span */
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
<!-- #docregion select-span -->
|
<!-- #docregion select-span -->
|
||||||
<select [(ngModel)]="hero">
|
<select [(ngModel)]="hero">
|
||||||
<span *ngFor="let h of heroes">
|
<span *ngFor="let h of heroes">
|
||||||
<span *ngIf="showSad || h?.emotion !== 'sad'">
|
<span *ngIf="showSad || h?.emotion != 'sad'">
|
||||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -147,7 +147,7 @@
|
|||||||
<!-- #docregion select-ngcontainer -->
|
<!-- #docregion select-ngcontainer -->
|
||||||
<select [(ngModel)]="hero">
|
<select [(ngModel)]="hero">
|
||||||
<ng-container *ngFor="let h of heroes">
|
<ng-container *ngFor="let h of heroes">
|
||||||
<ng-container *ngIf="showSad || h?.emotion !== 'sad'">
|
<ng-container *ngIf="showSad || h?.emotion != 'sad'">
|
||||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -6,15 +6,14 @@ import { heroes } from './hero';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.css']
|
styleUrls: [ './app.component.css' ]
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
heroes = heroes;
|
heroes = heroes;
|
||||||
hero = this.heroes[0];
|
hero = this.heroes[0];
|
||||||
heroTraits = ['honest', 'brave', 'considerate'];
|
heroTraits = [ 'honest', 'brave', 'considerate' ];
|
||||||
|
|
||||||
// flags for the table
|
// flags for the table
|
||||||
|
|
||||||
attrDirs = true;
|
attrDirs = true;
|
||||||
strucDirs = true;
|
strucDirs = true;
|
||||||
divNgIf = false;
|
divNgIf = false;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -34,15 +33,11 @@ export class ConfusedHeroComponent {
|
|||||||
export class UnknownHeroComponent {
|
export class UnknownHeroComponent {
|
||||||
@Input() hero: Hero;
|
@Input() hero: Hero;
|
||||||
get message() {
|
get message() {
|
||||||
return this.hero && this.hero.name
|
return this.hero && this.hero.name ?
|
||||||
? `${this.hero.name} is strange and mysterious.`
|
`${this.hero.name} is strange and mysterious.` :
|
||||||
: 'Are you feeling indecisive?';
|
'Are you feeling indecisive?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const heroComponents = [
|
export const heroComponents =
|
||||||
HappyHeroComponent,
|
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
|
||||||
SadHeroComponent,
|
|
||||||
ConfusedHeroComponent,
|
|
||||||
UnknownHeroComponent
|
|
||||||
];
|
|
||||||
|
@ -6,8 +6,8 @@ export class Hero {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const heroes: Hero[] = [
|
export const heroes: Hero[] = [
|
||||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy' },
|
{ id: 1, name: 'Mr. Nice', emotion: 'happy'},
|
||||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||||
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
||||||
{ id: 4, name: 'Magneta' }
|
{ id: 4, name: 'Magneta'}
|
||||||
];
|
];
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular//common/http';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import {
|
import {
|
||||||
|
@ -15,7 +15,6 @@ export class ComposeMessageComponent {
|
|||||||
@HostBinding('style.position') position = 'absolute';
|
@HostBinding('style.position') position = 'absolute';
|
||||||
|
|
||||||
details: string;
|
details: string;
|
||||||
message: string;
|
|
||||||
sending = false;
|
sending = false;
|
||||||
|
|
||||||
constructor(private router: Router) {}
|
constructor(private router: Router) {}
|
||||||
|
@ -7,9 +7,12 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"files": [
|
"files": [
|
||||||
"/favicon.ico",
|
"/favicon.ico",
|
||||||
"/index.html",
|
"/index.html"
|
||||||
"/*.css",
|
],
|
||||||
"/*.js"
|
"versionedFiles": [
|
||||||
|
"/*.bundle.css",
|
||||||
|
"/*.bundle.js",
|
||||||
|
"/*.chunk.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
// Import spec files individually for Stackblitz
|
// Import spec files individually for Stackblitz
|
||||||
import './app/about/about.component.spec.ts';
|
import 'app/about/about.component.spec.ts';
|
||||||
import './app/app-initial.component.spec.ts';
|
import 'app/app-initial.component.spec.ts';
|
||||||
import './app/app.component.router.spec.ts';
|
import 'app/app.component.router.spec.ts';
|
||||||
import './app/app.component.spec.ts';
|
import 'app/app.component.spec.ts';
|
||||||
import './app/banner/banner-initial.component.spec.ts';
|
import 'app/banner/banner-initial.component.spec.ts';
|
||||||
import './app/banner/banner.component.spec.ts';
|
import 'app/banner/banner.component.spec.ts';
|
||||||
import './app/banner/banner.component.detect-changes.spec.ts';
|
import 'app/banner/banner.component.detect-changes.spec.ts';
|
||||||
import './app/banner/banner-external.component.spec.ts';
|
import 'app/banner/banner-external.component.spec.ts';
|
||||||
import './app/dashboard/dashboard-hero.component.spec.ts';
|
import 'app/dashboard/dashboard-hero.component.spec.ts';
|
||||||
import './app/dashboard/dashboard.component.no-testbed.spec.ts';
|
import 'app/dashboard/dashboard.component.no-testbed.spec.ts';
|
||||||
import './app/dashboard/dashboard.component.spec.ts';
|
import 'app/dashboard/dashboard.component.spec.ts';
|
||||||
import './app/demo/async-helper.spec.ts';
|
import 'app/demo/async-helper.spec.ts';
|
||||||
import './app/demo/demo.spec.ts';
|
import 'app/demo/demo.spec.ts';
|
||||||
import './app/demo/demo.testbed.spec.ts';
|
import 'app/demo/demo.testbed.spec.ts';
|
||||||
import './app/hero/hero-detail.component.no-testbed.spec.ts';
|
import 'app/hero/hero-detail.component.no-testbed.spec.ts';
|
||||||
import './app/hero/hero-detail.component.spec.ts';
|
import 'app/hero/hero-detail.component.spec.ts';
|
||||||
import './app/hero/hero-list.component.spec.ts';
|
import 'app/hero/hero-list.component.spec.ts';
|
||||||
import './app/model/hero.service.spec.ts';
|
import 'app/model/hero.service.spec.ts';
|
||||||
import './app/model/http-hero.service.spec.ts';
|
import 'app/model/http-hero.service.spec.ts';
|
||||||
import './app/model/testing/http-client.spec.ts';
|
import 'app/model/testing/http-client.spec.ts';
|
||||||
import './app/shared/highlight.directive.spec.ts';
|
import 'app/shared/highlight.directive.spec.ts';
|
||||||
import './app/shared/title-case.pipe.spec.ts';
|
import 'app/shared/title-case.pipe.spec.ts';
|
||||||
import './app/twain/twain.component.spec.ts';
|
import 'app/twain/twain.component.spec.ts';
|
||||||
import './app/twain/twain.component.marbles.spec.ts';
|
import 'app/twain/twain.component.marbles.spec.ts';
|
||||||
import './app/welcome/welcome.component.spec.ts';
|
import 'app/welcome/welcome.component.spec.ts';
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<!-- #enddocregion show-hero-1 -->
|
<!-- #enddocregion show-hero-1 -->
|
||||||
|
|
||||||
<!-- #docregion show-hero-2 -->
|
<!-- #docregion show-hero-2 -->
|
||||||
<h2>{{hero.name}} Details</h2>
|
<h2>{{ hero.name }} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div><span>name: </span>{{hero.name}}</div>
|
<div><span>name: </span>{{hero.name}}</div>
|
||||||
<!-- #enddocregion show-hero-2 -->
|
<!-- #enddocregion show-hero-2 -->
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<!-- #docregion -->
|
<!-- #docregion -->
|
||||||
<!-- #docregion pipe -->
|
<!-- #docregion pipe -->
|
||||||
<h2>{{hero.name | uppercase}} Details</h2>
|
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||||
<!-- #enddocregion pipe -->
|
<!-- #enddocregion pipe -->
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<div *ngIf="selectedHero">
|
<div *ngIf="selectedHero">
|
||||||
|
|
||||||
<!-- #docregion selectedHero-details -->
|
<!-- #docregion selectedHero-details -->
|
||||||
<h2>{{selectedHero.name | uppercase}} Details</h2>
|
<h2>{{ selectedHero.name | uppercase }} Details</h2>
|
||||||
<div><span>id: </span>{{selectedHero.id}}</div>
|
<div><span>id: </span>{{selectedHero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div *ngIf="hero">
|
<div *ngIf="hero">
|
||||||
|
|
||||||
<h2>{{hero.name | uppercase}} Details</h2>
|
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div *ngIf="hero">
|
<div *ngIf="hero">
|
||||||
<h2>{{hero.name | uppercase}} Details</h2>
|
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
@ -18,11 +18,11 @@ nav a {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
nav a:visited, a:link {
|
nav a:visited, a:link {
|
||||||
color: #607d8b;
|
color: #607D8B;
|
||||||
}
|
}
|
||||||
nav a:hover {
|
nav a:hover {
|
||||||
color: #039be5;
|
color: #039be5;
|
||||||
background-color: #cfd8dc;
|
background-color: #CFD8DC;
|
||||||
}
|
}
|
||||||
nav a.active {
|
nav a.active {
|
||||||
color: #039be5;
|
color: #039be5;
|
||||||
|
@ -33,11 +33,11 @@ h4 {
|
|||||||
color: #eee;
|
color: #eee;
|
||||||
max-height: 120px;
|
max-height: 120px;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
background-color: #607d8b;
|
background-color: #607D8B;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.module:hover {
|
.module:hover {
|
||||||
background-color: #eee;
|
background-color: #EEE;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #607d8b;
|
color: #607d8b;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div *ngIf="hero">
|
<div *ngIf="hero">
|
||||||
<h2>{{hero.name | uppercase}} Details</h2>
|
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
@ -1,36 +1,16 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DashboardComponent } from './dashboard.component';
|
import { DashboardComponent } from './dashboard.component';
|
||||||
import { HeroSearchComponent } from '../hero-search/hero-search.component';
|
|
||||||
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { HEROES } from '../mock-heroes';
|
|
||||||
import { HeroService } from '../hero.service';
|
|
||||||
|
|
||||||
describe('DashboardComponent', () => {
|
describe('DashboardComponent', () => {
|
||||||
let component: DashboardComponent;
|
let component: DashboardComponent;
|
||||||
let fixture: ComponentFixture<DashboardComponent>;
|
let fixture: ComponentFixture<DashboardComponent>;
|
||||||
let heroService;
|
|
||||||
let getHeroesSpy;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
|
|
||||||
getHeroesSpy = heroService.getHeroes.and.returnValue( of(HEROES) );
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [ DashboardComponent ]
|
||||||
DashboardComponent,
|
|
||||||
HeroSearchComponent
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
RouterTestingModule.withRoutes([])
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{ provide: HeroService, useValue: heroService }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -42,17 +22,4 @@ describe('DashboardComponent', () => {
|
|||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display "Top Heroes" as headline', () => {
|
|
||||||
expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Top Heroes');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call heroService', async(() => {
|
|
||||||
expect(getHeroesSpy.calls.any()).toBe(true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should display 4 links', async(() => {
|
|
||||||
expect(fixture.nativeElement.querySelectorAll('a').length).toEqual(4);
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div *ngIf="hero">
|
<div *ngIf="hero">
|
||||||
<h2>{{hero.name | uppercase}} Details</h2>
|
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
|
|
||||||
import { HeroSearchComponent } from './hero-search.component';
|
import { HeroSearchComponent } from './hero-search.component';
|
||||||
|
|
||||||
|
|
||||||
describe('HeroSearchComponent', () => {
|
describe('HeroSearchComponent', () => {
|
||||||
let component: HeroSearchComponent;
|
let component: HeroSearchComponent;
|
||||||
let fixture: ComponentFixture<HeroSearchComponent>;
|
let fixture: ComponentFixture<HeroSearchComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ HeroSearchComponent ],
|
declarations: [ HeroSearchComponent ]
|
||||||
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule]
|
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@ -40,7 +40,7 @@ export class HeroService {
|
|||||||
// #enddocregion getHeroes-1
|
// #enddocregion getHeroes-1
|
||||||
.pipe(
|
.pipe(
|
||||||
// #enddocregion getHeroes-2
|
// #enddocregion getHeroes-2
|
||||||
tap(heroes => this.log('fetched heroes')),
|
tap(heroes => this.log(`fetched heroes`)),
|
||||||
// #docregion getHeroes-2
|
// #docregion getHeroes-2
|
||||||
catchError(this.handleError('getHeroes', []))
|
catchError(this.handleError('getHeroes', []))
|
||||||
);
|
);
|
||||||
@ -84,7 +84,7 @@ export class HeroService {
|
|||||||
// if not search term, return empty hero array.
|
// if not search term, return empty hero array.
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
|
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
|
||||||
tap(_ => this.log(`found heroes matching "${term}"`)),
|
tap(_ => this.log(`found heroes matching "${term}"`)),
|
||||||
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
||||||
);
|
);
|
||||||
@ -151,7 +151,7 @@ export class HeroService {
|
|||||||
// #docregion log
|
// #docregion log
|
||||||
/** Log a HeroService message with the MessageService */
|
/** Log a HeroService message with the MessageService */
|
||||||
private log(message: string) {
|
private log(message: string) {
|
||||||
this.messageService.add(`HeroService: ${message}`);
|
this.messageService.add('HeroService: ' + message);
|
||||||
}
|
}
|
||||||
// #enddocregion log
|
// #enddocregion log
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { HeroesComponent } from './heroes.component';
|
import { HeroesComponent } from './heroes.component';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
|
|
||||||
describe('HeroesComponent', () => {
|
describe('HeroesComponent', () => {
|
||||||
let component: HeroesComponent;
|
let component: HeroesComponent;
|
||||||
@ -9,8 +8,7 @@ describe('HeroesComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ HeroesComponent ],
|
declarations: [ HeroesComponent ]
|
||||||
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule],
|
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@ -17,7 +17,7 @@ const PORT = process.env.PORT || 4000;
|
|||||||
const DIST_FOLDER = join(process.cwd(), 'dist');
|
const DIST_FOLDER = join(process.cwd(), 'dist');
|
||||||
|
|
||||||
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
|
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
|
||||||
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
|
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');
|
||||||
|
|
||||||
// Express Engine
|
// Express Engine
|
||||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||||
@ -51,7 +51,7 @@ app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
|
|||||||
// #docregion navigation-request
|
// #docregion navigation-request
|
||||||
// All regular routes use the Universal engine
|
// All regular routes use the Universal engine
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
res.render('index', { req });
|
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
|
||||||
});
|
});
|
||||||
// #enddocregion navigation-request
|
// #enddocregion navigation-request
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export class HeroService {
|
|||||||
getHeroes (): Observable<Hero[]> {
|
getHeroes (): Observable<Hero[]> {
|
||||||
return this.http.get<Hero[]>(this.heroesUrl)
|
return this.http.get<Hero[]>(this.heroesUrl)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(heroes => this.log('fetched heroes')),
|
tap(heroes => this.log(`fetched heroes`)),
|
||||||
catchError(this.handleError('getHeroes', []))
|
catchError(this.handleError('getHeroes', []))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ export class HeroService {
|
|||||||
// if not search term, return empty hero array.
|
// if not search term, return empty hero array.
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
|
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
|
||||||
tap(_ => this.log(`found heroes matching "${term}"`)),
|
tap(_ => this.log(`found heroes matching "${term}"`)),
|
||||||
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
||||||
);
|
);
|
||||||
@ -123,6 +123,6 @@ export class HeroService {
|
|||||||
|
|
||||||
/** Log a HeroService message with the MessageService */
|
/** Log a HeroService message with the MessageService */
|
||||||
private log(message: string) {
|
private log(message: string) {
|
||||||
this.messageService.add(`HeroService: ${message}`);
|
this.messageService.add('HeroService: ' + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,8 @@ module.exports = {
|
|||||||
entry: { server: './server.ts' },
|
entry: { server: './server.ts' },
|
||||||
resolve: { extensions: ['.js', '.ts'] },
|
resolve: { extensions: ['.js', '.ts'] },
|
||||||
target: 'node',
|
target: 'node',
|
||||||
mode: 'none',
|
|
||||||
// this makes sure we include node_modules and other 3rd party libraries
|
// this makes sure we include node_modules and other 3rd party libraries
|
||||||
externals: [/node_modules/],
|
externals: [/(node_modules|main\..*\.js)/],
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
filename: '[name].js'
|
filename: '[name].js'
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"removeComments": false,
|
"removeComments": false,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"skipLibCheck": true,
|
|
||||||
"suppressImplicitAnyIndexErrors": true
|
"suppressImplicitAnyIndexErrors": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": [ "es2015", "dom" ],
|
"lib": [ "es2015", "dom" ],
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"skipLibCheck": true,
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
"suppressImplicitAnyIndexErrors": true
|
"typeRoots": [
|
||||||
|
"./node_modules/@types/"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { platformBrowser } from '@angular/platform-browser';
|
import { platformBrowser } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppModuleNgFactory } from './app.module.ngfactory';
|
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
||||||
|
|
||||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
// #enddocregion activatedroute
|
// #enddocregion activatedroute
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
|
|||||||
|
|
||||||
class MockPhone {
|
class MockPhone {
|
||||||
get(id: string): Observable<PhoneData> {
|
get(id: string): Observable<PhoneData> {
|
||||||
return of(xyzPhoneData());
|
return Observable.of(xyzPhoneData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { SpyLocation } from '@angular/common/testing';
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ class ActivatedRouteMock {
|
|||||||
|
|
||||||
class MockPhone {
|
class MockPhone {
|
||||||
query(): Observable<PhoneData[]> {
|
query(): Observable<PhoneData[]> {
|
||||||
return of([
|
return Observable.of([
|
||||||
{name: 'Nexus S', snippet: '', images: []},
|
{name: 'Nexus S', snippet: '', images: []},
|
||||||
{name: 'Motorola DROID', snippet: '', images: []}
|
{name: 'Motorola DROID', snippet: '', images: []}
|
||||||
]);
|
]);
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
"lib": ["es2015", "dom"],
|
"lib": ["es2015", "dom"],
|
||||||
"removeComments": false,
|
"removeComments": false,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"skipLibCheck": true,
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
"suppressImplicitAnyIndexErrors": true
|
"typeRoots": [
|
||||||
|
"./node_modules/@types/"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"files": [
|
"files": [
|
||||||
@ -19,6 +21,7 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
|
"genDir": "aot",
|
||||||
"skipMetadataEmit" : true
|
"skipMetadataEmit" : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": [ "es2015", "dom" ],
|
"lib": [ "es2015", "dom" ],
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"skipLibCheck": true,
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
"suppressImplicitAnyIndexErrors": true
|
"typeRoots": [
|
||||||
|
"./node_modules/@types/"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
// #enddocregion activatedroute
|
// #enddocregion activatedroute
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
|
|||||||
|
|
||||||
class MockPhone {
|
class MockPhone {
|
||||||
get(id: string): Observable<PhoneData> {
|
get(id: string): Observable<PhoneData> {
|
||||||
return of(xyzPhoneData());
|
return Observable.of(xyzPhoneData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// #docregion routestuff
|
// #docregion routestuff
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { SpyLocation } from '@angular/common/testing';
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class ActivatedRouteMock {
|
|||||||
|
|
||||||
class MockPhone {
|
class MockPhone {
|
||||||
query(): Observable<PhoneData[]> {
|
query(): Observable<PhoneData[]> {
|
||||||
return of([
|
return Observable.of([
|
||||||
{name: 'Nexus S', snippet: '', images: []},
|
{name: 'Nexus S', snippet: '', images: []},
|
||||||
{name: 'Motorola DROID', snippet: '', images: []}
|
{name: 'Motorola DROID', snippet: '', images: []}
|
||||||
]);
|
]);
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": [ "es2015", "dom" ],
|
"lib": [ "es2015", "dom" ],
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"skipLibCheck": true,
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
"suppressImplicitAnyIndexErrors": true
|
"typeRoots": [
|
||||||
|
"./node_modules/@types/"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
12
aio/content/examples/webpack/config/helpers.js
Normal file
12
aio/content/examples/webpack/config/helpers.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var _root = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
function root(args) {
|
||||||
|
args = Array.prototype.slice.call(arguments, 0);
|
||||||
|
return path.join.apply(path, [_root].concat(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.root = root;
|
||||||
|
// #enddocregion
|
17
aio/content/examples/webpack/config/karma-test-shim.js
Normal file
17
aio/content/examples/webpack/config/karma-test-shim.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// #docregion
|
||||||
|
Error.stackTraceLimit = Infinity;
|
||||||
|
|
||||||
|
require('core-js/es6');
|
||||||
|
require('core-js/es7/reflect');
|
||||||
|
|
||||||
|
require('zone.js/dist/zone');
|
||||||
|
require('zone.js/dist/zone-testing');
|
||||||
|
|
||||||
|
var appContext = require.context('../src', true, /\.spec\.ts/);
|
||||||
|
|
||||||
|
appContext.keys().forEach(appContext);
|
||||||
|
|
||||||
|
var testing = require('@angular/core/testing');
|
||||||
|
var browser = require('@angular/platform-browser-dynamic/testing');
|
||||||
|
|
||||||
|
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
|
39
aio/content/examples/webpack/config/karma.conf.js
Normal file
39
aio/content/examples/webpack/config/karma.conf.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpackConfig = require('./webpack.test');
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
var _config = {
|
||||||
|
basePath: '',
|
||||||
|
|
||||||
|
frameworks: ['jasmine'],
|
||||||
|
|
||||||
|
files: [
|
||||||
|
{pattern: './config/karma-test-shim.js', watched: false}
|
||||||
|
],
|
||||||
|
|
||||||
|
preprocessors: {
|
||||||
|
'./config/karma-test-shim.js': ['webpack', 'sourcemap']
|
||||||
|
},
|
||||||
|
|
||||||
|
webpack: webpackConfig,
|
||||||
|
|
||||||
|
webpackMiddleware: {
|
||||||
|
stats: 'errors-only'
|
||||||
|
},
|
||||||
|
|
||||||
|
webpackServer: {
|
||||||
|
noInfo: true
|
||||||
|
},
|
||||||
|
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: false,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: true
|
||||||
|
};
|
||||||
|
|
||||||
|
config.set(_config);
|
||||||
|
};
|
||||||
|
// #enddocregion
|
81
aio/content/examples/webpack/config/webpack.common.js
Normal file
81
aio/content/examples/webpack/config/webpack.common.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// #docregion entries, one-entry, two-entries
|
||||||
|
entry: {
|
||||||
|
// #enddocregion one-entry, two-entries
|
||||||
|
'polyfills': './src/polyfills.ts',
|
||||||
|
// #docregion two-entries
|
||||||
|
'vendor': './src/vendor.ts',
|
||||||
|
// #docregion one-entry
|
||||||
|
'app': './src/main.ts'
|
||||||
|
},
|
||||||
|
// #enddocregion entries, one-entry, two-entries
|
||||||
|
|
||||||
|
// #docregion resolve
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js']
|
||||||
|
},
|
||||||
|
// #enddocregion resolve
|
||||||
|
|
||||||
|
// #docregion loaders
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
loader: 'awesome-typescript-loader',
|
||||||
|
options: { configFileName: helpers.root('src', 'tsconfig.json') }
|
||||||
|
} , 'angular2-template-loader'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: 'html-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||||
|
loader: 'file-loader?name=assets/[name].[hash].[ext]'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: helpers.root('src', 'app'),
|
||||||
|
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: helpers.root('src', 'app'),
|
||||||
|
loader: 'raw-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// #enddocregion loaders
|
||||||
|
|
||||||
|
// #docregion plugins
|
||||||
|
plugins: [
|
||||||
|
// Workaround for angular/angular#11580
|
||||||
|
new webpack.ContextReplacementPlugin(
|
||||||
|
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||||
|
/angular(\\|\/)core(\\|\/)@angular/,
|
||||||
|
helpers.root('./src'), // location of your src
|
||||||
|
{} // a map of your routes
|
||||||
|
),
|
||||||
|
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: ['app', 'vendor', 'polyfills']
|
||||||
|
}),
|
||||||
|
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'src/index.html'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
// #enddocregion plugins
|
||||||
|
};
|
||||||
|
// #enddocregion
|
||||||
|
|
26
aio/content/examples/webpack/config/webpack.dev.js
Normal file
26
aio/content/examples/webpack/config/webpack.dev.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpackMerge = require('webpack-merge');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var commonConfig = require('./webpack.common.js');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = webpackMerge(commonConfig, {
|
||||||
|
devtool: 'cheap-module-eval-source-map',
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: helpers.root('dist'),
|
||||||
|
publicPath: '/',
|
||||||
|
filename: '[name].js',
|
||||||
|
chunkFilename: '[id].chunk.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new ExtractTextPlugin('[name].css')
|
||||||
|
],
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
historyApiFallback: true,
|
||||||
|
stats: 'minimal'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #enddocregion
|
41
aio/content/examples/webpack/config/webpack.prod.js
Normal file
41
aio/content/examples/webpack/config/webpack.prod.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var webpackMerge = require('webpack-merge');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var commonConfig = require('./webpack.common.js');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
|
||||||
|
|
||||||
|
module.exports = webpackMerge(commonConfig, {
|
||||||
|
devtool: 'source-map',
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: helpers.root('dist'),
|
||||||
|
publicPath: '/',
|
||||||
|
filename: '[name].[hash].js',
|
||||||
|
chunkFilename: '[id].[hash].chunk.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.NoEmitOnErrorsPlugin(),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
|
||||||
|
mangle: {
|
||||||
|
keep_fnames: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new ExtractTextPlugin('[name].[hash].css'),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
'ENV': JSON.stringify(ENV)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
htmlLoader: {
|
||||||
|
minimize: false // workaround for ng2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
55
aio/content/examples/webpack/config/webpack.test.js
Normal file
55
aio/content/examples/webpack/config/webpack.test.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js']
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
loader: 'awesome-typescript-loader',
|
||||||
|
options: { configFileName: helpers.root('src', 'tsconfig.json') }
|
||||||
|
} , 'angular2-template-loader'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: 'html-loader'
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||||
|
loader: 'null-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: helpers.root('src', 'app'),
|
||||||
|
loader: 'null-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: helpers.root('src', 'app'),
|
||||||
|
loader: 'raw-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.ContextReplacementPlugin(
|
||||||
|
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||||
|
/angular(\\|\/)core(\\|\/)@angular/,
|
||||||
|
helpers.root('./src'), // location of your src
|
||||||
|
{} // a map of your routes
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// #enddocregion
|
21
aio/content/examples/webpack/e2e-spec.ts
Normal file
21
aio/content/examples/webpack/e2e-spec.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use strict'; // necessary for es6 output in node
|
||||||
|
|
||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
describe('QuickStart E2E Tests', function () {
|
||||||
|
|
||||||
|
let expectedMsg = 'Hello from Angular App with Webpack';
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
browser.get('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should display: ${expectedMsg}`, function () {
|
||||||
|
expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an image', function () {
|
||||||
|
expect(element(by.css('img')).isPresent()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
5
aio/content/examples/webpack/example-config.json
Normal file
5
aio/content/examples/webpack/example-config.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"build": "build:webpack",
|
||||||
|
"run": "serve:cli",
|
||||||
|
"projectType": "systemjs"
|
||||||
|
}
|
2
aio/content/examples/webpack/karma.webpack.conf.js
Normal file
2
aio/content/examples/webpack/karma.webpack.conf.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// #docregion
|
||||||
|
module.exports = require('./config/karma.conf.js');
|
49
aio/content/examples/webpack/package.webpack.json
Normal file
49
aio/content/examples/webpack/package.webpack.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "angular2-webpack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A webpack starter for Angular",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server --inline --progress --port 8080",
|
||||||
|
"test": "karma start",
|
||||||
|
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/common": "~4.2.0",
|
||||||
|
"@angular/compiler": "~4.2.0",
|
||||||
|
"@angular/core": "~4.2.0",
|
||||||
|
"@angular/forms": "~4.2.0",
|
||||||
|
"@angular/http": "~4.2.0",
|
||||||
|
"@angular/platform-browser": "~4.2.0",
|
||||||
|
"@angular/platform-browser-dynamic": "~4.2.0",
|
||||||
|
"@angular/router": "~4.2.0",
|
||||||
|
"core-js": "^2.4.1",
|
||||||
|
"rxjs": "5.0.1",
|
||||||
|
"zone.js": "^0.8.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^6.0.45",
|
||||||
|
"@types/jasmine": "2.5.36",
|
||||||
|
"angular2-template-loader": "^0.6.0",
|
||||||
|
"awesome-typescript-loader": "^3.0.4",
|
||||||
|
"css-loader": "^0.26.1",
|
||||||
|
"extract-text-webpack-plugin": "2.0.0-beta.5",
|
||||||
|
"file-loader": "^0.9.0",
|
||||||
|
"html-loader": "^0.4.3",
|
||||||
|
"html-webpack-plugin": "^2.16.1",
|
||||||
|
"jasmine-core": "^2.4.1",
|
||||||
|
"karma": "^1.2.0",
|
||||||
|
"karma-chrome-launcher": "^2.0.0",
|
||||||
|
"karma-jasmine": "^1.0.2",
|
||||||
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
|
"karma-webpack": "^2.0.1",
|
||||||
|
"null-loader": "^0.1.1",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
|
"rimraf": "^2.5.2",
|
||||||
|
"style-loader": "^0.13.1",
|
||||||
|
"typescript": "~2.3.1",
|
||||||
|
"webpack": "2.2.1",
|
||||||
|
"webpack-dev-server": "2.4.1",
|
||||||
|
"webpack-merge": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
9
aio/content/examples/webpack/src/app/app.component.css
Normal file
9
aio/content/examples/webpack/src/app/app.component.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* #docregion */
|
||||||
|
main {
|
||||||
|
padding: 1em;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* #enddocregion */
|
7
aio/content/examples/webpack/src/app/app.component.html
Normal file
7
aio/content/examples/webpack/src/app/app.component.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!-- #docregion -->
|
||||||
|
<main>
|
||||||
|
<h1>Hello from Angular App with Webpack</h1>
|
||||||
|
|
||||||
|
<img src="../assets/images/angular.png">
|
||||||
|
</main>
|
||||||
|
<!-- #enddocregion -->
|
16
aio/content/examples/webpack/src/app/app.component.spec.ts
Normal file
16
aio/content/examples/webpack/src/app/app.component.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// #docregion
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({ declarations: [AppComponent]});
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should work', () => {
|
||||||
|
let fixture = TestBed.createComponent(AppComponent);
|
||||||
|
expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// #enddocregion
|
16
aio/content/examples/webpack/src/app/app.component.ts
Normal file
16
aio/content/examples/webpack/src/app/app.component.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
// #docregion component
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
// #enddocregion component
|
||||||
|
import '../assets/css/styles.css';
|
||||||
|
|
||||||
|
// #docregion component
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent { }
|
||||||
|
// #enddocregion
|
16
aio/content/examples/webpack/src/app/app.module.ts
Normal file
16
aio/content/examples/webpack/src/app/app.module.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// #docregion
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
bootstrap: [ AppComponent ]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
6
aio/content/examples/webpack/src/assets/css/styles.css
Normal file
6
aio/content/examples/webpack/src/assets/css/styles.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/* #docregion */
|
||||||
|
body {
|
||||||
|
background: #0147A7;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
/* #enddocregion */
|
BIN
aio/content/examples/webpack/src/assets/images/angular.png
Normal file
BIN
aio/content/examples/webpack/src/assets/images/angular.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
14
aio/content/examples/webpack/src/index.html
Normal file
14
aio/content/examples/webpack/src/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!-- #docregion -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<title>Angular With Webpack</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<my-app>Loading...</my-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<!-- #enddocregion -->
|
14
aio/content/examples/webpack/src/main.ts
Normal file
14
aio/content/examples/webpack/src/main.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// #docregion
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
|
||||||
|
// #docregion enable-prod
|
||||||
|
if (process.env.ENV === 'production') {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
// #enddocregion enable-prod
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
// #enddocregion
|
12
aio/content/examples/webpack/src/polyfills.ts
Normal file
12
aio/content/examples/webpack/src/polyfills.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
import 'core-js/es6';
|
||||||
|
import 'core-js/es7/reflect';
|
||||||
|
require('zone.js/dist/zone');
|
||||||
|
|
||||||
|
if (process.env.ENV === 'production') {
|
||||||
|
// Production
|
||||||
|
} else {
|
||||||
|
// Development and test
|
||||||
|
Error['stackTraceLimit'] = Infinity;
|
||||||
|
require('zone.js/dist/long-stack-trace-zone');
|
||||||
|
}
|
13
aio/content/examples/webpack/src/tsconfig.1.json
Normal file
13
aio/content/examples/webpack/src/tsconfig.1.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"lib": ["es2015", "dom"],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"suppressImplicitAnyIndexErrors": true
|
||||||
|
}
|
||||||
|
}
|
18
aio/content/examples/webpack/src/vendor.ts
Normal file
18
aio/content/examples/webpack/src/vendor.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// TODO(i): this no longer works. we need to review this example and if absolutely necessary rewrite it to use the
|
||||||
|
// rxjs-compat package
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
// Angular
|
||||||
|
import '@angular/platform-browser';
|
||||||
|
import '@angular/platform-browser-dynamic';
|
||||||
|
import '@angular/core';
|
||||||
|
import '@angular/common';
|
||||||
|
import '@angular/http';
|
||||||
|
import '@angular/router';
|
||||||
|
|
||||||
|
// RxJS
|
||||||
|
import 'rxjs';
|
||||||
|
|
||||||
|
// Other vendors for example jQuery, Lodash or Bootstrap
|
||||||
|
// You can import js, ts, css, sass, ...
|
||||||
|
// #enddocregion
|
3
aio/content/examples/webpack/webpack.config.js
Normal file
3
aio/content/examples/webpack/webpack.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// #docregion
|
||||||
|
module.exports = require('./config/webpack.dev.js');
|
||||||
|
// #enddocregion
|
12
aio/content/examples/webpack/zipper.json
Normal file
12
aio/content/examples/webpack/zipper.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"files":[
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[0-9].*",
|
||||||
|
"config/**/*",
|
||||||
|
"webpack.config.js",
|
||||||
|
"karma.webpack.conf.js"
|
||||||
|
],
|
||||||
|
"removeSystemJsConfig": true,
|
||||||
|
"type": "webpack"
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user