Compare commits
391 Commits
7.0.0-rc.0
...
6.1.x
Author | SHA1 | Date | |
---|---|---|---|
79fb9d449c | |||
73a93d3ab6 | |||
8eda5a152b | |||
7b82ce0c67 | |||
2eb5fe699f | |||
f99febcdf9 | |||
36cbfb1771 | |||
1f5315f6f7 | |||
eeebe621fe | |||
05f279df49 | |||
485d67bfed | |||
a1592f5a20 | |||
a251374ecd | |||
2b00c17091 | |||
81724f5790 | |||
1f06b6c99b | |||
6790709b93 | |||
9f7f67121c | |||
db49beae15 | |||
97609daea9 | |||
abcb03cb82 | |||
4f09f7db73 | |||
f1e14a3224 | |||
50de03a83a | |||
65555fe35d | |||
6da3867d63 | |||
c9488b5432 | |||
b22c376123 | |||
8a6f3723ca | |||
ccb0ec9c35 | |||
0c9a087809 | |||
2515ff660b | |||
70c79cb969 | |||
ed04e99c95 | |||
623adbbdf7 | |||
3660ff80b7 | |||
3b4d9dc576 | |||
8c6c2fc80d | |||
3886bfadb0 | |||
b35ab4f0e6 | |||
39d979c5fa | |||
ff980032e7 | |||
2fe401dfbb | |||
b4421bb96b | |||
ecb28bf5aa | |||
5c5164b6e7 | |||
b8a081a8a5 | |||
59d80c471a | |||
9e5b0794c5 | |||
1c1fd98591 | |||
d815e4137f | |||
fe0c5bfdb3 | |||
1975c0a4d2 | |||
efde073ab9 | |||
a68c29da4b | |||
becb775d08 | |||
a273491be0 | |||
7d45386262 | |||
82fcb325a1 | |||
115c874779 | |||
8cbebc673d | |||
94b2673c1f | |||
bc73dcb448 | |||
fefa171d83 | |||
5c84b91543 | |||
00b37310e1 | |||
bc27b95771 | |||
31f352c043 | |||
ad62eaa612 | |||
66c2d089f0 | |||
c1bf82adb9 | |||
c05d24e0fe | |||
adbb920ae8 | |||
f871fecf66 | |||
9f919f762a | |||
6df45a6d47 | |||
71128e2392 | |||
38980f1813 | |||
76f30524be | |||
721343349b | |||
4c19a2dba9 | |||
4aacbbe04b | |||
19c2d5b3d4 | |||
2f79aab084 | |||
63b178ec3d | |||
a48bf0bdb6 | |||
22dc8adae5 | |||
04a023c31a | |||
c12e553ff1 | |||
c8eb6182bc | |||
0a7a542edd | |||
9d7ad34873 | |||
63c2a2a74a | |||
2e0de01372 | |||
88e080003d | |||
8a62f0a36c | |||
6c8863aa09 | |||
fe92614c91 | |||
166bb8e048 | |||
c7da5d8cfd | |||
153738dce9 | |||
ce4aa5cb93 | |||
0647582292 | |||
c5620d1c7a | |||
e3a73dff45 | |||
5881f34787 | |||
acffa22a35 | |||
159e8b4fda | |||
d52dd0a8d1 | |||
05252769bf | |||
9c36a3520d | |||
1b282c278f | |||
c9fece997c | |||
c8817f39a9 | |||
2f1aec4744 | |||
e55127906a | |||
789ff49bcf | |||
df02d6dd86 | |||
fb8028a130 | |||
29647bb815 | |||
2911e99baf | |||
3952367bf3 | |||
ea6aade4ce | |||
26baf15b12 | |||
25c5cba7b3 | |||
fb06037392 | |||
90f8a1622e | |||
8c9edb8484 | |||
52cd20d4fe | |||
c7567b65f2 | |||
0fdd1bb929 | |||
559c647db7 | |||
42e2e7cf57 | |||
adad1706e0 | |||
a169743324 | |||
cea7fbe93f | |||
b907e5a2bc | |||
77d2cbda4a | |||
a730fc703f | |||
af26914ba9 | |||
7f7bc64186 | |||
33af76929f | |||
edbf3d2fe3 | |||
a39445fe09 | |||
0b05448a7d | |||
852a73ef82 | |||
b8bfc03875 | |||
c4887ab10a | |||
1abd3977be | |||
98961e3d44 | |||
3f89d3094b | |||
484d3d9a64 | |||
37f3b92ff5 | |||
50cd655c6c | |||
05d1b84f52 | |||
69452231df | |||
4f6bef5b32 | |||
ec96332559 | |||
ee9f0b5d9a | |||
a135f48b6d | |||
61b4c26893 | |||
f1cb46081c | |||
0ec925bd2f | |||
5f1b861525 | |||
f0d70545e8 | |||
26341c7fd4 | |||
e9f4f1b416 | |||
9cd534bd63 | |||
2f8e1fbab8 | |||
c7a6adc771 | |||
8fb2b473ca | |||
5886090d50 | |||
3988ebf432 | |||
5099b79545 | |||
038d06d2e9 | |||
9e1aff9fe6 | |||
a41f331cb4 | |||
71628f1837 | |||
df878a6b60 | |||
48d7f4e8b5 | |||
66f5d27e50 | |||
91dd160b21 | |||
1c44b71fd2 | |||
a5e0ae501d | |||
2d0e642dbe | |||
9ea656f20e | |||
97ae7aed41 | |||
678b4209c8 | |||
b7be4f55be | |||
e7c72ab556 | |||
af785f9e91 | |||
1ac5d68827 | |||
2c987625ae | |||
a77f567403 | |||
110c81f359 | |||
ef4b5c7e59 | |||
c69362442d | |||
6c8791ee32 | |||
274dc1e972 | |||
d9bd86050b | |||
076374ba4f | |||
e117b1ffd2 | |||
8d7fbb614b | |||
a31cfc521c | |||
55a1ce7adf | |||
9f3da659aa | |||
8f9aeaaa67 | |||
b9a5ce1c06 | |||
f67229efa3 | |||
f707f545aa | |||
62f4ea5f0f | |||
ecc3406ca6 | |||
e244b5180e | |||
f85d3d7857 | |||
b404d47b16 | |||
815d1ffa19 | |||
d1063c62b3 | |||
3a0b7355e5 | |||
3bdd4e249f | |||
2c1f55069f | |||
e72f741e78 | |||
f0bcfd0e78 | |||
82e06766b8 | |||
eea1600a38 | |||
8f8c390c75 | |||
23a96dca2d | |||
6f7df8a1fa | |||
92298e5271 | |||
27f0817000 | |||
4596fc0217 | |||
46de203f85 | |||
d752a8907b | |||
4fe369e188 | |||
d8930bbdc2 | |||
ad7be5087c | |||
a4405d7c6f | |||
88f7ddb27d | |||
98f5acebdb | |||
ff78149ec2 | |||
66b7870da7 | |||
82088a8489 | |||
ebcf762132 | |||
ed6b68babf | |||
2e09115c0c | |||
4a8d56a820 | |||
0a3dd872e3 | |||
3e690e0062 | |||
7f8d6c1066 | |||
c6d502f7f8 | |||
7aff3641a1 | |||
2194b5a5c3 | |||
8a35290686 | |||
e40519c32a | |||
b560189c0e | |||
59cfc8a729 | |||
72ed2e90d0 | |||
4e82a76998 | |||
51d5b433d0 | |||
cc0d0a9d1e | |||
82f26fe5f5 | |||
8de57c9887 | |||
ace4e4ffa5 | |||
1fa97903a3 | |||
7e61645b82 | |||
46b0ce9fc6 | |||
78750a7fec | |||
77d9975eb2 | |||
7eed4ee837 | |||
292b435495 | |||
5939c420ce | |||
a5cc9dbb53 | |||
2b810a4e57 | |||
2acf369664 | |||
860b79289f | |||
b519d41f42 | |||
faf184ad63 | |||
1e0f455855 | |||
ced30982df | |||
fed429b0cc | |||
9cb3107dda | |||
548a972c2a | |||
20dcc25eed | |||
620d1402fe | |||
36fb4f4fdb | |||
ea83445149 | |||
1319ff4376 | |||
9c1311c801 | |||
2ce93482b9 | |||
ed2a47f822 | |||
cdee9add01 | |||
2f85b1691a | |||
bf441e8b9e | |||
1c86e9b3b2 | |||
9d6e869899 | |||
e906bf4f31 | |||
5f08bdf8b9 | |||
f1ed022a4d | |||
151e4b9fcc | |||
d0f089a55d | |||
cb05f9bbe9 | |||
fda30cb3e3 | |||
2951e721df | |||
3449f1e256 | |||
6480d1b288 | |||
e76211aa32 | |||
a16de8f842 | |||
24f1dd3b81 | |||
f39551ce7e | |||
3beb7116af | |||
4b1a825efc | |||
01e62551f5 | |||
2f23533a25 | |||
054fbbe8b8 | |||
155d938e04 | |||
94a2ac7884 | |||
b75a98522a | |||
d7dc1b5e44 | |||
e075ea7ae7 | |||
415519acd3 | |||
8cbb836985 | |||
8d0f8bd657 | |||
66547d8fd0 | |||
6e7d5f0925 | |||
29dfa5570a | |||
0c028a03ec | |||
a54c049051 | |||
40904ce0c4 | |||
88f01f5653 | |||
c66794c265 | |||
e4acd83541 | |||
a57f8a1301 | |||
ae9b4e6fa7 | |||
478eca31c7 | |||
2e1603938c | |||
0c9c2accc2 | |||
0fb41e5ced | |||
3f43dbb642 | |||
5069c06906 | |||
58698d7806 | |||
e26c25a062 | |||
0a6434b066 | |||
ff3550c304 | |||
6d4a14082c | |||
9ddf269c2c | |||
25a76a1492 | |||
8439a6ec2a | |||
1ef2eae3aa | |||
d5d034a0ff | |||
5ca35b3cd2 | |||
0a6a3f3163 | |||
3a601382e6 | |||
7a1fdde69e | |||
cbc2ea1b1a | |||
bdf801b0e8 | |||
fe5e8b7177 | |||
11f0f98ad8 | |||
801b534421 | |||
0fc83215e2 | |||
3d3a1a4642 | |||
32a40ba5de | |||
045271230d | |||
ec31f6bf9a | |||
4798d77088 | |||
08c6762039 | |||
26516045e7 | |||
a83b9f7911 | |||
1b7c77e49f | |||
3ab31a4be6 | |||
43dcf77123 | |||
d4bf2da3bd | |||
fa3882845a | |||
fa59748e00 | |||
c38ecb3b5b | |||
875efa8492 | |||
74964bde99 | |||
785fb5cc5a | |||
26d9f0278b | |||
22ebd53c17 | |||
a972c039c3 | |||
f5e18029fa | |||
317c7087c5 | |||
39abe7b7c1 | |||
36a7705a44 | |||
50a21885cf | |||
e86f3d9a49 | |||
738f2961ba | |||
f2bf8287ba | |||
9d5b34e1e7 | |||
d237f4014a | |||
8743a9bfd6 | |||
514d03f2d0 |
@ -20,6 +20,18 @@ build --announce_rc
|
||||
# We use this when uploading artifacts after the build finishes
|
||||
build --symlink_prefix=dist/
|
||||
|
||||
# Enable experimental CircleCI bazel remote cache proxy
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:7643
|
||||
|
||||
# Prevent unstable environment variables from tainting cache keys
|
||||
build --experimental_strict_action_env
|
||||
|
||||
# Save downloaded repositories such as the go toolchain
|
||||
# This directory can then be included in the CircleCI cache
|
||||
# It should save time running the first build
|
||||
build --experimental_repository_cache=/home/circleci/bazel_repository_cache
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
@ -28,6 +40,3 @@ build --local_resources=14336,8.0,1.0
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
||||
|
@ -26,11 +26,6 @@ var_4: &setup-bazel-remote-cache
|
||||
command: ~/bazel-remote-proxy -backend circleci://
|
||||
background: true
|
||||
|
||||
var_5: &setup_bazel_remote_execution
|
||||
run:
|
||||
name: "Setup bazel RBE remote execution"
|
||||
command: openssl aes-256-cbc -d -in .circleci/gcp_token -k "${CIRCLE_PROJECT_REPONAME}" -out /home/circleci/.gcp_credentials && echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV && sudo bash -c "cat .circleci/rbe-bazel.rc >> /etc/bazel.bazelrc"
|
||||
|
||||
# Settings common to each job
|
||||
anchor_1: &job_defaults
|
||||
working_directory: ~/ng
|
||||
@ -47,18 +42,19 @@ version: 2
|
||||
jobs:
|
||||
lint:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
# Check BUILD.bazel formatting before we have a node_modules directory
|
||||
# Then we don't need any exclude pattern to avoid checking those files
|
||||
- run: 'yarn buildifier -mode=check ||
|
||||
- run: 'buildifier -mode=check $(find . -type f \( -name "*.bzl" -or -name BUILD.bazel -or -name BUILD \)) ||
|
||||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
|
||||
# Run the skylark linter to check our Bazel rules
|
||||
- run: 'yarn skylint ||
|
||||
# deprecated-api is disabled because we use actions.new_file(genfiles_dir)
|
||||
# which has no replacement, see https://github.com/bazelbuild/bazel/issues/4858
|
||||
- run: 'find . -type f -name "*.bzl" |
|
||||
xargs java -jar /usr/local/bin/Skylint_deploy.jar --disable-checks=deprecated-api ||
|
||||
(echo -e "\n.bzl files have lint errors. Please run ''yarn skylint''"; exit 1)'
|
||||
|
||||
- restore_cache:
|
||||
@ -74,20 +70,22 @@ jobs:
|
||||
- *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: ls /home/circleci/bazel_repository_cache || true
|
||||
- run: bazel info release
|
||||
- run: bazel run @nodejs//:yarn
|
||||
# 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
|
||||
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||
# NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/...
|
||||
# Setup remote execution and run RBE-compatible tests.
|
||||
- *setup_bazel_remote_execution
|
||||
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only,-local
|
||||
# Now run RBE incompatible tests locally.
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only,local --test_tag_filters=-manual,-ivy-only,local
|
||||
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only
|
||||
|
||||
# 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.
|
||||
@ -121,10 +119,15 @@ jobs:
|
||||
- *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
|
||||
- *setup_bazel_remote_execution
|
||||
- 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:
|
||||
@ -134,10 +137,15 @@ jobs:
|
||||
- *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
|
||||
- *setup_bazel_remote_execution
|
||||
- run: bazel query --output=label //... | xargs bazel test --define=compile=local --build_tag_filters=ivy-local --test_tag_filters=-manual,ivy-local
|
||||
|
||||
# This job should only be run on PR builds, where `CIRCLE_PR_NUMBER` is defined.
|
||||
@ -187,9 +195,12 @@ jobs:
|
||||
- *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
|
||||
- *setup_bazel_remote_execution
|
||||
- run: scripts/build-packages-dist.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
@ -256,7 +267,11 @@ jobs:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- run: xvfb-run --auto-servernum ./aio/scripts/test-production.sh
|
||||
- run:
|
||||
name: Run tests against the deployed apps
|
||||
command: |
|
||||
source "./scripts/ci/env.sh" print
|
||||
xvfb-run --auto-servernum ./aio/scripts/test-production.sh $AIO_MIN_PWA_SCORE
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
@ -306,4 +321,4 @@ workflows:
|
||||
|
||||
notify:
|
||||
webhooks:
|
||||
- url: https://ngbuilds.io/circle-build
|
||||
- url: https://ngbuilds.io/circle-build
|
Binary file not shown.
@ -1,77 +0,0 @@
|
||||
# These options are enabled when running on CI with Remote Build Execution.
|
||||
|
||||
################################################################
|
||||
# Toolchain related flags for remote build execution. #
|
||||
################################################################
|
||||
# Remote Build Execution requires a strong hash function, such as SHA256.
|
||||
startup --host_jvm_args=-Dbazel.DigestFunction=SHA256
|
||||
|
||||
# Depending on how many machines are in the remote execution instance, setting
|
||||
# this higher can make builds faster by allowing more jobs to run in parallel.
|
||||
# Setting it too high can result in jobs that timeout, however, while waiting
|
||||
# for a remote machine to execute them.
|
||||
build --jobs=150
|
||||
|
||||
# Set several flags related to specifying the platform, toolchain and java
|
||||
# properties.
|
||||
# These flags are duplicated rather than imported from (for example)
|
||||
# %workspace%/configs/ubuntu16_04_clang/1.0/toolchain.bazelrc to make this
|
||||
# bazelrc a standalone file that can be copied more easily.
|
||||
# These flags should only be used as is for the rbe-ubuntu16-04 container
|
||||
# and need to be adapted to work with other toolchain containers.
|
||||
build --host_javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:jdk8
|
||||
build --javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:jdk8
|
||||
build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build --crosstool_top=@bazel_toolchains//configs/ubuntu16_04_clang/1.0/bazel_0.15.0/default:toolchain
|
||||
build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
|
||||
# Platform flags:
|
||||
# The toolchain container used for execution is defined in the target indicated
|
||||
# by "extra_execution_platforms", "host_platform" and "platforms".
|
||||
# If you are using your own toolchain container, you need to create a platform
|
||||
# target with "constraint_values" that allow for the toolchain specified with
|
||||
# "extra_toolchains" to be selected (given constraints defined in
|
||||
# "exec_compatible_with").
|
||||
# More about platforms: https://docs.bazel.build/versions/master/platforms.html
|
||||
build --extra_toolchains=@bazel_toolchains//configs/ubuntu16_04_clang/1.0/bazel_0.15.0/cpp:cc-toolchain-clang-x86_64-default
|
||||
build --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
||||
build --host_platform=//tools:rbe_ubuntu1604-angular
|
||||
build --platforms=//tools:rbe_ubuntu1604-angular
|
||||
|
||||
# Set various strategies so that all actions execute remotely. Mixing remote
|
||||
# and local execution will lead to errors unless the toolchain and remote
|
||||
# machine exactly match the host machine.
|
||||
build --spawn_strategy=remote
|
||||
build --strategy=Javac=remote
|
||||
build --strategy=Closure=remote
|
||||
build --genrule_strategy=remote
|
||||
build --define=EXECUTOR=remote
|
||||
|
||||
# Enable the remote cache so action results can be shared across machines,
|
||||
# developers, and workspaces.
|
||||
build --remote_cache=remotebuildexecution.googleapis.com
|
||||
|
||||
# Enable remote execution so actions are performed on the remote systems.
|
||||
build --remote_executor=remotebuildexecution.googleapis.com
|
||||
|
||||
# Remote instance.
|
||||
build --remote_instance_name=projects/internal-200822/instances/default_instance
|
||||
|
||||
# Enable encryption.
|
||||
build --tls_enabled=true
|
||||
|
||||
# Enforce stricter environment rules, which eliminates some non-hermetic
|
||||
# behavior and therefore improves both the remote cache hit rate and the
|
||||
# correctness and repeatability of the build.
|
||||
build --experimental_strict_action_env=true
|
||||
|
||||
# Set a higher timeout value, just in case.
|
||||
build --remote_timeout=3600
|
||||
|
||||
# Enable authentication. This will pick up application default credentials by
|
||||
# default. You can use --auth_credentials=some_file.json to use a service
|
||||
# account credential instead.
|
||||
build --auth_enabled=true
|
||||
|
||||
# Do not accept remote cache.
|
||||
build --remote_accept_cached=false
|
@ -49,12 +49,14 @@ env:
|
||||
- CI_MODE=browserstack_optional
|
||||
- CI_MODE=aio_tools_test
|
||||
- CI_MODE=aio
|
||||
- CI_MODE=aio_local
|
||||
- CI_MODE=aio_e2e AIO_SHARD=0
|
||||
- CI_MODE=aio_e2e AIO_SHARD=1
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: "CI_MODE=aio_local"
|
||||
- env: "CI_MODE=saucelabs_optional"
|
||||
- env: "CI_MODE=browserstack_optional"
|
||||
|
||||
|
145
CHANGELOG.md
145
CHANGELOG.md
@ -1,27 +1,11 @@
|
||||
<a name="7.0.0-rc.0"></a>
|
||||
# [7.0.0-rc.0](https://github.com/angular/angular/compare/7.0.0-beta.7...7.0.0-rc.0) (2018-09-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for TypeScript 3.1 ([#26151](https://github.com/angular/angular/issues/26151)) ([9993c72](https://github.com/angular/angular/commit/9993c72))
|
||||
|
||||
|
||||
|
||||
<a name="7.0.0-beta.7"></a>
|
||||
# [7.0.0-beta.7](https://github.com/angular/angular/compare/7.0.0-beta.6...7.0.0-beta.7) (2018-09-26)
|
||||
<a name="6.1.10"></a>
|
||||
## [6.1.10](https://github.com/angular/angular/compare/6.1.9...6.1.10) (2018-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** add missing `peerDependency ` to `[@angular](https://github.com/angular)/compiler` ([#26033](https://github.com/angular/angular/issues/26033)) ([549de1e](https://github.com/angular/angular/commit/549de1e)), closes [/github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e#diff-58563046c4439699f2e6a89187099a54](https://github.com//github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e/issues/diff-58563046c4439699f2e6a89187099a54)
|
||||
* **service-worker:** do not blow up when caches are unwritable ([#26042](https://github.com/angular/angular/issues/26042)) ([2bd767c](https://github.com/angular/angular/commit/2bd767c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler-cli:** add support to extend `angularCompilerOptions` ([#22717](https://github.com/angular/angular/issues/22717)) ([d7e5bbf](https://github.com/angular/angular/commit/d7e5bbf)), closes [#22684](https://github.com/angular/angular/issues/22684)
|
||||
* **platform-server:** update domino to v2.1.0 ([#25564](https://github.com/angular/angular/issues/25564)) ([3fb0da2](https://github.com/angular/angular/commit/3fb0da2))
|
||||
* **platform-browser:** fix [#22155](https://github.com/angular/angular/issues/22155), destroy hammer manager when `HammerInstance.off()` is run ([#22156](https://github.com/angular/angular/issues/22156)) ([3b4d9dc](https://github.com/angular/angular/commit/3b4d9dc))
|
||||
* **upgrade:** properly destroy upgraded component elements and descendants ([#26209](https://github.com/angular/angular/issues/26209)) ([623adbb](https://github.com/angular/angular/commit/623adbb)), closes [#26208](https://github.com/angular/angular/issues/26208)
|
||||
|
||||
|
||||
|
||||
@ -35,57 +19,24 @@
|
||||
|
||||
|
||||
|
||||
<a name="7.0.0-beta.6"></a>
|
||||
# [7.0.0-beta.6](https://github.com/angular/angular/compare/7.0.0-beta.5...7.0.0-beta.6) (2018-09-19)
|
||||
<a name="6.1.8"></a>
|
||||
## [6.1.8](https://github.com/angular/angular/compare/6.1.7...6.1.8) (2018-09-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** specify the package and lock files using the workspace ([#25694](https://github.com/angular/angular/issues/25694)) ([ddc1335](https://github.com/angular/angular/commit/ddc1335))
|
||||
* **common:** register locale data for all equivalent closure locales ([#25867](https://github.com/angular/angular/issues/25867)) ([d83f9d4](https://github.com/angular/angular/commit/d83f9d4))
|
||||
* **compiler:** Fix look up of entryComponents in AOT Summaries ([#24892](https://github.com/angular/angular/issues/24892)) ([00d3666](https://github.com/angular/angular/commit/00d3666))
|
||||
* **ivy:** add [@nocollapse](https://github.com/nocollapse) when writing closure-annotated code ([#25775](https://github.com/angular/angular/issues/25775)) ([a0c4b2d](https://github.com/angular/angular/commit/a0c4b2d))
|
||||
* **ivy:** don't accidently read the inherited definition ([#25736](https://github.com/angular/angular/issues/25736)) ([d5bd86a](https://github.com/angular/angular/commit/d5bd86a)), closes [#24011](https://github.com/angular/angular/issues/24011) [#25026](https://github.com/angular/angular/issues/25026)
|
||||
* **ivy:** ensure Ivy *Ref classes derive from view engine equivalents ([#25775](https://github.com/angular/angular/issues/25775)) ([a9099e8](https://github.com/angular/angular/commit/a9099e8))
|
||||
* **ivy:** events should not mark views dirty by default ([#25969](https://github.com/angular/angular/issues/25969)) ([5653874](https://github.com/angular/angular/commit/5653874))
|
||||
* **ivy:** ngcc should compile entry-points in the correct order ([#25862](https://github.com/angular/angular/issues/25862)) ([9b1bb37](https://github.com/angular/angular/commit/9b1bb37))
|
||||
* **ivy:** use proper sanitizer names ([#25817](https://github.com/angular/angular/issues/25817)) ([21009b0](https://github.com/angular/angular/commit/21009b0)), closes [#25816](https://github.com/angular/angular/issues/25816)
|
||||
* **router:** mount correct component if router outlet was not instantiated and if using a route reuse strategy ([#25313](https://github.com/angular/angular/issues/25313)) ([#25314](https://github.com/angular/angular/issues/25314)) ([8dc2b11](https://github.com/angular/angular/commit/8dc2b11))
|
||||
* **bazel:** allow compile_strategy to be (privately) imported ([#25080](https://github.com/angular/angular/issues/25080)) ([2d0e642](https://github.com/angular/angular/commit/2d0e642))
|
||||
* **bazel:** correct type concatenated to devmode_js ([#25467](https://github.com/angular/angular/issues/25467)) ([91dd160](https://github.com/angular/angular/commit/91dd160))
|
||||
* **bazel:** move bazel managed runtime deps for downstream usage ([#25690](https://github.com/angular/angular/issues/25690)) ([48d7f4e](https://github.com/angular/angular/commit/48d7f4e))
|
||||
* **bazel:** specify the package and lock files using the workspace ([#25694](https://github.com/angular/angular/issues/25694)) ([678b420](https://github.com/angular/angular/commit/678b420))
|
||||
* **compiler:** Fix look up of entryComponents in AOT Summaries ([#24892](https://github.com/angular/angular/issues/24892)) ([a31cfc5](https://github.com/angular/angular/commit/a31cfc5))
|
||||
* **router:** mount correct component if router outlet was not instantiated and if using a route reuse strategy ([#25313](https://github.com/angular/angular/issues/25313)) ([#25314](https://github.com/angular/angular/issues/25314)) ([e117b1f](https://github.com/angular/angular/commit/e117b1f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** add additional parameters to `ts_api_guardian_test` def ([#25694](https://github.com/angular/angular/issues/25694)) ([2a21ca0](https://github.com/angular/angular/commit/2a21ca0))
|
||||
* **ivy:** allow combined context discovery for components, directives and elements ([#25754](https://github.com/angular/angular/issues/25754)) ([62be8c2](https://github.com/angular/angular/commit/62be8c2))
|
||||
* **ivy:** patch animations into metadata ([#25828](https://github.com/angular/angular/issues/25828)) ([d2dfd48](https://github.com/angular/angular/commit/d2dfd48))
|
||||
* **ivy:** resolve references to vars in .d.ts files ([#25775](https://github.com/angular/angular/issues/25775)) ([96d6b79](https://github.com/angular/angular/commit/96d6b79))
|
||||
* **ivy:** support animation [@triggers](https://github.com/triggers) in templates ([#25849](https://github.com/angular/angular/issues/25849)) ([e363388](https://github.com/angular/angular/commit/e363388))
|
||||
* **ivy:** support bootstrap in ngModuleDef ([#25775](https://github.com/angular/angular/issues/25775)) ([13ccdfd](https://github.com/angular/angular/commit/13ccdfd))
|
||||
|
||||
|
||||
|
||||
<a name="7.0.0-beta.5"></a>
|
||||
# [7.0.0-beta.5](https://github.com/angular/angular/compare/7.0.0-beta.4...7.0.0-beta.5) (2018-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** protractor rule should include *.e2e-spec.js ([#25701](https://github.com/angular/angular/issues/25701)) ([3809e0f](https://github.com/angular/angular/commit/3809e0f))
|
||||
* **benchpress:** Use performance.mark() instead of console.time() ([#24114](https://github.com/angular/angular/issues/24114)) ([06d0400](https://github.com/angular/angular/commit/06d0400))
|
||||
* **compiler:** add hostVars and support pure functions in host bindings ([#25626](https://github.com/angular/angular/issues/25626)) ([b424b31](https://github.com/angular/angular/commit/b424b31))
|
||||
* **core:** do not clear element content when using shadow dom ([#24861](https://github.com/angular/angular/issues/24861)) ([6e828bb](https://github.com/angular/angular/commit/6e828bb))
|
||||
* **core:** size regression with closure compiler ([#25531](https://github.com/angular/angular/issues/25531)) ([1f59f2f](https://github.com/angular/angular/commit/1f59f2f))
|
||||
* **elements:** add compiler dependency ([#24861](https://github.com/angular/angular/issues/24861)) ([6143da6](https://github.com/angular/angular/commit/6143da6))
|
||||
* **elements:** add compiler to integration ([#24861](https://github.com/angular/angular/issues/24861)) ([a080ffc](https://github.com/angular/angular/commit/a080ffc))
|
||||
* **elements:** strict null checks ([#24861](https://github.com/angular/angular/issues/24861)) ([a8210d0](https://github.com/angular/angular/commit/a8210d0))
|
||||
* **upgrade:** trigger `$destroy` event on upgraded component element ([#25357](https://github.com/angular/angular/issues/25357)) ([2a672a9](https://github.com/angular/angular/commit/2a672a9)), closes [#25334](https://github.com/angular/angular/issues/25334)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **elements:** enable Shadow DOM v1 and slots ([#24861](https://github.com/angular/angular/issues/24861)) ([c9844a2](https://github.com/angular/angular/commit/c9844a2))
|
||||
* **router:** warn if navigation triggered outside Angular zone ([#24959](https://github.com/angular/angular/issues/24959)) ([010e35d](https://github.com/angular/angular/commit/010e35d)), closes [#15770](https://github.com/angular/angular/issues/15770) [#15946](https://github.com/angular/angular/issues/15946) [#24728](https://github.com/angular/angular/issues/24728)
|
||||
|
||||
* **bazel:** add additional parameters to `ts_api_guardian_test` def ([#25694](https://github.com/angular/angular/issues/25694)) ([97ae7ae](https://github.com/angular/angular/commit/97ae7ae))
|
||||
* **ivy:** enable .ngfactory.js generation in g3 only ([#25392](https://github.com/angular/angular/issues/25392)) ([1c44b71](https://github.com/angular/angular/commit/1c44b71))
|
||||
|
||||
|
||||
|
||||
@ -102,18 +53,6 @@
|
||||
* **router:** warn if navigation triggered outside Angular zone ([#24959](https://github.com/angular/angular/issues/24959)) ([23a96dc](https://github.com/angular/angular/commit/23a96dc)), closes [#15770](https://github.com/angular/angular/issues/15770) [#15946](https://github.com/angular/angular/issues/15946) [#24728](https://github.com/angular/angular/issues/24728)
|
||||
|
||||
|
||||
<a name="7.0.0-beta.4"></a>
|
||||
# [7.0.0-beta.4](https://github.com/angular/angular/compare/7.0.0-beta.3...7.0.0-beta.4) (2018-08-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** Cache fileNameToModuleName lookups ([#25731](https://github.com/angular/angular/issues/25731)) ([f394ba0](https://github.com/angular/angular/commit/f394ba0))
|
||||
* **bazel:** move bazel managed runtime deps for downstream usage ([#25690](https://github.com/angular/angular/issues/25690)) ([6ed7993](https://github.com/angular/angular/commit/6ed7993))
|
||||
* **bazel:** only lookup amd module-name tags in .d.ts files ([#25710](https://github.com/angular/angular/issues/25710)) ([42072c4](https://github.com/angular/angular/commit/42072c4))
|
||||
* **compiler:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.6"></a>
|
||||
## [6.1.6](https://github.com/angular/angular/compare/6.1.5...6.1.6) (2018-08-29)
|
||||
@ -127,16 +66,6 @@
|
||||
|
||||
Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6 instead. sorry! :-)
|
||||
|
||||
<a name="7.0.0-beta.3"></a>
|
||||
# [7.0.0-beta.3](https://github.com/angular/angular/compare/7.0.0-beta.2...7.0.0-beta.3) (2018-08-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** add UrlSegment[] to CanLoad interface ([#13127](https://github.com/angular/angular/issues/13127)) ([07d8d39](https://github.com/angular/angular/commit/07d8d39)), closes [#12411](https://github.com/angular/angular/issues/12411)
|
||||
|
||||
|
||||
|
||||
<a name="6.1.4"></a>
|
||||
## [6.1.4](https://github.com/angular/angular/compare/6.1.3...6.1.4) (2018-08-22)
|
||||
|
||||
@ -147,15 +76,6 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
|
||||
|
||||
|
||||
|
||||
<a name="7.0.0-beta.2"></a>
|
||||
# [7.0.0-beta.2](https://github.com/angular/angular/compare/7.0.0-beta.1...7.0.0-beta.2) (2018-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** correct type concatenated to devmode_js ([#25467](https://github.com/angular/angular/issues/25467)) ([fb2c524](https://github.com/angular/angular/commit/fb2c524))
|
||||
|
||||
|
||||
<a name="6.1.3"></a>
|
||||
## [6.1.3](https://github.com/angular/angular/compare/6.1.2...6.1.3) (2018-08-15)
|
||||
|
||||
@ -166,24 +86,6 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
|
||||
|
||||
|
||||
|
||||
<a name="7.0.0-beta.1"></a>
|
||||
# [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** use the oldProgram option in watch mode ([#21364](https://github.com/angular/angular/issues/21364)) ([c6e5b97](https://github.com/angular/angular/commit/c6e5b97)), closes [#21361](https://github.com/angular/angular/issues/21361)
|
||||
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
|
||||
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([02e201a](https://github.com/angular/angular/commit/02e201a))
|
||||
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ba1e25f](https://github.com/angular/angular/commit/ba1e25f)), closes [#20061](https://github.com/angular/angular/issues/20061)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add DoBootstrap interface. ([#24558](https://github.com/angular/angular/issues/24558)) ([732026c](https://github.com/angular/angular/commit/732026c)), closes [#24557](https://github.com/angular/angular/issues/24557)
|
||||
|
||||
|
||||
|
||||
<a name="6.1.2"></a>
|
||||
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
|
||||
|
||||
@ -194,26 +96,13 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
|
||||
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([054fbbe](https://github.com/angular/angular/commit/054fbbe))
|
||||
|
||||
|
||||
<a name="7.0.0-beta.0"></a>
|
||||
# [7.0.0-beta.0](https://github.com/angular/angular/compare/6.1.0...7.0.0-beta.0) (2018-08-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow compile_strategy to be (privately) imported ([#25080](https://github.com/angular/angular/issues/25080)) ([0d1d589](https://github.com/angular/angular/commit/0d1d589))
|
||||
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
|
||||
* **compiler-cli:** correct realPath to realpath. ([#25023](https://github.com/angular/angular/issues/25023)) ([01e6dab](https://github.com/angular/angular/commit/01e6dab))
|
||||
* **core:** throw error message when @Output not initialized ([#19116](https://github.com/angular/angular/issues/19116)) ([adf510f](https://github.com/angular/angular/commit/adf510f)), closes [#3664](https://github.com/angular/angular/issues/3664)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** add "original" placeholder value on extracted XMB ([#25079](https://github.com/angular/angular/issues/25079)) ([e99d860](https://github.com/angular/angular/commit/e99d860))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.1"></a>
|
||||
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** correct tsickle dependency version to fix typescript 2.9 compatibility ([fec29fa](https://github.com/angular/angular/commit/317c7087c56b72aa74cd6d6a8f719e6e7fec29fa))
|
||||
|
||||
|
||||
|
@ -71,6 +71,8 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
||||
Discussing the design up front helps to ensure that we're ready to accept your work.
|
||||
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
|
||||
1. Fork the angular/angular repo.
|
||||
|
62
WORKSPACE
62
WORKSPACE
@ -5,30 +5,28 @@ workspace(name = "angular")
|
||||
#
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
sha256 = "1626ee2cc9770af6950bfc77dffa027f9aedf330fe2ea2ee7e504428927bd95d",
|
||||
strip_prefix = "rules_typescript-0.17.0",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.17.0.zip",
|
||||
strip_prefix = "rules_typescript-0.17.0",
|
||||
sha256 = "1626ee2cc9770af6950bfc77dffa027f9aedf330fe2ea2ee7e504428927bd95d",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
|
||||
|
||||
rules_typescript_dependencies()
|
||||
|
||||
http_archive(
|
||||
name = "bazel_toolchains",
|
||||
sha256 = "c3b08805602cd1d2b67ebe96407c1e8c6ed3d4ce55236ae2efe2f1948f38168d",
|
||||
strip_prefix = "bazel-toolchains-5124557861ebf4c0b67f98180bff1f8551e0b421",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
|
||||
],
|
||||
name = "bazel_toolchains",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
|
||||
],
|
||||
strip_prefix = "bazel-toolchains-5124557861ebf4c0b67f98180bff1f8551e0b421",
|
||||
sha256 = "c3b08805602cd1d2b67ebe96407c1e8c6ed3d4ce55236ae2efe2f1948f38168d",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "dbe9fb97d5a7833b2a733eebc78c9c1e3880f676ac8af16e58ccf2139cbcad03",
|
||||
strip_prefix = "rules_sass-1.11.0",
|
||||
url = "https://github.com/bazelbuild/rules_sass/archive/1.11.0.zip",
|
||||
strip_prefix = "rules_sass-1.11.0",
|
||||
sha256 = "dbe9fb97d5a7833b2a733eebc78c9c1e3880f676ac8af16e58ccf2139cbcad03",
|
||||
)
|
||||
|
||||
# This commit matches the version of buildifier in angular/ngcontainer
|
||||
@ -38,25 +36,25 @@ BAZEL_BUILDTOOLS_VERSION = "49a6c199e3fbf5d94534b2771868677d3f9c6de9"
|
||||
|
||||
http_archive(
|
||||
name = "com_github_bazelbuild_buildtools",
|
||||
sha256 = "edf39af5fc257521e4af4c40829fffe8fba6d0ebff9f4dd69a6f8f1223ae047b",
|
||||
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
||||
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
|
||||
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
||||
sha256 = "edf39af5fc257521e4af4c40829fffe8fba6d0ebff9f4dd69a6f8f1223ae047b",
|
||||
)
|
||||
|
||||
# Fetching the Bazel source code allows us to compile the Skylark linter
|
||||
http_archive(
|
||||
name = "io_bazel",
|
||||
sha256 = "ace8cced3b21e64a8fdad68508e9b0644201ec848ad583651719841d567fc66d",
|
||||
strip_prefix = "bazel-0.17.1",
|
||||
url = "https://github.com/bazelbuild/bazel/archive/0.17.1.zip",
|
||||
strip_prefix = "bazel-0.17.1",
|
||||
sha256 = "ace8cced3b21e64a8fdad68508e9b0644201ec848ad583651719841d567fc66d",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_skydoc",
|
||||
sha256 = "7bfb5545f59792a2745f2523b9eef363f9c3e7274791c030885e7069f8116016",
|
||||
strip_prefix = "skydoc-fe2e9f888d28e567fef62ec9d4a93c425526d701",
|
||||
# TODO: switch to upstream when https://github.com/bazelbuild/skydoc/pull/103 is merged
|
||||
url = "https://github.com/alexeagle/skydoc/archive/fe2e9f888d28e567fef62ec9d4a93c425526d701.zip",
|
||||
strip_prefix = "skydoc-fe2e9f888d28e567fef62ec9d4a93c425526d701",
|
||||
sha256 = "7bfb5545f59792a2745f2523b9eef363f9c3e7274791c030885e7069f8116016",
|
||||
)
|
||||
|
||||
# We have a source dependency on the Devkit repository, because it's built with
|
||||
@ -67,16 +65,16 @@ http_archive(
|
||||
# ts_library rules in the devkit repository.
|
||||
http_archive(
|
||||
name = "angular_cli",
|
||||
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
|
||||
strip_prefix = "angular-cli-6.1.0-rc.0",
|
||||
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",
|
||||
sha256 = "774b893a0700b0692a76e2e5b7e7610dbbe330ffbe3fe864b4b52ca718061d5a",
|
||||
strip_prefix = "brotli-1.0.5",
|
||||
url = "https://github.com/google/brotli/archive/v1.0.5.zip",
|
||||
strip_prefix = "brotli-1.0.5",
|
||||
sha256 = "774b893a0700b0692a76e2e5b7e7610dbbe330ffbe3fe864b4b52ca718061d5a",
|
||||
)
|
||||
|
||||
#
|
||||
@ -106,24 +104,21 @@ If you are on a Mac and using Homebrew, there is a breaking change to the instal
|
||||
See https://blog.bazel.build/2018/08/22/bazel-homebrew.html
|
||||
|
||||
""")
|
||||
|
||||
node_repositories(
|
||||
node_version = "10.9.0",
|
||||
package_json = ["//:package.json"],
|
||||
preserve_symlinks = True,
|
||||
node_version = "10.9.0",
|
||||
yarn_version = "1.9.2",
|
||||
)
|
||||
|
||||
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,
|
||||
@ -141,20 +136,7 @@ ng_setup_workspace()
|
||||
# Skylark documentation generation
|
||||
|
||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||
|
||||
sass_repositories()
|
||||
|
||||
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
|
||||
|
||||
skydoc_repositories()
|
||||
|
||||
##################################
|
||||
# Prevent Bazel from trying to build rxjs under angular devkit
|
||||
local_repository(
|
||||
name = "rxjs_ignore_nested_1",
|
||||
path = "node_modules/@angular-devkit/core/node_modules/rxjs/src",
|
||||
)
|
||||
local_repository(
|
||||
name = "rxjs_ignore_nested_2",
|
||||
path = "node_modules/@angular-devkit/schematics/node_modules/rxjs/src",
|
||||
)
|
||||
|
@ -36,6 +36,11 @@ server {
|
||||
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
||||
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
||||
|
||||
error_page 404 /404.html;
|
||||
location "=/404.html" {
|
||||
internal;
|
||||
}
|
||||
|
||||
location "~/[^/]+\.[^/]+$" {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
"@types/shelljs": "^0.8.0",
|
||||
"@types/supertest": "^2.0.5",
|
||||
"nodemon": "^1.18.3",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"supertest": "^3.1.0",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||
|
@ -129,7 +129,7 @@ ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
|
||||
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
dependencies:
|
||||
@ -384,7 +384,7 @@ chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
|
||||
chalk@^2.0.1, chalk@^2.3.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
|
||||
dependencies:
|
||||
@ -392,6 +392,15 @@ chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^2.4.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
check-error@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
@ -532,9 +541,10 @@ cross-spawn@^5.0.1:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^6.0.4:
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
|
||||
dependencies:
|
||||
nice-try "^1.0.4"
|
||||
path-key "^2.0.1"
|
||||
@ -1630,16 +1640,17 @@ npm-packlist@^1.1.6:
|
||||
ignore-walk "^3.0.1"
|
||||
npm-bundled "^1.0.1"
|
||||
|
||||
npm-run-all@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.3.tgz#49f15b55a66bb4101664ce270cb18e7103f8f185"
|
||||
npm-run-all@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
|
||||
integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
chalk "^2.1.0"
|
||||
cross-spawn "^6.0.4"
|
||||
ansi-styles "^3.2.1"
|
||||
chalk "^2.4.1"
|
||||
cross-spawn "^6.0.5"
|
||||
memorystream "^0.3.1"
|
||||
minimatch "^3.0.4"
|
||||
ps-tree "^1.1.0"
|
||||
pidtree "^0.3.0"
|
||||
read-pkg "^3.0.0"
|
||||
shell-quote "^1.6.1"
|
||||
string.prototype.padend "^3.0.0"
|
||||
@ -1786,6 +1797,11 @@ pause-stream@0.0.11:
|
||||
dependencies:
|
||||
through "~2.3"
|
||||
|
||||
pidtree@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b"
|
||||
integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==
|
||||
|
||||
pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
|
1
aio/content/cli-src/.gitignore
vendored
1
aio/content/cli-src/.gitignore
vendored
@ -1 +0,0 @@
|
||||
yarn.lock
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@angular/cli": "https://github.com/angular/cli-builds#master"
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
<h1 class="no-toc">CLI Command Reference</h1>
|
||||
|
||||
The Angular CLI is a command-line tool that you use to initialize, develop, scaffold, and maintain Angular applications.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installing Angular CLI
|
||||
|
||||
The current version of Angular CLI is 6.x.
|
||||
|
||||
* Both the CLI and the projects that you generate with the tool have dependencies that require Node 8.9 or higher, together with NPM 5.5.1 or higher.
|
||||
* Install the CLI using npm:
|
||||
`npm install -g @angular/cli`
|
||||
* The CLI is an open-source tool:
|
||||
https://github.com/angular/angular-cli/tree/master/packages/angular/cli
|
||||
|
||||
For details about changes between versions, and information about updating from previous releases, see the Releases tab on GitHub.
|
||||
|
||||
### Basic workflow
|
||||
|
||||
Invoke the tool on the command line through the ng executable. Online help is available on the command line:
|
||||
|
||||
```
|
||||
> ng help Lists commands with short descriptions
|
||||
> ng <command> --help Lists options for a command.
|
||||
```
|
||||
|
||||
To create, build, and serve a new, basic Angular project on a development server, use the following commands:
|
||||
|
||||
```
|
||||
cd <parent of new workspace>
|
||||
ng new my-project
|
||||
cd my-project
|
||||
ng serve
|
||||
```
|
||||
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
|
||||
### Workspaces and project files
|
||||
|
||||
Angular 6 introduced the workspace directory structure for Angular apps. A workspace defines a project. A project can contain multiple apps, as well as libraries that can be used in any of the apps.
|
||||
|
||||
Some commands (such as build) must be executed from within a workspace folder, and others (such as new) must be executed from outside any workspace. This requirement is called out in the description of each command where it applies.The `new` command creates a [workspace](guide/glossary#workspace) to contain [projects](guide/glossary#project). A project can be an app or a library, and a workspace can contain multiple apps and libraries.
|
||||
|
||||
A newly generated app project contains the source files for a root module, with a root component and template, which you can edit directly, or add to and modify using CLI commands. Use the generate command to add new files for additional components and services, and code for new pipes, directives, and so on.
|
||||
|
||||
* Commands such as `add` and `generate`, that create or operate on apps and libraries, must be executed from within a workspace folder.
|
||||
* Apps in a workspace can use libraries in the same workspace.
|
||||
* Each project has a `src` folder that contains the logic, data, and assets.
|
||||
See an example of the [file structure](guide/quickstart#project-file-review) in [Getting Started](guide/quickstart).
|
||||
|
||||
When you use the `serve` command to build an app, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
||||
|
||||
### Configuring the CLI
|
||||
|
||||
Configuration files let you customize your project. The CLI configuration file, angular.json, is created at the top level of the project folder. This is where you can set CLI defaults for your project, and specify which files to include when the CLI builds the project.
|
||||
|
||||
The CLI config command lets you set and retrieve configuration values from the command line, or you can edit the angular.json file directly.
|
||||
|
||||
* See the complete schema for angular.json.
|
||||
* Learn more about configuration options for Angular (link to new guide?)
|
||||
|
||||
### Command options and arguments
|
||||
|
||||
All commands and some options have aliases, as listed in the descriptions. Option names are prefixed with a double dash (--), but arguments and option aliases are not.
|
||||
|
||||
Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option. Most commands have additional options.
|
||||
|
||||
Command syntax is shown as follows:
|
||||
|
||||
```
|
||||
ng commandNameOrAlias <arg> [options]
|
||||
```
|
||||
|
||||
Options take either string or Boolean arguments. Defaults are shown in bold for Boolean or enumerated values, and are given with the description. For example:
|
||||
|
||||
```
|
||||
--optionNameOrAlias=<filename>
|
||||
--optionNameOrAlias=true|false
|
||||
--optionNameOrAlias=allowedValue1|allowedValue2|allowedValue3
|
||||
```
|
||||
|
||||
Boolean options can also be expressed with a prefix `no-` to indicate a value of false. For example, `--no-prod` is equivalent to `--prod=false`.
|
@ -4,7 +4,7 @@ import { Observable, of } from 'rxjs';
|
||||
// #docregion observer
|
||||
|
||||
// Create simple observable that emits three values
|
||||
const myObservable = Observable.of(1, 2, 3);
|
||||
const myObservable = of(1, 2, 3);
|
||||
|
||||
// Create observer object
|
||||
const myObserver = {
|
||||
|
@ -1,8 +1,7 @@
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
import { async, fakeAsync, tick } from '@angular/core/testing';
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { interval, of } from 'rxjs';
|
||||
import { delay, take } from 'rxjs/operators';
|
||||
|
||||
describe('Angular async helper', () => {
|
||||
let actuallyDone = false;
|
||||
@ -21,49 +20,120 @@ describe('Angular async helper', () => {
|
||||
});
|
||||
|
||||
it('should run async test with task',
|
||||
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
||||
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
||||
|
||||
it('should run async test with task', async(() => {
|
||||
const id = setInterval(() => {
|
||||
actuallyDone = true;
|
||||
clearInterval(id);
|
||||
}, 100);
|
||||
}));
|
||||
|
||||
it('should run async test with successful promise', async(() => {
|
||||
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
||||
p.then(() => { actuallyDone = true; });
|
||||
}));
|
||||
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
||||
p.then(() => { actuallyDone = true; });
|
||||
}));
|
||||
|
||||
it('should run async test with failed promise', async(() => {
|
||||
const p = new Promise((resolve, reject) => { setTimeout(reject, 10); });
|
||||
p.catch(() => { actuallyDone = true; });
|
||||
}));
|
||||
const p = new Promise((resolve, reject) => { setTimeout(reject, 10); });
|
||||
p.catch(() => { actuallyDone = true; });
|
||||
}));
|
||||
|
||||
// Use done. Cannot use setInterval with async or fakeAsync
|
||||
// See https://github.com/angular/angular/issues/10127
|
||||
// Use done. Can also use async or fakeAsync.
|
||||
it('should run async test with successful delayed Observable', (done: DoneFn) => {
|
||||
const source = of(true).pipe(delay(10));
|
||||
source.subscribe(
|
||||
val => actuallyDone = true,
|
||||
err => fail(err),
|
||||
done
|
||||
);
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err), done);
|
||||
});
|
||||
|
||||
// Cannot use setInterval from within an async zone test
|
||||
// See https://github.com/angular/angular/issues/10127
|
||||
// xit('should run async test with successful delayed Observable', async(() => {
|
||||
// const source = of(true).pipe(delay(10));
|
||||
// source.subscribe(
|
||||
// val => actuallyDone = true,
|
||||
// err => fail(err)
|
||||
// );
|
||||
// }));
|
||||
// #docregion fake-async-test-tick
|
||||
it('should run timeout callback with delay after call tick with millis', fakeAsync(() => {
|
||||
let called = false;
|
||||
setTimeout(() => { called = true; }, 100);
|
||||
tick(100);
|
||||
expect(called).toBe(true);
|
||||
}));
|
||||
// #enddocregion fake-async-test-tick
|
||||
|
||||
// // Fail message: Error: 1 periodic timer(s) still in the queue
|
||||
// // See https://github.com/angular/angular/issues/10127
|
||||
// xit('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||
// const source = of(true).pipe(delay(10));
|
||||
// source.subscribe(
|
||||
// val => actuallyDone = true,
|
||||
// err => fail(err)
|
||||
// );
|
||||
// #docregion fake-async-test-date
|
||||
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
|
||||
const start = Date.now();
|
||||
tick(100);
|
||||
const end = Date.now();
|
||||
expect(end - start).toBe(100);
|
||||
}));
|
||||
// #enddocregion fake-async-test-date
|
||||
|
||||
// tick();
|
||||
// }));
|
||||
// #docregion fake-async-test-rxjs
|
||||
it('should get Date diff correctly in fakeAsync with rxjs scheduler', fakeAsync(() => {
|
||||
// need to add `import 'zone.js/dist/zone-patch-rxjs-fake-async'
|
||||
// to patch rxjs scheduler
|
||||
let result = null;
|
||||
of ('hello').pipe(delay(1000)).subscribe(v => { result = v; });
|
||||
expect(result).toBeNull();
|
||||
tick(1000);
|
||||
expect(result).toBe('hello');
|
||||
|
||||
const start = new Date().getTime();
|
||||
let dateDiff = 0;
|
||||
interval(1000).pipe(take(2)).subscribe(() => dateDiff = (new Date().getTime() - start));
|
||||
|
||||
tick(1000);
|
||||
expect(dateDiff).toBe(1000);
|
||||
tick(1000);
|
||||
expect(dateDiff).toBe(2000);
|
||||
}));
|
||||
// #enddocregion fake-async-test-rxjs
|
||||
|
||||
// #docregion fake-async-test-clock
|
||||
describe('use jasmine.clock()', () => {
|
||||
// need to config __zone_symbol__fakeAsyncPatchLock flag
|
||||
// before loading zone.js/dist/zone-testing
|
||||
beforeEach(() => { jasmine.clock().install(); });
|
||||
afterEach(() => { jasmine.clock().uninstall(); });
|
||||
it('should auto enter fakeAsync', () => {
|
||||
// is in fakeAsync now, don't need to call fakeAsync(testFn)
|
||||
let called = false;
|
||||
setTimeout(() => { called = true; }, 100);
|
||||
jasmine.clock().tick(100);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
});
|
||||
// #enddocregion fake-async-test-clock
|
||||
|
||||
// #docregion async-test-promise-then
|
||||
describe('test jsonp', () => {
|
||||
function jsonp(url: string, callback: Function) {
|
||||
// do a jsonp call which is not zone aware
|
||||
}
|
||||
// need to config __zone_symbol__supportWaitUnResolvedChainedPromise flag
|
||||
// before loading zone.js/dist/zone-testing
|
||||
it('should wait until promise.then is called', async(() => {
|
||||
let finished = false;
|
||||
new Promise((res, rej) => {
|
||||
jsonp('localhost:8080/jsonp', () => {
|
||||
// success callback and resolve the promise
|
||||
finished = true;
|
||||
res();
|
||||
});
|
||||
}).then(() => {
|
||||
// async will wait until promise.then is called
|
||||
// if __zone_symbol__supportWaitUnResolvedChainedPromise is set
|
||||
expect(finished).toBe(true);
|
||||
});
|
||||
}));
|
||||
});
|
||||
// #enddocregion async-test-promise-then
|
||||
|
||||
it('should run async test with successful delayed Observable', async(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
}));
|
||||
|
||||
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
|
||||
tick(10);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -1241,7 +1241,7 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
|
||||
### Non-null type assertion operator
|
||||
|
||||
Use the [non-null type assertion operator](guide/template-syntax#non-null-assertion-operator)
|
||||
to suppress the `Object is possibly 'undefined'` error when it is incovienent to use
|
||||
to suppress the `Object is possibly 'undefined'` error when it is inconvenient to use
|
||||
`*ngIf` or when some constraint in the component ensures that the expression is always
|
||||
non-null when the binding expression is interpolated.
|
||||
|
||||
@ -1308,28 +1308,6 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
|
||||
}
|
||||
```
|
||||
|
||||
{@a tsconfig-extends}
|
||||
## Configuration inheritance with extends
|
||||
Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the `tsconfig.json` on `angularCompilerOptions`. A tsconfig file can inherit configurations from another file using the `extends` property.
|
||||
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
|
||||
The configuration from the base file are loaded first, then overridden by those in the inheriting config file.
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
...
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"preserveWhitespaces": true,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
More information about tsconfig extends can be found in the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
|
||||
{@a compiler-options}
|
||||
## Angular template compiler options
|
||||
|
||||
@ -1366,7 +1344,7 @@ for example, the content of annotations (such as a component's template), which
|
||||
emits to the `.js` file but not to the `.d.ts` file.
|
||||
|
||||
This option should be set to `true` if you are using TypeScript's `--outFile` option, because the metadata files
|
||||
are not valid for this style of TypeScript output. It is not recommeded to use `--outFile` with
|
||||
are not valid for this style of TypeScript output. It is not recommended to use `--outFile` with
|
||||
Angular. Use a bundler, such as [webpack](https://webpack.js.org/), instead.
|
||||
|
||||
This option can also be set to `true` when using factory summaries because the factory summaries
|
||||
@ -1461,7 +1439,7 @@ JavaScript with [JSDoc](http://usejsdoc.org/) comments needed by the
|
||||
### *annotationsAs*
|
||||
|
||||
Use this option to modify how the Angular specific annotations are emitted to improve tree-shaking. Non-Angular
|
||||
annotations and decorators are unnaffected. Default is `static fields`.
|
||||
annotations and decorators are unaffected. Default is `static fields`.
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
|
@ -17,7 +17,7 @@ Here are some key features.
|
||||
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
|
||||
|
||||
* [Service Workers](guide/service-worker-intro): Use a service worker to reduce dependency on the network
|
||||
significantly improving the use experience.
|
||||
significantly improving the user experience.
|
||||
|
||||
## Domain-specific libraries
|
||||
|
||||
|
@ -15,7 +15,7 @@ There are three kinds of directives in Angular:
|
||||
1. Attribute directives—change the appearance or behavior of an element, component, or another directive.
|
||||
|
||||
*Components* are the most common of the three directives.
|
||||
You saw a component for the first time in the [QuickStart](guide/quickstart) guide.
|
||||
You saw a component for the first time in the [Getting Started](guide/quickstart).
|
||||
|
||||
*Structural Directives* change the structure of the view.
|
||||
Two examples are [NgFor](guide/template-syntax#ngFor) and [NgIf](guide/template-syntax#ngIf).
|
||||
|
@ -1,536 +0,0 @@
|
||||
# Building and serving Angular apps
|
||||
|
||||
*intro - here are some topics of interest in the app development cycle*
|
||||
|
||||
{@a app-environments}
|
||||
|
||||
## Configuring application environments
|
||||
|
||||
You can define different named build configurations for your project, such as *stage* and *production*, with different defaults.
|
||||
|
||||
Each named build configuration can have defaults for any of the options that apply to the various build targets, such as `build`, `serve`, and `test`. The CLI `build`, `serve`, and `test` commands can then replace files with appropriate versions for your intended target environment.
|
||||
|
||||
The following figure shows how a project has multiple build targets, which can be executed using the named configurations that you define.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/build/build-config-targets.gif" alt="build configurations and targets">
|
||||
</figure>
|
||||
|
||||
### Configure environment-specific defaults
|
||||
|
||||
A project's `src/environments/` folder contains the base configuration file, `environment.ts`, which provides a default environment.
|
||||
You can add override defaults for additional environments, such as production and staging, in target-specific configuration files.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
└──myProject/src/environments/
|
||||
└──environment.ts
|
||||
└──environment.prod.ts
|
||||
└──environment.stage.ts
|
||||
```
|
||||
|
||||
The base file `environment.ts`, contains the default environment settings. For example:
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
</code-example>
|
||||
|
||||
The `build` command uses this as the build target when no environment is specified.
|
||||
You can add further variables, either as additional properties on the environment object, or as separate objects.
|
||||
For example, the following adds a default for a variable to the default environment:
|
||||
|
||||
```
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: 'http://my-api-url'
|
||||
};
|
||||
```
|
||||
|
||||
You can add target-specific configuration files, such as `environment.prod.ts`.
|
||||
The following sets content sets default values for the production build target:
|
||||
|
||||
```
|
||||
export const environment = {
|
||||
production: true
|
||||
apiUrl: 'http://my-prod-url'
|
||||
};
|
||||
```
|
||||
|
||||
### Using environment-specific variables in your app
|
||||
|
||||
The following application structure configures build targets for production and staging environments:
|
||||
|
||||
```
|
||||
└── src
|
||||
└── app
|
||||
├── app.component.html
|
||||
└── app.component.ts
|
||||
└── environments
|
||||
├── environment.prod.ts
|
||||
├── environment.staging.ts
|
||||
└── environment.ts
|
||||
```
|
||||
|
||||
To use the environment configurations you have defined, your components must import the original environments file:
|
||||
|
||||
```
|
||||
import { environment } from './../environments/environment';
|
||||
```
|
||||
|
||||
This ensures that the build and serve commands can find the configurations for specific build targets.
|
||||
|
||||
The following code in the component file (`app.component.ts`) uses an environment variable defined in the configuration files.
|
||||
|
||||
```
|
||||
import { Component } from '@angular/core';
|
||||
import { environment } from './../environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
console.log(environment.production); // Logs false for default environment
|
||||
}
|
||||
title = 'app works!';
|
||||
}
|
||||
```
|
||||
{@a file-replacement}
|
||||
|
||||
## Configure target-specific file replacements
|
||||
|
||||
The main CLI configuration file, `angular.json`, contains a `fileReplacements` section in the configuration for each build target, which allows you to replace any file with a target-specific version of that file.
|
||||
This is useful for including target-specific code or variables in a build that targets a specific environment, such as production or staging.
|
||||
|
||||
By default no files are replaced.
|
||||
You can add file replacements for specific build targets.
|
||||
For example:
|
||||
|
||||
```
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
...
|
||||
```
|
||||
|
||||
This means that when you build your production configuration (using `ng build --prod` or `ng build --configuration=production`), the `src/environments/environment.ts` file is replaced with the target-specific version of the file, `src/environments/environment.prod.ts`.
|
||||
|
||||
You can add additional configurations as required. To add a staging environment, create a copy of `src/environments/environment.ts` called `src/environments/environment.staging.ts`, then add a `staging` configuration to `angular.json`:
|
||||
|
||||
```
|
||||
"configurations": {
|
||||
"production": { ... },
|
||||
"staging": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.staging.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can add more configuration options to this target environment as well.
|
||||
Any option that your build supports can be overridden in a build target configuration.
|
||||
|
||||
To build using the staging configuration, run `ng build --configuration=staging`.
|
||||
|
||||
You can also configure the `serve` command to use the targeted build configuration if you add it to the "serve:configurations" section of `angular.json`:
|
||||
|
||||
```
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "your-project-name:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "your-project-name:build:production"
|
||||
},
|
||||
"staging": {
|
||||
"browserTarget": "your-project-name:build:staging"
|
||||
}
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
{@a size-budgets}
|
||||
|
||||
## Configure size budgets
|
||||
|
||||
As applications grow in functionality, they also grow in size.
|
||||
The CLI allows you to set size thresholds in your configuration to ensure that parts of your application stay within size boundaries that you define.
|
||||
|
||||
Define your size boundaries in the CLI configuration file, `angular.json`, in a `budgets` section for each [configured environment](#app-environments).
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"configurations": {
|
||||
"production": {
|
||||
...
|
||||
budgets: []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can specify size budgets for the entire app, and for particular parts.
|
||||
Each budget entry configures a budget of a given type.
|
||||
Specify size values in the following formats:
|
||||
|
||||
* 123 or 123b: Size in bytes
|
||||
|
||||
* 123kb: Size in kilobytes
|
||||
|
||||
* 123mb: Size in megabytes
|
||||
|
||||
* 12%: Percentage of size relative to baseline. (Not valid for baseline values.)
|
||||
|
||||
When you configure a budget, the build system warns or reports and error when a given part of the app reaches or exceeds a boundary size that you set.
|
||||
|
||||
Each budget entry is a JSON object with the following properties:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>The type of budget. One of:
|
||||
|
||||
* bundle - The size of a specific bundle.
|
||||
* initial - The initial size of the app.
|
||||
* allScript - The size of all scripts.
|
||||
* all - The size of the entire app.
|
||||
* anyScript - The size of any one script.
|
||||
* any - The size of any file.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>name</td>
|
||||
<td>
|
||||
|
||||
The name of the bundle (for `type=bundle`).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>baseline</td>
|
||||
<td>An absolute baseline size for percentage values. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>maximumWarning</td>
|
||||
<td>Warns when a size exceeds this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>maximumError</td>
|
||||
<td>Reports an error when the size exceeds this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>minimumWarning</td>
|
||||
<td>Warns when the size reaches this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>minimumError</td>
|
||||
<td>Reports an error when the size reaches this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>warning</td>
|
||||
<td>Warns when the size ??reaches or exceeds?? this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>error</td>
|
||||
<td>Reports an error when the size ??reaches or exceeds?? this threshold percentage of the baseline.</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
{@a assets}
|
||||
|
||||
## Adding project assets
|
||||
|
||||
You can configure your project with a set of assets, such as images, to copy directly into the build for a particular build target.
|
||||
|
||||
Each build target section of the CLI configuration file, `angular.json`, has an `assets` section that lists files or folders you want to copy into the build for that target.
|
||||
By default, the `src/assets/` folder and `src/favicon.ico` are copied into a build.
|
||||
|
||||
```
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/favicon.ico"
|
||||
]
|
||||
```
|
||||
|
||||
You can edit the assets configuration to extend it for assets outside your project.
|
||||
For example, the following invokes the [node-glob pattern matcher](https://github.com/isaacs/node-glob) using input from a given base folder.
|
||||
It sends output to a folder that is relative to `outDir`, a configuration value that defaults to `dist/`*project-name*).
|
||||
The result in this cased is the same as for the default assets configuration.
|
||||
|
||||
```
|
||||
"assets": [
|
||||
{ "glob": "**/*", "input": "src/assets/", "output": "/assets/" },
|
||||
{ "glob": "favicon.ico", "input": "/src", "output": "/" },
|
||||
]
|
||||
```
|
||||
|
||||
You can use this extended configuration to copy assets from outside your project.
|
||||
For instance, you can copy assets from a node package with the following value:
|
||||
|
||||
```
|
||||
"assets": [
|
||||
{ "glob": "**/*", "input": "./node_modules/some-package/images", "output": "/some-package/" },
|
||||
]
|
||||
```
|
||||
|
||||
This makes the contents of `node_modules/some-package/images/` available in the output folder `dist/some-package/`.
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
For reasons of security, the CLI never writes files outside of the project output path.
|
||||
|
||||
</div>
|
||||
|
||||
{@a browser-compat}
|
||||
|
||||
## Configuring browser compatibility
|
||||
|
||||
The CLI uses [Autoprefixer](https://github.com/postcss/autoprefixer) to ensure compatibility with different browser and browser versions.
|
||||
You may find it necessary to target specific browsers or exclude certain browser versions from your build.
|
||||
|
||||
Internally, Autoprefixer relies on a library called [Browserslist(https://github.com/ai/browserslist)] to figure out which browsers to support with prefixing.
|
||||
Browserlist looks for configuration options in a `browserlist` property of the package configuration file, or in a configuration file named `.browserslistrc`.
|
||||
Autoprefixer looks for the Browserlist configuration when it prefixes your CSS.
|
||||
|
||||
* You can tell Autoprefixer what browsers to target by adding a browserslist property to the package configuration file, `package.json`:
|
||||
```
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
```
|
||||
|
||||
* Alternatively, you can add a new file, `.browserslistrc`, to the project directory, that specifies browsers you want to support:
|
||||
```
|
||||
### Supported Browsers
|
||||
> 1%
|
||||
last 2 versions
|
||||
```
|
||||
|
||||
See the [browserslist repo](https://github.com/ai/browserslist) for more examples of how to target specific browsers and versions.
|
||||
|
||||
<div class="alert is-helpful">>
|
||||
Backward compatibility
|
||||
|
||||
If you want to produce a progressive web app and are using [Lighthouse](https://developers.google.com/web/tools/lighthouse/) to grade the project, add the following browserslist entry to your `package.json` file, in order to eliminate the [old flexbox](https://developers.google.com/web/tools/lighthouse/audits/old-flexbox) prefixes:
|
||||
|
||||
```
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"not ie <= 10",
|
||||
"not ie_mob <= 10"
|
||||
]
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
{@a proxy}
|
||||
|
||||
## Proxying to a backend server
|
||||
|
||||
You can use the [proxying support](https://webpack.js.org/configuration/dev-server/#devserver-proxy) in the `webpack` dev server to divert certain URLs to a backend server, by passing a file to the `--proxy-config` build option.
|
||||
For example, to divert all calls for http://localhost:4200/api to a server running on http://localhost:3000/api, take the following steps.
|
||||
|
||||
1. Create a file `proxy.conf.json` in the projects `src/` folder, next to `package.json`.
|
||||
|
||||
1. Add the following content to the new proxy file:
|
||||
```
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. In the CLI configuration file, `angular.json`, add the `proxyConfig` option to the `serve` target:
|
||||
```
|
||||
...
|
||||
"architect": {
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "your-application-name:build",
|
||||
"proxyConfig": "src/proxy.conf.json"
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
1. To run the dev server with this proxy configuration, call `ng serve`.
|
||||
|
||||
You can edit the proxy configuration file to add configuration options; some examples are given below.
|
||||
For a description of all options, see [webpack DevServer documentation](https://webpack.js.org/configuration/dev-server/#devserver-proxy).
|
||||
|
||||
Note that if you edit the proxy configuration file, you must relaunch the `ng serve` process to make your changes effective.
|
||||
|
||||
### Rewrite the URL path
|
||||
|
||||
The `pathRewrite` proxy configuration option lets you rewrite the URL path at run time.
|
||||
For example, you can specify the following `pathRewrite` value to the proxy configuration to remove "api" from the end of a path.
|
||||
|
||||
```
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you need to access a backend that is not on `localhost`, set the `changeOrigin` option as well. For example:
|
||||
|
||||
```
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://npmjs.org",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api": ""
|
||||
},
|
||||
"changeOrigin": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To help determine whether your proxy is working as intended, set the `logLevel` option. For example:
|
||||
|
||||
```
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api": ""
|
||||
},
|
||||
"logLevel": "debug"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Proxy log levels are `info` (the default), `debug`, `warn`, `error`, and `silent`.
|
||||
|
||||
### Proxy multiple entries
|
||||
|
||||
You can proxy multiple entries to the same target by defining the configuration in JavaScript.
|
||||
|
||||
Set the proxy configuration file to `proxy.conf.js` (instead of `proxy.conf.json`), and specify configuration files as in the following example.
|
||||
|
||||
```
|
||||
const PROXY_CONFIG = [
|
||||
{
|
||||
context: [
|
||||
"/my",
|
||||
"/many",
|
||||
"/endpoints",
|
||||
"/i",
|
||||
"/need",
|
||||
"/to",
|
||||
"/proxy"
|
||||
],
|
||||
target: "http://localhost:3000",
|
||||
secure: false
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = PROXY_CONFIG;
|
||||
```
|
||||
|
||||
In the CLI configuration file, `angular.json`, point to the JavaScript proxy configuration file:
|
||||
|
||||
```
|
||||
...
|
||||
"architect": {
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "your-application-name:build",
|
||||
"proxyConfig": "src/proxy.conf.js"
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
### Bypass the proxy
|
||||
|
||||
If you need to optionally bypass the proxy, or dynamically change the request before it's sent, add the bypass option, as shown in this JavaScript example.
|
||||
|
||||
```
|
||||
const PROXY_CONFIG = {
|
||||
"/api/proxy": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false,
|
||||
"bypass": function (req, res, proxyOptions) {
|
||||
if (req.headers.accept.indexOf("html") !== -1) {
|
||||
console.log("Skipping proxy for browser request.");
|
||||
return "/index.html";
|
||||
}
|
||||
req.headers["X-Custom-Header"] = "yes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PROXY_CONFIG;
|
||||
```
|
||||
|
||||
### Using corporate proxy
|
||||
|
||||
If you work behind a corporate proxy, the cannot directly proxy calls to any URL outside your local network.
|
||||
In this case, you can configure the backend proxy to redirect calls through your corporate proxy using an agent:
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
npm install --save-dev https-proxy-agent
|
||||
</code-example>
|
||||
|
||||
When you define an environment variable `http_proxy` or `HTTP_PROXY`, an agent is automatically added to pass calls through your corporate proxy when running `npm start`.
|
||||
|
||||
Use the following content in the JavaScript configuration file.
|
||||
|
||||
```
|
||||
var HttpsProxyAgent = require('https-proxy-agent');
|
||||
var proxyConfig = [{
|
||||
context: '/api',
|
||||
target: 'http://your-remote-server.com:3000',
|
||||
secure: false
|
||||
}];
|
||||
|
||||
function setupForCorporateProxy(proxyConfig) {
|
||||
var proxyServer = process.env.http_proxy || process.env.HTTP_PROXY;
|
||||
if (proxyServer) {
|
||||
var agent = new HttpsProxyAgent(proxyServer);
|
||||
console.log('Using corporate proxy server: ' + proxyServer);
|
||||
proxyConfig.forEach(function(entry) {
|
||||
entry.agent = agent;
|
||||
});
|
||||
}
|
||||
return proxyConfig;
|
||||
}
|
||||
|
||||
module.exports = setupForCorporateProxy(proxyConfig);
|
||||
```
|
||||
|
@ -19,7 +19,7 @@ Observables are often compared to promises. Here are some key differences:
|
||||
|
||||
### Creation and subscription
|
||||
|
||||
* Observables are not executed until a consumer subcribes. The `subscribe()` executes the defined behavior once, and it can be called again. Each subscription has its own computation. Resubscription causes recomputation of values.
|
||||
* Observables are not executed until a consumer subscribes. The `subscribe()` executes the defined behavior once, and it can be called again. Each subscription has its own computation. Resubscription causes recomputation of values.
|
||||
|
||||
<code-example hideCopy>
|
||||
// declare a publishing operation
|
||||
|
@ -149,7 +149,7 @@ By default, the DI framework searches for a provider in the injector hierarchy,
|
||||
starting at the component's local injector of the component, and if necessary bubbling up
|
||||
through the injector tree until it reaches the root injector.
|
||||
|
||||
* The first injector configured with a provider supplies the the dependency (a service instance or value) to the constructor.
|
||||
* The first injector configured with a provider supplies the dependency (a service instance or value) to the constructor.
|
||||
|
||||
* If no provider is found in the root injector, the DI framework returns null to the constructor.
|
||||
|
||||
@ -260,7 +260,7 @@ Using a custom provider allows you to provide a concrete implementation for impl
|
||||
|
||||
</code-example>
|
||||
|
||||
The `factory` function returns the `localStorage` property that is attached to the browser window object. The `Inject` decorator is a constructor parameter used to specify a custom provider of a dependency. This custom provider can now be overriden during testing with a mock API of `localStorage` instead of interactive with real browser APIs.
|
||||
The `factory` function returns the `localStorage` property that is attached to the browser window object. The `Inject` decorator is a constructor parameter used to specify a custom provider of a dependency. This custom provider can now be overridden during testing with a mock API of `localStorage` instead of interactive with real browser APIs.
|
||||
|
||||
### Modify the provider search with `@Self` and `@SkipSelf`
|
||||
|
||||
|
@ -112,7 +112,7 @@ The `CraigComponent` tries to inject `Base` into its `alex` constructor paramete
|
||||
|
||||
|
||||
|
||||
Unfortunately, this does'nt work.
|
||||
Unfortunately, this doesn't work.
|
||||
The <live-example name="dependency-injection-in-action"></live-example>
|
||||
confirms that the `alex` parameter is null.
|
||||
*You cannot inject a parent by its base class.*
|
||||
|
@ -240,7 +240,7 @@ The `@Injectable()` decorator is the standard decorator for service classes.
|
||||
|
||||
<div class="alert-is-helpful">
|
||||
|
||||
The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles]((guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`.
|
||||
The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles](guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`.
|
||||
|
||||
This means you're responsible for putting `@Injectable()` on your service classes.
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Deployment
|
||||
|
||||
When you are ready to deploy your Angular application to a remote server, you have various options for
|
||||
deployment. You may need to configure
|
||||
This page describes techniques for deploying your Angular application to a remote server.
|
||||
|
||||
{@a dev-deploy}
|
||||
{@a copy-files}
|
||||
@ -19,214 +18,27 @@ For the simplest deployment, build for development and copy the output directory
|
||||
|
||||
2. Copy _everything_ within the output folder (`dist/` by default) to a folder on the server.
|
||||
|
||||
3. Configure the server to redirect requests for missing files to `index.html`.
|
||||
|
||||
3. If you copy the files into a server _sub-folder_, append the build flag, `--base-href` and set the `<base href>` appropriately.<br><br>
|
||||
|
||||
For example, if the `index.html` is on the server at `/my/app/index.html`, set the _base href_ to
|
||||
`<base href="/my/app/">` like this.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
ng build --base-href=/my/app/
|
||||
</code-example>
|
||||
|
||||
You'll see that the `<base href>` is set properly in the generated `dist/index.html`.<br><br>
|
||||
If you copy to the server's root directory, omit this step and leave the `<base href>` alone.<br><br>
|
||||
Learn more about the role of `<base href>` [below](guide/deployment#base-tag).
|
||||
|
||||
|
||||
4. Configure the server to redirect requests for missing files to `index.html`.
|
||||
Learn more about server-side redirects [below](guide/deployment#fallback).
|
||||
|
||||
|
||||
This is _not_ a production deployment. It's not optimized and it won't be fast for users.
|
||||
It might be good enough for sharing your progress and ideas internally with managers, teammates, and other stakeholders. For the next steps in deployment, see [Optimize for production](#optimize).
|
||||
|
||||
{@a deploy-to-github}
|
||||
|
||||
## Deploy to GitHub pages
|
||||
|
||||
Another simple way to deploy your Angular app is to use [GitHub Pages](https://help.github.com/articles/what-is-github-pages/).
|
||||
|
||||
1. You will need to [create a GitHub account](https://github.com/join) if you don't have one, then [create a repository](https://help.github.com/articles/create-a-repo/) for your project.
|
||||
Make a note of the user name and project name in GitHub.
|
||||
|
||||
1. Build your project using Github project name, with the following CLI command:
|
||||
<code-example language="none" class="code-shell">
|
||||
ng build --prod --output-path docs --base-href <project_name>
|
||||
</code-example>
|
||||
|
||||
1. When the build is complete, make a copy of `docs/index.html` and name it `docs/404.html`.
|
||||
|
||||
1. Commit your changes and push.
|
||||
|
||||
1. On the GitHub project page, configure it to [publish from the docs folder](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch).
|
||||
|
||||
You can see your deployed page at `https://<user_name>.github.io/<project_name>/`.
|
||||
|
||||
<div class="alert-is-helpful>
|
||||
|
||||
Check out [angular-cli-ghpages](https://github.com/angular-buch/angular-cli-ghpages), a full featured package that does all this for you and has extra functionality.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
{@a server-configuration}
|
||||
|
||||
## Server configuration
|
||||
|
||||
This section covers changes you may have make to the server or to files deployed to the server.
|
||||
|
||||
{@a fallback}
|
||||
|
||||
### Routed apps must fallback to `index.html`
|
||||
|
||||
Angular apps are perfect candidates for serving with a simple static HTML server.
|
||||
You don't need a server-side engine to dynamically compose application pages because
|
||||
Angular does that on the client-side.
|
||||
|
||||
If the app uses the Angular router, you must configure the server
|
||||
to return the application's host page (`index.html`) when asked for a file that it does not have.
|
||||
|
||||
{@a deep-link}
|
||||
|
||||
A routed application should support "deep links".
|
||||
A _deep link_ is a URL that specifies a path to a component inside the app.
|
||||
For example, `http://www.mysite.com/heroes/42` is a _deep link_ to the hero detail page
|
||||
that displays the hero with `id: 42`.
|
||||
|
||||
There is no issue when the user navigates to that URL from within a running client.
|
||||
The Angular router interprets the URL and routes to that page and hero.
|
||||
|
||||
But clicking a link in an email, entering it in the browser address bar,
|
||||
or merely refreshing the browser while on the hero detail page —
|
||||
all of these actions are handled by the browser itself, _outside_ the running application.
|
||||
The browser makes a direct request to the server for that URL, bypassing the router.
|
||||
|
||||
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
|
||||
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
|
||||
configured to return `index.html` instead.
|
||||
|
||||
#### Fallback configuration examples
|
||||
|
||||
There is no single configuration that works for every server.
|
||||
The following sections describe configurations for some of the most popular servers.
|
||||
The list is by no means exhaustive, but should provide you with a good starting point.
|
||||
|
||||
#### Development servers
|
||||
|
||||
During development, the `ng serve` command lets you run your app in a local browser.
|
||||
The CLI recompiles the application each time you save a file,
|
||||
and reloads the browser with the newly compiled application.
|
||||
|
||||
The app is hosted in local memory and served on `http://localhost:4200/`, using [webpack-dev-server](https://webpack.js.org/guides/development/#webpack-dev-server).
|
||||
|
||||
{@a serve-from-disk}
|
||||
|
||||
Later in development, you might want a closer approximation of how your app will behave when deployed.
|
||||
You can output your distribution folder (`dist`) to disk, but you need to install a different web server.
|
||||
Try installing [lite-server](https://github.com/johnpapa/lite-server); like `webpack-dev-server`, it can automatically reload your browser when you write new files.
|
||||
|
||||
To get the live-reload experience, you will need to run two terminals.
|
||||
The first runs the build in a watch mode and compiles the application to the `dist` folder.
|
||||
The second runs the web server against the `dist` folder.
|
||||
The combination of these two processes provides the same behavior as `ng serve`.
|
||||
|
||||
1. Start the build in terminal A:
|
||||
<code-example language="none" class="code-shell">
|
||||
ng build --watch
|
||||
</code-example>
|
||||
|
||||
1. Start the web server in terminal B:
|
||||
<code-example language="none" class="code-shell">
|
||||
lite-server --baseDir="dist"
|
||||
</code-example>
|
||||
The default browser opens to the appropriate URL.
|
||||
|
||||
* [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
|
||||
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
|
||||
|
||||
* [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
|
||||
`historyApiFallback` entry in the dev server options as follows:
|
||||
|
||||
<code-example>
|
||||
historyApiFallback: {
|
||||
disableDotRule: true,
|
||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
||||
}
|
||||
</code-example>
|
||||
|
||||
#### Production servers
|
||||
|
||||
* [Apache](https://httpd.apache.org/): add a
|
||||
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown
|
||||
(https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
|
||||
|
||||
<code-example format=".">
|
||||
RewriteEngine On
|
||||
# If an existing asset or directory is requested go to it as it is
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# If the requested resource doesn't exist, use index.html
|
||||
RewriteRule ^ /index.html
|
||||
</code-example>
|
||||
|
||||
|
||||
* [Nginx](http://nginx.org/): use `try_files`, as described in
|
||||
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
||||
modified to serve `index.html`:
|
||||
|
||||
<code-example format=".">
|
||||
try_files $uri $uri/ /index.html;
|
||||
</code-example>
|
||||
|
||||
|
||||
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
[here](http://stackoverflow.com/a/26152011/2116927):
|
||||
|
||||
<code-example format='.'>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Angular Routes" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/index.html" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
* [GitHub Pages](https://pages.github.com/): you can't
|
||||
[directly configure](https://github.com/isaacs/github/issues/408)
|
||||
the GitHub Pages server, but you can add a 404 page.
|
||||
Copy `index.html` into `404.html`.
|
||||
It will still be served as the 404 response, but the browser will process that page and load the app properly.
|
||||
It's also a good idea to
|
||||
[serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
|
||||
and to
|
||||
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
|
||||
|
||||
|
||||
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
||||
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
|
||||
|
||||
<code-example format=".">
|
||||
"rewrites": [ {
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
} ]
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a cors}
|
||||
|
||||
### Requesting services from a different server (CORS)
|
||||
|
||||
Angular developers may encounter a
|
||||
<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" title="Cross-origin resource sharing">
|
||||
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request)
|
||||
to a server other than the application's own host server.
|
||||
Browsers forbid such requests unless the server permits them explicitly.
|
||||
|
||||
There isn't anything the client application can do about these errors.
|
||||
The server must be configured to accept the application's requests.
|
||||
Read about how to enable CORS for specific servers at
|
||||
<a href="http://enable-cors.org/server.html" title="Enabling CORS server">enable-cors.org</a>.
|
||||
|
||||
<hr>
|
||||
It might be good enough for sharing your progress and ideas internally with managers, teammates, and other stakeholders.
|
||||
|
||||
{@a optimize}
|
||||
|
||||
@ -253,8 +65,14 @@ The `--prod` _meta-flag_ engages the following optimization features.
|
||||
|
||||
The remaining [copy deployment steps](#copy-files) are the same as before.
|
||||
|
||||
See [Building and serving Angular apps](guide/build)
|
||||
for more about about CLI build options and what they do.
|
||||
You may further reduce bundle sizes by adding the `build-optimizer` flag.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
ng build --prod --build-optimizer
|
||||
</code-example>
|
||||
|
||||
See the [CLI Documentation](https://github.com/angular/angular-cli/wiki/build)
|
||||
for details about available build options and what they do.
|
||||
|
||||
{@a enable-prod-mode}
|
||||
|
||||
@ -284,24 +102,22 @@ Configure the Angular Router to defer loading of all other modules (and their as
|
||||
or by [_lazy loading_](guide/router#asynchronous-routing "Lazy loading")
|
||||
them on demand.
|
||||
|
||||
<div class="alert-is-helpful>
|
||||
#### Don't eagerly import something from a lazy loaded module
|
||||
|
||||
#### Don't eagerly import something from a lazy-loaded module
|
||||
|
||||
If you mean to lazy-load a module, be careful not import it
|
||||
in a file that's eagerly loaded when the app starts (such as the root `AppModule`).
|
||||
It's a common mistake.
|
||||
You've arranged to lazy load a module.
|
||||
But you unintentionally import it, with a JavaScript `import` statement,
|
||||
in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`.
|
||||
If you do that, the module will be loaded immediately.
|
||||
|
||||
The bundling configuration must take lazy loading into consideration.
|
||||
Because lazy-loaded modules aren't imported in JavaScript, bundlers exclude them by default.
|
||||
Bundlers don't know about the router configuration and can't create separate bundles for lazy-loaded modules.
|
||||
You would have to create these bundles manually.
|
||||
Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default.
|
||||
Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules.
|
||||
You have to create these bundles manually.
|
||||
|
||||
The CLI runs the
|
||||
[Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)
|
||||
which automatically recognizes lazy-loaded `NgModules` and creates separate bundles for them.
|
||||
|
||||
</div>
|
||||
which automatically recognizes lazy loaded `NgModules` and creates separate bundles for them.
|
||||
|
||||
{@a measure}
|
||||
|
||||
@ -387,12 +203,17 @@ the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the se
|
||||
When the `base` tag is mis-configured, the app fails to load and the browser console displays `404 - Not Found` errors
|
||||
for the missing files. Look at where it _tried_ to find those files and adjust the base tag appropriately.
|
||||
|
||||
## Building and serving for deployment
|
||||
## _build_ vs. _serve_
|
||||
|
||||
When you are designing and developing applications, you typically use `ng serve` to build your app for fast, local, iterative development.
|
||||
When you are ready to deploy, however, you must use the `ng build` command to build the app and deploy the build artifacts elsewhere.
|
||||
You'll probably prefer `ng build` for deployments.
|
||||
|
||||
Both `ng build` and `ng serve` clear the output folder before they build the project, but only the `build` command writes the generated build artifacts to the output folder.
|
||||
The **ng build** command is intended for building the app and deploying the build artifacts elsewhere.
|
||||
The **ng serve** command is intended for fast, local, iterative development.
|
||||
|
||||
Both `ng build` and `ng serve` **clear the output folder** before they build the project.
|
||||
The `ng build` command writes generated build artifacts to the output folder.
|
||||
The `ng serve` command does not.
|
||||
It serves build artifacts from memory instead for a faster development experience.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -401,13 +222,160 @@ To output to a different folder, change the `outputPath` in `angular.json`.
|
||||
|
||||
</div>
|
||||
|
||||
The`serve` command builds, watches, and serves the application from local memory, using a local development server.
|
||||
When you have deployed your app to another server, however, you might still want to serve the app so that you can continue to see changes that you make in it.
|
||||
You can do this by adding the `--watch` option to the `build` command.
|
||||
The `ng serve` command builds, watches, and serves the application from a local CLI development server.
|
||||
|
||||
```
|
||||
ng build --watch
|
||||
```
|
||||
Like the `serve` command, this regenerates output files when source files change.
|
||||
The `ng build` command generates output files just once and does not serve them.
|
||||
The `ng build --watch` command will regenerate output files when source files change.
|
||||
This `--watch` flag is useful if you're building during development and
|
||||
are automatically re-deploying changes to another server.
|
||||
|
||||
For complete details of the CLI commands and configuration options, see the *CLI Reference (link TBD)*.
|
||||
|
||||
See the [CLI `build` topic](https://github.com/angular/angular-cli/wiki/build) for more details and options.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a server-configuration}
|
||||
|
||||
## Server configuration
|
||||
|
||||
This section covers changes you may have make to the server or to files deployed to the server.
|
||||
|
||||
{@a fallback}
|
||||
|
||||
### Routed apps must fallback to `index.html`
|
||||
|
||||
Angular apps are perfect candidates for serving with a simple static HTML server.
|
||||
You don't need a server-side engine to dynamically compose application pages because
|
||||
Angular does that on the client-side.
|
||||
|
||||
If the app uses the Angular router, you must configure the server
|
||||
to return the application's host page (`index.html`) when asked for a file that it does not have.
|
||||
|
||||
{@a deep-link}
|
||||
|
||||
|
||||
A routed application should support "deep links".
|
||||
A _deep link_ is a URL that specifies a path to a component inside the app.
|
||||
For example, `http://www.mysite.com/heroes/42` is a _deep link_ to the hero detail page
|
||||
that displays the hero with `id: 42`.
|
||||
|
||||
There is no issue when the user navigates to that URL from within a running client.
|
||||
The Angular router interprets the URL and routes to that page and hero.
|
||||
|
||||
But clicking a link in an email, entering it in the browser address bar,
|
||||
or merely refreshing the browser while on the hero detail page —
|
||||
all of these actions are handled by the browser itself, _outside_ the running application.
|
||||
The browser makes a direct request to the server for that URL, bypassing the router.
|
||||
|
||||
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
|
||||
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
|
||||
configured to return `index.html` instead.
|
||||
|
||||
#### Fallback configuration examples
|
||||
|
||||
There is no single configuration that works for every server.
|
||||
The following sections describe configurations for some of the most popular servers.
|
||||
The list is by no means exhaustive, but should provide you with a good starting point.
|
||||
|
||||
#### Development servers
|
||||
|
||||
* [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
|
||||
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
|
||||
|
||||
|
||||
* [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
|
||||
`historyApiFallback` entry in the dev server options as follows:
|
||||
|
||||
<code-example>
|
||||
historyApiFallback: {
|
||||
disableDotRule: true,
|
||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
||||
}
|
||||
</code-example>
|
||||
|
||||
|
||||
#### Production servers
|
||||
|
||||
* [Apache](https://httpd.apache.org/): add a
|
||||
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown
|
||||
(https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
|
||||
|
||||
<code-example format=".">
|
||||
RewriteEngine On
|
||||
# If an existing asset or directory is requested go to it as it is
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# If the requested resource doesn't exist, use index.html
|
||||
RewriteRule ^ /index.html
|
||||
</code-example>
|
||||
|
||||
|
||||
* [NGinx](http://nginx.org/): use `try_files`, as described in
|
||||
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
||||
modified to serve `index.html`:
|
||||
|
||||
<code-example format=".">
|
||||
try_files $uri $uri/ /index.html;
|
||||
</code-example>
|
||||
|
||||
|
||||
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
[here](http://stackoverflow.com/a/26152011/2116927):
|
||||
|
||||
<code-example format='.'>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Angular Routes" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/index.html" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
* [GitHub Pages](https://pages.github.com/): you can't
|
||||
[directly configure](https://github.com/isaacs/github/issues/408)
|
||||
the GitHub Pages server, but you can add a 404 page.
|
||||
Copy `index.html` into `404.html`.
|
||||
It will still be served as the 404 response, but the browser will process that page and load the app properly.
|
||||
It's also a good idea to
|
||||
[serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
|
||||
and to
|
||||
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
|
||||
|
||||
|
||||
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
||||
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
|
||||
|
||||
<code-example format=".">
|
||||
"rewrites": [ {
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
} ]
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a cors}
|
||||
|
||||
### Requesting services from a different server (CORS)
|
||||
|
||||
Angular developers may encounter a
|
||||
<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" title="Cross-origin resource sharing">
|
||||
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request)
|
||||
to a server other than the application's own host server.
|
||||
Browsers forbid such requests unless the server permits them explicitly.
|
||||
|
||||
There isn't anything the client application can do about these errors.
|
||||
The server must be configured to accept the application's requests.
|
||||
Read about how to enable CORS for specific servers at
|
||||
<a href="http://enable-cors.org/server.html" title="Enabling CORS server">enable-cors.org</a>.
|
||||
|
@ -31,7 +31,7 @@ The easiest way to display a component property
|
||||
is to bind the property name through interpolation.
|
||||
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
|
||||
|
||||
Follow the [quickstart](guide/quickstart) instructions for creating a new project
|
||||
Follow the [Getting Started](guide/quickstart) instructions for creating a new project
|
||||
named <code>displaying-data</code>.
|
||||
|
||||
Delete the <code>app.component.html</code> file. It is not needed for this example.
|
||||
|
@ -1,382 +0,0 @@
|
||||
# Workspace and project file structure
|
||||
|
||||
An Angular CLI project is the foundation for both quick experiments and enterprise solutions. The CLI command `ng new` creates an Angular workspace in your file system that is the root for new projects, which can be apps and libraries.
|
||||
|
||||
## Workspaces and project files
|
||||
|
||||
Angular 6 introduced the [workspace](guide/glossary#workspace) directory structure for Angular [projects](guide/glossary#project). A project can be a standalone *application* or a *library*, and a workspace can contain multiple applications, as well as libraries that can be used in any of the apps.
|
||||
|
||||
The CLI command `ng new my-workspace` creates a workspace folder and generates a new app skeleton in an `app` folder within that workspace.
|
||||
Within the `app/` folder, a `src/` subfolder contains the logic, data, and assets.
|
||||
A newly generated `app/` folder contains the source files for a root module, with a root component and template.
|
||||
|
||||
The `app/` folder also contains project-specific configuration files, end-to-end tests, and the Angular system modules.
|
||||
|
||||
```
|
||||
my-workspace/
|
||||
app/
|
||||
e2e/
|
||||
src/
|
||||
node_modules/
|
||||
src/
|
||||
```
|
||||
|
||||
When this workspace file structure is in place, you can use the `ng generate` command on the command line to add functionality and data to the initial app.
|
||||
|
||||
<div class="alert-is-helpful>
|
||||
|
||||
Besides using the CLI on the command line, You can also use an interactive development environment like [Angular Console](https://angular.console.com), or manipulate files directly in the app's source folder and configuration files.
|
||||
|
||||
</div>
|
||||
|
||||
{@a global-config}
|
||||
|
||||
## Global workspace configuration
|
||||
|
||||
A workspace can contain additional apps and libraries, each with its own root folder under `projects/`.
|
||||
|
||||
```
|
||||
my-workspace/
|
||||
app/
|
||||
projects/
|
||||
my-app/
|
||||
helpful-library/
|
||||
my-other-app/
|
||||
angular.json
|
||||
|
||||
```
|
||||
At the top level of the workspace, the CLI configuration file, `angular.json`, let you set defaults for all projects in the workspace. You can configure a workspace, for example, such that all projects in it have global access to libraries, scripts, and CSS styles. (For more in this, see [Configuring global access](#global-access).)
|
||||
|
||||
You can also use `ng generate app` to create new Angular apps in the workspace, and use the `ng add` command to add libraries.
|
||||
If you add libraries or generate more apps within a workspace, a `projects/` folder is created to contain the new libraries or apps.
|
||||
Additional apps and library subfolders have the same file structure as the initial app.
|
||||
|
||||
All of the projects within a workspace share a CLI configuration context, controlled by the `angular.json` configuration file at the root level of the workspace.
|
||||
|
||||
| GLOBAL CONFIG FILES | PURPOSE |
|
||||
| :------------- | :------------------------------------------|
|
||||
| `angular.json` | Sets defaults for the CLI and configuration options for build, serve, and test tools that the CLI uses, such as [Karma](https://karma-runner.github.io/) and [Protractor](http://www.protractortest.org/). For complete details, see *CLI Reference (link TBD)*. |
|
||||
|
||||
## App source folder
|
||||
|
||||
The app-root source folder contains your app's logic and data. Angular components, templates, styles, images, and anything else your app needs go here. Files outside of the source folder support testing and building your app.
|
||||
|
||||
```
|
||||
src/
|
||||
app/
|
||||
app.component.css
|
||||
app.component.html
|
||||
app.component.spec.ts
|
||||
app.component.ts
|
||||
app.module.ts
|
||||
assets/...
|
||||
favicon.ico
|
||||
index.html
|
||||
main.ts
|
||||
test.ts
|
||||
```
|
||||
|
||||
| APP SOURCE FILES | PURPOSE |
|
||||
| :----------------------------- | :------------------------------------------|
|
||||
| `app/app.component.ts` | Defines the logic for the app's root component, named AppComponent. The view associated with this root component becomes the root of the [view hierarchy](guide/glossary#view-hierarchy) as you add components and services to your app. |
|
||||
| `app/app.component.html` | Defines the HTML template associated with the root AppComponent. |
|
||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root AppComponent. |
|
||||
| `app/app.component.spec.ts` | Defines a unit test for the root AppComponent. |
|
||||
| `app/app.module.ts` | Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. As you add more components to the app, they must be declared here. |
|
||||
| `assets/*` | Contains image files and other asset files to be copied as-is when you build your application. |
|
||||
|
||||
| PROJECT-LEVEL FILES | PURPOSE |
|
||||
| :------------------------ | :------------------------------------------|
|
||||
| `favicon.ico` | An icon to use for this app in the bookmark bar. |
|
||||
| `index.html` | The main HTML page that is served when someone visits your site. The CLI automatically adds all JavaScript and CSS files when building your app, so you typically don't need to add any `<script>` or` <link>` tags here manually. |
|
||||
| `main.ts` | The main entry point for your app. Compiles the application with the [JIT compiler](https://angular.io/guide/glossary#jit) and bootstraps the application's root module (AppModule) to run in the browser. You can also use the [AOT compiler](https://angular.io/guide/aot-compiler) without changing any code by appending the `--aot` flag to the CLI `build` and `serve` commands. |
|
||||
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
|
||||
|
||||
## App support file structure
|
||||
|
||||
Additional files in a project's root folder help you build, test, maintain, document, and deploy the app. These files go in the app root folder next to `src/`.
|
||||
|
||||
```
|
||||
workspace_root/
|
||||
my-app/
|
||||
e2e/
|
||||
src/
|
||||
app.e2e-spec.ts
|
||||
app.po.ts
|
||||
tsconfig.e2e.json
|
||||
protractor.conf.js
|
||||
node_modules/...
|
||||
src/...
|
||||
```
|
||||
|
||||
| FOLDERS | PURPOSE |
|
||||
| :---------------- | :-------------------------------------------- |
|
||||
| `e2e/` | This folder contains end-to-end tests for the app. This is a separate app that tests your main app. The folder and its contents are generated automatically when you create a new app with the CLI `new` or `generate` command. |
|
||||
| `node_modules/` | Node.js creates this folder and puts all third party modules listed in `package.json` in it. *when? doesn't ng new create it?* |
|
||||
|
||||
### Project-level configuration
|
||||
|
||||
Each project uses the CLI configuration at the workspace root-level `angular.json` file.
|
||||
Additional project-specific configuration files are found at the project root level of each app or library.
|
||||
|
||||
```
|
||||
my-workspace/
|
||||
app/
|
||||
.editorconfig
|
||||
.gitignore
|
||||
package.json
|
||||
README.md
|
||||
tsconfig.json
|
||||
tsconfig.test.json
|
||||
tslint.json
|
||||
projects/
|
||||
helpful-library/
|
||||
my-other-app/
|
||||
.editorconfig
|
||||
.gitignore
|
||||
package.json
|
||||
README.md
|
||||
tsconfig.json
|
||||
tsconfig.test.json
|
||||
tslint.json
|
||||
angular.json
|
||||
```
|
||||
|
||||
| CONFIGURATION FILES | PURPOSE |
|
||||
| :------------------ | :-------------------------------------------- |
|
||||
| `.editorconfig` | Simple configuration for your editor to make sure everyone who uses your project has the same basic configuration. Supported by most editors. See http://editorconfig.org for more information. |
|
||||
| `.gitignore` | Git configuration to make sure autogenerated files are not committed to source control. |
|
||||
| `package.json` | Configures the `npm` package manager, listing the third party packages your project uses. You can also add your own custom scripts here. |
|
||||
| `README.md` | Basic documentation for your project, pre-filled with CLI command information. We recommend that you keep this updated so that anyone checking out the repo can build your app. |
|
||||
| `tsconfig.json` | TypeScript compiler configuration, that an IDE such as [Visual Studio Code](https://code.visualstudio.com/) uses to give you helpful tooling. |
|
||||
| `tslint.json` | Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with [Codelyzer](http://codelyzer.com/), used when running the CLI `lint` command. Linting helps keep your code style consistent. |
|
||||
|
||||
{@a global-access}
|
||||
|
||||
## Configuring global access
|
||||
|
||||
The CLI configuration scope is global for a workspace.
|
||||
You can make scripts, libraries, and styles available to all projects in the workspace by setting options in the `angular.json` file in the root workspace folder.
|
||||
|
||||
|
||||
### Adding global scripts
|
||||
|
||||
You can configure your project to add JavaScript files to the global scope.
|
||||
This is especially useful for legacy libraries or analytic snippets.
|
||||
|
||||
In the CLI configuration file, `angular.json`, add the associated script files to the `scripts` array.
|
||||
The scripts are loaded exactly as if you had added them in a `<script>` tag inside `index.html`.
|
||||
For example:
|
||||
|
||||
```
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"scripts": [
|
||||
"src/global-script.js",
|
||||
],
|
||||
```
|
||||
|
||||
You can also rename the output from a script and lazy load it by using the object format in the "scripts" entry.
|
||||
For example:
|
||||
|
||||
```
|
||||
"scripts": [
|
||||
"src/global-script.js",
|
||||
{ "input": "src/lazy-script.js", "lazy": true },
|
||||
{ "input": "src/pre-rename-script.js", "bundleName": "renamed-script" },
|
||||
],
|
||||
```
|
||||
|
||||
If you need to add scripts for unit tests, specify them the same way in the "test" target.
|
||||
|
||||
{@a add-lib}
|
||||
### Adding a global library
|
||||
|
||||
Some JavaScript libraries need to be added to the global scope and loaded as if they were in a script tag.
|
||||
Configure the CLI to do this using the "scripts" and "styles" options of the build target in the CLI configuration file, `angular.json`.
|
||||
|
||||
For example, to use the [Bootstrap 4](https://getbootstrap.com/docs/4.0/getting-started/introduction/)
|
||||
library, first install the library and its dependencies using the `npm` package manager:
|
||||
|
||||
```
|
||||
npm install jquery --save
|
||||
npm install popper.js --save
|
||||
npm install bootstrap --save
|
||||
```
|
||||
|
||||
In the `angular.json` configuration file, add the associated script files to the "scripts" array:
|
||||
|
||||
```
|
||||
"scripts": [
|
||||
"node_modules/jquery/dist/jquery.slim.js",
|
||||
"node_modules/popper.js/dist/umd/popper.js",
|
||||
"node_modules/bootstrap/dist/js/bootstrap.js"
|
||||
],
|
||||
```
|
||||
|
||||
Add the Bootstrap CSS file to the "styles" array:
|
||||
|
||||
```
|
||||
"styles": [
|
||||
"node_modules/bootstrap/dist/css/bootstrap.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
```
|
||||
|
||||
Run or restart `ng serve` to see Bootstrap 4 working in your app.
|
||||
|
||||
#### Using global libraries inside your app
|
||||
|
||||
Once you import a library using the "scripts" array in the configuration file,
|
||||
you should *not* import it using an `import` statement in your TypeScript code
|
||||
(such as `import * as $ from 'jquery';`).
|
||||
If you do, you'll end up with two different copies of the library:
|
||||
one imported as a global library, and one imported as a module.
|
||||
This is especially bad for libraries with plugins, like JQuery,
|
||||
because each copy will have different plugins.
|
||||
|
||||
Instead, download typings for your library (`npm install @types/jquery`) and follow the installation steps
|
||||
given above in [Adding a global library](#add-lib).
|
||||
This gives you access to the global variables exposed by that library.
|
||||
|
||||
{@a define-types}
|
||||
#### Defining typings for global libraries
|
||||
|
||||
If the global library you need to use does not have global typings,
|
||||
you can declare them manually as `any` in `src/typings.d.ts`. For example:
|
||||
|
||||
```
|
||||
declare var libraryName: any;
|
||||
```
|
||||
|
||||
Some scripts extend other libraries; for instance with JQuery plugins:
|
||||
|
||||
```
|
||||
$('.test').myPlugin();
|
||||
```
|
||||
|
||||
In this case, the installed `@types/jquery` doesn't include `myPlugin`,
|
||||
so you need to add an *interface* in `src/typings.d.ts`.
|
||||
For example:
|
||||
|
||||
```
|
||||
interface JQuery {
|
||||
myPlugin(options?: any): any;
|
||||
}
|
||||
```
|
||||
|
||||
If you fail to add the interface for the script-defined extension, your IDE shows an error:
|
||||
|
||||
```
|
||||
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
|
||||
```
|
||||
|
||||
### Adding global styles and style preprocessors
|
||||
|
||||
Angular CLI supports CSS imports and all major CSS preprocessors:
|
||||
|
||||
* [Sass/Scss](http://sass-lang.com/)
|
||||
* [Less](http://lesscss.org/)
|
||||
* [Stylus](http://stylus-lang.com/)
|
||||
|
||||
Angular assumes CSS styles by default, but when you create a project with the
|
||||
CLI `new` command, you can specify the `--style` option to use SASS or STYL styles.
|
||||
|
||||
```
|
||||
> ng new sassyproject --style=sass
|
||||
> ng new scss-project --style=scss
|
||||
> ng new less-project --style=less
|
||||
> ng new styl-project --style=styl
|
||||
```
|
||||
|
||||
You can also set the default style for an existing project by configuring `@schematics/angular`,
|
||||
the default schematic for the Angular CLI:
|
||||
|
||||
```
|
||||
> ng config schematics.@schematics/angular:component.styleext scss
|
||||
```
|
||||
|
||||
#### Style configuration
|
||||
|
||||
By default, the `styles.css` configuration file lists CSS files that supply global styles for a project.
|
||||
If you are using another style type, there is a similar configuration file for global style files of that type,
|
||||
with the extension for that style type, such as `styles.sass`.
|
||||
|
||||
You can add more global styles by configuring the build options in the `angular.json` configuration file.
|
||||
List the style files in the "styles" section in your project's "build" target
|
||||
The files are loaded exactly as if you had added them in a `<link>` tag inside `index.html`.
|
||||
|
||||
```
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"styles": [
|
||||
"src/styles.css",
|
||||
"src/more-styles.css",
|
||||
],
|
||||
...
|
||||
```
|
||||
|
||||
If you need the styles in your unit tests, add them to the "styles" option in the "test" target configuration as well.
|
||||
|
||||
You can specify styles in an object format to rename the output and lazy load it:
|
||||
|
||||
```
|
||||
"styles": [
|
||||
"src/styles.css",
|
||||
"src/more-styles.css",
|
||||
{ "input": "src/lazy-style.scss", "lazy": true },
|
||||
{ "input": "src/pre-rename-style.scss", "bundleName": "renamed-style" },
|
||||
],
|
||||
```
|
||||
|
||||
In Sass and Stylus you can make use of the `includePaths` functionality for both component and global styles,
|
||||
which allows you to add extra base paths to be checked for imports.
|
||||
To add paths, use the `stylePreprocessorOptions` build-target option:
|
||||
|
||||
```
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"src/style-paths"
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
You can then import files in the given folder (such as `src/style-paths/_variables.scss`)
|
||||
anywhere in your project without the need for a relative path:
|
||||
|
||||
```
|
||||
// src/app/app.component.scss
|
||||
// A relative path works
|
||||
@import '../style-paths/variables';
|
||||
// But now this works as well
|
||||
@import 'variables';
|
||||
```
|
||||
|
||||
#### CSS preprocessor integration
|
||||
|
||||
To use a supported CSS preprocessor, add the URL for the preprocessor
|
||||
to your component's `styleUrls` in the `@Component()` metadata.
|
||||
For example:
|
||||
|
||||
```
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'app works!';
|
||||
}
|
||||
```
|
||||
|
||||
<div class="alert-is-helpful>
|
||||
|
||||
Style strings added directly to the `@Component() styles array must be written in CSS.
|
||||
The CLI cannot apply a pre-processor to inline styles.
|
||||
|
||||
</div>
|
||||
|
@ -84,7 +84,7 @@ The abstraction of the form model promotes simplicity over structure. The templa
|
||||
|
||||
## Data flow in forms
|
||||
|
||||
When building forms in Angular, it's important to understand how the the framework handles data flowing from the user or from programmatic changes. Reactive and template-driven forms follow two different strategies when handling form input. The data flow examples below begin with the favorite color input field example from above, and they show how changes to favorite color are handled in reactive forms compared to template-driven forms.
|
||||
When building forms in Angular, it's important to understand how the framework handles data flowing from the user or from programmatic changes. Reactive and template-driven forms follow two different strategies when handling form input. The data flow examples below begin with the favorite color input field example from above, and they show how changes to favorite color are handled in reactive forms compared to template-driven forms.
|
||||
|
||||
### Data flow in reactive forms
|
||||
|
||||
@ -253,7 +253,7 @@ To learn more about reactive forms, see the following guides:
|
||||
* [Form Validation](guide/form-validation#reactive-form-validation)
|
||||
* [Dynamic forms](guide/dynamic-form)
|
||||
|
||||
To learn more about tempate-driven forms, see the following guides:
|
||||
To learn more about template-driven forms, see the following guides:
|
||||
|
||||
* [Template-driven Forms](guide/forms)
|
||||
* [Form Validation](guide/form-validation#template-driven-validation)
|
||||
|
@ -77,7 +77,7 @@ A way to initialize and launch an app or system.
|
||||
|
||||
In Angular, an app's root NgModule (`AppModule`) has a `bootstrap` property that identifies the app's top-level [components](guide/glossary#component).
|
||||
During the bootstrap process, Angular creates and inserts these components into the `index.html` host web page.
|
||||
You can bootstrap multiple apps in the same `index.html`. Each app ccontains its own components.
|
||||
You can bootstrap multiple apps in the same `index.html`. Each app contains its own components.
|
||||
|
||||
Learn more in [Bootstrapping](guide/bootstrapping).
|
||||
|
||||
@ -132,7 +132,7 @@ A [decorator](guide/glossary#decorator) statement immediately before a field in
|
||||
|
||||
The [Angular CLI](https://cli.angular.io/) is a command-line tool for managing the Angular development cycle. Use it to create the initial filesystem scaffolding for a [workspace](guide/glossary#workspace) or [project](guide/glossary#project), and to run [schematics](guide/glossary#schematic) that add and modify code for initial generic versions of various elements. The CLI supports all stages of the development cycle, including building, testing, bundling, and deployment.
|
||||
|
||||
* To begin using the CLI for a new project, see [QuickStart](guide/quickstart).
|
||||
* To begin using the CLI for a new project, see [Getting Started](guide/quickstart).
|
||||
* To learn more about the full capabilities of the CLI, see the [Angular CLI documentation](https://github.com/angular/angular-cli/wiki).
|
||||
|
||||
{@a component}
|
||||
|
@ -936,7 +936,7 @@ setting up such mocking straightforward.
|
||||
### Mocking philosophy
|
||||
|
||||
Angular's HTTP testing library is designed for a pattern of testing wherein
|
||||
the the app executes code and makes requests first.
|
||||
the app executes code and makes requests first.
|
||||
|
||||
Then a test expects that certain requests have or have not been made,
|
||||
performs assertions against those requests,
|
||||
|
@ -359,7 +359,7 @@ Pluralization categories include (depending on the language):
|
||||
After the pluralization category, put the default English text in braces (`{}`).
|
||||
|
||||
In the example above, the three options are specified according to that pluralization pattern. For
|
||||
talking about about zero minutes, you use `=0 {just now}`. For one minute, you use `=1 {one minute}`.
|
||||
talking about zero minutes, you use `=0 {just now}`. For one minute, you use `=1 {one minute}`.
|
||||
Any unmatched cardinality uses `other {{{minutes}} minutes ago}`. You could choose to add patterns
|
||||
for two, three, or any other number if the pluralization rules were different. For the example of
|
||||
"minute", only these three patterns are necessary in English.
|
||||
@ -542,7 +542,7 @@ This sample file is easy to translate without a special editor or knowledge of F
|
||||
|
||||
|
||||
2. Duplicate the `<source/>` tag, rename it `target`, and then replace its content with the French
|
||||
greeting. If you were working with a more complex translation, you could use the the information
|
||||
greeting. If you were working with a more complex translation, you could use the information
|
||||
and context provided by the source, description, and meaning elements to guide your selection of
|
||||
the appropriate French translation.
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
# Observables
|
||||
|
||||
Observables provide support for passing messages between publishers and subscribers in your application. Observables offer significant benefits over other techniques for event handling, asynchronous programming, and handling multiple values.
|
||||
@ -36,10 +37,10 @@ An `Observable` instance begins publishing values only when someone subscribes t
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In order to show how subscribing works, we need to create a new observable. There is a constructor that you use to create new instances, but for illustration, we can use some static methods on the `Observable` class that create simple observables of frequently used types:
|
||||
In order to show how subscribing works, we need to create a new observable. There is a constructor that you use to create new instances, but for illustration, we can use some methods from the RxJS library that create simple observables of frequently used types:
|
||||
|
||||
* `Observable.of(...items)`—Returns an `Observable` instance that synchronously delivers the values provided as arguments.
|
||||
* `Observable.from(iterable)`—Converts its argument to an `Observable` instance. This method is commonly used to convert an array to an observable.
|
||||
* `of(...items)`—Returns an `Observable` instance that synchronously delivers the values provided as arguments.
|
||||
* `from(iterable)`—Converts its argument to an `Observable` instance. This method is commonly used to convert an array to an observable.
|
||||
|
||||
</div>
|
||||
|
||||
@ -62,7 +63,7 @@ Note that a `next()` function could receive, for instance, message strings, or e
|
||||
|
||||
Use the `Observable` constructor to create an observable stream of any type. The constructor takes as its argument the subscriber function to run when the observable’s `subscribe()` method executes. A subscriber function receives an `Observer` object, and can publish values to the observer's `next()` method.
|
||||
|
||||
For example, to create an observable equivalent to the `Observable.of(1, 2, 3)` above, you could do something like this:
|
||||
For example, to create an observable equivalent to the `of(1, 2, 3)` above, you could do something like this:
|
||||
|
||||
<code-example path="observables/src/creating.ts" region="subscriber" title="Create observable with constructor"></code-example>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# QuickStart
|
||||
# Getting Started
|
||||
|
||||
Good tools make application development quicker and easier to maintain than
|
||||
if you did everything by hand.
|
||||
@ -147,7 +147,7 @@ Open `src/app/app.component.css` and give the component some style.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app">
|
||||
<img src='generated/images/guide/cli-quickstart/my-first-app.png' alt="Output of Getting Started app">
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -70,7 +70,7 @@ The following table contains our current target release dates for the next two m
|
||||
September/October 2018 | 7.0.0 | ^6.0.0
|
||||
March/April 2019 | 8.0.0 | ^7.0.0
|
||||
|
||||
Compatiblity note: The primary goal of the backwards compatibility promise is to ensure that changes in the core framework and tooling don't break the existing ecosystem of components and applications and don't put undue upgrade/migration burden on Angular application and component authors.
|
||||
Compatibility note: The primary goal of the backward compatibility promise is to ensure that changes in the core framework and tooling don't break the existing ecosystem of components and applications and don't put undue upgrade/migration burden on Angular application and component authors.
|
||||
|
||||
|
||||
{@a lts}
|
||||
|
@ -3918,7 +3918,7 @@ the `queryParamsHandling` and `preserveFragment` bindings respectively.
|
||||
|
||||
As you've worked through the milestones, the application has naturally gotten larger.
|
||||
As you continue to build out feature areas, the overall application size will continue to grow.
|
||||
At some point you'll reach a tipping point where the application takes long time to load.
|
||||
At some point you'll reach a tipping point where the application takes a long time to load.
|
||||
|
||||
How do you combat this problem? With asynchronous routing, which loads feature modules _lazily_, on request.
|
||||
Lazy loading has multiple benefits.
|
||||
|
@ -330,7 +330,7 @@ You may need [nvm](https://github.com/creationix/nvm) if you already have projec
|
||||
Links on almost every documentation page open completed samples in the browser.
|
||||
You can play with the sample code, share your changes with friends, and download and run the code on your own machine.
|
||||
|
||||
The [QuickStart](guide/quickstart "Angular QuickStart Playground") shows just the `AppComponent` file.
|
||||
The [Getting Started](guide/quickstart "Angular QuickStart Playground") shows just the `AppComponent` file.
|
||||
It creates the equivalent of `app.module.ts` and `main.ts` internally _for the playground only_.
|
||||
so the reader can discover Angular without distraction.
|
||||
The other samples are based on the QuickStart seed.
|
||||
@ -356,3 +356,24 @@ If you develop angular locally with `ng serve`, there will be `websocket` connec
|
||||
|
||||
In windows, by default one application can only have 6 websocket connections, <a href="https://msdn.microsoft.com/library/ee330736%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#websocket_maxconn" title="MSDN WebSocket settings">MSDN WebSocket Settings</a>.
|
||||
So if IE was refreshed manunally or automatically by `ng serve`, sometimes, the websocket will not close properly, when websocket connections exceed limitations, `SecurityError` will be thrown, this error will not affect the angular application, you can just restart IE to clear this error, or modify the windows registry to update the limitations.
|
||||
|
||||
## Appendix: test using `fakeAsync()/async()`
|
||||
|
||||
If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read [testing guide](guide/testing#async-test-with-fakeasync)), you need to import `zone.js/dist/zone-testing` in your test setup file.
|
||||
|
||||
<div class="alert is-important">
|
||||
If you create project with `Angular/CLI`, it is already imported in `src/test.ts`.
|
||||
</div>
|
||||
|
||||
And in the earlier versions of `Angular`, the following files were imported or added in your html file:
|
||||
|
||||
```
|
||||
import 'zone.js/dist/long-stack-trace-zone';
|
||||
import 'zone.js/dist/proxy';
|
||||
import 'zone.js/dist/sync-test';
|
||||
import 'zone.js/dist/jasmine-patch';
|
||||
import 'zone.js/dist/async-test';
|
||||
import 'zone.js/dist/fake-async-test';
|
||||
```
|
||||
|
||||
You can still load those files separately, but the order is important, you must import `proxy` before `sync-test`, `async-test`, `fake-async-test` and `jasmine-patch`. And you also need to import `sync-test` before `jasmine-patch`, so it is recommended to just import `zone-testing` instead of loading those separated files.
|
@ -45,7 +45,7 @@ If you fully understand the meaning behind the guideline and have a good reason
|
||||
|
||||
|
||||
|
||||
**Avoid** indicates something you should almost never do. Code examples to *avoid* have an unmistakeable red header.
|
||||
**Avoid** indicates something you should almost never do. Code examples to *avoid* have an unmistakable red header.
|
||||
|
||||
|
||||
</div>
|
||||
@ -2865,7 +2865,7 @@ and more difficult in a flat structure.
|
||||
|
||||
|
||||
|
||||
**Why?** NgModules make it easier to isolate, test, and re-use features.
|
||||
**Why?** NgModules make it easier to isolate, test, and reuse features.
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -89,189 +89,6 @@ The root file names (`app.component`) are the same for both files.
|
||||
|
||||
Adopt these two conventions in your own projects for _every kind_ of test file.
|
||||
|
||||
{@a ci}
|
||||
|
||||
## Set up continuous integration
|
||||
|
||||
One of the best ways to keep your project bug free is through a test suite, but it's easy to forget to run tests all the time.
|
||||
Continuous integration (CI) servers let you set up your project repository so that your tests run on every commit and pull request.
|
||||
|
||||
There are paid CI services like Circle CI and Travis CI, and you can also host your own for free using Jenkins and others.
|
||||
Although Circle CI and Travis CI are paid services, they are provided free for open source projects.
|
||||
You can create a public project on GitHub and add these services without paying.
|
||||
Contributions to the Angular repo are automatically run through a whole suite of Circle CI and Travis CI tests.
|
||||
|
||||
This article explains how to configure your project to run Circle CI and Travis CI, and also update your test configuration to be able to run tests in the Chrome browser in either environment.
|
||||
|
||||
|
||||
### Configure project for Circle CI
|
||||
|
||||
Step 1: Create a folder called `.circleci` at the project root.
|
||||
|
||||
Step 2: In the new folder, create a file called `config.yml` with the following content:
|
||||
|
||||
```
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/my-project
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: my-project-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
- run: npm install
|
||||
- save_cache:
|
||||
key: my-project-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- "node_modules"
|
||||
- run: xvfb-run -a npm run test -- --single-run --no-progress --browser=ChromeNoSandbox
|
||||
- run: xvfb-run -a npm run e2e -- --no-progress --config=protractor-ci.conf.js
|
||||
```
|
||||
|
||||
This configuration caches `node_modules/` and uses [`npm run`](https://docs.npmjs.com/cli/run-script) to run CLI commands, because `@angular/cli` is not installed globally.
|
||||
The double dash (`--`) is needed to pass arguments into the `npm` script.
|
||||
|
||||
For Chrome, it uses `xvfb-run` to run the `npm run` command using a virtual screen.
|
||||
|
||||
Step 3: Commit your changes and push them to your repository.
|
||||
|
||||
Step 4: [Sign up for Circle CI](https://circleci.com/docs/2.0/first-steps/) and [add your project](https://circleci.com/add-projects).
|
||||
Your project should start building.
|
||||
|
||||
* Learn more about Circle CI from [Circle CI documentation](https://circleci.com/docs/2.0/).
|
||||
|
||||
### Configure project for Travis CI
|
||||
|
||||
Step 1: Create a file called `.travis.yml` at the project root, with the following content:
|
||||
|
||||
```
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ./node_modules
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
# Use Chromium instead of Chrome.
|
||||
- export CHROME_BIN=chromium-browser
|
||||
- xvfb-run -a npm run test -- --single-run --no-progress --browser=ChromeNoSandbox
|
||||
- xvfb-run -a npm run e2e -- --no-progress --config=protractor-ci.conf.js
|
||||
```
|
||||
|
||||
This does the same things as the Circle CI configuration, except that Travis doesn't come with Chrome, so we use Chromium instead.
|
||||
|
||||
Step 2: Commit your changes and push them to your repository.
|
||||
|
||||
Step 3: [Sign up for Travis CI](https://travis-ci.org/auth) and [add your project](https://travis-ci.org/profile).
|
||||
You'll need to push a new commit to trigger a build.
|
||||
|
||||
* Learn more about Travis CI testing from [Travis CI documentation](https://docs.travis-ci.com/).
|
||||
|
||||
### Configure CLI for CI testing in Chrome
|
||||
|
||||
When the CLI commands `ng test` and `ng e2e` are generally running the CI tests in your environment, you might still need to adjust your configuration to run the Chrome browser tests.
|
||||
|
||||
There are configuration files for both the [Karma JavaScript test runner](http://karma-runner.github.io/2.0/config/configuration-file.html)
|
||||
and [Protractor](https://www.protractortest.org/#/api-overview) end-to-end testing tool,
|
||||
which you must adjust to start Chrome without sandboxing.
|
||||
|
||||
* In the Karma configuration file, `karma.conf.js`, add a custom launcher called ChromeNoSandbox below browsers:
|
||||
```
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeNoSandbox: {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox']
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
* Create a new file, `protractor-ci.conf.js`, in the root folder of your project, which extends the original `protractor.conf.js`:
|
||||
```
|
||||
const config = require('./protractor.conf').config;
|
||||
|
||||
config.capabilities = {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: ['--no-sandbox']
|
||||
}
|
||||
};
|
||||
|
||||
exports.config = config;
|
||||
```
|
||||
|
||||
Now you can run the following commands to use the `--no-sandbox` flag:
|
||||
|
||||
```
|
||||
ng test --single-run --no-progress --browser=ChromeNoSandbox
|
||||
ng e2e --no-progress --config=protractor-ci.conf.js
|
||||
```
|
||||
|
||||
{@a code-coverage}
|
||||
|
||||
## Enable code coverage reports
|
||||
|
||||
The CLI can run unit tests and create code coverage reports.
|
||||
Code coverage reports show you any parts of our code base that may not be properly tested by your unit tests.
|
||||
|
||||
To generate a coverage report run the following command in the root of your project.
|
||||
|
||||
```
|
||||
ng test --watch=false --code-coverage
|
||||
```
|
||||
|
||||
When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values.
|
||||
|
||||
If you want to create code-coverage reports every time you test, you can set the following option in the CLI configuration file, `angular.json`:
|
||||
|
||||
```
|
||||
"test": {
|
||||
"options": {
|
||||
"codeCoverage": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Code coverage enforcement
|
||||
|
||||
The code coverage percentages let you estimate how much of your code is tested.
|
||||
If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI.
|
||||
|
||||
For example, suppose you want the code base to have a minimum of 80% code coverage.
|
||||
To enable this, open the [Karma](http://karma-runner.github.io/0.13/index.html) test platform configuration file, `karma.conf.js`, and add the following in the `coverageIstanbulReporter:` key.
|
||||
|
||||
```
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true,
|
||||
thresholds: {
|
||||
statements: 80,
|
||||
lines: 80,
|
||||
branches: 80,
|
||||
functions: 80
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `thresholds` property causes the tool to enforce a minimum of 80% code coverage when the unit tests are run in the project.
|
||||
|
||||
## Service Tests
|
||||
|
||||
Services are often the easiest files to unit test.
|
||||
@ -1237,6 +1054,8 @@ value becomes available. The test must become _asynchronous_.
|
||||
|
||||
#### Async test with _fakeAsync()_
|
||||
|
||||
To use `fakeAsync()` functionality, you need to import `zone-testing`, for details, please read [setup guide](guide/setup#appendix-test-using-fakeasyncasync).
|
||||
|
||||
The following test confirms the expected behavior when the service returns an `ErrorObservable`.
|
||||
|
||||
<code-example
|
||||
@ -1250,7 +1069,7 @@ Note that the `it()` function receives an argument of the following form.
|
||||
fakeAsync(() => { /* test body */ })`
|
||||
```
|
||||
|
||||
The `fakeAsync` function enables a linear coding style by running the test body in a special _fakeAsync test zone_.
|
||||
The `fakeAsync()` function enables a linear coding style by running the test body in a special `fakeAsync test zone`.
|
||||
The test body appears to be synchronous.
|
||||
There is no nested syntax (like a `Promise.then()`) to disrupt the flow of control.
|
||||
|
||||
@ -1263,12 +1082,55 @@ You do have to call `tick()` to advance the (virtual) clock.
|
||||
Calling `tick()` simulates the passage of time until all pending asynchronous activities finish.
|
||||
In this case, it waits for the error handler's `setTimeout()`;
|
||||
|
||||
The `tick` function is one of the Angular testing utilities that you import with `TestBed`.
|
||||
It's a companion to `fakeAsync` and you can only call it within a `fakeAsync` body.
|
||||
The `tick()` function accepts milliseconds as parameter (defaults to 0 if not provided). The parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/async-helper.spec.ts"
|
||||
region="fake-async-test-tick">
|
||||
</code-example>
|
||||
|
||||
The `tick()` function is one of the Angular testing utilities that you import with `TestBed`.
|
||||
It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body.
|
||||
|
||||
#### Comparing dates inside fakeAsync()
|
||||
|
||||
`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/async-helper.spec.ts"
|
||||
region="fake-async-test-date">
|
||||
</code-example>
|
||||
|
||||
#### jasmine.clock with fakeAsync()
|
||||
|
||||
Jasmine also provides a `clock` feature to mock dates. Angular automatically runs tests that are run after
|
||||
`jasmine.clock().install()` is called inside a `fakeAsync()` method until `jasmine.clock().uninstall()` is called. `fakeAsync()` is not needed and throws an error if nested.
|
||||
|
||||
By default, this feature is disabled. To enable it, set a global flag before import `zone-testing`.
|
||||
|
||||
If you use the Angular CLI, configure this flag in `src/test.ts`.
|
||||
|
||||
```
|
||||
(window as any)['__zone_symbol__fakeAsyncPatchLock'] = true;
|
||||
import 'zone.js/dist/zone-testing';
|
||||
```
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/async-helper.spec.ts"
|
||||
region="fake-async-test-clock">
|
||||
</code-example>
|
||||
|
||||
#### Using the RxJS scheduler inside fakeAsync()
|
||||
|
||||
You can also use RxJS scheduler in `fakeAsync()` just like using `setTimeout()` or `setInterval()`, but you need to import `zone.js/dist/zone-patch-rxjs-fake-async` to patch RxJS scheduler.
|
||||
<code-example
|
||||
path="testing/src/app/demo/async-helper.spec.ts"
|
||||
region="fake-async-test-rxjs">
|
||||
</code-example>
|
||||
|
||||
#### Support more macroTasks
|
||||
|
||||
By default `fakeAsync` supports the following `macroTasks`.
|
||||
By default `fakeAsync()` supports the following `macroTasks`.
|
||||
|
||||
- setTimeout
|
||||
- setInterval
|
||||
@ -1289,7 +1151,7 @@ If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macr
|
||||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
If you want to support such case, you need to define the `macroTask` you want to support in `beforeEach`.
|
||||
If you want to support such case, you need to define the `macroTask` you want to support in `beforeEach()`.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
@ -1386,6 +1248,8 @@ Then you can assert that the quote element displays the expected text.
|
||||
|
||||
#### Async test with _async()_
|
||||
|
||||
To use `async()` functionality, you need to import `zone-testing`, for details, please read [setup guide](guide/setup#appendix-test-using-fakeasyncasync).
|
||||
|
||||
The `fakeAsync()` utility function has a few limitations.
|
||||
In particular, it won't work if the test body makes an `XHR` call.
|
||||
|
||||
@ -1409,12 +1273,13 @@ Here's the previous `fakeAsync()` test, re-written with the `async()` utility.
|
||||
|
||||
The `async()` utility hides some asynchronous boilerplate by arranging for the tester's code
|
||||
to run in a special _async test zone_.
|
||||
You don't have to pass Jasmine's `done()` into the test and call `done()`
|
||||
in promise or observable callbacks.
|
||||
You don't need to pass Jasmine's `done()` into the test and call `done()` because it is `undefined` in promise or observable callbacks.
|
||||
|
||||
But the test's asynchronous nature is revealed by the call to `fixture.whenStable()`,
|
||||
which breaks the linear flow of control.
|
||||
|
||||
When using an `intervalTimer()` such as `setInterval()` in `async()`, remember to cancel the timer with `clearInterval()` after the test, otherwise the `async()` never ends.
|
||||
|
||||
{@a when-stable}
|
||||
|
||||
#### _whenStable_
|
||||
@ -1433,18 +1298,19 @@ update the quote element with the expected text.
|
||||
|
||||
#### Jasmine _done()_
|
||||
|
||||
While the `async` and `fakeAsync` functions greatly
|
||||
While the `async()` and `fakeAsync()` functions greatly
|
||||
simplify Angular asynchronous testing,
|
||||
you can still fall back to the traditional technique
|
||||
and pass `it` a function that takes a
|
||||
[`done` callback](http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support).
|
||||
|
||||
You can't call `done()` in `async()` or `fakeAsync()` functions, because the `done parameter`
|
||||
is `undefined`.
|
||||
|
||||
Now you are responsible for chaining promises, handling errors, and calling `done()` at the appropriate moments.
|
||||
|
||||
Writing test functions with `done()`, is more cumbersome than `async`and `fakeAsync`.
|
||||
But it is occasionally necessary.
|
||||
For example, you can't call `async` or `fakeAsync` when testing
|
||||
code that involves the `intervalTimer()` or the RxJS `delay()` operator.
|
||||
Writing test functions with `done()`, is more cumbersome than `async()`and `fakeAsync()`.
|
||||
But it is occasionally necessary when code involves the `intervalTimer()` like `setInterval`.
|
||||
|
||||
Here are two more versions of the previous test, written with `done()`.
|
||||
The first one subscribes to the `Observable` exposed to the template by the component's `quote` property.
|
||||
@ -2307,7 +2173,6 @@ Here are a few more `HeroDetailComponent` tests to reinforce the point.
|
||||
|
||||
{@a compile-components}
|
||||
### Calling _compileComponents()_
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can ignore this section if you _only_ run tests with the CLI `ng test` command
|
||||
@ -2871,7 +2736,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
||||
|
||||
<td>
|
||||
|
||||
When a `fakeAsync` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks),
|
||||
When a `fakeAsync()` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks),
|
||||
the test fails with a clear error message.
|
||||
|
||||
In general, a test should end with no queued tasks.
|
||||
@ -2888,7 +2753,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
||||
|
||||
<td>
|
||||
|
||||
When a `fakeAsync` test ends with pending _micro-tasks_ such as unresolved promises,
|
||||
When a `fakeAsync()` test ends with pending _micro-tasks_ such as unresolved promises,
|
||||
the test fails with a clear error message.
|
||||
|
||||
In general, a test should wait for micro-tasks to finish.
|
||||
|
@ -255,7 +255,7 @@ You can combine keyframes with `duration`, `delay`, and `easing` within a single
|
||||
|
||||
### Keyframes with a pulsation
|
||||
|
||||
Use ketframes to create a pulse effect in your animations by defining styles at specific offset throughout the animation.
|
||||
Use keyframes to create a pulse effect in your animations by defining styles at specific offset throughout the animation.
|
||||
|
||||
Here's an example of using keyframes to create a pulse effect:
|
||||
|
||||
@ -305,4 +305,4 @@ You may also be interested in the following:
|
||||
* [Introduction to Angular animations](guide/animations)
|
||||
* [Complex animation sequences](guide/complex-animation-sequences)
|
||||
* [Reusable animations](guide/reusable-animations)
|
||||
* [Route transition animations](guide/route-animations)
|
||||
* [Route transition animations](guide/route-animations)
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
aio/content/images/bios/lacolaco.jpg
Normal file
BIN
aio/content/images/bios/lacolaco.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
@ -671,5 +671,14 @@
|
||||
"website": "https://sajeetharan.herokuapp.com/",
|
||||
"bio": "Sajeetharan is a Developer, Top contributor on stackoverflow for #Angular, ng-SriLanka organizer. He makes use of his extensive knowledge over the past years to contribute to community to make the world a better place.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"lacolaco": {
|
||||
"name": "Suguru Inatomi",
|
||||
"picture": "lacolaco.jpg",
|
||||
"twitter": "laco2net",
|
||||
"website": "https://lacolaco.net",
|
||||
"bio": "Suguru is a community-loving Frontend developer and a lead of Angular Japan User Group. He organizes the largest Angular event in Japan (ng-japan). And he is a contributor to Angular by sending patches, writing, speaking, and localizing resources in Japanese.",
|
||||
"group": "GDE"
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ Angular is a platform that makes it easy to build applications with the web. Ang
|
||||
<p class="card-footer">Angular in Action</p>
|
||||
</a>
|
||||
|
||||
<a href="guide/quickstart" class="docs-card" title="Angular Quickstart">
|
||||
<a href="guide/quickstart" class="docs-card" title="Angular Getting Started">
|
||||
<section>Get Going with Angular</section>
|
||||
<p>Get going on your own environment with the Quickstart.</p>
|
||||
<p>Get going on your own environment with the Getting Started.</p>
|
||||
<p class="card-footer">Quickstart</p>
|
||||
</a>
|
||||
|
||||
|
@ -13,6 +13,12 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- AngularMix -->
|
||||
<tr>
|
||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
||||
<td>Orlando, Florida</td>
|
||||
<td>October 10-12, 2018</td>
|
||||
</tr>
|
||||
<!-- ReactiveConf -->
|
||||
<tr>
|
||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
||||
@ -59,13 +65,13 @@
|
||||
<!-- ngconf 2018-->
|
||||
<tr>
|
||||
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
||||
<td>Salt Lake City, UT</td>
|
||||
<td>Salt Lake City, Utah</td>
|
||||
<td>April 18-20, 2018</td>
|
||||
</tr>
|
||||
<!-- WeRDevs-->
|
||||
<tr>
|
||||
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
|
||||
<td>Vienna</td>
|
||||
<td>Vienna, Austria</td>
|
||||
<td>May 16-18, 2018</td>
|
||||
</tr>
|
||||
<!-- ngJapan-->
|
||||
|
@ -57,14 +57,14 @@
|
||||
{
|
||||
"url": "guide/docs-style-guide",
|
||||
"title": "Doc authors style guide",
|
||||
"tooltip": "Style guide for documentation authors.",
|
||||
"tooltip": "Style guide for documentation authors",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/quickstart",
|
||||
"title": "Getting Started",
|
||||
"tooltip": "A brief introduction to Angular and Angular CLI essentials."
|
||||
"tooltip": "A gentle introduction to Angular."
|
||||
},
|
||||
|
||||
{
|
||||
@ -73,43 +73,43 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "tutorial",
|
||||
"title": "Introduction",
|
||||
"tooltip": "Introduction to the Tour of Heroes tutorial"
|
||||
"title": "1. Introduction",
|
||||
"tooltip": "Part 1: Introduction to the Tour of Heroes tutorial"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt0",
|
||||
"title": "The Application Shell",
|
||||
"tooltip": "Creating the application shell"
|
||||
"title": "2. The Application Shell",
|
||||
"tooltip": "Part 2: Creating the application shell"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt1",
|
||||
"title": "1. The Hero Editor",
|
||||
"tooltip": "Part 1: Build a simple hero editor"
|
||||
"title": "3. The Hero Editor",
|
||||
"tooltip": "Part 3: Build a simple hero editor"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt2",
|
||||
"title": "2. Displaying a List",
|
||||
"tooltip": "Part 2: Build a master/detail page with a list of heroes."
|
||||
"title": "4. Displaying a List",
|
||||
"tooltip": "Part 4: Build a master/detail page with a list of heroes."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt3",
|
||||
"title": "3. Master/Detail Components",
|
||||
"tooltip": "Part 3: Refactor the master/detail view into separate components."
|
||||
"title": "5. Master/Detail Components",
|
||||
"tooltip": "Part 5: Refactor the master/detail view into separate components."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt4",
|
||||
"title": "4. Services",
|
||||
"tooltip": "Part 4: Create a reusable service to manage hero data."
|
||||
"title": "6. Services",
|
||||
"tooltip": "Part 6: Create a reusable service to manage hero data."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt5",
|
||||
"title": "5. Routing",
|
||||
"tooltip": "Part 5: Add the Angular router and navigate among the views."
|
||||
"title": "7. Routing",
|
||||
"tooltip": "Part 7: Add the Angular router and navigate among the views."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt6",
|
||||
"title": "6. HTTP",
|
||||
"tooltip": "Part 6: Use HTTP to retrieve and save hero data."
|
||||
"title": "8. HTTP",
|
||||
"tooltip": "Part 8: Use HTTP to retrieve and save hero data."
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -210,13 +210,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Forms",
|
||||
"tooltip": "Angular Forms",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/forms-overview",
|
||||
"title": "Introduction",
|
||||
"title": "Forms Overview",
|
||||
"tooltip": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors."
|
||||
},
|
||||
{
|
||||
@ -240,8 +241,8 @@
|
||||
"tooltip": "Render dynamic forms with FormGroup."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"title": "Observables & RxJS",
|
||||
"tooltip": "Observables & RxJS",
|
||||
"children": [
|
||||
@ -417,29 +418,84 @@
|
||||
"tooltip": "Animate route transitions."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/testing",
|
||||
"title": "Testing",
|
||||
"tooltip": "Techniques and practices for testing an Angular app."
|
||||
},
|
||||
{
|
||||
"url": "guide/cheatsheet",
|
||||
"title": "Cheat Sheet",
|
||||
"tooltip": "A quick guide to common Angular coding techniques."
|
||||
}
|
||||
]},
|
||||
|
||||
{
|
||||
"title": "Techniques",
|
||||
"tooltip": "Techniques for putting Angular to work in your environment",
|
||||
"children": [
|
||||
|
||||
{
|
||||
"url": "guide/i18n",
|
||||
"title": "Internationalization (i18n)",
|
||||
"tooltip": "Translate the app's template text into multiple languages."
|
||||
},
|
||||
{
|
||||
"url": "guide/language-service",
|
||||
"title": "Language Service",
|
||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
||||
},
|
||||
{
|
||||
"url": "guide/security",
|
||||
"title": "Security",
|
||||
"tooltip": "Developing for content security in Angular applications."
|
||||
},
|
||||
{
|
||||
"url": "guide/i18n",
|
||||
"title": "Internationalization (i18n)",
|
||||
"tooltip": "Translate the app's template text into multiple languages."
|
||||
},
|
||||
"title": "Setup & Deployment",
|
||||
"tooltip": "Setup and Deployment",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/setup",
|
||||
"title": "Setup for local development",
|
||||
"tooltip": "Install the Angular QuickStart seed for faster, more efficient development on your machine."
|
||||
},
|
||||
{
|
||||
"url": "guide/setup-systemjs-anatomy",
|
||||
"title": "Anatomy of the Setup",
|
||||
"tooltip": "Inside the local development environment for SystemJS."
|
||||
},
|
||||
{
|
||||
"url": "guide/browser-support",
|
||||
"title": "Browser Support",
|
||||
"tooltip": "Browser support and polyfills guide."
|
||||
},
|
||||
{
|
||||
"url": "guide/npm-packages",
|
||||
"title": "Npm Packages",
|
||||
"tooltip": "Recommended npm packages, and how to specify package dependencies."
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/typescript-configuration",
|
||||
"title": "TypeScript Configuration",
|
||||
"tooltip": "TypeScript configuration for Angular developers."
|
||||
},
|
||||
{
|
||||
"url": "guide/aot-compiler",
|
||||
"title": "Ahead-of-Time Compilation",
|
||||
"tooltip": "Learn why and how to use the Ahead-of-Time (AOT) compiler."
|
||||
},
|
||||
{
|
||||
"url": "guide/deployment",
|
||||
"title": "Deployment",
|
||||
"tooltip": "Learn how to deploy your Angular app."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Service Workers & PWA",
|
||||
"title": "Service Workers",
|
||||
"tooltip": "Angular service workers: Controlling caching of application resources.",
|
||||
"children": [
|
||||
{
|
||||
@ -469,108 +525,24 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/universal",
|
||||
"title": "Server-side Rendering",
|
||||
"tooltip": "Render HTML server-side with Angular Universal."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Setup & Deployment",
|
||||
"tooltip": "Setup, build, testing, and deployment environment and tool information.",
|
||||
"children": [
|
||||
|
||||
{
|
||||
"url": "guide/setup",
|
||||
"title": "Setup for local development",
|
||||
"tooltip": "Install the Angular QuickStart seed for faster, more efficient development on your machine.",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/file-structure",
|
||||
"title": "Project File Structure",
|
||||
"tooltip": "Inside the local development environment for SystemJS."
|
||||
},
|
||||
{
|
||||
"url": "guide/npm-packages",
|
||||
"title": "Packages",
|
||||
"tooltip": "Explanation of npm packages installed into a project by default."
|
||||
},
|
||||
{
|
||||
"url": "guide/typescript-configuration",
|
||||
"title": "TypeScript Configuration",
|
||||
"tooltip": "TypeScript configuration for Angular developers."
|
||||
},
|
||||
{
|
||||
"url": "guide/aot-compiler",
|
||||
"title": "Ahead-of-Time Compilation",
|
||||
"tooltip": "Learn why and how to use the Ahead-of-Time (AOT) compiler."
|
||||
},
|
||||
{
|
||||
"url": "guide/build",
|
||||
"title": "Building & Serving",
|
||||
"tooltip": "Building and serving Angular apps."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing",
|
||||
"title": "Testing",
|
||||
"tooltip": "Techniques and practices for testing an Angular app."
|
||||
},
|
||||
{
|
||||
"url": "guide/deployment",
|
||||
"title": "Deployment",
|
||||
"tooltip": "Learn how to deploy your Angular app."
|
||||
},
|
||||
{
|
||||
"url": "guide/browser-support",
|
||||
"title": "Browser Support",
|
||||
"tooltip": "Browser support and polyfills guide."
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Dev Tool Integration",
|
||||
"tooltip": "Integration with your development environment and tool information.",
|
||||
"title": "Keeping Up-to-Date",
|
||||
"tooltip": "Angular release practices, planning for updates, and update resources.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/language-service",
|
||||
"title": "Language Service",
|
||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
||||
},
|
||||
{
|
||||
"url": "guide/visual-studio-2015",
|
||||
"title": "Visual Studio 2015 QuickStart",
|
||||
"tooltip": "Use Visual Studio 2015 with the QuickStart files."
|
||||
}
|
||||
{
|
||||
"url": "guide/updating",
|
||||
"title": "Updating Your Projects",
|
||||
"tooltip": "Information about updating Angular applications and libraries to the latest version."
|
||||
},
|
||||
{
|
||||
"url": "guide/releases",
|
||||
"title": "Angular Releases",
|
||||
"tooltip": "Angular versioning, release, support, and deprecation policies and practices."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/setup-systemjs-anatomy",
|
||||
"title": "Anatomy of the Setup",
|
||||
"tooltip": "Inside the local development environment for SystemJS.",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"title": "Release Information",
|
||||
"tooltip": "Angular release practices, updating, and upgrading.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/releases",
|
||||
"title": "Angular Releases",
|
||||
"tooltip": "Angular versioning, release, support, and deprecation policies and practices."
|
||||
},
|
||||
{
|
||||
"url": "guide/updating",
|
||||
"title": "Keeping Up-to-Date",
|
||||
"tooltip": "Information about updating Angular applications and libraries to the latest version."
|
||||
},
|
||||
{
|
||||
"title": "Upgrading from AngularJS",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular.",
|
||||
@ -580,25 +552,27 @@
|
||||
"title": "Upgrading Instructions",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade-performance",
|
||||
"title": "Upgrading for Performance",
|
||||
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
||||
},
|
||||
{
|
||||
"url": "guide/ajs-quick-reference",
|
||||
"title": "AngularJS-Angular Concepts",
|
||||
"tooltip": "Learn how AngularJS concepts and techniques map to Angular."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Quick Reference",
|
||||
"tooltip": "Summaries of Angular syntax, coding, and terminology.",
|
||||
"children": [
|
||||
|
||||
},
|
||||
{
|
||||
"url": "guide/cheatsheet",
|
||||
"title": "Cheat Sheet",
|
||||
"tooltip": "A quick guide to common Angular coding techniques."
|
||||
"url": "guide/universal",
|
||||
"title": "Server-side Rendering",
|
||||
"tooltip": "Render HTML server-side with Angular Universal."
|
||||
},
|
||||
{
|
||||
"url": "guide/visual-studio-2015",
|
||||
"title": "Visual Studio 2015 QuickStart",
|
||||
"tooltip": "Use Visual Studio 2015 with the QuickStart files."
|
||||
},
|
||||
{
|
||||
"url": "guide/styleguide",
|
||||
@ -615,20 +589,9 @@
|
||||
|
||||
{
|
||||
"title": "API",
|
||||
"tooltip": "Details of the Angular packages, classes, interfaces, and other types.",
|
||||
"tooltip": "Details of the Angular classes and values.",
|
||||
"url": "api"
|
||||
},
|
||||
{
|
||||
"title": "CLI Commands",
|
||||
"tooltip": "Angular CLI command reference.",
|
||||
"children": [
|
||||
{
|
||||
"title": "Using the CLI",
|
||||
"tooltip": "An overview of how to use the CLI tool",
|
||||
"url": "cli"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/change-log",
|
||||
"title": "Change Log",
|
||||
|
@ -18,20 +18,19 @@
|
||||
"build-for": "yarn ~~build --configuration",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build --configuration=stable",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
"e2e": "ng e2e --no-webdriver-update",
|
||||
"presetup": "yarn --cwd .. install && yarn install --frozen-lockfile && yarn ~~check-env && yarn ~~clean-generated && yarn boilerplate:remove",
|
||||
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn extract-cli-command-docs && yarn docs",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn docs",
|
||||
"presetup-local": "yarn presetup",
|
||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||
"postsetup-local": "yarn postsetup",
|
||||
"set-opensearch-url": "node --eval \"const sh = require('shelljs'); sh.set('-e'); sh.sed('-i', /PLACEHOLDER_URL/g, process.argv[1], 'dist/assets/opensearch.xml');\"",
|
||||
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score http://localhost:4200 90\"",
|
||||
"test-pwa-score": "node scripts/test-pwa-score",
|
||||
"test-pwa-score-localhost": "run-p --race \"~~http-server dist -p 4200 --silent\" \"test-pwa-score http://localhost:4200 {1} {2}\" --",
|
||||
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
|
||||
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
|
||||
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug",
|
||||
@ -42,8 +41,7 @@
|
||||
"postcheck-env": "yarn aio-check-local",
|
||||
"payload-size": "scripts/payload.sh",
|
||||
"predocs": "yarn generate-stackblitz && yarn generate-zips",
|
||||
"docs": "yarn docs-only",
|
||||
"docs-only": "dgeni ./tools/transforms/angular.io-package",
|
||||
"docs": "dgeni ./tools/transforms/angular.io-package",
|
||||
"docs-watch": "node tools/transforms/authors-package/watchr.js",
|
||||
"docs-lint": "eslint --ignore-path=\"tools/transforms/.eslintignore\" tools/transforms",
|
||||
"docs-test": "node tools/transforms/test.js",
|
||||
@ -52,7 +50,7 @@
|
||||
"tools-lint": "tslint -c \"tools/tslint.json\" \"tools/firebase-test-utils/**/*.ts\"",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js && yarn firebase-utils-test",
|
||||
"preserve-and-sync": "yarn docs",
|
||||
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch --watch-only\" \"yarn start\"",
|
||||
"serve-and-sync": "run-p \"start\" \"docs-watch --watch-only\"",
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
|
||||
"boilerplate:test": "node tools/examples/test.js",
|
||||
@ -64,7 +62,8 @@
|
||||
"~~check-env": "node scripts/check-environment",
|
||||
"~~clean-generated": "node --eval \"require('shelljs').rm('-rf', 'src/generated')\"",
|
||||
"~~build": "ng build",
|
||||
"post~~build": "yarn build-404-page"
|
||||
"post~~build": "yarn build-404-page",
|
||||
"~~http-server": "http-server"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.9.0 <11.0.0",
|
||||
@ -72,40 +71,40 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "6.1.0-rc.3",
|
||||
"@angular/cdk": "6.0.2",
|
||||
"@angular/common": "6.1.0-rc.3",
|
||||
"@angular/core": "6.1.0-rc.3",
|
||||
"@angular/elements": "6.1.0-rc.3",
|
||||
"@angular/forms": "6.1.0-rc.3",
|
||||
"@angular/material": "6.0.2",
|
||||
"@angular/platform-browser": "6.1.0-rc.3",
|
||||
"@angular/platform-browser-dynamic": "6.1.0-rc.3",
|
||||
"@angular/router": "6.1.0-rc.3",
|
||||
"@angular/service-worker": "6.1.0-rc.3",
|
||||
"@angular/animations": "^7.0.0-rc.1",
|
||||
"@angular/cdk": "7.0.0-rc.1",
|
||||
"@angular/common": "^7.0.0-rc.1",
|
||||
"@angular/core": "^7.0.0-rc.1",
|
||||
"@angular/elements": "^7.0.0-rc.1",
|
||||
"@angular/forms": "^7.0.0-rc.1",
|
||||
"@angular/material": "7.0.0-rc.1",
|
||||
"@angular/platform-browser": "^7.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^7.0.0-rc.1",
|
||||
"@angular/router": "^7.0.0-rc.1",
|
||||
"@angular/service-worker": "^7.0.0-rc.1",
|
||||
"@webcomponents/custom-elements": "^1.2.0",
|
||||
"classlist.js": "^1.1.20150312",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "^6.3.0",
|
||||
"rxjs": "6.2.2",
|
||||
"tslib": "^1.9.0",
|
||||
"web-animations-js": "^2.2.5",
|
||||
"zone.js": "^0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.6.7",
|
||||
"@angular/cli": "^6.1.0-rc.3",
|
||||
"@angular/compiler": "6.1.0-rc.3",
|
||||
"@angular/compiler-cli": "6.1.0-rc.3",
|
||||
"@angular/language-service": "6.1.0-rc.3",
|
||||
"@angular-devkit/build-angular": "~0.9.0-rc.1",
|
||||
"@angular/cli": "^7.0.0-rc.2",
|
||||
"@angular/compiler": "^7.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^7.0.0-rc.1",
|
||||
"@angular/language-service": "^7.0.0-rc.1",
|
||||
"@types/jasmine": "^2.5.52",
|
||||
"@types/jasminewd2": "^2.0.3",
|
||||
"@types/jasminewd2": "^2.0.4",
|
||||
"@types/node": "~6.0.60",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"archiver": "^1.3.0",
|
||||
"canonical-path": "^0.0.2",
|
||||
"chalk": "^2.1.0",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "~4.2.1",
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.7",
|
||||
@ -113,7 +112,7 @@
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
"firebase-tools": "^3.2.1",
|
||||
"firebase-tools": "^5.1.1",
|
||||
"fs-extra": "^2.1.2",
|
||||
"globby": "^6.1.0",
|
||||
"hast-util-is-element": "^1.0.0",
|
||||
@ -128,17 +127,16 @@
|
||||
"jasmine-spec-reporter": "^4.1.0",
|
||||
"jasmine-ts": "^0.2.1",
|
||||
"jsdom": "^9.12.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"json5": "^1.0.1",
|
||||
"karma": "^1.7.0",
|
||||
"karma-chrome-launcher": "^2.1.1",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "^1.3.0",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"lighthouse": "^2.5.0",
|
||||
"lighthouse": "^3.2.1",
|
||||
"lodash": "^4.17.4",
|
||||
"lunr": "^2.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"protractor": "^5.2.0",
|
||||
"rehype": "^4.0.0",
|
||||
"rehype-slug": "^2.0.0",
|
||||
@ -150,7 +148,7 @@
|
||||
"tree-kill": "^1.1.0",
|
||||
"ts-node": "^3.3.0",
|
||||
"tslint": "~5.9.1",
|
||||
"typescript": "^2.9.2",
|
||||
"typescript": "^3.1.2",
|
||||
"uglify-js": "^3.0.15",
|
||||
"unist-util-filter": "^0.2.1",
|
||||
"unist-util-source": "^1.0.1",
|
||||
@ -158,7 +156,7 @@
|
||||
"unist-util-visit-parents": "^1.1.1",
|
||||
"vrsource-tslint-rules": "^5.8.2",
|
||||
"watchr": "^3.0.1",
|
||||
"webpack-cli": "^2.0.14",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"xregexp": "^4.0.0",
|
||||
"yargs": "^7.0.2"
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
"aio": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 2768,
|
||||
"main": 476338,
|
||||
"polyfills": 53922,
|
||||
"prettify": 14913
|
||||
"runtime": 3173,
|
||||
"main": 494475,
|
||||
"polyfills": 53926,
|
||||
"prettify": 14917
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ fi
|
||||
yarn payload-size
|
||||
|
||||
# Deploy to Firebase
|
||||
firebase use "$projectId" --token "$firebaseToken"
|
||||
firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken"
|
||||
yarn firebase use "$projectId" --token "$firebaseToken"
|
||||
yarn firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken"
|
||||
|
||||
# Run PWA-score tests
|
||||
yarn test-pwa-score "$deployedUrl" "$AIO_MIN_PWA_SCORE"
|
||||
|
@ -6,7 +6,7 @@ set +x -eu -o pipefail
|
||||
readonly aioDir="$(realpath $thisDir/..)"
|
||||
|
||||
readonly protractorConf="$aioDir/tests/deployment/e2e/protractor.conf.js"
|
||||
readonly minPwaScore="95"
|
||||
readonly minPwaScore="$1"
|
||||
readonly urls=(
|
||||
"https://angular.io/"
|
||||
"https://next.angular.io/"
|
||||
|
@ -11,13 +11,15 @@
|
||||
*/
|
||||
|
||||
// Imports
|
||||
const chromeLauncher = require('chrome-launcher');
|
||||
const lighthouse = require('lighthouse');
|
||||
const chromeLauncher = require('lighthouse/chrome-launcher');
|
||||
const printer = require('lighthouse/lighthouse-cli/printer');
|
||||
const config = require('lighthouse/lighthouse-core/config/default.js');
|
||||
const config = require('lighthouse/lighthouse-core/config/default-config.js');
|
||||
const logger = require('lighthouse-logger');
|
||||
|
||||
// Constants
|
||||
const CHROME_LAUNCH_OPTS = {};
|
||||
const LIGHTHOUSE_FLAGS = {logLevel: 'info'};
|
||||
const SKIPPED_HTTPS_AUDITS = ['redirects-http'];
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
|
||||
@ -26,6 +28,7 @@ const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
|
||||
CHROME_LAUNCH_OPTS.chromeFlags = ['--no-sandbox'];
|
||||
LIGHTHOUSE_FLAGS.logLevel = 'error';
|
||||
}
|
||||
|
||||
// Run
|
||||
@ -42,18 +45,20 @@ function _main(args) {
|
||||
skipHttpsAudits(config);
|
||||
}
|
||||
|
||||
launchChromeAndRunLighthouse(url, {}, config).
|
||||
logger.setLevel(LIGHTHOUSE_FLAGS.logLevel);
|
||||
|
||||
launchChromeAndRunLighthouse(url, LIGHTHOUSE_FLAGS, config).
|
||||
then(results => processResults(results, logFile)).
|
||||
then(score => evaluateScore(minScore, score)).
|
||||
catch(onError);
|
||||
}
|
||||
|
||||
function evaluateScore(expectedScore, actualScore) {
|
||||
console.log('Lighthouse PWA score:');
|
||||
console.log(` - Expected: ${expectedScore} / 100 (or higher)`);
|
||||
console.log(` - Actual: ${actualScore} / 100`);
|
||||
console.log('\nLighthouse PWA score:');
|
||||
console.log(` - Expected: ${expectedScore.toFixed(0).padStart(3)} / 100 (or higher)`);
|
||||
console.log(` - Actual: ${actualScore.toFixed(0).padStart(3)} / 100\n`);
|
||||
|
||||
if (actualScore < expectedScore) {
|
||||
if (isNaN(actualScore) || (actualScore < expectedScore)) {
|
||||
throw new Error(`PWA score is too low. (${actualScore} < ${expectedScore})`);
|
||||
}
|
||||
}
|
||||
@ -87,20 +92,30 @@ function parseInput(args) {
|
||||
}
|
||||
|
||||
function processResults(results, logFile) {
|
||||
let promise = Promise.resolve();
|
||||
const categories = results.lhr.categories;
|
||||
const report = results.report;
|
||||
|
||||
if (logFile) {
|
||||
console.log(`Saving results in '${logFile}'...`);
|
||||
console.log(`(LightHouse viewer: ${VIEWER_URL})`);
|
||||
return Promise.resolve().
|
||||
then(() => {
|
||||
if (logFile) {
|
||||
console.log(`Saving results in '${logFile}'...`);
|
||||
console.log(`(LightHouse viewer: ${VIEWER_URL})`);
|
||||
|
||||
// Remove the artifacts, which are not necessary for the report.
|
||||
// (Saves ~1,500,000 lines of formatted JSON output \o/)
|
||||
results.artifacts = undefined;
|
||||
return printer.write(report, printer.OutputMode.json, logFile);
|
||||
}
|
||||
}).
|
||||
then(() => {
|
||||
const categoryData = Object.keys(categories).map(name => categories[name]);
|
||||
const maxTitleLen = Math.max(...categoryData.map(({title}) => title.length));
|
||||
|
||||
promise = printer.write(results, 'json', logFile);
|
||||
}
|
||||
|
||||
return promise.then(() => Math.round(results.score));
|
||||
console.log('\nAudit scores:');
|
||||
categoryData.forEach(({title, score}) => {
|
||||
const paddedTitle = `${title}:`.padEnd(maxTitleLen + 1);
|
||||
const paddedScore = (score * 100).toFixed(0).padStart(3);
|
||||
console.log(` - ${paddedTitle} ${paddedScore} / 100`);
|
||||
});
|
||||
}).
|
||||
then(() => categories.pwa.score * 100);
|
||||
}
|
||||
|
||||
function skipHttpsAudits(config) {
|
||||
|
@ -164,10 +164,10 @@ describe('AppComponent', () => {
|
||||
|
||||
describe('onScroll', () => {
|
||||
it('should update `tocMaxHeight` accordingly', () => {
|
||||
expect(component.tocMaxHeight).toBeUndefined();
|
||||
|
||||
component.tocMaxHeight = '';
|
||||
component.onScroll();
|
||||
expect(component.tocMaxHeight).toBeGreaterThan(0);
|
||||
|
||||
expect(component.tocMaxHeight).toMatch(/^\d+\.\d{2}$/);
|
||||
});
|
||||
});
|
||||
|
||||
@ -654,12 +654,13 @@ describe('AppComponent', () => {
|
||||
it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => {
|
||||
setHasFloatingToc(true);
|
||||
|
||||
expect(tocContainer!.style['max-height']).toBe('');
|
||||
|
||||
component.tocMaxHeight = '100';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(tocContainer!.style['max-height']).toBe('100px');
|
||||
|
||||
component.tocMaxHeight = '200';
|
||||
fixture.detectChanges();
|
||||
expect(tocContainer!.style['max-height']).toBe('200px');
|
||||
});
|
||||
|
||||
it('should restrain scrolling inside the ToC container', () => {
|
||||
|
@ -349,12 +349,17 @@ export class AppComponent implements OnInit {
|
||||
@HostListener('window:scroll')
|
||||
onScroll() {
|
||||
if (!this.tocMaxHeightOffset) {
|
||||
// Must wait until now for mat-toolbar to be measurable.
|
||||
// Must wait until `mat-toolbar` is measurable.
|
||||
const el = this.hostElement.nativeElement as Element;
|
||||
this.tocMaxHeightOffset =
|
||||
el.querySelector('footer')!.clientHeight +
|
||||
el.querySelector('.app-toolbar')!.clientHeight +
|
||||
24; // fudge margin
|
||||
const headerEl = el.querySelector('.app-toolbar');
|
||||
const footerEl = el.querySelector('footer');
|
||||
|
||||
if (headerEl && footerEl) {
|
||||
this.tocMaxHeightOffset =
|
||||
headerEl.clientHeight +
|
||||
footerEl.clientHeight +
|
||||
24; // fudge margin
|
||||
}
|
||||
}
|
||||
|
||||
this.tocMaxHeight = (document.body.scrollHeight - window.pageYOffset - this.tocMaxHeightOffset).toFixed(2);
|
||||
|
@ -44,7 +44,7 @@ describe('ContributorService', () => {
|
||||
it('contributors observable should complete', () => {
|
||||
let completed = false;
|
||||
contribService.contributors.subscribe(undefined, undefined, () => completed = true);
|
||||
expect(true).toBe(true, 'observable completed');
|
||||
expect(completed).toBe(true, 'observable completed');
|
||||
});
|
||||
|
||||
it('should reshape the contributor json to expected result', () => {
|
||||
|
@ -44,7 +44,7 @@ describe('ResourceService', () => {
|
||||
it('categories observable should complete', () => {
|
||||
let completed = false;
|
||||
resourceService.categories.subscribe(undefined, undefined, () => completed = true);
|
||||
expect(true).toBe(true, 'observable completed');
|
||||
expect(completed).toBe(true, 'observable completed');
|
||||
});
|
||||
|
||||
it('should reshape contributors.json to sorted category array', () => {
|
||||
|
@ -138,9 +138,6 @@ describe('TocComponent', () => {
|
||||
});
|
||||
|
||||
it('should have more than 4 displayed items', () => {
|
||||
let tocList: TocItem[];
|
||||
tocService.tocList.subscribe(v => tocList = v);
|
||||
|
||||
expect(page.listItems.length).toBeGreaterThan(4);
|
||||
});
|
||||
|
||||
|
@ -180,10 +180,8 @@ describe('DocumentService', () => {
|
||||
|
||||
describe('computeMap', () => {
|
||||
it('should map the "empty" location to the correct document request', () => {
|
||||
let latestDocument: DocumentContents;
|
||||
const { docService } = getServices();
|
||||
|
||||
docService.currentDocument.subscribe(doc => latestDocument = doc);
|
||||
docService.currentDocument.subscribe();
|
||||
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'index.json');
|
||||
});
|
||||
|
@ -2,7 +2,9 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/
|
||||
import { Injector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CurrentNodes, NavigationService, NavigationViews, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import {
|
||||
CurrentNodes, NavigationNode, navigationPath, NavigationService, NavigationViews, VersionInfo,
|
||||
} from 'app/navigation/navigation.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
|
||||
@ -46,10 +48,9 @@ describe('NavigationService', () => {
|
||||
it('navigationViews observable should complete', () => {
|
||||
let completed = false;
|
||||
navService.navigationViews.subscribe(undefined, undefined, () => completed = true);
|
||||
expect(true).toBe(true, 'observable completed');
|
||||
|
||||
// Stop `$httpMock.verify()` from complaining.
|
||||
httpMock.expectOne({});
|
||||
httpMock.expectOne({method: 'get', url: navigationPath}).flush({});
|
||||
expect(completed).toBe(true, 'observable completed');
|
||||
});
|
||||
|
||||
it('should return the same object to all subscribers', () => {
|
||||
|
@ -11,7 +11,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
import { CurrentNodes, NavigationNode, NavigationResponse, NavigationViews, VersionInfo } from './navigation.model';
|
||||
export { CurrentNodes, CurrentNode, NavigationNode, NavigationResponse, NavigationViews, VersionInfo } from './navigation.model';
|
||||
|
||||
const navigationPath = CONTENT_URL_PREFIX + 'navigation.json';
|
||||
export const navigationPath = CONTENT_URL_PREFIX + 'navigation.json';
|
||||
|
||||
@Injectable()
|
||||
export class NavigationService {
|
||||
@ -95,11 +95,8 @@ export class NavigationService {
|
||||
this.location.currentPath,
|
||||
|
||||
(navMap, url) => {
|
||||
const matchSpecialUrls = /^api|^cli/.exec(url);
|
||||
if (matchSpecialUrls) {
|
||||
url = matchSpecialUrls[0];
|
||||
}
|
||||
return navMap.get(url) || { '' : { view: '', url: url, nodes: [] }};
|
||||
const urlKey = url.startsWith('api/') ? 'api' : url;
|
||||
return navMap.get(urlKey) || { '' : { view: '', url: urlKey, nodes: [] }};
|
||||
})
|
||||
.pipe(publishReplay(1));
|
||||
(currentNodes as ConnectableObservable<CurrentNodes>).connect();
|
||||
|
@ -16,7 +16,8 @@ export class CopierService {
|
||||
* and makes a selection on it.
|
||||
*/
|
||||
createFake(text: string) {
|
||||
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
||||
const docElem = document.documentElement!;
|
||||
const isRTL = docElem.getAttribute('dir') === 'rtl';
|
||||
|
||||
// Create a fake element to hold the contents to copy
|
||||
this.fakeElem = document.createElement('textarea');
|
||||
@ -34,7 +35,7 @@ export class CopierService {
|
||||
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
|
||||
|
||||
// Move element to the same position vertically
|
||||
const yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||||
const yPosition = window.pageYOffset || docElem.scrollTop;
|
||||
this.fakeElem.style.top = yPosition + 'px';
|
||||
|
||||
this.fakeElem.setAttribute('readonly', '');
|
||||
|
@ -91,7 +91,7 @@
|
||||
|
||||
<noscript>
|
||||
<div class="background-sky hero"></div>
|
||||
<section id="intro">
|
||||
<section id="intro" style="text-shadow: 1px 1px #1976d2;">
|
||||
<div class="hero-logo">
|
||||
<img src="assets/images/logos/angular/angular.svg" width="250" height="250">
|
||||
</div>
|
||||
@ -99,7 +99,7 @@
|
||||
<div class="hero-headline">One framework.<br>Mobile & desktop.</div>
|
||||
</div>
|
||||
</section>
|
||||
<h2 style="color: red; text-align: center; margin-top: -50px;">
|
||||
<h2 style="color: red; margin-top: 40px; position: relative; text-align: center; text-shadow: 1px 1px #fafafa;">
|
||||
<b><i>This website requires JavaScript.</i></b>
|
||||
</h2>
|
||||
</noscript>
|
||||
|
@ -135,6 +135,17 @@ th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p > code, li > code, td > code, th > code {
|
||||
font-family: $code-font;
|
||||
font-size: 85%;
|
||||
color: $darkgray;
|
||||
letter-spacing: 0;
|
||||
line-height: 1;
|
||||
padding: 2px 0;
|
||||
background-color: $backgroundgray;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $code-font;
|
||||
font-size: 90%;
|
||||
|
@ -115,6 +115,7 @@ button.vertical-menu-item {
|
||||
}
|
||||
|
||||
.heading-children.collapsed {
|
||||
overflow: hidden; // Needed to prevent unnecessary sidenav scrollbar.
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
max-height: 1px; // Must have measurement to transition height.
|
||||
|
@ -31,6 +31,11 @@
|
||||
}
|
||||
|
||||
.method-table, .option-table, .list-table {
|
||||
td > code {
|
||||
background-color: inherit;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.with-github-links {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@ -66,23 +71,6 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.short-description {
|
||||
margin: 6px 0 0 10px;
|
||||
}
|
||||
|
||||
.properties-table {
|
||||
font-size: 14px;
|
||||
|
||||
thead th {
|
||||
&:nth-child(1) {
|
||||
width: 20%;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.parameters-table {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
@ -104,7 +92,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.from-constructor, .read-only-property {
|
||||
.from-constructor, .read-only-property, .write-only-property {
|
||||
font-style: italic;
|
||||
color: $blue;
|
||||
}
|
||||
@ -113,6 +101,29 @@
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.selector-list, .inherited-members-list {
|
||||
ul {
|
||||
padding: 0;
|
||||
li {
|
||||
list-style: none;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selector-list {
|
||||
li, a {
|
||||
font-weight: bold;
|
||||
i {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.deprecated-api-item {
|
||||
|
@ -1,7 +0,0 @@
|
||||
.cli-name, .cli-default {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cli-option-syntax {
|
||||
white-space: pre;
|
||||
}
|
@ -137,6 +137,7 @@ aio-code pre {
|
||||
.sidenav-content code a {
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/* PRETTY PRINTING STYLES for prettify.js. */
|
||||
|
@ -8,7 +8,6 @@
|
||||
@import 'buttons';
|
||||
@import 'callout';
|
||||
@import 'card';
|
||||
@import 'cli-pages';
|
||||
@import 'code';
|
||||
@import 'contribute';
|
||||
@import 'contributor';
|
||||
|
@ -1,5 +1,9 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
// Reflect.metadata polyfill is only needed in the JIT mode which we use only for unit tests
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
@ -9,10 +13,6 @@ import {
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// Reflect.metadata polyfill is only needed in the JIT mode which we use only for unit tests
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
|
@ -8,7 +8,19 @@ describe('Api pages', function() {
|
||||
|
||||
it('should show direct and indirect subclasses of a class', () => {
|
||||
const page = new ApiPage('api/forms/AbstractControlDirective');
|
||||
expect(page.getDescendants('class')).toEqual(['ControlContainer', 'AbstractFormGroupDirective', 'NgControl']);
|
||||
expect(page.getDescendants('class')).toEqual([
|
||||
'ControlContainer',
|
||||
'AbstractFormGroupDirective',
|
||||
'NgModelGroup',
|
||||
'FormGroupName',
|
||||
'NgForm',
|
||||
'FormGroupDirective',
|
||||
'FormArrayName',
|
||||
'NgControl',
|
||||
'NgModel',
|
||||
'FormControlDirective',
|
||||
'FormControlName'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should show child interfaces that extend an interface', () => {
|
||||
@ -26,14 +38,9 @@ describe('Api pages', function() {
|
||||
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
|
||||
});
|
||||
|
||||
it('should show readonly properties as getters', () => {
|
||||
const page = new ApiPage('api/common/http/HttpRequest');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T | null');
|
||||
});
|
||||
|
||||
it('should not show parenthesis for getters', () => {
|
||||
const page = new ApiPage('api/core/NgModuleRef');
|
||||
expect(page.getOverview('class').getText()).toContain('get injector: Injector');
|
||||
expect(page.getOverview('class').getText()).toContain('injector: Injector');
|
||||
});
|
||||
|
||||
it('should show both type and initializer if set', () => {
|
||||
|
@ -35,7 +35,7 @@
|
||||
"angular-in-memory-web-api": "^0.6.0",
|
||||
"core-js": "^2.5.4",
|
||||
"express": "^4.14.1",
|
||||
"rxjs": "^6.3.0",
|
||||
"rxjs": "^6.0.0",
|
||||
"systemjs": "0.19.39",
|
||||
"web-animations-js": "^2.3.1",
|
||||
"zone.js": "^0.8.24"
|
||||
@ -53,7 +53,7 @@
|
||||
"@types/angular-route": "^1.3.5",
|
||||
"@types/express": "^4.0.35",
|
||||
"@types/jasmine": "~2.8.0",
|
||||
"@types/jasminewd2": "^2.0.3",
|
||||
"@types/jasminewd2": "^2.0.4",
|
||||
"@types/jquery": "^3.3.4",
|
||||
"@types/node": "^6.0.45",
|
||||
"canonical-path": "0.0.2",
|
||||
|
@ -341,10 +341,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.4.tgz#5528fb5e53f1b27594f81f18debb7eab8dc532cb"
|
||||
integrity sha512-sVK48Oni/wTt4J0MBvZs2vjObZTAL8qGt2pHPEuj5OVXEkN5983OcHvGqBOR4rvLneFBOYV+RFp6r71fRvjoNA==
|
||||
|
||||
"@types/jasminewd2@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.3.tgz#0d2886b0cbdae4c0eeba55e30792f584bf040a95"
|
||||
integrity sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==
|
||||
"@types/jasminewd2@^2.0.4":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.5.tgz#0910db4bd50202dea9fe6ec37517b0ae6a01a9e6"
|
||||
integrity sha512-1awkm/O4pQCR9hI2F80HmIOda/L+ogkSL8Arj1k00eue5VLY5ooewhSOyF/cUJE0S+/34uD5EYY3zmd6fu2OCA==
|
||||
dependencies:
|
||||
"@types/jasmine" "*"
|
||||
|
||||
@ -8366,13 +8366,6 @@ rxjs@^6.0.0:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
rxjs@^6.3.0:
|
||||
version "6.3.3"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55"
|
||||
integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
|
||||
@ -9580,16 +9573,11 @@ tsickle@^0.27.2:
|
||||
source-map "^0.6.0"
|
||||
source-map-support "^0.5.0"
|
||||
|
||||
tslib@^1.8.0, tslib@^1.8.1:
|
||||
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
|
||||
integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==
|
||||
|
||||
tslib@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
|
||||
|
||||
tslint@^5.9.1:
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae"
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs-extra');
|
||||
const lockfile = require('@yarnpkg/lockfile');
|
||||
const path = require('canonical-path');
|
||||
const semver = require('semver');
|
||||
const shelljs = require('shelljs');
|
||||
const yargs = require('yargs');
|
||||
|
||||
const PACKAGE_JSON = 'package.json';
|
||||
const YARN_LOCK = 'yarn.lock';
|
||||
const LOCAL_MARKER_PATH = 'node_modules/_local_.json';
|
||||
const PACKAGE_JSON_REGEX = /^[^/]+\/package\.json$/;
|
||||
|
||||
@ -62,8 +65,10 @@ class NgPackagesInstaller {
|
||||
* contents and acts as an indicator that dependencies have been overridden.
|
||||
*/
|
||||
installLocalDependencies() {
|
||||
if (this._checkLocalMarker() !== true || this.force) {
|
||||
if (this.force || !this._checkLocalMarker()) {
|
||||
const pathToPackageConfig = path.resolve(this.projectDir, PACKAGE_JSON);
|
||||
const pathToLockfile = path.resolve(this.projectDir, YARN_LOCK);
|
||||
const parsedLockfile = this._parseLockfile(pathToLockfile);
|
||||
const packages = this._getDistPackages();
|
||||
|
||||
try {
|
||||
@ -88,17 +93,17 @@ class NgPackagesInstaller {
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(tmpConfig));
|
||||
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(tmpConfig, null, 2));
|
||||
});
|
||||
|
||||
const packageConfigFile = fs.readFileSync(pathToPackageConfig);
|
||||
const packageConfigFile = fs.readFileSync(pathToPackageConfig, 'utf8');
|
||||
const packageConfig = JSON.parse(packageConfigFile);
|
||||
|
||||
const [dependencies, peers] = this._collectDependencies(packageConfig.dependencies || {}, packages);
|
||||
const [devDependencies, devPeers] = this._collectDependencies(packageConfig.devDependencies || {}, packages);
|
||||
|
||||
this._assignPeerDependencies(peers, dependencies, devDependencies);
|
||||
this._assignPeerDependencies(devPeers, dependencies, devDependencies);
|
||||
this._assignPeerDependencies(peers, dependencies, devDependencies, parsedLockfile);
|
||||
this._assignPeerDependencies(devPeers, dependencies, devDependencies, parsedLockfile);
|
||||
|
||||
const localPackageConfig = Object.assign(Object.create(null), packageConfig, { dependencies, devDependencies });
|
||||
localPackageConfig.__angular = { local: true };
|
||||
@ -107,7 +112,7 @@ class NgPackagesInstaller {
|
||||
try {
|
||||
this._log(`Writing temporary local ${PACKAGE_JSON} to ${pathToPackageConfig}`);
|
||||
fs.writeFileSync(pathToPackageConfig, localPackageConfigJson);
|
||||
this._installDeps('--no-lockfile', '--check-files');
|
||||
this._installDeps('--pure-lockfile', '--check-files');
|
||||
this._setLocalMarker(localPackageConfigJson);
|
||||
} finally {
|
||||
this._log(`Restoring original ${PACKAGE_JSON} to ${pathToPackageConfig}`);
|
||||
@ -118,7 +123,7 @@ class NgPackagesInstaller {
|
||||
this._log(`Restoring original ${PACKAGE_JSON} for local Angular packages.`);
|
||||
Object.keys(packages).forEach(key => {
|
||||
const pkg = packages[key];
|
||||
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(pkg.config));
|
||||
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(pkg.config, null, 2));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -134,15 +139,23 @@ class NgPackagesInstaller {
|
||||
|
||||
// Protected helpers
|
||||
|
||||
_assignPeerDependencies(peerDependencies, dependencies, devDependencies) {
|
||||
_assignPeerDependencies(peerDependencies, dependencies, devDependencies, parsedLockfile) {
|
||||
Object.keys(peerDependencies).forEach(key => {
|
||||
const peerDepRange = peerDependencies[key];
|
||||
|
||||
// Ignore peerDependencies whose range is already satisfied by current version in lockfile.
|
||||
const originalRange = dependencies[key] || devDependencies[key];
|
||||
const lockfileVersion = originalRange && parsedLockfile[`${key}@${originalRange}`].version;
|
||||
|
||||
if (lockfileVersion && semver.satisfies(lockfileVersion, peerDepRange)) return;
|
||||
|
||||
// If there is already an equivalent dependency then override it - otherwise assign/override the devDependency
|
||||
if (dependencies[key]) {
|
||||
this._log(`Overriding dependency with peerDependency: ${key}: ${peerDependencies[key]}`);
|
||||
dependencies[key] = peerDependencies[key];
|
||||
this._log(`Overriding dependency with peerDependency: ${key}: ${peerDepRange}`);
|
||||
dependencies[key] = peerDepRange;
|
||||
} else {
|
||||
this._log(`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ${key}: ${peerDependencies[key]}`);
|
||||
devDependencies[key] = peerDependencies[key];
|
||||
this._log(`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ${key}: ${peerDepRange}`);
|
||||
devDependencies[key] = peerDepRange;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -166,11 +179,6 @@ class NgPackagesInstaller {
|
||||
}
|
||||
});
|
||||
|
||||
// FIXME: Temporarily use RxJS from root `node_modules/`.
|
||||
if (peerDependencies.rxjs) {
|
||||
peerDependencies.rxjs = `file:${ANGULAR_ROOT_DIR}/node_modules/rxjs`;
|
||||
}
|
||||
|
||||
return [mergedDependencies, peerDependencies];
|
||||
}
|
||||
|
||||
@ -226,6 +234,20 @@ class NgPackagesInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and return a `yarn.lock` file.
|
||||
*/
|
||||
_parseLockfile(lockfilePath) {
|
||||
const lockfileContent = fs.readFileSync(lockfilePath, 'utf8');
|
||||
const parsed = lockfile.parse(lockfileContent);
|
||||
|
||||
if (parsed.type !== 'success') {
|
||||
throw new Error(`[${NgPackagesInstaller.name}]: Error parsing lockfile '${lockfilePath}' (result type: ${parsed.type}).`);
|
||||
}
|
||||
|
||||
return parsed.object;
|
||||
}
|
||||
|
||||
_printWarning() {
|
||||
const relativeScriptPath = path.relative('.', __filename.replace(/\.js$/, ''));
|
||||
const absoluteProjectDir = path.resolve(this.projectDir);
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const lockfile = require('@yarnpkg/lockfile');
|
||||
const path = require('canonical-path');
|
||||
const shelljs = require('shelljs');
|
||||
|
||||
@ -11,6 +12,7 @@ describe('NgPackagesInstaller', () => {
|
||||
const absoluteRootDir = path.resolve(rootDir);
|
||||
const nodeModulesDir = path.resolve(absoluteRootDir, 'node_modules');
|
||||
const packageJsonPath = path.resolve(absoluteRootDir, 'package.json');
|
||||
const yarnLockPath = path.resolve(absoluteRootDir, 'yarn.lock');
|
||||
const packagesDir = path.resolve(path.resolve(__dirname, '../../../dist/packages-dist'));
|
||||
const toolsDir = path.resolve(path.resolve(__dirname, '../../../dist/tools/@angular'));
|
||||
let installer;
|
||||
@ -52,13 +54,26 @@ describe('NgPackagesInstaller', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(installer, '_checkLocalMarker');
|
||||
spyOn(installer, '_installDeps');
|
||||
spyOn(installer, '_setLocalMarker');
|
||||
|
||||
spyOn(installer, '_parseLockfile').and.returnValue({
|
||||
'rxjs@^6.3.0': {version: '6.3.3'},
|
||||
'zone.js@^0.8.26': {version: '0.8.27'}
|
||||
});
|
||||
|
||||
// These are the packages that are "found" in the dist directory
|
||||
dummyNgPackages = {
|
||||
'@angular/core': {
|
||||
parentDir: packagesDir,
|
||||
packageJsonPath: `${packagesDir}/core/package.json`,
|
||||
config: { peerDependencies: { 'some-package': '5.0.1' } }
|
||||
config: {
|
||||
peerDependencies: {
|
||||
'rxjs': '^6.4.0',
|
||||
'some-package': '5.0.1',
|
||||
'zone.js': '~0.8.26'
|
||||
}
|
||||
}
|
||||
},
|
||||
'@angular/common': {
|
||||
parentDir: packagesDir,
|
||||
@ -93,10 +108,12 @@ describe('NgPackagesInstaller', () => {
|
||||
dummyPackage = {
|
||||
dependencies: {
|
||||
'@angular/core': '4.4.1',
|
||||
'@angular/common': '4.4.1'
|
||||
'@angular/common': '4.4.1',
|
||||
rxjs: '^6.3.0'
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/compiler-cli': '4.4.1'
|
||||
'@angular/compiler-cli': '4.4.1',
|
||||
'zone.js': '^0.8.26'
|
||||
}
|
||||
};
|
||||
dummyPackageJson = JSON.stringify(dummyPackage);
|
||||
@ -104,14 +121,23 @@ describe('NgPackagesInstaller', () => {
|
||||
|
||||
// This is the package.json that is temporarily written to the "test" folder
|
||||
// Note that the Angular (dev)dependencies have been modified to use a "file:" path
|
||||
// And that the peerDependencies from `dummyNgPackages` have been added as (dev)dependencies.
|
||||
// And that the peerDependencies from `dummyNgPackages` have been updated or added as
|
||||
// (dev)dependencies (unless the current version in lockfile satisfies semver).
|
||||
//
|
||||
// For example, `zone.js@0.8.27` (from lockfile) satisfies `zone.js@~0.8.26` (from
|
||||
// `@angular/core`), thus `zone.js: ^0.8.26` (from original `package.json`) is retained.
|
||||
// In contrast, `rxjs@6.3.3` (from lockfile) does not satisfy `rxjs@^6.4.0 (from
|
||||
// `@angular/core`), thus `rxjs: ^6.3.0` (from original `package.json`) is replaced with
|
||||
// `rxjs: ^6.4.0` (from `@angular/core`).
|
||||
expectedModifiedPackage = {
|
||||
dependencies: {
|
||||
'@angular/core': `file:${packagesDir}/core`,
|
||||
'@angular/common': `file:${packagesDir}/common`
|
||||
'@angular/common': `file:${packagesDir}/common`,
|
||||
'rxjs': '^6.4.0'
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/compiler-cli': `file:${toolsDir}/compiler-cli`,
|
||||
'zone.js': '^0.8.26',
|
||||
'some-package': '5.0.1',
|
||||
typescript: '^2.4.2'
|
||||
},
|
||||
@ -121,12 +147,20 @@ describe('NgPackagesInstaller', () => {
|
||||
});
|
||||
|
||||
describe('when there is a local package marker', () => {
|
||||
beforeEach(() => installer._checkLocalMarker.and.returnValue(true));
|
||||
|
||||
it('should not continue processing', () => {
|
||||
installer._checkLocalMarker.and.returnValue(true);
|
||||
installer.installLocalDependencies();
|
||||
expect(installer._checkLocalMarker).toHaveBeenCalled();
|
||||
expect(installer._getDistPackages).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should continue processing (without checking for local marker) if `force` is true', () => {
|
||||
installer.force = true;
|
||||
installer.installLocalDependencies();
|
||||
expect(installer._checkLocalMarker).not.toHaveBeenCalled();
|
||||
expect(installer._getDistPackages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no local package marker', () => {
|
||||
@ -135,14 +169,14 @@ describe('NgPackagesInstaller', () => {
|
||||
beforeEach(() => {
|
||||
log = [];
|
||||
fs.writeFileSync.and.callFake((filePath, contents) => filePath === packageJsonPath && log.push(`writeFile: ${contents}`));
|
||||
spyOn(installer, '_installDeps').and.callFake(() => log.push('installDeps:'));
|
||||
spyOn(installer, '_setLocalMarker');
|
||||
installer._installDeps.and.callFake((...args) => log.push(`installDeps: ${args.join(' ')}`));
|
||||
installer._checkLocalMarker.and.returnValue(false);
|
||||
installer.installLocalDependencies();
|
||||
});
|
||||
|
||||
it('should get the dist packages', () => {
|
||||
it('should parse the lockfile and get the dist packages', () => {
|
||||
expect(installer._checkLocalMarker).toHaveBeenCalled();
|
||||
expect(installer._parseLockfile).toHaveBeenCalledWith(yarnLockPath);
|
||||
expect(installer._getDistPackages).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -150,31 +184,32 @@ describe('NgPackagesInstaller', () => {
|
||||
const pkgJsonFor = pkgName => dummyNgPackages[`@angular/${pkgName}`].packageJsonPath;
|
||||
const pkgConfigFor = pkgName => copyJsonObj(dummyNgPackages[`@angular/${pkgName}`].config);
|
||||
const overwriteConfigFor = (pkgName, newProps) => Object.assign(pkgConfigFor(pkgName), newProps);
|
||||
const stringifyConfig = config => JSON.stringify(config, null, 2);
|
||||
|
||||
const allArgs = fs.writeFileSync.calls.allArgs();
|
||||
const firstFiveArgs = allArgs.slice(0, 5);
|
||||
const lastFiveArgs = allArgs.slice(-5);
|
||||
|
||||
expect(firstFiveArgs).toEqual([
|
||||
[pkgJsonFor('core'), JSON.stringify(overwriteConfigFor('core', {private: true}))],
|
||||
[pkgJsonFor('common'), JSON.stringify(overwriteConfigFor('common', {private: true}))],
|
||||
[pkgJsonFor('compiler'), JSON.stringify(overwriteConfigFor('compiler', {private: true}))],
|
||||
[pkgJsonFor('compiler-cli'), JSON.stringify(overwriteConfigFor('compiler-cli', {
|
||||
[pkgJsonFor('core'), stringifyConfig(overwriteConfigFor('core', {private: true}))],
|
||||
[pkgJsonFor('common'), stringifyConfig(overwriteConfigFor('common', {private: true}))],
|
||||
[pkgJsonFor('compiler'), stringifyConfig(overwriteConfigFor('compiler', {private: true}))],
|
||||
[pkgJsonFor('compiler-cli'), stringifyConfig(overwriteConfigFor('compiler-cli', {
|
||||
private: true,
|
||||
dependencies: { '@angular/tsc-wrapped': `file:${toolsDir}/tsc-wrapped` }
|
||||
}))],
|
||||
[pkgJsonFor('tsc-wrapped'), JSON.stringify(overwriteConfigFor('tsc-wrapped', {
|
||||
[pkgJsonFor('tsc-wrapped'), stringifyConfig(overwriteConfigFor('tsc-wrapped', {
|
||||
private: true,
|
||||
devDependencies: { '@angular/common': `file:${packagesDir}/common` }
|
||||
}))],
|
||||
]);
|
||||
|
||||
expect(lastFiveArgs).toEqual(['core', 'common', 'compiler', 'compiler-cli', 'tsc-wrapped']
|
||||
.map(pkgName => [pkgJsonFor(pkgName), JSON.stringify(pkgConfigFor(pkgName))]));
|
||||
.map(pkgName => [pkgJsonFor(pkgName), stringifyConfig(pkgConfigFor(pkgName))]));
|
||||
});
|
||||
|
||||
it('should load the package.json', () => {
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath);
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath, 'utf8');
|
||||
});
|
||||
|
||||
it('should overwrite package.json with modified config', () => {
|
||||
@ -188,7 +223,7 @@ describe('NgPackagesInstaller', () => {
|
||||
it('should overwrite package.json, then install deps, then restore original package.json', () => {
|
||||
expect(log).toEqual([
|
||||
`writeFile: ${expectedModifiedPackageJson}`,
|
||||
`installDeps:`,
|
||||
`installDeps: --pure-lockfile --check-files`,
|
||||
`writeFile: ${dummyPackageJson}`
|
||||
]);
|
||||
});
|
||||
@ -264,6 +299,42 @@ describe('NgPackagesInstaller', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_parseLockfile()', () => {
|
||||
let originalLockfileParseDescriptor;
|
||||
|
||||
beforeEach(() => {
|
||||
// Workaround for `lockfile.parse()` being non-writable.
|
||||
let parse = lockfile.parse;
|
||||
originalLockfileParseDescriptor = Object.getOwnPropertyDescriptor(lockfile, 'parse');
|
||||
Object.defineProperty(lockfile, 'parse', {
|
||||
get() { return parse; },
|
||||
set(newParse) { parse = newParse; },
|
||||
});
|
||||
|
||||
fs.readFileSync.and.returnValue('mock content');
|
||||
spyOn(lockfile, 'parse').and.returnValue({type: 'success', object: {foo: {version: 'bar'}}});
|
||||
});
|
||||
|
||||
afterEach(() => Object.defineProperty(lockfile, 'parse', originalLockfileParseDescriptor));
|
||||
|
||||
it('should parse the specified lockfile', () => {
|
||||
installer._parseLockfile('/foo/bar/yarn.lock');
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith('/foo/bar/yarn.lock', 'utf8');
|
||||
expect(lockfile.parse).toHaveBeenCalledWith('mock content');
|
||||
});
|
||||
|
||||
it('should throw if parsing the lockfile fails', () => {
|
||||
lockfile.parse.and.returnValue({type: 'not success'});
|
||||
expect(() => installer._parseLockfile('/foo/bar/yarn.lock')).toThrowError(
|
||||
'[NgPackagesInstaller]: Error parsing lockfile \'/foo/bar/yarn.lock\' (result type: not success).');
|
||||
});
|
||||
|
||||
it('should return the parsed lockfile content as an object', () => {
|
||||
const parsed = installer._parseLockfile('/foo/bar/yarn.lock');
|
||||
expect(parsed).toEqual({foo: {version: 'bar'}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_printWarning()', () => {
|
||||
it('should mention the message passed in the warning', () => {
|
||||
installer._printWarning();
|
||||
|
@ -21,64 +21,63 @@ module.exports = function matchUpDirectiveDecorators() {
|
||||
if (doc.docType === 'directive') {
|
||||
|
||||
doc.selector = stripQuotes(doc.directiveOptions.selector);
|
||||
doc.selectorArray = doc.selector ? doc.selector.split(',') : [];
|
||||
doc.exportAs = stripQuotes(doc.directiveOptions.exportAs);
|
||||
doc.exportAsArray = doc.exportAs ? doc.exportAs.split(',') : [];
|
||||
|
||||
doc.inputs = getBindingInfo(doc.directiveOptions.inputs, doc.members, 'Input');
|
||||
doc.outputs = getBindingInfo(doc.directiveOptions.outputs, doc.members, 'Output');
|
||||
attachBindingInfo(doc.directiveOptions.inputs, doc.members, 'Input');
|
||||
attachBindingInfo(doc.directiveOptions.outputs, doc.members, 'Output');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function getBindingInfo(directiveBindings, members, bindingType) {
|
||||
function attachBindingInfo(directiveBindings, members, bindingType) {
|
||||
const bindings = {};
|
||||
|
||||
// Parse the bindings from the directive decorator
|
||||
if (directiveBindings) {
|
||||
directiveBindings.forEach(function(binding) {
|
||||
const bindingInfo = parseBinding(binding);
|
||||
bindings[bindingInfo.propertyName] = bindingInfo;
|
||||
});
|
||||
}
|
||||
|
||||
if (members) {
|
||||
// Parse the bindings from the directive decorator
|
||||
if (directiveBindings) {
|
||||
directiveBindings.forEach(function(binding) {
|
||||
const bindingInfo = parseBinding(bindingType, binding);
|
||||
bindings[bindingInfo.propertyName] = bindingInfo;
|
||||
});
|
||||
}
|
||||
|
||||
members.forEach(function(member) {
|
||||
if (member.decorators) {
|
||||
// Search for members with binding decorators
|
||||
member.decorators.forEach(function(decorator) {
|
||||
if (decorator.name === bindingType) {
|
||||
bindings[member.name] = createBindingInfo(member.name, decorator.arguments[0] || member.name);
|
||||
bindings[member.name] = createBindingInfo(bindingType, member.name, decorator.arguments[0] || member.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Now ensure that any bindings have the associated member attached
|
||||
// Note that this binding could have come from the directive decorator
|
||||
// Attach binding info to the member
|
||||
// Note: this may have come from the `@Directive` decorator or from a property decorator such as `@Input`.
|
||||
if (bindings[member.name]) {
|
||||
bindings[member.name].memberDoc = member;
|
||||
member.boundTo = bindings[member.name];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Convert the map back to an array
|
||||
return Object.keys(bindings).map(function(key) { return bindings[key]; });
|
||||
}
|
||||
|
||||
function stripQuotes(value) {
|
||||
return (typeof(value) === 'string') ? value.trim().replace(/^(['"])(.*)\1$/, '$2') : value;
|
||||
}
|
||||
|
||||
function parseBinding(option) {
|
||||
function parseBinding(bindingType, option) {
|
||||
// Directive decorator bindings are of the form: "propName : bindingName" (bindingName is optional)
|
||||
const optionPair = option.split(':');
|
||||
const propertyName = optionPair[0].trim();
|
||||
const bindingName = (optionPair[1] || '').trim() || propertyName;
|
||||
return createBindingInfo(propertyName, bindingName);
|
||||
return createBindingInfo(bindingType, propertyName, bindingName);
|
||||
}
|
||||
|
||||
function createBindingInfo(propertyName, bindingName) {
|
||||
function createBindingInfo(bindingType, propertyName, bindingName) {
|
||||
return {
|
||||
type: bindingType,
|
||||
propertyName: stripQuotes(propertyName),
|
||||
bindingName: stripQuotes(bindingName)
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ describe('matchUpDirectiveDecorators processor', () => {
|
||||
});
|
||||
|
||||
it('should extract inputs and outputs from the directive decorator', () => {
|
||||
const docs = [{
|
||||
const doc = {
|
||||
docType: 'directive',
|
||||
directiveOptions: {
|
||||
inputs: ['in1:in2', 'in3', ' in4:in5 ', ' in6 '],
|
||||
@ -70,28 +70,30 @@ describe('matchUpDirectiveDecorators processor', () => {
|
||||
{ name: 'in3' },
|
||||
{ name: 'in4' },
|
||||
{ name: 'in6' },
|
||||
{ name: 'prop1' },
|
||||
{ name: 'out1' },
|
||||
{ name: 'out2' },
|
||||
{ name: 'out4' }
|
||||
{ name: 'out4' },
|
||||
{ name: 'prop2' },
|
||||
]
|
||||
}];
|
||||
processorFactory().$process(docs);
|
||||
expect(docs[0].inputs).toEqual([
|
||||
{ propertyName: 'in1', bindingName: 'in2', memberDoc: docs[0].members[0] },
|
||||
{ propertyName: 'in3', bindingName: 'in3', memberDoc: docs[0].members[1] },
|
||||
{ propertyName: 'in4', bindingName: 'in5', memberDoc: docs[0].members[2] },
|
||||
{ propertyName: 'in6', bindingName: 'in6', memberDoc: docs[0].members[3] }
|
||||
]);
|
||||
};
|
||||
|
||||
expect(docs[0].outputs).toEqual([
|
||||
{ propertyName: 'out1', bindingName: 'out1', memberDoc: docs[0].members[4] },
|
||||
{ propertyName: 'out2', bindingName: 'out3', memberDoc: docs[0].members[5] },
|
||||
{ propertyName: 'out4', bindingName: 'out4', memberDoc: docs[0].members[6] }
|
||||
processorFactory().$process([doc]);
|
||||
expect(doc.members).toEqual([
|
||||
{ name: 'in1', boundTo: { type: 'Input', propertyName: 'in1', bindingName: 'in2' } },
|
||||
{ name: 'in3', boundTo: { type: 'Input', propertyName: 'in3', bindingName: 'in3' } },
|
||||
{ name: 'in4', boundTo: { type: 'Input', propertyName: 'in4', bindingName: 'in5' } },
|
||||
{ name: 'in6', boundTo: { type: 'Input', propertyName: 'in6', bindingName: 'in6' }},
|
||||
{ name: 'prop1' },
|
||||
{ name: 'out1', boundTo: { type: 'Output', propertyName: 'out1', bindingName: 'out1' } },
|
||||
{ name: 'out2', boundTo: { type: 'Output', propertyName: 'out2', bindingName: 'out3' } },
|
||||
{ name: 'out4', boundTo: { type: 'Output', propertyName: 'out4', bindingName: 'out4' }},
|
||||
{ name: 'prop2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should extract inputs and outputs from decorated properties', () => {
|
||||
const docs = [{
|
||||
const doc = {
|
||||
docType: 'directive',
|
||||
directiveOptions: {},
|
||||
members: [
|
||||
@ -100,21 +102,18 @@ describe('matchUpDirectiveDecorators processor', () => {
|
||||
{ name: 'c1', decorators: [{ name: 'Input', arguments: [] }] },
|
||||
{ name: 'd1', decorators: [{ name: 'Output', arguments: [] }] },
|
||||
]
|
||||
}];
|
||||
processorFactory().$process(docs);
|
||||
expect(docs[0].inputs).toEqual([
|
||||
{ propertyName: 'a1', bindingName: 'a2', memberDoc: docs[0].members[0] },
|
||||
{ propertyName: 'c1', bindingName: 'c1', memberDoc: docs[0].members[2] }
|
||||
]);
|
||||
|
||||
expect(docs[0].outputs).toEqual([
|
||||
{ propertyName: 'b1', bindingName: 'b2', memberDoc: docs[0].members[1] },
|
||||
{ propertyName: 'd1', bindingName: 'd1', memberDoc: docs[0].members[3] }
|
||||
};
|
||||
processorFactory().$process([doc]);
|
||||
expect(doc.members).toEqual([
|
||||
{ name: 'a1', decorators: [{ name: 'Input', arguments: ['a2'] }], boundTo: { type: 'Input', propertyName: 'a1', bindingName: 'a2' } },
|
||||
{ name: 'b1', decorators: [{ name: 'Output', arguments: ['b2'] }], boundTo: { type: 'Output', propertyName: 'b1', bindingName: 'b2' } },
|
||||
{ name: 'c1', decorators: [{ name: 'Input', arguments: [] }], boundTo: { type: 'Input', propertyName: 'c1', bindingName: 'c1' } },
|
||||
{ name: 'd1', decorators: [{ name: 'Output', arguments: [] }], boundTo: { type: 'Output', propertyName: 'd1', bindingName: 'd1' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should merge directive inputs/outputs with decorator property inputs/outputs', () => {
|
||||
const docs = [{
|
||||
const doc = {
|
||||
docType: 'directive',
|
||||
directiveOptions: {
|
||||
inputs: ['a1:a2'],
|
||||
@ -126,16 +125,13 @@ describe('matchUpDirectiveDecorators processor', () => {
|
||||
{ name: 'b1' },
|
||||
{ name: 'b3', decorators: [{ name: 'Output', arguments: ['b4'] }] },
|
||||
]
|
||||
}];
|
||||
processorFactory().$process(docs);
|
||||
expect(docs[0].inputs).toEqual([
|
||||
{ propertyName: 'a1', bindingName: 'a2', memberDoc: docs[0].members[0] },
|
||||
{ propertyName: 'a3', bindingName: 'a4', memberDoc: docs[0].members[1] }
|
||||
]);
|
||||
|
||||
expect(docs[0].outputs).toEqual([
|
||||
{ propertyName: 'b1', bindingName: 'b2', memberDoc: docs[0].members[2] },
|
||||
{ propertyName: 'b3', bindingName: 'b4', memberDoc: docs[0].members[3] }
|
||||
};
|
||||
processorFactory().$process([doc]);
|
||||
expect(doc.members).toEqual([
|
||||
{ name: 'a1', boundTo: { type: 'Input', propertyName: 'a1', bindingName: 'a2' } },
|
||||
{ name: 'a3', boundTo: { type: 'Input', propertyName: 'a3', bindingName: 'a4' }, decorators: [{ name: 'Input', arguments: ['a4'] }] },
|
||||
{ name: 'b1', boundTo: { type: 'Output', propertyName: 'b1', bindingName: 'b2' } },
|
||||
{ name: 'b3', boundTo: { type: 'Output', propertyName: 'b3', bindingName: 'b4' }, decorators: [{ name: 'Output', arguments: ['b4'] }] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
3
aio/tools/transforms/angular-api-package/tag-defs/selectors.js
vendored
Normal file
3
aio/tools/transforms/angular-api-package/tag-defs/selectors.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function() {
|
||||
return {name: 'selectors'};
|
||||
};
|
@ -3,7 +3,12 @@ module.exports = function filterBy() {
|
||||
name: 'filterByPropertyValue',
|
||||
process: function(list, property, value) {
|
||||
if (!list) return list;
|
||||
return list.filter(item => item[property] === value);
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
return list.filter(item => values.some(value => compare(item[property], value)));
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function compare(actual, expected) {
|
||||
return expected instanceof(RegExp) ? expected.test(actual) : actual === expected;
|
||||
}
|
@ -7,8 +7,19 @@ describe('filterByPropertyValue filter', () => {
|
||||
|
||||
it('should be called "filterByPropertyValue"', function() { expect(filter.name).toEqual('filterByPropertyValue'); });
|
||||
|
||||
it('should filter out items that do not match the given property value', function() {
|
||||
it('should filter out items that do not match the given property string value', function() {
|
||||
expect(filter.process([{ a: 1 }, { a: 2 }, { b: 1 }, { a: 1, b: 2 }, { a: null }, { a: undefined }], 'a', 1))
|
||||
.toEqual([{ a: 1 }, { a: 1, b: 2 }]);
|
||||
});
|
||||
|
||||
|
||||
it('should filter out items that do not match the given property regex value', function() {
|
||||
expect(filter.process([{ a: '1' }, { a: '12' }, { b: '1' }, { a: 'a' }, { a: '1', b: '2' }, { a: null }, { a: undefined }], 'a', /\d/))
|
||||
.toEqual([{ a: '1' }, { a: '12' }, { a: '1', b: '2' }]);
|
||||
});
|
||||
|
||||
it('should filter out items that do not match the given array of property regex/string values', function() {
|
||||
expect(filter.process([{ a: '1' }, { a: '12' }, { b: '1' }, { a: 'a' }, { a: '1', b: '2' }, { a: null }, { a: undefined }], 'a', [/\d/, 'a']))
|
||||
.toEqual([{ a: '1' }, { a: '12' }, { a: 'a' }, { a: '1', b: '2' }]);
|
||||
});
|
||||
});
|
@ -3,7 +3,21 @@ module.exports = function hasValues() {
|
||||
name: 'hasValues',
|
||||
process: function(list, property) {
|
||||
if (!list || !Array.isArray(list)) return false;
|
||||
return list.some(item => item[property]);
|
||||
return list.some(item => readProperty(item, property.split('.'), 0));
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Search deeply into an object via a collection of property segments, starting at the
|
||||
* indexed segment.
|
||||
*
|
||||
* E.g. if `obj = { a: { b: { c: 10 }}}` then
|
||||
* `readProperty(obj, ['a', 'b', 'c'], 0)` will return true;
|
||||
* but
|
||||
* `readProperty(obj, ['a', 'd'], 0)` will return false;
|
||||
*/
|
||||
function readProperty(obj, propertySegments, index) {
|
||||
const value = obj[propertySegments[index]];
|
||||
return !!value && (index === propertySegments.length - 1 || readProperty(value, propertySegments, index + 1));
|
||||
}
|
||||
|
@ -7,13 +7,37 @@ describe('hasValues filter', () => {
|
||||
|
||||
it('should be called "hasValues"', function() { expect(filter.name).toEqual('hasValues'); });
|
||||
|
||||
it('should return true if the specified property is truthy on any item in the list', function() {
|
||||
expect(filter.process([], 'a')).toEqual(false);
|
||||
expect(filter.process(0, 'a')).toEqual(false);
|
||||
expect(filter.process({}, 'a')).toEqual(false);
|
||||
it('should return true if the specified property path is truthy on any item in the list', function() {
|
||||
expect(filter.process([{a: 1}], 'a')).toEqual(true);
|
||||
expect(filter.process([{b: 2}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: 1, b: 2}], 'a')).toEqual(true);
|
||||
expect(filter.process([{b: 2}, {a: 1}], 'a')).toEqual(true);
|
||||
|
||||
expect(filter.process([{a:{b:1}}], 'a.b')).toEqual(true);
|
||||
expect(filter.process([{a:{b:1}, b: 2}], 'a.b')).toEqual(true);
|
||||
expect(filter.process([{b: 2}, {a:{b:1}}], 'a.b')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false if the value is not an object', () => {
|
||||
expect(filter.process([], 'a')).toEqual(false);
|
||||
expect(filter.process(0, 'a')).toEqual(false);
|
||||
expect(filter.process([], 'a.b')).toEqual(false);
|
||||
expect(filter.process(0, 'a.b')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false if the property exists but is falsy', () => {
|
||||
expect(filter.process([{a: false}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: ''}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: 0}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: null}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: undefined}], 'a')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false if any of the properties in the path do not exist', () => {
|
||||
expect(filter.process({}, 'a')).toEqual(false);
|
||||
expect(filter.process({}, 'a.b')).toEqual(false);
|
||||
|
||||
expect(filter.process([{b: 2}], 'a')).toEqual(false);
|
||||
expect(filter.process([{a: 2}], 'a.b')).toEqual(false);
|
||||
expect(filter.process([{a: {}}], 'a.b.c')).toEqual(false);
|
||||
});
|
||||
});
|
@ -9,12 +9,11 @@ const Package = require('dgeni').Package;
|
||||
const gitPackage = require('dgeni-packages/git');
|
||||
const apiPackage = require('../angular-api-package');
|
||||
const contentPackage = require('../angular-content-package');
|
||||
const cliDocsPackage = require('../cli-docs-package');
|
||||
const { extname, resolve } = require('canonical-path');
|
||||
const { existsSync } = require('fs');
|
||||
const { SRC_PATH } = require('../config');
|
||||
|
||||
module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage])
|
||||
module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage])
|
||||
|
||||
// This processor relies upon the versionInfo. See below...
|
||||
.processor(require('./processors/processNavigationMap'))
|
||||
|
@ -1,7 +0,0 @@
|
||||
const shelljs = require('shelljs');
|
||||
const {resolve} = require('canonical-path');
|
||||
const {CONTENTS_PATH} = require('../config');
|
||||
|
||||
shelljs.cd(resolve(CONTENTS_PATH, 'cli-src'));
|
||||
shelljs.exec('git clean -Xfd');
|
||||
shelljs.exec('yarn install');
|
@ -1,48 +0,0 @@
|
||||
const {resolve} = require('canonical-path');
|
||||
const Package = require('dgeni').Package;
|
||||
const basePackage = require('../angular-base-package');
|
||||
const contentPackage = require('../content-package');
|
||||
const {CONTENTS_PATH, TEMPLATES_PATH, requireFolder} = require('../config');
|
||||
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('cli-docs', [basePackage, contentPackage])
|
||||
|
||||
// Register the services and file readers
|
||||
.factory(require('./readers/cli-command'))
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/processCliContainerDoc'))
|
||||
.processor(require('./processors/processCliCommands'))
|
||||
.processor(require('./processors/filterHiddenCommands'))
|
||||
|
||||
// Configure file reading
|
||||
.config(function(readFilesProcessor, cliCommandFileReader) {
|
||||
const CLI_SOURCE_PATH = resolve(CONTENTS_PATH, 'cli-src/node_modules/@angular/cli/help');
|
||||
readFilesProcessor.fileReaders.push(cliCommandFileReader);
|
||||
readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([
|
||||
{
|
||||
basePath: CLI_SOURCE_PATH,
|
||||
include: resolve(CLI_SOURCE_PATH, '*.json'),
|
||||
fileReader: 'cliCommandFileReader'
|
||||
},
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: resolve(CONTENTS_PATH, 'cli/**'),
|
||||
fileReader: 'contentFileReader'
|
||||
},
|
||||
]);
|
||||
})
|
||||
|
||||
.config(function(templateFinder, templateEngine, getInjectables) {
|
||||
// Where to find the templates for the CLI doc rendering
|
||||
templateFinder.templateFolders.unshift(resolve(TEMPLATES_PATH, 'cli'));
|
||||
// Add in templating filters and tags
|
||||
templateEngine.filters = templateEngine.filters.concat(getInjectables(requireFolder(__dirname, './rendering')));
|
||||
})
|
||||
|
||||
|
||||
.config(function(convertToJsonProcessor, postProcessHtml) {
|
||||
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(['cli-command', 'cli-overview']);
|
||||
postProcessHtml.docTypes = postProcessHtml.docTypes.concat(['cli-command', 'cli-overview']);
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
module.exports = function filterHiddenCommands() {
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['processCliContainerDoc'],
|
||||
$process(docs) {
|
||||
return docs.filter(doc => doc.docType !== 'cli-command' || doc.hidden !== true);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
const processorFactory = require('./filterHiddenCommands');
|
||||
const Dgeni = require('dgeni');
|
||||
|
||||
describe('filterHiddenCommands processor', () => {
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
|
||||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('filterHiddenCommands');
|
||||
expect(processor.$process).toBeDefined();
|
||||
});
|
||||
|
||||
it('should run after the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runAfter).toEqual(['files-read']);
|
||||
});
|
||||
|
||||
it('should run before the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runBefore).toEqual(['processCliContainerDoc']);
|
||||
});
|
||||
|
||||
it('should remove CLI command docs that are hidden', () => {
|
||||
const processor = processorFactory();
|
||||
const filtered = processor.$process([
|
||||
{ docType: 'cli-command', id: 'one' },
|
||||
{ docType: 'cli-command', id: 'two', hidden: true },
|
||||
{ docType: 'cli-command', id: 'three', hidden: false },
|
||||
{ docType: 'other-doc', id: 'four', hidden: true },
|
||||
{ docType: 'other-doc', id: 'five', hidden: false },
|
||||
]);
|
||||
expect(filtered).toEqual([
|
||||
{ docType: 'cli-command', id: 'one' },
|
||||
{ docType: 'cli-command', id: 'three', hidden: false },
|
||||
{ docType: 'other-doc', id: 'four', hidden: true },
|
||||
{ docType: 'other-doc', id: 'five', hidden: false },
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,68 +0,0 @@
|
||||
module.exports = function processCliCommands() {
|
||||
return {
|
||||
$runAfter: ['extra-docs-added'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process(docs) {
|
||||
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
|
||||
const navigationNode = navigationDoc && navigationDoc.data['SideNav'].find(node => node.title === 'CLI Commands');
|
||||
|
||||
docs.forEach(doc => {
|
||||
if (doc.docType === 'cli-command') {
|
||||
doc.names = collectNames(doc.name, doc.commandAliases);
|
||||
|
||||
// Recursively process the options
|
||||
processOptions(doc, doc.options);
|
||||
|
||||
// Add to navigation doc
|
||||
if (navigationNode) {
|
||||
navigationNode.children.push({ url: doc.path, title: `ng ${doc.name}` });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function processOptions(container, options) {
|
||||
container.positionalOptions = [];
|
||||
container.namedOptions = [];
|
||||
|
||||
options.forEach(option => {
|
||||
|
||||
if (option.type === 'boolean' && option.default === undefined) {
|
||||
option.default = false;
|
||||
}
|
||||
|
||||
// Ignore any hidden options
|
||||
if (option.hidden) { return; }
|
||||
|
||||
option.types = option.types || [option.type];
|
||||
option.names = collectNames(option.name, option.aliases);
|
||||
|
||||
// Now work out what kind of option it is: positional/named
|
||||
if (option.positional !== undefined) {
|
||||
container.positionalOptions[option.positional] = option;
|
||||
} else {
|
||||
container.namedOptions.push(option);
|
||||
}
|
||||
|
||||
// Recurse if there are subcommands
|
||||
if (option.subcommands) {
|
||||
option.subcommands = getValues(option.subcommands);
|
||||
option.subcommands.forEach(subcommand => {
|
||||
subcommand.names = collectNames(subcommand.name, subcommand.aliases);
|
||||
processOptions(subcommand, subcommand.options);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
container.namedOptions.sort((a, b) => a.name > b.name ? 1 : -1);
|
||||
}
|
||||
|
||||
function collectNames(name, aliases) {
|
||||
return [name].concat(aliases);
|
||||
}
|
||||
|
||||
function getValues(obj) {
|
||||
return Object.keys(obj).map(key => obj[key]);
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
const processorFactory = require('./processCliCommands');
|
||||
const Dgeni = require('dgeni');
|
||||
|
||||
describe('processCliCommands processor', () => {
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
|
||||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('processCliCommands');
|
||||
expect(processor.$process).toBeDefined();
|
||||
});
|
||||
|
||||
it('should run after the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runAfter).toEqual(['extra-docs-added']);
|
||||
});
|
||||
|
||||
it('should run before the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runBefore).toEqual(['rendering-docs']);
|
||||
});
|
||||
|
||||
it('should collect the names (name + aliases)', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: ['alias1', 'alias2'],
|
||||
options: [],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.names).toEqual(['name', 'alias1', 'alias2']);
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
it('should remove the hidden options', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [
|
||||
{ name: 'option1' },
|
||||
{ name: 'option2', hidden: true },
|
||||
{ name: 'option3' },
|
||||
{ name: 'option4', hidden: true },
|
||||
],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'option1' }),
|
||||
jasmine.objectContaining({ name: 'option3' }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should collect the non-hidden positional and named options', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [
|
||||
{ name: 'named1' },
|
||||
{ name: 'positional1', positional: 0},
|
||||
{ name: 'named2', hidden: true },
|
||||
{ name: 'positional2', hidden: true, positional: 1},
|
||||
],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.positionalOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'positional1', positional: 0}),
|
||||
]);
|
||||
expect(doc.namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'named1' }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should sort the named options into order by name', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [
|
||||
{ name: 'c' },
|
||||
{ name: 'a' },
|
||||
{ name: 'b' },
|
||||
],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'a' }),
|
||||
jasmine.objectContaining({ name: 'b' }),
|
||||
jasmine.objectContaining({ name: 'c' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('subcommands', () => {
|
||||
it('should convert subcommands hash into a collection', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [{
|
||||
name: 'supercommand',
|
||||
subcommands: {
|
||||
subcommand1: {
|
||||
name: 'subcommand1',
|
||||
options: [
|
||||
{ name: 'subcommand1-option1' },
|
||||
{ name: 'subcommand1-option2' },
|
||||
],
|
||||
},
|
||||
subcommand2: {
|
||||
name: 'subcommand2',
|
||||
options: [
|
||||
{ name: 'subcommand2-option1' },
|
||||
{ name: 'subcommand2-option2' },
|
||||
],
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.options[0].subcommands).toEqual([
|
||||
jasmine.objectContaining({ name: 'subcommand1' }),
|
||||
jasmine.objectContaining({ name: 'subcommand2' }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove the hidden subcommand options', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [{
|
||||
name: 'supercommand',
|
||||
subcommands: {
|
||||
subcommand1: {
|
||||
name: 'subcommand1',
|
||||
options: [
|
||||
{ name: 'subcommand1-option1' },
|
||||
{ name: 'subcommand1-option2', hidden: true },
|
||||
],
|
||||
},
|
||||
subcommand2: {
|
||||
name: 'subcommand2',
|
||||
options: [
|
||||
{ name: 'subcommand2-option1', hidden: true },
|
||||
{ name: 'subcommand2-option2' },
|
||||
],
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'subcommand1-option1' }),
|
||||
]);
|
||||
expect(doc.options[0].subcommands[1].namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'subcommand2-option2' }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should collect the non-hidden positional arguments and named options', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [{
|
||||
name: 'supercommand',
|
||||
subcommands: {
|
||||
subcommand1: {
|
||||
name: 'subcommand1',
|
||||
options: [
|
||||
{ name: 'subcommand1-option1' },
|
||||
{ name: 'subcommand1-option2', positional: 0 },
|
||||
],
|
||||
},
|
||||
subcommand2: {
|
||||
name: 'subcommand2',
|
||||
options: [
|
||||
{ name: 'subcommand2-option1', hidden: true },
|
||||
{ name: 'subcommand2-option2', hidden: true, positional: 1 },
|
||||
],
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.options[0].subcommands[0].positionalOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'subcommand1-option2', positional: 0}),
|
||||
]);
|
||||
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'subcommand1-option1' }),
|
||||
]);
|
||||
|
||||
expect(doc.options[0].subcommands[1].positionalOptions).toEqual([]);
|
||||
expect(doc.options[0].subcommands[1].namedOptions).toEqual([]);
|
||||
});
|
||||
|
||||
it('should sort the named subcommand options into order by name', () => {
|
||||
const processor = processorFactory();
|
||||
const doc = {
|
||||
docType: 'cli-command',
|
||||
name: 'name',
|
||||
commandAliases: [],
|
||||
options: [{
|
||||
name: 'supercommand',
|
||||
subcommands: {
|
||||
subcommand1: {
|
||||
name: 'subcommand1',
|
||||
options: [
|
||||
{ name: 'c' },
|
||||
{ name: 'a' },
|
||||
{ name: 'b' },
|
||||
]
|
||||
}
|
||||
}
|
||||
}],
|
||||
};
|
||||
processor.$process([doc]);
|
||||
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
|
||||
jasmine.objectContaining({ name: 'a' }),
|
||||
jasmine.objectContaining({ name: 'b' }),
|
||||
jasmine.objectContaining({ name: 'c' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add the command to the CLI node in the navigation doc', () => {
|
||||
const processor = processorFactory();
|
||||
const command = {
|
||||
docType: 'cli-command',
|
||||
name: 'command1',
|
||||
commandAliases: ['alias1', 'alias2'],
|
||||
options: [],
|
||||
path: 'cli/command1',
|
||||
};
|
||||
const navigation = {
|
||||
docType: 'navigation-json',
|
||||
data: {
|
||||
SideNav: [
|
||||
{ url: 'some/page', title: 'Some Page' },
|
||||
{ url: 'cli', title: 'CLI Commands', children: [
|
||||
{ url: 'cli', title: 'Using the CLI' },
|
||||
]},
|
||||
{ url: 'other/page', title: 'Other Page' },
|
||||
]
|
||||
}
|
||||
};
|
||||
processor.$process([command, navigation]);
|
||||
expect(navigation.data.SideNav[1].title).toEqual('CLI Commands');
|
||||
expect(navigation.data.SideNav[1].children).toEqual([
|
||||
{ url: 'cli', title: 'Using the CLI' },
|
||||
{ url: 'cli/command1', title: 'ng command1' },
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
module.exports = function processCliContainerDoc() {
|
||||
return {
|
||||
$runAfter: ['extra-docs-added'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process(docs) {
|
||||
const cliDoc = docs.find(doc => doc.id === 'cli/index');
|
||||
cliDoc.id = 'cli-container';
|
||||
cliDoc.commands = docs.filter(doc => doc.docType === 'cli-command');
|
||||
}
|
||||
};
|
||||
};
|
@ -1,23 +0,0 @@
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
const processorFactory = require('./processCliContainerDoc');
|
||||
const Dgeni = require('dgeni');
|
||||
|
||||
describe('processCliContainerDoc processor', () => {
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
|
||||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('processCliContainerDoc');
|
||||
expect(processor.$process).toBeDefined();
|
||||
});
|
||||
|
||||
it('should run after the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runAfter).toEqual(['extra-docs-added']);
|
||||
});
|
||||
|
||||
it('should run before the correct processor', () => {
|
||||
const processor = processorFactory();
|
||||
expect(processor.$runBefore).toEqual(['rendering-docs']);
|
||||
});
|
||||
});
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* This file reader will pull the contents from a cli command json file
|
||||
*
|
||||
* The doc will initially have the form:
|
||||
* ```
|
||||
* {
|
||||
* startingLine: 1,
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
module.exports = function cliCommandFileReader(log) {
|
||||
const json5 = require('json5');
|
||||
return {
|
||||
name: 'cliCommandFileReader',
|
||||
defaultPattern: /\.json$/,
|
||||
getDocs(fileInfo) {
|
||||
try {
|
||||
const doc = json5.parse(fileInfo.content);
|
||||
const name = fileInfo.baseName;
|
||||
const path = `cli/${name}`;
|
||||
// We return a single element array because content files only contain one document
|
||||
const result = Object.assign(doc, {
|
||||
content: doc.description,
|
||||
docType: 'cli-command',
|
||||
startingLine: 1,
|
||||
id: `cli-${doc.name}`,
|
||||
commandAliases: doc.aliases || [],
|
||||
aliases: computeAliases(doc),
|
||||
path,
|
||||
outputPath: `${path}.json`,
|
||||
breadCrumbs: [
|
||||
{ text: 'CLI', path: 'cli' },
|
||||
{ text: name, path },
|
||||
]
|
||||
});
|
||||
return [result];
|
||||
} catch (e) {
|
||||
log.warn(`Failed to read cli command file: "${fileInfo.relativePath}" - ${e.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function computeAliases(doc) {
|
||||
return [doc.name].concat(doc.aliases || []).map(alias => `cli-${alias}`);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
const cliCommandReaderFactory = require('./cli-command');
|
||||
const reader = cliCommandReaderFactory();
|
||||
|
||||
const content = `
|
||||
{
|
||||
"name": "add",
|
||||
"description": "Add support for a library to your project.",
|
||||
"longDescription": "Add support for a library in your project, for example adding \`@angular/pwa\` which would configure\\nyour project for PWA support.\\n",
|
||||
"hidden": false,
|
||||
"type": "custom",
|
||||
"options": [
|
||||
{
|
||||
"name": "collection",
|
||||
"description": "The package to be added.",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"positional": 0
|
||||
},
|
||||
{
|
||||
"name": "help",
|
||||
"description": "Shows a help message.",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"aliases": [],
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"name": "helpJson",
|
||||
"description": "Shows the metadata associated with each flags, in JSON format.",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"aliases": [],
|
||||
"hidden": false
|
||||
}
|
||||
],
|
||||
"aliases": ['a'],
|
||||
"scope": "in"
|
||||
}
|
||||
`;
|
||||
|
||||
const fileInfo = {content, baseName: 'add'};
|
||||
|
||||
describe('cli-command reader', () => {
|
||||
describe('getDocs', () => {
|
||||
it('should return an array containing a single doc', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return a cli-command doc', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0]).toEqual(jasmine.objectContaining({
|
||||
id: 'cli-add',
|
||||
docType: 'cli-command',
|
||||
}));
|
||||
});
|
||||
|
||||
it('should extract the name from the fileInfo', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].name).toEqual('add');
|
||||
});
|
||||
|
||||
it('should compute the id and aliases', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].id).toEqual('cli-add');
|
||||
expect(docs[0].aliases).toEqual(['cli-add', 'cli-a']);
|
||||
});
|
||||
|
||||
it('should compute the path and outputPath', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].path).toEqual('cli/add');
|
||||
expect(docs[0].outputPath).toEqual('cli/add.json');
|
||||
});
|
||||
|
||||
it('should compute the bread crumbs', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].breadCrumbs).toEqual([
|
||||
{ text: 'CLI', path: 'cli' },
|
||||
{ text: 'add', path: 'cli/add' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should start at line 1', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].startingLine).toEqual(1);
|
||||
});
|
||||
|
||||
it('should extract the short description into the content', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].content).toEqual('Add support for a library to your project.');
|
||||
});
|
||||
|
||||
it('should extract the long description', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].longDescription).toEqual('Add support for a library in your project, for example adding `@angular/pwa` which would configure\nyour project for PWA support.\n');
|
||||
});
|
||||
|
||||
it('should extract the command type', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].type).toEqual('custom');
|
||||
});
|
||||
|
||||
it('should extract the command scope', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].scope).toEqual('in');
|
||||
});
|
||||
|
||||
it('should extract the command aliases', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].commandAliases).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should extract the options', () => {
|
||||
const docs = reader.getDocs(fileInfo);
|
||||
expect(docs[0].options).toEqual([
|
||||
jasmine.objectContaining({ name: 'collection' }),
|
||||
jasmine.objectContaining({ name: 'help' }),
|
||||
jasmine.objectContaining({ name: 'helpJson' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,6 +0,0 @@
|
||||
module.exports = function cliNegate() {
|
||||
return {
|
||||
name: 'cliNegate',
|
||||
process: function(str) { return 'no' + str.charAt(0).toUpperCase() + str.slice(1); }
|
||||
};
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
var factory = require('./cliNegate');
|
||||
|
||||
describe('cliNegate filter', function() {
|
||||
var filter;
|
||||
|
||||
beforeEach(function() { filter = factory(); });
|
||||
|
||||
it('should be called "cliNegate"', function() { expect(filter.name).toEqual('cliNegate'); });
|
||||
|
||||
it('should make the first char uppercase and add `no` to the front', function() {
|
||||
expect(filter.process('abc')).toEqual('noAbc');
|
||||
});
|
||||
|
||||
it('should make leave the rest of the chars alone', function() {
|
||||
expect(filter.process('abCdE')).toEqual('noAbCdE');
|
||||
});
|
||||
});
|
@ -1,21 +1,9 @@
|
||||
{% import "lib/memberHelpers.html" as memberHelpers -%}
|
||||
{% import "lib/descendants.html" as descendants -%}
|
||||
{% import "lib/paramList.html" as params -%}
|
||||
{% extends 'export-base.template.html' -%}
|
||||
|
||||
{% block overview %}
|
||||
{% include "includes/class-overview.html" %}
|
||||
{% endblock %}
|
||||
{% block details %}
|
||||
{% block additional %}{% endblock %}
|
||||
{% include "includes/description.html" %}
|
||||
{$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $}
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $}
|
||||
{% if doc.constructorDoc %}
|
||||
<h2>Constructor</h2>
|
||||
{$ memberHelpers.renderMethodDetail(versionInfo, doc.constructorDoc, 'constructor') $}{% endif %}
|
||||
|
||||
{$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-property', 'Properties') $}
|
||||
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $}
|
||||
{% include "includes/class-members.html" %}
|
||||
{% endblock %}
|
||||
|
@ -1,14 +1,28 @@
|
||||
{% import "lib/directiveHelpers.html" as directiveHelper -%}
|
||||
{% import "lib/paramList.html" as params -%}
|
||||
{% import "lib/memberHelpers.html" as memberHelpers -%}
|
||||
{% extends 'class.template.html' -%}
|
||||
|
||||
{% block overview %}{% include "includes/directive-overview.html" %}{% endblock %}
|
||||
{% block additional -%}
|
||||
{% include "includes/ngmodule.html" %}
|
||||
{% include "includes/selectors.html" %}
|
||||
{$ directiveHelper.renderBindings(doc.inputs, 'inputs', 'input', 'Inputs') $}
|
||||
{$ directiveHelper.renderBindings(doc.outputs, 'outputs', 'output', 'Outputs') $}
|
||||
{% include "includes/export-as.html" %}
|
||||
{% endblock %}
|
||||
{% block overview %}{% endblock %}
|
||||
|
||||
{% block annotations %}{% endblock %}
|
||||
{% block details -%}
|
||||
{% include "includes/ngmodule.html" %}
|
||||
{% include "includes/selectors.html" %}
|
||||
|
||||
{$ memberHelpers.renderDirectiveProperties(doc, 'Properties') $}
|
||||
|
||||
{% include "includes/export-as.html" %}
|
||||
|
||||
{% if doc.description or doc.usageNotes %}
|
||||
<section class="description">
|
||||
<h2>Description</h2>
|
||||
{$ (doc.description or '') | trimBlankLines | marked $}
|
||||
{$ (doc.usageNotes or '') | trimBlankLines | marked $}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $}
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $}
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $}
|
||||
{$ memberHelpers.renderDirectiveAncestors(doc, 'methods') $}
|
||||
{% endblock %}
|
||||
|
||||
{% block endNotes %}{% endblock %}
|
@ -10,5 +10,5 @@ enum {$ doc.name $} {{$ memberHelpers.renderMembers(doc) $}
|
||||
</section>
|
||||
|
||||
{% include "includes/description.html" %}
|
||||
{$ memberHelpers.renderProperties(doc.properties, 'members', 'member', 'Members', ['Member', 'Value']) $}
|
||||
{$ memberHelpers.renderProperties(doc.properties, 'members', 'member', 'Members', ['Member']) $}
|
||||
{% endblock %}
|
@ -1,11 +1,14 @@
|
||||
{% extends 'base.template.html' -%}
|
||||
|
||||
{% block body %}
|
||||
<p class="short-description">{$ doc.shortDescription | marked $}</p>
|
||||
<section class="short-description">
|
||||
{$ doc.shortDescription | marked $}
|
||||
{% if doc.description %}<p><a href="#description">See more...</a></p>{% endif %}
|
||||
</section>
|
||||
{% include "includes/security-notes.html" %}
|
||||
{% include "includes/deprecation.html" %}
|
||||
{% block overview %}{% endblock %}
|
||||
{% include "includes/see-also.html" %}
|
||||
{% block details %}{% endblock %}
|
||||
{% include "includes/usageNotes.html" %}
|
||||
{% block endNotes %}{% include "includes/usageNotes.html" %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,14 @@
|
||||
{% import "lib/memberHelpers.html" as memberHelpers -%}
|
||||
|
||||
{$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $}
|
||||
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $}
|
||||
|
||||
{% if doc.constructorDoc %}
|
||||
<h2>Constructor</h2>
|
||||
{$ memberHelpers.renderMethodDetail(versionInfo, doc.constructorDoc, 'constructor') $}
|
||||
{% endif %}
|
||||
|
||||
{$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-property', 'Properties') $}
|
||||
|
||||
{$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $}
|
@ -1,9 +1,10 @@
|
||||
{% import "lib/memberHelpers.html" as memberHelper -%}
|
||||
{% import "lib/descendants.html" as descendants -%}
|
||||
|
||||
<section class="{$ doc.docType $}-overview">
|
||||
<code-example language="ts" hideCopy="true" linenums="false">
|
||||
{% if doc.isAbstract %}abstract {% endif%}class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {{$ memberHelper.renderMembers(doc) $}
|
||||
}
|
||||
</code-example>
|
||||
{$ descendants.renderDescendants(doc, 'class', 'Subclasses') $}
|
||||
{$ descendants.renderDescendants(doc, 'class', 'Subclasses', true, r/class|directive|pipe|decorator/) $}
|
||||
</section>
|
||||
|
@ -1,12 +0,0 @@
|
||||
{% import "lib/memberHelpers.html" as memberHelper -%}
|
||||
|
||||
<section class="{$ doc.docType $}-overview">
|
||||
<code-example language="ts" hideCopy="true">{% for decorator in doc.decorators %}
|
||||
@{$ decorator.name $}({$ decorator.arguments $}){% endfor %}
|
||||
class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {
|
||||
{%- if doc.statics.length %}{% for member in doc.statics %}{% if not member.internal %}
|
||||
<a class="code-anchor" href="#{$ member.anchor | urlencode $}">{$ memberHelper.renderMemberSyntax(member, 1) $}</a>{% endif %}{% endfor %}{% endif -%}
|
||||
{$ memberHelper.renderMembers(doc) $}
|
||||
}
|
||||
</code-example>
|
||||
</section>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user