Compare commits
478 Commits
8.0.1
...
8.1.0-next
Author | SHA1 | Date | |
---|---|---|---|
2c78da00ae | |||
bb8a18e72d | |||
82e0b4a4fb | |||
574990e0fd | |||
aa9eb55e3c | |||
052ef654d2 | |||
58be2ff884 | |||
7912db3829 | |||
0ee09cdd7e | |||
b2937b16c3 | |||
a4b4f35533 | |||
b0866769b0 | |||
699283c4ee | |||
bb635c09fd | |||
c15e6750d5 | |||
87bf1791a9 | |||
8c4bd61b2f | |||
230e9766f6 | |||
2b4d5c7548 | |||
271d2b51a9 | |||
7b0bee73de | |||
c9ce485ce0 | |||
ba4ca5f9a5 | |||
4155ed439a | |||
2b9a4cc6a6 | |||
016b3f16a3 | |||
87b1a2af4d | |||
a2cd401a2e | |||
93af1f8456 | |||
5193acb0a3 | |||
532c1cb485 | |||
ef0b2cc74d | |||
a794143820 | |||
ba83d33dd0 | |||
17d87d4e10 | |||
9d9c9e43e5 | |||
c0386757b1 | |||
5c18f23788 | |||
7afd2603c9 | |||
81c2a94310 | |||
297222f892 | |||
64488b12ac | |||
8b6fbf8655 | |||
383ab8515d | |||
11a4454ab3 | |||
04587a33c5 | |||
b4b7af86c2 | |||
f440bd1793 | |||
3859bcc70c | |||
30efb6b8ea | |||
05a43ca869 | |||
b51d8dd438 | |||
ea2d453118 | |||
17bfedd224 | |||
540b01402f | |||
8852b793df | |||
d1df0a94d4 | |||
00cc905b98 | |||
337b6fe003 | |||
ef4a15bc0b | |||
0d4f8c7dd9 | |||
b74df20c2a | |||
4ecff42e7f | |||
55a14e4866 | |||
c7850fff3b | |||
456f2e70af | |||
4da5e9a156 | |||
812c231b0c | |||
3ed7463ad7 | |||
8154433130 | |||
07cd65b5ec | |||
cd6fc8bf06 | |||
fcdd784667 | |||
a981dd2aab | |||
b4e68025f8 | |||
680d38513b | |||
21328f2373 | |||
dd8cf19352 | |||
8bedf50073 | |||
32886cf9ac | |||
fcef39048a | |||
1315d23aa4 | |||
8ce288a852 | |||
f936590573 | |||
876cd603f1 | |||
aca339e864 | |||
f4cd3740b2 | |||
6d861f240b | |||
1f79c827a0 | |||
e0c36620c1 | |||
80394ce08b | |||
99c9bcab03 | |||
fa354888b1 | |||
d82adbe8c4 | |||
09c57ecf95 | |||
282abdb02d | |||
19bbf4bc8e | |||
53c6b78c51 | |||
91699259b2 | |||
41f372fe79 | |||
8e8c1ad47d | |||
b61784948a | |||
b4644d7bb0 | |||
7a0f8ac36c | |||
5e0f982961 | |||
f5b0c8a323 | |||
d2b0ac7de8 | |||
a2d26c6f2a | |||
62332b80da | |||
e3f3b106ac | |||
fd23663a29 | |||
71be1da0ad | |||
6a5cc8f95c | |||
2c406fb1a9 | |||
0b23adca80 | |||
671f64324b | |||
f1f8d70a41 | |||
8301028319 | |||
ba5309c1f8 | |||
fa5d8ca041 | |||
24ca8ba7ce | |||
af4925f374 | |||
77578266fd | |||
7bf7244ba3 | |||
1a98d51f98 | |||
82682bb93f | |||
d72479b628 | |||
dd0815095f | |||
48093823cb | |||
51cb5925c5 | |||
a36cacae79 | |||
1b5096d5a9 | |||
875eedbfb0 | |||
75cdda5f0a | |||
6363db89d0 | |||
573f57ac90 | |||
41cf066906 | |||
661c6e6f10 | |||
84dd2679a9 | |||
dc6406e5e8 | |||
deb77bd3df | |||
d5f96a887d | |||
68cd0cab8c | |||
9b3af1468d | |||
be0bc799f3 | |||
0d23cedab8 | |||
6bf5ec241d | |||
cdb5ea4200 | |||
bfedfa79ed | |||
911a8160ec | |||
8479cb4233 | |||
250887244e | |||
9b035e4038 | |||
ccc76f7498 | |||
f310a5960e | |||
b5c893916d | |||
132c61dfd4 | |||
5640cedc82 | |||
faac51fd2e | |||
214ae0ea4c | |||
dcdecfa9a8 | |||
4299ecf9be | |||
21e8ac1e02 | |||
01cf04c85f | |||
5c5cee9f39 | |||
2f336f15be | |||
e122b44269 | |||
9ce9561737 | |||
2545445ffb | |||
300f1b289a | |||
975845596d | |||
2cdbe9b2ef | |||
988afad2af | |||
1537aec1f9 | |||
7555a46e23 | |||
38d7acee4d | |||
10f48278c2 | |||
d4e6263453 | |||
86c46908d4 | |||
fa6cbb3ffe | |||
e20b92ba37 | |||
c7a9987067 | |||
1bdec3ed6a | |||
620cd5c148 | |||
76391f8999 | |||
02523debe5 | |||
70fd4300f4 | |||
3125376ec1 | |||
2cd5294394 | |||
3c548189b1 | |||
ae91f45b27 | |||
f74e0fd825 | |||
f1ea78ba09 | |||
87a2366eeb | |||
0f5da82cfd | |||
66f269c077 | |||
b613f90146 | |||
2fe6f350cb | |||
514ebf402a | |||
7c0667d215 | |||
f6bf8928f2 | |||
6debe9dfb9 | |||
0d52a27f41 | |||
697046c2b2 | |||
53a0b26348 | |||
8c00fced1c | |||
a50c1bb7bc | |||
f3c69e7f6b | |||
c9f5f3d802 | |||
a96976e88f | |||
ebfbc04000 | |||
e1af6e3c70 | |||
9e946c9715 | |||
0d97143965 | |||
df1d3fbd7b | |||
e946594bf8 | |||
abe4433202 | |||
c79bffaacb | |||
e58e5ec6a6 | |||
e688e02ee4 | |||
d7eaae6f22 | |||
1c3ee41902 | |||
f78bda9ff0 | |||
6454f76cf6 | |||
28ae22ecb9 | |||
c1135ee18f | |||
1114053daa | |||
95830ee584 | |||
661a57d9e2 | |||
660a091f41 | |||
7d31f7b540 | |||
ab4a23d0e0 | |||
a39f4e2301 | |||
8e2e9dcee6 | |||
9d5abfb3d9 | |||
fd7dd4d9fc | |||
01919fbaae | |||
80d4fc5e26 | |||
eda09e69ea | |||
0937062a64 | |||
6f073885b0 | |||
f03475cac8 | |||
848e53efd0 | |||
6bf8b1007c | |||
98ded949dd | |||
a5a1f4f52f | |||
8c1a041411 | |||
b1ba2d6f4e | |||
1714451a6d | |||
1f6fcb6cd3 | |||
60235b5aef | |||
d18c58816f | |||
222dde129d | |||
257e9646d0 | |||
a5e06ba629 | |||
9a6d298ca7 | |||
6ec621b72d | |||
07ebe9624f | |||
b4014e9a39 | |||
73e3f565e0 | |||
757d4c33df | |||
edd775eabc | |||
c613596658 | |||
f4655ea98a | |||
f6aa60c03c | |||
7fec1771fc | |||
c9b588b349 | |||
37f69eddc7 | |||
0fa72a8bc8 | |||
48b77459ef | |||
989bfd2e97 | |||
aeec66b657 | |||
e68490c5e4 | |||
95c5b1a7f6 | |||
8e201f713a | |||
ac34a1429b | |||
739e5a4f53 | |||
bf94932c7a | |||
6cb3b50a03 | |||
0e92332491 | |||
f5127f601d | |||
1b613c3ebf | |||
3f7e823b80 | |||
e9ead2bc09 | |||
b6b1aec22b | |||
1aff524b63 | |||
077809398c | |||
f9404d9e7c | |||
b766987b98 | |||
c62c5e2999 | |||
f2ae452f01 | |||
79903b1842 | |||
0051ddf0f3 | |||
3a8f74e392 | |||
8555016eec | |||
45e11915dc | |||
1a0e500eea | |||
35c1750fcc | |||
53f356427f | |||
d20b0f4b93 | |||
79d4b16f8a | |||
0fd9d086e4 | |||
4f9b16783b | |||
018a5168a5 | |||
cf86ed7b29 | |||
dbb150a9bd | |||
9e5377a2e3 | |||
0cdf5980e2 | |||
6ceb9034dc | |||
f9fb921f91 | |||
1b059e595f | |||
ff29cccb77 | |||
c7f9a95a3f | |||
06efc340b6 | |||
3fecab64b1 | |||
34e0d621fd | |||
cc2f175617 | |||
96baff3a85 | |||
090eac068a | |||
d09d8e0adf | |||
a57f3e7bbf | |||
197584d1af | |||
18c0ba5272 | |||
1bd4891c9a | |||
3a7bfc721e | |||
f74373f2dd | |||
2f35dbfd3b | |||
31df5139c5 | |||
7569a2e0d9 | |||
7dad3284e3 | |||
0cf09fc981 | |||
877b2285f9 | |||
0d4a0b6519 | |||
4feb9b1c72 | |||
cb6dea473f | |||
5ab809ddf9 | |||
fbff03b476 | |||
411524d341 | |||
0a0b4c1d8f | |||
5887ddfa3c | |||
d80ae6ba0d | |||
44cf981407 | |||
5fd39283ec | |||
1f2b39ad7d | |||
24e172d779 | |||
b68850215a | |||
9abf114fbb | |||
8d3365e4fc | |||
8cec8f5584 | |||
741a5dc5f7 | |||
de996c6d56 | |||
d750b1e10d | |||
d2c83ea81b | |||
bf031fc56b | |||
b1d45ee6d2 | |||
f26f036286 | |||
0ffdb48f62 | |||
cb6ad971c3 | |||
d70b1ff177 | |||
3aff79c251 | |||
d8665e639b | |||
c016e2c4ec | |||
452f121486 | |||
728db88280 | |||
9a807bd26a | |||
eccc41c5cf | |||
509352fc36 | |||
349935a434 | |||
6357d4a0d3 | |||
29786e856d | |||
bf6bedd714 | |||
f094bb54a7 | |||
4c12d742dc | |||
04a9f28c3d | |||
d9b9ed56b6 | |||
7c8a62d64d | |||
de651122a5 | |||
4a96ddfb54 | |||
8ced321bb6 | |||
2a0f497e94 | |||
392473ec79 | |||
345a3cd9aa | |||
bd37622050 | |||
48de6e87f0 | |||
3b551e0fcd | |||
be8fbac942 | |||
98a38ec98b | |||
4f055d4257 | |||
c59717571e | |||
f5b2ae616f | |||
1660b34e2d | |||
b40f6f3eae | |||
3327bd8eab | |||
ea9a381c8c | |||
b2437c4500 | |||
d6538eb2fd | |||
0926119977 | |||
39f2c9f46b | |||
60a8888b4f | |||
4537816c1d | |||
bf73fb7420 | |||
1ec092ba32 | |||
30d1f292c9 | |||
b70d20b510 | |||
066ec33342 | |||
5b80ab372d | |||
638ba4a2cf | |||
7ec8806dc5 | |||
7d6f4885b2 | |||
b1506a3271 | |||
b15a403c71 | |||
37c598db04 | |||
ad94e02981 | |||
00ffc03523 | |||
e53cf81689 | |||
089fcbf369 | |||
68ff2cc323 | |||
164d160b22 | |||
1353bf0277 | |||
6dc884f2ab | |||
05eabb19d6 | |||
f4916730b5 | |||
67012509a8 | |||
28a1caa0ed | |||
f015dbe1ba | |||
029a93963a | |||
20898f9f4f | |||
ef861958a9 | |||
16d7dde2ad | |||
1fd2cc6340 | |||
5ced8fbbd5 | |||
23152c37c8 | |||
4a2405929c | |||
eef4ca5dd3 | |||
321da5cc83 | |||
c2cf500da9 | |||
4c03208537 | |||
78b5bd5174 | |||
1195dabb84 | |||
2945f47977 | |||
a44b510087 | |||
572b54967c | |||
6f433887e0 | |||
537502d685 | |||
e5c3695dbd | |||
3cf318b498 | |||
54d4105264 | |||
dd59b1d371 | |||
124e49752f | |||
fc2dd5482e | |||
e4b81a6957 | |||
3efdd39a18 | |||
582ef2e7b4 | |||
214fef2ee4 | |||
531fcaa99a | |||
8a60239ae7 | |||
10705684c8 | |||
bdc6818716 | |||
5f95796b61 | |||
f2709ac3f9 | |||
cce9b9912f | |||
2ae26ce20b | |||
d47de60944 | |||
acfcf90528 | |||
c99d379cc8 | |||
0151ad432b | |||
1b0be8d656 | |||
47244ba2b8 | |||
0d66844ad6 | |||
b945bc3a9e | |||
4ae0ee86cb | |||
6c86ae710a | |||
f3ce8eeb83 | |||
876ceb3688 | |||
ee12c45473 | |||
1f4c380f58 | |||
b9f0720c95 | |||
71b8b355a6 |
@ -1,42 +0,0 @@
|
||||
# Heavily based on https://github.com/StefanScherer/dockerfiles-windows/ images.
|
||||
# Combines the node windowsservercore image with the Bazel Prerequisites (https://docs.bazel.build/versions/master/install-windows.html).
|
||||
# msys install taken from https://github.com/StefanScherer/dockerfiles-windows/issues/30
|
||||
# VS redist install taken from https://github.com/StefanScherer/dockerfiles-windows/blob/master/apache/Dockerfile
|
||||
# The nanoserver image won't work because MSYS2 does not run in it https://github.com/Alexpux/MSYS2-packages/issues/1493
|
||||
|
||||
# Before building this image, you must locally build node-windows:10.13.0-windowsservercore-1803.
|
||||
# Clone https://github.com/StefanScherer/dockerfiles-windows/commit/4ce7101a766b9b880ac262479dd9126b64d656cf and build using
|
||||
# docker build -t node-windows:10.13.0-windowsservercore-1803 --build-arg core=microsoft/windowsservercore:1803 --build-arg target=microsoft/windowsservercore:1803 .
|
||||
FROM node-windows:10.13.0-windowsservercore-1803
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
# Install 7zip to extract msys2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe
|
||||
# For some reason the last letter in the destination directory is lost. So '/D=C:\\7zip0' will extract to '/D=C:\\7zip'.
|
||||
RUN Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait
|
||||
|
||||
# Extract msys2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz
|
||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait
|
||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait
|
||||
RUN Remove-Item msys2.tar.xz
|
||||
RUN Remove-Item msys2.tar
|
||||
RUN Remove-Item 7z.exe
|
||||
RUN Remove-Item -Recurse 7zip
|
||||
|
||||
# Add MSYS2 to PATH, and set BAZEL_SH
|
||||
RUN [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine)
|
||||
RUN [Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Install Microsoft Visual C++ Redistributable for Visual Studio 2015
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe
|
||||
RUN Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait
|
||||
RUN Remove-Item vc_redist.x64.exe
|
||||
|
||||
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
|
||||
SHELL ["cmd", "/c"]
|
||||
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
|
||||
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
|
||||
|
||||
CMD ["cmd.exe"]
|
@ -1,96 +0,0 @@
|
||||
# BuildKite configuration
|
||||
|
||||
This folder contains configuration for the [BuildKite](https://buildkite.com) based CI checks for
|
||||
this repository.
|
||||
|
||||
BuildKite is a CI provider that provides build coordination and reports while we provide the
|
||||
infrastructure.
|
||||
|
||||
CI runs are triggered by new PRs and will show up on the GitHub checks interface, along with the
|
||||
other current CI solutions.
|
||||
|
||||
Currently it is only used for tests on Windows platforms.
|
||||
|
||||
|
||||
## The build pipeline
|
||||
|
||||
BuildKite uses a pipeline for each repository. The `pipeline.yml` file defines pipeline
|
||||
[build steps](https://buildkite.com/docs/pipelines/defining-steps) for this repository.
|
||||
|
||||
Run results can be seen in the GitHub checks interface and in the
|
||||
[pipeline dashboard](https://buildkite.com/angular/angular).
|
||||
|
||||
Although most configuration is done via `pipeline.yml`, some options are only available
|
||||
in the online [pipeline settings](https://buildkite.com/angular/angular/settings).
|
||||
|
||||
|
||||
## Infrastructure
|
||||
|
||||
BuildKite does not provide the host machines where the builds runs, providing instead the
|
||||
[BuildKite Agent](https://buildkite.com/docs/agent/v3) that should be run our own infrastructure.
|
||||
|
||||
|
||||
### Agents
|
||||
|
||||
This agent polls the BuildKite API for builds, runs them, and reports back the results.
|
||||
Agents are the unit of concurrency: each agent can run one build at any given time.
|
||||
Adding agents allows more builds to be ran at the same time.
|
||||
|
||||
Individual agents can have tags, and pipeline steps can target only agents with certain tags via the
|
||||
`agents` field in `pipeline.yml`.
|
||||
For example: agents on Windows machines are tagged as `windows`, and the Windows specific build
|
||||
steps list `windows: true` in their `agents` field.
|
||||
|
||||
You can see the current agent pool, along with their tags, in the
|
||||
[agents list](https://buildkite.com/organizations/angular/agents).
|
||||
|
||||
|
||||
### Our host machines
|
||||
|
||||
We use [Google Cloud](https://cloud.google.com/) as our cloud provider, under the
|
||||
[Angular project](https://console.cloud.google.com/home/dashboard?project=internal-200822).
|
||||
To access this project you need need to be logged in with a Google account that's a member of
|
||||
team@angular.io.
|
||||
For googlers this may be your google.com account, for others it is an angular.io account.
|
||||
|
||||
In this project we have a number of Windows VMs running, each of them with several agents.
|
||||
The `provision-windows-buildkite.ps1` file contains instructions on how to create new host VMs that
|
||||
are fully configured to run the BuildKite agents as services.
|
||||
|
||||
Our pipeline uses [docker-buildkite-plugin](https://github.com/buildkite-plugins/docker-buildkite-plugin)
|
||||
to run build steps inside docker containers.
|
||||
This way we achieve isolation and hermeticity.
|
||||
|
||||
The `Dockerfile` file describes a custom Docker image that includes NodeJs, Yarn, and the Bazel
|
||||
pre-requisites on Windows.
|
||||
|
||||
To upload a new version of the docker image, follow any build instructions in `Dockerfile` and then
|
||||
run `docker build -t angular/node-bazel-windows:NEW_VERSION`, followed by
|
||||
`docker push angular/node-bazel-windows:NEW_VERSION`.
|
||||
After being pushed it should be available online, and you can use the new version in `pipeline.yml`.
|
||||
|
||||
|
||||
## Caretaker
|
||||
|
||||
BuildKite status can be found at https://www.buildkitestatus.com/.
|
||||
|
||||
Issues related to the BuildKite setup should be escalated to the Tools Team via the current
|
||||
caretaker, followed by Alex Eagle and Filipe Silva.
|
||||
|
||||
Support requests should be submitted via email to support@buildkite.com and cc Igor, Misko, Alex,
|
||||
Jeremy and Manu
|
||||
|
||||
|
||||
## Rollout strategy
|
||||
|
||||
At the moment our BuildKite CI uses 1 host VM running 4 agents, thus being capable of 4 concurrent
|
||||
builds.
|
||||
The only test running is `bazel test //tools/ts-api-guardian:all`, and the PR check is not
|
||||
mandatory.
|
||||
|
||||
In the future we should add cache support to speed up the initial `yarn` install, and also Bazel
|
||||
remote caching to speed up Bazel builds.
|
||||
|
||||
After the current setup is verified as stable and reliable the GitHub PR check can become mandatory.
|
||||
|
||||
The tests ran should also be expanded to cover most, if not all, of the Bazel tests.
|
@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
REM Fix for https://github.com/docker/for-win/issues/2920
|
||||
REM echo "Fixing msys64 folder..."
|
||||
REM Touch all .dll files inside C:\msys64\
|
||||
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
|
||||
REM echo "Fixed msys64 folder."
|
@ -1,10 +0,0 @@
|
||||
steps:
|
||||
- label: windows-test
|
||||
commands:
|
||||
- "yarn install --frozen-lockfile --non-interactive --network-timeout 100000"
|
||||
- "yarn bazel test //tools/ts-api-guardian:all --noshow_progress"
|
||||
plugins:
|
||||
- docker#v2.1.0:
|
||||
image: "filipesilva/node-bazel-windows:0.0.2"
|
||||
agents:
|
||||
windows: true
|
@ -1,94 +0,0 @@
|
||||
# PowerShell script to provision a Windows Server with BuildKite
|
||||
# This script follows https://buildkite.com/docs/agent/v3/windows.
|
||||
|
||||
# Instructions
|
||||
|
||||
# VM creation:
|
||||
# In Google Cloud Platform, create a Compute Engine instance.
|
||||
# We recommend machine type n1-standard-16 (16 vCPUs, 60 GB memory).
|
||||
# Use a recent windows boot disk with container support such as
|
||||
# "Windows Server version 1803 Datacenter Core for Containers", and add a 128GB SSD disk.
|
||||
# Give it a name, then click "Create".
|
||||
|
||||
# VM setup:
|
||||
# In the Compute Engine menu, select "VM Instances". Click on the VM name you chose before.
|
||||
# Click "Set Windows Password" to choose a username and password.
|
||||
# Click RDP to open a remote desktop via browser, using the username and password.
|
||||
# In the Windows command prompt start an elevated powershell by inputing
|
||||
# "powershell -Command "Start-Process PowerShell -Verb RunAs" followed by Enter.
|
||||
# Download and execute this script from GitHub, passing the token (mandatory), tags (optional)
|
||||
# and number of agents (optional) as args:
|
||||
# ```
|
||||
# Invoke-WebRequest -Uri https://raw.githubusercontent.com/angular/angular/master/.buildkite/provision-windows-buildkite.ps1 -OutFile provision.ps1
|
||||
# .\provision.ps1 -token "MY_TOKEN" -tags "windows=true,another_tag=true" -agents 4
|
||||
# ```
|
||||
# The VM should restart and be fully configured.
|
||||
|
||||
# Creating extra VMs
|
||||
# You can create an image of the current VM by following the instructions below.
|
||||
# https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image
|
||||
# Then create a new VM and choose "Custom images".
|
||||
|
||||
|
||||
# Script proper.
|
||||
|
||||
# Get the token and tags from arguments.
|
||||
param (
|
||||
[Parameter(Mandatory=$true)][string]$token,
|
||||
[string]$tags = "",
|
||||
[Int]$agents = 1
|
||||
)
|
||||
|
||||
# Allow HTTPS
|
||||
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
|
||||
|
||||
# Helper to add to PATH.
|
||||
# Will take current PATH so avoid running it after anything to modifies only the powershell session path.
|
||||
function Add-Path ([string]$newPathItem) {
|
||||
$Env:Path+= ";" + $newPathItem + ";"
|
||||
[Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::Machine)
|
||||
}
|
||||
|
||||
# Install Git for Windows
|
||||
Write-Host "Installing Git for Windows."
|
||||
Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/Git-2.19.1-64-bit.exe -OutFile git.exe
|
||||
.\git.exe /VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /COMPONENTS="icons,ext\reg\shellhere,assoc,assoc_sh" /DIR="C:\git"
|
||||
Add-Path "C:\git\bin"
|
||||
# Sleep for 15s while git is installed. Trying to remove the git.exe before it finishes install causes an error.
|
||||
Start-Sleep -s 15
|
||||
Remove-Item git.exe
|
||||
|
||||
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
|
||||
Write-Host "Downloading NSSM."
|
||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
|
||||
Expand-Archive -Path nssm.zip -DestinationPath C:\nssm
|
||||
Add-Path "C:\nssm\nssm-2.24-101-g897c7ad\win64"
|
||||
Remove-Item nssm.zip
|
||||
|
||||
# Run the BuildKite agent install script
|
||||
Write-Host "Installing BuildKite agent."
|
||||
$env:buildkiteAgentToken = $token
|
||||
$env:buildkiteAgentTags = $tags
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/buildkite/agent/master/install.ps1'))
|
||||
|
||||
# Configure the BuildKite agent clone and timestamp behavior
|
||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ngit-clone-flags=--config core.autocrlf=input --config core.eol=lf --config core.longpaths=true --config core.symlinks=true`n"
|
||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ntimestamp-lines=true`n"
|
||||
|
||||
# Register the BuildKite agent service using NSSM, so that it persists through restarts and is
|
||||
# restarted if the process dies.
|
||||
for ($i=1; $i -le $agents; $i++)
|
||||
{
|
||||
$agentName = "buildkite-agent-$i"
|
||||
Write-Host "Registering $agentName as a service."
|
||||
nssm.exe install $agentName "C:\buildkite-agent\bin\buildkite-agent.exe" "start"
|
||||
nssm.exe set $agentName AppStdout "C:\buildkite-agent\$agentName.log"
|
||||
nssm.exe set $agentName AppStderr "C:\buildkite-agent\$agentName.log"
|
||||
nssm.exe status $agentName
|
||||
nssm.exe start $agentName
|
||||
nssm.exe status $agentName
|
||||
}
|
||||
|
||||
# Restart the machine.
|
||||
Restart-Computer
|
@ -364,7 +364,7 @@ jobs:
|
||||
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
|
@ -1,3 +1,5 @@
|
||||
# escape=`
|
||||
|
||||
ARG core=mcr.microsoft.com/windows/servercore:1809
|
||||
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
|
||||
|
||||
@ -10,57 +12,57 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref
|
||||
|
||||
ENV GPG_VERSION 2.3.4
|
||||
|
||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; \
|
||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; `
|
||||
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
|
||||
|
||||
RUN @( \
|
||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', \
|
||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', \
|
||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', \
|
||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', \
|
||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', \
|
||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', \
|
||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', \
|
||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', \
|
||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', \
|
||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', \
|
||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' \
|
||||
) | foreach { \
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; \
|
||||
RUN @( `
|
||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', `
|
||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', `
|
||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', `
|
||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', `
|
||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', `
|
||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', `
|
||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', `
|
||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', `
|
||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', `
|
||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', `
|
||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' `
|
||||
) | foreach { `
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; `
|
||||
}
|
||||
|
||||
ENV NODE_VERSION=$node_version
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; \
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; `
|
||||
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \
|
||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; \
|
||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; \
|
||||
Expand-Archive node.zip -DestinationPath C:\ ; \
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; `
|
||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; `
|
||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; `
|
||||
Expand-Archive node.zip -DestinationPath C:\ ; `
|
||||
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
|
||||
|
||||
ENV YARN_VERSION=$yarn_version
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; \
|
||||
$sig = Get-AuthenticodeSignature yarn.msi ; \
|
||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; \
|
||||
Write-Output $sig.SignerCertificate.Thumbprint ; \
|
||||
if (@( \
|
||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', \
|
||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' \
|
||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; \
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; `
|
||||
$sig = Get-AuthenticodeSignature yarn.msi ; `
|
||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; `
|
||||
Write-Output $sig.SignerCertificate.Thumbprint ; `
|
||||
if (@( `
|
||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', `
|
||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' `
|
||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; `
|
||||
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
|
||||
|
||||
ENV GIT_VERSION 2.20.1
|
||||
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
|
||||
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; \
|
||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; \
|
||||
Expand-Archive git.zip -DestinationPath C:\git; \
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; `
|
||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; `
|
||||
Expand-Archive git.zip -DestinationPath C:\git; `
|
||||
Remove-Item git.zip
|
||||
|
||||
FROM $target as baseimage
|
||||
@ -74,30 +76,45 @@ COPY --from=download /git /git
|
||||
ARG SETX=/M
|
||||
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
|
||||
|
||||
CMD [ "node.exe" ]
|
||||
CMD [ "node.exe" ]
|
||||
|
||||
FROM baseimage
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; \
|
||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; \
|
||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; \
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; \
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; \
|
||||
Remove-Item msys2.tar.xz; \
|
||||
Remove-Item msys2.tar; \
|
||||
Remove-Item 7z.exe; \
|
||||
Remove-Item -Recurse 7zip; \
|
||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); \
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine); \
|
||||
Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe; \
|
||||
Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait; \
|
||||
Remove-Item vc_redist.x64.exe
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; `
|
||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; `
|
||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; `
|
||||
Remove-Item msys2.tar.xz; `
|
||||
Remove-Item msys2.tar; `
|
||||
Remove-Item 7z.exe; `
|
||||
Remove-Item -Recurse 7zip; `
|
||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
|
||||
SHELL ["cmd", "/c"]
|
||||
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
|
||||
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
|
||||
# Install VS Build Tools
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/df649173-11e9-4af2-8eb7-0eb02ba8958a/cadb5bdac41e55bb8f6a6b7c45273370/vs_buildtools.exe -OutFile vs_BuildTools.exe; `
|
||||
# Installer won't detect DOTNET_SKIP_FIRST_TIME_EXPERIENCE if ENV is used, must use setx /M
|
||||
setx /M DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1; `
|
||||
Start-Process vs_BuildTools.exe `
|
||||
-ArgumentList `
|
||||
'--add', 'Microsoft.VisualStudio.Workload.VCTools', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', `
|
||||
'--add', 'Microsoft.Component.VC.Runtime.UCRTSDK', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', `
|
||||
'--quiet', '--norestart', '--nocache' `
|
||||
-NoNewWindow -Wait; `
|
||||
Remove-Item -Force vs_buildtools.exe; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\"; `
|
||||
Remove-Item -Force -Recurse ${Env:TEMP}\*; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramData}\Package Cache\"; `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_VC', \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\", [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
CMD ["cmd.exe"]
|
||||
# Install Python
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile python-3.5.1.exe; `
|
||||
Start-Process python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait; `
|
||||
Remove-Item -Force python-3.5.1.exe
|
||||
|
||||
CMD ["cmd.exe"]
|
||||
|
@ -2,11 +2,17 @@
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CodeFresh and shared between
|
||||
# builds. This helps speed up the analysis time significantly with Bazel managed node dependencies
|
||||
# on the CI.
|
||||
# Save built files and downloaded repositories in a location that can be cached by CodeFresh and
|
||||
# shared between builds. This helps speed up the analysis time significantly with Bazel managed node
|
||||
# dependencies on the CI.
|
||||
# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system
|
||||
build --repository_cache=C:/codefresh/volume/bazel_repository_cache
|
||||
# Setting the output_base to a Docker volume is currently broken because of a Docker bug on Windows:
|
||||
# https://github.com/moby/moby/issues/37024
|
||||
# This affects Bazel because bazel_output_base\external\bazel_tools is an absolute path junction.
|
||||
# When its fixed we can uncomment this line, and use a different output_base for Ivy tests (they
|
||||
# use a separate compiler and destructively replace the cache).
|
||||
# startup --output_base=C:/codefresh/volume/bazel_output_base
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
@ -23,20 +29,10 @@ build --announce_rc
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CodeFresh VMs
|
||||
# TODO(filipesilva): determine the correct memory limit
|
||||
build --local_resources=8000,8.0,1.0
|
||||
build --local_resources=10240,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
|
||||
|
||||
# Include PATH in Windows build/tests
|
||||
# https://github.com/bazelbuild/rules_typescript/pull/356
|
||||
build --action_env=PATH
|
||||
test --action_env=PATH --test_env=PATH
|
||||
|
||||
# Exclude tests known to not work on Windows.
|
||||
|
||||
# Chrome web tests are currently broken.
|
||||
test --test_tag_filters=-browser:chromium-local
|
||||
|
@ -2,6 +2,7 @@ version: '1.0'
|
||||
|
||||
steps:
|
||||
BuildImage:
|
||||
title: Build Docker image
|
||||
type: build
|
||||
image_name: node-bazel-windows
|
||||
working_directory: ./.codefresh
|
||||
@ -12,7 +13,7 @@ steps:
|
||||
dockerfile: ./Dockerfile.win-1809
|
||||
|
||||
RunTests:
|
||||
title: Run Example
|
||||
title: Run Bazel tests
|
||||
image: ${{BuildImage}}
|
||||
commands:
|
||||
# Install dependencies
|
||||
@ -20,7 +21,8 @@ steps:
|
||||
# Add Bazel CI config
|
||||
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
|
||||
# Run tests
|
||||
- yarn bazel test //tools/ts-api-guardian:all //packages/language-service/test //packages/compiler/test //packages/compiler-cli/test:ngc //packages/compiler-cli/test/ngtsc:ngtsc
|
||||
- yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test
|
||||
- yarn bazel test //tools/public_api_guard/...
|
||||
- yarn bazel test //packages/compiler-cli/integrationtest:integrationtest //packages/compiler-cli/test/compliance:compliance
|
||||
# At the moment 'browser:chromium-local' are broken in CI while locally they work
|
||||
# VE
|
||||
- yarn bazel test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-browser:chromium-local //...
|
||||
# Ivy
|
||||
- yarn bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local //...
|
||||
|
@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
REM Fix for https://github.com/docker/for-win/issues/2920
|
||||
REM echo "Fixing msys64 folder..."
|
||||
REM Touch all .dll files inside C:\msys64\
|
||||
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
|
||||
REM echo "Fixed msys64 folder."
|
232
CHANGELOG.md
232
CHANGELOG.md
@ -1,3 +1,17 @@
|
||||
<a name="8.1.0-next.2"></a>
|
||||
# [8.1.0-next.2](https://github.com/angular/angular/compare/8.1.0-next.1...8.1.0-next.2) (2019-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** do not modify tsconfig.json ([#30877](https://github.com/angular/angular/issues/30877)) ([b086676](https://github.com/angular/angular/commit/b086676))
|
||||
* **bazel:** exclude components schematics from build ([#30825](https://github.com/angular/angular/issues/30825)) ([05a43ca](https://github.com/angular/angular/commit/05a43ca))
|
||||
* **bazel:** Load global stylesheet in dev and prod ([#30879](https://github.com/angular/angular/issues/30879)) ([17bfedd](https://github.com/angular/angular/commit/17bfedd))
|
||||
* **common:** expose the `HttpUploadProgressEvent` interface as public API ([#30852](https://github.com/angular/angular/issues/30852)) ([5c18f23](https://github.com/angular/angular/commit/5c18f23)), closes [#30814](https://github.com/angular/angular/issues/30814)
|
||||
* **service-worker:** avoid uncaught rejection warning when registration fails ([#30876](https://github.com/angular/angular/issues/30876)) ([81c2a94](https://github.com/angular/angular/commit/81c2a94))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.1"></a>
|
||||
## [8.0.1](https://github.com/angular/angular/compare/8.0.0...8.0.1) (2019-06-13)
|
||||
|
||||
@ -12,24 +26,83 @@
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-next.1"></a>
|
||||
# [8.1.0-next.1](https://github.com/angular/angular/compare/8.1.0-beta.0...8.1.0-next.1) (2019-06-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([80394ce](https://github.com/angular/angular/commit/80394ce)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com//github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-beta.0"></a>
|
||||
# [8.1.0-beta.0](https://github.com/angular/angular/compare/8.0.0...8.1.0-beta.0) (2019-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow ts_library interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([3125376](https://github.com/angular/angular/commit/3125376))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([1353bf0](https://github.com/angular/angular/commit/1353bf0))
|
||||
* **bazel:** Directly spawn native Bazel binary ([#30306](https://github.com/angular/angular/issues/30306)) ([2a0f497](https://github.com/angular/angular/commit/2a0f497))
|
||||
* **bazel:** Disable sandbox on Mac OS ([#30460](https://github.com/angular/angular/issues/30460)) ([b6b1aec](https://github.com/angular/angular/commit/b6b1aec))
|
||||
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([1f4c380](https://github.com/angular/angular/commit/1f4c380))
|
||||
* **bazel:** ng test should run specific ts_web_test_suite ([#30526](https://github.com/angular/angular/issues/30526)) ([e688e02](https://github.com/angular/angular/commit/e688e02))
|
||||
* **bazel:** pass correct arguments to http_server in Windows ([#30346](https://github.com/angular/angular/issues/30346)) ([3aff79c](https://github.com/angular/angular/commit/3aff79c)), closes [#29785](https://github.com/angular/angular/issues/29785)
|
||||
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([4ae0ee8](https://github.com/angular/angular/commit/4ae0ee8))
|
||||
* **bazel:** Use existing npm/yarn lock files ([#30438](https://github.com/angular/angular/issues/30438)) ([ff29ccc](https://github.com/angular/angular/commit/ff29ccc))
|
||||
* **compiler-cli:** log ngcc skipping messages as debug instead of info ([#30232](https://github.com/angular/angular/issues/30232)) ([60a8888](https://github.com/angular/angular/commit/60a8888))
|
||||
* **core:** consistently use ng:/// for sourcemap URLs ([#29826](https://github.com/angular/angular/issues/29826)) ([392473e](https://github.com/angular/angular/commit/392473e))
|
||||
* **core:** CSS sanitizer now allows parens in file names ([#30322](https://github.com/angular/angular/issues/30322)) ([728db88](https://github.com/angular/angular/commit/728db88))
|
||||
* **core:** fix interpolate identifier in AOT ([#30243](https://github.com/angular/angular/issues/30243)) ([30d1f29](https://github.com/angular/angular/commit/30d1f29))
|
||||
* **core:** migrations not always migrating all files ([#30269](https://github.com/angular/angular/issues/30269)) ([349935a](https://github.com/angular/angular/commit/349935a))
|
||||
* **core:** remove deprecated `TestBed.deprecatedOverrideProvider` API ([#30576](https://github.com/angular/angular/issues/30576)) ([a96976e](https://github.com/angular/angular/commit/a96976e))
|
||||
* **core:** require 'static' flag on queries in typings ([#30639](https://github.com/angular/angular/issues/30639)) ([84dd267](https://github.com/angular/angular/commit/84dd267))
|
||||
* **core:** static-query migration errors not printed properly ([#30458](https://github.com/angular/angular/issues/30458)) ([6ceb903](https://github.com/angular/angular/commit/6ceb903))
|
||||
* **core:** static-query migration fails with default parameter values ([#30269](https://github.com/angular/angular/issues/30269)) ([6357d4a](https://github.com/angular/angular/commit/6357d4a))
|
||||
* **core:** static-query migration should gracefully exit if AOT compiler throws ([#30269](https://github.com/angular/angular/issues/30269)) ([509352f](https://github.com/angular/angular/commit/509352f))
|
||||
* **core:** static-query migration should handle queries on accessors ([#30327](https://github.com/angular/angular/issues/30327)) ([0ffdb48](https://github.com/angular/angular/commit/0ffdb48))
|
||||
* **core:** static-query migration should not fallback to test strategy ([#30458](https://github.com/angular/angular/issues/30458)) ([0cdf598](https://github.com/angular/angular/commit/0cdf598))
|
||||
* **core:** static-query migration should not prompt if no queries are used ([#30254](https://github.com/angular/angular/issues/30254)) ([4c12d74](https://github.com/angular/angular/commit/4c12d74))
|
||||
* **core:** static-query usage migration strategy should detect ambiguous query usage ([#30215](https://github.com/angular/angular/issues/30215)) ([8d3365e](https://github.com/angular/angular/commit/8d3365e))
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a TextBed.get overload ([#30514](https://github.com/angular/angular/issues/30514)) ([f6bf892](https://github.com/angular/angular/commit/f6bf892)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **language-service:** Remove tsserverlibrary from rollup globals ([#30123](https://github.com/angular/angular/issues/30123)) ([124e497](https://github.com/angular/angular/commit/124e497))
|
||||
* **router:** ensure `history.state` is set in `eager` update mode ([#30154](https://github.com/angular/angular/issues/30154)) ([b40f6f3](https://github.com/angular/angular/commit/b40f6f3))
|
||||
* **router:** ensure navigations start with the current URL value incase redirect is skipped ([#30344](https://github.com/angular/angular/issues/30344)) ([0fd9d08](https://github.com/angular/angular/commit/0fd9d08)), closes [#30340](https://github.com/angular/angular/issues/30340) [#30160](https://github.com/angular/angular/issues/30160)
|
||||
* **router:** fix a problem with router not responding to back button ([#30160](https://github.com/angular/angular/issues/30160)) ([3327bd8](https://github.com/angular/angular/commit/3327bd8))
|
||||
* **router:** IE 11 bug can break URL unification when comparing objects ([#30393](https://github.com/angular/angular/issues/30393)) ([197584d](https://github.com/angular/angular/commit/197584d))
|
||||
* **router:** type cast correctly for IE 11 bug breaking URL Unification when comparing objects ([#30464](https://github.com/angular/angular/issues/30464)) ([53f3564](https://github.com/angular/angular/commit/53f3564))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** use `rbe_autoconfig()` and new container. ([#29336](https://github.com/angular/angular/issues/29336)) ([9abf114](https://github.com/angular/angular/commit/9abf114))
|
||||
* **common:** add ability to watch for AngularJS URL updates through `onUrlChange` hook ([#30466](https://github.com/angular/angular/issues/30466)) ([1aff524](https://github.com/angular/angular/commit/1aff524))
|
||||
* **common:** stricter types for `SlicePipe` ([#30156](https://github.com/angular/angular/issues/30156)) ([95830ee](https://github.com/angular/angular/commit/95830ee))
|
||||
* **core:** deprecate integration with the Web Tracing Framework (WTF) ([#30642](https://github.com/angular/angular/issues/30642)) ([f310a59](https://github.com/angular/angular/commit/f310a59))
|
||||
* **language-service:** Implement `definitionAndBoundSpan` ([#30125](https://github.com/angular/angular/issues/30125)) ([f491673](https://github.com/angular/angular/commit/f491673))
|
||||
* **platform-webworker:** deprecate platform-webworker ([#30642](https://github.com/angular/angular/issues/30642)) ([ccc76f7](https://github.com/angular/angular/commit/ccc76f7))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.0"></a>
|
||||
# [8.0.0](https://github.com/angular/angular/compare/8.0.0-rc.5...8.0.0) (2019-05-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for TypeScript 3.3 (and drop older versions) ([#29004](https://github.com/angular/angular/issues/29004)) ([75748d6](https://github.com/angular/angular/commit/75748d6))
|
||||
* add support for TypeScript 3.4 (and drop older versions) ([#29372](https://github.com/angular/angular/issues/29372)) ([ef85336](https://github.com/angular/angular/commit/ef85336))
|
||||
* **common:** add ability to watch for AngularJS URL updates through `onUrlChange` hook ([#30466](https://github.com/angular/angular/issues/30466)) ([8022d36](https://github.com/angular/angular/commit/8022d36))
|
||||
* **common:** stricter types for SlicePipe ([#30156](https://github.com/angular/angular/issues/30156)) ([722b2fa](https://github.com/angular/angular/commit/722b2fa))
|
||||
* **bazel:** use rbe_autoconfig() and new container. ([#29336](https://github.com/angular/angular/issues/29336)) ([e562acc](https://github.com/angular/angular/commit/e562acc))
|
||||
* **common:** add [@angular](https://github.com/angular)/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from Location service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** stricter types for `SlicePipe` ([#30156](https://github.com/angular/angular/issues/30156)) ([722b2fa](https://github.com/angular/angular/commit/722b2fa))
|
||||
* **bazel:** use `rbe_autoconfig()` and new container ([#29336](https://github.com/angular/angular/issues/29336)) ([e562acc](https://github.com/angular/angular/commit/e562acc))
|
||||
* **common:** add @angular/common/upgrade package for `$location`-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from `Location` service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** add ability to track all location changes ([#30055](https://github.com/angular/angular/issues/30055)) ([3a9cf3f](https://github.com/angular/angular/commit/3a9cf3f))
|
||||
* **common:** add APIs to read component pieces of URL ([#30055](https://github.com/angular/angular/issues/30055)) ([b635fe8](https://github.com/angular/angular/commit/b635fe8))
|
||||
* **common:** add MockPlatformLocation to enable more robust testing of Location services ([#30055](https://github.com/angular/angular/issues/30055)) ([d0672c2](https://github.com/angular/angular/commit/d0672c2))
|
||||
* **common:** add UrlCodec type for use with upgrade applications ([#30055](https://github.com/angular/angular/issues/30055)) ([ec455e1](https://github.com/angular/angular/commit/ec455e1))
|
||||
* **common:** add `MockPlatformLocation` to enable more robust testing of `Location` services ([#30055](https://github.com/angular/angular/issues/30055)) ([d0672c2](https://github.com/angular/angular/commit/d0672c2))
|
||||
* **common:** add `UrlCodec` type for use with upgrade applications ([#30055](https://github.com/angular/angular/issues/30055)) ([ec455e1](https://github.com/angular/angular/commit/ec455e1))
|
||||
* **common:** provide replacement for AngularJS $location service ([#30055](https://github.com/angular/angular/issues/30055)) ([4277600](https://github.com/angular/angular/commit/4277600))
|
||||
* remove deprecated DOCUMENT token from platform-browser ([#28117](https://github.com/angular/angular/issues/28117)) ([3a9d247](https://github.com/angular/angular/commit/3a9d247))
|
||||
* remove deprecated `DOCUMENT` token from platform-browser ([#28117](https://github.com/angular/angular/issues/28117)) ([3a9d247](https://github.com/angular/angular/commit/3a9d247))
|
||||
* **compiler:** support skipping leading trivia in template source-maps ([#30095](https://github.com/angular/angular/issues/30095)) ([304a12f](https://github.com/angular/angular/commit/304a12f))
|
||||
* **core:** add missing ARIA attributes to html sanitizer ([#29685](https://github.com/angular/angular/issues/29685)) ([909557d](https://github.com/angular/angular/commit/909557d)), closes [#26815](https://github.com/angular/angular/issues/26815)
|
||||
* **router:** deprecate loadChildren:string ([#30073](https://github.com/angular/angular/issues/30073)) ([c61df39](https://github.com/angular/angular/commit/c61df39))
|
||||
@ -38,28 +111,28 @@
|
||||
* **service-worker:** support bypassing SW with specific header/query param ([#30010](https://github.com/angular/angular/issues/30010)) ([6200732](https://github.com/angular/angular/commit/6200732)), closes [#21191](https://github.com/angular/angular/issues/21191)
|
||||
* **compiler-cli:** export tooling definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e1f51ea](https://github.com/angular/angular/commit/e1f51ea))
|
||||
* **compiler-cli:** lower some exported expressions ([#30038](https://github.com/angular/angular/issues/30038)) ([8e73f9b](https://github.com/angular/angular/commit/8e73f9b))
|
||||
* **core:** add schematics to move deprecated DOCUMENT import ([#29950](https://github.com/angular/angular/issues/29950)) ([645e305](https://github.com/angular/angular/commit/645e305))
|
||||
* **core:** add schematics to move deprecated `DOCUMENT` import ([#29950](https://github.com/angular/angular/issues/29950)) ([645e305](https://github.com/angular/angular/commit/645e305))
|
||||
* **bazel:** update the build to use the new architect api ([#29720](https://github.com/angular/angular/issues/29720)) ([902a53a](https://github.com/angular/angular/commit/902a53a))
|
||||
* remove [@angular](https://github.com/angular)/http dependency from [@angular](https://github.com/angular)/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* remove @angular/http dependency from @angular/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* **compiler-cli:** ngcc - make logging more configurable ([#29591](https://github.com/angular/angular/issues/29591)) ([8d3d75e](https://github.com/angular/angular/commit/8d3d75e))
|
||||
* **core:** Add "AbstractType<T>" interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** Add `AbstractType<T>` interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** template-var-assignment update schematic ([#29608](https://github.com/angular/angular/issues/29608)) ([7c8f4e3](https://github.com/angular/angular/commit/7c8f4e3))
|
||||
* **bazel:** Upgrade rules_nodejs and rules_sass ([#29388](https://github.com/angular/angular/issues/29388)) ([d6d081e](https://github.com/angular/angular/commit/d6d081e))
|
||||
* **service-worker:** support multiple apps on different subpaths of a domain ([#27080](https://github.com/angular/angular/issues/27080)) ([e721c08](https://github.com/angular/angular/commit/e721c08)), closes [#21388](https://github.com/angular/angular/issues/21388)
|
||||
* **bazel:** Eject Bazel ([#29167](https://github.com/angular/angular/issues/29167)) ([36a1550](https://github.com/angular/angular/commit/36a1550))
|
||||
* **bazel:** Hide Bazel files in Bazel builder ([#29110](https://github.com/angular/angular/issues/29110)) ([7060d90](https://github.com/angular/angular/commit/7060d90))
|
||||
* **forms:** clear (remove all) components from a FormArray ([#28918](https://github.com/angular/angular/issues/28918)) ([a68b1a1](https://github.com/angular/angular/commit/a68b1a1)), closes [#18531](https://github.com/angular/angular/issues/18531)
|
||||
* **platform-server:** wait on returned BEFORE_APP_SERIALIZED promises ([#29120](https://github.com/angular/angular/issues/29120)) ([7102ea8](https://github.com/angular/angular/commit/7102ea8))
|
||||
* **platform-server:** wait on returned `BEFORE_APP_SERIALIZED` promises ([#29120](https://github.com/angular/angular/issues/29120)) ([7102ea8](https://github.com/angular/angular/commit/7102ea8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow ts_library interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([bf38df4](https://github.com/angular/angular/commit/bf38df4))
|
||||
* **bazel:** allow `ts_library` interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([bf38df4](https://github.com/angular/angular/commit/bf38df4))
|
||||
* **bazel:** Disable sandbox on Mac OS ([#30460](https://github.com/angular/angular/issues/30460)) ([3de26a8](https://github.com/angular/angular/commit/3de26a8))
|
||||
* **bazel:** ng test should run specific ts_web_test_suite ([#30526](https://github.com/angular/angular/issues/30526)) ([8bc4da8](https://github.com/angular/angular/commit/8bc4da8))
|
||||
* **core:** remove deprecated `TestBed.deprecatedOverrideProvider` API ([#30576](https://github.com/angular/angular/issues/30576)) ([5a46f94](https://github.com/angular/angular/commit/5a46f94))
|
||||
* **core:** require 'static' flag on queries in typings ([#30641](https://github.com/angular/angular/issues/30641)) ([c8af830](https://github.com/angular/angular/commit/c8af830))
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a TextBed.get overload ([#30514](https://github.com/angular/angular/issues/30514)) ([561e01d](https://github.com/angular/angular/commit/561e01d)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a `TextBed.get` overload ([#30514](https://github.com/angular/angular/issues/30514)) ([561e01d](https://github.com/angular/angular/commit/561e01d)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **router:** type cast correctly for IE 11 bug breaking URL Unification when comparing objects ([#30464](https://github.com/angular/angular/issues/30464)) ([32daa93](https://github.com/angular/angular/commit/32daa93))
|
||||
* **bazel:** Directly spawn native Bazel binary ([#30306](https://github.com/angular/angular/issues/30306)) ([d1fcc2b](https://github.com/angular/angular/commit/d1fcc2b))
|
||||
* **bazel:** pass correct arguments to http_server in Windows ([#30346](https://github.com/angular/angular/issues/30346)) ([71eba45](https://github.com/angular/angular/commit/71eba45)), closes [#29785](https://github.com/angular/angular/issues/29785)
|
||||
@ -75,7 +148,7 @@
|
||||
* **core:** static-query usage migration strategy should detect ambiguous query usage ([#30215](https://github.com/angular/angular/issues/30215)) ([e295c6a](https://github.com/angular/angular/commit/e295c6a))
|
||||
* **router:** ensure navigations start with the current URL value incase redirect is skipped ([#30344](https://github.com/angular/angular/issues/30344)) ([9b88920](https://github.com/angular/angular/commit/9b88920)), closes [#30340](https://github.com/angular/angular/issues/30340) [#30160](https://github.com/angular/angular/issues/30160)
|
||||
* **router:** IE 11 bug can break URL unification when comparing objects ([#30393](https://github.com/angular/angular/issues/30393)) ([c383491](https://github.com/angular/angular/commit/c383491))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([9f68c35](https://github.com/angular/angular/commit/9f68c35))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for Windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([9f68c35](https://github.com/angular/angular/commit/9f68c35))
|
||||
* **compiler-cli:** log ngcc skipping messages as debug instead of info ([#30232](https://github.com/angular/angular/issues/30232)) ([548b003](https://github.com/angular/angular/commit/548b003))
|
||||
* **core:** fix interpolate identifier in AOT ([#30243](https://github.com/angular/angular/issues/30243)) ([3fe3a84](https://github.com/angular/angular/commit/3fe3a84))
|
||||
* **router:** ensure `history.state` is set in `eager` update mode ([#30154](https://github.com/angular/angular/issues/30154)) ([9720227](https://github.com/angular/angular/commit/9720227))
|
||||
@ -85,57 +158,54 @@
|
||||
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([6711f22](https://github.com/angular/angular/commit/6711f22))
|
||||
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([6067583](https://github.com/angular/angular/commit/6067583))
|
||||
* **bazel:** make name param in ng add optional ([#30074](https://github.com/angular/angular/issues/30074)) ([0b5f480](https://github.com/angular/angular/commit/0b5f480))
|
||||
* **bazel:** Make sure only single copy of `[@angular](https://github.com/angular)/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** Make sure only single copy of @angular/bazel is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** transitive npm deps in ng_module ([#30065](https://github.com/angular/angular/issues/30065)) ([61365a9](https://github.com/angular/angular/commit/61365a9))
|
||||
* **common:** add upgrade sub-package to ng_package rule for [@angular](https://github.com/angular)/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust MockPlatformLocation to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **common:** add upgrade sub-package to `ng_package` rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust `MockPlatformLocation` to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
* **bazel:** do not typecheck core schematic files ([#29876](https://github.com/angular/angular/issues/29876)) ([2ba799d](https://github.com/angular/angular/commit/2ba799d))
|
||||
* **bazel:** restore ng build --prod ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
||||
* **common:** prevent repeated application of HttpParams mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
||||
* **common:** async pipe will properly check when it recieves an NaN value from an observable ([#22305](https://github.com/angular/angular/issues/22305)) ([3f6bf6d](https://github.com/angular/angular/commit/3f6bf6d)), closes [#15721](https://github.com/angular/angular/issues/15721)
|
||||
* **core:** don't include a local `EventListener` in typings ([#29809](https://github.com/angular/angular/issues/29809)) ([4bde40f](https://github.com/angular/angular/commit/4bde40f)), closes [/github.com/angular/angular/blob/303eae918d997070a36b523ddc97e018f622c258/packages/core/src/debug/debug_node.ts#L32](https://github.com//github.com/angular/angular/blob/303eae918d997070a36b523ddc97e018f622c258/packages/core/src/debug/debug_node.ts/issues/L32) [#29806](https://github.com/angular/angular/issues/29806)
|
||||
* **core:** use shakeable global definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e5905bb](https://github.com/angular/angular/commit/e5905bb)), closes [/github.com/angular/angular-cli/blob/5fc1f2499cbe57f9a95e4b0dfced130eb3a8046d/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts#L279-L282](https://github.com//github.com/angular/angular-cli/blob/5fc1f2499cbe57f9a95e4b0dfced130eb3a8046d/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts/issues/L279-L282)
|
||||
* **bazel:** restore `ng build --prod` ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
||||
* **common:** prevent repeated application of `HttpParams` mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
||||
* **common:** async pipe will properly check when it receives an NaN value from an observable ([#22305](https://github.com/angular/angular/issues/22305)) ([3f6bf6d](https://github.com/angular/angular/commit/3f6bf6d)), closes [#15721](https://github.com/angular/angular/issues/15721)
|
||||
* **core:** don't include a local `EventListener` in typings ([#29809](https://github.com/angular/angular/issues/29809)) ([4bde40f](https://github.com/angular/angular/commit/4bde40f)), closes [#29806](https://github.com/angular/angular/issues/29806)
|
||||
* **core:** use shakeable global definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e5905bb](https://github.com/angular/angular/commit/e5905bb))
|
||||
* **language-service:** Use proper types instead of any ([#29942](https://github.com/angular/angular/issues/29942)) ([1a56cd5](https://github.com/angular/angular/commit/1a56cd5))
|
||||
* **bazel:** Install packages after `ng add` when invoked independently ([#29852](https://github.com/angular/angular/issues/29852)) ([bd2ce9c](https://github.com/angular/angular/commit/bd2ce9c))
|
||||
* **compiler-cli:** pass config path to ts.parseJsonConfigFileContent ([#29872](https://github.com/angular/angular/issues/29872)) ([86a3f90](https://github.com/angular/angular/commit/86a3f90)), closes [/github.com/Microsoft/TypeScript/blob/025d82633915b67003ea38ba40b9239a19721c13/src/compiler/emitter.ts#L56-L57](https://github.com//github.com/Microsoft/TypeScript/blob/025d82633915b67003ea38ba40b9239a19721c13/src/compiler/emitter.ts/issues/L56-L57)
|
||||
* **compiler-cli:** pass config path to `ts.parseJsonConfigFileContent` ([#29872](https://github.com/angular/angular/issues/29872)) ([86a3f90](https://github.com/angular/angular/commit/86a3f90))
|
||||
* **router:** support non-NgFactory promise in loadChildren typings ([#29832](https://github.com/angular/angular/issues/29832)) ([2bfb6a0](https://github.com/angular/angular/commit/2bfb6a0))
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//[@angular](https://github.com/angular)/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** add `configuration_env_vars = ["compile"]` to generated `@npm//@angular/bazel/bin:ngc-wrapped` `nodejs_binary` ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** docs formatting ([#29817](https://github.com/angular/angular/issues/29817)) ([cc2e4b6](https://github.com/angular/angular/commit/cc2e4b6))
|
||||
* **bazel:** remove karma-jasmine from ts_web_test_suite ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** remove karma-jasmine from `ts_web_test_suite` ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** support running ng-add on minimal applications ([#29681](https://github.com/angular/angular/issues/29681)) ([9810c6c](https://github.com/angular/angular/commit/9810c6c)), closes [#29680](https://github.com/angular/angular/issues/29680)
|
||||
* **common:** add `@Injectable()` to common pipes ([#29834](https://github.com/angular/angular/issues/29834)) ([387fbb8](https://github.com/angular/angular/commit/387fbb8))
|
||||
* **compiler-cli:** ensure LogicalProjectPaths always start with a slash ([#29627](https://github.com/angular/angular/issues/29627)) ([e02684e](https://github.com/angular/angular/commit/e02684e))
|
||||
* **compiler-cli:** ensure `LogicalProjectPaths` always start with a slash ([#29627](https://github.com/angular/angular/issues/29627)) ([e02684e](https://github.com/angular/angular/commit/e02684e))
|
||||
* **core:** add missing migration to npm package ([#29705](https://github.com/angular/angular/issues/29705)) ([96b76dc](https://github.com/angular/angular/commit/96b76dc))
|
||||
* **core:** call ngOnDestroy for tree-shakeable providers ([#28943](https://github.com/angular/angular/issues/28943)) ([30b0442](https://github.com/angular/angular/commit/30b0442)), closes [#28927](https://github.com/angular/angular/issues/28927)
|
||||
* **core:** Deprecate TestBed.get(...):any ([#29290](https://github.com/angular/angular/issues/29290)) ([609024f](https://github.com/angular/angular/commit/609024f)), closes [#13785](https://github.com/angular/angular/issues/13785) [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** call `ngOnDestroy` for tree-shakeable providers ([#28943](https://github.com/angular/angular/issues/28943)) ([30b0442](https://github.com/angular/angular/commit/30b0442)), closes [#28927](https://github.com/angular/angular/issues/28927)
|
||||
* **core:** Deprecate `TestBed.get(...):any` ([#29290](https://github.com/angular/angular/issues/29290)) ([609024f](https://github.com/angular/angular/commit/609024f)), closes [#13785](https://github.com/angular/angular/issues/13785) [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** resolve ts compile issues due to lenient tsconfig ([#29843](https://github.com/angular/angular/issues/29843)) ([54058ba](https://github.com/angular/angular/commit/54058ba))
|
||||
* **platform-browser:** insert APP_ID in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([712d60e](https://github.com/angular/angular/commit/712d60e))
|
||||
* **bazel:** use //:tsconfig.json as the default for ng_module ([#29670](https://github.com/angular/angular/issues/29670)) ([#29711](https://github.com/angular/angular/issues/29711)) ([9e33dc3](https://github.com/angular/angular/commit/9e33dc3))
|
||||
* **platform-browser:** insert APP_ID in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([ca14509](https://github.com/angular/angular/commit/ca14509))
|
||||
* **platform-browser:** insert `APP_ID` in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([712d60e](https://github.com/angular/angular/commit/712d60e))
|
||||
* **bazel:** Update schematics to support routing ([#29548](https://github.com/angular/angular/issues/29548)) ([401b8ee](https://github.com/angular/angular/commit/401b8ee))
|
||||
* **bazel:** use //:tsconfig.json as the default for ng_module ([#29670](https://github.com/angular/angular/issues/29670)) ([b14537a](https://github.com/angular/angular/commit/b14537a))
|
||||
* **bazel:** use `//:tsconfig.json` as the default for `ng_module` ([#29670](https://github.com/angular/angular/issues/29670)) ([b14537a](https://github.com/angular/angular/commit/b14537a))
|
||||
* **compiler-cli:** ngcc - cope with processing entry-points multiple times ([#29657](https://github.com/angular/angular/issues/29657)) ([6b39c9c](https://github.com/angular/angular/commit/6b39c9c))
|
||||
* **core:** static-query schematic should detect static queries in getters. ([#29609](https://github.com/angular/angular/issues/29609)) ([33016b8](https://github.com/angular/angular/commit/33016b8))
|
||||
* **common:** escape query selector used when anchor scrolling ([#29577](https://github.com/angular/angular/issues/29577)) ([7671c73](https://github.com/angular/angular/commit/7671c73)), closes [#28193](https://github.com/angular/angular/issues/28193)
|
||||
* **router:** adjust setting navigationTransition when a new navigation cancels an existing one ([#29636](https://github.com/angular/angular/issues/29636)) ([e884c0c](https://github.com/angular/angular/commit/e884c0c)), closes [#29389](https://github.com/angular/angular/issues/29389) [#29590](https://github.com/angular/angular/issues/29590)
|
||||
* **bazel:** allow ng_module users to set createExternalSymbolFactoryReexports ([#29459](https://github.com/angular/angular/issues/29459)) ([21be0fb](https://github.com/angular/angular/commit/21be0fb))
|
||||
* **bazel:** allow `ng_module` users to set `createExternalSymbolFactoryReexports` ([#29459](https://github.com/angular/angular/issues/29459)) ([21be0fb](https://github.com/angular/angular/commit/21be0fb))
|
||||
* **bazel:** workaround problem reading summary files from node_modules ([#29459](https://github.com/angular/angular/issues/29459)) ([769d960](https://github.com/angular/angular/commit/769d960))
|
||||
* **compiler:** inherit param types when class has a constructor which takes no declared parameters and delegates up ([#29232](https://github.com/angular/angular/issues/29232)) ([0007564](https://github.com/angular/angular/commit/0007564))
|
||||
* **core:** parse incorrect ML open tag as text ([#29328](https://github.com/angular/angular/issues/29328)) ([dafbbf8](https://github.com/angular/angular/commit/dafbbf8)), closes [#29231](https://github.com/angular/angular/issues/29231)
|
||||
* **core:** static-query schematic should detect queries in "ngDoCheck" and "ngOnChanges" ([#29492](https://github.com/angular/angular/issues/29492)) ([09fab58](https://github.com/angular/angular/commit/09fab58))
|
||||
* **router:** support NgFactory promise in loadChildren typings ([#29392](https://github.com/angular/angular/issues/29392)) ([26a8c59](https://github.com/angular/angular/commit/26a8c59))
|
||||
* **core:** static-query schematic should detect queries in `ngDoCheck` and `ngOnChanges` ([#29492](https://github.com/angular/angular/issues/29492)) ([09fab58](https://github.com/angular/angular/commit/09fab58))
|
||||
* **router:** support `NgFactory` promise in loadChildren typings ([#29392](https://github.com/angular/angular/issues/29392)) ([26a8c59](https://github.com/angular/angular/commit/26a8c59))
|
||||
* **bazel:** correct regexp test for self-references in metadata ([#29346](https://github.com/angular/angular/issues/29346)) ([9d090cb](https://github.com/angular/angular/commit/9d090cb))
|
||||
* **bazel:** don't produce self-references in metadata ([#29317](https://github.com/angular/angular/issues/29317)) ([3facdeb](https://github.com/angular/angular/commit/3facdeb)), closes [#29315](https://github.com/angular/angular/issues/29315)
|
||||
* **bazel:** fix strict null checks compile error in packages/bazel/src/schematics/ng-add/index.ts ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove [@angular](https://github.com/angular)/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** fix strict null checks compile error in `packages/bazel/src/schematics/ng-add/index.ts` ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove @angular/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Support new e2e project layout ([#29318](https://github.com/angular/angular/issues/29318)) ([8ef690c](https://github.com/angular/angular/commit/8ef690c))
|
||||
* **bazel:** turn off pure call tree shaking for ng_package ([#29210](https://github.com/angular/angular/issues/29210)) ([4990b93](https://github.com/angular/angular/commit/4990b93))
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf)), closes [/github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf))
|
||||
* **core:** don't wrap `<tr>` and `<col>` elements into a required parent ([#29219](https://github.com/angular/angular/issues/29219)) ([f2dc32e](https://github.com/angular/angular/commit/f2dc32e))
|
||||
* **core:** parse incorrect ML open tag as text ([#29328](https://github.com/angular/angular/issues/29328)) ([4605df8](https://github.com/angular/angular/commit/4605df8)), closes [#29231](https://github.com/angular/angular/issues/29231)
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([cf8d934](https://github.com/angular/angular/commit/cf8d934)), closes [github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85)
|
||||
* **bazel:** add missing binary path for api-extractor ([#29202](https://github.com/angular/angular/issues/29202)) ([df354d1](https://github.com/angular/angular/commit/df354d1))
|
||||
* **bazel:** ng build should produce prod bundle ([#29136](https://github.com/angular/angular/issues/29136)) ([14ce8a9](https://github.com/angular/angular/commit/14ce8a9))
|
||||
* **compiler:** ensure template is updated if an output is transformed ([#29041](https://github.com/angular/angular/issues/29041)) ([c7e4931](https://github.com/angular/angular/commit/c7e4931))
|
||||
@ -149,16 +219,16 @@
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **bazel:** @bazel/typescript is now a peerDependency of @angular/bazel so user's of @angular/bazel must add @bazel/typescript to their package.json
|
||||
* **bazel:** ng_module now depends on a minimum of build_bazel_rules_nodejs 0.27.12
|
||||
* **core:** In Angular version 8, it's required that all @ViewChild and @ContentChild
|
||||
queries have a 'static' flag specifying whether the query is 'static' or
|
||||
* **bazel:** @bazel/typescript is now a peerDependency of @angular/bazel so users of @angular/bazel must add @bazel/typescript to their package.json
|
||||
* **bazel:** `ng_module` now depends on a minimum of build_bazel_rules_nodejs 0.27.12
|
||||
* **core:** In Angular version 8, it's required that all `@ViewChild` and `@ContentChild`
|
||||
queries have a `'static'` flag specifying whether the query is 'static' or
|
||||
'dynamic'. The compiler previously sorted queries automatically, but in
|
||||
8.0 developers are required to explicitly specify which behavior is wanted.
|
||||
This is a temporary requirement as part of a migration; see
|
||||
https://v8.angular.io/guide/static-query-migration for more details.
|
||||
[static query migration guide](https://v8.angular.io/guide/static-query-migration) for more details.
|
||||
|
||||
@ViewChildren and @ContentChildren queries are always dynamic, and so are
|
||||
`@ViewChildren` and `@ContentChildren` queries are always dynamic, and so are
|
||||
unaffected.
|
||||
|
||||
* `TestBed.get()` has two signatures, one which is typed and another which accepts and returns `any`. The signature for `any` is deprecated; all usage of `TestBed.get()` should go through the typed API. This mainly affects string tokens
|
||||
@ -192,8 +262,8 @@ https://v8.angular.io/guide/static-query-migration for more details.
|
||||
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
|
||||
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
|
||||
|
||||
This meachanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that has rules for parent type);
|
||||
This mechanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that have rules for parent type);
|
||||
- it is incorrect for cases where `<tr>` / `<col>` are at the root of a component's content, ex.:
|
||||
|
||||
```html
|
||||
@ -205,10 +275,13 @@ https://v8.angular.io/guide/static-query-migration for more details.
|
||||
In the above example the `<projecting-tr-inside-tbody>` component could be "surprised" to see additional
|
||||
`<tbody>` elements inserted by Angular HTML parser.
|
||||
|
||||
* **http:** The deprecated @angular/http package has been removed, the @angular/common/http package should be used instead.
|
||||
For details on how to migrate, please refer to [the deprecations guide](https://angular.io/guide/deprecations#angularhttp).
|
||||
|
||||
|
||||
* TypeScript 3.1 and 3.2 are no longer supported.
|
||||
|
||||
Please update your TypeScript version to 3.3
|
||||
Please update your TypeScript version to 3.4, as version 3.3 is also not supported.
|
||||
|
||||
|
||||
<a name="8.0.0-rc.5"></a>
|
||||
@ -294,6 +367,17 @@ unaffected.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="7.2.15"></a>
|
||||
## [7.2.15](https://github.com/angular/angular/compare/7.2.14...7.2.15) (2019-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([#30107](https://github.com/angular/angular/issues/30107)) ([1084c19](https://github.com/angular/angular/commit/1084c19)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
|
||||
|
||||
|
||||
<a name="8.0.0-rc.2"></a>
|
||||
# [8.0.0-rc.2](https://github.com/angular/angular/compare/8.0.0-rc.1...8.0.0-rc.2) (2019-04-29)
|
||||
|
||||
@ -323,9 +407,9 @@ unaffected.
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** make name param in ng add optional ([#30074](https://github.com/angular/angular/issues/30074)) ([0b5f480](https://github.com/angular/angular/commit/0b5f480))
|
||||
* **bazel:** Make sure only single copy of `[@angular](https://github.com/angular)/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** Make sure only single copy of `@angular/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** transitive npm deps in ng_module ([#30065](https://github.com/angular/angular/issues/30065)) ([61365a9](https://github.com/angular/angular/commit/61365a9))
|
||||
* **common:** add upgrade sub-package to ng_package rule for [@angular](https://github.com/angular)/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** add upgrade sub-package to ng_package rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust MockPlatformLocation to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
@ -333,7 +417,7 @@ unaffected.
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add [@angular](https://github.com/angular)/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add @angular/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from Location service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** add ability to track all location changes ([#30055](https://github.com/angular/angular/issues/30055)) ([3a9cf3f](https://github.com/angular/angular/commit/3a9cf3f))
|
||||
* **common:** add APIs to read component pieces of URL ([#30055](https://github.com/angular/angular/issues/30055)) ([b635fe8](https://github.com/angular/angular/commit/b635fe8))
|
||||
@ -396,7 +480,7 @@ unaffected.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//[@angular](https://github.com/angular)/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//@angular/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** docs formatting ([#29817](https://github.com/angular/angular/issues/29817)) ([cc2e4b6](https://github.com/angular/angular/commit/cc2e4b6))
|
||||
* **bazel:** remove karma-jasmine from ts_web_test_suite ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** support running ng-add on minimal applications ([#29681](https://github.com/angular/angular/issues/29681)) ([9810c6c](https://github.com/angular/angular/commit/9810c6c)), closes [#29680](https://github.com/angular/angular/issues/29680)
|
||||
@ -471,7 +555,7 @@ let service = TestBed.get(SERVICE_TOKEN); // type Service
|
||||
|
||||
### Features
|
||||
|
||||
* remove [@angular](https://github.com/angular)/http dependency from [@angular](https://github.com/angular)/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* remove @angular/http dependency from @angular/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* **compiler-cli:** ngcc - make logging more configurable ([#29591](https://github.com/angular/angular/issues/29591)) ([8d3d75e](https://github.com/angular/angular/commit/8d3d75e))
|
||||
* **core:** Add "AbstractType<T>" interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** template-var-assignment update schematic ([#29608](https://github.com/angular/angular/issues/29608)) ([7c8f4e3](https://github.com/angular/angular/commit/7c8f4e3))
|
||||
@ -531,7 +615,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** correct regexp test for self-references in metadata ([#29346](https://github.com/angular/angular/issues/29346)) ([9d090cb](https://github.com/angular/angular/commit/9d090cb))
|
||||
* **bazel:** don't produce self-references in metadata ([#29317](https://github.com/angular/angular/issues/29317)) ([3facdeb](https://github.com/angular/angular/commit/3facdeb)), closes [#29315](https://github.com/angular/angular/issues/29315)
|
||||
* **bazel:** fix strict null checks compile error in packages/bazel/src/schematics/ng-add/index.ts ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove [@angular](https://github.com/angular)/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Remove @angular/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Support new e2e project layout ([#29318](https://github.com/angular/angular/issues/29318)) ([8ef690c](https://github.com/angular/angular/commit/8ef690c))
|
||||
* **bazel:** turn off pure call tree shaking for ng_package ([#29210](https://github.com/angular/angular/issues/29210)) ([4990b93](https://github.com/angular/angular/commit/4990b93))
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf)), closes [/github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
|
||||
@ -552,8 +636,8 @@ This release contains various API docs improvements.
|
||||
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
|
||||
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
|
||||
|
||||
This meachanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that has rules for parent type);
|
||||
This mechanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that have rules for parent type);
|
||||
- it is incorrect for cases where `<tr>` / `<col>` are at the root of a component's content, ex.:
|
||||
|
||||
```html
|
||||
@ -954,7 +1038,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** unable to launch protractor test on windows ([#27850](https://github.com/angular/angular/issues/27850)) ([1e6c9be](https://github.com/angular/angular/commit/1e6c9be))
|
||||
* **bazel:** devserver entry_module should have underscore name ([#27719](https://github.com/angular/angular/issues/27719)) ([f57916c](https://github.com/angular/angular/commit/f57916c))
|
||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([522919a](https://github.com/angular/angular/commit/522919a))
|
||||
* **bazel:** fix major/minor semver check between [@angular](https://github.com/angular)/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([8313ffc](https://github.com/angular/angular/commit/8313ffc))
|
||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([44dfa60](https://github.com/angular/angular/commit/44dfa60)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([85866de](https://github.com/angular/angular/commit/85866de))
|
||||
@ -974,7 +1058,7 @@ This release contains various API docs improvements.
|
||||
* **core:** export a value for InjectFlags ([#27279](https://github.com/angular/angular/issues/27279)) ([23b06af](https://github.com/angular/angular/commit/23b06af)), closes [#27251](https://github.com/angular/angular/issues/27251)
|
||||
* **core:** More precise return type for `InjectableDecorator` ([#27360](https://github.com/angular/angular/issues/27360)) ([4b9948c](https://github.com/angular/angular/commit/4b9948c)), closes [#26942](https://github.com/angular/angular/issues/26942)
|
||||
* **forms:** typed argument for FormBuilder group ([#26985](https://github.com/angular/angular/issues/26985)) ([b0c7561](https://github.com/angular/angular/commit/b0c7561))
|
||||
* **platform-server:** add [@angular](https://github.com/angular)/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([32c5be9](https://github.com/angular/angular/commit/32c5be9)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([32c5be9](https://github.com/angular/angular/commit/32c5be9)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **router:** ensure URL is updated after second redirect with UrlUpdateStrategy="eager" ([#27523](https://github.com/angular/angular/issues/27523)) ([ad26cd6](https://github.com/angular/angular/commit/ad26cd6)), closes [#27116](https://github.com/angular/angular/issues/27116)
|
||||
* **router:** update URL after redirects when urlHandlingStrategy='eager' ([#27356](https://github.com/angular/angular/issues/27356)) ([11a8bd8](https://github.com/angular/angular/commit/11a8bd8)), closes [#27076](https://github.com/angular/angular/issues/27076)
|
||||
* **upgrade:** allow nesting components from different downgraded modules ([#27217](https://github.com/angular/angular/issues/27217)) ([bc0ee01](https://github.com/angular/angular/commit/bc0ee01))
|
||||
@ -994,7 +1078,7 @@ This release contains various API docs improvements.
|
||||
* **animations:** mark actual descendant node as disabled ([#26180](https://github.com/angular/angular/issues/26180)) ([453589f](https://github.com/angular/angular/commit/453589f))
|
||||
* **bazel:** devserver entry_module should have underscore name ([#27719](https://github.com/angular/angular/issues/27719)) ([b108e9a](https://github.com/angular/angular/commit/b108e9a))
|
||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([0d8528b](https://github.com/angular/angular/commit/0d8528b))
|
||||
* **bazel:** fix major/minor semver check between [@angular](https://github.com/angular)/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([89ace1a](https://github.com/angular/angular/commit/89ace1a))
|
||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([ef056c5](https://github.com/angular/angular/commit/ef056c5)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27591](https://github.com/angular/angular/issues/27591)) ([93078e3](https://github.com/angular/angular/commit/93078e3))
|
||||
@ -1026,7 +1110,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** ng_package should correctly map to source maps in secondary entry-points ([#27313](https://github.com/angular/angular/issues/27313)) ([fc2c23e](https://github.com/angular/angular/commit/fc2c23e)), closes [#25510](https://github.com/angular/angular/issues/25510)
|
||||
* **compiler-cli:** flatModuleIndex files not generated on windows with multiple input files ([#27200](https://github.com/angular/angular/issues/27200)) ([8087b6b](https://github.com/angular/angular/commit/8087b6b))
|
||||
* **compiler-cli:** ngtsc shim files not being generated on case-insensitive platforms ([#27466](https://github.com/angular/angular/issues/27466)) ([84f2928](https://github.com/angular/angular/commit/84f2928)), closes [/github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts#L681-L682](https://github.com//github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts/issues/L681-L682)
|
||||
* **platform-server:** add [@angular](https://github.com/angular)/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
|
||||
|
||||
|
||||
@ -1053,7 +1137,7 @@ This release contains various API docs improvements.
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
* **bazel:** unknown replay compiler error in windows ([#26711](https://github.com/angular/angular/issues/26711)) ([aed95fd](https://github.com/angular/angular/commit/aed95fd))
|
||||
* **core:** ensure that `ɵdefineNgModule` is available in flat-file formats ([#26403](https://github.com/angular/angular/issues/26403)) ([a64859b](https://github.com/angular/angular/commit/a64859b))
|
||||
@ -1160,7 +1244,7 @@ This release contains various API docs improvements.
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
|
||||
|
||||
@ -1172,7 +1256,7 @@ This release contains various API docs improvements.
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([c01f340](https://github.com/angular/angular/commit/c01f340))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([#26879](https://github.com/angular/angular/issues/26879)) ([257ac83](https://github.com/angular/angular/commit/257ac83))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([#26879](https://github.com/angular/angular/issues/26879)) ([257ac83](https://github.com/angular/angular/commit/257ac83))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([b3c6409](https://github.com/angular/angular/commit/b3c6409))
|
||||
|
||||
|
||||
@ -1249,7 +1333,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **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:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
|
||||
* **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))
|
||||
* **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)
|
||||
* **core:** add missing `peerDependency ` to `@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)
|
||||
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
||||
* **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))
|
||||
@ -1695,7 +1779,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **upgrade:** propagate return value of resumeBootstrap ([#22754](https://github.com/angular/angular/issues/22754)) ([a2330ff](https://github.com/angular/angular/commit/a2330ff)), closes [#22723](https://github.com/angular/angular/issues/22723)
|
||||
* **upgrade:** two-way binding and listening for event ([#22772](https://github.com/angular/angular/issues/22772)) ([2b3de63](https://github.com/angular/angular/commit/2b3de63)), closes [#22734](https://github.com/angular/angular/issues/22734)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([8a85888](https://github.com/angular/angular/commit/8a85888)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([f089bf5](https://github.com/angular/angular/commit/f089bf5))
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([f089bf5](https://github.com/angular/angular/commit/f089bf5))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([13ab91e](https://github.com/angular/angular/commit/13ab91e)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -1830,7 +1914,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **router:** don't mutate route configs ([#22358](https://github.com/angular/angular/issues/22358)) ([8f0a064](https://github.com/angular/angular/commit/8f0a064)), closes [#22203](https://github.com/angular/angular/issues/22203)
|
||||
* **router:** fix URL serialization so special characters are only encoded where needed ([#22337](https://github.com/angular/angular/issues/22337)) ([789a47e](https://github.com/angular/angular/commit/789a47e)), closes [#10280](https://github.com/angular/angular/issues/10280)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([4aef9de](https://github.com/angular/angular/commit/4aef9de)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([a9a0e27](https://github.com/angular/angular/commit/a9a0e27)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -1844,7 +1928,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **platform-server:** generate correct stylings for camel case names ([#22263](https://github.com/angular/angular/issues/22263)) ([de02a7a](https://github.com/angular/angular/commit/de02a7a)), closes [#19235](https://github.com/angular/angular/issues/19235)
|
||||
* **router:** don't mutate route configs ([#22358](https://github.com/angular/angular/issues/22358)) ([8f0a064](https://github.com/angular/angular/commit/8f0a064)), closes [#22203](https://github.com/angular/angular/issues/22203)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([4aef9de](https://github.com/angular/angular/commit/4aef9de)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([a9a0e27](https://github.com/angular/angular/commit/a9a0e27)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -2291,7 +2375,7 @@ Note: Due to an animation fix back in 5.1.1 ([c2b3792](https://github.com/angula
|
||||
* **platform-server:** provide a way to hook into renderModule* ([#19023](https://github.com/angular/angular/issues/19023)) ([8dfc3c3](https://github.com/angular/angular/commit/8dfc3c3))
|
||||
* **router:** add ActivationStart/End events ([8f79150](https://github.com/angular/angular/commit/8f79150))
|
||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
* **service-worker:** introduce the [@angular](https://github.com/angular)/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
* **service-worker:** introduce the @angular/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
* update angular to support TypeScript 2.4 ([ca5aeba](https://github.com/angular/angular/commit/ca5aeba))
|
||||
@ -2515,7 +2599,7 @@ Because of multiple bugs and browser inconsistencies, we have dropped the intl a
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([1cc3fe2](https://github.com/angular/angular/commit/1cc3fe2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix improper packaging for @angular/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([8821723](https://github.com/angular/angular/commit/8821723)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([497e017](https://github.com/angular/angular/commit/497e017))
|
||||
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([7d137d7](https://github.com/angular/angular/commit/7d137d7)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||
@ -2856,7 +2940,7 @@ Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1
|
||||
* **compiler-cli:** allow '==' to compare nullable types ([#16731](https://github.com/angular/angular/issues/16731)) ([d761059](https://github.com/angular/angular/commit/d761059))
|
||||
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([f0f6544](https://github.com/angular/angular/commit/f0f6544)), closes [#15578](https://github.com/angular/angular/issues/15578)
|
||||
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([fcc91d8](https://github.com/angular/angular/commit/fcc91d8)), closes [#14321](https://github.com/angular/angular/issues/14321)
|
||||
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9da6340](https://github.com/angular/angular/commit/9da6340)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** flatten metadata for @angular/http/testing ([9da6340](https://github.com/angular/angular/commit/9da6340)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** honor RequestArgs.search and RequestArgs.params map type ([aef5245](https://github.com/angular/angular/commit/aef5245)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
|
||||
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([7ae7a84](https://github.com/angular/angular/commit/7ae7a84)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
|
||||
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([5d4b36f](https://github.com/angular/angular/commit/5d4b36f)), closes [#16310](https://github.com/angular/angular/issues/16310)
|
||||
@ -2912,7 +2996,7 @@ Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1
|
||||
* **compiler:** avoid a `...null` spread in extraction ([#16547](https://github.com/angular/angular/issues/16547)) ([d0e1688](https://github.com/angular/angular/commit/d0e1688))
|
||||
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([ee6705a](https://github.com/angular/angular/commit/ee6705a)), closes [#15578](https://github.com/angular/angular/issues/15578)
|
||||
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([9218812](https://github.com/angular/angular/commit/9218812)), closes [#14321](https://github.com/angular/angular/issues/14321)
|
||||
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** flatten metadata for @angular/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** honor RequestArgs.search and RequestArgs.params map type ([63066f7](https://github.com/angular/angular/commit/63066f7)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
|
||||
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([ec3b6e9](https://github.com/angular/angular/commit/ec3b6e9)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
|
||||
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([915eae5](https://github.com/angular/angular/commit/915eae5)), closes [#16310](https://github.com/angular/angular/issues/16310)
|
||||
@ -3542,7 +3626,7 @@ Note: the 4.0.0-rc.0 release on npm accidentally omitted one bug fix, so we cut
|
||||
* **core:** add isStable Observable property to ApplicationRef to indicate when it's stable and unstable ([#14337](https://github.com/angular/angular/issues/14337)) ([c481798](https://github.com/angular/angular/commit/c481798))
|
||||
* **platform-server:** add API to render Module and ModuleFactory to string ([#14381](https://github.com/angular/angular/issues/14381)) ([b4d444a](https://github.com/angular/angular/commit/b4d444a))
|
||||
* **platform-server:** Implement PlatformLocation for platformServer() ([#14405](https://github.com/angular/angular/issues/14405)) ([9e28568](https://github.com/angular/angular/commit/9e28568))
|
||||
* **platform-server:** support [@angular](https://github.com/angular)/http from [@angular](https://github.com/angular)/platform-server ([9559d3e](https://github.com/angular/angular/commit/9559d3e))
|
||||
* **platform-server:** support @angular/http from @angular/platform-server ([9559d3e](https://github.com/angular/angular/commit/9559d3e))
|
||||
* **tsc-wrapped:** add an option to `ngc` to bundle metadata ([#14509](https://github.com/angular/angular/issues/14509)) ([3b89670](https://github.com/angular/angular/commit/3b89670))
|
||||
|
||||
|
||||
@ -4503,7 +4587,7 @@ Note: The 2.2.0-beta.0 release also contains all the changes present in the 2.1.
|
||||
* **compiler:** properly shim `:host:before` and `:host(:before)` ([#12171](https://github.com/angular/angular/issues/12171)) ([aa92512](https://github.com/angular/angular/commit/aa92512)), closes [#12165](https://github.com/angular/angular/issues/12165)
|
||||
* **compiler:** validate `@HostBinding` name ([#12139](https://github.com/angular/angular/issues/12139)) ([13ecc14](https://github.com/angular/angular/commit/13ecc14))
|
||||
* **compiler-cli:** don't clone static symbols when simplifying annotation metadata ([#12158](https://github.com/angular/angular/issues/12158)) ([8c477b2](https://github.com/angular/angular/commit/8c477b2))
|
||||
* **compiler-cli:** remove peerDependency on [@angular](https://github.com/angular)/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove peerDependency on @angular/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove unused parse5 dependency from package.json ([eaaec69](https://github.com/angular/angular/commit/eaaec69))
|
||||
* **forms:** allow optional fields with pattern and minlength validators ([#12147](https://github.com/angular/angular/issues/12147)) ([d22eeb7](https://github.com/angular/angular/commit/d22eeb7))
|
||||
* **forms:** properly validate blank strings with minlength ([#12091](https://github.com/angular/angular/issues/12091)) ([f50c1da](https://github.com/angular/angular/commit/f50c1da))
|
||||
|
@ -19,7 +19,7 @@
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
|
||||
"build-with-ivy": "yarn ~~build",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 7da10691d",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 032be4e17",
|
||||
"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",
|
||||
|
@ -81,6 +81,9 @@ See also: [`//.bazelrc`](https://github.com/angular/angular/blob/master/.bazelrc
|
||||
|
||||
The process should automatically connect to the debugger. For additional info and testing options, see the [nodejs_test documentation](https://bazelbuild.github.io/rules_nodejs/node/node.html#nodejs_test).
|
||||
|
||||
- Click on "Resume script execution" to let the code run until the first `debugger` statement or a previously set breakpoint.
|
||||
- If you're debugging an ivy test and you want to inspect the generated template instructions, find the template of your component in the call stack and click on `(source mapped from [CompName].js)` at the bottom of the code. You can also disable sourcemaps in the options or go to sources and look into ng:// namespace to see all the generated code.
|
||||
|
||||
### Debugging a Node Test in VSCode
|
||||
|
||||
First time setup:
|
||||
|
@ -37,7 +37,7 @@ We explicitly don't consider the following to be our public API surface:
|
||||
|
||||
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*` and other documented package entry-points.
|
||||
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
|
||||
- any class members or symbols marked as `private`, or prefixed with underscore (`_`), [barred latin o](https://en.wikipedia.org/wiki/%C6%9F) (`ɵ`), and double barred latin o, aka "frog eyes" (`ɵɵ`).
|
||||
- any class members or symbols marked as `private`, or prefixed with underscore (`_`), [barred latin o](https://en.wikipedia.org/wiki/%C6%9F) (`ɵ`), and double barred latin o (`ɵɵ`).
|
||||
- extending any of our classes unless the support for this is specifically documented in the API docs
|
||||
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 14487,
|
||||
"main": 14664,
|
||||
"polyfills": 43567
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 149205,
|
||||
"main": 149248,
|
||||
"polyfills": 43567
|
||||
}
|
||||
}
|
||||
@ -34,4 +34,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
"node_modules/@angular/compiler-cli/**",
|
||||
"node_modules/@angular/**/testing/**",
|
||||
"node_modules/@angular/common/upgrade*",
|
||||
"node_modules/@angular/router/upgrade*"
|
||||
"node_modules/@angular/router/upgrade*",
|
||||
"node_modules/@angular/cdk/schematics*",
|
||||
"node_modules/@angular/material/schematics*"
|
||||
]
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"seq": 0,
|
||||
"type": "response",
|
||||
"command": "definitionAndBoundSpan",
|
||||
"request_seq": 2,
|
||||
"success": true,
|
||||
"body": {
|
||||
"definitions": [
|
||||
{
|
||||
"file": "${PWD}/project/app/app.component.ts",
|
||||
"start": {
|
||||
"line": 7,
|
||||
"offset": 30
|
||||
},
|
||||
"end": {
|
||||
"line": 7,
|
||||
"offset": 47
|
||||
}
|
||||
}
|
||||
],
|
||||
"textSpan": {
|
||||
"start": {
|
||||
"line": 7,
|
||||
"offset": 30
|
||||
},
|
||||
"end": {
|
||||
"line": 7,
|
||||
"offset": 47
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"seq": 0,
|
||||
"type": "response",
|
||||
"command": "quickinfo",
|
||||
"request_seq": 4,
|
||||
"success": true,
|
||||
"body": {
|
||||
"kind": "const",
|
||||
"kindModifiers": "declare",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"offset": 17
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"offset": 21
|
||||
},
|
||||
"displayString": "const name: never",
|
||||
"documentation": "",
|
||||
"tags": []
|
||||
}
|
||||
}
|
@ -2,6 +2,6 @@ import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `<h1>Hello {{name}}</h1>`,
|
||||
template: `<h1>Hello {{name}}</h1><my-widget></my-widget>`,
|
||||
})
|
||||
export class AppComponent { name = 'Angular'; }
|
||||
|
@ -2,10 +2,11 @@ import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { WidgetComponent } from './widget.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
declarations: [ AppComponent, WidgetComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -0,0 +1 @@
|
||||
<h1>This is a {{name}} widget!</h1>
|
@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-widget',
|
||||
templateUrl: './widget.component.html',
|
||||
})
|
||||
export class WidgetComponent { name = 'Angular'; }
|
@ -8,6 +8,9 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"plugins": [
|
||||
{"name": "@angular/language-service"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ describe('Angular Language Service', () => {
|
||||
beforeEach(() => {
|
||||
jasmine.addMatchers(goldenMatcher);
|
||||
server = fork(SERVER_PATH, [
|
||||
'--globalPlugins', '@angular/language-service',
|
||||
'--logVerbosity', 'verbose',
|
||||
'--logFile', join(PWD, 'tsserver.log'),
|
||||
], {
|
||||
@ -118,6 +117,17 @@ describe('Angular Language Service', () => {
|
||||
offset: 28,
|
||||
});
|
||||
expect(resp2).toMatchGolden('quickinfo.json');
|
||||
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/widget.component.html`,
|
||||
});
|
||||
|
||||
const resp3 = await client.sendRequest('quickinfo', {
|
||||
file: `${PWD}/project/app/widget.component.html`,
|
||||
line: 1,
|
||||
offset: 19,
|
||||
});
|
||||
expect(resp3).toMatchGolden('quickinfo_externalTemplate.json');
|
||||
});
|
||||
|
||||
it('should perform definition', async () => {
|
||||
@ -139,4 +149,25 @@ describe('Angular Language Service', () => {
|
||||
});
|
||||
expect(resp2).toMatchGolden('definition.json');
|
||||
});
|
||||
|
||||
it('should perform definitionAndBoundSpan', async () => {
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
});
|
||||
|
||||
const resp1 = await client.sendRequest('reload', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
tmpFile: `${PWD}/project/app/app.component.ts`,
|
||||
}) as any;
|
||||
expect(resp1.command).toBe('reload');
|
||||
expect(resp1.success).toBe(true);
|
||||
|
||||
const resp2 = await client.sendRequest('definitionAndBoundSpan', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
line: 5,
|
||||
offset: 28,
|
||||
});
|
||||
expect(resp2).toMatchGolden('definitionAndBoundSpan.json');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
|
||||
"@angular/core@file:../../dist/packages-dist/core":
|
||||
version "7.2.0"
|
||||
version "8.0.0-beta.13"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/language-service@file:../../dist/packages-dist/language-service":
|
||||
version "7.2.0"
|
||||
version "8.0.0-beta.13"
|
||||
|
||||
"@types/node@file:../../node_modules/@types/node":
|
||||
version "10.9.4"
|
||||
@ -61,16 +61,16 @@ inherits@2:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
jasmine-core@~3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c"
|
||||
integrity sha1-pHheE11d9lAk38kiSVPfWFvSdmw=
|
||||
jasmine-core@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e"
|
||||
integrity sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==
|
||||
|
||||
"jasmine@file:../../node_modules/jasmine":
|
||||
version "3.1.0"
|
||||
version "3.3.1"
|
||||
dependencies:
|
||||
glob "^7.0.6"
|
||||
jasmine-core "~3.1.0"
|
||||
jasmine-core "~3.3.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
@ -97,7 +97,7 @@ tslib@^1.9.0:
|
||||
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
|
||||
|
||||
"typescript@file:../../node_modules/typescript":
|
||||
version "3.2.2"
|
||||
version "3.4.2"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
|
@ -4,13 +4,13 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/cdk": "^7.0.0-rc.1",
|
||||
"@angular/cdk": "^8.0.0",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/material": "7.0.0-rc.1",
|
||||
"@angular/material": "8.0.0",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
|
@ -61,6 +61,12 @@ if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "const ɵMatTable_BaseFactory = ɵngcc0.ɵɵgetInheritedFactory(MatTable);" node_modules/@angular/material/esm5/table.es5.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it generate a base definition for undecorated classes with inputs and view queries?
|
||||
grep "_MatMenuBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ inputs: {" node_modules/@angular/material/esm2015/menu.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "_MatMenuBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ inputs: {" node_modules/@angular/material/esm5/menu.es5.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Can it be safely run again (as a noop)?
|
||||
# And check that it logged skipping compilation as expected
|
||||
ivy-ngcc -l debug | grep 'Skipping'
|
||||
|
@ -3,26 +3,26 @@
|
||||
|
||||
|
||||
"@angular/animations@file:../../dist/packages-dist/animations":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/cdk@^7.0.0-rc.1":
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-7.0.2.tgz#35081cd27a8f5214568b6ad1acf8cb3b3c315f99"
|
||||
integrity sha512-MAugJsjQ3ye2RX7vNjd9IN1c7enAxJaCz9UZBFuzfo/pNxvjaEObFmF2NUKyU1DctknqjyOzZYYg0e+bBxrBbQ==
|
||||
"@angular/cdk@^8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-8.0.0.tgz#34ca95e71d71780b29b5ba07318c88c4577bdc25"
|
||||
integrity sha512-2vsRWEHNARe0iRmqgzvM67gwfRy+aKvdef4Qu9L+ndSsTrrZT3tSgG8SMn1v9SfBHnx5G8mo4d1AMquXG69AuQ==
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/common@file:../../dist/packages-dist/common":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
canonical-path "1.0.0"
|
||||
chokidar "^2.1.1"
|
||||
@ -34,44 +34,42 @@
|
||||
shelljs "^0.8.1"
|
||||
source-map "^0.6.1"
|
||||
tslib "^1.9.0"
|
||||
yargs "9.0.1"
|
||||
yargs "13.1.0"
|
||||
|
||||
"@angular/compiler@file:../../dist/packages-dist/compiler":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/core@file:../../dist/packages-dist/core":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/forms@file:../../dist/packages-dist/forms":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/material@7.0.0-rc.1":
|
||||
version "7.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-7.0.0-rc.1.tgz#e9a88c8b6f6b48b87ed3d1da3037d14a0e3e8290"
|
||||
integrity sha512-4LC3y5ZorNjdF1lEkVN/3zwOEnBwjfWLCY92tA06Kdvs1yENZdu0fGTRwcu9Hz4y+t84sL/KEKC9VjKN3Z7PSg==
|
||||
"@angular/material@8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-8.0.0.tgz#21dd15c318d1e29eb4d1a2dd888ddb027897eb49"
|
||||
integrity sha512-c7O7GhZd46xF2WB6T/YPam5lJkTgQLdIS53IqwZIFhL427+SEfPvejVzRnVfZCI3NdrKiWt/5VsvtQZwWzlGvw==
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/router@file:../../dist/packages-dist/router":
|
||||
version "8.0.0-beta.10"
|
||||
version "8.1.0-beta.0"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
@ -150,6 +148,11 @@ ansi-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||
|
||||
ansi-styles@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de"
|
||||
@ -517,10 +520,10 @@ camelcase@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
|
||||
|
||||
camelcase@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
|
||||
camelcase@^5.0.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
canonical-path@1.0.0:
|
||||
version "1.0.0"
|
||||
@ -617,6 +620,15 @@ cliui@^3.2.0:
|
||||
strip-ansi "^3.0.1"
|
||||
wrap-ansi "^2.0.0"
|
||||
|
||||
cliui@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
|
||||
integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
|
||||
dependencies:
|
||||
string-width "^2.1.1"
|
||||
strip-ansi "^4.0.0"
|
||||
wrap-ansi "^2.0.0"
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
@ -735,12 +747,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
cross-spawn@^5.0.1:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
|
||||
dependencies:
|
||||
lru-cache "^4.0.1"
|
||||
nice-try "^1.0.4"
|
||||
path-key "^2.0.1"
|
||||
semver "^5.5.0"
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
@ -777,7 +791,7 @@ debug@^3.1.0:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
decamelize@^1.1.1:
|
||||
decamelize@^1.1.1, decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
@ -889,11 +903,23 @@ ee-first@1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
|
||||
|
||||
encodeurl@~1.0.1, encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
engine.io-client@~3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36"
|
||||
@ -973,13 +999,13 @@ eventemitter3@1.x.x:
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
|
||||
integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=
|
||||
|
||||
execa@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
|
||||
integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
|
||||
execa@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
|
||||
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
|
||||
dependencies:
|
||||
cross-spawn "^5.0.1"
|
||||
get-stream "^3.0.0"
|
||||
cross-spawn "^6.0.0"
|
||||
get-stream "^4.0.0"
|
||||
is-stream "^1.1.0"
|
||||
npm-run-path "^2.0.0"
|
||||
p-finally "^1.0.0"
|
||||
@ -1126,12 +1152,12 @@ find-up@^1.0.0:
|
||||
path-exists "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
find-up@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||
integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
|
||||
find-up@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
|
||||
dependencies:
|
||||
locate-path "^2.0.0"
|
||||
locate-path "^3.0.0"
|
||||
|
||||
follow-redirects@^1.2.5:
|
||||
version "1.5.9"
|
||||
@ -1234,10 +1260,17 @@ get-caller-file@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
|
||||
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
|
||||
|
||||
get-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
|
||||
get-caller-file@^2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-stream@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
|
||||
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-value@^2.0.3, get-value@^2.0.6:
|
||||
version "2.0.6"
|
||||
@ -1487,6 +1520,11 @@ invert-kv@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
|
||||
|
||||
invert-kv@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
||||
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
|
||||
|
||||
is-accessor-descriptor@^0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
|
||||
@ -1840,6 +1878,13 @@ lcid@^1.0.0:
|
||||
dependencies:
|
||||
invert-kv "^1.0.0"
|
||||
|
||||
lcid@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
|
||||
integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
|
||||
dependencies:
|
||||
invert-kv "^2.0.0"
|
||||
|
||||
lie@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||
@ -1874,16 +1919,6 @@ load-json-file@^1.0.0:
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
load-json-file@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
|
||||
integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^2.2.0"
|
||||
pify "^2.0.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
localtunnel@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.1.tgz#1d1737eab658add5a40266d8e43f389b646ee3b1"
|
||||
@ -1894,12 +1929,12 @@ localtunnel@1.9.1:
|
||||
openurl "1.1.1"
|
||||
yargs "6.6.0"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
|
||||
locate-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
|
||||
dependencies:
|
||||
p-locate "^2.0.0"
|
||||
p-locate "^3.0.0"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
@ -1917,14 +1952,6 @@ lodash@^4.11.1, lodash@^4.17.10, lodash@^4.5.1:
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
||||
lru-cache@^4.0.1:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
|
||||
integrity sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==
|
||||
dependencies:
|
||||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
magic-string@^0.25.0:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e"
|
||||
@ -1932,6 +1959,13 @@ magic-string@^0.25.0:
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.1"
|
||||
|
||||
map-age-cleaner@^0.1.1:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
|
||||
integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
|
||||
dependencies:
|
||||
p-defer "^1.0.0"
|
||||
|
||||
map-cache@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||
@ -1949,12 +1983,14 @@ math-random@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac"
|
||||
integrity sha1-izqsWIuKZuSXXjzepn97sylgH6w=
|
||||
|
||||
mem@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
|
||||
integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=
|
||||
mem@^4.0.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
|
||||
integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
map-age-cleaner "^0.1.1"
|
||||
mimic-fn "^2.0.0"
|
||||
p-is-promise "^2.0.0"
|
||||
|
||||
micromatch@2.3.11:
|
||||
version "2.3.11"
|
||||
@ -2011,10 +2047,10 @@ mime@1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
|
||||
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
|
||||
|
||||
mimic-fn@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||
mimic-fn@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimatch@^3.0.2, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
@ -2124,6 +2160,11 @@ negotiator@0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
|
||||
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-pre-gyp@^0.10.0:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
|
||||
@ -2263,7 +2304,7 @@ on-finished@~2.3.0:
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
once@^1.3.0:
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
@ -2302,14 +2343,14 @@ os-locale@^1.4.0:
|
||||
dependencies:
|
||||
lcid "^1.0.0"
|
||||
|
||||
os-locale@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
|
||||
integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==
|
||||
os-locale@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
||||
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
|
||||
dependencies:
|
||||
execa "^0.7.0"
|
||||
lcid "^1.0.0"
|
||||
mem "^1.1.0"
|
||||
execa "^1.0.0"
|
||||
lcid "^2.0.0"
|
||||
mem "^4.0.0"
|
||||
|
||||
os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
|
||||
version "1.0.2"
|
||||
@ -2324,29 +2365,39 @@ osenv@^0.1.4:
|
||||
os-homedir "^1.0.0"
|
||||
os-tmpdir "^1.0.0"
|
||||
|
||||
p-defer@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
|
||||
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
|
||||
|
||||
p-finally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
|
||||
|
||||
p-limit@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
|
||||
integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
|
||||
dependencies:
|
||||
p-try "^1.0.0"
|
||||
p-is-promise@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
|
||||
integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
|
||||
|
||||
p-locate@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||
integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
|
||||
p-limit@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2"
|
||||
integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
|
||||
dependencies:
|
||||
p-limit "^1.1.0"
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-try@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
|
||||
p-locate@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
|
||||
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
|
||||
dependencies:
|
||||
p-limit "^2.0.0"
|
||||
|
||||
p-try@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
pako@~1.0.2:
|
||||
version "1.0.10"
|
||||
@ -2426,7 +2477,7 @@ path-is-inside@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
||||
|
||||
path-key@^2.0.0:
|
||||
path-key@^2.0.0, path-key@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
|
||||
@ -2445,13 +2496,6 @@ path-type@^1.0.0:
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-type@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
|
||||
integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
|
||||
dependencies:
|
||||
pify "^2.0.0"
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
@ -2516,16 +2560,19 @@ process-nextick-args@~2.0.0:
|
||||
webdriver-js-extender "2.1.0"
|
||||
webdriver-manager "^12.0.6"
|
||||
|
||||
pseudomap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
|
||||
|
||||
psl@^1.1.24:
|
||||
version "1.1.29"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
|
||||
integrity sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
@ -2593,14 +2640,6 @@ read-pkg-up@^1.0.1:
|
||||
find-up "^1.0.0"
|
||||
read-pkg "^1.0.0"
|
||||
|
||||
read-pkg-up@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
|
||||
integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
|
||||
dependencies:
|
||||
find-up "^2.0.0"
|
||||
read-pkg "^2.0.0"
|
||||
|
||||
read-pkg@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
|
||||
@ -2610,15 +2649,6 @@ read-pkg@^1.0.0:
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
read-pkg@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
|
||||
integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
|
||||
dependencies:
|
||||
load-json-file "^2.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^2.0.0"
|
||||
|
||||
readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@~2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
@ -2719,6 +2749,11 @@ require-main-filename@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
|
||||
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
|
||||
|
||||
require-main-filename@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
requires-port@1.x.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
@ -2822,6 +2857,11 @@ selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
||||
semver@^5.5.0:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||
|
||||
send@0.16.2:
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
|
||||
@ -3137,7 +3177,7 @@ string-width@^1.0.1, string-width@^1.0.2:
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
"string-width@^1.0.2 || 2", string-width@^2.0.0:
|
||||
"string-width@^1.0.2 || 2", string-width@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
@ -3145,6 +3185,15 @@ string-width@^1.0.1, string-width@^1.0.2:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string-width@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
|
||||
dependencies:
|
||||
emoji-regex "^7.0.1"
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^5.1.0"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
@ -3173,6 +3222,13 @@ strip-ansi@^4.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-ansi@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
|
||||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-bom@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
|
||||
@ -3180,11 +3236,6 @@ strip-bom@^2.0.0:
|
||||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
|
||||
|
||||
strip-eof@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||
@ -3306,7 +3357,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
"typescript@file:../../node_modules/typescript":
|
||||
version "3.3.3333"
|
||||
version "3.4.2"
|
||||
|
||||
ua-parser-js@0.7.17:
|
||||
version "0.7.17"
|
||||
@ -3502,16 +3553,24 @@ y18n@^3.2.1:
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
|
||||
y18n@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
|
||||
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
|
||||
|
||||
yallist@^3.0.0, yallist@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
|
||||
integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=
|
||||
|
||||
yargs-parser@^13.0.0:
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.0.tgz#7016b6dd03e28e1418a510e258be4bff5a31138f"
|
||||
integrity sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^4.1.0, yargs-parser@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
|
||||
@ -3519,12 +3578,22 @@ yargs-parser@^4.1.0, yargs-parser@^4.2.0:
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
|
||||
yargs-parser@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
|
||||
integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k=
|
||||
yargs@13.1.0:
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.1.0.tgz#b2729ce4bfc0c584939719514099d8a916ad2301"
|
||||
integrity sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==
|
||||
dependencies:
|
||||
camelcase "^4.1.0"
|
||||
cliui "^4.0.0"
|
||||
find-up "^3.0.0"
|
||||
get-caller-file "^2.0.1"
|
||||
os-locale "^3.1.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^2.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^3.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^13.0.0"
|
||||
|
||||
yargs@6.4.0:
|
||||
version "6.4.0"
|
||||
@ -3565,29 +3634,10 @@ yargs@6.6.0:
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^4.2.0"
|
||||
|
||||
yargs@9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c"
|
||||
integrity sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=
|
||||
dependencies:
|
||||
camelcase "^4.1.0"
|
||||
cliui "^3.2.0"
|
||||
decamelize "^1.1.1"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^2.0.0"
|
||||
read-pkg-up "^2.0.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^2.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^7.0.0"
|
||||
|
||||
yeast@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
||||
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
|
||||
|
||||
"zone.js@file:../../node_modules/zone.js":
|
||||
version "0.9.0"
|
||||
version "0.9.1"
|
||||
|
@ -1,147 +1,3 @@
|
||||
import { Subject, Subscription } from "rxjs";
|
||||
import "rxjs";
|
||||
|
||||
import "rxjs/operators";
|
||||
|
||||
function getGlobal() {
|
||||
const __globalThis = "undefined" !== typeof globalThis && globalThis;
|
||||
const __window = "undefined" !== typeof window && window;
|
||||
const __self = "undefined" !== typeof self && "undefined" !== typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && self;
|
||||
const __global = "undefined" !== typeof global && global;
|
||||
return __globalThis || __global || __window || __self;
|
||||
}
|
||||
|
||||
const _global = getGlobal();
|
||||
|
||||
let _symbolIterator = null;
|
||||
|
||||
function getSymbolIterator() {
|
||||
if (!_symbolIterator) {
|
||||
const Symbol = _global["Symbol"];
|
||||
if (Symbol && Symbol.iterator) _symbolIterator = Symbol.iterator; else {
|
||||
const keys = Object.getOwnPropertyNames(Map.prototype);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if ("entries" !== key && "size" !== key && Map.prototype[key] === Map.prototype["entries"]) _symbolIterator = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _symbolIterator;
|
||||
}
|
||||
|
||||
if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg;
|
||||
|
||||
function flatten(list, dst) {
|
||||
if (void 0 === dst) dst = list;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (Array.isArray(item)) {
|
||||
if (dst === list) dst = list.slice(0, i);
|
||||
flatten(item, dst);
|
||||
} else if (dst !== list) dst.push(item);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
class EventEmitter extends Subject {
|
||||
constructor(isAsync = false) {
|
||||
super();
|
||||
this.__isAsync = isAsync;
|
||||
}
|
||||
emit(value) {
|
||||
super.next(value);
|
||||
}
|
||||
subscribe(generatorOrNext, error, complete) {
|
||||
let schedulerFn;
|
||||
let errorFn = err => null;
|
||||
let completeFn = () => null;
|
||||
if (generatorOrNext && "object" === typeof generatorOrNext) {
|
||||
schedulerFn = this.__isAsync ? value => {
|
||||
setTimeout(() => generatorOrNext.next(value));
|
||||
} : value => {
|
||||
generatorOrNext.next(value);
|
||||
};
|
||||
if (generatorOrNext.error) errorFn = this.__isAsync ? err => {
|
||||
setTimeout(() => generatorOrNext.error(err));
|
||||
} : err => {
|
||||
generatorOrNext.error(err);
|
||||
};
|
||||
if (generatorOrNext.complete) completeFn = this.__isAsync ? () => {
|
||||
setTimeout(() => generatorOrNext.complete());
|
||||
} : () => {
|
||||
generatorOrNext.complete();
|
||||
};
|
||||
} else {
|
||||
schedulerFn = this.__isAsync ? value => {
|
||||
setTimeout(() => generatorOrNext(value));
|
||||
} : value => {
|
||||
generatorOrNext(value);
|
||||
};
|
||||
if (error) errorFn = this.__isAsync ? err => {
|
||||
setTimeout(() => error(err));
|
||||
} : err => {
|
||||
error(err);
|
||||
};
|
||||
if (complete) completeFn = this.__isAsync ? () => {
|
||||
setTimeout(() => complete());
|
||||
} : () => {
|
||||
complete();
|
||||
};
|
||||
}
|
||||
const sink = super.subscribe(schedulerFn, errorFn, completeFn);
|
||||
if (generatorOrNext instanceof Subscription) generatorOrNext.add(sink);
|
||||
return sink;
|
||||
}
|
||||
}
|
||||
|
||||
class QueryList {
|
||||
constructor() {
|
||||
this.dirty = true;
|
||||
this._results = [];
|
||||
this.changes = new EventEmitter();
|
||||
this.length = 0;
|
||||
}
|
||||
map(fn) {
|
||||
return this._results.map(fn);
|
||||
}
|
||||
filter(fn) {
|
||||
return this._results.filter(fn);
|
||||
}
|
||||
find(fn) {
|
||||
return this._results.find(fn);
|
||||
}
|
||||
reduce(fn, init) {
|
||||
return this._results.reduce(fn, init);
|
||||
}
|
||||
forEach(fn) {
|
||||
this._results.forEach(fn);
|
||||
}
|
||||
some(fn) {
|
||||
return this._results.some(fn);
|
||||
}
|
||||
toArray() {
|
||||
return this._results.slice();
|
||||
}
|
||||
[getSymbolIterator()]() {
|
||||
return this._results[getSymbolIterator()]();
|
||||
}
|
||||
toString() {
|
||||
return this._results.toString();
|
||||
}
|
||||
reset(resultsTree) {
|
||||
this._results = flatten(resultsTree);
|
||||
this.dirty = false;
|
||||
this.length = this._results.length;
|
||||
this.last = this._results[this.length - 1];
|
||||
this.first = this._results[0];
|
||||
}
|
||||
notifyOnChanges() {
|
||||
this.changes.emit(this);
|
||||
}
|
||||
setDirty() {
|
||||
this.dirty = true;
|
||||
}
|
||||
destroy() {
|
||||
this.changes.complete();
|
||||
this.changes.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,3 @@ import "tslib";
|
||||
import "rxjs";
|
||||
|
||||
import "rxjs/operators";
|
||||
|
||||
function getGlobal() {
|
||||
var __globalThis = "undefined" !== typeof globalThis && globalThis;
|
||||
var __window = "undefined" !== typeof window && window;
|
||||
var __self = "undefined" !== typeof self && "undefined" !== typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && self;
|
||||
var __global = "undefined" !== typeof global && global;
|
||||
return __globalThis || __global || __window || __self;
|
||||
}
|
||||
|
||||
var _global = getGlobal();
|
||||
|
||||
if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg;
|
||||
|
74
modules/benchmarks/src/expanding_rows/BUILD.bazel
Normal file
74
modules/benchmarks/src/expanding_rows/BUILD.bazel
Normal file
@ -0,0 +1,74 @@
|
||||
package(default_visibility = ["//modules/benchmarks:__subpackages__"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_devserver")
|
||||
load("//modules/benchmarks:benchmark_test.bzl", "benchmark_test")
|
||||
|
||||
ng_module(
|
||||
name = "application_lib",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//packages:types",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/platform-browser",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "perf_lib",
|
||||
testonly = 1,
|
||||
srcs = ["benchmark_perf.spec.ts"],
|
||||
deps = [
|
||||
"//modules/e2e_util",
|
||||
"@npm//protractor",
|
||||
],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":index.ts",
|
||||
deps = [
|
||||
":application_lib",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
ts_devserver(
|
||||
name = "prodserver",
|
||||
static_files = [
|
||||
":bundle.min_debug.js",
|
||||
":bundle.min.js",
|
||||
"@npm//node_modules/zone.js:dist/zone.js",
|
||||
"index.html",
|
||||
],
|
||||
)
|
||||
|
||||
ts_devserver(
|
||||
name = "devserver",
|
||||
entry_module = "angular/modules/benchmarks/src/expanding_rows/index",
|
||||
index_html = "index.html",
|
||||
scripts = [
|
||||
"@npm//node_modules/tslib:tslib.js",
|
||||
"//tools/rxjs:rxjs_umd_modules",
|
||||
],
|
||||
serving_path = "/index.js",
|
||||
static_files = [
|
||||
"@npm//node_modules/zone.js:dist/zone.js",
|
||||
"index.html",
|
||||
],
|
||||
deps = [":application_lib"],
|
||||
)
|
||||
|
||||
benchmark_test(
|
||||
name = "perf",
|
||||
server = ":prodserver",
|
||||
deps = [
|
||||
":perf_lib",
|
||||
],
|
||||
)
|
78
modules/benchmarks/src/expanding_rows/benchmark.ts
Normal file
78
modules/benchmarks/src/expanding_rows/benchmark.ts
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {AfterViewInit, Component, NgModule, ViewChild, ViewEncapsulation} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
import {BenchmarkModule} from './benchmark_module';
|
||||
import {BenchmarkableExpandingRow} from './benchmarkable_expanding_row';
|
||||
import {BenchmarkableExpandingRowModule} from './benchmarkable_expanding_row_module';
|
||||
|
||||
@Component({
|
||||
selector: 'benchmark-root',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
template: `
|
||||
<h2>cfc-expanding-row initialization benchmark</h2>
|
||||
|
||||
<section>
|
||||
<button id="reset" (click)="reset()">Reset</button>
|
||||
<button id="init" (click)="init()">Init</button>
|
||||
<button id="run" (click)="runAll()">Run All</button>
|
||||
</section>
|
||||
|
||||
<benchmark-area>
|
||||
<benchmarkable-expanding-row></benchmarkable-expanding-row>
|
||||
</benchmark-area>`,
|
||||
})
|
||||
export class InitializationRoot implements AfterViewInit {
|
||||
@ViewChild(BenchmarkableExpandingRow, {static: true})
|
||||
expandingRow !: BenchmarkableExpandingRow;
|
||||
|
||||
ngAfterViewInit() {}
|
||||
|
||||
reset() { this.expandingRow.reset(); }
|
||||
|
||||
init() { this.expandingRow.init(); }
|
||||
|
||||
async runAll() {
|
||||
await execTimed('initialization_benchmark', async() => { await this.doInit(); });
|
||||
}
|
||||
|
||||
async handleInitClick() { await this.doInit(); }
|
||||
|
||||
private async doInit() {
|
||||
await execTimed('initial_load', async() => { this.expandingRow.init(); });
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [InitializationRoot],
|
||||
exports: [InitializationRoot],
|
||||
imports: [
|
||||
CommonModule,
|
||||
BenchmarkableExpandingRowModule,
|
||||
BenchmarkModule,
|
||||
BrowserModule,
|
||||
],
|
||||
bootstrap: [InitializationRoot],
|
||||
})
|
||||
// Component benchmarks must export a BenchmarkModule.
|
||||
export class ExpandingRowBenchmarkModule {
|
||||
}
|
||||
|
||||
export async function execTimed(description: string, func: () => Promise<void>) {
|
||||
console.time(description);
|
||||
await func();
|
||||
await nextTick(200);
|
||||
console.timeEnd(description);
|
||||
}
|
||||
|
||||
export async function nextTick(delay = 1) {
|
||||
return new Promise((res, rej) => { setTimeout(() => { res(); }, delay); });
|
||||
}
|
51
modules/benchmarks/src/expanding_rows/benchmark_module.ts
Normal file
51
modules/benchmarks/src/expanding_rows/benchmark_module.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, ErrorHandler, Injectable, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'benchmark-area',
|
||||
template: '<ng-content></ng-content>',
|
||||
styles: [`
|
||||
:host {
|
||||
padding: 1;
|
||||
margin: 1;
|
||||
background-color: white;
|
||||
width: 1000px;
|
||||
display: block;
|
||||
}`],
|
||||
host: {
|
||||
'class': 'cfc-ng2-region',
|
||||
}
|
||||
})
|
||||
export class BenchmarkArea {
|
||||
}
|
||||
|
||||
declare interface ExtendedWindow extends Window { benchmarkErrors?: string[]; }
|
||||
const extendedWindow = window as ExtendedWindow;
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class BenchmarkErrorHandler implements ErrorHandler {
|
||||
handleError(error: Error) {
|
||||
if (!extendedWindow.benchmarkErrors) {
|
||||
extendedWindow.benchmarkErrors = [];
|
||||
}
|
||||
extendedWindow.benchmarkErrors.push(error.message);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [BenchmarkArea],
|
||||
exports: [BenchmarkArea],
|
||||
providers: [
|
||||
{provide: ErrorHandler, useClass: BenchmarkErrorHandler},
|
||||
]
|
||||
})
|
||||
export class BenchmarkModule {
|
||||
}
|
26
modules/benchmarks/src/expanding_rows/benchmark_perf.spec.ts
Normal file
26
modules/benchmarks/src/expanding_rows/benchmark_perf.spec.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {$, browser} from 'protractor';
|
||||
import {runBenchmark} from '../../../e2e_util/perf_util';
|
||||
|
||||
describe('benchmarks', () => {
|
||||
|
||||
it('should work for create', done => {
|
||||
browser.rootEl = '#root';
|
||||
runBenchmark({
|
||||
id: 'create',
|
||||
url: '',
|
||||
ignoreBrowserSynchronization: true,
|
||||
params: [],
|
||||
prepare: () => $('#reset').click(),
|
||||
work: () => $('#init').click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
export interface MlbTeam {
|
||||
name: string;
|
||||
id: number;
|
||||
division: string;
|
||||
stadium: string;
|
||||
projection: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'benchmarkable-expanding-row',
|
||||
template: `
|
||||
<cfc-expanding-row-host *ngIf="showExpandingRow">
|
||||
<cfc-expanding-row *ngFor="let team of teams" [rowId]="$any(team.id)">
|
||||
<cfc-expanding-row-summary>
|
||||
Team {{team.id}}
|
||||
</cfc-expanding-row-summary>
|
||||
<cfc-expanding-row-details-caption>
|
||||
{{team.name}}
|
||||
<a href="https://www.google.com" class="cfc-demo-expanding-row-caption-link">
|
||||
{{team.id}}
|
||||
</a>
|
||||
</cfc-expanding-row-details-caption>
|
||||
<cfc-expanding-row-details-content>
|
||||
<ul ace-list>
|
||||
<li>Division: {{team.division}}</li>
|
||||
<li>
|
||||
<a href="https://www.google.com">{{team.stadium}}</a>
|
||||
</li>
|
||||
<li>Projected Record: {{team.projection}}</li>
|
||||
</ul>
|
||||
</cfc-expanding-row-details-content>
|
||||
</cfc-expanding-row>
|
||||
</cfc-expanding-row-host>`,
|
||||
})
|
||||
export class BenchmarkableExpandingRow {
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
showExpandingRow !: boolean;
|
||||
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
teams !: MlbTeam[];
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
private fakeTeams !: MlbTeam[];
|
||||
|
||||
init(): void {
|
||||
this.teams = this.fakeTeams;
|
||||
this.showExpandingRow = true;
|
||||
}
|
||||
|
||||
reset(numItems = 5000): void {
|
||||
this.showExpandingRow = false;
|
||||
|
||||
this.fakeTeams = [];
|
||||
for (let i = 0; i < numItems; i++) {
|
||||
this.fakeTeams.push({
|
||||
name: `name ${i}`,
|
||||
id: i,
|
||||
division: `division ${i}`,
|
||||
stadium: `stadium ${i}`,
|
||||
projection: `projection ${i}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {BenchmarkableExpandingRow} from './benchmarkable_expanding_row';
|
||||
import {ExpandingRowModule} from './expanding_row_module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [BenchmarkableExpandingRow],
|
||||
exports: [BenchmarkableExpandingRow],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ExpandingRowModule,
|
||||
],
|
||||
})
|
||||
export class BenchmarkableExpandingRowModule {
|
||||
}
|
355
modules/benchmarks/src/expanding_rows/expanding_row.ts
Normal file
355
modules/benchmarks/src/expanding_rows/expanding_row.ts
Normal file
@ -0,0 +1,355 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, InjectionToken, Input, Output, QueryList, ViewChild} from '@angular/core';
|
||||
|
||||
import {expanding_row_css} from './expanding_row_css';
|
||||
import {ExpandingRowSummary} from './expanding_row_summary';
|
||||
import {ExpandingRowToggleEvent} from './expanding_row_toggle_event';
|
||||
|
||||
|
||||
/**
|
||||
* Injection token to break cylic dependency between ExpandingRow and
|
||||
* ExpandingRowHost
|
||||
*/
|
||||
export const EXPANDING_ROW_HOST_INJECTION_TOKEN =
|
||||
new InjectionToken<ExpandingRowHostBase>('ExpandingRowHost');
|
||||
|
||||
/** The base class for ExpandingRowHost component to break cylic dependency. */
|
||||
export interface ExpandingRowHostBase {
|
||||
/**
|
||||
* A reference to all child cfc-expanding-row elements. We will need for
|
||||
* keyboard accessibility and scroll adjustments. For example, we need to know
|
||||
* which row is previous row when user presses "left arrow" on a focused row.
|
||||
*/
|
||||
contentRows: QueryList<ExpandingRow>;
|
||||
|
||||
/**
|
||||
* Keeps track of the last row that had focus before focus left the list
|
||||
* of expanding rows.
|
||||
*/
|
||||
lastFocusedRow?: ExpandingRow;
|
||||
|
||||
/**
|
||||
* Handles summary element click on a cfc-expanding-row component. Note
|
||||
* that summary element is visible only when the row is collapsed. So this
|
||||
* event will fired prior to expansion of a collapsed row. Scroll adjustment
|
||||
* below makes sure mouse stays on the caption element when the collapsed
|
||||
* row expands.
|
||||
*/
|
||||
handleRowSummaryClick(row: ExpandingRow): void;
|
||||
|
||||
/**
|
||||
* Check if element is blacklisted. Blacklisted elements will not collapse an
|
||||
* open row when clicked.
|
||||
*/
|
||||
isBlacklisted(element: HTMLElement|null): boolean;
|
||||
|
||||
/**
|
||||
* Handles caption element click on a cfc-expanding-row component. Note
|
||||
* that caption element is visible only when the row is expanded. So this
|
||||
* means we will collapse the expanded row. The scroll adjustment below
|
||||
* makes sure that the mouse stays under the summary of the expanded row
|
||||
* when the row collapses.
|
||||
*/
|
||||
handleRowCaptionClick(row: ExpandingRow): void;
|
||||
|
||||
/**
|
||||
* Handles expansion of a row. When a new row expands, we need to remove
|
||||
* previous expansion and collapse. We also need to save the currently
|
||||
* expanded row so that we can collapse this row once another row expands.
|
||||
*/
|
||||
handleRowExpand(row: ExpandingRow): void;
|
||||
|
||||
/**
|
||||
* Handles focus on a row. When a new row gets focus (note that this is
|
||||
* different from expansion), we need to remove previous focus and expansion.
|
||||
* We need to save the reference to this focused row so that we can unfocus
|
||||
* this row when another row is focused.
|
||||
*/
|
||||
handleRowFocus(row: ExpandingRow): void;
|
||||
|
||||
/**
|
||||
* Function that is called by expanding row summary to focus on the last
|
||||
* focusable element before the list of expanding rows.
|
||||
*/
|
||||
focusOnPreviousFocusableElement(): void;
|
||||
|
||||
/**
|
||||
* Function that is called by expanding row summary to focus on the next
|
||||
* focusable element after the list of expanding rows.
|
||||
*/
|
||||
focusOnNextFocusableElement(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is used to render a single expanding row. It should contain
|
||||
* cfc-expanding-row-summary, cfc-expanding-row-details-caption and
|
||||
* cfc-expanding-row-details-content components.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'cfc-expanding-row',
|
||||
styles: [expanding_row_css],
|
||||
template: `
|
||||
<div #expandingRowMainElement
|
||||
class="cfc-expanding-row"
|
||||
cdkMonitorSubtreeFocus
|
||||
[attr.tabindex]="isExpanded ? '0' : '-1'"
|
||||
[class.cfc-expanding-row-has-focus]="isFocused"
|
||||
[class.cfc-expanding-row-is-expanded]="isExpanded"
|
||||
ve="CfcExpandingRow">
|
||||
<ng-content></ng-content>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExpandingRow {
|
||||
/**
|
||||
* The identifier for this node provided by the user code. We need this
|
||||
* while we are emitting onToggle event.
|
||||
*/
|
||||
@Input() rowId !: string;
|
||||
|
||||
/**
|
||||
* An ElementRef to the main element in this component. We need a reference
|
||||
* to this element to compute the height. The height of cfc-expanding-row
|
||||
* is used in [cfcExpandingRowHost] directive for scroll adjustments.
|
||||
*/
|
||||
@ViewChild('expandingRowMainElement', {static: true})
|
||||
expandingRowMainElement !: ElementRef;
|
||||
|
||||
/**
|
||||
* This @Output event emitter will be triggered when the user expands or
|
||||
* collapses this node.
|
||||
*/
|
||||
@Output() onToggle = new EventEmitter<ExpandingRowToggleEvent>();
|
||||
|
||||
/**
|
||||
* A boolean indicating if this node is expanded. This value is used to
|
||||
* hide/show summary, caption, and content of the expanding row. There should
|
||||
* only be one expanded row within [cfcExpandingRowHost] directive. And if
|
||||
* there is an expanded row, there shouldn't be any focused rows.
|
||||
*/
|
||||
set isExpanded(value: boolean) {
|
||||
const changed: boolean = this.isExpandedInternal !== value;
|
||||
this.isExpandedInternal = value;
|
||||
|
||||
if (changed) {
|
||||
this.isExpandedChange.emit();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
/** TS getter for isExpanded property. */
|
||||
get isExpanded(): boolean { return this.isExpandedInternal; }
|
||||
|
||||
/** Triggered when isExpanded property changes. */
|
||||
isExpandedChange = new EventEmitter<void>();
|
||||
|
||||
/** Triggered when index property changes. */
|
||||
indexChange = new EventEmitter<void>();
|
||||
|
||||
/**
|
||||
* A boolean indicating if this node is focused. This value is used to add
|
||||
* a CSS class that should render a blue border on the right. There should
|
||||
* only be one focused row in [cfcExpandingRowHost] directive.
|
||||
*/
|
||||
set isFocused(value: boolean) {
|
||||
this.isFocusedInternal = value;
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
/** TS getter for isFocused property. */
|
||||
get isFocused(): boolean { return this.isFocusedInternal; }
|
||||
|
||||
/** The index of the row in the context of the entire collection. */
|
||||
set index(value: number) {
|
||||
const changed: boolean = this.indexInternal !== value;
|
||||
this.indexInternal = value;
|
||||
|
||||
if (changed) {
|
||||
this.indexChange.emit();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
/** TS getter for index property. */
|
||||
get index(): number { return this.indexInternal; }
|
||||
|
||||
/**
|
||||
* We should probably rename this to summaryContentChild. Because technically
|
||||
* this is not a @ViewChild that is in a template. This will be transcluded.
|
||||
* Note that we are not using @ContentChild directive here. The @ContentChild
|
||||
* will cause cyclic reference if the class definition for ExpandingRowSummary
|
||||
* component is not in the same file as ExpandingRow.
|
||||
*/
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
summaryViewChild !: ExpandingRowSummary;
|
||||
|
||||
/**
|
||||
* We compute the collapsed height (which is just height of
|
||||
* cfc-expanding-row-summary component) in this component. This is used in
|
||||
* [cfcExpandingRowHost] for scroll adjustment calculation.
|
||||
*/
|
||||
collapsedHeight = -1;
|
||||
|
||||
/** Internal storage for isExpanded public property. */
|
||||
private isExpandedInternal = false;
|
||||
|
||||
/** Internal storage for isFocused public property. */
|
||||
private isFocusedInternal = false;
|
||||
|
||||
/** Internal storage for index public property. */
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
private indexInternal !: number;
|
||||
|
||||
/**
|
||||
* This holds a reference to [cfcExpandingRowHost] directive. We need
|
||||
* this reference to notify the host when this row expands/collapses or is
|
||||
* focused.
|
||||
*/
|
||||
constructor(
|
||||
public elementRef: ElementRef,
|
||||
@Inject(EXPANDING_ROW_HOST_INJECTION_TOKEN) public expandingRowHost: ExpandingRowHostBase,
|
||||
private readonly changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
/**
|
||||
* Handles click on cfc-expanding-row-summary component. This will expand
|
||||
* this row and collapse the previously expanded row. The collapse & blur
|
||||
* is handled in [cfcExpandingRowHost] directive.
|
||||
*/
|
||||
handleSummaryClick(): void {
|
||||
this.collapsedHeight =
|
||||
this.elementRef.nativeElement.querySelector('.cfc-expanding-row-summary').offsetHeight;
|
||||
this.expandingRowHost.handleRowSummaryClick(this);
|
||||
this.expand();
|
||||
}
|
||||
|
||||
/**
|
||||
* When user tabs into child cfc-expanding-row-summary component. This method
|
||||
* will make sure we focuse on this row, and blur on previously focused row.
|
||||
*/
|
||||
handleSummaryFocus(): void { this.focus(); }
|
||||
|
||||
/**
|
||||
* cfc-expanding-row-details-caption component will call this function to
|
||||
* notify click on its host element. Note that caption is only shown when
|
||||
* the row is expanded. Hence this will collapse this row and put the focus
|
||||
* on it.
|
||||
* If a blacklisted element exists in the caption, clicking that element will
|
||||
* not trigger the row collapse.
|
||||
*/
|
||||
handleCaptionClick(event: MouseEvent): void {
|
||||
if (this.expandingRowHost.isBlacklisted(event.target as {} as HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
this.expandingRowHost.handleRowCaptionClick(this);
|
||||
this.collapse();
|
||||
this.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of this component. This height is used in parent
|
||||
* [cfcExpandingRowHost] directive to compute scroll adjustment.
|
||||
*/
|
||||
getHeight(): number { return this.expandingRowMainElement.nativeElement.offsetHeight; }
|
||||
|
||||
/**
|
||||
* Expands this row. This will notify the host so that it can collapse
|
||||
* previously expanded row. This function also emits onToggle @Output event
|
||||
* to the user code.
|
||||
*/
|
||||
expand(): void {
|
||||
this.isExpanded = true;
|
||||
this.expandingRowHost.handleRowExpand(this);
|
||||
|
||||
// setTimeout here makes sure we scroll this row into view after animation.
|
||||
setTimeout(() => { this.expandingRowMainElement.nativeElement.focus(); });
|
||||
|
||||
this.onToggle.emit({rowId: this.rowId, isExpand: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapses this row. Setting isExpanded to false will make sure we hide
|
||||
* the caption and details, and show cfc-expanding-row-summary component.
|
||||
* This also emits onToggle @Output event to the user code.
|
||||
*/
|
||||
collapse(): void {
|
||||
this.isExpanded = false;
|
||||
this.onToggle.emit({rowId: this.rowId, isExpand: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* Blurs this row. This should remove the blue border on the left if there
|
||||
* is any. This function will remove DOM focus on the
|
||||
* cfc-expanding-row-summary
|
||||
* component.
|
||||
*/
|
||||
blur(): void {
|
||||
this.isFocused = false;
|
||||
this.summaryViewChild.blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses this row. This should put blue border on the left. If there is
|
||||
* any previous focus/selection, those should be gone. Parent
|
||||
* [cfcExpandingRowHost] component takes care of that.
|
||||
*/
|
||||
focus(): void {
|
||||
this.isFocused = true;
|
||||
this.expandingRowHost.handleRowFocus(this);
|
||||
|
||||
// Summary child is not present currently. We need to NG2 to update the
|
||||
// template.
|
||||
setTimeout(() => { this.summaryViewChild.focus(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* We listen for TAB press here to make sure we trap the focus on the
|
||||
* expanded
|
||||
* row. If the row is not expanded, we don't care about this event since focus
|
||||
* trap should work for expanded rows only.
|
||||
*/
|
||||
@HostListener('keydown', ['$event'])
|
||||
handleKeyDown(event: KeyboardEvent) {
|
||||
const charCode = event.which || event.keyCode;
|
||||
|
||||
switch (charCode) {
|
||||
case 9:
|
||||
if (!this.isExpanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trapFocus(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When this row is expanded, this function traps the focus between focusable
|
||||
* elements contained in this row.
|
||||
*/
|
||||
private trapFocus(event: KeyboardEvent): void {
|
||||
const rowElement: HTMLElement = this.expandingRowMainElement.nativeElement;
|
||||
const focusableEls: HTMLElement[] = [];
|
||||
let lastFocusableEl: HTMLElement = rowElement;
|
||||
|
||||
if (focusableEls.length) {
|
||||
lastFocusableEl = focusableEls[focusableEls.length - 1];
|
||||
}
|
||||
|
||||
if (event.target === lastFocusableEl && !event.shiftKey) {
|
||||
rowElement.focus();
|
||||
event.preventDefault();
|
||||
} else if (event.target === rowElement && event.shiftKey) {
|
||||
lastFocusableEl.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive} from '@angular/core';
|
||||
|
||||
|
||||
/**
|
||||
* This directive is used to flag an element to NOT trigger collapsing an
|
||||
* expanded row
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[cfcExpandingRowBlacklist]',
|
||||
})
|
||||
export class ExpandingRowBlacklist {
|
||||
}
|
87
modules/benchmarks/src/expanding_rows/expanding_row_css.ts
Normal file
87
modules/benchmarks/src/expanding_rows/expanding_row_css.ts
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export const expanding_row_css = `
|
||||
::ng-deep [cfcExpandingRowHost] {
|
||||
display: block;
|
||||
margin-bottom: 2;
|
||||
}
|
||||
|
||||
:host(cfc-expanding-row),
|
||||
:host(cfc-expanding-row-summary),
|
||||
:host(cfc-expanding-row-details-caption),
|
||||
:host(cfc-expanding-row-details-content) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cfc-expanding-row {
|
||||
background: white;
|
||||
border-top: 1 solid black;
|
||||
box-shadow: 0 1 1 gray;
|
||||
transition: margin 1 1;
|
||||
will-change: margin;
|
||||
}
|
||||
|
||||
.cfc-expanding-row.cfc-expanding-row-is-expanded {
|
||||
margin: 1 (-1);
|
||||
}
|
||||
|
||||
.cfc-expanding-row:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.cfc-expanding-row-summary {
|
||||
|
||||
display: flex;
|
||||
border-left: 6 solid transparent;
|
||||
cursor: pointer;
|
||||
padding: 6 2;
|
||||
|
||||
}
|
||||
|
||||
.cfc-expanding-row-summary:focus {
|
||||
outline: none;
|
||||
border-left-color: $cfc-color-active;
|
||||
}
|
||||
|
||||
// Adjust icons to be positioned correctly in the row.
|
||||
.cfc-expanding-row-summary::ng-deep cfc-icon {
|
||||
margin-top: 3;
|
||||
}
|
||||
|
||||
.cfc-expanding-row-details-caption {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding: 4 2;
|
||||
|
||||
}
|
||||
|
||||
.cfc-expanding-row-details-caption::ng-deep a,
|
||||
.cfc-expanding-row-details-caption::ng-deep a:visited,
|
||||
.cfc-expanding-row-details-caption::ng-deep a .cfc-external-link-content {
|
||||
border-color: $cfc-color-text-primary-inverse;
|
||||
color: $cfc-color-text-primary-inverse;
|
||||
}
|
||||
|
||||
// Adjust icons to be positioned correctly in the row.
|
||||
::ng-deep cfc-icon {
|
||||
margin-top: 3;
|
||||
}
|
||||
|
||||
.cfc-expanding-row-details-content {
|
||||
padding: 2;
|
||||
}
|
||||
|
||||
.cfc-expanding-row-details-content::ng-deep .ace-kv-list.cfc-full-bleed {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
|
||||
.cfc-expanding-row-accessibility-text {
|
||||
display: none;
|
||||
}`;
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnDestroy} from '@angular/core';
|
||||
import {Subject} from 'rxjs';
|
||||
import {takeUntil} from 'rxjs/operators';
|
||||
|
||||
import {ExpandingRow} from './expanding_row';
|
||||
import {expanding_row_css} from './expanding_row_css';
|
||||
|
||||
/**
|
||||
* This component should be within cfc-expanding-row component. The caption
|
||||
* is only visible when the row is expanded.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'cfc-expanding-row-details-caption',
|
||||
styles: [expanding_row_css],
|
||||
template: `
|
||||
<div *ngIf="expandingRow.isExpanded"
|
||||
(click)="expandingRow.handleCaptionClick($event)"
|
||||
[style.backgroundColor]="color"
|
||||
class="cfc-expanding-row-details-caption">
|
||||
<ng-content></ng-content>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExpandingRowDetailsCaption implements OnDestroy {
|
||||
/** The background color of this component. */
|
||||
@Input() color: string = 'blue';
|
||||
|
||||
/** This is triggered when this component is destroyed. */
|
||||
private readonly onDestroy = new Subject();
|
||||
|
||||
/**
|
||||
* We need a reference to parent cfc-expanding-row component here to hide
|
||||
* this component when the row is collapsed. We also need to relay clicks
|
||||
* to the parent component.
|
||||
*/
|
||||
constructor(@Host() public expandingRow: ExpandingRow, changeDetectorRef: ChangeDetectorRef) {
|
||||
this.expandingRow.isExpandedChange.pipe(takeUntil(this.onDestroy)).subscribe(() => {
|
||||
changeDetectorRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
/** When component is destroyed, unlisten to isExpanded. */
|
||||
ngOnDestroy(): void { this.onDestroy.next(); }
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, OnDestroy} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {ExpandingRow} from './expanding_row';
|
||||
import {expanding_row_css} from './expanding_row_css';
|
||||
|
||||
/**
|
||||
* This component should be within cfc-expanding-row component. Note that the
|
||||
* content is visible only when the row is expanded.
|
||||
*/
|
||||
@Component({
|
||||
styles: [expanding_row_css],
|
||||
selector: 'cfc-expanding-row-details-content',
|
||||
template: `
|
||||
<div class="cfc-expanding-row-details-content"
|
||||
*ngIf="expandingRow.isExpanded">
|
||||
<ng-content></ng-content>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExpandingRowDetailsContent implements OnDestroy {
|
||||
/** Used for unsubscribing to changes in isExpanded parent property. */
|
||||
private isExpandedChangeSubscription: Subscription;
|
||||
|
||||
/**
|
||||
* We need a reference to parent cfc-expanding-row component to make sure we
|
||||
* hide this component if the row is collapsed.
|
||||
*/
|
||||
constructor(@Host() public expandingRow: ExpandingRow, changeDetectorRef: ChangeDetectorRef) {
|
||||
this.isExpandedChangeSubscription =
|
||||
this.expandingRow.isExpandedChange.subscribe(() => { changeDetectorRef.markForCheck(); });
|
||||
}
|
||||
|
||||
/** Unsubscribe from changes in parent isExpanded property. */
|
||||
ngOnDestroy(): void { this.isExpandedChangeSubscription.unsubscribe(); }
|
||||
}
|
497
modules/benchmarks/src/expanding_rows/expanding_row_host.ts
Normal file
497
modules/benchmarks/src/expanding_rows/expanding_row_host.ts
Normal file
@ -0,0 +1,497 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterContentInit, AfterViewInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, EventEmitter, HostListener, Input, OnDestroy, Output, QueryList, ViewChild, forwardRef} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {EXPANDING_ROW_HOST_INJECTION_TOKEN, ExpandingRow, ExpandingRowHostBase} from './expanding_row';
|
||||
|
||||
|
||||
/**
|
||||
* We use this class in <cfc-expanding-row/> template to identify the row.
|
||||
* The [cfcExpandingRowHost] directive also uses this class to check if a given
|
||||
* HTMLElement is within an <cfc-expanding-row/>.
|
||||
*/
|
||||
const EXPANDING_ROW_CLASS_NAME = 'cfc-expanding-row';
|
||||
|
||||
/** Throttle duration in milliseconds for repeated key presses. */
|
||||
export const EXPANDING_ROW_KEYPRESS_THORTTLE_MS = 50;
|
||||
|
||||
/**
|
||||
* This type union is created to make arguments of handleUpOrDownPress*
|
||||
* methods in ExpandingRowHost class more readable.
|
||||
*/
|
||||
type UpOrDown = 'up' | 'down';
|
||||
|
||||
/**
|
||||
* This is the wrapper directive for the cfc-expanding-row components. Note that
|
||||
* we wanted to make this a directive instead of component because child
|
||||
* cfc-expanding-row components does not have to be a direct child.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'cfc-expanding-row-host',
|
||||
template: `
|
||||
<div #firstFocusable
|
||||
(focus)="focusOnLastFocusedRow()"
|
||||
tabindex="0">
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
<div #lastFocusable
|
||||
(focus)="focusOnLastFocusedRow()"
|
||||
tabindex="0">
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [{provide: EXPANDING_ROW_HOST_INJECTION_TOKEN, useExisting: ExpandingRowHost}],
|
||||
})
|
||||
export class ExpandingRowHost implements AfterViewInit,
|
||||
OnDestroy, ExpandingRowHostBase {
|
||||
/**
|
||||
* An HTML selector (e.g. "body") for the scroll element. We need this to
|
||||
* make some scroll adjustments.
|
||||
*/
|
||||
@Input() scrollElementSelector = '.cfc-panel-body-scrollable';
|
||||
|
||||
/**
|
||||
* An HTML selector (e.g. "body") for the click root. While the row is
|
||||
* expanded, and user clicks outside of the expanded row, we collapse this row
|
||||
* But to do this, we need to know the clickable area.
|
||||
*/
|
||||
@Input() clickRootElementSelector = 'cfc-panel-body';
|
||||
|
||||
/**
|
||||
* The @Output will be triggered when the user wants to focus on the
|
||||
* previously expanded row, and we are already at the first row. The logs team
|
||||
* will use this to prepend data on demand.
|
||||
*/
|
||||
@Output() onPrepend = new EventEmitter<void>();
|
||||
|
||||
/** A reference to the last focusable element in list of expanding rows. */
|
||||
@ViewChild('lastFocusable', {static: true}) lastFocusableElement !: ElementRef;
|
||||
|
||||
/** A reference to the first focusable element in list of expanding rows. */
|
||||
@ViewChild('firstFocusable', {static: true})
|
||||
firstFocusableElement !: ElementRef;
|
||||
|
||||
/**
|
||||
* A reference to all child cfc-expanding-row elements. We will need for
|
||||
* keyboard accessibility and scroll adjustments. For example, we need to know
|
||||
* which row is previous row when user presses "left arrow" on a focused row.
|
||||
*/
|
||||
@ContentChildren(forwardRef(() => ExpandingRow), {descendants: true})
|
||||
contentRows !: QueryList<ExpandingRow>;
|
||||
|
||||
/**
|
||||
* Keeps track of the last row that had focus before focus left the list
|
||||
* of expanding rows.
|
||||
*/
|
||||
lastFocusedRow?: ExpandingRow = undefined;
|
||||
|
||||
/**
|
||||
* Focused rows just show a blue left border. This node is not expanded. We
|
||||
* need to keep a reference to the focused row to unfocus when another row
|
||||
* is focused.
|
||||
*/
|
||||
private focusedRow?: ExpandingRow = undefined;
|
||||
|
||||
/**
|
||||
* This is the expanded row. If there is an expanded row there shouldn't be
|
||||
* any focused rows. We need a reference to this. For example we need to
|
||||
* collapse the currently expanded row, if another row is expanded.
|
||||
*/
|
||||
private expandedRow?: ExpandingRow = undefined;
|
||||
|
||||
/**
|
||||
* This is just handleRootMouseUp.bind(this). handleRootMouseUp handles
|
||||
* click events on root element (defined by clickRootElementSelector @Input)
|
||||
* Since we attach the click listener dynamically, we need to keep this
|
||||
* function around. This enables us to detach the click listener when
|
||||
* component is destroyed.
|
||||
*/
|
||||
private handleRootMouseUpBound: EventListenerObject = this.handleRootMouseUp.bind(this);
|
||||
|
||||
/**
|
||||
* 16px is the margin animation we have on cfc-expanding-row component.
|
||||
* We need this value to compute scroll adjustments.
|
||||
*/
|
||||
private static rowMargin = 16;
|
||||
|
||||
/** Subscription to changes in the expanding rows. */
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
private rowChangeSubscription !: Subscription;
|
||||
|
||||
/**
|
||||
* When component initializes we need to attach click listener to the root
|
||||
* element. This click listener will allows us to collapse the
|
||||
* currently expanded row when user clicks outside of it.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
const clickRootElement: HTMLElement = this.getClickRootElement();
|
||||
|
||||
if (!clickRootElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
clickRootElement.addEventListener('mouseup', this.handleRootMouseUpBound);
|
||||
|
||||
this.rowChangeSubscription =
|
||||
this.contentRows.changes.subscribe(() => { this.recalcRowIndexes(); });
|
||||
this.recalcRowIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the click listener on the root element. Note that we are attaching
|
||||
* this listener on ngAfterViewInit function.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
const clickRootElement: HTMLElement = this.getClickRootElement();
|
||||
|
||||
if (!clickRootElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
clickRootElement.removeEventListener('mouseup', this.handleRootMouseUpBound);
|
||||
|
||||
if (this.rowChangeSubscription) {
|
||||
this.rowChangeSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles caption element click on a cfc-expanding-row component. Note
|
||||
* that caption element is visible only when the row is expanded. So this
|
||||
* means we will collapse the expanded row. The scroll adjustment below
|
||||
* makes sure that the mouse stays under the summary of the expanded row
|
||||
* when the row collapses.
|
||||
*/
|
||||
handleRowCaptionClick(row: ExpandingRow): void {
|
||||
const scrollAdjustment: number = -ExpandingRowHost.rowMargin;
|
||||
const scrollElement: HTMLElement = this.getScrollElement() as HTMLElement;
|
||||
if (!scrollElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollElement.scrollTop += scrollAdjustment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles summary element click on a cfc-expanding-row component. Note
|
||||
* that summary element is visible only when the row is collapsed. So this
|
||||
* event will fired prior to expansion of a collapsed row. Scroll adjustment
|
||||
* below makes sure mouse stays on the caption element when the collapsed
|
||||
* row expands.
|
||||
*/
|
||||
handleRowSummaryClick(row: ExpandingRow): void {
|
||||
const hadPreviousSelection: boolean = !!this.expandedRow;
|
||||
const previousSelectedRowIndex: number = this.getRowIndex(this.expandedRow as ExpandingRow);
|
||||
const newSelectedRowIndex: number = this.getRowIndex(row);
|
||||
const previousCollapsedHeight: number = this.getSelectedRowCollapsedHeight();
|
||||
const previousExpansionHeight = this.getSelectedRowExpandedHeight();
|
||||
|
||||
if (this.expandedRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scrollAdjustment = 0;
|
||||
const scrollElement: HTMLElement = this.getScrollElement() as HTMLElement;
|
||||
if (!scrollElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousExpansionHeight > 0 && previousCollapsedHeight >= 0) {
|
||||
scrollAdjustment = previousExpansionHeight - previousCollapsedHeight;
|
||||
}
|
||||
|
||||
const newSelectionIsInfrontOfPrevious: boolean = newSelectedRowIndex > previousSelectedRowIndex;
|
||||
const multiplier = newSelectionIsInfrontOfPrevious ? -1 : 0;
|
||||
scrollAdjustment = scrollAdjustment * multiplier + ExpandingRowHost.rowMargin;
|
||||
|
||||
scrollElement.scrollTop += scrollAdjustment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles expansion of a row. When a new row expands, we need to remove
|
||||
* previous expansion and collapse. We also need to save the currently
|
||||
* expanded row so that we can collapse this row once another row expands.
|
||||
*/
|
||||
handleRowExpand(row: ExpandingRow): void {
|
||||
this.removePreviousFocus();
|
||||
this.removePreviousExpansion();
|
||||
this.expandedRow = row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles focus on a row. When a new row gets focus (note that this is
|
||||
* different from expansion), we need to remove previous focus and expansion.
|
||||
* We need to save the reference to this focused row so that we can unfocus
|
||||
* this row when another row is focused.
|
||||
*/
|
||||
handleRowFocus(row: ExpandingRow): void {
|
||||
// Do not blur then refocus the row if it's already selected.
|
||||
if (row === this.focusedRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.removePreviousFocus();
|
||||
this.removePreviousExpansion();
|
||||
this.focusedRow = row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when shift+tabbing from the first focusable element after the list
|
||||
* of expanding rows or tabbing from the last focusable element before.
|
||||
*/
|
||||
focusOnLastFocusedRow(): void {
|
||||
if (!this.lastFocusedRow) {
|
||||
this.lastFocusedRow = this.contentRows.toArray()[0];
|
||||
}
|
||||
this.lastFocusedRow.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that is called by expanding row summary to focus on the last
|
||||
* focusable element before the list of expanding rows.
|
||||
*/
|
||||
focusOnPreviousFocusableElement(): void { this.lastFocusedRow = this.focusedRow; }
|
||||
|
||||
/**
|
||||
* Function that is called by expanding row summary to focus on the next
|
||||
* focusable element after the list of expanding rows.
|
||||
*/
|
||||
focusOnNextFocusableElement(): void { this.lastFocusedRow = this.focusedRow; }
|
||||
|
||||
/**
|
||||
* Handles keydown event on the host. We are just concerned with up,
|
||||
* down arrow, ESC, and ENTER presses here. Note that Up/Down presses
|
||||
* can be repeated.
|
||||
*
|
||||
* - Up: Focuses on the row above.
|
||||
* - Down: Focuses on the row below.
|
||||
* - Escape: Collapses the expanded row.
|
||||
* - Enter: Expands the focused row.
|
||||
*/
|
||||
@HostListener('keydown', ['$event'])
|
||||
handleKeyDown(event: KeyboardEvent) {}
|
||||
|
||||
/**
|
||||
* Recursively returns true if target HTMLElement is within a
|
||||
* cfc-expanding-row component. It will return false otherwise.
|
||||
* We need this function in handleRootMouseUp to collapse the expanded row
|
||||
* when user clicks outside of all expanded rows.
|
||||
*/
|
||||
private isTargetInRow(target: HTMLElement): boolean {
|
||||
return target.classList.contains(EXPANDING_ROW_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the click root element that is described by clickRootElementSelector
|
||||
* @Input value.
|
||||
*/
|
||||
private getClickRootElement(): HTMLElement {
|
||||
return document.querySelector(this.clickRootElementSelector) as HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all of the mouseup events on the click root. When user clicks
|
||||
* outside of an expanded row, we need to collapse that row.
|
||||
* We trigger collapse by calling handleCaptionClick() on the expanded row.
|
||||
*/
|
||||
private handleRootMouseUp(event: MouseEvent): void {
|
||||
if (!this.expandedRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isTargetInRow(event.target as {} as HTMLElement)) {
|
||||
this.expandedRow.handleCaptionClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element is blacklisted. Blacklisted elements will not collapse an
|
||||
* open row when clicked.
|
||||
*/
|
||||
isBlacklisted(element: HTMLElement|null): boolean {
|
||||
const clickRoot = this.getClickRootElement();
|
||||
while (element && element !== clickRoot) {
|
||||
if (element.hasAttribute('cfcexpandingrowblacklist')) {
|
||||
return true;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes focus state from a previously focused row. We blur this row and
|
||||
* set the focusedRow to undefined in this method. This usually happens when
|
||||
* another row is focused.
|
||||
*/
|
||||
private removePreviousFocus(): void {
|
||||
if (this.focusedRow) {
|
||||
this.focusedRow.blur();
|
||||
this.focusedRow = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the expanded state from a previously expanded row. We collapse this
|
||||
* row and set the expandedRow to undefined in this method. This usually
|
||||
* happens when another row is expanded.
|
||||
*/
|
||||
private removePreviousExpansion(): void {
|
||||
if (this.expandedRow) {
|
||||
this.expandedRow.collapse();
|
||||
this.expandedRow = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collapsed height of the currently expanded row. We need this for
|
||||
* scroll adjustments. Note that collapsed height of a cfc-expanding-row
|
||||
* component is equal to height of cfc-expanding-row-summary component within
|
||||
* the row.
|
||||
*/
|
||||
private getSelectedRowCollapsedHeight(): number {
|
||||
if (this.expandedRow) {
|
||||
return this.expandedRow.collapsedHeight;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current height of the expanded row. We need this value for the
|
||||
* scroll adjustment computation.
|
||||
*/
|
||||
private getSelectedRowExpandedHeight(): number {
|
||||
if (this.expandedRow) {
|
||||
return this.expandedRow.getHeight();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML element described by scrollElementSelector @Input value.
|
||||
* We need this value for scroll adjustments.
|
||||
*/
|
||||
private getScrollElement(): HTMLElement|undefined {
|
||||
if (!this.scrollElementSelector) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return document.querySelector(this.scrollElementSelector) as HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles escape presses on the host element. Escape removes previous focus
|
||||
* if there is one. If there is an expanded row, escape row collapses this
|
||||
* row and focuses on it. A subsequent escape press will blur this row.
|
||||
*/
|
||||
private handleEscapePress(): void {
|
||||
this.removePreviousFocus();
|
||||
|
||||
if (this.expandedRow) {
|
||||
this.expandedRow.collapse();
|
||||
this.expandedRow.focus();
|
||||
this.expandedRow = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles enter keypress. If there is a focused row, an enter key press on
|
||||
* host element will expand this row.
|
||||
*/
|
||||
private handleEnterPress(): void {
|
||||
if (document.activeElement !== this.focusedRowSummary()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.focusedRow) {
|
||||
this.focusedRow.expand();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the HTMLElement that is the currently focused row summary. */
|
||||
private focusedRowSummary(): HTMLElement|undefined {
|
||||
return this.focusedRow ? this.focusedRow.summaryViewChild.mainElementRef.nativeElement :
|
||||
undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of a given row. This enables us to figure out the row
|
||||
* above/below the focused row.
|
||||
*/
|
||||
private getRowIndex(rowToLookFor: ExpandingRow): number {
|
||||
return rowToLookFor ? rowToLookFor.index : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles up/down arrow presses on the host element. Up arrow press will
|
||||
* focus/expand on the row above. Down arrow press will focus/expand the row
|
||||
* below. If we have a focus on the current row, this function will focus on
|
||||
* the computed (the one above or below) row. If host has an expanded row,
|
||||
* this function will expand the computed row.
|
||||
*/
|
||||
private handleUpOrDownPressOnce(upOrDown: UpOrDown, event: KeyboardEvent): void {
|
||||
event.preventDefault();
|
||||
|
||||
// If row is expanded but focus is inside the expanded element, arrow
|
||||
// key presses should not do anything.
|
||||
if (this.expandedRow &&
|
||||
document.activeElement !== this.expandedRow.expandingRowMainElement.nativeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If focus is inside a collapsed row header, arrow key presses should not
|
||||
// do anything.
|
||||
if (this.focusedRow && document.activeElement !== this.focusedRowSummary()) {
|
||||
return;
|
||||
}
|
||||
// We only want screen reader to read the message the first time we enter
|
||||
// the list of expanding rows, so we must reset the variable here
|
||||
this.lastFocusedRow = undefined;
|
||||
|
||||
const rowToLookFor: ExpandingRow|undefined = this.expandedRow || this.focusedRow;
|
||||
if (!rowToLookFor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isFocus: boolean = (rowToLookFor === this.focusedRow);
|
||||
|
||||
const rowIndex: number = this.getRowIndex(rowToLookFor);
|
||||
const contentRowsArray: ExpandingRow[] = this.contentRows.toArray();
|
||||
|
||||
if (rowIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const potentialIndex: number = (upOrDown === 'up' ? -1 : +1) + rowIndex;
|
||||
if (potentialIndex < 0) {
|
||||
this.onPrepend.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (potentialIndex >= contentRowsArray.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const potentialRow: ExpandingRow = contentRowsArray[potentialIndex];
|
||||
if (isFocus) {
|
||||
potentialRow.focus();
|
||||
} else {
|
||||
potentialRow.expand();
|
||||
}
|
||||
}
|
||||
|
||||
// Updates all of the rows with their new index.
|
||||
private recalcRowIndexes() {
|
||||
let index = 0;
|
||||
setTimeout(
|
||||
() => { this.contentRows.forEach((row: ExpandingRow) => { row.index = index++; }); });
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {ExpandingRow} from './expanding_row';
|
||||
import {ExpandingRowBlacklist} from './expanding_row_blacklist';
|
||||
import {ExpandingRowDetailsCaption} from './expanding_row_details_caption';
|
||||
import {ExpandingRowDetailsContent} from './expanding_row_details_content';
|
||||
import {ExpandingRowHost} from './expanding_row_host';
|
||||
import {ExpandingRowSummary} from './expanding_row_summary';
|
||||
|
||||
/** The main module for the cfc-expanding-row component. */
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ExpandingRow,
|
||||
ExpandingRowDetailsCaption,
|
||||
ExpandingRowDetailsContent,
|
||||
ExpandingRowHost,
|
||||
ExpandingRowSummary,
|
||||
ExpandingRowBlacklist,
|
||||
],
|
||||
exports: [
|
||||
ExpandingRow,
|
||||
ExpandingRowDetailsCaption,
|
||||
ExpandingRowDetailsContent,
|
||||
ExpandingRowHost,
|
||||
ExpandingRowSummary,
|
||||
ExpandingRowBlacklist,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
})
|
||||
export class ExpandingRowModule {
|
||||
}
|
207
modules/benchmarks/src/expanding_rows/expanding_row_summary.ts
Normal file
207
modules/benchmarks/src/expanding_rows/expanding_row_summary.ts
Normal file
@ -0,0 +1,207 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Host, HostListener, OnDestroy, ViewChild} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {ExpandingRow} from './expanding_row';
|
||||
import {expanding_row_css} from './expanding_row_css';
|
||||
|
||||
const KEY_CODE_TAB = 9;
|
||||
|
||||
/**
|
||||
* This component should be used within cfc-expanding-row component. Note that
|
||||
* summary is visible only when the row is collapsed.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'cfc-expanding-row-summary',
|
||||
styles: [expanding_row_css],
|
||||
template: `
|
||||
<div *ngIf="!expandingRow.isExpanded"
|
||||
#expandingRowSummaryMainElement
|
||||
class="cfc-expanding-row-summary"
|
||||
tabindex="-1"
|
||||
(click)="expandingRow.handleSummaryClick()"
|
||||
(focus)="handleFocus()">
|
||||
<ng-content></ng-content>
|
||||
<div class="cfc-expanding-row-accessibility-text">.</div>
|
||||
<div class="cfc-expanding-row-accessibility-text"
|
||||
i18n="This is the label used to indicate that the user is in a list of expanding rows.">
|
||||
Row {{expandingRow.index + 1}} in list of expanding rows.
|
||||
</div>
|
||||
<div *ngIf="isPreviouslyFocusedRow()"
|
||||
class="cfc-expanding-row-accessibility-text"
|
||||
i18n="This is the label used for the first row in list of expanding rows.">
|
||||
Use arrow keys to navigate.
|
||||
</div>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExpandingRowSummary implements OnDestroy {
|
||||
/**
|
||||
* A reference to the main element. This element should be focusable. We need
|
||||
* reference to compute collapsed height of the row. We also use this
|
||||
* reference for focus and blur methods below.
|
||||
*/
|
||||
@ViewChild('expandingRowSummaryMainElement', {static: false})
|
||||
mainElementRef !: ElementRef;
|
||||
|
||||
/** Subscription for changes in parent isExpanded property. */
|
||||
private isExpandedSubscription: Subscription;
|
||||
|
||||
/** Subscription for changes in parent index property. */
|
||||
private indexSubscription: Subscription;
|
||||
|
||||
/**
|
||||
* We need the parent cfc-expanding-row component here to hide this element
|
||||
* when the row is expanded. cfc-expanding-row-details-caption element
|
||||
* will act as a header for expanded rows. We also need to relay tab-in and
|
||||
* click events to the parent.
|
||||
*/
|
||||
constructor(@Host() public expandingRow: ExpandingRow, changeDetectorRef: ChangeDetectorRef) {
|
||||
this.expandingRow.summaryViewChild = this;
|
||||
this.isExpandedSubscription =
|
||||
this.expandingRow.isExpandedChange.subscribe(() => { changeDetectorRef.markForCheck(); });
|
||||
|
||||
this.indexSubscription =
|
||||
this.expandingRow.indexChange.subscribe(() => { changeDetectorRef.markForCheck(); });
|
||||
}
|
||||
|
||||
|
||||
/** When component is destroyed, unlisten to isExpanded. */
|
||||
ngOnDestroy(): void {
|
||||
if (this.isExpandedSubscription) {
|
||||
this.isExpandedSubscription.unsubscribe();
|
||||
}
|
||||
if (this.indexSubscription) {
|
||||
this.indexSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles focus event on the element. We basically want to detect any focus
|
||||
* in this component and relay this information to parent cfc-expanding-row
|
||||
* component.
|
||||
*/
|
||||
handleFocus(): void {
|
||||
// Clicking causes a focus event to occur before the click event. Filter
|
||||
// out click events using the cdkFocusMonitor.
|
||||
//
|
||||
// TODO(b/62385992) Use the KeyboardFocusService to detect focus cause
|
||||
// instead of creating multiple monitors on a page.
|
||||
if (this.expandingRow.expandingRowMainElement.nativeElement.classList.contains(
|
||||
'cdk-mouse-focused')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.expandingRow.isFocused && !this.expandingRow.isExpanded) {
|
||||
this.expandingRow.handleSummaryFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles tab & shift+tab presses on expanding row summaries in case there
|
||||
* are tabbable elements inside the summaries.
|
||||
*/
|
||||
@HostListener('keydown', ['$event'])
|
||||
handleKeyDown(event: KeyboardEvent) {
|
||||
const charCode = event.which || event.keyCode;
|
||||
if (charCode === KEY_CODE_TAB) {
|
||||
this.handleTabKeypress(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles tab and shift+tab presses inside expanding row summaries;
|
||||
*
|
||||
* From inside collapsed row summary:
|
||||
* - Tab: If focus was on the last focusable child, should shift focus to
|
||||
* the next focusable element outside the list of expanding rows.
|
||||
* - Shift+tab: If focus was on first focusable child, should shift focus to
|
||||
* the main collapsed row summary element
|
||||
* If focus was on main collapsed row summary element, should
|
||||
* shift focus to the last focusable element before the list of
|
||||
* expanding rows.
|
||||
*/
|
||||
handleTabKeypress(event: KeyboardEvent): void {
|
||||
const focusableChildren = this.getFocusableChildren();
|
||||
|
||||
if (focusableChildren.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shift+tab on expanding row summary should focus on last focusable element
|
||||
// before expanding row list. Otherwise, if shift+tab is pressed on first
|
||||
// focusable child inside expanding row summary, it should focus on main
|
||||
// expanding row summary element.
|
||||
if (event.shiftKey && document.activeElement === this.mainElementRef.nativeElement) {
|
||||
event.preventDefault();
|
||||
this.expandingRow.expandingRowHost.focusOnPreviousFocusableElement();
|
||||
return;
|
||||
} else if (event.shiftKey && document.activeElement === focusableChildren[0]) {
|
||||
event.preventDefault();
|
||||
this.expandingRow.focus();
|
||||
}
|
||||
|
||||
// If tab is pressed on the last focusable element inside an expanding row
|
||||
// summary, focus should be set to the next focusable element after the list
|
||||
// of expanding rows.
|
||||
if (!event.shiftKey &&
|
||||
document.activeElement === focusableChildren[focusableChildren.length - 1]) {
|
||||
event.preventDefault();
|
||||
this.expandingRow.expandingRowHost.focusOnNextFocusableElement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the row that had focus before focus left the list of expanding rows
|
||||
* and checks if the current row summary is that row.
|
||||
*/
|
||||
isPreviouslyFocusedRow(): boolean {
|
||||
if (!this.expandingRow.expandingRowHost.contentRows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expandingRowHost = this.expandingRow.expandingRowHost;
|
||||
|
||||
if (!this.mainElementRef || !expandingRowHost.lastFocusedRow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!expandingRowHost.lastFocusedRow.summaryViewChild.mainElementRef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the current expanding row summary was the last focused one before
|
||||
// focus exited the list, then return true to trigger the screen reader
|
||||
if (this.mainElementRef.nativeElement ===
|
||||
expandingRowHost.lastFocusedRow.summaryViewChild.mainElementRef.nativeElement) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Puts the DOM focus on the main element. */
|
||||
focus(): void {
|
||||
if (this.mainElementRef && document.activeElement !== this.mainElementRef.nativeElement) {
|
||||
this.mainElementRef.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the DOM focus on the main element. */
|
||||
blur(): void {
|
||||
if (!this.mainElementRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mainElementRef.nativeElement.blur();
|
||||
}
|
||||
|
||||
/** Returns array of focusable elements within this component. */
|
||||
private getFocusableChildren(): HTMLElement[] { return []; }
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* This interface is used to send toggle (expand/collapse) events to the user
|
||||
* code.
|
||||
*/
|
||||
export interface ExpandingRowToggleEvent {
|
||||
/** The identifier of the row that was toggled. */
|
||||
rowId: string;
|
||||
|
||||
/**
|
||||
* A boolean indicating whether or not this row was expanded. This is set to
|
||||
* false if the row was collapsed.
|
||||
*/
|
||||
isExpand: boolean;
|
||||
}
|
39
modules/benchmarks/src/expanding_rows/index.html
Normal file
39
modules/benchmarks/src/expanding_rows/index.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- Prevent the browser from requesting any favicon. -->
|
||||
<link rel="icon" href="data:,">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Change Detection Benchmark</h1>
|
||||
<div id="rendererMode">...</div>
|
||||
|
||||
<benchmark-root>loading...</benchmark-root>
|
||||
|
||||
<script>
|
||||
addEventListener('DOMContentLoaded', () => {
|
||||
// DevServer has automatic bootstrap code, so if we already have <scripts> than we don't need to bootstrap
|
||||
var alreadyBootstraped = document.querySelectorAll('script').length > 1; // 1 for ourselves
|
||||
if (!alreadyBootstraped) {
|
||||
function loadScript(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.body.append(script);
|
||||
});
|
||||
}
|
||||
// zone.js must be loaded and processed before Angular bundle gets executed
|
||||
loadScript('/npm/node_modules/zone.js/dist/zone.js').then(function () {
|
||||
loadScript(document.location.search.endsWith('debug') ? 'bundle.min_debug.js' : 'bundle.min.js');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
20
modules/benchmarks/src/expanding_rows/index.ts
Normal file
20
modules/benchmarks/src/expanding_rows/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {enableProdMode} from '@angular/core';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
import {ExpandingRowBenchmarkModule} from './benchmark';
|
||||
import {ExpandingRowBenchmarkModuleNgFactory} from './benchmark.ngfactory';
|
||||
|
||||
setMode(ExpandingRowBenchmarkModule.hasOwnProperty('ngModuleDef') ? 'Ivy' : 'ViewEngine');
|
||||
enableProdMode();
|
||||
platformBrowser().bootstrapModuleFactory(ExpandingRowBenchmarkModuleNgFactory);
|
||||
|
||||
function setMode(name: string): void {
|
||||
document.querySelector('#rendererMode') !.textContent = `Render Mode: ${name}`;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵRenderFlags, ɵrenderComponent as renderComponent, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation1, ɵɵtext, ɵɵtextBinding} from '@angular/core';
|
||||
import {ɵRenderFlags, ɵrenderComponent as renderComponent, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation1, ɵɵselect, ɵɵstyleProp, ɵɵstyling, ɵɵtext, ɵɵtextBinding} from '@angular/core';
|
||||
|
||||
import {bindAction, profile} from '../../util';
|
||||
import {createDom, destroyDom, detectChanges} from '../render3/tree';
|
||||
@ -38,7 +38,7 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
ɵɵelementStart(0, 'tree');
|
||||
{
|
||||
ɵɵelementStart(1, 'span');
|
||||
ɵɵelementStyling(null, c1);
|
||||
ɵɵstyling(null, c1);
|
||||
{ ɵɵtext(2); }
|
||||
ɵɵelementEnd();
|
||||
ɵɵcontainer(3);
|
||||
@ -47,7 +47,10 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵelementStyleProp(1, 0, ctx.depth % 2 ? '' : 'grey');
|
||||
ɵɵselect(1);
|
||||
ɵɵstyleProp(0, ctx.depth % 2 ? '' : 'grey');
|
||||
ɵɵstyling();
|
||||
ɵɵselect(2);
|
||||
ɵɵtextBinding(2, ɵɵinterpolation1(' ', ctx.value, ' '));
|
||||
ɵɵcontainerRefreshStart(3);
|
||||
{
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.0-next.2",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
"bugs": "https://github.com/angular/angular/issues",
|
||||
@ -166,4 +165,4 @@
|
||||
"resolutions": {
|
||||
"natives": "1.1.6"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../../",
|
||||
"paths": {
|
||||
"@angular/animations": ["../../../../dist/packages/animations"],
|
||||
"@angular/animations/browser": ["../../../../dist/packages/animations/browser"],
|
||||
"@angular/core": ["../../../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../../../dist/packages/animations"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../../../node_modules/@types/hammerjs/index.d.ts",
|
||||
"../../../../node_modules/@types/jasmine/index.d.ts",
|
||||
"../../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "testing.js",
|
||||
"flatModuleId": "@angular/animations/browser/testing"
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../",
|
||||
"paths": {
|
||||
"@angular/animations": ["../../../dist/packages/animations"],
|
||||
"@angular/core": ["../../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../../dist/packages/animations"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../../node_modules/@types/node/index.d.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts",
|
||||
"../../system.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "browser.js",
|
||||
"flatModuleId": "@angular/animations/browser"
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"@angular/core": ["../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../dist/packages/animations"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../node_modules/zone.js/dist/zone.js.d.ts",
|
||||
"../system.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "animations.js",
|
||||
"flatModuleId": "@angular/animations"
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@
|
||||
"node_modules/@angular/compiler-cli/**",
|
||||
"node_modules/@angular/**/testing/**",
|
||||
"node_modules/@angular/common/upgrade*",
|
||||
"node_modules/@angular/router/upgrade*"
|
||||
"node_modules/@angular/router/upgrade*",
|
||||
"node_modules/@angular/cdk/schematics*",
|
||||
"node_modules/@angular/material/schematics*"
|
||||
]
|
||||
}
|
||||
|
@ -61,22 +61,38 @@ describe('ng-add schematic', () => {
|
||||
new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'));
|
||||
});
|
||||
|
||||
it('throws if package.json is not found', () => {
|
||||
it('throws if package.json is not found', async() => {
|
||||
expect(host.files).toContain('/package.json');
|
||||
host.delete('/package.json');
|
||||
expect(() => schematicRunner.runSchematic('ng-add', defaultOptions))
|
||||
.toThrowError('Could not find package.json');
|
||||
|
||||
let message = 'No error';
|
||||
|
||||
try {
|
||||
await schematicRunner.runSchematicAsync('ng-add', defaultOptions).toPromise();
|
||||
} catch (e) {
|
||||
message = e.message;
|
||||
}
|
||||
|
||||
expect(message).toBe('Could not find package.json');
|
||||
});
|
||||
|
||||
it('throws if angular.json is not found', () => {
|
||||
it('throws if angular.json is not found', async() => {
|
||||
expect(host.files).toContain('/angular.json');
|
||||
host.delete('/angular.json');
|
||||
expect(() => schematicRunner.runSchematic('ng-add', defaultOptions, host))
|
||||
.toThrowError('Could not find angular.json');
|
||||
|
||||
let message = 'No error';
|
||||
|
||||
try {
|
||||
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
} catch (e) {
|
||||
message = e.message;
|
||||
}
|
||||
|
||||
expect(message).toBe('Could not find angular.json');
|
||||
});
|
||||
|
||||
it('should add @angular/bazel to package.json dependencies', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should add @angular/bazel to package.json dependencies', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/package.json');
|
||||
const content = host.readContent('/package.json');
|
||||
@ -91,8 +107,8 @@ describe('ng-add schematic', () => {
|
||||
expect(json.dependencies[core]).toBe(json.devDependencies[bazel]);
|
||||
});
|
||||
|
||||
it('should add @bazel/* dev dependencies', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should add @bazel/* dev dependencies', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const content = host.readContent('/package.json');
|
||||
const json = JSON.parse(content);
|
||||
const devDeps = Object.keys(json.devDependencies);
|
||||
@ -101,12 +117,12 @@ describe('ng-add schematic', () => {
|
||||
expect(devDeps).toContain('@bazel/karma');
|
||||
});
|
||||
|
||||
it('should replace an existing dev dependency', () => {
|
||||
it('should replace an existing dev dependency', async() => {
|
||||
expect(host.files).toContain('/package.json');
|
||||
const packageJson = JSON.parse(host.readContent('/package.json'));
|
||||
packageJson.devDependencies['@angular/bazel'] = '4.2.42';
|
||||
host.overwrite('/package.json', JSON.stringify(packageJson));
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const content = host.readContent('/package.json');
|
||||
// It is possible that a dep gets added twice if the package already exists.
|
||||
expect(content.match(/@angular\/bazel/g) !.length).toEqual(1);
|
||||
@ -114,29 +130,29 @@ describe('ng-add schematic', () => {
|
||||
expect(json.devDependencies['@angular/bazel']).toBe('1.2.3');
|
||||
});
|
||||
|
||||
it('should remove an existing dependency', () => {
|
||||
it('should remove an existing dependency', async() => {
|
||||
expect(host.files).toContain('/package.json');
|
||||
const packageJson = JSON.parse(host.readContent('/package.json'));
|
||||
packageJson.dependencies['@angular/bazel'] = '4.2.42';
|
||||
expect(Object.keys(packageJson.dependencies)).toContain('@angular/bazel');
|
||||
host.overwrite('/package.json', JSON.stringify(packageJson));
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const content = host.readContent('/package.json');
|
||||
const json = JSON.parse(content);
|
||||
expect(Object.keys(json.dependencies)).not.toContain('@angular/bazel');
|
||||
expect(json.devDependencies['@angular/bazel']).toBe('1.2.3');
|
||||
});
|
||||
|
||||
it('should not create Bazel workspace file', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should not create Bazel workspace file', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).not.toContain('/WORKSPACE');
|
||||
expect(files).not.toContain('/BUILD.bazel');
|
||||
});
|
||||
|
||||
it('should produce main.dev.ts and main.prod.ts for AOT', () => {
|
||||
it('should produce main.dev.ts and main.prod.ts for AOT', async() => {
|
||||
host.create('/src/main.ts', 'generated by CLI');
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
// main.dev.ts and main.prod.ts are used by Bazel for AOT
|
||||
expect(files).toContain('/src/main.dev.ts');
|
||||
@ -146,9 +162,9 @@ describe('ng-add schematic', () => {
|
||||
expect(files).toContain('/src/main.ts');
|
||||
});
|
||||
|
||||
it('should not overwrite index.html with script tags', () => {
|
||||
it('should not overwrite index.html with script tags', async() => {
|
||||
host.create('/src/index.html', '<html>Hello World</html>');
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/src/index.html');
|
||||
const content = host.readContent('/src/index.html');
|
||||
@ -156,34 +172,34 @@ describe('ng-add schematic', () => {
|
||||
expect(content).not.toMatch('<script src="/bundle.min.js"></script>');
|
||||
});
|
||||
|
||||
it('should generate main.dev.ts and main.prod.ts', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should generate main.dev.ts and main.prod.ts', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/src/main.dev.ts');
|
||||
expect(files).toContain('/src/main.prod.ts');
|
||||
});
|
||||
|
||||
it('should overwrite .gitignore for bazel-out directory', () => {
|
||||
it('should overwrite .gitignore for bazel-out directory', async() => {
|
||||
host.create('.gitignore', '\n# compiled output\n');
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/.gitignore');
|
||||
const content = host.readContent('/.gitignore');
|
||||
expect(content).toMatch('\n# compiled output\n/bazel-out\n');
|
||||
});
|
||||
|
||||
it('should create a backup for original angular.json', () => {
|
||||
it('should create a backup for original angular.json', async() => {
|
||||
expect(host.files).toContain('/angular.json');
|
||||
const original = host.readContent('/angular.json');
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.files).toContain('/angular.json.bak');
|
||||
const content = host.readContent('/angular.json.bak');
|
||||
expect(content.startsWith('// This is a backup file')).toBe(true);
|
||||
expect(content).toMatch(original);
|
||||
});
|
||||
|
||||
it('should update angular.json to use Bazel builder', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should update angular.json to use Bazel builder', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/angular.json');
|
||||
const content = host.readContent('/angular.json');
|
||||
@ -203,9 +219,9 @@ describe('ng-add schematic', () => {
|
||||
expect(lint.builder).toBe('@angular-devkit/build-angular:tslint');
|
||||
});
|
||||
|
||||
it('should get defaultProject if name is not provided', () => {
|
||||
it('should get defaultProject if name is not provided', async() => {
|
||||
const options = {};
|
||||
host = schematicRunner.runSchematic('ng-add', options, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', options, host).toPromise();
|
||||
const content = host.readContent('/angular.json');
|
||||
const json = JSON.parse(content);
|
||||
const builder = json.projects.demo.architect.build.builder;
|
||||
@ -228,7 +244,7 @@ describe('ng-add schematic', () => {
|
||||
['~7.0.1', false],
|
||||
];
|
||||
for (const [version, upgrade] of cases) {
|
||||
it(`should ${upgrade ? '' : 'not '}upgrade v${version}')`, () => {
|
||||
it(`should ${upgrade ? '' : 'not '}upgrade v${version}')`, async() => {
|
||||
host.overwrite('package.json', JSON.stringify({
|
||||
name: 'demo',
|
||||
dependencies: {
|
||||
@ -239,7 +255,7 @@ describe('ng-add schematic', () => {
|
||||
'typescript': '3.2.2',
|
||||
},
|
||||
}));
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.files).toContain('/package.json');
|
||||
const content = host.readContent('/package.json');
|
||||
const json = JSON.parse(content);
|
||||
@ -252,15 +268,15 @@ describe('ng-add schematic', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should add a postinstall step to package.json', () => {
|
||||
host = schematicRunner.runSchematic('ng-add', defaultOptions, host);
|
||||
it('should add a postinstall step to package.json', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.files).toContain('/package.json');
|
||||
const content = host.readContent('/package.json');
|
||||
const json = JSON.parse(content);
|
||||
expect(json.scripts.postinstall).toBe('ngc -p ./angular-metadata.tsconfig.json');
|
||||
});
|
||||
|
||||
it('should work when run on a minimal project (without test and e2e targets)', () => {
|
||||
it('should work when run on a minimal project (without test and e2e targets)', async() => {
|
||||
host.overwrite('angular.json', JSON.stringify({
|
||||
projects: {
|
||||
'demo': {
|
||||
@ -275,7 +291,15 @@ describe('ng-add schematic', () => {
|
||||
},
|
||||
}));
|
||||
|
||||
expect(() => schematicRunner.runSchematic('ng-add', defaultOptions, host)).not.toThrowError();
|
||||
let error: Error|null = null;
|
||||
|
||||
try {
|
||||
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -16,18 +16,18 @@ describe('ng-new schematic', () => {
|
||||
version: '7.0.0',
|
||||
};
|
||||
|
||||
it('should call external @schematics/angular', () => {
|
||||
it('should call external @schematics/angular', async() => {
|
||||
const options = {...defaultOptions};
|
||||
const host = schematicRunner.runSchematic('ng-new', options);
|
||||
const host = await schematicRunner.runSchematicAsync('ng-new', options).toPromise();
|
||||
const {files} = host;
|
||||
// External schematic should produce workspace file angular.json
|
||||
expect(files).toContain('/demo/angular.json');
|
||||
expect(files).toContain('/demo/package.json');
|
||||
});
|
||||
|
||||
it('should call ng-add to generate additional files needed by Bazel', () => {
|
||||
it('should call ng-add to generate additional files needed by Bazel', async() => {
|
||||
const options = {...defaultOptions};
|
||||
const host = schematicRunner.runSchematic('ng-new', options);
|
||||
const host = await schematicRunner.runSchematicAsync('ng-new', options).toPromise();
|
||||
const {files} = host;
|
||||
expect(files).toContain('/demo/src/main.dev.ts');
|
||||
expect(files).toContain('/demo/src/main.prod.ts');
|
||||
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
"sourceRoot": ".",
|
||||
"paths": {
|
||||
"@angular/core": ["../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../dist/packages/benchpress"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"index.ts",
|
||||
"../../node_modules/@types/node/index.d.ts",
|
||||
"../../node_modules/@types/jasmine/index.d.ts",
|
||||
"../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
]
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../../",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../../dist/packages/core"],
|
||||
"@angular/common": ["../../../../dist/packages/common"],
|
||||
"@angular/common/http": ["../../../../dist/packages/common/http"],
|
||||
"@angular/platform-browser": ["../../../../dist/packages/platform-browser"]
|
||||
},
|
||||
"outDir": "../../../../dist/packages/common"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "testing.js",
|
||||
"flatModuleId": "@angular/common/http/testing"
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../",
|
||||
"paths": {
|
||||
"@angular/common": ["../../../dist/packages/common"],
|
||||
"@angular/core": ["../../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../../dist/packages/common"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "http.js",
|
||||
"flatModuleId": "@angular/common/http"
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../../dist/packages/common/locales",
|
||||
"paths": {
|
||||
"@angular/common": ["../../../dist/packages/common"],
|
||||
"@angular/core": ["../../../dist/packages/core"]
|
||||
},
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"target": "es5",
|
||||
"skipLibCheck": true,
|
||||
"lib": ["es2015", "dom"]
|
||||
},
|
||||
"exclude": [
|
||||
"./closure-locale.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"skipMetadataEmit": true
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementHostStylingMap} from '@angular/core';
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵclassMap, ɵɵdefineDirective, ɵɵstyling, ɵɵstylingApply} from '@angular/core';
|
||||
|
||||
import {NgClassImpl, NgClassImplProvider} from './ng_class_impl';
|
||||
|
||||
@ -35,11 +35,11 @@ export const ngClassDirectiveDef__POST_R3__ = ɵɵdefineDirective({
|
||||
factory: () => {},
|
||||
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵɵelementHostStyling();
|
||||
ɵɵstyling();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵelementHostStylingMap(ctx.getValue());
|
||||
ɵɵelementHostStylingApply();
|
||||
ɵɵclassMap(ctx.getValue());
|
||||
ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -219,12 +219,12 @@ export class NgIf {
|
||||
/**
|
||||
* Assert the correct type of the expression bound to the `ngIf` input within the template.
|
||||
*
|
||||
* The presence of this method is a signal to the Ivy template type check compiler that when the
|
||||
* `NgIf` structural directive renders its template, the type of the expression bound to `ngIf`
|
||||
* should be narrowed in some way. For `NgIf`, it is narrowed to be non-null, which allows the
|
||||
* strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||
* The presence of this static field is a signal to the Ivy template type check compiler that
|
||||
* when the `NgIf` structural directive renders its template, the type of the expression bound
|
||||
* to `ngIf` should be narrowed in some way. For `NgIf`, the binding expression itself is used to
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||
*/
|
||||
static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E> { return true; }
|
||||
static ngTemplateGuard_ngIf: 'binding';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementHostStylingMap} from '@angular/core';
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵstyleMap, ɵɵstyling, ɵɵstylingApply} from '@angular/core';
|
||||
|
||||
import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl';
|
||||
|
||||
@ -35,11 +35,11 @@ export const ngStyleDirectiveDef__POST_R3__ = ɵɵdefineDirective({
|
||||
factory: () => {},
|
||||
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵɵelementHostStyling();
|
||||
ɵɵstyling();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵelementHostStylingMap(null, ctx.getValue());
|
||||
ɵɵelementHostStylingApply();
|
||||
ɵɵstyleMap(ctx.getValue());
|
||||
ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -6,10 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export const LOCALE_DATA: {[localeId: string]: any} = {};
|
||||
import {ɵLOCALE_DATA as LOCALE_DATA, ɵLocaleDataIndex as LocaleDataIndex} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Register global data to be used internally by Angular. See the
|
||||
@ -33,32 +30,6 @@ export function registerLocaleData(data: any, localeId?: string | any, extraData
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index of each type of locale data from the locale data array
|
||||
*/
|
||||
export const enum LocaleDataIndex {
|
||||
LocaleId = 0,
|
||||
DayPeriodsFormat,
|
||||
DayPeriodsStandalone,
|
||||
DaysFormat,
|
||||
DaysStandalone,
|
||||
MonthsFormat,
|
||||
MonthsStandalone,
|
||||
Eras,
|
||||
FirstDayOfWeek,
|
||||
WeekendRange,
|
||||
DateFormat,
|
||||
TimeFormat,
|
||||
DateTimeFormat,
|
||||
NumberSymbols,
|
||||
NumberFormats,
|
||||
CurrencySymbol,
|
||||
CurrencyName,
|
||||
Currencies,
|
||||
PluralCase,
|
||||
ExtraData
|
||||
}
|
||||
|
||||
/**
|
||||
* Index of each type of locale data from the extra locale data array
|
||||
*/
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import localeEn from './locale_en';
|
||||
import {LOCALE_DATA, LocaleDataIndex, ExtraLocaleDataIndex, CurrencyIndex} from './locale_data';
|
||||
import {ɵLocaleDataIndex as LocaleDataIndex, ɵfindLocaleData as findLocaleData, ɵgetLocalePluralCase} from '@angular/core';
|
||||
import {CURRENCIES_EN, CurrenciesSymbols} from './currencies';
|
||||
import {CurrencyIndex, ExtraLocaleDataIndex} from './locale_data';
|
||||
|
||||
/**
|
||||
* Format styles that can be used to represent numbers.
|
||||
@ -31,7 +31,8 @@ export enum NumberFormatStyle {
|
||||
* @see `NgPluralCase`
|
||||
* @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n)
|
||||
*
|
||||
* @publicApi */
|
||||
* @publicApi
|
||||
*/
|
||||
export enum Plural {
|
||||
Zero = 0,
|
||||
One = 1,
|
||||
@ -485,19 +486,11 @@ function getLocaleCurrencies(locale: string): {[code: string]: CurrenciesSymbols
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the plural function used by ICU expressions to determine the plural case to use
|
||||
* for a given locale.
|
||||
* @param locale A locale code for the locale format rules to use.
|
||||
* @returns The plural function for the locale.
|
||||
* @see `NgPlural`
|
||||
* @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n)
|
||||
*
|
||||
* @alias core/ɵgetLocalePluralCase
|
||||
* @publicApi
|
||||
*/
|
||||
export function getLocalePluralCase(locale: string): (value: number) => Plural {
|
||||
const data = findLocaleData(locale);
|
||||
return data[LocaleDataIndex.PluralCase];
|
||||
}
|
||||
export const getLocalePluralCase: (locale: string) => ((value: number) => Plural) =
|
||||
ɵgetLocalePluralCase;
|
||||
|
||||
function checkFullData(data: any) {
|
||||
if (!data[LocaleDataIndex.ExtraData]) {
|
||||
@ -609,37 +602,7 @@ function extractTime(time: string): Time {
|
||||
return {hours: +h, minutes: +m};
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the locale data for a given locale.
|
||||
*
|
||||
* @param locale The locale code.
|
||||
* @returns The locale data.
|
||||
* @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function findLocaleData(locale: string): any {
|
||||
const normalizedLocale = locale.toLowerCase().replace(/_/g, '-');
|
||||
|
||||
let match = LOCALE_DATA[normalizedLocale];
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
|
||||
// let's try to find a parent locale
|
||||
const parentLocale = normalizedLocale.split('-')[0];
|
||||
match = LOCALE_DATA[parentLocale];
|
||||
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
|
||||
if (parentLocale === 'en') {
|
||||
return localeEn;
|
||||
}
|
||||
|
||||
throw new Error(`Missing locale data for the locale "${locale}".`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the currency symbol for a given currency code.
|
||||
|
@ -22,6 +22,7 @@ export abstract class ViewportScroller {
|
||||
// See #23917
|
||||
/** @nocollapse */
|
||||
static ngInjectableDef = ɵɵdefineInjectable({
|
||||
token: ViewportScroller,
|
||||
providedIn: 'root',
|
||||
factory: () => new BrowserViewportScroller(ɵɵinject(DOCUMENT), window, ɵɵinject(ErrorHandler))
|
||||
});
|
||||
|
@ -204,6 +204,20 @@ describe('NgTemplateOutlet', () => {
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
// https://github.com/angular/angular/issues/30801
|
||||
it('should not throw if the context is left blank', () => {
|
||||
const template = `
|
||||
<ng-template #testTemplate>test</ng-template>
|
||||
<ng-template [ngTemplateOutlet]="testTemplate" [ngTemplateOutletContext]=""></ng-template>
|
||||
`;
|
||||
|
||||
expect(() => {
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('test');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵfindLocaleData as findLocaleData} from '@angular/core';
|
||||
import localeCaESVALENCIA from '@angular/common/locales/ca-ES-VALENCIA';
|
||||
import localeEn from '@angular/common/locales/en';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
@ -13,7 +14,7 @@ import localeZh from '@angular/common/locales/zh';
|
||||
import localeFrCA from '@angular/common/locales/fr-CA';
|
||||
import localeEnAU from '@angular/common/locales/en-AU';
|
||||
import {registerLocaleData} from '../../src/i18n/locale_data';
|
||||
import {findLocaleData, getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';
|
||||
import {getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';
|
||||
|
||||
{
|
||||
describe('locale data api', () => {
|
||||
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../",
|
||||
"paths": {
|
||||
"@angular/core": [
|
||||
"../../../dist/packages/core"
|
||||
],
|
||||
"@angular/common": [
|
||||
"../../../dist/packages/common"
|
||||
]
|
||||
},
|
||||
"outDir": "../../../dist/packages/common"
|
||||
},
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "testing.js",
|
||||
"flatModuleId": "@angular/common/testing"
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@angular/core": ["../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../dist/packages/common"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "common.js",
|
||||
"flatModuleId": "@angular/common"
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../",
|
||||
"paths": {
|
||||
"@angular/common": [
|
||||
"../../../dist/packages/common"
|
||||
],
|
||||
"@angular/core": [
|
||||
"../../../dist/packages/core"
|
||||
],
|
||||
"@angular/platform-browser": [
|
||||
"../../../dist/packages/platform-browser"
|
||||
]
|
||||
},
|
||||
"outDir": "../../../dist/packages/common"
|
||||
},
|
||||
"files": [
|
||||
"public_api.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "upgrade.js",
|
||||
"flatModuleId": "@angular/common/upgrade"
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entr
|
||||
export {ConsoleLogger, LogLevel} from './src/logging/console_logger';
|
||||
export {Logger} from './src/logging/logger';
|
||||
export {NgccOptions, mainNgcc as process} from './src/main';
|
||||
export {PathMappings} from './src/utils';
|
||||
|
||||
export function hasBeenProcessed(packageJson: object, format: string) {
|
||||
// We are wrapping this function to hide the internal types.
|
||||
|
@ -6,9 +6,9 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as path from 'canonical-path';
|
||||
import * as yargs from 'yargs';
|
||||
|
||||
import {AbsoluteFsPath} from '../src/ngtsc/path';
|
||||
import {mainNgcc} from './src/main';
|
||||
import {ConsoleLogger, LogLevel} from './src/logging/console_logger';
|
||||
|
||||
@ -56,7 +56,7 @@ if (require.main === module) {
|
||||
'The formats option (-f/--formats) has been removed. Consider the properties option (-p/--properties) instead.');
|
||||
process.exit(1);
|
||||
}
|
||||
const baseSourcePath = path.resolve(options['s'] || './node_modules');
|
||||
const baseSourcePath = AbsoluteFsPath.resolve(options['s'] || './node_modules');
|
||||
const propertiesToConsider: string[] = options['p'];
|
||||
const targetEntryPointPath = options['t'] ? options['t'] : undefined;
|
||||
const compileAllFormats = !options['first-only'];
|
||||
|
@ -6,9 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ConstantPool} from '@angular/compiler';
|
||||
import {NOOP_PERF_RECORDER} from '@angular/compiler-cli/src/ngtsc/perf';
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
|
||||
@ -17,9 +14,10 @@ import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy,
|
||||
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {AbsoluteFsPath, LogicalFileSystem} from '../../../src/ngtsc/path';
|
||||
import {ClassDeclaration, ClassSymbol, Decorator} from '../../../src/ngtsc/reflection';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
|
||||
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
|
||||
import {DecoratedClass} from '../host/decorated_class';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
||||
|
||||
@ -28,7 +26,10 @@ export interface AnalyzedFile {
|
||||
analyzedClasses: AnalyzedClass[];
|
||||
}
|
||||
|
||||
export interface AnalyzedClass extends DecoratedClass {
|
||||
export interface AnalyzedClass {
|
||||
name: string;
|
||||
decorators: Decorator[]|null;
|
||||
declaration: ClassDeclaration;
|
||||
diagnostics?: ts.Diagnostic[];
|
||||
matches: {handler: DecoratorHandler<any, any>; analysis: any;}[];
|
||||
}
|
||||
@ -53,11 +54,12 @@ export interface MatchingHandler<A, M> {
|
||||
* Simple class that resolves and loads files directly from the filesystem.
|
||||
*/
|
||||
class NgccResourceLoader implements ResourceLoader {
|
||||
constructor(private fs: FileSystem) {}
|
||||
canPreload = false;
|
||||
preload(): undefined|Promise<void> { throw new Error('Not implemented.'); }
|
||||
load(url: string): string { return fs.readFileSync(url, 'utf8'); }
|
||||
load(url: string): string { return this.fs.readFile(AbsoluteFsPath.resolve(url)); }
|
||||
resolve(url: string, containingFile: string): string {
|
||||
return path.resolve(path.dirname(containingFile), url);
|
||||
return AbsoluteFsPath.resolve(AbsoluteFsPath.dirname(AbsoluteFsPath.from(containingFile)), url);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,16 +67,17 @@ class NgccResourceLoader implements ResourceLoader {
|
||||
* This Analyzer will analyze the files that have decorated classes that need to be transformed.
|
||||
*/
|
||||
export class DecorationAnalyzer {
|
||||
resourceManager = new NgccResourceLoader();
|
||||
resourceManager = new NgccResourceLoader(this.fs);
|
||||
metaRegistry = new LocalMetadataRegistry();
|
||||
dtsMetaReader = new DtsMetadataReader(this.typeChecker, this.reflectionHost);
|
||||
fullMetaReader = new CompoundMetadataReader([this.metaRegistry, this.dtsMetaReader]);
|
||||
refEmitter = new ReferenceEmitter([
|
||||
new LocalIdentifierStrategy(),
|
||||
new AbsoluteModuleStrategy(this.program, this.typeChecker, this.options, this.host),
|
||||
new AbsoluteModuleStrategy(
|
||||
this.program, this.typeChecker, this.options, this.host, this.reflectionHost),
|
||||
// TODO(alxhub): there's no reason why ngcc needs the "logical file system" logic here, as ngcc
|
||||
// projects only ever have one rootDir. Instead, ngcc should just switch its emitted imort based
|
||||
// on whether a bestGuessOwningModule is present in the Reference.
|
||||
// projects only ever have one rootDir. Instead, ngcc should just switch its emitted import
|
||||
// based on whether a bestGuessOwningModule is present in the Reference.
|
||||
new LogicalProjectStrategy(this.typeChecker, new LogicalFileSystem(this.rootDirs)),
|
||||
]);
|
||||
dtsModuleScopeResolver =
|
||||
@ -110,7 +113,7 @@ export class DecorationAnalyzer {
|
||||
];
|
||||
|
||||
constructor(
|
||||
private program: ts.Program, private options: ts.CompilerOptions,
|
||||
private fs: FileSystem, private program: ts.Program, private options: ts.CompilerOptions,
|
||||
private host: ts.CompilerHost, private typeChecker: ts.TypeChecker,
|
||||
private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry,
|
||||
private rootDirs: AbsoluteFsPath[], private isCore: boolean) {}
|
||||
@ -133,19 +136,18 @@ export class DecorationAnalyzer {
|
||||
}
|
||||
|
||||
protected analyzeFile(sourceFile: ts.SourceFile): AnalyzedFile|undefined {
|
||||
const decoratedClasses = this.reflectionHost.findDecoratedClasses(sourceFile);
|
||||
return decoratedClasses.length ? {
|
||||
sourceFile,
|
||||
analyzedClasses: decoratedClasses.map(clazz => this.analyzeClass(clazz)).filter(isDefined)
|
||||
} :
|
||||
undefined;
|
||||
const analyzedClasses = this.reflectionHost.findClassSymbols(sourceFile)
|
||||
.map(symbol => this.analyzeClass(symbol))
|
||||
.filter(isDefined);
|
||||
return analyzedClasses.length ? {sourceFile, analyzedClasses} : undefined;
|
||||
}
|
||||
|
||||
protected analyzeClass(clazz: DecoratedClass): AnalyzedClass|null {
|
||||
protected analyzeClass(symbol: ClassSymbol): AnalyzedClass|null {
|
||||
const declaration = symbol.valueDeclaration;
|
||||
const decorators = this.reflectionHost.getDecoratorsOfSymbol(symbol);
|
||||
const matchingHandlers = this.handlers
|
||||
.map(handler => {
|
||||
const detected =
|
||||
handler.detect(clazz.declaration, clazz.decorators);
|
||||
const detected = handler.detect(declaration, decorators);
|
||||
return {handler, detected};
|
||||
})
|
||||
.filter(isMatchingHandler);
|
||||
@ -183,13 +185,19 @@ export class DecorationAnalyzer {
|
||||
const matches: {handler: DecoratorHandler<any, any>, analysis: any}[] = [];
|
||||
const allDiagnostics: ts.Diagnostic[] = [];
|
||||
for (const {handler, detected} of detections) {
|
||||
const {analysis, diagnostics} = handler.analyze(clazz.declaration, detected.metadata);
|
||||
const {analysis, diagnostics} = handler.analyze(declaration, detected.metadata);
|
||||
if (diagnostics !== undefined) {
|
||||
allDiagnostics.push(...diagnostics);
|
||||
}
|
||||
matches.push({handler, analysis});
|
||||
}
|
||||
return {...clazz, matches, diagnostics: allDiagnostics.length > 0 ? allDiagnostics : undefined};
|
||||
return {
|
||||
name: symbol.name,
|
||||
declaration,
|
||||
decorators,
|
||||
matches,
|
||||
diagnostics: allDiagnostics.length > 0 ? allDiagnostics : undefined
|
||||
};
|
||||
}
|
||||
|
||||
protected compileFile(analyzedFile: AnalyzedFile): CompiledFile {
|
||||
|
@ -63,7 +63,7 @@ export class ModuleWithProvidersAnalyzer {
|
||||
ngModule = {node: dtsNgModule, viaModule: null};
|
||||
}
|
||||
const dtsFile = dtsFn.getSourceFile();
|
||||
const analysis = analyses.get(dtsFile) || [];
|
||||
const analysis = analyses.has(dtsFile) ? analyses.get(dtsFile) : [];
|
||||
analysis.push({declaration: dtsFn, ngModule});
|
||||
analyses.set(dtsFile, analysis);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {Declaration} from '../../../src/ngtsc/reflection';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {hasNameIdentifier, isDefined} from '../utils';
|
||||
@ -14,8 +15,8 @@ import {NgccReferencesRegistry} from './ngcc_references_registry';
|
||||
|
||||
export interface ExportInfo {
|
||||
identifier: string;
|
||||
from: string;
|
||||
dtsFrom?: string|null;
|
||||
from: AbsoluteFsPath;
|
||||
dtsFrom?: AbsoluteFsPath|null;
|
||||
alias?: string|null;
|
||||
}
|
||||
export type PrivateDeclarationsAnalyses = ExportInfo[];
|
||||
@ -48,8 +49,8 @@ export class PrivateDeclarationsAnalyzer {
|
||||
if (exports) {
|
||||
exports.forEach((declaration, exportedName) => {
|
||||
if (hasNameIdentifier(declaration.node)) {
|
||||
const privateDeclaration = privateDeclarations.get(declaration.node.name);
|
||||
if (privateDeclaration) {
|
||||
if (privateDeclarations.has(declaration.node.name)) {
|
||||
const privateDeclaration = privateDeclarations.get(declaration.node.name) !;
|
||||
if (privateDeclaration.node !== declaration.node) {
|
||||
throw new Error(`${declaration.node.name.text} is declared multiple times.`);
|
||||
}
|
||||
@ -93,11 +94,12 @@ export class PrivateDeclarationsAnalyzer {
|
||||
});
|
||||
|
||||
return Array.from(privateDeclarations.keys()).map(id => {
|
||||
const from = id.getSourceFile().fileName;
|
||||
const from = AbsoluteFsPath.fromSourceFile(id.getSourceFile());
|
||||
const declaration = privateDeclarations.get(id) !;
|
||||
const alias = exportAliasDeclarations.get(id) || null;
|
||||
const alias = exportAliasDeclarations.has(id) ? exportAliasDeclarations.get(id) ! : null;
|
||||
const dtsDeclaration = this.host.getDtsDeclaration(declaration.node);
|
||||
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
|
||||
const dtsFrom =
|
||||
dtsDeclaration && AbsoluteFsPath.fromSourceFile(dtsDeclaration.getSourceFile());
|
||||
|
||||
return {identifier: id.text, from, dtsFrom, alias};
|
||||
});
|
||||
|
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {isRequireCall} from '../host/commonjs_host';
|
||||
|
||||
import {DependencyHost, DependencyInfo} from './dependency_host';
|
||||
import {ModuleResolver, ResolvedDeepImport, ResolvedRelativeModule} from './module_resolver';
|
||||
|
||||
/**
|
||||
* Helper functions for computing dependencies.
|
||||
*/
|
||||
export class CommonJsDependencyHost implements DependencyHost {
|
||||
constructor(private fs: FileSystem, private moduleResolver: ModuleResolver) {}
|
||||
|
||||
/**
|
||||
* Find all the dependencies for the entry-point at the given path.
|
||||
*
|
||||
* @param entryPointPath The absolute path to the JavaScript file that represents an entry-point.
|
||||
* @returns Information about the dependencies of the entry-point, including those that were
|
||||
* missing or deep imports into other entry-points.
|
||||
*/
|
||||
findDependencies(entryPointPath: AbsoluteFsPath): DependencyInfo {
|
||||
const dependencies = new Set<AbsoluteFsPath>();
|
||||
const missing = new Set<AbsoluteFsPath|PathSegment>();
|
||||
const deepImports = new Set<AbsoluteFsPath>();
|
||||
const alreadySeen = new Set<AbsoluteFsPath>();
|
||||
this.recursivelyFindDependencies(
|
||||
entryPointPath, dependencies, missing, deepImports, alreadySeen);
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the dependencies of the given file.
|
||||
*
|
||||
* @param file An absolute path to the file whose dependencies we want to get.
|
||||
* @param dependencies A set that will have the absolute paths of resolved entry points added to
|
||||
* it.
|
||||
* @param missing A set that will have the dependencies that could not be found added to it.
|
||||
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
|
||||
* entry-points, i.e. deep-imports.
|
||||
* @param alreadySeen A set that is used to track internal dependencies to prevent getting stuck
|
||||
* in a
|
||||
* circular dependency loop.
|
||||
*/
|
||||
private recursivelyFindDependencies(
|
||||
file: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath>, missing: Set<string>,
|
||||
deepImports: Set<AbsoluteFsPath>, alreadySeen: Set<AbsoluteFsPath>): void {
|
||||
const fromContents = this.fs.readFile(file);
|
||||
if (!this.hasRequireCalls(fromContents)) {
|
||||
// Avoid parsing the source file as there are no require calls.
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
|
||||
const sf =
|
||||
ts.createSourceFile(file, fromContents, ts.ScriptTarget.ES2015, false, ts.ScriptKind.JS);
|
||||
|
||||
for (const statement of sf.statements) {
|
||||
const declarations =
|
||||
ts.isVariableStatement(statement) ? statement.declarationList.declarations : [];
|
||||
for (const declaration of declarations) {
|
||||
if (declaration.initializer && isRequireCall(declaration.initializer)) {
|
||||
const importPath = declaration.initializer.arguments[0].text;
|
||||
const resolvedModule = this.moduleResolver.resolveModuleImport(importPath, file);
|
||||
if (resolvedModule) {
|
||||
if (resolvedModule instanceof ResolvedRelativeModule) {
|
||||
const internalDependency = resolvedModule.modulePath;
|
||||
if (!alreadySeen.has(internalDependency)) {
|
||||
alreadySeen.add(internalDependency);
|
||||
this.recursivelyFindDependencies(
|
||||
internalDependency, dependencies, missing, deepImports, alreadySeen);
|
||||
}
|
||||
} else {
|
||||
if (resolvedModule instanceof ResolvedDeepImport) {
|
||||
deepImports.add(resolvedModule.importPath);
|
||||
} else {
|
||||
dependencies.add(resolvedModule.entryPointPath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
missing.add(importPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a source file needs to be parsed for imports.
|
||||
* This is a performance short-circuit, which saves us from creating
|
||||
* a TypeScript AST unnecessarily.
|
||||
*
|
||||
* @param source The content of the source file to check.
|
||||
*
|
||||
* @returns false if there are definitely no require calls
|
||||
* in this file, true otherwise.
|
||||
*/
|
||||
hasRequireCalls(source: string): boolean { return /require\(['"]/.test(source); }
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
|
||||
export interface DependencyHost {
|
||||
findDependencies(entryPointPath: AbsoluteFsPath): DependencyInfo;
|
||||
}
|
||||
|
||||
export interface DependencyInfo {
|
||||
dependencies: Set<AbsoluteFsPath>;
|
||||
missing: Set<AbsoluteFsPath|PathSegment>;
|
||||
deepImports: Set<AbsoluteFsPath>;
|
||||
}
|
@ -6,15 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {resolve} from 'canonical-path';
|
||||
import {DepGraph} from 'dependency-graph';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
|
||||
import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointFormat} from '../packages/entry_point';
|
||||
import {DependencyHost} from './dependency_host';
|
||||
import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat} from './entry_point';
|
||||
|
||||
|
||||
/**
|
||||
* Holds information about entry points that are removed because
|
||||
@ -48,6 +45,11 @@ export interface IgnoredDependency {
|
||||
dependencyPath: string;
|
||||
}
|
||||
|
||||
export interface DependencyDiagnostics {
|
||||
invalidEntryPoints: InvalidEntryPoint[];
|
||||
ignoredDependencies: IgnoredDependency[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of entry-points, sorted by their dependencies.
|
||||
*
|
||||
@ -57,17 +59,15 @@ export interface IgnoredDependency {
|
||||
* Some entry points or their dependencies may be have been ignored. These are captured for
|
||||
* diagnostic purposes in `invalidEntryPoints` and `ignoredDependencies` respectively.
|
||||
*/
|
||||
export interface SortedEntryPointsInfo {
|
||||
entryPoints: EntryPoint[];
|
||||
invalidEntryPoints: InvalidEntryPoint[];
|
||||
ignoredDependencies: IgnoredDependency[];
|
||||
}
|
||||
export interface SortedEntryPointsInfo extends DependencyDiagnostics { entryPoints: EntryPoint[]; }
|
||||
|
||||
/**
|
||||
* A class that resolves dependencies between entry-points.
|
||||
*/
|
||||
export class DependencyResolver {
|
||||
constructor(private logger: Logger, private host: DependencyHost) {}
|
||||
constructor(
|
||||
private fs: FileSystem, private logger: Logger,
|
||||
private hosts: Partial<Record<EntryPointFormat, DependencyHost>>) {}
|
||||
/**
|
||||
* Sort the array of entry points so that the dependant entry points always come later than
|
||||
* their dependencies in the array.
|
||||
@ -77,12 +77,17 @@ export class DependencyResolver {
|
||||
*/
|
||||
sortEntryPointsByDependency(entryPoints: EntryPoint[], target?: EntryPoint):
|
||||
SortedEntryPointsInfo {
|
||||
const {invalidEntryPoints, ignoredDependencies, graph} = this.createDependencyInfo(entryPoints);
|
||||
const {invalidEntryPoints, ignoredDependencies, graph} =
|
||||
this.computeDependencyGraph(entryPoints);
|
||||
|
||||
let sortedEntryPointNodes: string[];
|
||||
if (target) {
|
||||
sortedEntryPointNodes = graph.dependenciesOf(target.path);
|
||||
sortedEntryPointNodes.push(target.path);
|
||||
if (target.compiledByAngular) {
|
||||
sortedEntryPointNodes = graph.dependenciesOf(target.path);
|
||||
sortedEntryPointNodes.push(target.path);
|
||||
} else {
|
||||
sortedEntryPointNodes = [];
|
||||
}
|
||||
} else {
|
||||
sortedEntryPointNodes = graph.overallOrder();
|
||||
}
|
||||
@ -100,18 +105,25 @@ export class DependencyResolver {
|
||||
* The graph only holds entry-points that ngcc cares about and whose dependencies
|
||||
* (direct and transitive) all exist.
|
||||
*/
|
||||
private createDependencyInfo(entryPoints: EntryPoint[]) {
|
||||
private computeDependencyGraph(entryPoints: EntryPoint[]): DependencyGraph {
|
||||
const invalidEntryPoints: InvalidEntryPoint[] = [];
|
||||
const ignoredDependencies: IgnoredDependency[] = [];
|
||||
const graph = new DepGraph<EntryPoint>();
|
||||
|
||||
// Add the entry points to the graph as nodes
|
||||
entryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint));
|
||||
const angularEntryPoints = entryPoints.filter(entryPoint => entryPoint.compiledByAngular);
|
||||
|
||||
// Add the Angular compiled entry points to the graph as nodes
|
||||
angularEntryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint));
|
||||
|
||||
// Now add the dependencies between them
|
||||
entryPoints.forEach(entryPoint => {
|
||||
const entryPointPath = getEntryPointPath(entryPoint);
|
||||
const {dependencies, missing, deepImports} = this.host.computeDependencies(entryPointPath);
|
||||
angularEntryPoints.forEach(entryPoint => {
|
||||
const formatInfo = this.getEntryPointFormatInfo(entryPoint);
|
||||
const host = this.hosts[formatInfo.format];
|
||||
if (!host) {
|
||||
throw new Error(
|
||||
`Could not find a suitable format for computing dependencies of entry-point: '${entryPoint.path}'.`);
|
||||
}
|
||||
const {dependencies, missing, deepImports} = host.findDependencies(formatInfo.path);
|
||||
|
||||
if (missing.size > 0) {
|
||||
// This entry point has dependencies that are missing
|
||||
@ -120,9 +132,11 @@ export class DependencyResolver {
|
||||
} else {
|
||||
dependencies.forEach(dependencyPath => {
|
||||
if (graph.hasNode(dependencyPath)) {
|
||||
// The dependency path maps to an entry point that exists in the graph
|
||||
// so add the dependency.
|
||||
graph.addDependency(entryPoint.path, dependencyPath);
|
||||
if (graph.hasNode(entryPoint.path)) {
|
||||
// The entry-point is still valid (i.e. has no missing dependencies) and
|
||||
// the dependency maps to an entry point that exists in the graph so add it
|
||||
graph.addDependency(entryPoint.path, dependencyPath);
|
||||
}
|
||||
} else if (invalidEntryPoints.some(i => i.entryPoint.path === dependencyPath)) {
|
||||
// The dependency path maps to an entry-point that was previously removed
|
||||
// from the graph, so remove this entry-point as well.
|
||||
@ -152,18 +166,24 @@ export class DependencyResolver {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEntryPointPath(entryPoint: EntryPoint): AbsoluteFsPath {
|
||||
const properties = Object.keys(entryPoint.packageJson);
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const property = properties[i] as EntryPointJsonProperty;
|
||||
const format = getEntryPointFormat(property);
|
||||
private getEntryPointFormatInfo(entryPoint: EntryPoint):
|
||||
{format: EntryPointFormat, path: AbsoluteFsPath} {
|
||||
const properties = Object.keys(entryPoint.packageJson);
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const property = properties[i] as EntryPointJsonProperty;
|
||||
const format = getEntryPointFormat(this.fs, entryPoint, property);
|
||||
|
||||
if (format === 'esm2015' || format === 'esm5') {
|
||||
const formatPath = entryPoint.packageJson[property] !;
|
||||
return AbsoluteFsPath.from(resolve(entryPoint.path, formatPath));
|
||||
if (format === 'esm2015' || format === 'esm5' || format === 'umd' || format === 'commonjs') {
|
||||
const formatPath = entryPoint.packageJson[property] !;
|
||||
return {format, path: AbsoluteFsPath.resolve(entryPoint.path, formatPath)};
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`There is no appropriate source code format in '${entryPoint.path}' entry-point.`);
|
||||
}
|
||||
throw new Error(`There is no format with import statements in '${entryPoint.path}' entry-point.`);
|
||||
}
|
||||
|
||||
interface DependencyGraph extends DependencyDiagnostics {
|
||||
graph: DepGraph<EntryPoint>;
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {DependencyHost, DependencyInfo} from './dependency_host';
|
||||
import {ModuleResolver, ResolvedDeepImport, ResolvedRelativeModule} from './module_resolver';
|
||||
|
||||
|
||||
/**
|
||||
* Helper functions for computing dependencies.
|
||||
*/
|
||||
export class EsmDependencyHost implements DependencyHost {
|
||||
constructor(private fs: FileSystem, private moduleResolver: ModuleResolver) {}
|
||||
|
||||
/**
|
||||
* Find all the dependencies for the entry-point at the given path.
|
||||
*
|
||||
* @param entryPointPath The absolute path to the JavaScript file that represents an entry-point.
|
||||
* @returns Information about the dependencies of the entry-point, including those that were
|
||||
* missing or deep imports into other entry-points.
|
||||
*/
|
||||
findDependencies(entryPointPath: AbsoluteFsPath): DependencyInfo {
|
||||
const dependencies = new Set<AbsoluteFsPath>();
|
||||
const missing = new Set<AbsoluteFsPath|PathSegment>();
|
||||
const deepImports = new Set<AbsoluteFsPath>();
|
||||
const alreadySeen = new Set<AbsoluteFsPath>();
|
||||
this.recursivelyFindDependencies(
|
||||
entryPointPath, dependencies, missing, deepImports, alreadySeen);
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the dependencies of the given file.
|
||||
*
|
||||
* @param file An absolute path to the file whose dependencies we want to get.
|
||||
* @param dependencies A set that will have the absolute paths of resolved entry points added to
|
||||
* it.
|
||||
* @param missing A set that will have the dependencies that could not be found added to it.
|
||||
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
|
||||
* entry-points, i.e. deep-imports.
|
||||
* @param alreadySeen A set that is used to track internal dependencies to prevent getting stuck
|
||||
* in a
|
||||
* circular dependency loop.
|
||||
*/
|
||||
private recursivelyFindDependencies(
|
||||
file: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath>, missing: Set<string>,
|
||||
deepImports: Set<string>, alreadySeen: Set<AbsoluteFsPath>): void {
|
||||
const fromContents = this.fs.readFile(file);
|
||||
if (!this.hasImportOrReexportStatements(fromContents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
|
||||
const sf =
|
||||
ts.createSourceFile(file, fromContents, ts.ScriptTarget.ES2015, false, ts.ScriptKind.JS);
|
||||
sf.statements
|
||||
// filter out statements that are not imports or reexports
|
||||
.filter(this.isStringImportOrReexport)
|
||||
// Grab the id of the module that is being imported
|
||||
.map(stmt => stmt.moduleSpecifier.text)
|
||||
// Resolve this module id into an absolute path
|
||||
.forEach(importPath => {
|
||||
const resolvedModule = this.moduleResolver.resolveModuleImport(importPath, file);
|
||||
if (resolvedModule) {
|
||||
if (resolvedModule instanceof ResolvedRelativeModule) {
|
||||
const internalDependency = resolvedModule.modulePath;
|
||||
if (!alreadySeen.has(internalDependency)) {
|
||||
alreadySeen.add(internalDependency);
|
||||
this.recursivelyFindDependencies(
|
||||
internalDependency, dependencies, missing, deepImports, alreadySeen);
|
||||
}
|
||||
} else {
|
||||
if (resolvedModule instanceof ResolvedDeepImport) {
|
||||
deepImports.add(resolvedModule.importPath);
|
||||
} else {
|
||||
dependencies.add(resolvedModule.entryPointPath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
missing.add(importPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given statement is an import with a string literal module specifier.
|
||||
* @param stmt the statement node to check.
|
||||
* @returns true if the statement is an import with a string literal module specifier.
|
||||
*/
|
||||
isStringImportOrReexport(stmt: ts.Statement): stmt is ts.ImportDeclaration&
|
||||
{moduleSpecifier: ts.StringLiteral} {
|
||||
return ts.isImportDeclaration(stmt) ||
|
||||
ts.isExportDeclaration(stmt) && !!stmt.moduleSpecifier &&
|
||||
ts.isStringLiteral(stmt.moduleSpecifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a source file needs to be parsed for imports.
|
||||
* This is a performance short-circuit, which saves us from creating
|
||||
* a TypeScript AST unnecessarily.
|
||||
*
|
||||
* @param source The content of the source file to check.
|
||||
*
|
||||
* @returns false if there are definitely no import or re-export statements
|
||||
* in this file, true otherwise.
|
||||
*/
|
||||
hasImportOrReexportStatements(source: string): boolean {
|
||||
return /(import|export)\s.+from/.test(source);
|
||||
}
|
||||
}
|
280
packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts
Normal file
280
packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts
Normal file
@ -0,0 +1,280 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {PathMappings, isRelativePath} from '../utils';
|
||||
|
||||
/**
|
||||
* This is a very cut-down implementation of the TypeScript module resolution strategy.
|
||||
*
|
||||
* It is specific to the needs of ngcc and is not intended to be a drop-in replacement
|
||||
* for the TS module resolver. It is used to compute the dependencies between entry-points
|
||||
* that may be compiled by ngcc.
|
||||
*
|
||||
* The algorithm only finds `.js` files for internal/relative imports and paths to
|
||||
* the folder containing the `package.json` of the entry-point for external imports.
|
||||
*
|
||||
* It can cope with nested `node_modules` folders and also supports `paths`/`baseUrl`
|
||||
* configuration properties, as provided in a `ts.CompilerOptions` object.
|
||||
*/
|
||||
export class ModuleResolver {
|
||||
private pathMappings: ProcessedPathMapping[];
|
||||
|
||||
constructor(private fs: FileSystem, pathMappings?: PathMappings, private relativeExtensions = [
|
||||
'.js', '/index.js'
|
||||
]) {
|
||||
this.pathMappings = pathMappings ? this.processPathMappings(pathMappings) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an absolute path for the `moduleName` imported into a file at `fromPath`.
|
||||
* @param moduleName The name of the import to resolve.
|
||||
* @param fromPath The path to the file containing the import.
|
||||
* @returns A path to the resolved module or null if missing.
|
||||
* Specifically:
|
||||
* * the absolute path to the package.json of an external module
|
||||
* * a JavaScript file of an internal module
|
||||
* * null if none exists.
|
||||
*/
|
||||
resolveModuleImport(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null {
|
||||
if (isRelativePath(moduleName)) {
|
||||
return this.resolveAsRelativePath(moduleName, fromPath);
|
||||
} else {
|
||||
return this.pathMappings.length && this.resolveByPathMappings(moduleName, fromPath) ||
|
||||
this.resolveAsEntryPoint(moduleName, fromPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the `pathMappings` into a collection of `PathMapper` functions.
|
||||
*/
|
||||
private processPathMappings(pathMappings: PathMappings): ProcessedPathMapping[] {
|
||||
const baseUrl = AbsoluteFsPath.from(pathMappings.baseUrl);
|
||||
return Object.keys(pathMappings.paths).map(pathPattern => {
|
||||
const matcher = splitOnStar(pathPattern);
|
||||
const templates = pathMappings.paths[pathPattern].map(splitOnStar);
|
||||
return {matcher, templates, baseUrl};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve a module name, as a relative path, from the `fromPath`.
|
||||
*
|
||||
* As it is relative, it only looks for files that end in one of the `relativeExtensions`.
|
||||
* For example: `${moduleName}.js` or `${moduleName}/index.js`.
|
||||
* If neither of these files exist then the method returns `null`.
|
||||
*/
|
||||
private resolveAsRelativePath(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null {
|
||||
const resolvedPath = this.resolvePath(
|
||||
AbsoluteFsPath.resolve(AbsoluteFsPath.dirname(fromPath), moduleName),
|
||||
this.relativeExtensions);
|
||||
return resolvedPath && new ResolvedRelativeModule(resolvedPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve the `moduleName`, by applying the computed `pathMappings` and
|
||||
* then trying to resolve the mapped path as a relative or external import.
|
||||
*
|
||||
* Whether the mapped path is relative is defined as it being "below the `fromPath`" and not
|
||||
* containing `node_modules`.
|
||||
*
|
||||
* If the mapped path is not relative but does not resolve to an external entry-point, then we
|
||||
* check whether it would have resolved to a relative path, in which case it is marked as a
|
||||
* "deep-import".
|
||||
*/
|
||||
private resolveByPathMappings(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null {
|
||||
const mappedPaths = this.findMappedPaths(moduleName);
|
||||
if (mappedPaths.length > 0) {
|
||||
const packagePath = this.findPackagePath(fromPath);
|
||||
if (packagePath !== null) {
|
||||
for (const mappedPath of mappedPaths) {
|
||||
const isRelative =
|
||||
mappedPath.startsWith(packagePath) && !mappedPath.includes('node_modules');
|
||||
if (isRelative) {
|
||||
return this.resolveAsRelativePath(mappedPath, fromPath);
|
||||
} else if (this.isEntryPoint(mappedPath)) {
|
||||
return new ResolvedExternalModule(mappedPath);
|
||||
} else if (this.resolveAsRelativePath(mappedPath, fromPath)) {
|
||||
return new ResolvedDeepImport(mappedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve the `moduleName` as an external entry-point by searching the `node_modules`
|
||||
* folders up the tree for a matching `.../node_modules/${moduleName}`.
|
||||
*
|
||||
* If a folder is found but the path does not contain a `package.json` then it is marked as a
|
||||
* "deep-import".
|
||||
*/
|
||||
private resolveAsEntryPoint(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null {
|
||||
let folder = fromPath;
|
||||
while (!AbsoluteFsPath.isRoot(folder)) {
|
||||
folder = AbsoluteFsPath.dirname(folder);
|
||||
if (folder.endsWith('node_modules')) {
|
||||
// Skip up if the folder already ends in node_modules
|
||||
folder = AbsoluteFsPath.dirname(folder);
|
||||
}
|
||||
const modulePath = AbsoluteFsPath.resolve(folder, 'node_modules', moduleName);
|
||||
if (this.isEntryPoint(modulePath)) {
|
||||
return new ResolvedExternalModule(modulePath);
|
||||
} else if (this.resolveAsRelativePath(modulePath, fromPath)) {
|
||||
return new ResolvedDeepImport(modulePath);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to resolve a `path` to a file by appending the provided `postFixes`
|
||||
* to the `path` and checking if the file exists on disk.
|
||||
* @returns An absolute path to the first matching existing file, or `null` if none exist.
|
||||
*/
|
||||
private resolvePath(path: AbsoluteFsPath, postFixes: string[]): AbsoluteFsPath|null {
|
||||
for (const postFix of postFixes) {
|
||||
const testPath = AbsoluteFsPath.fromUnchecked(path + postFix);
|
||||
if (this.fs.exists(testPath)) {
|
||||
return testPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we consider the given path as an entry-point to a package?
|
||||
*
|
||||
* This is achieved by checking for the existence of `${modulePath}/package.json`.
|
||||
*/
|
||||
private isEntryPoint(modulePath: AbsoluteFsPath): boolean {
|
||||
return this.fs.exists(AbsoluteFsPath.join(modulePath, 'package.json'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the `pathMappers` to the `moduleName` and return all the possible
|
||||
* paths that match.
|
||||
*
|
||||
* The mapped path is computed for each template in `mapping.templates` by
|
||||
* replacing the `matcher.prefix` and `matcher.postfix` strings in `path with the
|
||||
* `template.prefix` and `template.postfix` strings.
|
||||
*/
|
||||
private findMappedPaths(moduleName: string): AbsoluteFsPath[] {
|
||||
const matches = this.pathMappings.map(mapping => this.matchMapping(moduleName, mapping));
|
||||
|
||||
let bestMapping: ProcessedPathMapping|undefined;
|
||||
let bestMatch: string|undefined;
|
||||
|
||||
for (let index = 0; index < this.pathMappings.length; index++) {
|
||||
const mapping = this.pathMappings[index];
|
||||
const match = matches[index];
|
||||
if (match !== null) {
|
||||
// If this mapping had no wildcard then this must be a complete match.
|
||||
if (!mapping.matcher.hasWildcard) {
|
||||
bestMatch = match;
|
||||
bestMapping = mapping;
|
||||
break;
|
||||
}
|
||||
// The best matched mapping is the one with the longest prefix.
|
||||
if (!bestMapping || mapping.matcher.prefix > bestMapping.matcher.prefix) {
|
||||
bestMatch = match;
|
||||
bestMapping = mapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (bestMapping && bestMatch) ? this.computeMappedTemplates(bestMapping, bestMatch) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find a mapped path for the given `path` and a `mapping`.
|
||||
*
|
||||
* The `path` matches the `mapping` if if it starts with `matcher.prefix` and ends with
|
||||
* `matcher.postfix`.
|
||||
*
|
||||
* @returns the wildcard segment of a matched `path`, or `null` if no match.
|
||||
*/
|
||||
private matchMapping(path: string, mapping: ProcessedPathMapping): string|null {
|
||||
const {prefix, postfix, hasWildcard} = mapping.matcher;
|
||||
if (path.startsWith(prefix) && path.endsWith(postfix)) {
|
||||
return hasWildcard ? path.substring(prefix.length, path.length - postfix.length) : '';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the candidate paths from the given mapping's templates using the matched
|
||||
* string.
|
||||
*/
|
||||
private computeMappedTemplates(mapping: ProcessedPathMapping, match: string) {
|
||||
return mapping.templates.map(
|
||||
template =>
|
||||
AbsoluteFsPath.resolve(mapping.baseUrl, template.prefix + match + template.postfix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search up the folder tree for the first folder that contains `package.json`
|
||||
* or `null` if none is found.
|
||||
*/
|
||||
private findPackagePath(path: AbsoluteFsPath): AbsoluteFsPath|null {
|
||||
let folder = path;
|
||||
while (!AbsoluteFsPath.isRoot(folder)) {
|
||||
folder = AbsoluteFsPath.dirname(folder);
|
||||
if (this.fs.exists(AbsoluteFsPath.join(folder, 'package.json'))) {
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The result of resolving an import to a module. */
|
||||
export type ResolvedModule = ResolvedExternalModule | ResolvedRelativeModule | ResolvedDeepImport;
|
||||
|
||||
/**
|
||||
* A module that is external to the package doing the importing.
|
||||
* In this case we capture the folder containing the entry-point.
|
||||
*/
|
||||
export class ResolvedExternalModule {
|
||||
constructor(public entryPointPath: AbsoluteFsPath) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module that is relative to the module doing the importing, and so internal to the
|
||||
* source module's package.
|
||||
*/
|
||||
export class ResolvedRelativeModule {
|
||||
constructor(public modulePath: AbsoluteFsPath) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module that is external to the package doing the importing but pointing to a
|
||||
* module that is deep inside a package, rather than to an entry-point of the package.
|
||||
*/
|
||||
export class ResolvedDeepImport {
|
||||
constructor(public importPath: AbsoluteFsPath) {}
|
||||
}
|
||||
|
||||
function splitOnStar(str: string): PathMappingPattern {
|
||||
const [prefix, postfix] = str.split('*', 2);
|
||||
return {prefix, postfix: postfix || '', hasWildcard: postfix !== undefined};
|
||||
}
|
||||
|
||||
interface ProcessedPathMapping {
|
||||
baseUrl: AbsoluteFsPath;
|
||||
matcher: PathMappingPattern;
|
||||
templates: PathMappingPattern[];
|
||||
}
|
||||
|
||||
interface PathMappingPattern {
|
||||
prefix: string;
|
||||
postfix: string;
|
||||
hasWildcard: boolean;
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {getImportsOfUmdModule, parseStatementForUmdModule} from '../host/umd_host';
|
||||
|
||||
import {DependencyHost, DependencyInfo} from './dependency_host';
|
||||
import {ModuleResolver, ResolvedDeepImport, ResolvedRelativeModule} from './module_resolver';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper functions for computing dependencies.
|
||||
*/
|
||||
export class UmdDependencyHost implements DependencyHost {
|
||||
constructor(private fs: FileSystem, private moduleResolver: ModuleResolver) {}
|
||||
|
||||
/**
|
||||
* Find all the dependencies for the entry-point at the given path.
|
||||
*
|
||||
* @param entryPointPath The absolute path to the JavaScript file that represents an entry-point.
|
||||
* @returns Information about the dependencies of the entry-point, including those that were
|
||||
* missing or deep imports into other entry-points.
|
||||
*/
|
||||
findDependencies(entryPointPath: AbsoluteFsPath): DependencyInfo {
|
||||
const dependencies = new Set<AbsoluteFsPath>();
|
||||
const missing = new Set<AbsoluteFsPath|PathSegment>();
|
||||
const deepImports = new Set<AbsoluteFsPath>();
|
||||
const alreadySeen = new Set<AbsoluteFsPath>();
|
||||
this.recursivelyFindDependencies(
|
||||
entryPointPath, dependencies, missing, deepImports, alreadySeen);
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the dependencies of the given file.
|
||||
*
|
||||
* @param file An absolute path to the file whose dependencies we want to get.
|
||||
* @param dependencies A set that will have the absolute paths of resolved entry points added to
|
||||
* it.
|
||||
* @param missing A set that will have the dependencies that could not be found added to it.
|
||||
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
|
||||
* entry-points, i.e. deep-imports.
|
||||
* @param alreadySeen A set that is used to track internal dependencies to prevent getting stuck
|
||||
* in a
|
||||
* circular dependency loop.
|
||||
*/
|
||||
private recursivelyFindDependencies(
|
||||
file: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath>, missing: Set<string>,
|
||||
deepImports: Set<string>, alreadySeen: Set<AbsoluteFsPath>): void {
|
||||
const fromContents = this.fs.readFile(file);
|
||||
if (!this.hasRequireCalls(fromContents)) {
|
||||
// Avoid parsing the source file as there are no require calls.
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
|
||||
const sf =
|
||||
ts.createSourceFile(file, fromContents, ts.ScriptTarget.ES2015, false, ts.ScriptKind.JS);
|
||||
if (sf.statements.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const umdModule = parseStatementForUmdModule(sf.statements[0]);
|
||||
const umdImports = umdModule && getImportsOfUmdModule(umdModule);
|
||||
if (umdImports === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
umdImports.forEach(umdImport => {
|
||||
const resolvedModule = this.moduleResolver.resolveModuleImport(umdImport.path, file);
|
||||
if (resolvedModule) {
|
||||
if (resolvedModule instanceof ResolvedRelativeModule) {
|
||||
const internalDependency = resolvedModule.modulePath;
|
||||
if (!alreadySeen.has(internalDependency)) {
|
||||
alreadySeen.add(internalDependency);
|
||||
this.recursivelyFindDependencies(
|
||||
internalDependency, dependencies, missing, deepImports, alreadySeen);
|
||||
}
|
||||
} else {
|
||||
if (resolvedModule instanceof ResolvedDeepImport) {
|
||||
deepImports.add(resolvedModule.importPath);
|
||||
} else {
|
||||
dependencies.add(resolvedModule.entryPointPath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
missing.add(umdImport.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a source file needs to be parsed for imports.
|
||||
* This is a performance short-circuit, which saves us from creating
|
||||
* a TypeScript AST unnecessarily.
|
||||
*
|
||||
* @param source The content of the source file to check.
|
||||
*
|
||||
* @returns false if there are definitely no require calls
|
||||
* in this file, true otherwise.
|
||||
*/
|
||||
hasRequireCalls(source: string): boolean { return /require\(['"]/.test(source); }
|
||||
}
|
38
packages/compiler-cli/ngcc/src/file_system/file_system.ts
Normal file
38
packages/compiler-cli/ngcc/src/file_system/file_system.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
|
||||
/**
|
||||
* A basic interface to abstract the underlying file-system.
|
||||
*
|
||||
* This makes it easier to provide mock file-systems in unit tests,
|
||||
* but also to create clever file-systems that have features such as caching.
|
||||
*/
|
||||
export interface FileSystem {
|
||||
exists(path: AbsoluteFsPath): boolean;
|
||||
readFile(path: AbsoluteFsPath): string;
|
||||
writeFile(path: AbsoluteFsPath, data: string): void;
|
||||
readdir(path: AbsoluteFsPath): PathSegment[];
|
||||
lstat(path: AbsoluteFsPath): FileStats;
|
||||
stat(path: AbsoluteFsPath): FileStats;
|
||||
pwd(): AbsoluteFsPath;
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||
ensureDir(path: AbsoluteFsPath): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about an object in the FileSystem.
|
||||
* This is analogous to the `fs.Stats` class in Node.js.
|
||||
*/
|
||||
export interface FileStats {
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import {cp, mkdir, mv} from 'shelljs';
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from './file_system';
|
||||
|
||||
/**
|
||||
* A wrapper around the Node.js file-system (i.e the `fs` package).
|
||||
*/
|
||||
export class NodeJSFileSystem implements FileSystem {
|
||||
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); }
|
||||
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); }
|
||||
writeFile(path: AbsoluteFsPath, data: string): void {
|
||||
return fs.writeFileSync(path, data, 'utf8');
|
||||
}
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; }
|
||||
lstat(path: AbsoluteFsPath): fs.Stats { return fs.lstatSync(path); }
|
||||
stat(path: AbsoluteFsPath): fs.Stats { return fs.statSync(path); }
|
||||
pwd() { return AbsoluteFsPath.from(process.cwd()); }
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { cp(from, to); }
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { mv(from, to); }
|
||||
ensureDir(path: AbsoluteFsPath): void { mkdir('-p', path); }
|
||||
}
|
186
packages/compiler-cli/ngcc/src/host/commonjs_host.ts
Normal file
186
packages/compiler-cli/ngcc/src/host/commonjs_host.ts
Normal file
@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Declaration, Import} from '../../../src/ngtsc/reflection';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {Esm5ReflectionHost} from './esm5_host';
|
||||
|
||||
export class CommonJsReflectionHost extends Esm5ReflectionHost {
|
||||
protected commonJsExports = new Map<ts.SourceFile, Map<string, Declaration>|null>();
|
||||
constructor(
|
||||
logger: Logger, isCore: boolean, protected program: ts.Program,
|
||||
protected compilerHost: ts.CompilerHost, dts?: BundleProgram|null) {
|
||||
super(logger, isCore, program.getTypeChecker(), dts);
|
||||
}
|
||||
|
||||
getImportOfIdentifier(id: ts.Identifier): Import|null {
|
||||
const requireCall = this.findCommonJsImport(id);
|
||||
if (requireCall === null) {
|
||||
return null;
|
||||
}
|
||||
return {from: requireCall.arguments[0].text, name: id.text};
|
||||
}
|
||||
|
||||
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
||||
return this.getCommonJsImportedDeclaration(id) || super.getDeclarationOfIdentifier(id);
|
||||
}
|
||||
|
||||
getExportsOfModule(module: ts.Node): Map<string, Declaration>|null {
|
||||
return super.getExportsOfModule(module) || this.getCommonJsExports(module.getSourceFile());
|
||||
}
|
||||
|
||||
getCommonJsExports(sourceFile: ts.SourceFile): Map<string, Declaration>|null {
|
||||
if (!this.commonJsExports.has(sourceFile)) {
|
||||
const moduleExports = this.computeExportsOfCommonJsModule(sourceFile);
|
||||
this.commonJsExports.set(sourceFile, moduleExports);
|
||||
}
|
||||
return this.commonJsExports.get(sourceFile) !;
|
||||
}
|
||||
|
||||
private computeExportsOfCommonJsModule(sourceFile: ts.SourceFile): Map<string, Declaration> {
|
||||
const moduleMap = new Map<string, Declaration>();
|
||||
for (const statement of this.getModuleStatements(sourceFile)) {
|
||||
if (isCommonJsExportStatement(statement)) {
|
||||
const exportDeclaration = this.extractCommonJsExportDeclaration(statement);
|
||||
if (exportDeclaration !== null) {
|
||||
moduleMap.set(exportDeclaration.name, exportDeclaration.declaration);
|
||||
}
|
||||
} else if (isReexportStatement(statement)) {
|
||||
const reexports = this.extractCommonJsReexports(statement, sourceFile);
|
||||
for (const reexport of reexports) {
|
||||
moduleMap.set(reexport.name, reexport.declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleMap;
|
||||
}
|
||||
|
||||
private extractCommonJsExportDeclaration(statement: CommonJsExportStatement):
|
||||
CommonJsExportDeclaration|null {
|
||||
const exportExpression = statement.expression.right;
|
||||
const declaration = this.getDeclarationOfExpression(exportExpression);
|
||||
if (declaration === null) {
|
||||
return null;
|
||||
}
|
||||
const name = statement.expression.left.name.text;
|
||||
return {name, declaration};
|
||||
}
|
||||
|
||||
private extractCommonJsReexports(statement: ReexportStatement, containingFile: ts.SourceFile):
|
||||
CommonJsExportDeclaration[] {
|
||||
const reexports: CommonJsExportDeclaration[] = [];
|
||||
const requireCall = statement.expression.arguments[0];
|
||||
const importPath = requireCall.arguments[0].text;
|
||||
const importedFile = this.resolveModuleName(importPath, containingFile);
|
||||
if (importedFile !== undefined) {
|
||||
const viaModule = stripExtension(importedFile.fileName);
|
||||
const importedExports = this.getExportsOfModule(importedFile);
|
||||
if (importedExports !== null) {
|
||||
importedExports.forEach(
|
||||
(decl, name) => reexports.push({name, declaration: {node: decl.node, viaModule}}));
|
||||
}
|
||||
}
|
||||
return reexports;
|
||||
}
|
||||
|
||||
private findCommonJsImport(id: ts.Identifier): RequireCall|null {
|
||||
// Is `id` a namespaced property access, e.g. `Directive` in `core.Directive`?
|
||||
// If so capture the symbol of the namespace, e.g. `core`.
|
||||
const nsIdentifier = findNamespaceOfIdentifier(id);
|
||||
const nsSymbol = nsIdentifier && this.checker.getSymbolAtLocation(nsIdentifier) || null;
|
||||
const nsDeclaration = nsSymbol && nsSymbol.valueDeclaration;
|
||||
const initializer =
|
||||
nsDeclaration && ts.isVariableDeclaration(nsDeclaration) && nsDeclaration.initializer ||
|
||||
null;
|
||||
return initializer && isRequireCall(initializer) ? initializer : null;
|
||||
}
|
||||
|
||||
private getCommonJsImportedDeclaration(id: ts.Identifier): Declaration|null {
|
||||
const importInfo = this.getImportOfIdentifier(id);
|
||||
if (importInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const importedFile = this.resolveModuleName(importInfo.from, id.getSourceFile());
|
||||
if (importedFile === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {node: importedFile, viaModule: importInfo.from};
|
||||
}
|
||||
|
||||
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|
||||
|undefined {
|
||||
if (this.compilerHost.resolveModuleNames) {
|
||||
const moduleInfo =
|
||||
this.compilerHost.resolveModuleNames([moduleName], containingFile.fileName)[0];
|
||||
return moduleInfo && this.program.getSourceFile(moduleInfo.resolvedFileName);
|
||||
} else {
|
||||
const moduleInfo = ts.resolveModuleName(
|
||||
moduleName, containingFile.fileName, this.program.getCompilerOptions(),
|
||||
this.compilerHost);
|
||||
return moduleInfo.resolvedModule &&
|
||||
this.program.getSourceFile(moduleInfo.resolvedModule.resolvedFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CommonJsExportStatement = ts.ExpressionStatement & {
|
||||
expression:
|
||||
ts.BinaryExpression & {left: ts.PropertyAccessExpression & {expression: ts.Identifier}}
|
||||
};
|
||||
export function isCommonJsExportStatement(s: ts.Statement): s is CommonJsExportStatement {
|
||||
return ts.isExpressionStatement(s) && ts.isBinaryExpression(s.expression) &&
|
||||
ts.isPropertyAccessExpression(s.expression.left) &&
|
||||
ts.isIdentifier(s.expression.left.expression) &&
|
||||
s.expression.left.expression.text === 'exports';
|
||||
}
|
||||
|
||||
interface CommonJsExportDeclaration {
|
||||
name: string;
|
||||
declaration: Declaration;
|
||||
}
|
||||
|
||||
export type RequireCall = ts.CallExpression & {arguments: [ts.StringLiteral]};
|
||||
export function isRequireCall(node: ts.Node): node is RequireCall {
|
||||
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
|
||||
node.expression.text === 'require' && node.arguments.length === 1 &&
|
||||
ts.isStringLiteral(node.arguments[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the identifier `id` is the RHS of a property access of the form `namespace.id`
|
||||
* and `namespace` is an identifer then return `namespace`, otherwise `null`.
|
||||
* @param id The identifier whose namespace we want to find.
|
||||
*/
|
||||
function findNamespaceOfIdentifier(id: ts.Identifier): ts.Identifier|null {
|
||||
return id.parent && ts.isPropertyAccessExpression(id.parent) &&
|
||||
ts.isIdentifier(id.parent.expression) ?
|
||||
id.parent.expression :
|
||||
null;
|
||||
}
|
||||
|
||||
export function stripParentheses(node: ts.Node): ts.Node {
|
||||
return ts.isParenthesizedExpression(node) ? node.expression : node;
|
||||
}
|
||||
|
||||
type ReexportStatement = ts.ExpressionStatement & {expression: {arguments: [RequireCall]}};
|
||||
function isReexportStatement(statement: ts.Statement): statement is ReexportStatement {
|
||||
return ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression) &&
|
||||
ts.isIdentifier(statement.expression.expression) &&
|
||||
statement.expression.expression.text === '__export' &&
|
||||
statement.expression.arguments.length === 1 &&
|
||||
isRequireCall(statement.expression.arguments[0]);
|
||||
}
|
||||
|
||||
function stripExtension(fileName: string): string {
|
||||
return fileName.replace(/\..+$/, '');
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
|
||||
|
||||
/**
|
||||
* A simple container that holds the details of a decorated class that has been
|
||||
* found in a `DecoratedFile`.
|
||||
*/
|
||||
export class DecoratedClass {
|
||||
/**
|
||||
* Initialize a `DecoratedClass` that was found in a `DecoratedFile`.
|
||||
* @param name The name of the class that has been found. This is mostly used
|
||||
* for informational purposes.
|
||||
* @param declaration The TypeScript AST node where this class is declared. In ES5 code, where a
|
||||
* class can be represented by both a variable declaration and a function declaration (inside an
|
||||
* IIFE), `declaration` will always refer to the outer variable declaration, which represents the
|
||||
* class to the rest of the program.
|
||||
* @param decorators The collection of decorators that have been found on this class.
|
||||
*/
|
||||
constructor(
|
||||
public name: string, public declaration: ClassDeclaration, public decorators: Decorator[]) {}
|
||||
}
|
@ -13,7 +13,6 @@ import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {findAll, getNameText, hasNameIdentifier, isDefined} from '../utils';
|
||||
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||
|
||||
export const DECORATORS = 'decorators' as ts.__String;
|
||||
@ -233,6 +232,16 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return superDeclaration;
|
||||
}
|
||||
|
||||
/** Gets all decorators of the given class symbol. */
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
} else {
|
||||
return this.getClassDecoratorsFromHelperCall(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given module for variable declarations in which the initializer
|
||||
* is an identifier marked with the `PRE_R3_MARKER`.
|
||||
@ -306,37 +315,24 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an identifier was imported from another module and return `Import` metadata
|
||||
* describing its origin.
|
||||
*
|
||||
* @param id a TypeScript `ts.Identifer` to reflect.
|
||||
*
|
||||
* @returns metadata about the `Import` if the identifier was imported from another module, or
|
||||
* `null` if the identifier doesn't resolve to an import but instead is locally defined.
|
||||
* Find all top-level class symbols in the given file.
|
||||
* @param sourceFile The source file to search for classes.
|
||||
* @returns An array of class symbols.
|
||||
*/
|
||||
getImportOfIdentifier(id: ts.Identifier): Import|null {
|
||||
return super.getImportOfIdentifier(id) || this.getImportOfNamespacedIdentifier(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the classes that contain decorations in a given file.
|
||||
* @param sourceFile The source file to search for decorated classes.
|
||||
* @returns An array of decorated classes.
|
||||
*/
|
||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[] {
|
||||
const classes: DecoratedClass[] = [];
|
||||
sourceFile.statements.map(statement => {
|
||||
findClassSymbols(sourceFile: ts.SourceFile): ClassSymbol[] {
|
||||
const classes: ClassSymbol[] = [];
|
||||
this.getModuleStatements(sourceFile).forEach(statement => {
|
||||
if (ts.isVariableStatement(statement)) {
|
||||
statement.declarationList.declarations.forEach(declaration => {
|
||||
const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(declaration));
|
||||
if (decoratedClass) {
|
||||
classes.push(decoratedClass);
|
||||
const classSymbol = this.getClassSymbol(declaration);
|
||||
if (classSymbol) {
|
||||
classes.push(classSymbol);
|
||||
}
|
||||
});
|
||||
} else if (ts.isClassDeclaration(statement)) {
|
||||
const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(statement));
|
||||
if (decoratedClass) {
|
||||
classes.push(decoratedClass);
|
||||
const classSymbol = this.getClassSymbol(statement);
|
||||
if (classSymbol) {
|
||||
classes.push(classSymbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -379,7 +375,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
throw new Error(
|
||||
`Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
return this.dtsDeclarationMap.get(declaration.name.text) || null;
|
||||
return this.dtsDeclarationMap.has(declaration.name.text) ?
|
||||
this.dtsDeclarationMap.get(declaration.name.text) ! :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -432,7 +430,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
*/
|
||||
protected resolveAliasedClassIdentifier(declaration: ts.Declaration): ts.Identifier|null {
|
||||
this.ensurePreprocessed(declaration.getSourceFile());
|
||||
return this.aliasedClassDeclarations.get(declaration) || null;
|
||||
return this.aliasedClassDeclarations.has(declaration) ?
|
||||
this.aliasedClassDeclarations.get(declaration) ! :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -487,23 +487,14 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
this.aliasedClassDeclarations.set(aliasedDeclaration.node, declaration.name);
|
||||
}
|
||||
|
||||
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
} else {
|
||||
return this.getClassDecoratorsFromHelperCall(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected getDecoratedClassFromSymbol(symbol: ClassSymbol|undefined): DecoratedClass|null {
|
||||
if (symbol) {
|
||||
const decorators = this.getDecoratorsOfSymbol(symbol);
|
||||
if (decorators && decorators.length) {
|
||||
return new DecoratedClass(symbol.name, symbol.valueDeclaration, decorators);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/** Get the top level statements for a module.
|
||||
*
|
||||
* In ES5 and ES2015 this is just the top level statements of the file.
|
||||
* @param sourceFile The module whose statements we want.
|
||||
* @returns An array of top level statements for the given module.
|
||||
*/
|
||||
protected getModuleStatements(sourceFile: ts.SourceFile): ts.Statement[] {
|
||||
return Array.from(sourceFile.statements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -741,7 +732,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
helperCall, makeMemberTargetFilter(classSymbol.name));
|
||||
memberDecorators.forEach((decorators, memberName) => {
|
||||
if (memberName) {
|
||||
const memberDecorators = memberDecoratorMap.get(memberName) || [];
|
||||
const memberDecorators =
|
||||
memberDecoratorMap.has(memberName) ? memberDecoratorMap.get(memberName) ! : [];
|
||||
const coreDecorators = decorators.filter(decorator => this.isFromCore(decorator));
|
||||
memberDecoratorMap.set(memberName, memberDecorators.concat(coreDecorators));
|
||||
}
|
||||
@ -778,7 +770,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
if (keyName === undefined) {
|
||||
classDecorators.push(decorator);
|
||||
} else {
|
||||
const decorators = memberDecorators.get(keyName) || [];
|
||||
const decorators =
|
||||
memberDecorators.has(keyName) ? memberDecorators.get(keyName) ! : [];
|
||||
decorators.push(decorator);
|
||||
memberDecorators.set(keyName, decorators);
|
||||
}
|
||||
@ -877,14 +870,20 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
const decorator = reflectObjectLiteral(node);
|
||||
|
||||
// Is the value of the `type` property an identifier?
|
||||
const typeIdentifier = decorator.get('type');
|
||||
if (typeIdentifier && ts.isIdentifier(typeIdentifier)) {
|
||||
decorators.push({
|
||||
name: typeIdentifier.text,
|
||||
identifier: typeIdentifier,
|
||||
import: this.getImportOfIdentifier(typeIdentifier), node,
|
||||
args: getDecoratorArgs(node),
|
||||
});
|
||||
if (decorator.has('type')) {
|
||||
let typeIdentifier = decorator.get('type') !;
|
||||
if (ts.isPropertyAccessExpression(typeIdentifier)) {
|
||||
// the type is in a namespace, e.g. `core.Directive`
|
||||
typeIdentifier = typeIdentifier.name;
|
||||
}
|
||||
if (ts.isIdentifier(typeIdentifier)) {
|
||||
decorators.push({
|
||||
name: typeIdentifier.text,
|
||||
identifier: typeIdentifier,
|
||||
import: this.getImportOfIdentifier(typeIdentifier), node,
|
||||
args: getDecoratorArgs(node),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1033,8 +1032,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
*/
|
||||
protected getConstructorParameterDeclarations(classSymbol: ClassSymbol):
|
||||
ts.ParameterDeclaration[]|null {
|
||||
const constructorSymbol = classSymbol.members && classSymbol.members.get(CONSTRUCTOR);
|
||||
if (constructorSymbol) {
|
||||
if (classSymbol.members && classSymbol.members.has(CONSTRUCTOR)) {
|
||||
const constructorSymbol = classSymbol.members.get(CONSTRUCTOR) !;
|
||||
// For some reason the constructor does not have a `valueDeclaration` ?!?
|
||||
const constructor = constructorSymbol.declarations &&
|
||||
constructorSymbol.declarations[0] as ts.ConstructorDeclaration | undefined;
|
||||
@ -1110,8 +1109,10 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
element =>
|
||||
ts.isObjectLiteralExpression(element) ? reflectObjectLiteral(element) : null)
|
||||
.map(paramInfo => {
|
||||
const typeExpression = paramInfo && paramInfo.get('type') || null;
|
||||
const decoratorInfo = paramInfo && paramInfo.get('decorators') || null;
|
||||
const typeExpression =
|
||||
paramInfo && paramInfo.has('type') ? paramInfo.get('type') ! : null;
|
||||
const decoratorInfo =
|
||||
paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators') ! : null;
|
||||
const decorators = decoratorInfo &&
|
||||
this.reflectDecorators(decoratorInfo)
|
||||
.filter(decorator => this.isFromCore(decorator));
|
||||
@ -1318,7 +1319,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return null;
|
||||
}
|
||||
const declaration = implementation;
|
||||
const body = this.getDefinitionOfFunction(declaration).body;
|
||||
const definition = this.getDefinitionOfFunction(declaration);
|
||||
if (definition === null) {
|
||||
return null;
|
||||
}
|
||||
const body = definition.body;
|
||||
const lastStatement = body && body[body.length - 1];
|
||||
const returnExpression =
|
||||
lastStatement && ts.isReturnStatement(lastStatement) && lastStatement.expression || null;
|
||||
@ -1327,27 +1332,56 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
prop =>
|
||||
!!prop.name && ts.isIdentifier(prop.name) && prop.name.text === 'ngModule') ||
|
||||
null;
|
||||
const ngModuleIdentifier = ngModuleProperty && ts.isPropertyAssignment(ngModuleProperty) &&
|
||||
ts.isIdentifier(ngModuleProperty.initializer) && ngModuleProperty.initializer ||
|
||||
null;
|
||||
|
||||
// If no `ngModule` property was found in an object literal return value, return `null` to
|
||||
// indicate that the provided node does not appear to be a `ModuleWithProviders` function.
|
||||
if (ngModuleIdentifier === null) {
|
||||
if (!ngModuleProperty || !ts.isPropertyAssignment(ngModuleProperty)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ngModuleDeclaration = this.getDeclarationOfIdentifier(ngModuleIdentifier);
|
||||
// The ngModuleValue could be of the form `SomeModule` or `namespace_1.SomeModule`
|
||||
const ngModuleValue = ngModuleProperty.initializer;
|
||||
if (!ts.isIdentifier(ngModuleValue) && !ts.isPropertyAccessExpression(ngModuleValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ngModuleDeclaration = this.getDeclarationOfExpression(ngModuleValue);
|
||||
if (!ngModuleDeclaration) {
|
||||
throw new Error(
|
||||
`Cannot find a declaration for NgModule ${ngModuleIdentifier.text} referenced in "${declaration!.getText()}"`);
|
||||
`Cannot find a declaration for NgModule ${ngModuleValue.getText()} referenced in "${declaration!.getText()}"`);
|
||||
}
|
||||
if (!hasNameIdentifier(ngModuleDeclaration.node)) {
|
||||
return null;
|
||||
}
|
||||
const ngModule = ngModuleDeclaration as Declaration<ClassDeclaration>;
|
||||
return {
|
||||
name,
|
||||
ngModule: ngModuleDeclaration as Declaration<ClassDeclaration>, declaration, container
|
||||
};
|
||||
}
|
||||
|
||||
return {name, ngModule, declaration, container};
|
||||
protected getDeclarationOfExpression(expression: ts.Expression): Declaration|null {
|
||||
if (ts.isIdentifier(expression)) {
|
||||
return this.getDeclarationOfIdentifier(expression);
|
||||
}
|
||||
|
||||
if (!ts.isPropertyAccessExpression(expression) || !ts.isIdentifier(expression.expression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const namespaceDecl = this.getDeclarationOfIdentifier(expression.expression);
|
||||
if (!namespaceDecl || !ts.isSourceFile(namespaceDecl.node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const namespaceExports = this.getExportsOfModule(namespaceDecl.node);
|
||||
if (namespaceExports === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!namespaceExports.has(expression.name.text)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exportDecl = namespaceExports.get(expression.name.text) !;
|
||||
return {...exportDecl, viaModule: namespaceDecl.viaModule};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Declaration, Decorator, FunctionDefinition, Parameter, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Declaration, Decorator, FunctionDefinition, Parameter, TsHelperFn, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {isFromDtsFile} from '../../../src/ngtsc/util/src/typescript';
|
||||
import {getNameText, hasNameIdentifier} from '../utils';
|
||||
|
||||
@ -148,8 +148,28 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
* @param node the function declaration to parse.
|
||||
* @returns an object containing the node, statements and parameters of the function.
|
||||
*/
|
||||
getDefinitionOfFunction<T extends ts.FunctionDeclaration|ts.MethodDeclaration|
|
||||
ts.FunctionExpression>(node: T): FunctionDefinition<T> {
|
||||
getDefinitionOfFunction(node: ts.Node): FunctionDefinition|null {
|
||||
if (!ts.isFunctionDeclaration(node) && !ts.isMethodDeclaration(node) &&
|
||||
!ts.isFunctionExpression(node) && !ts.isVariableDeclaration(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tsHelperFn = getTsHelperFn(node);
|
||||
if (tsHelperFn !== null) {
|
||||
return {
|
||||
node,
|
||||
body: null,
|
||||
helper: tsHelperFn,
|
||||
parameters: [],
|
||||
};
|
||||
}
|
||||
|
||||
// If the node was not identified to be a TypeScript helper, a variable declaration at this
|
||||
// point cannot be resolved as a function.
|
||||
if (ts.isVariableDeclaration(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parameters =
|
||||
node.parameters.map(p => ({name: getNameText(p.name), node: p, initializer: null}));
|
||||
let lookingForParamInitializers = true;
|
||||
@ -161,7 +181,7 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return !lookingForParamInitializers;
|
||||
});
|
||||
|
||||
return {node, body: statements || null, parameters};
|
||||
return {node, body: statements || null, helper: null, parameters};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,6 +211,16 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return this.getMembersOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
/** Gets all decorators of the given class symbol. */
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol =
|
||||
this.getInnerFunctionSymbolFromClassDeclaration(symbol.valueDeclaration);
|
||||
if (!innerFunctionSymbol) return null;
|
||||
|
||||
return super.getDecoratorsOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
@ -301,15 +331,6 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return super.getConstructorParamInfo(innerFunctionSymbol, parameterNodes);
|
||||
}
|
||||
|
||||
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol =
|
||||
this.getInnerFunctionSymbolFromClassDeclaration(symbol.valueDeclaration);
|
||||
if (!innerFunctionSymbol) return null;
|
||||
|
||||
return super.getDecoratorsOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type and decorators for the constructor of a class,
|
||||
* where the information is stored on a static method of the class.
|
||||
@ -339,8 +360,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
if (expression && ts.isArrayLiteralExpression(expression)) {
|
||||
const elements = expression.elements;
|
||||
return elements.map(reflectArrayElement).map(paramInfo => {
|
||||
const typeExpression = paramInfo && paramInfo.get('type') || null;
|
||||
const decoratorInfo = paramInfo && paramInfo.get('decorators') || null;
|
||||
const typeExpression = paramInfo && paramInfo.has('type') ? paramInfo.get('type') ! : null;
|
||||
const decoratorInfo =
|
||||
paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators') ! : null;
|
||||
const decorators = decoratorInfo && this.reflectDecorators(decoratorInfo);
|
||||
return {typeExpression, decorators};
|
||||
});
|
||||
@ -564,6 +586,21 @@ function reflectArrayElement(element: ts.Expression) {
|
||||
return ts.isObjectLiteralExpression(element) ? reflectObjectLiteral(element) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a function declaration to determine if it corresponds with a TypeScript helper function,
|
||||
* returning its kind if so or null if the declaration does not seem to correspond with such a
|
||||
* helper.
|
||||
*/
|
||||
function getTsHelperFn(node: ts.NamedDeclaration): TsHelperFn|null {
|
||||
const name = node.name !== undefined && ts.isIdentifier(node.name) && node.name.text;
|
||||
|
||||
if (name === '__spread') {
|
||||
return TsHelperFn.Spread;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor function may have been "synthesized" by TypeScript during JavaScript emit,
|
||||
* in the case no user-defined constructor exists and e.g. property initializers are used.
|
||||
|
@ -6,8 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {ClassDeclaration, ClassSymbol, Declaration, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ClassDeclaration, ClassSymbol, Declaration, Decorator, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
|
||||
export const PRE_R3_MARKER = '__PRE_R3__';
|
||||
export const POST_R3_MARKER = '__POST_R3__';
|
||||
@ -64,11 +63,18 @@ export interface NgccReflectionHost extends ReflectionHost {
|
||||
getSwitchableDeclarations(module: ts.Node): SwitchableVariableDeclaration[];
|
||||
|
||||
/**
|
||||
* Find all the classes that contain decorations in a given file.
|
||||
* @param sourceFile The source file to search for decorated classes.
|
||||
* @returns An array of decorated classes.
|
||||
* Retrieves all decorators of a given class symbol.
|
||||
* @param symbol Class symbol that can refer to a declaration which can hold decorators.
|
||||
* @returns An array of decorators or null if none are declared.
|
||||
*/
|
||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null;
|
||||
|
||||
/**
|
||||
* Retrieves all class symbols of a given source file.
|
||||
* @param sourceFile The source file to search for classes.
|
||||
* @returns An array of found class symbols.
|
||||
*/
|
||||
findClassSymbols(sourceFile: ts.SourceFile): ClassSymbol[];
|
||||
|
||||
/**
|
||||
* Search the given source file for exported functions and static class methods that return
|
||||
|
278
packages/compiler-cli/ngcc/src/host/umd_host.ts
Normal file
278
packages/compiler-cli/ngcc/src/host/umd_host.ts
Normal file
@ -0,0 +1,278 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Declaration, Import} from '../../../src/ngtsc/reflection';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {Esm5ReflectionHost} from './esm5_host';
|
||||
|
||||
export class UmdReflectionHost extends Esm5ReflectionHost {
|
||||
protected umdModules = new Map<ts.SourceFile, UmdModule|null>();
|
||||
protected umdExports = new Map<ts.SourceFile, Map<string, Declaration>|null>();
|
||||
protected umdImportPaths = new Map<ts.ParameterDeclaration, string|null>();
|
||||
constructor(
|
||||
logger: Logger, isCore: boolean, protected program: ts.Program,
|
||||
protected compilerHost: ts.CompilerHost, dts?: BundleProgram|null) {
|
||||
super(logger, isCore, program.getTypeChecker(), dts);
|
||||
}
|
||||
|
||||
getImportOfIdentifier(id: ts.Identifier): Import|null {
|
||||
const importParameter = this.findUmdImportParameter(id);
|
||||
const from = importParameter && this.getUmdImportPath(importParameter);
|
||||
return from !== null ? {from, name: id.text} : null;
|
||||
}
|
||||
|
||||
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
||||
return this.getUmdImportedDeclaration(id) || super.getDeclarationOfIdentifier(id);
|
||||
}
|
||||
|
||||
getExportsOfModule(module: ts.Node): Map<string, Declaration>|null {
|
||||
return super.getExportsOfModule(module) || this.getUmdExports(module.getSourceFile());
|
||||
}
|
||||
|
||||
getUmdModule(sourceFile: ts.SourceFile): UmdModule|null {
|
||||
if (sourceFile.isDeclarationFile) {
|
||||
return null;
|
||||
}
|
||||
if (!this.umdModules.has(sourceFile)) {
|
||||
if (sourceFile.statements.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected UMD module file (${sourceFile.fileName}) to contain exactly one statement, but found ${sourceFile.statements}.`);
|
||||
}
|
||||
this.umdModules.set(sourceFile, parseStatementForUmdModule(sourceFile.statements[0]));
|
||||
}
|
||||
return this.umdModules.get(sourceFile) !;
|
||||
}
|
||||
|
||||
getUmdImportPath(importParameter: ts.ParameterDeclaration): string|null {
|
||||
if (this.umdImportPaths.has(importParameter)) {
|
||||
return this.umdImportPaths.get(importParameter) !;
|
||||
}
|
||||
|
||||
const umdModule = this.getUmdModule(importParameter.getSourceFile());
|
||||
if (umdModule === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const imports = getImportsOfUmdModule(umdModule);
|
||||
if (imports === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const i of imports) {
|
||||
this.umdImportPaths.set(i.parameter, i.path);
|
||||
if (i.parameter === importParameter) {
|
||||
return i.path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getUmdExports(sourceFile: ts.SourceFile): Map<string, Declaration>|null {
|
||||
if (!this.umdExports.has(sourceFile)) {
|
||||
const moduleExports = this.computeExportsOfUmdModule(sourceFile);
|
||||
this.umdExports.set(sourceFile, moduleExports);
|
||||
}
|
||||
return this.umdExports.get(sourceFile) !;
|
||||
}
|
||||
|
||||
/** Get the top level statements for a module.
|
||||
*
|
||||
* In UMD modules these are the body of the UMD factory function.
|
||||
*
|
||||
* @param sourceFile The module whose statements we want.
|
||||
* @returns An array of top level statements for the given module.
|
||||
*/
|
||||
protected getModuleStatements(sourceFile: ts.SourceFile): ts.Statement[] {
|
||||
const umdModule = this.getUmdModule(sourceFile);
|
||||
return umdModule !== null ? Array.from(umdModule.factoryFn.body.statements) : [];
|
||||
}
|
||||
|
||||
private computeExportsOfUmdModule(sourceFile: ts.SourceFile): Map<string, Declaration>|null {
|
||||
const moduleMap = new Map<string, Declaration>();
|
||||
const exportStatements = this.getModuleStatements(sourceFile).filter(isUmdExportStatement);
|
||||
const exportDeclarations =
|
||||
exportStatements.map(statement => this.extractUmdExportDeclaration(statement));
|
||||
exportDeclarations.forEach(decl => {
|
||||
if (decl) {
|
||||
moduleMap.set(decl.name, decl.declaration);
|
||||
}
|
||||
});
|
||||
return moduleMap;
|
||||
}
|
||||
|
||||
private extractUmdExportDeclaration(statement: UmdExportStatement): UmdExportDeclaration|null {
|
||||
const exportExpression = statement.expression.right;
|
||||
const name = statement.expression.left.name.text;
|
||||
|
||||
const declaration = this.getDeclarationOfExpression(exportExpression);
|
||||
if (declaration === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {name, declaration};
|
||||
}
|
||||
|
||||
private findUmdImportParameter(id: ts.Identifier): ts.ParameterDeclaration|null {
|
||||
// Is `id` a namespaced property access, e.g. `Directive` in `core.Directive`?
|
||||
// If so capture the symbol of the namespace, e.g. `core`.
|
||||
const nsIdentifier = findNamespaceOfIdentifier(id);
|
||||
const nsSymbol = nsIdentifier && this.checker.getSymbolAtLocation(nsIdentifier) || null;
|
||||
|
||||
// Is the namespace a parameter on a UMD factory function, e.g. `function factory(this, core)`?
|
||||
// If so then return its declaration.
|
||||
const nsDeclaration = nsSymbol && nsSymbol.valueDeclaration;
|
||||
return nsDeclaration && ts.isParameter(nsDeclaration) ? nsDeclaration : null;
|
||||
}
|
||||
|
||||
private getUmdImportedDeclaration(id: ts.Identifier): Declaration|null {
|
||||
const importInfo = this.getImportOfIdentifier(id);
|
||||
if (importInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const importedFile = this.resolveModuleName(importInfo.from, id.getSourceFile());
|
||||
if (importedFile === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We need to add the `viaModule` because the `getExportsOfModule()` call
|
||||
// did not know that we were importing the declaration.
|
||||
return {node: importedFile, viaModule: importInfo.from};
|
||||
}
|
||||
|
||||
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|
||||
|undefined {
|
||||
if (this.compilerHost.resolveModuleNames) {
|
||||
const moduleInfo =
|
||||
this.compilerHost.resolveModuleNames([moduleName], containingFile.fileName)[0];
|
||||
return moduleInfo && this.program.getSourceFile(moduleInfo.resolvedFileName);
|
||||
} else {
|
||||
const moduleInfo = ts.resolveModuleName(
|
||||
moduleName, containingFile.fileName, this.program.getCompilerOptions(),
|
||||
this.compilerHost);
|
||||
return moduleInfo.resolvedModule &&
|
||||
this.program.getSourceFile(moduleInfo.resolvedModule.resolvedFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function parseStatementForUmdModule(statement: ts.Statement): UmdModule|null {
|
||||
const wrapperCall = getUmdWrapperCall(statement);
|
||||
if (!wrapperCall) return null;
|
||||
|
||||
const wrapperFn = wrapperCall.expression;
|
||||
if (!ts.isFunctionExpression(wrapperFn)) return null;
|
||||
|
||||
const factoryFnParamIndex = wrapperFn.parameters.findIndex(
|
||||
parameter => ts.isIdentifier(parameter.name) && parameter.name.text === 'factory');
|
||||
if (factoryFnParamIndex === -1) return null;
|
||||
|
||||
const factoryFn = stripParentheses(wrapperCall.arguments[factoryFnParamIndex]);
|
||||
if (!factoryFn || !ts.isFunctionExpression(factoryFn)) return null;
|
||||
|
||||
return {wrapperFn, factoryFn};
|
||||
}
|
||||
|
||||
function getUmdWrapperCall(statement: ts.Statement): ts.CallExpression&
|
||||
{expression: ts.FunctionExpression}|null {
|
||||
if (!ts.isExpressionStatement(statement) || !ts.isParenthesizedExpression(statement.expression) ||
|
||||
!ts.isCallExpression(statement.expression.expression) ||
|
||||
!ts.isFunctionExpression(statement.expression.expression.expression)) {
|
||||
return null;
|
||||
}
|
||||
return statement.expression.expression as ts.CallExpression & {expression: ts.FunctionExpression};
|
||||
}
|
||||
|
||||
|
||||
export function getImportsOfUmdModule(umdModule: UmdModule):
|
||||
{parameter: ts.ParameterDeclaration, path: string}[] {
|
||||
const imports: {parameter: ts.ParameterDeclaration, path: string}[] = [];
|
||||
for (let i = 1; i < umdModule.factoryFn.parameters.length; i++) {
|
||||
imports.push({
|
||||
parameter: umdModule.factoryFn.parameters[i],
|
||||
path: getRequiredModulePath(umdModule.wrapperFn, i)
|
||||
});
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
interface UmdModule {
|
||||
wrapperFn: ts.FunctionExpression;
|
||||
factoryFn: ts.FunctionExpression;
|
||||
}
|
||||
|
||||
type UmdExportStatement = ts.ExpressionStatement & {
|
||||
expression:
|
||||
ts.BinaryExpression & {left: ts.PropertyAccessExpression & {expression: ts.Identifier}}
|
||||
};
|
||||
|
||||
function isUmdExportStatement(s: ts.Statement): s is UmdExportStatement {
|
||||
return ts.isExpressionStatement(s) && ts.isBinaryExpression(s.expression) &&
|
||||
ts.isPropertyAccessExpression(s.expression.left) &&
|
||||
ts.isIdentifier(s.expression.left.expression) &&
|
||||
s.expression.left.expression.text === 'exports';
|
||||
}
|
||||
|
||||
interface UmdExportDeclaration {
|
||||
name: string;
|
||||
declaration: Declaration;
|
||||
}
|
||||
|
||||
function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: number): string {
|
||||
const statement = wrapperFn.body.statements[0];
|
||||
if (!ts.isExpressionStatement(statement)) {
|
||||
throw new Error(
|
||||
'UMD wrapper body is not an expression statement:\n' + wrapperFn.body.getText());
|
||||
}
|
||||
const modulePaths: string[] = [];
|
||||
findModulePaths(statement.expression);
|
||||
|
||||
// Since we were only interested in the `require()` calls, we miss the `exports` argument, so we
|
||||
// need to subtract 1.
|
||||
// E.g. `function(exports, dep1, dep2)` maps to `function(exports, require('path/to/dep1'),
|
||||
// require('path/to/dep2'))`
|
||||
return modulePaths[paramIndex - 1];
|
||||
|
||||
// Search the statement for calls to `require('...')` and extract the string value of the first
|
||||
// argument
|
||||
function findModulePaths(node: ts.Node) {
|
||||
if (isRequireCall(node)) {
|
||||
const argument = node.arguments[0];
|
||||
if (ts.isStringLiteral(argument)) {
|
||||
modulePaths.push(argument.text);
|
||||
}
|
||||
} else {
|
||||
node.forEachChild(findModulePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isRequireCall(node: ts.Node): node is ts.CallExpression {
|
||||
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
|
||||
node.expression.text === 'require' && node.arguments.length === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the identifier `id` is the RHS of a property access of the form `namespace.id`
|
||||
* and `namespace` is an identifer then return `namespace`, otherwise `null`.
|
||||
* @param id The identifier whose namespace we want to find.
|
||||
*/
|
||||
function findNamespaceOfIdentifier(id: ts.Identifier): ts.Identifier|null {
|
||||
return id.parent && ts.isPropertyAccessExpression(id.parent) &&
|
||||
ts.isIdentifier(id.parent.expression) ?
|
||||
id.parent.expression :
|
||||
null;
|
||||
}
|
||||
|
||||
export function stripParentheses(node: ts.Node): ts.Node {
|
||||
return ts.isParenthesizedExpression(node) ? node.expression : node;
|
||||
}
|
@ -5,27 +5,27 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {resolve} from 'canonical-path';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../src/ngtsc/path';
|
||||
|
||||
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
||||
import {DependencyResolver} from './dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from './dependencies/module_resolver';
|
||||
import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
||||
import {FileSystem} from './file_system/file_system';
|
||||
import {NodeJSFileSystem} from './file_system/node_js_file_system';
|
||||
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
||||
import {Logger} from './logging/logger';
|
||||
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
||||
import {DependencyHost} from './packages/dependency_host';
|
||||
import {DependencyResolver} from './packages/dependency_resolver';
|
||||
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
||||
import {makeEntryPointBundle} from './packages/entry_point_bundle';
|
||||
import {EntryPointFinder} from './packages/entry_point_finder';
|
||||
import {Transformer} from './packages/transformer';
|
||||
import {PathMappings} from './utils';
|
||||
import {FileWriter} from './writing/file_writer';
|
||||
import {InPlaceFileWriter} from './writing/in_place_file_writer';
|
||||
import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The options to configure the ngcc compiler.
|
||||
*/
|
||||
@ -58,9 +58,14 @@ export interface NgccOptions {
|
||||
* Provide a logger that will be called with log messages.
|
||||
*/
|
||||
logger?: Logger;
|
||||
/**
|
||||
* Paths mapping configuration (`paths` and `baseUrl`), as found in `ts.CompilerOptions`.
|
||||
* These are used to resolve paths to locally built Angular libraries.
|
||||
*/
|
||||
pathMappings?: PathMappings;
|
||||
}
|
||||
|
||||
const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
|
||||
const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015', 'umd', 'commonjs'];
|
||||
|
||||
/**
|
||||
* This is the main entry-point into ngcc (aNGular Compatibility Compiler).
|
||||
@ -70,44 +75,47 @@ const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
|
||||
*
|
||||
* @param options The options telling ngcc what to compile and how.
|
||||
*/
|
||||
export function mainNgcc({basePath, targetEntryPointPath,
|
||||
propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
|
||||
compileAllFormats = true, createNewEntryPointFormats = false,
|
||||
logger = new ConsoleLogger(LogLevel.info)}: NgccOptions): void {
|
||||
const transformer = new Transformer(logger, basePath);
|
||||
const host = new DependencyHost();
|
||||
const resolver = new DependencyResolver(logger, host);
|
||||
const finder = new EntryPointFinder(logger, resolver);
|
||||
const fileWriter = getFileWriter(createNewEntryPointFormats);
|
||||
export function mainNgcc(
|
||||
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
|
||||
compileAllFormats = true, createNewEntryPointFormats = false,
|
||||
logger = new ConsoleLogger(LogLevel.info), pathMappings}: NgccOptions): void {
|
||||
const fs = new NodeJSFileSystem();
|
||||
const transformer = new Transformer(fs, logger);
|
||||
const moduleResolver = new ModuleResolver(fs, pathMappings);
|
||||
const esmDependencyHost = new EsmDependencyHost(fs, moduleResolver);
|
||||
const umdDependencyHost = new UmdDependencyHost(fs, moduleResolver);
|
||||
const commonJsDependencyHost = new CommonJsDependencyHost(fs, moduleResolver);
|
||||
const resolver = new DependencyResolver(fs, logger, {
|
||||
esm5: esmDependencyHost,
|
||||
esm2015: esmDependencyHost,
|
||||
umd: umdDependencyHost,
|
||||
commonjs: commonJsDependencyHost
|
||||
});
|
||||
const finder = new EntryPointFinder(fs, logger, resolver);
|
||||
const fileWriter = getFileWriter(fs, createNewEntryPointFormats);
|
||||
|
||||
const absoluteTargetEntryPointPath = targetEntryPointPath ?
|
||||
AbsoluteFsPath.from(resolve(basePath, targetEntryPointPath)) :
|
||||
undefined;
|
||||
const absoluteTargetEntryPointPath =
|
||||
targetEntryPointPath ? AbsoluteFsPath.resolve(basePath, targetEntryPointPath) : undefined;
|
||||
|
||||
if (absoluteTargetEntryPointPath &&
|
||||
hasProcessedTargetEntryPoint(
|
||||
absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
fs, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
logger.debug('The target entry-point has already been processed');
|
||||
return;
|
||||
}
|
||||
|
||||
const {entryPoints} =
|
||||
finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath);
|
||||
const {entryPoints, invalidEntryPoints} = finder.findEntryPoints(
|
||||
AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath, pathMappings);
|
||||
|
||||
if (absoluteTargetEntryPointPath && entryPoints.every(entryPoint => {
|
||||
return entryPoint.path !== absoluteTargetEntryPointPath;
|
||||
})) {
|
||||
// If we get here, then the requested entry-point did not contain anything compiled by
|
||||
// the old Angular compiler. Therefore there is nothing for ngcc to do.
|
||||
// So mark all formats in this entry-point as processed so that clients of ngcc can avoid
|
||||
// triggering ngcc for this entry-point in the future.
|
||||
const packageJsonPath =
|
||||
AbsoluteFsPath.from(resolve(absoluteTargetEntryPointPath, 'package.json'));
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
propertiesToConsider.forEach(formatProperty => {
|
||||
if (packageJson[formatProperty])
|
||||
markAsProcessed(packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
|
||||
});
|
||||
invalidEntryPoints.forEach(invalidEntryPoint => {
|
||||
logger.debug(
|
||||
`Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`,
|
||||
`It is missing required dependencies:\n` +
|
||||
invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n'));
|
||||
});
|
||||
|
||||
if (absoluteTargetEntryPointPath && entryPoints.length === 0) {
|
||||
markNonAngularPackageAsProcessed(fs, absoluteTargetEntryPointPath, propertiesToConsider);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -117,14 +125,15 @@ export function mainNgcc({basePath, targetEntryPointPath,
|
||||
|
||||
const compiledFormats = new Set<string>();
|
||||
const entryPointPackageJson = entryPoint.packageJson;
|
||||
const entryPointPackageJsonPath = AbsoluteFsPath.from(resolve(entryPoint.path, 'package.json'));
|
||||
const entryPointPackageJsonPath =
|
||||
AbsoluteFsPath.fromUnchecked(`${entryPoint.path}/package.json`);
|
||||
|
||||
const hasProcessedDts = hasBeenProcessed(entryPointPackageJson, 'typings');
|
||||
|
||||
for (let i = 0; i < propertiesToConsider.length; i++) {
|
||||
const property = propertiesToConsider[i] as EntryPointJsonProperty;
|
||||
const formatPath = entryPointPackageJson[property];
|
||||
const format = getEntryPointFormat(property);
|
||||
const format = getEntryPointFormat(fs, entryPoint, property);
|
||||
|
||||
// No format then this property is not supposed to be compiled.
|
||||
if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue;
|
||||
@ -142,7 +151,8 @@ export function mainNgcc({basePath, targetEntryPointPath,
|
||||
// the property as processed even if its underlying format has been built already.
|
||||
if (!compiledFormats.has(formatPath) && (compileAllFormats || isFirstFormat)) {
|
||||
const bundle = makeEntryPointBundle(
|
||||
entryPoint.path, formatPath, entryPoint.typings, isCore, property, format, processDts);
|
||||
fs, entryPoint.path, formatPath, entryPoint.typings, isCore, property, format,
|
||||
processDts, pathMappings);
|
||||
if (bundle) {
|
||||
logger.info(`Compiling ${entryPoint.name} : ${property} as ${format}`);
|
||||
const transformedFiles = transformer.transform(bundle);
|
||||
@ -159,9 +169,9 @@ export function mainNgcc({basePath, targetEntryPointPath,
|
||||
// Either this format was just compiled or its underlying format was compiled because of a
|
||||
// previous property.
|
||||
if (compiledFormats.has(formatPath)) {
|
||||
markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, property);
|
||||
markAsProcessed(fs, entryPointPackageJson, entryPointPackageJsonPath, property);
|
||||
if (processDts) {
|
||||
markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, 'typings');
|
||||
markAsProcessed(fs, entryPointPackageJson, entryPointPackageJsonPath, 'typings');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,14 +183,15 @@ export function mainNgcc({basePath, targetEntryPointPath,
|
||||
});
|
||||
}
|
||||
|
||||
function getFileWriter(createNewEntryPointFormats: boolean): FileWriter {
|
||||
return createNewEntryPointFormats ? new NewEntryPointFileWriter() : new InPlaceFileWriter();
|
||||
function getFileWriter(fs: FileSystem, createNewEntryPointFormats: boolean): FileWriter {
|
||||
return createNewEntryPointFormats ? new NewEntryPointFileWriter(fs) : new InPlaceFileWriter(fs);
|
||||
}
|
||||
|
||||
function hasProcessedTargetEntryPoint(
|
||||
targetPath: AbsoluteFsPath, propertiesToConsider: string[], compileAllFormats: boolean) {
|
||||
const packageJsonPath = AbsoluteFsPath.from(resolve(targetPath, 'package.json'));
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
||||
compileAllFormats: boolean) {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(targetPath, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
||||
|
||||
for (const property of propertiesToConsider) {
|
||||
if (packageJson[property]) {
|
||||
@ -201,3 +212,19 @@ function hasProcessedTargetEntryPoint(
|
||||
// property before the first processed format that was unprocessed.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we get here, then the requested entry-point did not contain anything compiled by
|
||||
* the old Angular compiler. Therefore there is nothing for ngcc to do.
|
||||
* So mark all formats in this entry-point as processed so that clients of ngcc can avoid
|
||||
* triggering ngcc for this entry-point in the future.
|
||||
*/
|
||||
function markNonAngularPackageAsProcessed(
|
||||
fs: FileSystem, path: AbsoluteFsPath, propertiesToConsider: string[]) {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(path, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
||||
propertiesToConsider.forEach(formatProperty => {
|
||||
if (packageJson[formatProperty])
|
||||
markAsProcessed(fs, packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
|
||||
});
|
||||
}
|
||||
|
@ -6,10 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {writeFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
|
||||
|
||||
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
|
||||
@ -49,9 +47,9 @@ export function hasBeenProcessed(
|
||||
* @param format the property in the package.json of the format for which we are writing the marker.
|
||||
*/
|
||||
export function markAsProcessed(
|
||||
packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
|
||||
fs: FileSystem, packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
|
||||
format: EntryPointJsonProperty) {
|
||||
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
|
||||
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
|
||||
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
||||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
||||
}
|
||||
|
@ -5,10 +5,11 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {dirname, resolve} from 'canonical-path';
|
||||
import {existsSync, lstatSync, readdirSync} from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
|
||||
/**
|
||||
* An entry point bundle contains one or two programs, e.g. `src` and `dts`,
|
||||
* that are compiled via TypeScript.
|
||||
@ -21,9 +22,9 @@ export interface BundleProgram {
|
||||
program: ts.Program;
|
||||
options: ts.CompilerOptions;
|
||||
host: ts.CompilerHost;
|
||||
path: string;
|
||||
path: AbsoluteFsPath;
|
||||
file: ts.SourceFile;
|
||||
r3SymbolsPath: string|null;
|
||||
r3SymbolsPath: AbsoluteFsPath|null;
|
||||
r3SymbolsFile: ts.SourceFile|null;
|
||||
}
|
||||
|
||||
@ -31,9 +32,10 @@ export interface BundleProgram {
|
||||
* Create a bundle program.
|
||||
*/
|
||||
export function makeBundleProgram(
|
||||
isCore: boolean, path: string, r3FileName: string, options: ts.CompilerOptions,
|
||||
host: ts.CompilerHost): BundleProgram {
|
||||
const r3SymbolsPath = isCore ? findR3SymbolsPath(dirname(path), r3FileName) : null;
|
||||
fs: FileSystem, isCore: boolean, path: AbsoluteFsPath, r3FileName: string,
|
||||
options: ts.CompilerOptions, host: ts.CompilerHost): BundleProgram {
|
||||
const r3SymbolsPath =
|
||||
isCore ? findR3SymbolsPath(fs, AbsoluteFsPath.dirname(path), r3FileName) : null;
|
||||
const rootPaths = r3SymbolsPath ? [path, r3SymbolsPath] : [path];
|
||||
const program = ts.createProgram(rootPaths, options, host);
|
||||
const file = program.getSourceFile(path) !;
|
||||
@ -45,26 +47,28 @@ export function makeBundleProgram(
|
||||
/**
|
||||
* Search the given directory hierarchy to find the path to the `r3_symbols` file.
|
||||
*/
|
||||
export function findR3SymbolsPath(directory: string, filename: string): string|null {
|
||||
const r3SymbolsFilePath = resolve(directory, filename);
|
||||
if (existsSync(r3SymbolsFilePath)) {
|
||||
export function findR3SymbolsPath(
|
||||
fs: FileSystem, directory: AbsoluteFsPath, filename: string): AbsoluteFsPath|null {
|
||||
const r3SymbolsFilePath = AbsoluteFsPath.resolve(directory, filename);
|
||||
if (fs.exists(r3SymbolsFilePath)) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
|
||||
const subDirectories =
|
||||
readdirSync(directory)
|
||||
fs.readdir(directory)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = lstatSync(resolve(directory, p));
|
||||
const stat = fs.lstat(AbsoluteFsPath.resolve(directory, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
});
|
||||
|
||||
for (const subDirectory of subDirectories) {
|
||||
const r3SymbolsFilePath = findR3SymbolsPath(resolve(directory, subDirectory, ), filename);
|
||||
const r3SymbolsFilePath =
|
||||
findR3SymbolsPath(fs, AbsoluteFsPath.resolve(directory, subDirectory), filename);
|
||||
if (r3SymbolsFilePath) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
|
@ -1,157 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
|
||||
/**
|
||||
* Helper functions for computing dependencies.
|
||||
*/
|
||||
export class DependencyHost {
|
||||
/**
|
||||
* Get a list of the resolved paths to all the dependencies of this entry point.
|
||||
* @param from An absolute path to the file whose dependencies we want to get.
|
||||
* @param dependencies A set that will have the absolute paths of resolved entry points added to
|
||||
* it.
|
||||
* @param missing A set that will have the dependencies that could not be found added to it.
|
||||
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
|
||||
* entry-points, i.e. deep-imports.
|
||||
* @param internal A set that is used to track internal dependencies to prevent getting stuck in a
|
||||
* circular dependency loop.
|
||||
*/
|
||||
computeDependencies(
|
||||
from: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath> = new Set(),
|
||||
missing: Set<PathSegment> = new Set(), deepImports: Set<PathSegment> = new Set(),
|
||||
internal: Set<AbsoluteFsPath> = new Set()): {
|
||||
dependencies: Set<AbsoluteFsPath>,
|
||||
missing: Set<PathSegment>,
|
||||
deepImports: Set<PathSegment>
|
||||
} {
|
||||
const fromContents = fs.readFileSync(from, 'utf8');
|
||||
if (!this.hasImportOrReexportStatements(fromContents)) {
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
|
||||
const sf =
|
||||
ts.createSourceFile(from, fromContents, ts.ScriptTarget.ES2015, false, ts.ScriptKind.JS);
|
||||
sf.statements
|
||||
// filter out statements that are not imports or reexports
|
||||
.filter(this.isStringImportOrReexport)
|
||||
// Grab the id of the module that is being imported
|
||||
.map(stmt => stmt.moduleSpecifier.text)
|
||||
// Resolve this module id into an absolute path
|
||||
.forEach((importPath: PathSegment) => {
|
||||
if (importPath.startsWith('.')) {
|
||||
// This is an internal import so follow it
|
||||
const internalDependency = this.resolveInternal(from, importPath);
|
||||
// Avoid circular dependencies
|
||||
if (!internal.has(internalDependency)) {
|
||||
internal.add(internalDependency);
|
||||
this.computeDependencies(
|
||||
internalDependency, dependencies, missing, deepImports, internal);
|
||||
}
|
||||
} else {
|
||||
const resolvedEntryPoint = this.tryResolveEntryPoint(from, importPath);
|
||||
if (resolvedEntryPoint !== null) {
|
||||
dependencies.add(resolvedEntryPoint);
|
||||
} else {
|
||||
// If the import could not be resolved as entry point, it either does not exist
|
||||
// at all or is a deep import.
|
||||
const deeplyImportedFile = this.tryResolve(from, importPath);
|
||||
if (deeplyImportedFile !== null) {
|
||||
deepImports.add(importPath);
|
||||
} else {
|
||||
missing.add(importPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an internal module import.
|
||||
* @param from the absolute file path from where to start trying to resolve this module
|
||||
* @param to the module specifier of the internal dependency to resolve
|
||||
* @returns the resolved path to the import.
|
||||
*/
|
||||
resolveInternal(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath {
|
||||
const fromDirectory = path.dirname(from);
|
||||
// `fromDirectory` is absolute so we don't need to worry about telling `require.resolve`
|
||||
// about it by adding it to a `paths` parameter - unlike `tryResolve` below.
|
||||
return AbsoluteFsPath.from(require.resolve(path.resolve(fromDirectory, to)));
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't want to resolve external dependencies directly because if it is a path to a
|
||||
* sub-entry-point (e.g. @angular/animations/browser rather than @angular/animations)
|
||||
* then `require.resolve()` may return a path to a UMD bundle, which may actually live
|
||||
* in the folder containing the sub-entry-point
|
||||
* (e.g. @angular/animations/bundles/animations-browser.umd.js).
|
||||
*
|
||||
* Instead we try to resolve it as a package, which is what we would need anyway for it to be
|
||||
* compilable by ngcc.
|
||||
*
|
||||
* If `to` is actually a path to a file then this will fail, which is what we want.
|
||||
*
|
||||
* @param from the file path from where to start trying to resolve this module
|
||||
* @param to the module specifier of the dependency to resolve
|
||||
* @returns the resolved path to the entry point directory of the import or null
|
||||
* if it cannot be resolved.
|
||||
*/
|
||||
tryResolveEntryPoint(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
|
||||
const entryPoint = this.tryResolve(from, `${to}/package.json` as PathSegment);
|
||||
return entryPoint && AbsoluteFsPath.from(path.dirname(entryPoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the absolute path of a module from a particular starting point.
|
||||
*
|
||||
* @param from the file path from where to start trying to resolve this module
|
||||
* @param to the module specifier of the dependency to resolve
|
||||
* @returns an absolute path to the entry-point of the dependency or null if it could not be
|
||||
* resolved.
|
||||
*/
|
||||
tryResolve(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
|
||||
try {
|
||||
return AbsoluteFsPath.from(require.resolve(to, {paths: [from]}));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given statement is an import with a string literal module specifier.
|
||||
* @param stmt the statement node to check.
|
||||
* @returns true if the statement is an import with a string literal module specifier.
|
||||
*/
|
||||
isStringImportOrReexport(stmt: ts.Statement): stmt is ts.ImportDeclaration&
|
||||
{moduleSpecifier: ts.StringLiteral} {
|
||||
return ts.isImportDeclaration(stmt) ||
|
||||
ts.isExportDeclaration(stmt) && !!stmt.moduleSpecifier &&
|
||||
ts.isStringLiteral(stmt.moduleSpecifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a source file needs to be parsed for imports.
|
||||
* This is a performance short-circuit, which saves us from creating
|
||||
* a TypeScript AST unnecessarily.
|
||||
*
|
||||
* @param source The content of the source file to check.
|
||||
*
|
||||
* @returns false if there are definitely no import or re-export statements
|
||||
* in this file, true otherwise.
|
||||
*/
|
||||
hasImportOrReexportStatements(source: string): boolean {
|
||||
return /(import|export)\s.+from/.test(source);
|
||||
}
|
||||
}
|
@ -5,18 +5,16 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {parseStatementForUmdModule} from '../host/umd_host';
|
||||
import {Logger} from '../logging/logger';
|
||||
|
||||
|
||||
/**
|
||||
* The possible values for the format of an entry-point.
|
||||
*/
|
||||
export type EntryPointFormat = 'esm5' | 'esm2015' | 'umd';
|
||||
export type EntryPointFormat = 'esm5' | 'esm2015' | 'umd' | 'commonjs';
|
||||
|
||||
/**
|
||||
* An object containing information about an entry-point, including paths
|
||||
@ -33,6 +31,8 @@ export interface EntryPoint {
|
||||
path: AbsoluteFsPath;
|
||||
/** The path to a typings (.d.ts) file for this entry-point. */
|
||||
typings: AbsoluteFsPath;
|
||||
/** Is this EntryPoint compiled with the Angular View Engine compiler? */
|
||||
compiledByAngular: boolean;
|
||||
}
|
||||
|
||||
interface PackageJsonFormatProperties {
|
||||
@ -68,13 +68,14 @@ export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
||||
* @returns An entry-point if it is valid, `null` otherwise.
|
||||
*/
|
||||
export function getEntryPointInfo(
|
||||
logger: Logger, packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
fs: FileSystem, logger: Logger, packagePath: AbsoluteFsPath,
|
||||
entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.exists(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryPointPackageJson = loadEntryPointPackage(logger, packageJsonPath);
|
||||
const entryPointPackageJson = loadEntryPointPackage(fs, logger, packageJsonPath);
|
||||
if (!entryPointPackageJson) {
|
||||
return null;
|
||||
}
|
||||
@ -88,17 +89,15 @@ export function getEntryPointInfo(
|
||||
|
||||
// Also there must exist a `metadata.json` file next to the typings entry-point.
|
||||
const metadataPath =
|
||||
path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
||||
if (!fs.existsSync(metadataPath)) {
|
||||
return null;
|
||||
}
|
||||
AbsoluteFsPath.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
||||
|
||||
const entryPointInfo: EntryPoint = {
|
||||
name: entryPointPackageJson.name,
|
||||
packageJson: entryPointPackageJson,
|
||||
package: packagePath,
|
||||
path: entryPointPath,
|
||||
typings: AbsoluteFsPath.from(path.resolve(entryPointPath, typings)),
|
||||
typings: AbsoluteFsPath.resolve(entryPointPath, typings),
|
||||
compiledByAngular: fs.exists(metadataPath),
|
||||
};
|
||||
|
||||
return entryPointInfo;
|
||||
@ -110,7 +109,8 @@ export function getEntryPointInfo(
|
||||
* @param property The property to convert to a format.
|
||||
* @returns An entry-point format or `undefined` if none match the given property.
|
||||
*/
|
||||
export function getEntryPointFormat(property: string): EntryPointFormat|undefined {
|
||||
export function getEntryPointFormat(
|
||||
fs: FileSystem, entryPoint: EntryPoint, property: string): EntryPointFormat|undefined {
|
||||
switch (property) {
|
||||
case 'fesm2015':
|
||||
return 'esm2015';
|
||||
@ -123,7 +123,12 @@ export function getEntryPointFormat(property: string): EntryPointFormat|undefine
|
||||
case 'esm5':
|
||||
return 'esm5';
|
||||
case 'main':
|
||||
return 'umd';
|
||||
const mainFile = entryPoint.packageJson['main'];
|
||||
if (mainFile === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const pathToMain = AbsoluteFsPath.join(entryPoint.path, mainFile);
|
||||
return isUmdModule(fs, pathToMain) ? 'umd' : 'commonjs';
|
||||
case 'module':
|
||||
return 'esm5';
|
||||
default:
|
||||
@ -136,13 +141,20 @@ export function getEntryPointFormat(property: string): EntryPointFormat|undefine
|
||||
* @param packageJsonPath the absolute path to the package.json file.
|
||||
* @returns JSON from the package.json file if it is valid, `null` otherwise.
|
||||
*/
|
||||
function loadEntryPointPackage(logger: Logger, packageJsonPath: string): EntryPointPackageJson|
|
||||
null {
|
||||
function loadEntryPointPackage(
|
||||
fs: FileSystem, logger: Logger, packageJsonPath: AbsoluteFsPath): EntryPointPackageJson|null {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
return JSON.parse(fs.readFile(packageJsonPath));
|
||||
} catch (e) {
|
||||
// We may have run into a package.json with unexpected symbols
|
||||
logger.warn(`Failed to read entry point info from ${packageJsonPath} with error ${e}.`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isUmdModule(fs: FileSystem, sourceFilePath: AbsoluteFsPath): boolean {
|
||||
const sourceFile =
|
||||
ts.createSourceFile(sourceFilePath, fs.readFile(sourceFilePath), ts.ScriptTarget.ES5);
|
||||
return sourceFile.statements.length > 0 &&
|
||||
parseStatementForUmdModule(sourceFile.statements[0]) !== null;
|
||||
}
|
||||
|
@ -5,14 +5,14 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {resolve} from 'canonical-path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {PathMappings} from '../utils';
|
||||
import {BundleProgram, makeBundleProgram} from './bundle_program';
|
||||
import {EntryPointFormat, EntryPointJsonProperty} from './entry_point';
|
||||
|
||||
|
||||
import {NgccCompilerHost, NgccSourcesCompilerHost} from './ngcc_compiler_host';
|
||||
|
||||
/**
|
||||
* A bundle of files and paths (and TS programs) that correspond to a particular
|
||||
@ -38,25 +38,28 @@ export interface EntryPointBundle {
|
||||
* @param transformDts Whether to transform the typings along with this bundle.
|
||||
*/
|
||||
export function makeEntryPointBundle(
|
||||
entryPointPath: string, formatPath: string, typingsPath: string, isCore: boolean,
|
||||
formatProperty: EntryPointJsonProperty, format: EntryPointFormat,
|
||||
transformDts: boolean): EntryPointBundle|null {
|
||||
fs: FileSystem, entryPointPath: string, formatPath: string, typingsPath: string,
|
||||
isCore: boolean, formatProperty: EntryPointJsonProperty, format: EntryPointFormat,
|
||||
transformDts: boolean, pathMappings?: PathMappings): EntryPointBundle|null {
|
||||
// Create the TS program and necessary helpers.
|
||||
const options: ts.CompilerOptions = {
|
||||
allowJs: true,
|
||||
maxNodeModuleJsDepth: Infinity,
|
||||
rootDir: entryPointPath,
|
||||
noLib: true,
|
||||
rootDir: entryPointPath, ...pathMappings
|
||||
};
|
||||
const host = ts.createCompilerHost(options);
|
||||
const srcHost = new NgccSourcesCompilerHost(fs, options, entryPointPath);
|
||||
const dtsHost = new NgccCompilerHost(fs, options);
|
||||
const rootDirs = [AbsoluteFsPath.from(entryPointPath)];
|
||||
|
||||
// Create the bundle programs, as necessary.
|
||||
const src = makeBundleProgram(
|
||||
isCore, resolve(entryPointPath, formatPath), 'r3_symbols.js', options, host);
|
||||
const dts = transformDts ?
|
||||
makeBundleProgram(
|
||||
isCore, resolve(entryPointPath, typingsPath), 'r3_symbols.d.ts', options, host) :
|
||||
null;
|
||||
fs, isCore, AbsoluteFsPath.resolve(entryPointPath, formatPath), 'r3_symbols.js', options,
|
||||
srcHost);
|
||||
const dts = transformDts ? makeBundleProgram(
|
||||
fs, isCore, AbsoluteFsPath.resolve(entryPointPath, typingsPath),
|
||||
'r3_symbols.d.ts', options, dtsHost) :
|
||||
null;
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
|
||||
return {format, formatProperty, rootDirs, isCore, isFlatCore, src, dts};
|
||||
|
@ -5,31 +5,65 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from './dependency_resolver';
|
||||
import {PathMappings} from '../utils';
|
||||
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
||||
|
||||
|
||||
export class EntryPointFinder {
|
||||
constructor(private logger: Logger, private resolver: DependencyResolver) {}
|
||||
constructor(
|
||||
private fs: FileSystem, private logger: Logger, private resolver: DependencyResolver) {}
|
||||
/**
|
||||
* Search the given directory, and sub-directories, for Angular package entry points.
|
||||
* @param sourceDirectory An absolute path to the directory to search for entry points.
|
||||
*/
|
||||
findEntryPoints(sourceDirectory: AbsoluteFsPath, targetEntryPointPath?: AbsoluteFsPath):
|
||||
SortedEntryPointsInfo {
|
||||
const unsortedEntryPoints = this.walkDirectoryForEntryPoints(sourceDirectory);
|
||||
findEntryPoints(
|
||||
sourceDirectory: AbsoluteFsPath, targetEntryPointPath?: AbsoluteFsPath,
|
||||
pathMappings?: PathMappings): SortedEntryPointsInfo {
|
||||
const basePaths = this.getBasePaths(sourceDirectory, pathMappings);
|
||||
const unsortedEntryPoints = basePaths.reduce<EntryPoint[]>(
|
||||
(entryPoints, basePath) => entryPoints.concat(this.walkDirectoryForEntryPoints(basePath)),
|
||||
[]);
|
||||
const targetEntryPoint = targetEntryPointPath ?
|
||||
unsortedEntryPoints.find(entryPoint => entryPoint.path === targetEntryPointPath) :
|
||||
undefined;
|
||||
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints, targetEntryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the base-paths that we need to search for entry-points.
|
||||
*
|
||||
* This always contains the standard base-path (`sourceDirectory`).
|
||||
* But it also parses the `paths` mappings object to guess additional base-paths.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* getBasePaths('/node_modules', {baseUrl: '/dist', paths: {'*': ['lib/*', 'lib/generated/*']}})
|
||||
* > ['/node_modules', '/dist/lib']
|
||||
* ```
|
||||
*
|
||||
* Notice that `'/dist'` is not included as there is no `'*'` path,
|
||||
* and `'/dist/lib/generated'` is not included as it is covered by `'/dist/lib'`.
|
||||
*
|
||||
* @param sourceDirectory The standard base-path (e.g. node_modules).
|
||||
* @param pathMappings Path mapping configuration, from which to extract additional base-paths.
|
||||
*/
|
||||
private getBasePaths(sourceDirectory: AbsoluteFsPath, pathMappings?: PathMappings):
|
||||
AbsoluteFsPath[] {
|
||||
const basePaths = [sourceDirectory];
|
||||
if (pathMappings) {
|
||||
const baseUrl = AbsoluteFsPath.resolve(pathMappings.baseUrl);
|
||||
values(pathMappings.paths).forEach(paths => paths.forEach(path => {
|
||||
basePaths.push(AbsoluteFsPath.join(baseUrl, extractPathPrefix(path)));
|
||||
}));
|
||||
}
|
||||
basePaths.sort(); // Get the paths in order with the shorter ones first.
|
||||
return basePaths.filter(removeDeeperPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for entry points that need to be compiled, starting at the source directory.
|
||||
* The function will recurse into directories that start with `@...`, e.g. `@angular/...`.
|
||||
@ -37,29 +71,29 @@ export class EntryPointFinder {
|
||||
*/
|
||||
private walkDirectoryForEntryPoints(sourceDirectory: AbsoluteFsPath): EntryPoint[] {
|
||||
const entryPoints: EntryPoint[] = [];
|
||||
fs.readdirSync(sourceDirectory)
|
||||
this.fs
|
||||
.readdir(sourceDirectory)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = fs.lstatSync(path.resolve(sourceDirectory, p));
|
||||
const stat = this.fs.lstat(AbsoluteFsPath.resolve(sourceDirectory, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
})
|
||||
.forEach(p => {
|
||||
// Either the directory is a potential package or a namespace containing packages (e.g
|
||||
// `@angular`).
|
||||
const packagePath = AbsoluteFsPath.from(path.join(sourceDirectory, p));
|
||||
const packagePath = AbsoluteFsPath.join(sourceDirectory, p);
|
||||
if (p.startsWith('@')) {
|
||||
entryPoints.push(...this.walkDirectoryForEntryPoints(packagePath));
|
||||
} else {
|
||||
entryPoints.push(...this.getEntryPointsForPackage(packagePath));
|
||||
|
||||
// Also check for any nested node_modules in this package
|
||||
const nestedNodeModulesPath =
|
||||
AbsoluteFsPath.from(path.resolve(packagePath, 'node_modules'));
|
||||
if (fs.existsSync(nestedNodeModulesPath)) {
|
||||
const nestedNodeModulesPath = AbsoluteFsPath.join(packagePath, 'node_modules');
|
||||
if (this.fs.exists(nestedNodeModulesPath)) {
|
||||
entryPoints.push(...this.walkDirectoryForEntryPoints(nestedNodeModulesPath));
|
||||
}
|
||||
}
|
||||
@ -76,14 +110,14 @@ export class EntryPointFinder {
|
||||
const entryPoints: EntryPoint[] = [];
|
||||
|
||||
// Try to get an entry point from the top level package directory
|
||||
const topLevelEntryPoint = getEntryPointInfo(this.logger, packagePath, packagePath);
|
||||
const topLevelEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, packagePath);
|
||||
if (topLevelEntryPoint !== null) {
|
||||
entryPoints.push(topLevelEntryPoint);
|
||||
}
|
||||
|
||||
// Now search all the directories of this package for possible entry points
|
||||
this.walkDirectory(packagePath, subdir => {
|
||||
const subEntryPoint = getEntryPointInfo(this.logger, packagePath, subdir);
|
||||
const subEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, subdir);
|
||||
if (subEntryPoint !== null) {
|
||||
entryPoints.push(subEntryPoint);
|
||||
}
|
||||
@ -99,21 +133,53 @@ export class EntryPointFinder {
|
||||
* @param fn the function to apply to each directory.
|
||||
*/
|
||||
private walkDirectory(dir: AbsoluteFsPath, fn: (dir: AbsoluteFsPath) => void) {
|
||||
return fs
|
||||
.readdirSync(dir)
|
||||
return this.fs
|
||||
.readdir(dir)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = fs.lstatSync(path.resolve(dir, p));
|
||||
const stat = this.fs.lstat(AbsoluteFsPath.resolve(dir, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
})
|
||||
.forEach(subDir => {
|
||||
const resolvedSubDir = AbsoluteFsPath.from(path.resolve(dir, subDir));
|
||||
const resolvedSubDir = AbsoluteFsPath.resolve(dir, subDir);
|
||||
fn(resolvedSubDir);
|
||||
this.walkDirectory(resolvedSubDir, fn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract everything in the `path` up to the first `*`.
|
||||
* @param path The path to parse.
|
||||
* @returns The extracted prefix.
|
||||
*/
|
||||
function extractPathPrefix(path: string) {
|
||||
return path.split('*', 1)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter function that removes paths that are already covered by higher paths.
|
||||
*
|
||||
* @param value The current path.
|
||||
* @param index The index of the current path.
|
||||
* @param array The array of paths (sorted alphabetically).
|
||||
* @returns true if this path is not already covered by a previous path.
|
||||
*/
|
||||
function removeDeeperPaths(value: AbsoluteFsPath, index: number, array: AbsoluteFsPath[]) {
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (value.startsWith(array[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the values (not keys) from an object.
|
||||
* @param obj The object to process.
|
||||
*/
|
||||
function values<T>(obj: {[key: string]: T}): T[] {
|
||||
return Object.keys(obj).map(key => obj[key]);
|
||||
}
|
||||
|
105
packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts
Normal file
105
packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as os from 'os';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {isRelativePath} from '../utils';
|
||||
|
||||
export class NgccCompilerHost implements ts.CompilerHost {
|
||||
private _caseSensitive = this.fs.exists(AbsoluteFsPath.fromUnchecked(__filename.toUpperCase()));
|
||||
|
||||
constructor(protected fs: FileSystem, protected options: ts.CompilerOptions) {}
|
||||
|
||||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined {
|
||||
const text = this.readFile(fileName);
|
||||
return text !== undefined ? ts.createSourceFile(fileName, text, languageVersion) : undefined;
|
||||
}
|
||||
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return this.getDefaultLibLocation() + '/' + ts.getDefaultLibFileName(options);
|
||||
}
|
||||
|
||||
getDefaultLibLocation(): string {
|
||||
const nodeLibPath = AbsoluteFsPath.from(require.resolve('typescript'));
|
||||
return AbsoluteFsPath.join(nodeLibPath, '..');
|
||||
}
|
||||
|
||||
writeFile(fileName: string, data: string): void {
|
||||
this.fs.writeFile(AbsoluteFsPath.fromUnchecked(fileName), data);
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string { return this.fs.pwd(); }
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean { return this._caseSensitive; }
|
||||
|
||||
getNewLine(): string {
|
||||
switch (this.options.newLine) {
|
||||
case ts.NewLineKind.CarriageReturnLineFeed:
|
||||
return '\r\n';
|
||||
case ts.NewLineKind.LineFeed:
|
||||
return '\n';
|
||||
default:
|
||||
return os.EOL;
|
||||
}
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.fs.exists(AbsoluteFsPath.fromUnchecked(fileName));
|
||||
}
|
||||
|
||||
readFile(fileName: string): string|undefined {
|
||||
if (!this.fileExists(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
return this.fs.readFile(AbsoluteFsPath.fromUnchecked(fileName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a compiler host that resolves a module import as a JavaScript source file if
|
||||
* available, instead of the .d.ts typings file that would have been resolved by TypeScript. This
|
||||
* is necessary for packages that have their typings in the same directory as the sources, which
|
||||
* would otherwise let TypeScript prefer the .d.ts file instead of the JavaScript source file.
|
||||
*/
|
||||
export class NgccSourcesCompilerHost extends NgccCompilerHost {
|
||||
private cache = ts.createModuleResolutionCache(
|
||||
this.getCurrentDirectory(), file => this.getCanonicalFileName(file));
|
||||
|
||||
constructor(fs: FileSystem, options: ts.CompilerOptions, protected entryPointPath: string) {
|
||||
super(fs, options);
|
||||
}
|
||||
|
||||
resolveModuleNames(
|
||||
moduleNames: string[], containingFile: string, reusedNames?: string[],
|
||||
redirectedReference?: ts.ResolvedProjectReference): Array<ts.ResolvedModule|undefined> {
|
||||
return moduleNames.map(moduleName => {
|
||||
const {resolvedModule} = ts.resolveModuleName(
|
||||
moduleName, containingFile, this.options, this, this.cache, redirectedReference);
|
||||
|
||||
// If the module request originated from a relative import in a JavaScript source file,
|
||||
// TypeScript may have resolved the module to its .d.ts declaration file if the .js source
|
||||
// file was in the same directory. This is undesirable, as we need to have the actual
|
||||
// JavaScript being present in the program. This logic recognizes this scenario and rewrites
|
||||
// the resolved .d.ts declaration file to its .js counterpart, if it exists.
|
||||
if (resolvedModule !== undefined && resolvedModule.extension === ts.Extension.Dts &&
|
||||
containingFile.endsWith('.js') && isRelativePath(moduleName)) {
|
||||
const jsFile = resolvedModule.resolvedFileName.replace(/\.d\.ts$/, '.js');
|
||||
if (this.fileExists(jsFile)) {
|
||||
return {...resolvedModule, resolvedFileName: jsFile, extension: ts.Extension.Js};
|
||||
}
|
||||
}
|
||||
return resolvedModule;
|
||||
});
|
||||
}
|
||||
}
|
@ -12,13 +12,21 @@ import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../analy
|
||||
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
|
||||
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {CommonJsReflectionHost} from '../host/commonjs_host';
|
||||
import {Esm2015ReflectionHost} from '../host/esm2015_host';
|
||||
import {Esm5ReflectionHost} from '../host/esm5_host';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {UmdReflectionHost} from '../host/umd_host';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {Esm5Renderer} from '../rendering/esm5_renderer';
|
||||
import {EsmRenderer} from '../rendering/esm_renderer';
|
||||
import {FileInfo, Renderer} from '../rendering/renderer';
|
||||
import {CommonJsRenderingFormatter} from '../rendering/commonjs_rendering_formatter';
|
||||
import {DtsRenderer} from '../rendering/dts_renderer';
|
||||
import {Esm5RenderingFormatter} from '../rendering/esm5_rendering_formatter';
|
||||
import {EsmRenderingFormatter} from '../rendering/esm_rendering_formatter';
|
||||
import {Renderer} from '../rendering/renderer';
|
||||
import {RenderingFormatter} from '../rendering/rendering_formatter';
|
||||
import {UmdRenderingFormatter} from '../rendering/umd_rendering_formatter';
|
||||
import {FileToWrite} from '../rendering/utils';
|
||||
|
||||
import {EntryPointBundle} from './entry_point_bundle';
|
||||
|
||||
@ -46,14 +54,14 @@ import {EntryPointBundle} from './entry_point_bundle';
|
||||
* - Some formats may contain multiple "modules" in a single file.
|
||||
*/
|
||||
export class Transformer {
|
||||
constructor(private logger: Logger, private sourcePath: string) {}
|
||||
constructor(private fs: FileSystem, private logger: Logger) {}
|
||||
|
||||
/**
|
||||
* Transform the source (and typings) files of a bundle.
|
||||
* @param bundle the bundle to transform.
|
||||
* @returns information about the files that were transformed.
|
||||
*/
|
||||
transform(bundle: EntryPointBundle): FileInfo[] {
|
||||
transform(bundle: EntryPointBundle): FileToWrite[] {
|
||||
const isCore = bundle.isCore;
|
||||
const reflectionHost = this.getHost(isCore, bundle);
|
||||
|
||||
@ -62,10 +70,21 @@ export class Transformer {
|
||||
moduleWithProvidersAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle);
|
||||
|
||||
// Transform the source files and source maps.
|
||||
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
|
||||
const renderedFiles = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
const srcFormatter = this.getRenderingFormatter(reflectionHost, isCore, bundle);
|
||||
|
||||
const renderer =
|
||||
new Renderer(srcFormatter, this.fs, this.logger, reflectionHost, isCore, bundle);
|
||||
let renderedFiles = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
if (bundle.dts) {
|
||||
const dtsFormatter = new EsmRenderingFormatter(reflectionHost, isCore);
|
||||
const dtsRenderer =
|
||||
new DtsRenderer(dtsFormatter, this.fs, this.logger, reflectionHost, isCore, bundle);
|
||||
const renderedDtsFiles = dtsRenderer.renderProgram(
|
||||
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
||||
renderedFiles = renderedFiles.concat(renderedDtsFiles);
|
||||
}
|
||||
|
||||
return renderedFiles;
|
||||
}
|
||||
@ -77,17 +96,31 @@ export class Transformer {
|
||||
return new Esm2015ReflectionHost(this.logger, isCore, typeChecker, bundle.dts);
|
||||
case 'esm5':
|
||||
return new Esm5ReflectionHost(this.logger, isCore, typeChecker, bundle.dts);
|
||||
case 'umd':
|
||||
return new UmdReflectionHost(
|
||||
this.logger, isCore, bundle.src.program, bundle.src.host, bundle.dts);
|
||||
case 'commonjs':
|
||||
return new CommonJsReflectionHost(
|
||||
this.logger, isCore, bundle.src.program, bundle.src.host, bundle.dts);
|
||||
default:
|
||||
throw new Error(`Reflection host for "${bundle.format}" not yet implemented.`);
|
||||
}
|
||||
}
|
||||
|
||||
getRenderer(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle): Renderer {
|
||||
getRenderingFormatter(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle):
|
||||
RenderingFormatter {
|
||||
switch (bundle.format) {
|
||||
case 'esm2015':
|
||||
return new EsmRenderer(this.logger, host, isCore, bundle, this.sourcePath);
|
||||
return new EsmRenderingFormatter(host, isCore);
|
||||
case 'esm5':
|
||||
return new Esm5Renderer(this.logger, host, isCore, bundle, this.sourcePath);
|
||||
return new Esm5RenderingFormatter(host, isCore);
|
||||
case 'umd':
|
||||
if (!(host instanceof UmdReflectionHost)) {
|
||||
throw new Error('UmdRenderer requires a UmdReflectionHost');
|
||||
}
|
||||
return new UmdRenderingFormatter(host, isCore);
|
||||
case 'commonjs':
|
||||
return new CommonJsRenderingFormatter(host, isCore);
|
||||
default:
|
||||
throw new Error(`Renderer for "${bundle.format}" not yet implemented.`);
|
||||
}
|
||||
@ -102,8 +135,8 @@ export class Transformer {
|
||||
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
const decorationAnalyzer = new DecorationAnalyzer(
|
||||
bundle.src.program, bundle.src.options, bundle.src.host, typeChecker, reflectionHost,
|
||||
referencesRegistry, bundle.rootDirs, isCore);
|
||||
this.fs, bundle.src.program, bundle.src.options, bundle.src.host, typeChecker,
|
||||
reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
|
||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram();
|
||||
|
||||
const moduleWithProvidersAnalyzer =
|
||||
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {dirname, relative} from 'canonical-path';
|
||||
import * as ts from 'typescript';
|
||||
import MagicString from 'magic-string';
|
||||
import {Import, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {isRequireCall} from '../host/commonjs_host';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {Esm5RenderingFormatter} from './esm5_rendering_formatter';
|
||||
import {stripExtension} from './utils';
|
||||
|
||||
/**
|
||||
* A RenderingFormatter that works with CommonJS files, instead of `import` and `export` statements
|
||||
* the module is an IIFE with a factory function call with dependencies, which are defined in a
|
||||
* wrapper function for AMD, CommonJS and global module formats.
|
||||
*/
|
||||
export class CommonJsRenderingFormatter extends Esm5RenderingFormatter {
|
||||
constructor(protected commonJsHost: NgccReflectionHost, isCore: boolean) {
|
||||
super(commonJsHost, isCore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the imports below any in situ imports as `require` calls.
|
||||
*/
|
||||
addImports(output: MagicString, imports: Import[], file: ts.SourceFile): void {
|
||||
const insertionPoint = this.findEndOfImports(file);
|
||||
const renderedImports =
|
||||
imports.map(i => `var ${i.qualifier} = require('${i.specifier}');\n`).join('');
|
||||
output.appendLeft(insertionPoint, renderedImports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the exports to the bottom of the file.
|
||||
*/
|
||||
addExports(
|
||||
output: MagicString, entryPointBasePath: string, exports: ExportInfo[],
|
||||
importManager: ImportManager, file: ts.SourceFile): void {
|
||||
exports.forEach(e => {
|
||||
const basePath = stripExtension(e.from);
|
||||
const relativePath = './' + relative(dirname(entryPointBasePath), basePath);
|
||||
const namedImport = entryPointBasePath !== basePath ?
|
||||
importManager.generateNamedImport(relativePath, e.identifier) :
|
||||
{symbol: e.identifier, moduleImport: null};
|
||||
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
||||
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
||||
output.append(exportStr);
|
||||
});
|
||||
}
|
||||
|
||||
protected findEndOfImports(sf: ts.SourceFile): number {
|
||||
for (const statement of sf.statements) {
|
||||
if (ts.isExpressionStatement(statement) && isRequireCall(statement.expression)) {
|
||||
continue;
|
||||
}
|
||||
const declarations = ts.isVariableStatement(statement) ?
|
||||
Array.from(statement.declarationList.declarations) :
|
||||
[];
|
||||
if (declarations.some(d => !d.initializer || !isRequireCall(d.initializer))) {
|
||||
return statement.getStart();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
161
packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts
Normal file
161
packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {translateType, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {FileToWrite, getImportRewriter} from './utils';
|
||||
import {RenderingFormatter} from './rendering_formatter';
|
||||
import {extractSourceMap, renderSourceAndMap} from './source_maps';
|
||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
|
||||
/**
|
||||
* A structure that captures information about what needs to be rendered
|
||||
* in a typings file.
|
||||
*
|
||||
* It is created as a result of processing the analysis passed to the renderer.
|
||||
*
|
||||
* The `renderDtsFile()` method consumes it when rendering a typings file.
|
||||
*/
|
||||
class DtsRenderInfo {
|
||||
classInfo: DtsClassInfo[] = [];
|
||||
moduleWithProviders: ModuleWithProvidersInfo[] = [];
|
||||
privateExports: ExportInfo[] = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about a class in a typings file.
|
||||
*/
|
||||
export interface DtsClassInfo {
|
||||
dtsDeclaration: ts.Declaration;
|
||||
compilation: CompileResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A base-class for rendering an `AnalyzedFile`.
|
||||
*
|
||||
* Package formats have output files that must be rendered differently. Concrete sub-classes must
|
||||
* implement the `addImports`, `addDefinitions` and `removeDecorators` abstract methods.
|
||||
*/
|
||||
export class DtsRenderer {
|
||||
constructor(
|
||||
private dtsFormatter: RenderingFormatter, private fs: FileSystem, private logger: Logger,
|
||||
private host: NgccReflectionHost, private isCore: boolean, private bundle: EntryPointBundle) {
|
||||
}
|
||||
|
||||
renderProgram(
|
||||
decorationAnalyses: DecorationAnalyses,
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null): FileToWrite[] {
|
||||
const renderedFiles: FileToWrite[] = [];
|
||||
|
||||
// Transform the .d.ts files
|
||||
if (this.bundle.dts) {
|
||||
const dtsFiles = this.getTypingsFilesToRender(
|
||||
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
||||
|
||||
// If the dts entry-point is not already there (it did not have compiled classes)
|
||||
// then add it now, to ensure it gets its extra exports rendered.
|
||||
if (!dtsFiles.has(this.bundle.dts.file)) {
|
||||
dtsFiles.set(this.bundle.dts.file, new DtsRenderInfo());
|
||||
}
|
||||
dtsFiles.forEach(
|
||||
(renderInfo, file) => renderedFiles.push(...this.renderDtsFile(file, renderInfo)));
|
||||
}
|
||||
|
||||
return renderedFiles;
|
||||
}
|
||||
|
||||
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileToWrite[] {
|
||||
const input = extractSourceMap(this.fs, this.logger, dtsFile);
|
||||
const outputText = new MagicString(input.source);
|
||||
const printer = ts.createPrinter();
|
||||
const importManager = new ImportManager(
|
||||
getImportRewriter(this.bundle.dts !.r3SymbolsFile, this.isCore, false), IMPORT_PREFIX);
|
||||
|
||||
renderInfo.classInfo.forEach(dtsClass => {
|
||||
const endOfClass = dtsClass.dtsDeclaration.getEnd();
|
||||
dtsClass.compilation.forEach(declaration => {
|
||||
const type = translateType(declaration.type, importManager);
|
||||
const typeStr = printer.printNode(ts.EmitHint.Unspecified, type, dtsFile);
|
||||
const newStatement = ` static ${declaration.name}: ${typeStr};\n`;
|
||||
outputText.appendRight(endOfClass - 1, newStatement);
|
||||
});
|
||||
});
|
||||
|
||||
this.dtsFormatter.addModuleWithProvidersParams(
|
||||
outputText, renderInfo.moduleWithProviders, importManager);
|
||||
this.dtsFormatter.addExports(
|
||||
outputText, dtsFile.fileName, renderInfo.privateExports, importManager, dtsFile);
|
||||
this.dtsFormatter.addImports(
|
||||
outputText, importManager.getAllImports(dtsFile.fileName), dtsFile);
|
||||
|
||||
|
||||
|
||||
return renderSourceAndMap(dtsFile, input, outputText);
|
||||
}
|
||||
|
||||
private getTypingsFilesToRender(
|
||||
decorationAnalyses: DecorationAnalyses,
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|
|
||||
null): Map<ts.SourceFile, DtsRenderInfo> {
|
||||
const dtsMap = new Map<ts.SourceFile, DtsRenderInfo>();
|
||||
|
||||
// Capture the rendering info from the decoration analyses
|
||||
decorationAnalyses.forEach(compiledFile => {
|
||||
compiledFile.compiledClasses.forEach(compiledClass => {
|
||||
const dtsDeclaration = this.host.getDtsDeclaration(compiledClass.declaration);
|
||||
if (dtsDeclaration) {
|
||||
const dtsFile = dtsDeclaration.getSourceFile();
|
||||
const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile) ! : new DtsRenderInfo();
|
||||
renderInfo.classInfo.push({dtsDeclaration, compilation: compiledClass.compilation});
|
||||
dtsMap.set(dtsFile, renderInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Capture the ModuleWithProviders functions/methods that need updating
|
||||
if (moduleWithProvidersAnalyses !== null) {
|
||||
moduleWithProvidersAnalyses.forEach((moduleWithProvidersToFix, dtsFile) => {
|
||||
const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile) ! : new DtsRenderInfo();
|
||||
renderInfo.moduleWithProviders = moduleWithProvidersToFix;
|
||||
dtsMap.set(dtsFile, renderInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Capture the private declarations that need to be re-exported
|
||||
if (privateDeclarationsAnalyses.length) {
|
||||
privateDeclarationsAnalyses.forEach(e => {
|
||||
if (!e.dtsFrom && !e.alias) {
|
||||
throw new Error(
|
||||
`There is no typings path for ${e.identifier} in ${e.from}.\n` +
|
||||
`We need to add an export for this class to a .d.ts typings file because ` +
|
||||
`Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` +
|
||||
`The simplest fix for this is to ensure that this class is exported from the package's entry-point.`);
|
||||
}
|
||||
});
|
||||
const dtsEntryPoint = this.bundle.dts !.file;
|
||||
const renderInfo =
|
||||
dtsMap.has(dtsEntryPoint) ? dtsMap.get(dtsEntryPoint) ! : new DtsRenderInfo();
|
||||
renderInfo.privateExports = privateDeclarationsAnalyses;
|
||||
dtsMap.set(dtsEntryPoint, renderInfo);
|
||||
}
|
||||
|
||||
return dtsMap;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user