Compare commits
90 Commits
8.0.0-beta
...
g3
Author | SHA1 | Date | |
---|---|---|---|
c34c223122 | |||
26a8c5961e | |||
41225289d7 | |||
3612ddb433 | |||
09fab58109 | |||
b3102b9de1 | |||
cfe6581fc8 | |||
bef5043a5a | |||
b5295ad277 | |||
769d960db1 | |||
21be0fb926 | |||
9bcc1e8dce | |||
a06f0340d2 | |||
66b72bfa58 | |||
00075647be | |||
ce789b75a4 | |||
7baf45fe88 | |||
f3e0cc89ed | |||
cfa6e8e008 | |||
02debebcff | |||
ed8d60d060 | |||
9ea0d64d8b | |||
dfcf759e33 | |||
21835af70c | |||
2790352d04 | |||
bb6a3632f6 | |||
2d859a8c3a | |||
70fffba054 | |||
e185d3a4ad | |||
6f3052b799 | |||
c18fa7b5bd | |||
693b350567 | |||
0b27c09b51 | |||
861d6f1523 | |||
dc10355d61 | |||
6cd3743b44 | |||
a24f4b51b3 | |||
84baa0bb08 | |||
e721c08c7f | |||
37a154e4e6 | |||
415de9a291 | |||
b3dda0ebc1 | |||
41737bb4d3 | |||
afe3e72601 | |||
5e3bbf79a6 | |||
d2b64cc008 | |||
9eb8274991 | |||
17b3f11e07 | |||
10734ac607 | |||
68a9fe817c | |||
64e5628897 | |||
849b327986 | |||
a827bc2e3a | |||
083fb99033 | |||
55ea8da6eb | |||
7ea0d1bd3a | |||
bdcbd9ed4b | |||
b48d6e1b13 | |||
68f9d705f8 | |||
229f035969 | |||
c9f7cdaafd | |||
7b55ba58b9 | |||
cd449021c1 | |||
4bb0259bc0 | |||
66239b9d09 | |||
a770aa231d | |||
cf4718c366 | |||
07aeafa75c | |||
7a67f8935d | |||
bc88816c10 | |||
0d0445063a | |||
ddfdf3cd26 | |||
d6d081e120 | |||
38c778e371 | |||
3249020466 | |||
d6e27a41ed | |||
1a4d4a0e13 | |||
3121409957 | |||
fd122b0739 | |||
9a364a82fb | |||
a530ed11e8 | |||
2d7435daae | |||
b460b26308 | |||
ea90435a6b | |||
ea0e832e5f | |||
7c4afb0da7 | |||
603df13b14 | |||
f9b7b6d2f1 | |||
dafbbf8b64 | |||
d59f02d902 |
@ -573,24 +573,30 @@ jobs:
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run:
|
||||
name: "Determining SHA of Material Ivy branch"
|
||||
command: git ls-remote ${MATERIAL_REPO_URL} ${MATERIAL_REPO_BRANCH} | cut -f1 > material_repo_sha
|
||||
- run:
|
||||
name: "Cloning Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
- restore_cache:
|
||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
||||
keys:
|
||||
- v1-angular-material-{{ checksum "material_repo_sha" }}
|
||||
- v1-angular-material-
|
||||
- v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
||||
- v2-angular-material-
|
||||
- run:
|
||||
name: Installing Material dependencies.
|
||||
command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
|
||||
# Save the cache before we run the Material unit tests script. This is necessary
|
||||
# because we don't want to cache the node modules which have been modified to contain
|
||||
# the attached Ivy package output.
|
||||
- save_cache:
|
||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
||||
key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
||||
paths:
|
||||
- "/tmp/material2/node_modules"
|
||||
- run:
|
||||
name: "Running Material unit tests"
|
||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
||||
- save_cache:
|
||||
key: v1-angular-material-{{ checksum "material_repo_sha" }}
|
||||
paths:
|
||||
# Needs to be hardcoded because environment variables are not interpolated here.
|
||||
- "/tmp/material2/node_modules"
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
103
.codefresh/Dockerfile.win-1809
Normal file
@ -0,0 +1,103 @@
|
||||
ARG core=mcr.microsoft.com/windows/servercore:1809
|
||||
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
|
||||
|
||||
FROM $core as download
|
||||
|
||||
ARG node_version=10.13.0
|
||||
ARG yarn_version=1.13.0
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
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 ; \
|
||||
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 $_ ; \
|
||||
}
|
||||
|
||||
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 ; \
|
||||
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:\ ; \
|
||||
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' } ; \
|
||||
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; \
|
||||
Remove-Item git.zip
|
||||
|
||||
FROM $target as baseimage
|
||||
|
||||
ENV NPM_CONFIG_LOGLEVEL info
|
||||
|
||||
COPY --from=download /nodejs /nodejs
|
||||
COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ]
|
||||
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" ]
|
||||
|
||||
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
|
||||
|
||||
# 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"]
|
33
.codefresh/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# CodeFresh configuration
|
||||
|
||||
[](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
||||
|
||||
This folder contains configuration for the [CodeFresh](<https://codefresh.io/>) based CI checks for this repository.
|
||||
|
||||
## The build pipeline
|
||||
|
||||
CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository.
|
||||
|
||||
Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
||||
|
||||
Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access.
|
||||
|
||||
|
||||
## Caretaker
|
||||
|
||||
CodeFresh status can be found at <http://status.codefresh.io/>.
|
||||
|
||||
Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva.
|
||||
|
||||
## Rollout strategy
|
||||
|
||||
Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular).
|
||||
|
||||
After a week or two of running like this, we should reassess how stable and reliable it is.
|
||||
|
||||
Next steps include:
|
||||
- building PRs
|
||||
- showing build status publicly
|
||||
- blocking PRs that break the build
|
||||
- expanding the test suite
|
||||
|
40
.codefresh/bazel.rc
Normal file
@ -0,0 +1,40 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CodeFresh. This helps us
|
||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||
# build --repository_cache=C:/codefresh/volume/bazel_repository_cache
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
# Presently, CircleCI can timeout during bazel test ... with the following
|
||||
# error: Too long with no output (exceeded 10m0s)
|
||||
# build --noshow_progress
|
||||
|
||||
# Print all the options that apply to the build.
|
||||
# This helps us diagnose which options override others
|
||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
||||
build --announce_rc
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CodeFresh VMs
|
||||
# TODO(filipesilva): determine the correct memory limit
|
||||
build --local_resources=8000,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
|
26
.codefresh/codefresh.yml
Normal file
@ -0,0 +1,26 @@
|
||||
version: '1.0'
|
||||
|
||||
steps:
|
||||
BuildImage:
|
||||
type: build
|
||||
image_name: node-bazel-windows
|
||||
working_directory: ./.codefresh
|
||||
no_cf_cache: true
|
||||
build_arguments:
|
||||
- node_version=10.13.0
|
||||
- yarn_version=1.13.0
|
||||
dockerfile: ./Dockerfile.win-1809
|
||||
|
||||
RunTests:
|
||||
title: Run Example
|
||||
image: ${{BuildImage}}
|
||||
commands:
|
||||
# Install dependencies
|
||||
- yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress
|
||||
# Create symlinks needed for Windows.
|
||||
- scripts\windows\create-symlinks.cmd
|
||||
# Add Bazel CI config
|
||||
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
|
||||
# Run tests
|
||||
- yarn bazel test //tools/ts-api-guardian:all
|
||||
- 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
|
6
.codefresh/fix-msys64.cmd
Normal file
@ -0,0 +1,6 @@
|
||||
@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."
|
4
.github/CODEOWNERS
vendored
@ -46,6 +46,7 @@
|
||||
# andrewseguin - Andrew Seguin
|
||||
# benlesh - Ben Lesh
|
||||
# brandonroberts - Brandon Roberts
|
||||
# devversion - Paul Gschwendtner
|
||||
# filipesilva - Filipe Silva
|
||||
# gkalpak - George Kalpakas
|
||||
# hansl - Hans Larsen
|
||||
@ -87,6 +88,7 @@
|
||||
# - IgorMinar
|
||||
# - kara
|
||||
# - mhevery
|
||||
# - alexeagle
|
||||
|
||||
|
||||
# ===========================================================
|
||||
@ -325,6 +327,7 @@
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - devversion
|
||||
# - filipesilva
|
||||
# - gkalpak
|
||||
# - IgorMinar
|
||||
@ -818,6 +821,7 @@ testing/** @angular/fw-test
|
||||
/* @angular/fw-dev-infra
|
||||
/.buildkite/** @angular/fw-dev-infra
|
||||
/.circleci/** @angular/fw-dev-infra
|
||||
/.codefresh/** @angular/fw-dev-infra
|
||||
/.github/** @angular/fw-dev-infra
|
||||
/.vscode/** @angular/fw-dev-infra
|
||||
/docs/BAZEL.md @angular/fw-dev-infra
|
||||
|
3
.vscode/settings.json
vendored
@ -16,9 +16,6 @@
|
||||
"**/dist/**": true,
|
||||
"**/aio/src/generated/**": true,
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"statusBar.background" : "#CB2B38",
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/bower_components": true,
|
||||
|
14
CHANGELOG.md
@ -23,7 +23,7 @@
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **core:** Certain elements (like `<tr>` or `<col>`) require parent elements to be of a certain type by the HTML specification
|
||||
(ex. <tr> can only be inside <tbody> / <thead>). Before this change Angular template parser was auto-correcting
|
||||
(ex. `<tr>` can only be inside `<tbody>` / `<thead>`). Before this change Angular template parser was auto-correcting
|
||||
"invalid" HTML using the following rules:
|
||||
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
|
||||
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
|
||||
@ -38,7 +38,7 @@ This meachanism of automatic wrapping / auto-correcting was problematic for seve
|
||||
</projecting-tr-inside-tbody>
|
||||
```
|
||||
|
||||
In the above example the `<projecting-tr-inside-tbody>` component culd be "surprised" to see additional
|
||||
In the above example the `<projecting-tr-inside-tbody>` component could be "surprised" to see additional
|
||||
`<tbody>` elements inserted by Angular HTML parser.
|
||||
|
||||
|
||||
@ -48,6 +48,16 @@ Please update your TypeScript version to 3.3
|
||||
|
||||
|
||||
|
||||
<a name="7.2.10"></a>
|
||||
## [7.2.10](https://github.com/angular/angular/compare/7.2.9...7.2.10) (2019-03-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **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//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
|
||||
|
||||
|
||||
|
||||
<a name="8.0.0-beta.8"></a>
|
||||
# [8.0.0-beta.8](https://github.com/angular/angular/compare/8.0.0-beta.7...8.0.0-beta.8) (2019-03-12)
|
||||
|
||||
|
14
WORKSPACE
@ -15,8 +15,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.4/rules_nodejs-0.27.4.tar.gz"],
|
||||
sha256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.7/rules_nodejs-0.27.7.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the bazel version and download npm dependencies
|
||||
@ -48,7 +48,15 @@ node_repositories(
|
||||
node_version = "10.9.0",
|
||||
package_json = ["//:package.json"],
|
||||
preserve_symlinks = True,
|
||||
yarn_version = "1.13.0",
|
||||
# yarn 1.13.0 under Bazel has a regression on Windows that causes build errors on rebuilds:
|
||||
# ```
|
||||
# ERROR: Source forest creation failed: C:/.../fyuc5c3n/execroot/angular/external (Directory not empty)
|
||||
# ```
|
||||
# See https://github.com/angular/angular/pull/29431 for more information.
|
||||
# It possible that versions of yarn past 1.13.0 do not have this issue, however, before
|
||||
# advancing this version we need to test manually on Windows that the above error does not
|
||||
# happen as the issue is not caught by CI.
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
|
59
aio/content/guide/app-shell.md
Normal file
@ -0,0 +1,59 @@
|
||||
# App shell
|
||||
|
||||
App shell is a way to render a portion of your application via a route at build time.
|
||||
It can improve the user experience by quickly launching a static rendered page (a skeleton common to all pages) while the browser downloads the full client version and switches to it automatically after the code loads.
|
||||
|
||||
This gives users a meaningful first paint of your application that appears quickly because the browser can simply render the HTML and CSS without the need to initialize any JavaScript.
|
||||
|
||||
Learn more in [The App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell).
|
||||
|
||||
## Step 1: Prepare the application
|
||||
|
||||
You can do this with the following CLI command:
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng new my-app --routing
|
||||
</code-example>
|
||||
|
||||
For an existing application, you have to manually add the `RouterModule` and defining a `<router-outlet>` within your application.
|
||||
|
||||
## Step 2: Create the app shell
|
||||
|
||||
Use the CLI to automatically create the app shell.
|
||||
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng generate app-shell --client-project my-app --universal-project server-app
|
||||
</code-example>
|
||||
|
||||
* `my-app` takes the name of your client application.
|
||||
* `server-app` takes the name of the Universal (or server) application.
|
||||
|
||||
After running this command you will notice that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
"server": {
|
||||
"builder": "@angular-devkit/build-angular:server",
|
||||
"options": {
|
||||
"outputPath": "dist/my-app-server",
|
||||
"main": "src/main.server.ts",
|
||||
"tsConfig": "tsconfig.server.json"
|
||||
}
|
||||
},
|
||||
"app-shell": {
|
||||
"builder": "@angular-devkit/build-angular:app-shell",
|
||||
"options": {
|
||||
"browserTarget": "my-app:build",
|
||||
"serverTarget": "my-app:server",
|
||||
"route": "shell"
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
## Step 3: Verify the app is built with the shell content
|
||||
|
||||
Use the CLI to build the `app-shell` target.
|
||||
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng run my-app:app-shell
|
||||
</code-example>
|
||||
|
||||
To verify the build output, open `dist/my-app/index.html`. Look for default text `app-shell works!` to show that the app shell route was rendered as part of the output.
|
@ -158,7 +158,7 @@ You can <a href="generated/zips/cli-quickstart/cli-quickstart.zip" target="_blan
|
||||
</div>
|
||||
|
||||
|
||||
For more information about Angular project files and the file structure, see [Workspace and project file struture](guide/file-structure).
|
||||
For more information about Angular project files and the file structure, see [Workspace and project file structure](guide/file-structure).
|
||||
|
||||
|
||||
|
||||
|
@ -1749,447 +1749,6 @@ A consistent class and file name convention make these modules easy to spot and
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
|
||||
## Coding conventions
|
||||
|
||||
Have a consistent set of coding, naming, and whitespace conventions.
|
||||
|
||||
|
||||
|
||||
{@a 03-01}
|
||||
|
||||
### Classes
|
||||
|
||||
#### Style 03-01
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** use upper camel case when naming classes.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Follows conventional thinking for class names.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** Classes can be instantiated and construct an instance.
|
||||
By convention, upper camel case indicates a constructable asset.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-01/app/core/exception.service.avoid.ts" region="example" header="app/shared/exception.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-01/app/core/exception.service.ts" region="example" header="app/shared/exception.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
|
||||
{@a 03-02}
|
||||
|
||||
### Constants
|
||||
|
||||
#### Style 03-02
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** declare variables with `const` if their values should not change during the application lifetime.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Conveys to readers that the value is invariant.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** TypeScript helps enforce that intent by requiring immediate initialization and by
|
||||
preventing subsequent re-assignment.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** spelling `const` variables in lower camel case.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Lower camel case variable names (`heroRoutes`) are easier to read and understand
|
||||
than the traditional UPPER_SNAKE_CASE names (`HERO_ROUTES`).
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** The tradition of naming constants in UPPER_SNAKE_CASE reflects
|
||||
an era before the modern IDEs that quickly reveal the `const` declaration.
|
||||
TypeScript prevents accidental reassignment.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** tolerate _existing_ `const` variables that are spelled in UPPER_SNAKE_CASE.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** The tradition of UPPER_SNAKE_CASE remains popular and pervasive,
|
||||
especially in third party modules.
|
||||
It is rarely worth the effort to change them at the risk of breaking existing code and documentation.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-02/app/core/data.service.ts" header="app/shared/data.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
|
||||
{@a 03-03}
|
||||
|
||||
### Interfaces
|
||||
|
||||
#### Style 03-03
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** name an interface using upper camel case.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** naming an interface without an `I` prefix.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** using a class instead of an interface for services and declarables (components, directives, and pipes).
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** using an interface for data models.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** <a href="https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines">TypeScript guidelines</a>
|
||||
discourage the `I` prefix.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** A class alone is less code than a _class-plus-interface_.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** A class can act as an interface (use `implements` instead of `extends`).
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** An interface-class can be a provider lookup token in Angular dependency injection.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-03/app/core/hero-collector.service.avoid.ts" region="example" header="app/shared/hero-collector.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-03/app/core/hero-collector.service.ts" region="example" header="app/shared/hero-collector.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 03-04}
|
||||
|
||||
### Properties and methods
|
||||
|
||||
#### Style 03-04
|
||||
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** use lower camel case to name properties and methods.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule avoid">
|
||||
|
||||
|
||||
|
||||
**Avoid** prefixing private properties and methods with an underscore.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Follows conventional thinking for properties and methods.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** JavaScript lacks a true private property or method.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** TypeScript tooling makes it easy to identify private vs. public properties and methods.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-04/app/core/toast.service.avoid.ts" region="example" header="app/shared/toast.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-04/app/core/toast.service.ts" region="example" header="app/shared/toast.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 03-06}
|
||||
|
||||
### Import line spacing
|
||||
|
||||
#### Style 03-06
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** leaving one empty line between third party imports and application imports.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** listing import lines alphabetized by the module.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-rule consider">
|
||||
|
||||
|
||||
|
||||
**Consider** listing destructured imported symbols alphabetically.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** The empty line separates _your_ stuff from _their_ stuff.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** Alphabetizing makes it easier to read and locate symbols.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-06/app/heroes/shared/hero.service.avoid.ts" region="example" header="app/heroes/shared/hero.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/03-06/app/heroes/shared/hero.service.ts" region="example" header="app/heroes/shared/hero.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
|
||||
|
@ -289,7 +289,7 @@ written without assistance from Angular testing utilities.
|
||||
#### Services with dependencies
|
||||
|
||||
Services often depend on other services that Angular injects into the constructor.
|
||||
In many cases, it easy to create and _inject_ these dependencies by hand while
|
||||
In many cases, it's easy to create and _inject_ these dependencies by hand while
|
||||
calling the service's constructor.
|
||||
|
||||
The `MasterService` is a simple example:
|
||||
@ -318,7 +318,7 @@ Prefer spies as they are usually the easiest way to mock services.
|
||||
|
||||
These standard testing techniques are great for unit testing services in isolation.
|
||||
|
||||
However, you almost always inject service into application classes using Angular
|
||||
However, you almost always inject services into application classes using Angular
|
||||
dependency injection and you should have tests that reflect that usage pattern.
|
||||
Angular testing utilities make it easy to investigate how injected services behave.
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 14 KiB |
BIN
aio/content/images/bios/denny.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
aio/content/images/bios/jennifer.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
aio/content/images/bios/judy.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
aio/content/images/bios/justinschwartzenberger.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
aio/content/images/bios/kapunahele.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
aio/content/images/bios/luixaviles.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
aio/content/images/bios/sidd-ajmera.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
aio/content/images/bios/sreevani.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
@ -673,17 +673,86 @@
|
||||
"group": "Collaborator",
|
||||
"picture": "cexbrayat.jpg",
|
||||
"bio": "Author of `Become a ninja with Angular (2+)` https://books.ninja-squad.com/angular - Angular trainer and @Ninja-Squad co-founder"
|
||||
},
|
||||
},
|
||||
"CaerusKaru": {
|
||||
"name": "Adam Plumer",
|
||||
"group": "Collaborator",
|
||||
"mentor": "vikerman",
|
||||
"picture": "CaerusKaru.jpg"
|
||||
},
|
||||
},
|
||||
"jbedard": {
|
||||
"name": "Jason Bedard",
|
||||
"group": "Collaborator",
|
||||
"mentor": "kyliau",
|
||||
"picture": "jbedard.png"
|
||||
},
|
||||
"jschwarty": {
|
||||
"name": "Justin Schwartzenberger",
|
||||
"picture": "justinschwartzenberger.jpg",
|
||||
"twitter": "schwarty",
|
||||
"website": "https://schwarty.com",
|
||||
"bio": "Justin (aka Schwarty) is a Google Developer Expert in Web Technologies and Angular, the host and maintainer of the weekly AngularAir live video broadcast, educator, writer and content creator. He has Angular courses available on LinkedIn Learning and Pluralsight and loves passing on years of full stack development knowledge to help empower others to find their inner awesomeness!",
|
||||
"group": "GDE"
|
||||
},
|
||||
"brandonroberts": {
|
||||
"name": "Brandon Roberts",
|
||||
"picture": "brandonroberts.jpg",
|
||||
"twitter": "brandontroberts",
|
||||
"website": "https://brandonroberts.dev",
|
||||
"bio": "Brandon is a developer and technical writer working on guides, tutorials, application development, and infrastructure for the Angular docs. He is also a maintainer of the NgRx project, building reactive libraries for Angular.",
|
||||
"group": "Angular",
|
||||
"lead": "dennispbrown"
|
||||
},
|
||||
"chembu": {
|
||||
"name": "Sreevani Sreejith",
|
||||
"picture": "sreevani.jpg",
|
||||
"bio": "Sreevani is a tech writer with prior programming experience. She writes documentation for the Angular framework team. Outside of work, she likes practicing yoga, honing her skills on classical dance forms, and baking cakes.",
|
||||
"group": "Angular",
|
||||
"lead": "dennispbrown"
|
||||
},
|
||||
"dennispbrown": {
|
||||
"name": "Denny Brown",
|
||||
"picture": "denny.jpg",
|
||||
"bio": "Denny is founder of Expert Support, a professional services firm specializing in technical communication, and leads the Angular technical writing team. His lifelong passion has been to reduce the time and effort required to understand complex technical information. Early on, he was Associate Chairman of the Computer Science Department at Stanford, where he taught introductory courses in programming. He also plays old-timers baseball in local leagues and national tournaments.",
|
||||
"group": "Angular",
|
||||
"lead": "bradlygreen"
|
||||
},
|
||||
"jbogarthyde": {
|
||||
"name": "Judy Bogart",
|
||||
"picture": "judy.png",
|
||||
"group": "Angular",
|
||||
"lead": "dennispbrown"
|
||||
},
|
||||
"jenniferfell": {
|
||||
"name": "Jennifer Fell",
|
||||
"picture": "jennifer.jpg",
|
||||
"website": "http://silverpath.org",
|
||||
"bio": "Jennifer is a technical content strategist, architect, designer, and writer. As lead of the Angular docs team, she's always interested in learning more about how developers learn and use Angular. Her offline persona is a horsewoman in Idaho.",
|
||||
"group": "Angular",
|
||||
"lead": "dennispbrown"
|
||||
},
|
||||
"kapunahelewong": {
|
||||
"name": "Kapunahele Wong",
|
||||
"picture": "kapunahele.jpg",
|
||||
"twitter": "kapunahele",
|
||||
"bio": "Kapunahele is a developer and Angular fan who works on the Angular docs writing guides and developing example apps. She also enjoys Native Hawaiian practices, textile arts, and marveling at little, inconspicuous plants growing in forgotten places outdoors.",
|
||||
"group": "Angular",
|
||||
"lead": "dennispbrown"
|
||||
},
|
||||
"luixaviles": {
|
||||
"name": "Luis Aviles",
|
||||
"picture": "luixaviles.jpg",
|
||||
"twitter": "luixaviles",
|
||||
"website": "https://luixaviles.com",
|
||||
"bio": "Luis is an enthusiast of Open Source software and communities, as well as being a public speaker, a technology trainer and an author of courses and technical articles. He is the organizer of the Angular Bolivia community and NG Bolivia conference. When he's not coding, Luis is reading about Astronomy or nerding about outer space, photography or even doing Astrophotography.",
|
||||
"group": "GDE"
|
||||
},
|
||||
"siddajmera": {
|
||||
"name": "Siddharth Ajmera",
|
||||
"picture": "sidd-ajmera.jpg",
|
||||
"twitter": "SiddAjmera",
|
||||
"website": "https://webstackup.com/",
|
||||
"bio": "Siddharth is a Full Stack JavaScript Developer and a GDE in Angular. He's passionate about sharing his knowledge on Angular, Firebase and the Web in general. He's the organizer of WebStack, a local community of developers focused on Web, Mobile, Voice and Server related technologies in general. WebStack hosts free monthly meetups every 2nd or 3rd Saturday of the month. Siddharth is also an avid photographer and loves traveling. Find him anywhere on the Web with `SiddAjmera`.",
|
||||
"group": "GDE"
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,12 @@
|
||||
<td>London, UK</td>
|
||||
<td>September 19-20, 2019</td>
|
||||
</tr>
|
||||
<!-- NG-DE 2019-->
|
||||
<tr>
|
||||
<th><a href="https://ng-de.org/" title="NG-DE">NG-DE</a></th>
|
||||
<td>Berlin, Germany</td>
|
||||
<td>August 29th workshops, 30-31 conference, 2019</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -439,6 +439,11 @@
|
||||
"title": "Getting Started",
|
||||
"tooltip": "Enabling the service worker in a CLI project and observing behavior in the browser."
|
||||
},
|
||||
{
|
||||
"url": "guide/app-shell",
|
||||
"title": "App Shell",
|
||||
"tooltip": "Enabling the App Shell in a CLI project."
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-communications",
|
||||
"title": "Service Worker Communication",
|
||||
|
@ -17,9 +17,9 @@
|
||||
"build": "yarn ~~build",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-with-ivy": "yarn setup-local && yarn ivy-ngcc --formats fesm2015 fesm5",
|
||||
"prebuild-with-ivy": "yarn setup-local && yarn ivy-ngcc --properties module es2015",
|
||||
"build-with-ivy": "node scripts/build-with-ivy",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js cafa558cf",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 18d979cdc",
|
||||
"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",
|
||||
|
@ -12,80 +12,36 @@ const EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
|
||||
|
||||
const BOILERPLATE_PATHS = {
|
||||
cli: [
|
||||
'src/environments/environment.prod.ts',
|
||||
'src/environments/environment.ts',
|
||||
'src/assets/.gitkeep',
|
||||
'src/browserslist',
|
||||
'src/favicon.ico',
|
||||
'src/karma.conf.js',
|
||||
'src/polyfills.ts',
|
||||
'src/test.ts',
|
||||
'src/tsconfig.app.json',
|
||||
'src/tsconfig.spec.json',
|
||||
'src/tslint.json',
|
||||
'e2e/src/app.po.ts',
|
||||
'e2e/protractor.conf.js',
|
||||
'e2e/tsconfig.e2e.json',
|
||||
'.editorconfig',
|
||||
'angular.json',
|
||||
'package.json',
|
||||
'tsconfig.json',
|
||||
'tslint.json'
|
||||
'src/environments/environment.prod.ts', 'src/environments/environment.ts',
|
||||
'src/assets/.gitkeep', 'src/browserslist', 'src/favicon.ico', 'src/karma.conf.js',
|
||||
'src/polyfills.ts', 'src/test.ts', 'src/tsconfig.app.json', 'src/tsconfig.spec.json',
|
||||
'src/tslint.json', 'e2e/src/app.po.ts', 'e2e/protractor.conf.js', 'e2e/tsconfig.e2e.json',
|
||||
'.editorconfig', 'angular.json', 'package.json', 'tsconfig.json', 'tslint.json'
|
||||
],
|
||||
systemjs: [
|
||||
'src/systemjs-angular-loader.js',
|
||||
'src/systemjs.config.js',
|
||||
'src/tsconfig.json',
|
||||
'bs-config.json',
|
||||
'bs-config.e2e.json',
|
||||
'package.json',
|
||||
'tslint.json'
|
||||
'src/systemjs-angular-loader.js', 'src/systemjs.config.js', 'src/tsconfig.json',
|
||||
'bs-config.json', 'bs-config.e2e.json', 'package.json', 'tslint.json'
|
||||
],
|
||||
common: [
|
||||
'src/styles.css'
|
||||
]
|
||||
common: ['src/styles.css']
|
||||
};
|
||||
|
||||
// All paths in this tool are relative to the current boilerplate folder, i.e boilerplate/i18n
|
||||
// This maps the CLI files that exists in a parent folder
|
||||
const cliRelativePath = BOILERPLATE_PATHS.cli.map(file => `../cli/${file}`);
|
||||
|
||||
BOILERPLATE_PATHS.elements = [
|
||||
...cliRelativePath,
|
||||
'tsconfig.json'
|
||||
];
|
||||
BOILERPLATE_PATHS.elements = [...cliRelativePath, 'tsconfig.json'];
|
||||
|
||||
BOILERPLATE_PATHS.i18n = [
|
||||
...cliRelativePath,
|
||||
'angular.json',
|
||||
'package.json'
|
||||
];
|
||||
BOILERPLATE_PATHS.i18n = [...cliRelativePath, 'angular.json', 'package.json'];
|
||||
|
||||
BOILERPLATE_PATHS['service-worker'] = [
|
||||
...cliRelativePath,
|
||||
'angular.json',
|
||||
'package.json'
|
||||
];
|
||||
BOILERPLATE_PATHS['service-worker'] = [...cliRelativePath, 'angular.json', 'package.json'];
|
||||
|
||||
BOILERPLATE_PATHS.testing = [
|
||||
...cliRelativePath,
|
||||
'angular.json'
|
||||
];
|
||||
BOILERPLATE_PATHS.testing = [...cliRelativePath, 'angular.json'];
|
||||
|
||||
BOILERPLATE_PATHS.universal = [
|
||||
...cliRelativePath,
|
||||
'angular.json',
|
||||
'package.json'
|
||||
];
|
||||
BOILERPLATE_PATHS.universal = [...cliRelativePath, 'angular.json', 'package.json'];
|
||||
|
||||
BOILERPLATE_PATHS.ivy = {
|
||||
systemjs: [
|
||||
'rollup-config.js',
|
||||
'tsconfig-aot.json'
|
||||
],
|
||||
cli: [
|
||||
'src/tsconfig.app.json'
|
||||
]
|
||||
systemjs: ['rollup-config.js', 'tsconfig-aot.json'],
|
||||
cli: ['src/tsconfig.app.json']
|
||||
};
|
||||
|
||||
BOILERPLATE_PATHS.schematics = [
|
||||
@ -101,20 +57,19 @@ class ExampleBoilerPlate {
|
||||
*/
|
||||
add(ivy = false) {
|
||||
// Get all the examples folders, indicated by those that contain a `example-config.json` file
|
||||
const exampleFolders = this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
|
||||
const exampleFolders =
|
||||
this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
|
||||
|
||||
if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) {
|
||||
throw new Error(`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` +
|
||||
`Perhaps you need to run "yarn example-use-npm" or "yarn example-use-local" to install the dependencies?`);
|
||||
throw new Error(
|
||||
`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` +
|
||||
`Perhaps you need to run "yarn example-use-npm" or "yarn example-use-local" to install the dependencies?`);
|
||||
}
|
||||
|
||||
if (ivy) {
|
||||
// We only need the "fesm5" bundles as the CLI webpack build does not need
|
||||
// any other formats for building and serving. Ngcc currently only updates
|
||||
// the module typings if we specified an "es2015" format. This means that
|
||||
// we also need to build with "fesm2015" in order to get updated typings
|
||||
// which are needed for compilation.
|
||||
shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --formats fesm2015 fesm5`);
|
||||
// any other formats for building and serving.
|
||||
shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --properties module`);
|
||||
}
|
||||
|
||||
exampleFolders.forEach(exampleFolder => {
|
||||
@ -128,16 +83,20 @@ class ExampleBoilerPlate {
|
||||
const boilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, boilerPlateType);
|
||||
|
||||
// Copy the boilerplate specific files
|
||||
BOILERPLATE_PATHS[boilerPlateType].forEach(filePath => this.copyFile(boilerPlateBasePath, exampleFolder, filePath));
|
||||
BOILERPLATE_PATHS[boilerPlateType].forEach(
|
||||
filePath => this.copyFile(boilerPlateBasePath, exampleFolder, filePath));
|
||||
|
||||
// Copy the boilerplate common files
|
||||
BOILERPLATE_PATHS.common.forEach(filePath => this.copyFile(BOILERPLATE_COMMON_BASE_PATH, exampleFolder, filePath));
|
||||
BOILERPLATE_PATHS.common.forEach(
|
||||
filePath => this.copyFile(BOILERPLATE_COMMON_BASE_PATH, exampleFolder, filePath));
|
||||
|
||||
// Copy Ivy specific files
|
||||
if (ivy) {
|
||||
const ivyBoilerPlateType = boilerPlateType === 'systemjs' ? 'systemjs' : 'cli';
|
||||
const ivyBoilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, 'ivy', ivyBoilerPlateType);
|
||||
BOILERPLATE_PATHS.ivy[ivyBoilerPlateType].forEach(filePath => this.copyFile(ivyBoilerPlateBasePath, exampleFolder, filePath));
|
||||
const ivyBoilerPlateBasePath =
|
||||
path.resolve(BOILERPLATE_BASE_PATH, 'ivy', ivyBoilerPlateType);
|
||||
BOILERPLATE_PATHS.ivy[ivyBoilerPlateType].forEach(
|
||||
filePath => this.copyFile(ivyBoilerPlateBasePath, exampleFolder, filePath));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -145,23 +104,20 @@ class ExampleBoilerPlate {
|
||||
/**
|
||||
* Remove all the boilerplate files from all the examples
|
||||
*/
|
||||
remove() {
|
||||
shelljs.exec('git clean -xdfq', { cwd: EXAMPLES_BASE_PATH });
|
||||
}
|
||||
remove() { shelljs.exec('git clean -xdfq', {cwd: EXAMPLES_BASE_PATH}); }
|
||||
|
||||
main() {
|
||||
yargs
|
||||
.usage('$0 <cmd> [args]')
|
||||
.command('add', 'add the boilerplate to each example', (yrgs) => this.add(yrgs.argv.ivy))
|
||||
.command('remove', 'remove the boilerplate from each example', () => this.remove())
|
||||
.demandCommand(1, 'Please supply a command from the list above')
|
||||
.argv;
|
||||
yargs.usage('$0 <cmd> [args]')
|
||||
.command('add', 'add the boilerplate to each example', (yrgs) => this.add(yrgs.argv.ivy))
|
||||
.command('remove', 'remove the boilerplate from each example', () => this.remove())
|
||||
.demandCommand(1, 'Please supply a command from the list above')
|
||||
.argv;
|
||||
}
|
||||
|
||||
getFoldersContaining(basePath, filename, ignore) {
|
||||
const pattern = path.resolve(basePath, '**', filename);
|
||||
const ignorePattern = path.resolve(basePath, '**', ignore, '**');
|
||||
return glob.sync(pattern, { ignore: [ignorePattern] }).map(file => path.dirname(file));
|
||||
return glob.sync(pattern, {ignore: [ignorePattern]}).map(file => path.dirname(file));
|
||||
}
|
||||
|
||||
copyFile(sourceFolder, destinationFolder, filePath) {
|
||||
@ -171,13 +127,11 @@ class ExampleBoilerPlate {
|
||||
filePath = this.normalizePath(filePath);
|
||||
|
||||
const destinationPath = path.resolve(destinationFolder, filePath);
|
||||
fs.copySync(sourcePath, destinationPath, { overwrite: true });
|
||||
fs.copySync(sourcePath, destinationPath, {overwrite: true});
|
||||
fs.chmodSync(destinationPath, 444);
|
||||
}
|
||||
|
||||
loadJsonFile(filePath) {
|
||||
return fs.readJsonSync(filePath, { throws: false }) || {};
|
||||
}
|
||||
loadJsonFile(filePath) { return fs.readJsonSync(filePath, {throws: false}) || {}; }
|
||||
|
||||
normalizePath(filePath) {
|
||||
// transform for example ../cli/src/tsconfig.app.json to src/tsconfig.app.json
|
||||
|
@ -85,12 +85,15 @@ To build Angular run:
|
||||
|
||||
Bazel is used as the primary tool for building and testing Angular. Building and testing is
|
||||
incremental with Bazel, and it's possible to only run tests for an individual package instead
|
||||
of for all packages.
|
||||
of for all packages. Read more about this in the [BAZEL.md](./BAZEL.md) document.
|
||||
|
||||
Read more about this in the [BAZEL.md](./BAZEL.md) document. You should execute all test suites
|
||||
before submitting a PR to Github.
|
||||
You should execute all test suites before submitting a PR to GitHub:
|
||||
- `yarn bazel test packages/...`
|
||||
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be
|
||||
**Note**: The first test run will be much slower than future runs. This is because future runs will
|
||||
benefit from Bazel's capability to do incremental builds.
|
||||
|
||||
All the tests are executed on our Continuous Integration infrastructure. PRs can only be
|
||||
merged if the code is formatted properly and all tests are passing.
|
||||
|
||||
## <a name="clang-format"></a> Formatting your source code
|
||||
@ -109,6 +112,13 @@ A better way is to set up your IDE to format the changed file on each file save.
|
||||
|
||||
It will automatically pick up the settings from Angular's [settings.json](../.vscode/settings.json).
|
||||
|
||||
### WebStorm / IntelliJ
|
||||
1. Install the [ClangFormatIJ](https://plugins.jetbrains.com/plugin/8396-clangformatij) plugin
|
||||
1. Open `Preferences->Tools->clang-format`
|
||||
1. Find the field named "PATH"
|
||||
1. Add `<PATH_TO_YOUR_WORKSPACE>/angular/node_modules/clang-format/bin/<OS>/`
|
||||
where the OS options are: `darwin_x64`, `linux_x64`, and `win32`.
|
||||
|
||||
## Linting/verifying your source code
|
||||
|
||||
You can check that your code is properly formatted and adheres to coding style by running:
|
||||
@ -128,17 +138,17 @@ You may find that your un-merged change needs some validation from external part
|
||||
Rather than requiring them to pull your Pull Request and build Angular locally, you can
|
||||
publish the `*-builds` snapshots just like our CircleCI build does.
|
||||
|
||||
First time, you need to create the github repositories:
|
||||
First time, you need to create the GitHub repositories:
|
||||
|
||||
``` shell
|
||||
$ export TOKEN=[get one from https://github.com/settings/tokens]
|
||||
$ CREATE_REPOS=1 ./scripts/ci/publish-build-artifacts.sh [github username]
|
||||
$ CREATE_REPOS=1 ./scripts/ci/publish-build-artifacts.sh [GitHub username]
|
||||
```
|
||||
|
||||
For subsequent snapshots, just run
|
||||
|
||||
``` shell
|
||||
$ ./scripts/ci/publish-build-artifacts.sh [github username]
|
||||
$ ./scripts/ci/publish-build-artifacts.sh [GitHub username]
|
||||
```
|
||||
|
||||
The script will publish the build snapshot to a branch with the same name as your current branch,
|
||||
@ -148,3 +158,9 @@ and create it if it doesn't exist.
|
||||
### VS Code
|
||||
|
||||
1. Install [Bazel](https://marketplace.visualstudio.com/items?itemName=DevonDCarew.bazel-code) extension for VS Code.
|
||||
|
||||
### WebStorm / IntelliJ
|
||||
1. Install the [Bazel](https://plugins.jetbrains.com/plugin/8609-bazel) plugin
|
||||
1. You can find the settings under `Preferences->Other Settings->Bazel Settings`
|
||||
|
||||
It will automatically recognize `*.bazel` and `*.bzl` files.
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||
const engines = require('./package.json').engines;
|
||||
|
1
integration/bazel-schematics/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/demo
|
@ -37,6 +37,7 @@ function testBazel() {
|
||||
ng build
|
||||
ng test
|
||||
ng e2e
|
||||
ng e2e --prod
|
||||
if [ -e 'WORKSPACE' ] || [ -e 'BUILD.bazel' ]; then
|
||||
echo 'WORKSPACE / BUILD.bazel file should not exist in project'
|
||||
exit 1
|
||||
|
@ -68,18 +68,18 @@
|
||||
rxjs "6.3.3"
|
||||
|
||||
"@angular/bazel@file:../../dist/packages-dist/bazel":
|
||||
version "8.0.0-beta.7"
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "^0.13.4"
|
||||
"@angular-devkit/core" "^7.0.4"
|
||||
"@angular-devkit/schematics" "^7.3.0-rc.0"
|
||||
"@bazel/typescript" "^0.26.0"
|
||||
"@bazel/typescript" "^0.27.7"
|
||||
"@microsoft/api-extractor" "^7.0.21"
|
||||
"@schematics/angular" "^7.3.5"
|
||||
"@types/node" "6.0.84"
|
||||
semver "^5.6.0"
|
||||
shelljs "0.8.2"
|
||||
tsickle "0.34.0"
|
||||
tsickle "0.34.3"
|
||||
|
||||
"@angular/cli@file:../../node_modules/@angular/cli":
|
||||
version "7.3.2"
|
||||
@ -120,12 +120,11 @@
|
||||
"@bazel/bazel-linux_x64" "0.23.0"
|
||||
"@bazel/bazel-win32_x64" "0.23.0"
|
||||
|
||||
"@bazel/typescript@^0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.26.0.tgz#c06139d76c8b9d3a3ed98a721b776fedb4b11c82"
|
||||
integrity sha512-dh/Y/SZzmeChsLap8FVHYl0FuaeLh/6t9WBVhm5nOgyVrqfEyVpNzy4W20E4NqnmJY2/PqmD5qncf+Oo1q9h1A==
|
||||
"@bazel/typescript@^0.27.7":
|
||||
version "0.27.7"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.27.7.tgz#ff2d6469357ff68faccc75976d435fec0175e988"
|
||||
integrity sha512-Eeu42RM6Ss/se3Bdv+OBfY159N9uOlUZp4XHgZixq1PHwuohRDPIEdEnci/pe3TDsiY25YOi7hjemM90gu5ppg==
|
||||
dependencies:
|
||||
jasmine-core "2.8.0"
|
||||
protobufjs "5.0.3"
|
||||
semver "5.6.0"
|
||||
source-map-support "0.5.9"
|
||||
@ -1279,11 +1278,6 @@ isobject@^3.0.0, isobject@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||
|
||||
jasmine-core@2.8.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
|
||||
integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=
|
||||
|
||||
jju@~1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
|
||||
@ -2364,10 +2358,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
||||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
tsickle@0.34.0:
|
||||
version "0.34.0"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.0.tgz#10187fa6401a288a65efb93a60bf28b2ff95f90b"
|
||||
integrity sha512-O3wCPRtL18Hc/ZBnaiKwmmjVzeCWTOTpsi0btfC7FWL3RnXpxLPxD6hoJ0QEXuSfG/0QJk+MWNjqT9N6fOyyIg==
|
||||
tsickle@0.34.3:
|
||||
version "0.34.3"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.3.tgz#8085067a26d7bff466ddadb2eba18849b49159b8"
|
||||
integrity sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.4/rules_nodejs-0.27.4.tar.gz"],
|
||||
sha256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.7/rules_nodejs-0.27.7.tar.gz"],
|
||||
)
|
||||
|
||||
# Fetch sass rules for compiling sass files
|
||||
@ -37,7 +37,7 @@ Try running `yarn bazel` instead.
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
node_version = "10.9.0",
|
||||
yarn_version = "1.13.0",
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
# Install our npm dependencies into @npm
|
||||
|
@ -8,7 +8,9 @@
|
||||
"es2015"
|
||||
],
|
||||
"experimentalDecorators": true,
|
||||
"types": []
|
||||
"types": [],
|
||||
"module": "umd",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"node_modules/@angular/**/*"
|
||||
@ -16,6 +18,7 @@
|
||||
"exclude": [
|
||||
"node_modules/@angular/bazel/**",
|
||||
"node_modules/@angular/compiler-cli/**",
|
||||
"node_modules/@angular/**/testing/**"
|
||||
"node_modules/@angular/**/testing/**",
|
||||
"node_modules/@angular/router/upgrade*"
|
||||
]
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ ng_module(
|
||||
"@npm//@angular/common",
|
||||
"@npm//@angular/core",
|
||||
"@npm//@angular/platform-browser",
|
||||
"@npm//@angular/router",
|
||||
"@npm//@types",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
@ -39,6 +40,8 @@ ts_devserver(
|
||||
"@npm//node_modules/@angular/common:bundles/common-http.umd.js",
|
||||
"@npm//node_modules/@angular/core:bundles/core.umd.js",
|
||||
"@npm//node_modules/@angular/platform-browser:bundles/platform-browser.umd.js",
|
||||
"@npm//node_modules/@angular/router:bundles/router.umd.js",
|
||||
"@npm//node_modules/@angular/router:router.ngfactory.js",
|
||||
"@npm//node_modules/tslib:tslib.js",
|
||||
":rxjs_umd_modules",
|
||||
],
|
||||
@ -54,9 +57,12 @@ rollup_bundle(
|
||||
entry_point = "src/main",
|
||||
deps = [
|
||||
"//src",
|
||||
"@npm//@angular/common",
|
||||
"@npm//@angular/core",
|
||||
"@npm//@angular/platform-browser",
|
||||
# TODO(kyliau): These are not necessary. Bundle compiles fine without
|
||||
# these deps.
|
||||
# "@npm//@angular/common",
|
||||
# "@npm//@angular/core",
|
||||
# "@npm//@angular/platform-browser",
|
||||
"@npm//@angular/router", # needed here due to additional imports generated by ngc
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
14
integration/bazel/src/app-routing.module.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
// TODO(kyliau): Empty routes is enough for now to expose the bug in
|
||||
// https://github.com/angular/angular/issues/29454.
|
||||
// Consider adding non-lazy loaded routes.
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Component} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map, startWith} from 'rxjs/operators';
|
||||
|
||||
@ -8,12 +8,13 @@ import {map, startWith} from 'rxjs/operators';
|
||||
template: `
|
||||
<hello-world-app></hello-world-app>
|
||||
<div>The current time is {{ time$ | async }}</div>
|
||||
`})
|
||||
<router-outlet></router-outlet>
|
||||
`
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
time$ = this.http.get('http://worldclockapi.com/api/json/pst/now').pipe(
|
||||
map((result: any) => result.currentDateTime),
|
||||
startWith(['...']));
|
||||
time$: Observable<string> =
|
||||
this.http.get('http://worldclockapi.com/api/json/pst/now')
|
||||
.pipe(map((result: any) => result.currentDateTime), startWith(['...']));
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {HelloWorldModule} from './hello-world/hello-world.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, BrowserModule, HttpClientModule, HelloWorldModule],
|
||||
imports: [
|
||||
AppRoutingModule,
|
||||
CommonModule,
|
||||
BrowserModule,
|
||||
HttpClientModule,
|
||||
HelloWorldModule,
|
||||
],
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule {
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-app',
|
||||
@ -7,8 +7,7 @@ import {Component, NgModule} from '@angular/core';
|
||||
<div>Hello {{ name }}!</div>
|
||||
<input type="text" [value]="name" (input)="name = $event.target.value"/>
|
||||
`,
|
||||
// TODO: might be better to point to .scss so this looks valid at design-time
|
||||
styleUrls: ['./hello-world.component.css'],
|
||||
styleUrls: ['./hello-world.component.scss'],
|
||||
})
|
||||
export class HelloWorldComponent {
|
||||
name: string = 'world';
|
||||
|
@ -9,6 +9,7 @@
|
||||
"@angular/core": "packages-dist:core",
|
||||
"@angular/platform-browser": "packages-dist:platform-browser",
|
||||
"@angular/platform-browser-dynamic": "packages-dist:platform-browser-dynamic",
|
||||
"@angular/router": "packages-dist:router",
|
||||
"reflect-metadata": "0.1.12",
|
||||
"rxjs": "6.4.0",
|
||||
"tslib": "1.9.3",
|
||||
@ -18,7 +19,7 @@
|
||||
"@angular/bazel": "packages-dist:bazel",
|
||||
"@angular/compiler": "packages-dist:compiler",
|
||||
"@angular/compiler-cli": "packages-dist:compiler-cli",
|
||||
"@bazel/karma": "0.27.4",
|
||||
"@bazel/karma": "0.27.7",
|
||||
"@types/jasmine": "2.8.8",
|
||||
"@types/source-map": "0.5.1",
|
||||
"protractor": "5.1.2",
|
||||
@ -28,4 +29,4 @@
|
||||
"postinstall": "ngc -p ./angular-metadata.tsconfig.json",
|
||||
"//": "TODO(gregmagolan): figure out how to keep dependencies here up to date with the root package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,36 +2,14 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular-devkit/architect@^0.10.6":
|
||||
version "0.10.7"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.10.7.tgz#c74b9f6b7f1b4261ada2d24c832328aa4c394464"
|
||||
integrity sha512-S49LSslNRxIflHzrIrEgK7mGQ7HzETr/FU0fyTbB0vubcmfzMoYTsgYdK7SUz583lovc+UvASoUAhPJI3e35ng==
|
||||
"@angular-devkit/architect@^0.13.4":
|
||||
version "0.13.6"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.13.6.tgz#7758ab9f288cfd4f64d8b88b0700bac5c4961d06"
|
||||
integrity sha512-Cg9z4lmCvjt5uD00E/0tBRz3ESjYicmqT3NL/BIsNVNb+s1GwCCoPSOIM8Ss4nyGDtrdono1XKSOmkJnlzF3Cw==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "7.0.7"
|
||||
"@angular-devkit/core" "7.3.6"
|
||||
rxjs "6.3.3"
|
||||
|
||||
"@angular-devkit/core@7.0.7":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.0.7.tgz#665176ad8421adfd5f3ea0b2c4a9a432a158b1bb"
|
||||
integrity sha512-M8tTT9r3nUtWI3YyiyynHIQn+lQQgeKkxVZ+rdxvyvgE3U9+wn0yep5HkFLQETTuJetu9ARRRD94sD2XL3F/3A==
|
||||
dependencies:
|
||||
ajv "6.5.3"
|
||||
chokidar "2.0.4"
|
||||
fast-json-stable-stringify "2.0.0"
|
||||
rxjs "6.3.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/core@7.1.2", "@angular-devkit/core@^7.0.4":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.1.2.tgz#86b0e5a4cdeaa3198f6b1b50e7e114fac403e57c"
|
||||
integrity sha512-LyjHGuLnLWrgX7SYDkKmc3eW4H5uuaoC+CXYjRfgx3qundrLfvTCRgNGC6FPjhQNnVXH9qar+j9P1aMmKFb4Lw==
|
||||
dependencies:
|
||||
ajv "6.5.3"
|
||||
chokidar "2.0.4"
|
||||
fast-json-stable-stringify "2.0.0"
|
||||
rxjs "6.3.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/core@7.3.3":
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.3.tgz#cd6d5a8eca25ef07b6394bc2b08133d90d08d39f"
|
||||
@ -43,12 +21,34 @@
|
||||
rxjs "6.3.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/schematics@7.1.2":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.1.2.tgz#847639044417d044bf1bc87f64508a0c3f99fae2"
|
||||
integrity sha512-NFhHLYWf9gpGQm0s19lq+nAw3CZ0udBpoBLzCm8Crlmu6+7aAXgw7Fv5P4ukWJ/e1m7NDGVids+B6kBGXaY6Ig==
|
||||
"@angular-devkit/core@7.3.6":
|
||||
version "7.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.6.tgz#bc11ca571187f0f0ce9df012332794907e0b8133"
|
||||
integrity sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "7.1.2"
|
||||
ajv "6.9.1"
|
||||
chokidar "2.0.4"
|
||||
fast-json-stable-stringify "2.0.0"
|
||||
rxjs "6.3.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/core@^7.0.4":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.1.2.tgz#86b0e5a4cdeaa3198f6b1b50e7e114fac403e57c"
|
||||
integrity sha512-LyjHGuLnLWrgX7SYDkKmc3eW4H5uuaoC+CXYjRfgx3qundrLfvTCRgNGC6FPjhQNnVXH9qar+j9P1aMmKFb4Lw==
|
||||
dependencies:
|
||||
ajv "6.5.3"
|
||||
chokidar "2.0.4"
|
||||
fast-json-stable-stringify "2.0.0"
|
||||
rxjs "6.3.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/schematics@7.3.6":
|
||||
version "7.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.3.6.tgz#5580b730b1bae5397f5860cfb0d7270fec6e6deb"
|
||||
integrity sha512-YXF7QusmMy3D9H0vNczc1n5BkuEHLwt7cW33euNeGNgTIsD0n6DrUhgClurXicnr2GNPSDYE5+3115lmJkhyrg==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "7.3.6"
|
||||
rxjs "6.3.3"
|
||||
|
||||
"@angular-devkit/schematics@^7.3.0-rc.0":
|
||||
@ -59,32 +59,32 @@
|
||||
"@angular-devkit/core" "7.3.3"
|
||||
rxjs "6.3.3"
|
||||
|
||||
"@angular/animations@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/animations":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/animations@file:../../../dist/packages-dist/animations":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/bazel@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/bazel":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/bazel@file:../../../dist/packages-dist/bazel":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "^0.10.6"
|
||||
"@angular-devkit/architect" "^0.13.4"
|
||||
"@angular-devkit/core" "^7.0.4"
|
||||
"@angular-devkit/schematics" "^7.3.0-rc.0"
|
||||
"@bazel/typescript" "^0.26.0"
|
||||
"@microsoft/api-extractor" "^7.0.17"
|
||||
"@schematics/angular" "^7.0.4"
|
||||
"@bazel/typescript" "^0.27.7"
|
||||
"@microsoft/api-extractor" "^7.0.21"
|
||||
"@schematics/angular" "^7.3.5"
|
||||
"@types/node" "6.0.84"
|
||||
semver "^5.6.0"
|
||||
shelljs "0.8.2"
|
||||
tsickle "0.34.0"
|
||||
tsickle "0.34.3"
|
||||
|
||||
"@angular/common@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/common":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/common@file:../../../dist/packages-dist/common":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/compiler-cli@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/compiler-cli":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/compiler-cli@file:../../../dist/packages-dist/compiler-cli":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
canonical-path "1.0.0"
|
||||
chokidar "^2.1.1"
|
||||
@ -98,30 +98,35 @@
|
||||
tslib "^1.9.0"
|
||||
yargs "9.0.1"
|
||||
|
||||
"@angular/compiler@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/compiler":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/compiler@file:../../../dist/packages-dist/compiler":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/core@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/core":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/core@file:../../../dist/packages-dist/core":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-browser-dynamic@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/platform-browser-dynamic":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/platform-browser-dynamic@file:../../../dist/packages-dist/platform-browser-dynamic":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-browser@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/platform-browser":
|
||||
version "8.0.0-beta.6"
|
||||
"@angular/platform-browser@file:../../../dist/packages-dist/platform-browser":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@bazel/karma@0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-0.26.0.tgz#6ad796686f5775df33a96fa5ef3df76a66aed3b2"
|
||||
integrity sha512-yZv0fgAjVrfrM0ld8e+wNaPIpYCpwBzVQi2GUErsKStUZXPUUofpBwBjlYsdY1Osn5/FmAF9e6xDkk1JWn/wSg==
|
||||
"@angular/router@file:../../../dist/packages-dist/router":
|
||||
version "8.0.0-beta.9"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@bazel/karma@0.27.7":
|
||||
version "0.27.7"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-0.27.7.tgz#abe1cf48be97fbad84ad38e9d32b823e367b8d81"
|
||||
integrity sha512-SlYtePQWoa1+xKMEZdp27n4WZsocfUaUR10p3jaZZ0uhOcoAvuOVTaG+sidUJCoVuF8s6IPjbSOGN4FWVgXUjg==
|
||||
dependencies:
|
||||
jasmine-core "2.8.0"
|
||||
karma "^4.0.0"
|
||||
@ -135,37 +140,44 @@
|
||||
semver "5.6.0"
|
||||
tmp "0.0.33"
|
||||
|
||||
"@bazel/typescript@^0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.26.0.tgz#c06139d76c8b9d3a3ed98a721b776fedb4b11c82"
|
||||
integrity sha512-dh/Y/SZzmeChsLap8FVHYl0FuaeLh/6t9WBVhm5nOgyVrqfEyVpNzy4W20E4NqnmJY2/PqmD5qncf+Oo1q9h1A==
|
||||
"@bazel/typescript@^0.27.7":
|
||||
version "0.27.7"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.27.7.tgz#ff2d6469357ff68faccc75976d435fec0175e988"
|
||||
integrity sha512-Eeu42RM6Ss/se3Bdv+OBfY159N9uOlUZp4XHgZixq1PHwuohRDPIEdEnci/pe3TDsiY25YOi7hjemM90gu5ppg==
|
||||
dependencies:
|
||||
jasmine-core "2.8.0"
|
||||
protobufjs "5.0.3"
|
||||
semver "5.6.0"
|
||||
source-map-support "0.5.9"
|
||||
tsutils "2.27.2"
|
||||
|
||||
"@microsoft/api-extractor@^7.0.17":
|
||||
version "7.0.18"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.0.18.tgz#4eb931436495177dfcef8f2d8da3d084d10eebb6"
|
||||
integrity sha512-puQisjyoYK1A0I8DqyBoLPV9noyFUlxTE3WsjhgJw//TrmegGHYmsRlD3rnHeXcKPM1F7sd/VKJXeXC3IPTf2Q==
|
||||
"@microsoft/api-extractor-model@7.0.28":
|
||||
version "7.0.28"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.0.28.tgz#691b077415143a9015ed1656bca6d0b9a77f58cb"
|
||||
integrity sha512-kZJaWwdu3z5A1DugJpOZ9dI5+DjIEhqQJwHn2/kLTpsKT7gOyqNRbGHlDGG8xSiJ6/m994+cwh3qSGYDC17dtw==
|
||||
dependencies:
|
||||
"@microsoft/node-core-library" "3.10.0"
|
||||
"@microsoft/ts-command-line" "4.2.3"
|
||||
"@microsoft/tsdoc" "0.12.5"
|
||||
"@microsoft/node-core-library" "3.13.0"
|
||||
"@microsoft/tsdoc" "0.12.8"
|
||||
"@types/node" "8.5.8"
|
||||
"@types/z-schema" "3.16.31"
|
||||
|
||||
"@microsoft/api-extractor@^7.0.21":
|
||||
version "7.0.31"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.0.31.tgz#4d72755bf234e1c3a0afb22f678b7106158a9b89"
|
||||
integrity sha512-7oiMvxaR9/qOO4SdlG2JFhdCZ1B+JtaX4pDb/aDGsm+7n+PMeLSVgQKeZwVXUIgslHlXxkxbX7zXIz41VqL8sQ==
|
||||
dependencies:
|
||||
"@microsoft/api-extractor-model" "7.0.28"
|
||||
"@microsoft/node-core-library" "3.13.0"
|
||||
"@microsoft/ts-command-line" "4.2.3"
|
||||
"@microsoft/tsdoc" "0.12.8"
|
||||
colors "~1.2.1"
|
||||
lodash "~4.17.5"
|
||||
resolve "1.8.1"
|
||||
source-map "~0.6.1"
|
||||
typescript "~3.1.6"
|
||||
z-schema "~3.18.3"
|
||||
|
||||
"@microsoft/node-core-library@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.10.0.tgz#70e089534d8e20f6a0f9c7a4a12a6aeafd6a1ddb"
|
||||
integrity sha512-1SbU+XNYAabhV9noGXHtsUVPc5ELV+oEuJQtZQoCncbOd6WAMeTgB1xFwh96hmdEXyKQyML/pnByiKocmh/nbQ==
|
||||
"@microsoft/node-core-library@3.13.0":
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.13.0.tgz#ba24e16182149dc817bf52a886d22aced5cd8070"
|
||||
integrity sha512-mnsL/1ikVWHl8sPNssavaAgtUaIM3hkQ8zeySuApU5dNmsMPzovJPfx9m5JGiMvs1v5QNAIVeiS9jnWwe/7anw==
|
||||
dependencies:
|
||||
"@types/fs-extra" "5.0.4"
|
||||
"@types/jju" "~1.4.0"
|
||||
@ -186,19 +198,19 @@
|
||||
argparse "~1.0.9"
|
||||
colors "~1.2.1"
|
||||
|
||||
"@microsoft/tsdoc@0.12.5":
|
||||
version "0.12.5"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.5.tgz#c448a38902ccb5601c1b2ef3b1a105012ef7712c"
|
||||
integrity sha512-xEAyvLXo4Cter/b0EMCWUZTgXOfLOPJ/Xr52WdjVclPx9eDmNTGFtZl8Pn/nqSnZsQBNcHL0eHk/YyRyyXXpiQ==
|
||||
"@microsoft/tsdoc@0.12.8":
|
||||
version "0.12.8"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.8.tgz#e9646c91c650d536f37b04762eaa81afdc9a19d3"
|
||||
integrity sha512-0smzAmVIUCsssAqDSPn9AfOPKUobq2WXMygbzC5JNswAJOs4uJK6DTZgfnHC8QLE2q374sPNwWU5D5LuoAJQSA==
|
||||
|
||||
"@schematics/angular@^7.0.4":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.1.2.tgz#b3eefbc81d12b0b53816896f6172eb613885826c"
|
||||
integrity sha512-coypNxjRjCExCbkJ8Vser4iZbdksl3cNqgdokDlEtpXnnph3ZYvNDhDD9TBWYQ+cwDhCHAOzT3U3IjN4R2MCgQ==
|
||||
"@schematics/angular@^7.3.5":
|
||||
version "7.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.3.6.tgz#e827111e7d3cdf950efcca504389cbade3c9c59a"
|
||||
integrity sha512-Q4VXAjVaCDb2zXFXoIdOfNPsn+EQjqDBHK4a97omytnSNAmu1erl3l2FkEMi6x/VuzK2mQSzBbmHJIgauMmOAA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "7.1.2"
|
||||
"@angular-devkit/schematics" "7.1.2"
|
||||
typescript "3.1.6"
|
||||
"@angular-devkit/core" "7.3.6"
|
||||
"@angular-devkit/schematics" "7.3.6"
|
||||
typescript "3.2.4"
|
||||
|
||||
"@types/argparse@1.0.33":
|
||||
version "1.0.33"
|
||||
@ -3112,7 +3124,7 @@ source-map@^0.5.6:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||
|
||||
source-map@^0.6.0, source-map@^0.6.1:
|
||||
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
@ -3337,10 +3349,10 @@ tough-cookie@~2.4.3:
|
||||
psl "^1.1.24"
|
||||
punycode "^1.4.1"
|
||||
|
||||
tsickle@0.34.0:
|
||||
version "0.34.0"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.0.tgz#10187fa6401a288a65efb93a60bf28b2ff95f90b"
|
||||
integrity sha512-O3wCPRtL18Hc/ZBnaiKwmmjVzeCWTOTpsi0btfC7FWL3RnXpxLPxD6hoJ0QEXuSfG/0QJk+MWNjqT9N6fOyyIg==
|
||||
tsickle@0.34.3:
|
||||
version "0.34.3"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.3.tgz#8085067a26d7bff466ddadb2eba18849b49159b8"
|
||||
integrity sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
@ -3378,12 +3390,17 @@ type-is@~1.6.16:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.18"
|
||||
|
||||
typescript@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96"
|
||||
integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ==
|
||||
typescript@3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
|
||||
integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==
|
||||
|
||||
typescript@3.1.6, typescript@~3.1.6:
|
||||
typescript@3.3.3333:
|
||||
version "3.3.3333"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
|
||||
integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
|
||||
|
||||
typescript@~3.1.6:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
|
||||
integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==
|
||||
|
@ -45,5 +45,8 @@ protractor_web_test_suite(
|
||||
],
|
||||
on_prepare = ":ts_on_prepare",
|
||||
server = "//src:prodserver",
|
||||
# TODO(kyliau): Re-enable once we figure out why rollup complains about
|
||||
# "Dynamic requires are not currently supported by rollup-plugin-commonjs"
|
||||
tags = ["manual"],
|
||||
deps = [":e2e"],
|
||||
)
|
||||
|
@ -10,11 +10,25 @@ ivy-ngcc --help
|
||||
ivy-ngcc
|
||||
|
||||
# Did it add the appropriate build markers?
|
||||
# - fesm2015
|
||||
ls node_modules/@angular/common | grep __modified_by_ngcc_for_fesm2015
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# - esm2015
|
||||
ls node_modules/@angular/common | grep __modified_by_ngcc_for_esm2015
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"esm2015":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# - fesm2015
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"fesm2015":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"es2015":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# - esm5
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"esm5":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# - fesm5
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"module":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep '"__processed_by_ivy_ngcc__":[^}]*"fesm5":"' node_modules/@angular/common/package.json
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it replace the PRE_R3 markers correctly?
|
||||
@ -48,6 +62,9 @@ ivy-ngcc
|
||||
# Can it be safely run again (as a noop)?
|
||||
ivy-ngcc
|
||||
|
||||
# Does running it with --formats fail?
|
||||
ivy-ngcc --formats fesm2015 && exit 1
|
||||
|
||||
# Now try compiling the app using the ngcc compiled libraries
|
||||
ngc -p tsconfig-app.json
|
||||
|
||||
|
@ -37,8 +37,8 @@
|
||||
"@angular-devkit/schematics": "^7.3.2",
|
||||
"@angular/bazel": "file:./tools/npm/@angular_bazel",
|
||||
"@bazel/jasmine": "0.26.0",
|
||||
"@bazel/karma": "0.27.4",
|
||||
"@bazel/typescript": "0.27.4",
|
||||
"@bazel/karma": "0.27.7",
|
||||
"@bazel/typescript": "0.27.7",
|
||||
"@microsoft/api-extractor": "^7.0.21",
|
||||
"@schematics/angular": "^7.3.5",
|
||||
"@types/angular": "^1.6.47",
|
||||
@ -110,7 +110,7 @@
|
||||
"// 3": "when updating @bazel/bazel version you also need to update the RBE settings in .bazelrc (see https://github.com/angular/angular/pull/27935)",
|
||||
"devDependencies": {
|
||||
"@angular/cli": "^7.3.2",
|
||||
"@bazel/bazel": "0.23.0",
|
||||
"@bazel/bazel": "0.23.2",
|
||||
"@bazel/buildifier": "^0.19.2",
|
||||
"@bazel/ibazel": "~0.9.0",
|
||||
"@types/minimist": "^1.2.0",
|
||||
|
@ -22,7 +22,7 @@
|
||||
"@angular-devkit/architect": "^0.13.4",
|
||||
"@angular-devkit/core": "^7.0.4",
|
||||
"@angular-devkit/schematics": "^7.3.0-rc.0",
|
||||
"@bazel/typescript": "^0.27.4",
|
||||
"@bazel/typescript": "^0.27.7",
|
||||
"@microsoft/api-extractor": "^7.0.21",
|
||||
"@schematics/angular": "^7.3.5",
|
||||
"@types/node": "6.0.84",
|
||||
|
@ -12,18 +12,20 @@ workspace(name = "project")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
RULES_NODEJS_VERSION = "0.27.4"
|
||||
RULES_NODEJS_VERSION = "0.27.7"
|
||||
RULES_NODEJS_SHA256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51"
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
|
||||
sha256 = RULES_NODEJS_SHA256,
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/releases/download/%s/rules_nodejs-%s.tar.gz" % (RULES_NODEJS_VERSION, RULES_NODEJS_VERSION),
|
||||
)
|
||||
|
||||
# Rules for compiling sass
|
||||
RULES_SASS_VERSION = "1.17.2"
|
||||
RULES_SASS_VERSION = "1.17.3"
|
||||
RULES_SASS_SHA256 = "ea79647e5cd36867568d80811a951c7b3170791058f50a5cbd3d542627e78881"
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "e5316ee8a09d1cbb732d3938b400836bf94dba91a27476e9e27706c4c0edae1f",
|
||||
sha256 = RULES_SASS_SHA256,
|
||||
url = "https://github.com/bazelbuild/rules_sass/archive/%s.zip" % RULES_SASS_VERSION,
|
||||
strip_prefix = "rules_sass-%s" % RULES_SASS_VERSION,
|
||||
)
|
||||
|
@ -105,6 +105,10 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
|
||||
|
||||
angularCompilerOptions.preserveWhitespaces = angularCompilerOptions.preserveWhitespaces ||
|
||||
userConfig.angularCompilerOptions.preserveWhitespaces;
|
||||
|
||||
angularCompilerOptions.createExternalSymbolFactoryReexports =
|
||||
angularCompilerOptions.createExternalSymbolFactoryReexports ||
|
||||
userConfig.angularCompilerOptions.createExternalSymbolFactoryReexports;
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,6 +311,31 @@ export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost,
|
||||
// File does not exist or parse error. Ignore this case and continue onto the
|
||||
// other methods of resolving the module below.
|
||||
}
|
||||
|
||||
// It can happen that the ViewEngine compiler needs to write an import in a factory file,
|
||||
// and is using an ngsummary file to get the symbols.
|
||||
// The ngsummary comes from an upstream ng_module rule.
|
||||
// The upstream rule based its imports on ngsummary file which was generated from a
|
||||
// metadata.json file that was published to npm in an Angular library.
|
||||
// However, the ngsummary doesn't propagate the 'importAs' from the original metadata.json
|
||||
// so we would normally not be able to supply the correct module name for it.
|
||||
// For example, if the rootDir-relative filePath is
|
||||
// node_modules/@angular/material/toolbar/typings/index
|
||||
// we would supply a module name
|
||||
// @angular/material/toolbar/typings/index
|
||||
// but there is no JavaScript file to load at this path.
|
||||
// This is a workaround for https://github.com/angular/angular/issues/29454
|
||||
if (importedFilePath.indexOf('node_modules') >= 0) {
|
||||
const maybeMetadataFile = importedFilePath.replace(EXT, '') + '.metadata.json';
|
||||
if (fs.existsSync(maybeMetadataFile)) {
|
||||
const moduleName =
|
||||
JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})).importAs;
|
||||
if (moduleName) {
|
||||
return moduleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((compilerOpts.module === ts.ModuleKind.UMD || compilerOpts.module === ts.ModuleKind.AMD) &&
|
||||
ngHost.amdModuleName) {
|
||||
return ngHost.amdModuleName({ fileName: importedFilePath } as ts.SourceFile);
|
||||
|
@ -49,7 +49,7 @@ function addDevDependenciesToPackageJson(options: Schema) {
|
||||
'@angular/bazel': angularCoreVersion,
|
||||
'@bazel/bazel': '^0.23.0',
|
||||
'@bazel/ibazel': '^0.9.0',
|
||||
'@bazel/karma': '^0.27.4',
|
||||
'@bazel/karma': '^0.27.7',
|
||||
};
|
||||
|
||||
const recorder = host.beginUpdate(packageJson);
|
||||
|
@ -34,9 +34,9 @@ ng_package(
|
||||
visibility = [
|
||||
"//packages/bazel/test/ng_package:__pkg__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/ngcc/test:__pkg__",
|
||||
"//packages/compiler-cli/test:__pkg__",
|
||||
"//packages/compiler-cli/test/diagnostics:__pkg__",
|
||||
"//packages/compiler-cli/test/ngcc:__pkg__",
|
||||
"//packages/compiler-cli/test/transformers:__pkg__",
|
||||
"//packages/compiler/test:__pkg__",
|
||||
"//packages/language-service/test:__pkg__",
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Attribute, Component, Directive} from '@angular/core';
|
||||
import {Attribute, Component, Directive, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
@ -26,7 +26,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestComponent],
|
||||
declarations: [TestComponent, ComplexComponent],
|
||||
imports: [CommonModule],
|
||||
});
|
||||
});
|
||||
@ -171,6 +171,20 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
});
|
||||
|
||||
it('should support nested NgSwitch on ng-container with ngTemplateOutlet', () => {
|
||||
fixture = TestBed.createComponent(ComplexComponent);
|
||||
detectChangesAndExpectText('Foo');
|
||||
|
||||
fixture.componentInstance.state = 'case2';
|
||||
detectChangesAndExpectText('Bar');
|
||||
|
||||
fixture.componentInstance.state = 'notACase';
|
||||
detectChangesAndExpectText('Default');
|
||||
|
||||
fixture.componentInstance.state = 'case1';
|
||||
detectChangesAndExpectText('Foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -182,6 +196,38 @@ class TestComponent {
|
||||
when2: any = null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'complex-cmp',
|
||||
template: `
|
||||
<div [ngSwitch]="state">
|
||||
<ng-container *ngSwitchCase="'case1'" [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="foo"></ng-container>
|
||||
<span *ngSwitchDefault>Should never render</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'case2'" [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="bar"></ng-container>
|
||||
<span *ngSwitchDefault>Should never render</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault [ngSwitch]="false">
|
||||
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="foo"></ng-container>
|
||||
<span *ngSwitchDefault>Default</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #foo>
|
||||
<span>Foo</span>
|
||||
</ng-template>
|
||||
<ng-template #bar>
|
||||
<span>Bar</span>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class ComplexComponent {
|
||||
@ViewChild('foo') foo !: TemplateRef<any>;
|
||||
@ViewChild('bar') bar !: TemplateRef<any>;
|
||||
state: string = 'case1';
|
||||
}
|
||||
|
||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.createComponent(TestComponent);
|
||||
|
@ -60,6 +60,6 @@ npm_package(
|
||||
],
|
||||
deps = [
|
||||
":compiler-cli",
|
||||
"//packages/compiler-cli/src/ngcc",
|
||||
"//packages/compiler-cli/ngcc",
|
||||
],
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ This conversion will allow such "legacy" packages to be used by the Ivy renderin
|
||||
The project is built using Bazel:
|
||||
|
||||
```bash
|
||||
yarn bazel build //packages/compiler-cli/src/ngcc
|
||||
yarn bazel build //packages/compiler-cli/ngcc
|
||||
```
|
||||
|
||||
## Unit Testing
|
||||
@ -18,7 +18,7 @@ yarn bazel build //packages/compiler-cli/src/ngcc
|
||||
The unit tests are built and run using Bazel:
|
||||
|
||||
```bash
|
||||
yarn bazel test //packages/compiler-cli/src/ngcc/test
|
||||
yarn bazel test //packages/compiler-cli/ngcc/test
|
||||
```
|
||||
|
||||
## Integration Testing
|
||||
@ -26,5 +26,5 @@ yarn bazel test //packages/compiler-cli/src/ngcc/test
|
||||
There are tests that check the behavior of the overall executable:
|
||||
|
||||
```bash
|
||||
yarn bazel test //packages/compiler-cli/test/ngcc
|
||||
yarn bazel test //packages/compiler-cli/ngcc/test:integration
|
||||
```
|
17
packages/compiler-cli/ngcc/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @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 {hasBeenProcessed as _hasBeenProcessed} from './src/packages/build_marker';
|
||||
import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entry_point';
|
||||
|
||||
export {NgccOptions, mainNgcc as process} from './src/main';
|
||||
|
||||
export function hasBeenProcessed(packageJson: object, format: string) {
|
||||
// We are wrapping this function to hide the internal types.
|
||||
return _hasBeenProcessed(packageJson as EntryPointPackageJson, format as EntryPointJsonProperty);
|
||||
}
|
65
packages/compiler-cli/ngcc/main-ngcc.ts
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @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 yargs from 'yargs';
|
||||
|
||||
import {mainNgcc} from './src/main';
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
const options =
|
||||
yargs
|
||||
.option('s', {
|
||||
alias: 'source',
|
||||
describe:
|
||||
'A path (relative to the working directory) of the `node_modules` folder to process.',
|
||||
default: './node_modules'
|
||||
})
|
||||
.option('f', {alias: 'formats', hidden: true, array: true})
|
||||
.option('p', {
|
||||
alias: 'properties',
|
||||
array: true,
|
||||
describe:
|
||||
'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' +
|
||||
'Each of these properties should hold the path to a bundle-format.\n' +
|
||||
'If provided, only the specified properties are considered for processing.\n' +
|
||||
'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.'
|
||||
})
|
||||
.option('t', {
|
||||
alias: 'target',
|
||||
describe:
|
||||
'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).',
|
||||
})
|
||||
.option('first-only', {
|
||||
describe:
|
||||
'If specified then only the first matching package.json property will be compiled',
|
||||
type: 'boolean'
|
||||
})
|
||||
.help()
|
||||
.parse(args);
|
||||
|
||||
if (options['f'] && options['f'].length) {
|
||||
console.error(
|
||||
'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 propertiesToConsider: string[] = options['p'];
|
||||
const targetEntryPointPath = options['t'] ? options['t'] : undefined;
|
||||
const compileAllFormats = !options['first-only'];
|
||||
try {
|
||||
mainNgcc(
|
||||
{basePath: baseSourcePath, propertiesToConsider, targetEntryPointPath, compileAllFormats});
|
||||
process.exitCode = 0;
|
||||
} catch (e) {
|
||||
console.error(e.stack || e.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
@ -10,13 +10,13 @@ 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 '../../../ngtsc/annotations';
|
||||
import {CycleAnalyzer, ImportGraph} from '../../../ngtsc/cycles';
|
||||
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../ngtsc/imports';
|
||||
import {PartialEvaluator} from '../../../ngtsc/partial_evaluator';
|
||||
import {AbsoluteFsPath, LogicalFileSystem} from '../../../ngtsc/path';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../ngtsc/scope';
|
||||
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../ngtsc/transform';
|
||||
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
|
||||
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
|
||||
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../src/ngtsc/imports';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {AbsoluteFsPath, LogicalFileSystem} from '../../../src/ngtsc/path';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
|
||||
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
|
||||
import {DecoratedClass} from '../host/decorated_class';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
@ -7,10 +7,10 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReferencesRegistry} from '../../../ngtsc/annotations';
|
||||
import {Reference} from '../../../ngtsc/imports';
|
||||
import {Declaration} from '../../../ngtsc/reflection';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {Declaration} from '../../../src/ngtsc/reflection';
|
||||
import {ModuleWithProvidersFunction, NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
||||
|
||||
export interface ModuleWithProvidersInfo {
|
||||
@ -38,7 +38,7 @@ export class ModuleWithProvidersAnalyzer {
|
||||
rootFiles.forEach(f => {
|
||||
const fns = this.host.getModuleWithProvidersFunctions(f);
|
||||
fns && fns.forEach(fn => {
|
||||
const dtsFn = this.getDtsDeclaration(fn.declaration);
|
||||
const dtsFn = this.getDtsDeclarationForFunction(fn);
|
||||
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
|
||||
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
|
||||
null;
|
||||
@ -82,28 +82,27 @@ export class ModuleWithProvidersAnalyzer {
|
||||
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
|
||||
}
|
||||
|
||||
private getDtsDeclaration(fn: ts.SignatureDeclaration) {
|
||||
private getDtsDeclarationForFunction(fn: ModuleWithProvidersFunction) {
|
||||
let dtsFn: ts.Declaration|null = null;
|
||||
const containerClass = this.host.getClassSymbol(fn.parent);
|
||||
const fnName = fn.name && ts.isIdentifier(fn.name) && fn.name.text;
|
||||
if (containerClass && fnName) {
|
||||
const containerClass = fn.container && this.host.getClassSymbol(fn.container);
|
||||
if (containerClass) {
|
||||
const dtsClass = this.host.getDtsDeclaration(containerClass.valueDeclaration);
|
||||
// Get the declaration of the matching static method
|
||||
dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ?
|
||||
dtsClass.members
|
||||
.find(
|
||||
member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
|
||||
member.name.text === fnName) as ts.Declaration :
|
||||
member.name.text === fn.name) as ts.Declaration :
|
||||
null;
|
||||
} else {
|
||||
dtsFn = this.host.getDtsDeclaration(fn);
|
||||
dtsFn = this.host.getDtsDeclaration(fn.declaration);
|
||||
}
|
||||
if (!dtsFn) {
|
||||
throw new Error(`Matching type declaration for ${fn.getText()} is missing`);
|
||||
throw new Error(`Matching type declaration for ${fn.declaration.getText()} is missing`);
|
||||
}
|
||||
if (!isFunctionOrMethod(dtsFn)) {
|
||||
throw new Error(
|
||||
`Matching type declaration for ${fn.getText()} is not a function: ${dtsFn.getText()}`);
|
||||
`Matching type declaration for ${fn.declaration.getText()} is not a function: ${dtsFn.getText()}`);
|
||||
}
|
||||
return dtsFn;
|
||||
}
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import {ReferencesRegistry} from '../../../ngtsc/annotations';
|
||||
import {Reference} from '../../../ngtsc/imports';
|
||||
import {Declaration, ReflectionHost} from '../../../ngtsc/reflection';
|
||||
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {Declaration, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
import {hasNameIdentifier} from '../utils';
|
||||
|
||||
/**
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Declaration} from '../../../ngtsc/reflection';
|
||||
import {Declaration} from '../../../src/ngtsc/reflection';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {hasNameIdentifier, isDefined} from '../utils';
|
||||
import {NgccReferencesRegistry} from './ngcc_references_registry';
|
@ -6,8 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import {Decorator} from '../../../ngtsc/reflection';
|
||||
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
|
||||
|
||||
/**
|
||||
* A simple container that holds the details of a decorated class that has been
|
||||
@ -18,9 +17,12 @@ 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
|
||||
* @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: ts.Declaration, public decorators: Decorator[], ) {}
|
||||
public name: string, public declaration: ClassDeclaration, public decorators: Decorator[]) {}
|
||||
}
|
@ -8,9 +8,9 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMember, ClassMemberKind, CtorParameter, Decorator, Import, TypeScriptReflectionHost, reflectObjectLiteral} from '../../../ngtsc/reflection';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Decorator, Import, TypeScriptReflectionHost, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {findAll, getNameText, isDefined} from '../utils';
|
||||
import {findAll, getNameText, hasNameIdentifier, isDefined} from '../utils';
|
||||
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||
@ -54,6 +54,37 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the declaration of a node that we think is a class.
|
||||
* Classes should have a `name` identifier, because they may need to be referenced in other parts
|
||||
* of the program.
|
||||
*
|
||||
* @param node the node that represents the class whose declaration we are finding.
|
||||
* @returns the declaration of the class or `undefined` if it is not a "class".
|
||||
*/
|
||||
getClassDeclaration(node: ts.Node): ClassDeclaration|undefined {
|
||||
if (ts.isVariableDeclaration(node) && node.initializer) {
|
||||
node = node.initializer;
|
||||
}
|
||||
|
||||
if (!ts.isClassDeclaration(node) && !ts.isClassExpression(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return hasNameIdentifier(node) ? node : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a symbol for a node that we think is a class.
|
||||
* @param node the node whose symbol we are finding.
|
||||
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
|
||||
*/
|
||||
getClassSymbol(declaration: ts.Node): ClassSymbol|undefined {
|
||||
const classDeclaration = this.getClassDeclaration(declaration);
|
||||
return classDeclaration &&
|
||||
this.checker.getSymbolAtLocation(classDeclaration.name) as ClassSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine a declaration (for example, of a class or function) and return metadata about any
|
||||
* decorators present on the declaration.
|
||||
@ -79,89 +110,19 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* Examine a declaration which should be of a class, and return metadata about the members of the
|
||||
* class.
|
||||
*
|
||||
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
|
||||
* reflect. If the source is in ES6 format, this will be a `ts.ClassDeclaration` node. If the
|
||||
* source is in ES5 format, this might be a `ts.VariableDeclaration` as classes in ES5 are
|
||||
* represented as the result of an IIFE execution.
|
||||
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
|
||||
*
|
||||
* @returns an array of `ClassMember` metadata representing the members of the class.
|
||||
*
|
||||
* @throws if `declaration` does not resolve to a class declaration.
|
||||
*/
|
||||
getMembersOfClass(clazz: ts.Declaration): ClassMember[] {
|
||||
const members: ClassMember[] = [];
|
||||
const symbol = this.getClassSymbol(clazz);
|
||||
if (!symbol) {
|
||||
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
|
||||
const classSymbol = this.getClassSymbol(clazz);
|
||||
if (!classSymbol) {
|
||||
throw new Error(`Attempted to get members of a non-class: "${clazz.getText()}"`);
|
||||
}
|
||||
|
||||
// The decorators map contains all the properties that are decorated
|
||||
const decoratorsMap = this.getMemberDecorators(symbol);
|
||||
|
||||
// The member map contains all the method (instance and static); and any instance properties
|
||||
// that are initialized in the class.
|
||||
if (symbol.members) {
|
||||
symbol.members.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The static property map contains all the static properties
|
||||
if (symbol.exports) {
|
||||
symbol.exports.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators, true);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If this class was declared as a VariableDeclaration then it may have static properties
|
||||
// attached to the variable rather than the class itself
|
||||
// For example:
|
||||
// ```
|
||||
// let MyClass = class MyClass {
|
||||
// // no static properties here!
|
||||
// }
|
||||
// MyClass.staticProperty = ...;
|
||||
// ```
|
||||
if (ts.isVariableDeclaration(symbol.valueDeclaration.parent)) {
|
||||
const variableSymbol = this.checker.getSymbolAtLocation(symbol.valueDeclaration.parent.name);
|
||||
if (variableSymbol && variableSymbol.exports) {
|
||||
variableSymbol.exports.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators, true);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with any decorated properties that were not initialized in the class
|
||||
decoratorsMap.forEach((value, key) => {
|
||||
members.push({
|
||||
implementation: null,
|
||||
decorators: value,
|
||||
isStatic: false,
|
||||
kind: ClassMemberKind.Property,
|
||||
name: key,
|
||||
nameNode: null,
|
||||
node: null,
|
||||
type: null,
|
||||
value: null
|
||||
});
|
||||
});
|
||||
|
||||
return members;
|
||||
return this.getMembersOfSymbol(classSymbol);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,10 +131,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* This method only looks at the constructor of a class directly and not at any inherited
|
||||
* constructors.
|
||||
*
|
||||
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
|
||||
* reflect. If the source is in ES6 format, this will be a `ts.ClassDeclaration` node. If the
|
||||
* source is in ES5 format, this might be a `ts.VariableDeclaration` as classes in ES5 are
|
||||
* represented as the result of an IIFE execution.
|
||||
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
|
||||
*
|
||||
* @returns an array of `Parameter` metadata representing the parameters of the constructor, if
|
||||
* a constructor exists. If the constructor exists and has 0 parameters, this array will be empty.
|
||||
@ -181,7 +139,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
*
|
||||
* @throws if `declaration` does not resolve to a class declaration.
|
||||
*/
|
||||
getConstructorParameters(clazz: ts.Declaration): CtorParameter[]|null {
|
||||
getConstructorParameters(clazz: ClassDeclaration): CtorParameter[]|null {
|
||||
const classSymbol = this.getClassSymbol(clazz);
|
||||
if (!classSymbol) {
|
||||
throw new Error(
|
||||
@ -194,24 +152,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a symbol for a node that we think is a class.
|
||||
* @param node the node whose symbol we are finding.
|
||||
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
|
||||
*/
|
||||
getClassSymbol(declaration: ts.Node): ts.Symbol|undefined {
|
||||
if (ts.isClassDeclaration(declaration)) {
|
||||
return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
|
||||
}
|
||||
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
||||
declaration = declaration.initializer;
|
||||
}
|
||||
if (ts.isClassExpression(declaration)) {
|
||||
return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given module for variable declarations in which the initializer
|
||||
* is an identifier marked with the `PRE_R3_MARKER`.
|
||||
@ -325,10 +265,12 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
/**
|
||||
* Get the number of generic type parameters of a given class.
|
||||
*
|
||||
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
|
||||
*
|
||||
* @returns the number of type parameters of the class, if known, or `null` if the declaration
|
||||
* is not a class or has an unknown number of type parameters.
|
||||
*/
|
||||
getGenericArityOfClass(clazz: ts.Declaration): number|null {
|
||||
getGenericArityOfClass(clazz: ClassDeclaration): number|null {
|
||||
const dtsDeclaration = this.getDtsDeclaration(clazz);
|
||||
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
|
||||
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
|
||||
@ -374,16 +316,20 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
if (this.isClass(declaration.node)) {
|
||||
this.getMembersOfClass(declaration.node).forEach(member => {
|
||||
if (member.isStatic) {
|
||||
const info = this.parseForModuleWithProviders(member.node);
|
||||
const info = this.parseForModuleWithProviders(
|
||||
member.name, member.node, member.implementation, declaration.node);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const info = this.parseForModuleWithProviders(declaration.node);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
if (isNamedDeclaration(declaration.node)) {
|
||||
const info =
|
||||
this.parseForModuleWithProviders(declaration.node.name.text, declaration.node);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -392,7 +338,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
protected getDecoratorsOfSymbol(symbol: ts.Symbol): Decorator[]|null {
|
||||
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
@ -401,7 +347,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
}
|
||||
|
||||
protected getDecoratedClassFromSymbol(symbol: ts.Symbol|undefined): DecoratedClass|null {
|
||||
protected getDecoratedClassFromSymbol(symbol: ClassSymbol|undefined): DecoratedClass|null {
|
||||
if (symbol) {
|
||||
const decorators = this.getDecoratorsOfSymbol(symbol);
|
||||
if (decorators && decorators.length) {
|
||||
@ -439,7 +385,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @param propertyName the name of static property.
|
||||
* @returns the symbol if it is found or `undefined` if not.
|
||||
*/
|
||||
protected getStaticProperty(symbol: ts.Symbol, propertyName: ts.__String): ts.Symbol|undefined {
|
||||
protected getStaticProperty(symbol: ClassSymbol, propertyName: ts.__String): ts.Symbol|undefined {
|
||||
return symbol.exports && symbol.exports.get(propertyName);
|
||||
}
|
||||
|
||||
@ -485,7 +431,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @param symbol the class whose decorators we want to get.
|
||||
* @returns an array of decorators or null if none where found.
|
||||
*/
|
||||
protected getClassDecoratorsFromHelperCall(symbol: ts.Symbol): Decorator[]|null {
|
||||
protected getClassDecoratorsFromHelperCall(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decorators: Decorator[] = [];
|
||||
const helperCalls = this.getHelperCallsForClass(symbol, '__decorate');
|
||||
helperCalls.forEach(helperCall => {
|
||||
@ -497,13 +443,91 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return decorators.length ? decorators : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine a symbol which should be of a class, and return metadata about its members.
|
||||
*
|
||||
* @param symbol the `ClassSymbol` representing the class over which to reflect.
|
||||
* @returns an array of `ClassMember` metadata representing the members of the class.
|
||||
*/
|
||||
protected getMembersOfSymbol(symbol: ClassSymbol): ClassMember[] {
|
||||
const members: ClassMember[] = [];
|
||||
|
||||
// The decorators map contains all the properties that are decorated
|
||||
const decoratorsMap = this.getMemberDecorators(symbol);
|
||||
|
||||
// The member map contains all the method (instance and static); and any instance properties
|
||||
// that are initialized in the class.
|
||||
if (symbol.members) {
|
||||
symbol.members.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The static property map contains all the static properties
|
||||
if (symbol.exports) {
|
||||
symbol.exports.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators, true);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If this class was declared as a VariableDeclaration then it may have static properties
|
||||
// attached to the variable rather than the class itself
|
||||
// For example:
|
||||
// ```
|
||||
// let MyClass = class MyClass {
|
||||
// // no static properties here!
|
||||
// }
|
||||
// MyClass.staticProperty = ...;
|
||||
// ```
|
||||
if (ts.isVariableDeclaration(symbol.valueDeclaration.parent)) {
|
||||
const variableSymbol = this.checker.getSymbolAtLocation(symbol.valueDeclaration.parent.name);
|
||||
if (variableSymbol && variableSymbol.exports) {
|
||||
variableSymbol.exports.forEach((value, key) => {
|
||||
const decorators = decoratorsMap.get(key as string);
|
||||
const reflectedMembers = this.reflectMembers(value, decorators, true);
|
||||
if (reflectedMembers) {
|
||||
decoratorsMap.delete(key as string);
|
||||
members.push(...reflectedMembers);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with any decorated properties that were not initialized in the class
|
||||
decoratorsMap.forEach((value, key) => {
|
||||
members.push({
|
||||
implementation: null,
|
||||
decorators: value,
|
||||
isStatic: false,
|
||||
kind: ClassMemberKind.Property,
|
||||
name: key,
|
||||
nameNode: null,
|
||||
node: null,
|
||||
type: null,
|
||||
value: null
|
||||
});
|
||||
});
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the member decorators for the given class.
|
||||
* @param classSymbol the class whose member decorators we are interested in.
|
||||
* @returns a map whose keys are the name of the members and whose values are collections of
|
||||
* decorators for the given member.
|
||||
*/
|
||||
protected getMemberDecorators(classSymbol: ts.Symbol): Map<string, Decorator[]> {
|
||||
protected getMemberDecorators(classSymbol: ClassSymbol): Map<string, Decorator[]> {
|
||||
const decoratorsProperty = this.getStaticProperty(classSymbol, PROP_DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getMemberDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
@ -559,7 +583,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @returns a map whose keys are the name of the members and whose values are collections of
|
||||
* decorators for the given member.
|
||||
*/
|
||||
protected getMemberDecoratorsFromHelperCalls(classSymbol: ts.Symbol): Map<string, Decorator[]> {
|
||||
protected getMemberDecoratorsFromHelperCalls(classSymbol: ClassSymbol): Map<string, Decorator[]> {
|
||||
const memberDecoratorMap = new Map<string, Decorator[]>();
|
||||
const helperCalls = this.getHelperCallsForClass(classSymbol, '__decorate');
|
||||
helperCalls.forEach(helperCall => {
|
||||
@ -659,8 +683,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* Matching statements will look like: `tslib_1.__decorate(...);`.
|
||||
* @param statement the statement that may contain the call.
|
||||
* @param helperName the name of the helper we are looking for.
|
||||
* @returns the node that corresponds to the `__decorate(...)` call or null if the statement does
|
||||
* not match.
|
||||
* @returns the node that corresponds to the `__decorate(...)` call or null if the statement
|
||||
* does not match.
|
||||
*/
|
||||
protected getHelperCall(statement: ts.Statement, helperName: string): ts.CallExpression|null {
|
||||
if (ts.isExpressionStatement(statement)) {
|
||||
@ -858,7 +882,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @returns an array of `ts.ParameterDeclaration` objects representing each of the parameters in
|
||||
* the class's constructor or null if there is no constructor.
|
||||
*/
|
||||
protected getConstructorParameterDeclarations(classSymbol: ts.Symbol):
|
||||
protected getConstructorParameterDeclarations(classSymbol: ClassSymbol):
|
||||
ts.ParameterDeclaration[]|null {
|
||||
const constructorSymbol = classSymbol.members && classSymbol.members.get(CONSTRUCTOR);
|
||||
if (constructorSymbol) {
|
||||
@ -887,7 +911,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @returns an array of constructor parameter info objects.
|
||||
*/
|
||||
protected getConstructorParamInfo(
|
||||
classSymbol: ts.Symbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
|
||||
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
|
||||
const paramsProperty = this.getStaticProperty(classSymbol, CONSTRUCTOR_PARAMS);
|
||||
const paramInfo: ParamInfo[]|null = paramsProperty ?
|
||||
this.getParamInfoFromStaticProperty(paramsProperty) :
|
||||
@ -961,7 +985,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @returns an array of objects containing the type and decorators for each parameter.
|
||||
*/
|
||||
protected getParamInfoFromHelperCall(
|
||||
classSymbol: ts.Symbol, parameterNodes: ts.ParameterDeclaration[]): ParamInfo[] {
|
||||
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): ParamInfo[] {
|
||||
const parameters: ParamInfo[] =
|
||||
parameterNodes.map(() => ({typeExpression: null, decorators: null}));
|
||||
const helperCalls = this.getHelperCallsForClass(classSymbol, '__decorate');
|
||||
@ -1004,10 +1028,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
/**
|
||||
* Search statements related to the given class for calls to the specified helper.
|
||||
* @param classSymbol the class whose helper calls we are interested in.
|
||||
* @param helperName the name of the helper (e.g. `__decorate`) whose calls we are interested in.
|
||||
* @param helperName the name of the helper (e.g. `__decorate`) whose calls we are interested
|
||||
* in.
|
||||
* @returns an array of CallExpression nodes for each matching helper call.
|
||||
*/
|
||||
protected getHelperCallsForClass(classSymbol: ts.Symbol, helperName: string):
|
||||
protected getHelperCallsForClass(classSymbol: ClassSymbol, helperName: string):
|
||||
ts.CallExpression[] {
|
||||
return this.getStatementsForClass(classSymbol)
|
||||
.map(statement => this.getHelperCall(statement, helperName))
|
||||
@ -1023,7 +1048,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @param classSymbol the class whose helper calls we are interested in.
|
||||
* @returns an array of statements that may contain helper calls.
|
||||
*/
|
||||
protected getStatementsForClass(classSymbol: ts.Symbol): ts.Statement[] {
|
||||
protected getStatementsForClass(classSymbol: ClassSymbol): ts.Statement[] {
|
||||
return Array.from(classSymbol.valueDeclaration.getSourceFile().statements);
|
||||
}
|
||||
|
||||
@ -1099,8 +1124,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* Only the first declaration with a given name is added to the map; subsequent classes will be
|
||||
* ignored.
|
||||
*
|
||||
* We are most interested in classes that are publicly exported from the entry point, so these are
|
||||
* added to the map first, to ensure that they are not ignored.
|
||||
* We are most interested in classes that are publicly exported from the entry point, so these
|
||||
* are added to the map first, to ensure that they are not ignored.
|
||||
*
|
||||
* @param dtsRootFileName The filename of the entry-point to the `dtsTypings` program.
|
||||
* @param dtsProgram The program containing all the typings files.
|
||||
@ -1126,15 +1151,23 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given node, to see if it is a function that returns a `ModuleWithProviders` object.
|
||||
* @param node a node to check to see if it is a function that returns a `ModuleWithProviders`
|
||||
* object.
|
||||
* Parse a function/method node (or its implementation), to see if it returns a
|
||||
* `ModuleWithProviders` object.
|
||||
* @param name The name of the function.
|
||||
* @param node the node to check - this could be a function, a method or a variable declaration.
|
||||
* @param implementation the actual function expression if `node` is a variable declaration.
|
||||
* @param container the class that contains the function, if it is a method.
|
||||
* @returns info about the function if it does return a `ModuleWithProviders` object; `null`
|
||||
* otherwise.
|
||||
*/
|
||||
protected parseForModuleWithProviders(node: ts.Node|null): ModuleWithProvidersFunction|null {
|
||||
const declaration =
|
||||
node && (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) ? node : null;
|
||||
protected parseForModuleWithProviders(
|
||||
name: string, node: ts.Node|null, implementation: ts.Node|null = node,
|
||||
container: ts.Declaration|null = null): ModuleWithProvidersFunction|null {
|
||||
const declaration = implementation &&
|
||||
(ts.isFunctionDeclaration(implementation) || ts.isMethodDeclaration(implementation) ||
|
||||
ts.isFunctionExpression(implementation)) ?
|
||||
implementation :
|
||||
null;
|
||||
const body = declaration ? this.getDefinitionOfFunction(declaration).body : null;
|
||||
const lastStatement = body && body[body.length - 1];
|
||||
const returnExpression =
|
||||
@ -1147,7 +1180,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
const ngModule = ngModuleProperty && ts.isPropertyAssignment(ngModuleProperty) &&
|
||||
ts.isIdentifier(ngModuleProperty.initializer) && ngModuleProperty.initializer ||
|
||||
null;
|
||||
return ngModule && declaration && {ngModule, declaration};
|
||||
return ngModule && declaration && {name, ngModule, declaration, container};
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,13 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, reflectObjectLiteral} from '../../../ngtsc/reflection';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Declaration, Decorator, FunctionDefinition, Parameter, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {getNameText, hasNameIdentifier} from '../utils';
|
||||
|
||||
import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignmentStatement} from './esm2015_host';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ESM5 packages contain ECMAScript IIFE functions that act like classes. For example:
|
||||
*
|
||||
@ -32,26 +33,26 @@ import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignme
|
||||
*
|
||||
*/
|
||||
export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
constructor(isCore: boolean, checker: ts.TypeChecker) { super(isCore, checker); }
|
||||
|
||||
/**
|
||||
* Check whether the given node actually represents a class.
|
||||
*/
|
||||
isClass(node: ts.Node): node is ts.NamedDeclaration {
|
||||
return super.isClass(node) || !!this.getClassSymbol(node);
|
||||
}
|
||||
isClass(node: ts.Node): node is ClassDeclaration { return !!this.getClassDeclaration(node); }
|
||||
|
||||
/**
|
||||
* Determines whether the given declaration has a base class.
|
||||
* Determines whether the given declaration, which should be a "class", has a base "class".
|
||||
*
|
||||
* In ES5, we need to determine if the IIFE wrapper takes a `_super` parameter .
|
||||
* In ES5 code, we need to determine if the IIFE wrapper takes a `_super` parameter .
|
||||
*
|
||||
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
|
||||
*/
|
||||
hasBaseClass(node: ts.Declaration): boolean {
|
||||
const classSymbol = this.getClassSymbol(node);
|
||||
if (!classSymbol) return false;
|
||||
hasBaseClass(clazz: ClassDeclaration): boolean {
|
||||
if (super.hasBaseClass(clazz)) return true;
|
||||
|
||||
const iifeBody = classSymbol.valueDeclaration.parent;
|
||||
if (!iifeBody || !ts.isBlock(iifeBody)) return false;
|
||||
const classDeclaration = this.getClassDeclaration(clazz);
|
||||
if (!classDeclaration) return false;
|
||||
|
||||
const iifeBody = getIifeBody(classDeclaration);
|
||||
if (!iifeBody) return false;
|
||||
|
||||
const iife = iifeBody.parent;
|
||||
if (!iife || !ts.isFunctionExpression(iife)) return false;
|
||||
@ -60,38 +61,39 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a symbol for a node that we think is a class.
|
||||
* Find the declaration of a class given a node that we think represents the class.
|
||||
*
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE.
|
||||
* So we might need to dig around inside to get hold of the "class" symbol.
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE,
|
||||
* whose value is assigned to a variable (which represents the class to the rest of the program).
|
||||
* So we might need to dig around to get hold of the "class" declaration.
|
||||
*
|
||||
* `node` might be one of:
|
||||
* - A class declaration (from a declaration file).
|
||||
* - A class declaration (from a typings file).
|
||||
* - The declaration of the outer variable, which is assigned the result of the IIFE.
|
||||
* - The function declaration inside the IIFE, which is eventually returned and assigned to the
|
||||
* outer variable.
|
||||
*
|
||||
* @param node the top level declaration that represents an exported class or the function
|
||||
* expression inside the IIFE.
|
||||
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
|
||||
* The returned declaration is either the class declaration (from the typings file) or the outer
|
||||
* variable declaration.
|
||||
*
|
||||
* @param node the node that represents the class whose declaration we are finding.
|
||||
* @returns the declaration of the class or `undefined` if it is not a "class".
|
||||
*/
|
||||
getClassSymbol(node: ts.Node): ts.Symbol|undefined {
|
||||
const symbol = super.getClassSymbol(node);
|
||||
if (symbol) return symbol;
|
||||
getClassDeclaration(node: ts.Node): ClassDeclaration|undefined {
|
||||
const superDeclaration = super.getClassDeclaration(node);
|
||||
if (superDeclaration) return superDeclaration;
|
||||
|
||||
if (ts.isVariableDeclaration(node)) {
|
||||
const iifeBody = getIifeBody(node);
|
||||
if (!iifeBody) return undefined;
|
||||
const outerClass = getClassDeclarationFromInnerFunctionDeclaration(node);
|
||||
if (outerClass) return outerClass;
|
||||
|
||||
const innerClassIdentifier = getReturnIdentifier(iifeBody);
|
||||
if (!innerClassIdentifier) return undefined;
|
||||
|
||||
return this.checker.getSymbolAtLocation(innerClassIdentifier);
|
||||
// At this point, `node` could be the outer variable declaration of an ES5 class.
|
||||
// If so, ensure that it has a `name` identifier and the correct structure.
|
||||
if (!isNamedVariableDeclaration(node) ||
|
||||
!this.getInnerFunctionDeclarationFromClassDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const outerClassNode = getClassDeclarationFromInnerFunctionDeclaration(node);
|
||||
|
||||
return outerClassNode && this.getClassSymbol(outerClassNode);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,13 +116,30 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
||||
// Get the identifier for the outer class node (if any).
|
||||
const outerClassNode = getClassDeclarationFromInnerFunctionDeclaration(id.parent);
|
||||
const declaration = super.getDeclarationOfIdentifier(outerClassNode ? outerClassNode.name : id);
|
||||
|
||||
if (outerClassNode && hasNameIdentifier(outerClassNode)) {
|
||||
id = outerClassNode.name;
|
||||
if (!declaration || !ts.isVariableDeclaration(declaration.node) ||
|
||||
declaration.node.initializer !== undefined ||
|
||||
// VariableDeclaration => VariableDeclarationList => VariableStatement => IIFE Block
|
||||
!ts.isBlock(declaration.node.parent.parent.parent)) {
|
||||
return declaration;
|
||||
}
|
||||
|
||||
// Resolve the identifier to a Symbol, and return the declaration of that.
|
||||
return super.getDeclarationOfIdentifier(id);
|
||||
// We might have an alias to another variable declaration.
|
||||
// Search the containing iife body for it.
|
||||
const block = declaration.node.parent.parent.parent;
|
||||
const aliasSymbol = this.checker.getSymbolAtLocation(declaration.node.name);
|
||||
for (let i = 0; i < block.statements.length; i++) {
|
||||
const statement = block.statements[i];
|
||||
// Looking for statement that looks like: `AliasedVariable = OriginalVariable;`
|
||||
if (isAssignmentStatement(statement) && ts.isIdentifier(statement.expression.left) &&
|
||||
ts.isIdentifier(statement.expression.right) &&
|
||||
this.checker.getSymbolAtLocation(statement.expression.left) === aliasSymbol) {
|
||||
return this.getDeclarationOfIdentifier(statement.expression.right);
|
||||
}
|
||||
}
|
||||
|
||||
return declaration;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,31 +168,149 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return {node, body: statements || null, parameters};
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine a declaration which should be of a class, and return metadata about the members of the
|
||||
* class.
|
||||
*
|
||||
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
|
||||
* reflect.
|
||||
*
|
||||
* @returns an array of `ClassMember` metadata representing the members of the class.
|
||||
*
|
||||
* @throws if `declaration` does not resolve to a class declaration.
|
||||
*/
|
||||
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
|
||||
if (super.isClass(clazz)) return super.getMembersOfClass(clazz);
|
||||
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol = this.getInnerFunctionSymbolFromClassDeclaration(clazz);
|
||||
if (!innerFunctionSymbol) {
|
||||
throw new Error(
|
||||
`Attempted to get members of a non-class: "${(clazz as ClassDeclaration).getText()}"`);
|
||||
}
|
||||
|
||||
return this.getMembersOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
/**
|
||||
* Get the inner function declaration of an ES5-style class.
|
||||
*
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE
|
||||
* and returned to be assigned to a variable outside the IIFE, which is what the rest of the
|
||||
* program interacts with.
|
||||
*
|
||||
* Given the outer variable declaration, we want to get to the inner function declaration.
|
||||
*
|
||||
* @param node a node that could be the variable expression outside an ES5 class IIFE.
|
||||
* @param checker the TS program TypeChecker
|
||||
* @returns the inner function declaration or `undefined` if it is not a "class".
|
||||
*/
|
||||
protected getInnerFunctionDeclarationFromClassDeclaration(node: ts.Node): ts.FunctionDeclaration
|
||||
|undefined {
|
||||
if (!ts.isVariableDeclaration(node)) return undefined;
|
||||
|
||||
// Extract the IIFE body (if any).
|
||||
const iifeBody = getIifeBody(node);
|
||||
if (!iifeBody) return undefined;
|
||||
|
||||
// Extract the function declaration from inside the IIFE.
|
||||
const functionDeclaration = iifeBody.statements.find(ts.isFunctionDeclaration);
|
||||
if (!functionDeclaration) return undefined;
|
||||
|
||||
// Extract the return identifier of the IIFE.
|
||||
const returnIdentifier = getReturnIdentifier(iifeBody);
|
||||
const returnIdentifierSymbol =
|
||||
returnIdentifier && this.checker.getSymbolAtLocation(returnIdentifier);
|
||||
if (!returnIdentifierSymbol) return undefined;
|
||||
|
||||
// Verify that the inner function is returned.
|
||||
if (returnIdentifierSymbol.valueDeclaration !== functionDeclaration) return undefined;
|
||||
|
||||
return functionDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier symbol of the inner function declaration of an ES5-style class.
|
||||
*
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE
|
||||
* and returned to be assigned to a variable outside the IIFE, which is what the rest of the
|
||||
* program interacts with.
|
||||
*
|
||||
* Given the outer variable declaration, we want to get to the identifier symbol of the inner
|
||||
* function declaration.
|
||||
*
|
||||
* @param clazz a node that could be the variable expression outside an ES5 class IIFE.
|
||||
* @param checker the TS program TypeChecker
|
||||
* @returns the inner function declaration identifier symbol or `undefined` if it is not a "class"
|
||||
* or has no identifier.
|
||||
*/
|
||||
protected getInnerFunctionSymbolFromClassDeclaration(clazz: ClassDeclaration): ClassSymbol
|
||||
|undefined {
|
||||
const innerFunctionDeclaration = this.getInnerFunctionDeclarationFromClassDeclaration(clazz);
|
||||
if (!innerFunctionDeclaration || !hasNameIdentifier(innerFunctionDeclaration)) return undefined;
|
||||
|
||||
return this.checker.getSymbolAtLocation(innerFunctionDeclaration.name) as ClassSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the declarations of the constructor parameters of a class identified by its symbol.
|
||||
*
|
||||
* In ESM5 there is no "class" so the constructor that we want is actually the declaration
|
||||
* function itself.
|
||||
* In ESM5, there is no "class" so the constructor that we want is actually the inner function
|
||||
* declaration inside the IIFE, whose return value is assigned to the outer variable declaration
|
||||
* (that represents the class to the rest of the program).
|
||||
*
|
||||
* @param classSymbol the class whose parameters we want to find.
|
||||
* @param classSymbol the symbol of the class (i.e. the outer variable declaration) whose
|
||||
* parameters we want to find.
|
||||
* @returns an array of `ts.ParameterDeclaration` objects representing each of the parameters in
|
||||
* the class's constructor or null if there is no constructor.
|
||||
* the class's constructor or `null` if there is no constructor.
|
||||
*/
|
||||
protected getConstructorParameterDeclarations(classSymbol: ts.Symbol):
|
||||
protected getConstructorParameterDeclarations(classSymbol: ClassSymbol):
|
||||
ts.ParameterDeclaration[]|null {
|
||||
const constructor = classSymbol.valueDeclaration as ts.FunctionDeclaration;
|
||||
const constructor =
|
||||
this.getInnerFunctionDeclarationFromClassDeclaration(classSymbol.valueDeclaration);
|
||||
if (!constructor) return null;
|
||||
|
||||
if (constructor.parameters.length > 0) {
|
||||
return Array.from(constructor.parameters);
|
||||
}
|
||||
|
||||
if (isSynthesizedConstructor(constructor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter decorators of a class constructor.
|
||||
*
|
||||
* @param classSymbol the symbol of the class (i.e. the outer variable declaration) whose
|
||||
* parameter info we want to get.
|
||||
* @param parameterNodes the array of TypeScript parameter nodes for this class's constructor.
|
||||
* @returns an array of constructor parameter info objects.
|
||||
*/
|
||||
protected getConstructorParamInfo(
|
||||
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol =
|
||||
this.getInnerFunctionSymbolFromClassDeclaration(classSymbol.valueDeclaration);
|
||||
if (!innerFunctionSymbol) return [];
|
||||
|
||||
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.
|
||||
@ -289,10 +426,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
* to reference the inner identifier inside the IIFE.
|
||||
* @returns an array of statements that may contain helper calls.
|
||||
*/
|
||||
protected getStatementsForClass(classSymbol: ts.Symbol): ts.Statement[] {
|
||||
const classDeclaration = classSymbol.valueDeclaration;
|
||||
return ts.isBlock(classDeclaration.parent) ? Array.from(classDeclaration.parent.statements) :
|
||||
[];
|
||||
protected getStatementsForClass(classSymbol: ClassSymbol): ts.Statement[] {
|
||||
const classDeclarationParent = classSymbol.valueDeclaration.parent;
|
||||
return ts.isBlock(classDeclarationParent) ? Array.from(classDeclarationParent.statements) : [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,8 +503,8 @@ function readPropertyFunctionExpression(object: ts.ObjectLiteralExpression, name
|
||||
* @param node a node that could be the function expression inside an ES5 class IIFE.
|
||||
* @returns the outer variable declaration or `undefined` if it is not a "class".
|
||||
*/
|
||||
function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node): ts.VariableDeclaration|
|
||||
undefined {
|
||||
function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node):
|
||||
ClassDeclaration<ts.VariableDeclaration>|undefined {
|
||||
if (ts.isFunctionDeclaration(node)) {
|
||||
// It might be the function expression inside the IIFE. We need to go 5 levels up...
|
||||
|
||||
@ -392,14 +528,16 @@ function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node): ts.Vari
|
||||
outerNode = outerNode.parent;
|
||||
if (!outerNode || !ts.isVariableDeclaration(outerNode)) return undefined;
|
||||
|
||||
return outerNode;
|
||||
// Finally, ensure that the variable declaration has a `name` identifier.
|
||||
return hasNameIdentifier(outerNode) ? outerNode : undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getIifeBody(declaration: ts.VariableDeclaration): ts.Block|undefined {
|
||||
if (!declaration.initializer || !ts.isParenthesizedExpression(declaration.initializer)) {
|
||||
export function getIifeBody(declaration: ts.Declaration): ts.Block|undefined {
|
||||
if (!ts.isVariableDeclaration(declaration) || !declaration.initializer ||
|
||||
!ts.isParenthesizedExpression(declaration.initializer)) {
|
||||
return undefined;
|
||||
}
|
||||
const call = declaration.initializer;
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {ReflectionHost} from '../../../ngtsc/reflection';
|
||||
import {ClassSymbol, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
|
||||
export const PRE_R3_MARKER = '__PRE_R3__';
|
||||
@ -24,10 +24,18 @@ export function isSwitchableVariableDeclaration(node: ts.Node):
|
||||
* that return ModuleWithProviders objects.
|
||||
*/
|
||||
export interface ModuleWithProvidersFunction {
|
||||
/**
|
||||
* The name of the declared function.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The declaration of the function that returns the `ModuleWithProviders` object.
|
||||
*/
|
||||
declaration: ts.SignatureDeclaration;
|
||||
/**
|
||||
* Declaration of the containing class (if this is a method)
|
||||
*/
|
||||
container: ts.Declaration|null;
|
||||
/**
|
||||
* The identifier of the `ngModule` property on the `ModuleWithProviders` object.
|
||||
*/
|
||||
@ -44,7 +52,7 @@ export interface NgccReflectionHost extends ReflectionHost {
|
||||
* @returns the symbol for the declaration or `undefined` if it is not
|
||||
* a "class" or has no symbol.
|
||||
*/
|
||||
getClassSymbol(node: ts.Node): ts.Symbol|undefined;
|
||||
getClassSymbol(node: ts.Node): ClassSymbol|undefined;
|
||||
|
||||
/**
|
||||
* Search the given module for variable declarations in which the initializer
|
154
packages/compiler-cli/ngcc/src/main.ts
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @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 {resolve} from 'canonical-path';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../src/ngtsc/path';
|
||||
|
||||
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 {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.
|
||||
*/
|
||||
export interface NgccOptions {
|
||||
/** The absolute path to the `node_modules` folder that contains the packages to process. */
|
||||
basePath: string;
|
||||
/**
|
||||
* The path, relative to `basePath` to the primary package to be processed.
|
||||
*
|
||||
* All its dependencies will need to be processed too.
|
||||
*/
|
||||
targetEntryPointPath?: string;
|
||||
/**
|
||||
* Which entry-point properties in the package.json to consider when processing an entry-point.
|
||||
* Each property should hold a path to the particular bundle format for the entry-point.
|
||||
* Defaults to all the properties in the package.json.
|
||||
*/
|
||||
propertiesToConsider?: string[];
|
||||
/**
|
||||
* Whether to process all formats specified by (`propertiesToConsider`) or to stop processing
|
||||
* this entry-point at the first matching format. Defaults to `true`.
|
||||
*/
|
||||
compileAllFormats?: boolean;
|
||||
/**
|
||||
* Whether to create new entry-points bundles rather than overwriting the original files.
|
||||
*/
|
||||
createNewEntryPointFormats?: boolean;
|
||||
}
|
||||
|
||||
const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
|
||||
|
||||
/**
|
||||
* This is the main entry-point into ngcc (aNGular Compatibility Compiler).
|
||||
*
|
||||
* You can call this function to process one or more npm packages, to ensure
|
||||
* that they are compatible with the ivy compiler (ngtsc).
|
||||
*
|
||||
* @param options The options telling ngcc what to compile and how.
|
||||
*/
|
||||
export function mainNgcc(
|
||||
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
|
||||
compileAllFormats = true, createNewEntryPointFormats = false}: NgccOptions): void {
|
||||
const transformer = new Transformer(basePath, basePath);
|
||||
const host = new DependencyHost();
|
||||
const resolver = new DependencyResolver(host);
|
||||
const finder = new EntryPointFinder(resolver);
|
||||
const fileWriter = getFileWriter(createNewEntryPointFormats);
|
||||
|
||||
const absoluteTargetEntryPointPath = targetEntryPointPath ?
|
||||
AbsoluteFsPath.from(resolve(basePath, targetEntryPointPath)) :
|
||||
undefined;
|
||||
const {entryPoints} =
|
||||
finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath);
|
||||
|
||||
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);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
entryPoints.forEach(entryPoint => {
|
||||
// Are we compiling the Angular core?
|
||||
const isCore = entryPoint.name === '@angular/core';
|
||||
|
||||
const compiledFormats = new Set<string>();
|
||||
const entryPointPackageJson = entryPoint.packageJson;
|
||||
const entryPointPackageJsonPath = AbsoluteFsPath.from(resolve(entryPoint.path, 'package.json'));
|
||||
|
||||
for (let i = 0; i < propertiesToConsider.length; i++) {
|
||||
const property = propertiesToConsider[i] as EntryPointJsonProperty;
|
||||
const formatPath = entryPointPackageJson[property];
|
||||
const format = getEntryPointFormat(property);
|
||||
|
||||
// No format then this property is not supposed to be compiled.
|
||||
if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue;
|
||||
|
||||
if (hasBeenProcessed(entryPointPackageJson, property)) {
|
||||
compiledFormats.add(formatPath);
|
||||
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't break if this if statement fails because we still want to mark
|
||||
// the property as processed even if its underlying format has been built already.
|
||||
if (!compiledFormats.has(formatPath) && (compileAllFormats || compiledFormats.size === 0)) {
|
||||
const bundle = makeEntryPointBundle(
|
||||
entryPoint.path, formatPath, entryPoint.typings, isCore, property, format,
|
||||
compiledFormats.size === 0);
|
||||
if (bundle) {
|
||||
console.warn(`Compiling ${entryPoint.name} : ${property} as ${format}`);
|
||||
const transformedFiles = transformer.transform(bundle);
|
||||
fileWriter.writeBundle(entryPoint, bundle, transformedFiles);
|
||||
compiledFormats.add(formatPath);
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping ${entryPoint.name} : ${format} (no valid entry point file for this format).`);
|
||||
}
|
||||
} else if (!compileAllFormats) {
|
||||
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (compiledFormats.size === 0) {
|
||||
throw new Error(
|
||||
`Failed to compile any formats for entry-point at (${entryPoint.path}). Tried ${propertiesToConsider}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getFileWriter(createNewEntryPointFormats: boolean): FileWriter {
|
||||
return createNewEntryPointFormats ? new NewEntryPointFileWriter() : new InPlaceFileWriter();
|
||||
}
|
57
packages/compiler-cli/ngcc/src/packages/build_marker.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @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 {writeFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
|
||||
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
|
||||
|
||||
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
|
||||
|
||||
/**
|
||||
* Check whether ngcc has already processed a given entry-point format.
|
||||
*
|
||||
* The entry-point is defined by the package.json contents provided.
|
||||
* The format is defined by the provided property name of the path to the bundle in the package.json
|
||||
*
|
||||
* @param packageJson The parsed contents of the package.json file for the entry-point.
|
||||
* @param format The entry-point format property in the package.json to check.
|
||||
* @returns true if the entry-point and format have already been processed with this ngcc version.
|
||||
* @throws Error if the `packageJson` property is not an object.
|
||||
* @throws Error if the entry-point has already been processed with a different ngcc version.
|
||||
*/
|
||||
export function hasBeenProcessed(
|
||||
packageJson: EntryPointPackageJson, format: EntryPointJsonProperty): boolean {
|
||||
if (!packageJson.__processed_by_ivy_ngcc__) {
|
||||
return false;
|
||||
}
|
||||
if (Object.keys(packageJson.__processed_by_ivy_ngcc__)
|
||||
.some(property => packageJson.__processed_by_ivy_ngcc__ ![property] !== NGCC_VERSION)) {
|
||||
throw new Error(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
}
|
||||
|
||||
return packageJson.__processed_by_ivy_ngcc__[format] === NGCC_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a build marker for the given entry-point and format property, to indicate that it has
|
||||
* been compiled by this version of ngcc.
|
||||
*
|
||||
* @param entryPoint the entry-point to write a marker.
|
||||
* @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,
|
||||
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), 'utf8');
|
||||
}
|
@ -10,6 +10,8 @@ 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.
|
||||
*/
|
||||
@ -17,7 +19,8 @@ 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 resolved A set that will have the absolute paths of resolved entry points added to it.
|
||||
* @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.
|
||||
@ -25,11 +28,16 @@ export class DependencyHost {
|
||||
* circular dependency loop.
|
||||
*/
|
||||
computeDependencies(
|
||||
from: string, resolved: Set<string>, missing: Set<string>, deepImports: Set<string>,
|
||||
internal: Set<string> = new Set()): void {
|
||||
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;
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
|
||||
@ -41,7 +49,7 @@ export class DependencyHost {
|
||||
// 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 => {
|
||||
.forEach((importPath: PathSegment) => {
|
||||
if (importPath.startsWith('.')) {
|
||||
// This is an internal import so follow it
|
||||
const internalDependency = this.resolveInternal(from, importPath);
|
||||
@ -49,12 +57,12 @@ export class DependencyHost {
|
||||
if (!internal.has(internalDependency)) {
|
||||
internal.add(internalDependency);
|
||||
this.computeDependencies(
|
||||
internalDependency, resolved, missing, deepImports, internal);
|
||||
internalDependency, dependencies, missing, deepImports, internal);
|
||||
}
|
||||
} else {
|
||||
const resolvedEntryPoint = this.tryResolveEntryPoint(from, importPath);
|
||||
if (resolvedEntryPoint !== null) {
|
||||
resolved.add(resolvedEntryPoint);
|
||||
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.
|
||||
@ -67,6 +75,7 @@ export class DependencyHost {
|
||||
}
|
||||
}
|
||||
});
|
||||
return {dependencies, missing, deepImports};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,11 +84,11 @@ export class DependencyHost {
|
||||
* @param to the module specifier of the internal dependency to resolve
|
||||
* @returns the resolved path to the import.
|
||||
*/
|
||||
resolveInternal(from: string, to: string): string {
|
||||
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 - unlike `tryResolve` below.
|
||||
return require.resolve(path.resolve(fromDirectory, to));
|
||||
// about it by adding it to a `paths` parameter - unlike `tryResolve` below.
|
||||
return AbsoluteFsPath.from(require.resolve(path.resolve(fromDirectory, to)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,9 +108,9 @@ export class DependencyHost {
|
||||
* @returns the resolved path to the entry point directory of the import or null
|
||||
* if it cannot be resolved.
|
||||
*/
|
||||
tryResolveEntryPoint(from: string, to: string): string|null {
|
||||
const entryPoint = this.tryResolve(from, `${to}/package.json`);
|
||||
return entryPoint && path.dirname(entryPoint);
|
||||
tryResolveEntryPoint(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
|
||||
const entryPoint = this.tryResolve(from, `${to}/package.json` as PathSegment);
|
||||
return entryPoint && AbsoluteFsPath.from(path.dirname(entryPoint));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,9 +121,9 @@ export class DependencyHost {
|
||||
* @returns an absolute path to the entry-point of the dependency or null if it could not be
|
||||
* resolved.
|
||||
*/
|
||||
tryResolve(from: string, to: string): string|null {
|
||||
tryResolve(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
|
||||
try {
|
||||
return require.resolve(to, {paths: [from]});
|
||||
return AbsoluteFsPath.from(require.resolve(to, {paths: [from]}));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
@ -6,9 +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 {DependencyHost} from './dependency_host';
|
||||
import {EntryPoint} from './entry_point';
|
||||
import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat} from './entry_point';
|
||||
|
||||
|
||||
/**
|
||||
@ -44,7 +47,7 @@ export interface IgnoredDependency {
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of sorting the entry-points by their dependencies.
|
||||
* A list of entry-points, sorted by their dependencies.
|
||||
*
|
||||
* The `entryPoints` array will be ordered so that no entry point depends upon an entry point that
|
||||
* appears later in the array.
|
||||
@ -67,28 +70,46 @@ export class DependencyResolver {
|
||||
* Sort the array of entry points so that the dependant entry points always come later than
|
||||
* their dependencies in the array.
|
||||
* @param entryPoints An array entry points to sort.
|
||||
* @returns the result of sorting the entry points.
|
||||
* @param target If provided, only return entry-points depended on by this entry-point.
|
||||
* @returns the result of sorting the entry points by dependency.
|
||||
*/
|
||||
sortEntryPointsByDependency(entryPoints: EntryPoint[]): SortedEntryPointsInfo {
|
||||
sortEntryPointsByDependency(entryPoints: EntryPoint[], target?: EntryPoint):
|
||||
SortedEntryPointsInfo {
|
||||
const {invalidEntryPoints, ignoredDependencies, graph} = this.createDependencyInfo(entryPoints);
|
||||
|
||||
let sortedEntryPointNodes: string[];
|
||||
if (target) {
|
||||
sortedEntryPointNodes = graph.dependenciesOf(target.path);
|
||||
sortedEntryPointNodes.push(target.path);
|
||||
} else {
|
||||
sortedEntryPointNodes = graph.overallOrder();
|
||||
}
|
||||
|
||||
return {
|
||||
entryPoints: sortedEntryPointNodes.map(path => graph.getNodeData(path)),
|
||||
invalidEntryPoints,
|
||||
ignoredDependencies,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a dependency graph of the given entry-points.
|
||||
*
|
||||
* The graph only holds entry-points that ngcc cares about and whose dependencies
|
||||
* (direct and transitive) all exist.
|
||||
*/
|
||||
private createDependencyInfo(entryPoints: EntryPoint[]) {
|
||||
const invalidEntryPoints: InvalidEntryPoint[] = [];
|
||||
const ignoredDependencies: IgnoredDependency[] = [];
|
||||
const graph = new DepGraph<EntryPoint>();
|
||||
|
||||
// Add the entry ponts to the graph as nodes
|
||||
// Add the entry points to the graph as nodes
|
||||
entryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint));
|
||||
|
||||
// Now add the dependencies between them
|
||||
entryPoints.forEach(entryPoint => {
|
||||
const entryPointPath = entryPoint.fesm2015 || entryPoint.esm2015;
|
||||
if (!entryPointPath) {
|
||||
throw new Error(
|
||||
`ESM2015 format (flat and non-flat) missing in '${entryPoint.path}' entry-point.`);
|
||||
}
|
||||
|
||||
const dependencies = new Set<string>();
|
||||
const missing = new Set<string>();
|
||||
const deepImports = new Set<string>();
|
||||
this.host.computeDependencies(entryPointPath, dependencies, missing, deepImports);
|
||||
const entryPointPath = getEntryPointPath(entryPoint);
|
||||
const {dependencies, missing, deepImports} = this.host.computeDependencies(entryPointPath);
|
||||
|
||||
if (missing.size > 0) {
|
||||
// This entry point has dependencies that are missing
|
||||
@ -119,13 +140,7 @@ export class DependencyResolver {
|
||||
}
|
||||
});
|
||||
|
||||
// The map now only holds entry-points that ngcc cares about and whose dependencies
|
||||
// (direct and transitive) all exist.
|
||||
return {
|
||||
entryPoints: graph.overallOrder().map(path => graph.getNodeData(path)),
|
||||
invalidEntryPoints,
|
||||
ignoredDependencies
|
||||
};
|
||||
return {invalidEntryPoints, ignoredDependencies, graph};
|
||||
|
||||
function removeNodes(entryPoint: EntryPoint, missingDependencies: string[]) {
|
||||
const nodesToRemove = [entryPoint.path, ...graph.dependantsOf(entryPoint.path)];
|
||||
@ -136,3 +151,17 @@ 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);
|
||||
|
||||
if (format === 'esm2015' || format === 'esm5') {
|
||||
const formatPath = entryPoint.packageJson[property] !;
|
||||
return AbsoluteFsPath.from(resolve(entryPoint.path, formatPath));
|
||||
}
|
||||
}
|
||||
throw new Error(`There is no format with import statements in '${entryPoint.path}' entry-point.`);
|
||||
}
|
@ -9,43 +9,32 @@
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
|
||||
/**
|
||||
* An object containing paths to the entry-points for each format.
|
||||
*/
|
||||
export interface EntryPointPaths {
|
||||
esm5?: string;
|
||||
fesm5?: string;
|
||||
esm2015?: string;
|
||||
fesm2015?: string;
|
||||
umd?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The possible values for the format of an entry-point.
|
||||
*/
|
||||
export type EntryPointFormat = keyof(EntryPointPaths);
|
||||
export type EntryPointFormat = 'esm5' | 'esm2015' | 'umd';
|
||||
|
||||
/**
|
||||
* An object containing information about an entry-point, including paths
|
||||
* to each of the possible entry-point formats.
|
||||
*/
|
||||
export interface EntryPoint extends EntryPointPaths {
|
||||
export interface EntryPoint {
|
||||
/** The name of the package (e.g. `@angular/core`). */
|
||||
name: string;
|
||||
/** The parsed package.json file for this entry-point. */
|
||||
packageJson: EntryPointPackageJson;
|
||||
/** The path to the package that contains this entry-point. */
|
||||
package: string;
|
||||
package: AbsoluteFsPath;
|
||||
/** The path to this entry point. */
|
||||
path: string;
|
||||
path: AbsoluteFsPath;
|
||||
/** The path to a typings (.d.ts) file for this entry-point. */
|
||||
typings: string;
|
||||
typings: AbsoluteFsPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties that may be loaded from the `package.json` file.
|
||||
*/
|
||||
interface EntryPointPackageJson {
|
||||
name: string;
|
||||
interface PackageJsonFormatProperties {
|
||||
fesm2015?: string;
|
||||
fesm5?: string;
|
||||
es2015?: string; // if exists then it is actually FESM2015
|
||||
@ -57,6 +46,90 @@ interface EntryPointPackageJson {
|
||||
typings?: string; // TypeScript .d.ts files
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties that may be loaded from the `package.json` file.
|
||||
*/
|
||||
export interface EntryPointPackageJson extends PackageJsonFormatProperties {
|
||||
name: string;
|
||||
__processed_by_ivy_ngcc__?: {[key: string]: string};
|
||||
}
|
||||
|
||||
export type EntryPointJsonProperty = keyof(PackageJsonFormatProperties);
|
||||
// We need to keep the elements of this const and the `EntryPointJsonProperty` type in sync.
|
||||
export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
||||
['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module'];
|
||||
|
||||
/**
|
||||
* Try to create an entry-point from the given paths and properties.
|
||||
*
|
||||
* @param packagePath the absolute path to the containing npm package
|
||||
* @param entryPointPath the absolute path to the potential entry-point.
|
||||
* @returns An entry-point if it is valid, `null` otherwise.
|
||||
*/
|
||||
export function getEntryPointInfo(
|
||||
packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryPointPackageJson = loadEntryPointPackage(packageJsonPath);
|
||||
if (!entryPointPackageJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// We must have a typings property
|
||||
const typings = entryPointPackageJson.typings || entryPointPackageJson.types;
|
||||
if (!typings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const entryPointInfo: EntryPoint = {
|
||||
name: entryPointPackageJson.name,
|
||||
packageJson: entryPointPackageJson,
|
||||
package: packagePath,
|
||||
path: entryPointPath,
|
||||
typings: AbsoluteFsPath.from(path.resolve(entryPointPath, typings)),
|
||||
};
|
||||
|
||||
return entryPointInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a package.json property into an entry-point format.
|
||||
*
|
||||
* @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 {
|
||||
switch (property) {
|
||||
case 'fesm2015':
|
||||
return 'esm2015';
|
||||
case 'fesm5':
|
||||
return 'esm5';
|
||||
case 'es2015':
|
||||
return 'esm2015';
|
||||
case 'esm2015':
|
||||
return 'esm2015';
|
||||
case 'esm5':
|
||||
return 'esm5';
|
||||
case 'main':
|
||||
return 'umd';
|
||||
case 'module':
|
||||
return 'esm5';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the JSON from a package.json file.
|
||||
* @param packageJsonPath the absolute path to the package.json file.
|
||||
@ -71,72 +144,3 @@ function loadEntryPointPackage(packageJsonPath: string): EntryPointPackageJson|n
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get an entry point from the given path.
|
||||
* @param packagePath the absolute path to the containing npm package
|
||||
* @param entryPointPath the absolute path to the potential entry point.
|
||||
* @returns Info about the entry point if it is valid, `null` otherwise.
|
||||
*/
|
||||
export function getEntryPointInfo(packagePath: string, entryPointPath: string): EntryPoint|null {
|
||||
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryPointPackageJson = loadEntryPointPackage(packageJsonPath);
|
||||
if (!entryPointPackageJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If there is `esm2015` then `es2015` will be FESM2015, otherwise ESM2015.
|
||||
// If there is `esm5` then `module` will be FESM5, otherwise it will be ESM5.
|
||||
const {
|
||||
name,
|
||||
module: modulePath,
|
||||
types,
|
||||
typings = types, // synonymous
|
||||
es2015,
|
||||
fesm2015 = es2015, // synonymous
|
||||
fesm5 = modulePath, // synonymous
|
||||
esm2015,
|
||||
esm5,
|
||||
main
|
||||
} = entryPointPackageJson;
|
||||
// Minimum requirement is that we have typings and one of esm2015 or fesm2015 formats.
|
||||
if (!typings || !(fesm2015 || esm2015)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const entryPointInfo: EntryPoint = {
|
||||
name,
|
||||
package: packagePath,
|
||||
path: entryPointPath,
|
||||
typings: path.resolve(entryPointPath, typings),
|
||||
};
|
||||
|
||||
if (esm2015) {
|
||||
entryPointInfo.esm2015 = path.resolve(entryPointPath, esm2015);
|
||||
}
|
||||
if (fesm2015) {
|
||||
entryPointInfo.fesm2015 = path.resolve(entryPointPath, fesm2015);
|
||||
}
|
||||
if (fesm5) {
|
||||
entryPointInfo.fesm5 = path.resolve(entryPointPath, fesm5);
|
||||
}
|
||||
if (esm5) {
|
||||
entryPointInfo.esm5 = path.resolve(entryPointPath, esm5);
|
||||
}
|
||||
if (main) {
|
||||
entryPointInfo.umd = path.resolve(entryPointPath, main);
|
||||
}
|
||||
|
||||
return entryPointInfo;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @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 {resolve} from 'canonical-path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {BundleProgram, makeBundleProgram} from './bundle_program';
|
||||
import {EntryPointFormat, EntryPointJsonProperty} from './entry_point';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A bundle of files and paths (and TS programs) that correspond to a particular
|
||||
* format of a package entry-point.
|
||||
*/
|
||||
export interface EntryPointBundle {
|
||||
formatProperty: EntryPointJsonProperty;
|
||||
format: EntryPointFormat;
|
||||
isCore: boolean;
|
||||
isFlatCore: boolean;
|
||||
rootDirs: AbsoluteFsPath[];
|
||||
src: BundleProgram;
|
||||
dts: BundleProgram|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object that describes a formatted bundle for an entry-point.
|
||||
* @param entryPointPath The path to the entry-point that contains the bundle.
|
||||
* @param formatPath The path to the source files for this bundle.
|
||||
* @param typingsPath The path to the typings files if we should transform them with this bundle.
|
||||
* @param isCore This entry point is the Angular core package.
|
||||
* @param format The underlying format of the bundle.
|
||||
* @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 {
|
||||
// Create the TS program and necessary helpers.
|
||||
const options: ts.CompilerOptions = {
|
||||
allowJs: true,
|
||||
maxNodeModuleJsDepth: Infinity,
|
||||
rootDir: entryPointPath,
|
||||
};
|
||||
const host = ts.createCompilerHost(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;
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
|
||||
return {format, formatProperty, rootDirs, isCore, isFlatCore, src, dts};
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from './dependency_resolver';
|
||||
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
||||
|
||||
@ -18,9 +19,13 @@ export class EntryPointFinder {
|
||||
* 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: string): SortedEntryPointsInfo {
|
||||
findEntryPoints(sourceDirectory: AbsoluteFsPath, targetEntryPointPath?: AbsoluteFsPath):
|
||||
SortedEntryPointsInfo {
|
||||
const unsortedEntryPoints = walkDirectoryForEntryPoints(sourceDirectory);
|
||||
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints);
|
||||
const targetEntryPoint = targetEntryPointPath ?
|
||||
unsortedEntryPoints.find(entryPoint => entryPoint.path === targetEntryPointPath) :
|
||||
undefined;
|
||||
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints, targetEntryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +34,7 @@ export class EntryPointFinder {
|
||||
* The function will recurse into directories that start with `@...`, e.g. `@angular/...`.
|
||||
* @param sourceDirectory An absolute path to the root directory where searching begins.
|
||||
*/
|
||||
function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
|
||||
function walkDirectoryForEntryPoints(sourceDirectory: AbsoluteFsPath): EntryPoint[] {
|
||||
const entryPoints: EntryPoint[] = [];
|
||||
fs.readdirSync(sourceDirectory)
|
||||
// Not interested in hidden files
|
||||
@ -44,14 +49,15 @@ function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
|
||||
.forEach(p => {
|
||||
// Either the directory is a potential package or a namespace containing packages (e.g
|
||||
// `@angular`).
|
||||
const packagePath = path.join(sourceDirectory, p);
|
||||
const packagePath = AbsoluteFsPath.from(path.join(sourceDirectory, p));
|
||||
if (p.startsWith('@')) {
|
||||
entryPoints.push(...walkDirectoryForEntryPoints(packagePath));
|
||||
} else {
|
||||
entryPoints.push(...getEntryPointsForPackage(packagePath));
|
||||
|
||||
// Also check for any nested node_modules in this package
|
||||
const nestedNodeModulesPath = path.resolve(packagePath, 'node_modules');
|
||||
const nestedNodeModulesPath =
|
||||
AbsoluteFsPath.from(path.resolve(packagePath, 'node_modules'));
|
||||
if (fs.existsSync(nestedNodeModulesPath)) {
|
||||
entryPoints.push(...walkDirectoryForEntryPoints(nestedNodeModulesPath));
|
||||
}
|
||||
@ -65,7 +71,7 @@ function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
|
||||
* @param packagePath The absolute path to an npm package that may contain entry points
|
||||
* @returns An array of entry points that were discovered.
|
||||
*/
|
||||
function getEntryPointsForPackage(packagePath: string): EntryPoint[] {
|
||||
function getEntryPointsForPackage(packagePath: AbsoluteFsPath): EntryPoint[] {
|
||||
const entryPoints: EntryPoint[] = [];
|
||||
|
||||
// Try to get an entry point from the top level package directory
|
||||
@ -91,7 +97,7 @@ function getEntryPointsForPackage(packagePath: string): EntryPoint[] {
|
||||
* @param dir the directory to recursively walk.
|
||||
* @param fn the function to apply to each directory.
|
||||
*/
|
||||
function walkDirectory(dir: string, fn: (dir: string) => void) {
|
||||
function walkDirectory(dir: AbsoluteFsPath, fn: (dir: AbsoluteFsPath) => void) {
|
||||
return fs
|
||||
.readdirSync(dir)
|
||||
// Not interested in hidden files
|
||||
@ -103,9 +109,9 @@ function walkDirectory(dir: string, fn: (dir: string) => void) {
|
||||
const stat = fs.lstatSync(path.resolve(dir, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
})
|
||||
.forEach(subdir => {
|
||||
subdir = path.resolve(dir, subdir);
|
||||
fn(subdir);
|
||||
walkDirectory(subdir, fn);
|
||||
.forEach(subDir => {
|
||||
const resolvedSubDir = AbsoluteFsPath.from(path.resolve(dir, subDir));
|
||||
fn(resolvedSubDir);
|
||||
walkDirectory(resolvedSubDir, fn);
|
||||
});
|
||||
}
|
@ -5,9 +5,6 @@
|
||||
* 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} from 'canonical-path';
|
||||
import {existsSync, writeFileSync} from 'fs';
|
||||
import {mkdir, mv} from 'shelljs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
|
||||
@ -22,7 +19,6 @@ import {Esm5Renderer} from '../rendering/esm5_renderer';
|
||||
import {EsmRenderer} from '../rendering/esm_renderer';
|
||||
import {FileInfo, Renderer} from '../rendering/renderer';
|
||||
|
||||
import {EntryPoint} from './entry_point';
|
||||
import {EntryPointBundle} from './entry_point_bundle';
|
||||
|
||||
|
||||
@ -54,10 +50,10 @@ export class Transformer {
|
||||
/**
|
||||
* Transform the source (and typings) files of a bundle.
|
||||
* @param bundle the bundle to transform.
|
||||
* @returns information about the files that were transformed.
|
||||
*/
|
||||
transform(entryPoint: EntryPoint, isCore: boolean, bundle: EntryPointBundle): void {
|
||||
console.warn(`Compiling ${entryPoint.name} - ${bundle.format}`);
|
||||
|
||||
transform(bundle: EntryPointBundle): FileInfo[] {
|
||||
const isCore = bundle.isCore;
|
||||
const reflectionHost = this.getHost(isCore, bundle);
|
||||
|
||||
// Parse and analyze the files.
|
||||
@ -70,19 +66,16 @@ export class Transformer {
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
|
||||
// Write out all the transformed files.
|
||||
renderedFiles.forEach(file => this.writeFile(file));
|
||||
return renderedFiles;
|
||||
}
|
||||
|
||||
getHost(isCore: boolean, bundle: EntryPointBundle): NgccReflectionHost {
|
||||
const typeChecker = bundle.src.program.getTypeChecker();
|
||||
switch (bundle.format) {
|
||||
case 'esm2015':
|
||||
case 'fesm2015':
|
||||
return new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
|
||||
case 'esm5':
|
||||
case 'fesm5':
|
||||
return new Esm5ReflectionHost(isCore, typeChecker);
|
||||
return new Esm5ReflectionHost(isCore, typeChecker, bundle.dts);
|
||||
default:
|
||||
throw new Error(`Reflection host for "${bundle.format}" not yet implemented.`);
|
||||
}
|
||||
@ -91,10 +84,8 @@ export class Transformer {
|
||||
getRenderer(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle): Renderer {
|
||||
switch (bundle.format) {
|
||||
case 'esm2015':
|
||||
case 'fesm2015':
|
||||
return new EsmRenderer(host, isCore, bundle, this.sourcePath, this.targetPath);
|
||||
case 'esm5':
|
||||
case 'fesm5':
|
||||
return new Esm5Renderer(host, isCore, bundle, this.sourcePath, this.targetPath);
|
||||
default:
|
||||
throw new Error(`Renderer for "${bundle.format}" not yet implemented.`);
|
||||
@ -127,15 +118,6 @@ export class Transformer {
|
||||
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses};
|
||||
}
|
||||
|
||||
writeFile(file: FileInfo): void {
|
||||
mkdir('-p', dirname(file.path));
|
||||
const backPath = file.path + '.bak';
|
||||
if (existsSync(file.path) && !existsSync(backPath)) {
|
||||
mv(file.path, backPath);
|
||||
}
|
||||
writeFileSync(file.path, file.contents, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import MagicString from 'magic-string';
|
||||
import {getIifeBody} from '../host/esm5_host';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {EsmRenderer} from './esm_renderer';
|
||||
@ -23,21 +24,18 @@ export class Esm5Renderer extends EsmRenderer {
|
||||
* Add the definitions to each decorated class
|
||||
*/
|
||||
addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string): void {
|
||||
const classSymbol = this.host.getClassSymbol(compiledClass.declaration);
|
||||
if (!classSymbol) {
|
||||
throw new Error(
|
||||
`Compiled class does not have a valid symbol: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
const parent = classSymbol.valueDeclaration && classSymbol.valueDeclaration.parent;
|
||||
if (!parent || !ts.isBlock(parent)) {
|
||||
const iifeBody = getIifeBody(compiledClass.declaration);
|
||||
if (!iifeBody) {
|
||||
throw new Error(
|
||||
`Compiled class declaration is not inside an IIFE: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
const returnStatement = parent.statements.find(statement => ts.isReturnStatement(statement));
|
||||
|
||||
const returnStatement = iifeBody.statements.find(ts.isReturnStatement);
|
||||
if (!returnStatement) {
|
||||
throw new Error(
|
||||
`Compiled class wrapper IIFE does not have a return statement: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
|
||||
const insertionPoint = returnStatement.getFullStart();
|
||||
output.appendLeft(insertionPoint, '\n' + definitions);
|
||||
}
|
@ -13,7 +13,7 @@ import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {RedundantDecoratorMap, Renderer, stripExtension} from './renderer';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {isDtsPath} from '../../../ngtsc/util/src/typescript';
|
||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||
|
||||
export class EsmRenderer extends Renderer {
|
||||
constructor(
|
@ -7,7 +7,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ImportRewriter, validateAndRewriteCoreSymbol} from '../../../ngtsc/imports';
|
||||
import {ImportRewriter, validateAndRewriteCoreSymbol} from '../../../src/ngtsc/imports';
|
||||
|
||||
export class NgccFlatImportRewriter implements ImportRewriter {
|
||||
shouldImportSymbol(symbol: string, specifier: string): boolean {
|
@ -15,7 +15,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {NoopImportRewriter, ImportRewriter, R3SymbolsImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
|
||||
import {translateStatement, translateType, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
|
||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||
@ -137,7 +137,8 @@ export abstract class Renderer {
|
||||
|
||||
if (compiledFile) {
|
||||
const importManager = new ImportManager(
|
||||
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlat), IMPORT_PREFIX);
|
||||
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlatCore),
|
||||
IMPORT_PREFIX);
|
||||
|
||||
// TODO: remove constructor param metadata and property decorators (we need info from the
|
||||
// handlers to do this)
|
||||
@ -480,7 +481,7 @@ export function renderConstantPool(
|
||||
export function renderDefinitions(
|
||||
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager): string {
|
||||
const printer = ts.createPrinter();
|
||||
const name = (compiledClass.declaration as ts.NamedDeclaration).name !;
|
||||
const name = compiledClass.declaration.name;
|
||||
const translate = (stmt: Statement) =>
|
||||
translateStatement(stmt, imports, NOOP_DEFAULT_IMPORT_RECORDER);
|
||||
const definitions =
|
19
packages/compiler-cli/ngcc/src/writing/file_writer.ts
Normal file
@ -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 {EntryPoint} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileInfo} from '../rendering/renderer';
|
||||
|
||||
|
||||
/**
|
||||
* Responsible for writing out the transformed files to disk.
|
||||
*/
|
||||
export interface FileWriter {
|
||||
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]): void;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
|
||||
/**
|
||||
* @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} from 'canonical-path';
|
||||
import {existsSync, writeFileSync} from 'fs';
|
||||
import {mkdir, mv} from 'shelljs';
|
||||
|
||||
import {EntryPoint} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileInfo} from '../rendering/renderer';
|
||||
|
||||
import {FileWriter} from './file_writer';
|
||||
|
||||
/**
|
||||
* This FileWriter overwrites the transformed file, in-place, while creating
|
||||
* a back-up of the original file with an extra `.bak` extension.
|
||||
*/
|
||||
export class InPlaceFileWriter implements FileWriter {
|
||||
writeBundle(_entryPoint: EntryPoint, _bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
|
||||
transformedFiles.forEach(file => this.writeFileAndBackup(file));
|
||||
}
|
||||
protected writeFileAndBackup(file: FileInfo): void {
|
||||
mkdir('-p', dirname(file.path));
|
||||
const backPath = file.path + '.__ivy_ngcc_bak';
|
||||
if (existsSync(backPath)) {
|
||||
throw new Error(
|
||||
`Tried to overwrite ${backPath} with an ngcc back up file, which is disallowed.`);
|
||||
}
|
||||
if (existsSync(file.path)) {
|
||||
mv(file.path, backPath);
|
||||
}
|
||||
writeFileSync(file.path, file.contents, 'utf8');
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
|
||||
/**
|
||||
* @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, join, relative} from 'canonical-path';
|
||||
import {writeFileSync} from 'fs';
|
||||
import {cp, mkdir} from 'shelljs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileInfo} from '../rendering/renderer';
|
||||
|
||||
import {InPlaceFileWriter} from './in_place_file_writer';
|
||||
|
||||
const NGCC_DIRECTORY = '__ivy_ngcc__';
|
||||
|
||||
/**
|
||||
* This FileWriter creates a copy of the original entry-point, then writes the transformed
|
||||
* files onto the files in this copy, and finally updates the package.json with a new
|
||||
* entry-point format property that points to this new entry-point.
|
||||
*
|
||||
* If there are transformed typings files in this bundle, they are updated in-place (see the
|
||||
* `InPlaceFileWriter`).
|
||||
*/
|
||||
export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
||||
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
|
||||
// The new folder is at the root of the overall package
|
||||
const relativeEntryPointPath = relative(entryPoint.package, entryPoint.path);
|
||||
const relativeNewDir = join(NGCC_DIRECTORY, relativeEntryPointPath);
|
||||
const newDir = AbsoluteFsPath.fromUnchecked(join(entryPoint.package, relativeNewDir));
|
||||
this.copyBundle(bundle, entryPoint.path, newDir);
|
||||
transformedFiles.forEach(file => this.writeFile(file, entryPoint.path, newDir));
|
||||
this.updatePackageJson(entryPoint, bundle.formatProperty, newDir);
|
||||
}
|
||||
|
||||
protected copyBundle(
|
||||
bundle: EntryPointBundle, entryPointPath: AbsoluteFsPath, newDir: AbsoluteFsPath) {
|
||||
bundle.src.program.getSourceFiles().forEach(sourceFile => {
|
||||
const relativePath = relative(entryPointPath, sourceFile.fileName);
|
||||
const newFilePath = join(newDir, relativePath);
|
||||
mkdir('-p', dirname(newFilePath));
|
||||
cp(sourceFile.fileName, newFilePath);
|
||||
});
|
||||
}
|
||||
|
||||
protected writeFile(file: FileInfo, entryPointPath: AbsoluteFsPath, newDir: AbsoluteFsPath):
|
||||
void {
|
||||
if (isDtsPath(file.path)) {
|
||||
super.writeFileAndBackup(file);
|
||||
} else {
|
||||
const relativePath = relative(entryPointPath, file.path);
|
||||
const newFilePath = join(newDir, relativePath);
|
||||
mkdir('-p', dirname(newFilePath));
|
||||
writeFileSync(newFilePath, file.contents, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
protected updatePackageJson(
|
||||
entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, newDir: AbsoluteFsPath) {
|
||||
const bundlePath = entryPoint.packageJson[formatProperty] !;
|
||||
const newBundlePath = relative(entryPoint.path, join(newDir, bundlePath));
|
||||
(entryPoint.packageJson as any)[formatProperty + '_ivy_ngcc'] = newBundlePath;
|
||||
writeFileSync(join(entryPoint.path, 'package.json'), JSON.stringify(entryPoint.packageJson));
|
||||
}
|
||||
}
|
75
packages/compiler-cli/ngcc/test/BUILD.bazel
Normal file
@ -0,0 +1,75 @@
|
||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ts_library(
|
||||
name = "test_lib",
|
||||
testonly = True,
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["integration/**/*.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//packages/compiler-cli/ngcc",
|
||||
"//packages/compiler-cli/src/ngtsc/imports",
|
||||
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
|
||||
"//packages/compiler-cli/src/ngtsc/path",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"//packages/compiler-cli/src/ngtsc/testing",
|
||||
"//packages/compiler-cli/src/ngtsc/transform",
|
||||
"//packages/compiler-cli/test:test_utils",
|
||||
"@npm//@types/convert-source-map",
|
||||
"@npm//@types/mock-fs",
|
||||
"@npm//canonical-path",
|
||||
"@npm//magic-string",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"//tools/testing:node_no_angular",
|
||||
"@npm//canonical-path",
|
||||
"@npm//convert-source-map",
|
||||
"@npm//shelljs",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "integration_lib",
|
||||
testonly = True,
|
||||
srcs = glob(
|
||||
["integration/**/*.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//packages/compiler-cli/ngcc",
|
||||
"//packages/compiler-cli/src/ngtsc/path",
|
||||
"//packages/compiler-cli/test:test_utils",
|
||||
"@npm//@types/mock-fs",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "integration",
|
||||
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
|
||||
data = [
|
||||
"//packages/common:npm_package",
|
||||
"//packages/core:npm_package",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
tags = [
|
||||
# Disabled in AOT mode because we want ngcc to compile non-AOT Angular packages.
|
||||
"no-ivy-aot",
|
||||
],
|
||||
deps = [
|
||||
":integration_lib",
|
||||
"//tools/testing:node_no_angular",
|
||||
"@npm//canonical-path",
|
||||
"@npm//convert-source-map",
|
||||
"@npm//shelljs",
|
||||
],
|
||||
)
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../ngtsc/path';
|
||||
import {Decorator} from '../../../ngtsc/reflection';
|
||||
import {DecoratorHandler, DetectResult} from '../../../ngtsc/transform';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {Decorator} from '../../../src/ngtsc/reflection';
|
||||
import {DecoratorHandler, DetectResult} from '../../../src/ngtsc/transform';
|
||||
import {CompiledClass, DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../../ngtsc/imports';
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
@ -8,10 +8,10 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../../ngtsc/imports';
|
||||
import {PartialEvaluator} from '../../../ngtsc/partial_evaluator';
|
||||
import {TypeScriptReflectionHost} from '../../../ngtsc/reflection';
|
||||
import {getDeclaration, makeProgram} from '../../../ngtsc/testing/in_memory_typescript';
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
import {getDeclaration, makeProgram} from '../../../src/ngtsc/testing/in_memory_typescript';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
|
||||
describe('NgccReferencesRegistry', () => {
|
@ -7,15 +7,13 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../ngtsc/path';
|
||||
import {makeProgram} from '../../../ngtsc/testing/in_memory_typescript';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {makeProgram} from '../../../src/ngtsc/testing/in_memory_typescript';
|
||||
import {BundleProgram} from '../../src/packages/bundle_program';
|
||||
import {EntryPointFormat} from '../../src/packages/entry_point';
|
||||
import {EntryPointFormat, EntryPointJsonProperty} from '../../src/packages/entry_point';
|
||||
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
|
||||
|
||||
export {getDeclaration} from '../../../ngtsc/testing/in_memory_typescript';
|
||||
|
||||
|
||||
export {getDeclaration} from '../../../src/ngtsc/testing/in_memory_typescript';
|
||||
|
||||
/**
|
||||
*
|
||||
@ -24,12 +22,17 @@ export {getDeclaration} from '../../../ngtsc/testing/in_memory_typescript';
|
||||
* @param dtsFiles The typings files to include the bundle.
|
||||
*/
|
||||
export function makeTestEntryPointBundle(
|
||||
format: EntryPointFormat, files: {name: string, contents: string, isRoot?: boolean}[],
|
||||
formatProperty: EntryPointJsonProperty, format: EntryPointFormat, isCore: boolean,
|
||||
files: {name: string, contents: string, isRoot?: boolean}[],
|
||||
dtsFiles?: {name: string, contents: string, isRoot?: boolean}[]): EntryPointBundle {
|
||||
const src = makeTestBundleProgram(files);
|
||||
const dts = dtsFiles ? makeTestBundleProgram(dtsFiles) : null;
|
||||
const isFlat = src.r3SymbolsFile === null;
|
||||
return {format, rootDirs: [AbsoluteFsPath.fromUnchecked('/')], src, dts, isFlat};
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
return {
|
||||
formatProperty,
|
||||
format,
|
||||
rootDirs: [AbsoluteFsPath.fromUnchecked('/')], src, dts, isCore, isFlatCore
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
|
||||
import {ClassMemberKind, Import, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
@ -108,7 +108,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -132,7 +132,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
@ -148,7 +148,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const host = new Esm2015ReflectionHost(true, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -168,7 +168,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -186,7 +186,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
|
||||
@ -200,7 +200,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticMethod = members.find(member => member.name === 'staticMethod') !;
|
||||
@ -213,7 +213,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const staticProperty = members.find(member => member.name === 'staticProperty') !;
|
||||
@ -230,7 +230,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
|
||||
host.getMembersOfClass(classNode);
|
||||
const identifiers = spy.calls.all().map(call => (call.args[0] as ts.Identifier).text);
|
||||
@ -242,7 +242,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const host = new Esm2015ReflectionHost(true, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -257,7 +257,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toBeDefined();
|
||||
@ -280,7 +280,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![2].decorators !;
|
||||
|
||||
@ -298,7 +298,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const ctrDecorators = host.getConstructorParameters(classNode) !;
|
||||
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
|
||||
local: true,
|
||||
@ -318,7 +318,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
const decoratorNode = classDecorators[0].node;
|
||||
const identifierOfDirective =
|
||||
@ -328,7 +328,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
|
||||
import {ClassMemberKind, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
@ -553,14 +553,14 @@ const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||
{name: '/src/module', contents: 'export class ExternalModule {}'},
|
||||
];
|
||||
|
||||
describe('Fesm2015ReflectionHost', () => {
|
||||
describe('Esm2015ReflectionHost', () => {
|
||||
|
||||
describe('getDecoratorsOfDeclaration()', () => {
|
||||
it('should find the decorators on a class', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -578,7 +578,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(functionNode);
|
||||
expect(decorators).toBe(null);
|
||||
});
|
||||
@ -587,7 +587,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode);
|
||||
expect(decorators).toBe(null);
|
||||
});
|
||||
@ -596,7 +596,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode);
|
||||
expect(decorators).toEqual([]);
|
||||
});
|
||||
@ -605,7 +605,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -616,7 +616,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -627,7 +627,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -641,8 +641,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toEqual(1);
|
||||
@ -657,7 +657,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -670,7 +670,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -682,7 +682,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -696,8 +696,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find decorated properties on a class', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -714,8 +714,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find non decorated properties on a class', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
|
||||
@ -729,7 +729,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(ACCESSORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const [combinedSetter, combinedGetter] =
|
||||
@ -749,8 +749,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find static methods on a class', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticMethod = members.find(member => member.name === 'staticMethod') !;
|
||||
@ -762,8 +762,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find static properties on a class', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticProperty = members.find(member => member.name === 'staticProperty') !;
|
||||
@ -777,7 +777,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
expect(() => {
|
||||
host.getMembersOfClass(functionNode);
|
||||
}).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`);
|
||||
@ -787,7 +787,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(members).toEqual([]);
|
||||
@ -798,7 +798,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral',
|
||||
isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(members.map(member => member.name)).not.toContain('prop');
|
||||
@ -809,7 +810,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -822,7 +823,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -835,7 +836,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -854,8 +855,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -878,7 +879,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -893,7 +894,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -908,7 +909,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -924,8 +925,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find the decorated constructor parameters', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters).toBeDefined();
|
||||
@ -939,7 +940,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
expect(() => { host.getConstructorParameters(functionNode); })
|
||||
.toThrowError(
|
||||
'Attempted to get constructor parameters of a non-class: "function foo() {}"');
|
||||
@ -949,7 +950,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
expect(parameters).toBe(null);
|
||||
});
|
||||
@ -958,7 +959,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isClassDeclaration);
|
||||
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters).toEqual(jasmine.any(Array));
|
||||
@ -971,7 +972,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toEqual([]);
|
||||
@ -981,7 +982,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
@ -995,7 +996,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
@ -1009,7 +1010,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
@ -1033,7 +1034,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
|
||||
const program = makeTestProgram(file);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedClassDeclaration);
|
||||
return host.getConstructorParameters(classNode);
|
||||
}
|
||||
|
||||
@ -1088,7 +1089,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral',
|
||||
isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters !.length).toBe(2);
|
||||
@ -1106,7 +1108,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1118,7 +1120,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1134,7 +1136,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
const decorators = parameters[2].decorators !;
|
||||
|
||||
@ -1152,7 +1154,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
expect(parameters !.length).toBe(1);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
@ -1167,7 +1169,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1181,7 +1183,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
|
||||
ts.isClassDeclaration);
|
||||
isNamedClassDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1198,7 +1200,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
|
||||
const fooNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !;
|
||||
const fooDef = host.getDefinitionOfFunction(fooNode);
|
||||
expect(fooDef.node).toBe(fooNode);
|
||||
expect(fooDef.body !.length).toEqual(1);
|
||||
@ -1208,7 +1210,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
expect(fooDef.parameters[0].initializer).toBe(null);
|
||||
|
||||
const barNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !;
|
||||
const barDef = host.getDefinitionOfFunction(barNode);
|
||||
expect(barDef.node).toBe(barNode);
|
||||
expect(barDef.body !.length).toEqual(1);
|
||||
@ -1221,7 +1223,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
expect(barDef.parameters[1].initializer !.getText()).toEqual('42');
|
||||
|
||||
const bazNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !;
|
||||
const bazDef = host.getDefinitionOfFunction(bazNode);
|
||||
expect(bazDef.node).toBe(bazNode);
|
||||
expect(bazDef.body !.length).toEqual(3);
|
||||
@ -1230,7 +1232,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
expect(bazDef.parameters[0].initializer).toBe(null);
|
||||
|
||||
const quxNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !;
|
||||
const quxDef = host.getDefinitionOfFunction(quxNode);
|
||||
expect(quxDef.node).toBe(quxNode);
|
||||
expect(quxDef.body !.length).toEqual(2);
|
||||
@ -1239,14 +1241,14 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
expect(quxDef.parameters[0].initializer).toBe(null);
|
||||
|
||||
const mooNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'moo', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'moo', isNamedFunctionDeclaration) !;
|
||||
const mooDef = host.getDefinitionOfFunction(mooNode);
|
||||
expect(mooDef.node).toBe(mooNode);
|
||||
expect(mooDef.body !.length).toEqual(3);
|
||||
expect(mooDef.parameters).toEqual([]);
|
||||
|
||||
const juuNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'juu', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'juu', isNamedFunctionDeclaration) !;
|
||||
const juuDef = host.getDefinitionOfFunction(juuNode);
|
||||
expect(juuDef.node).toBe(juuNode);
|
||||
expect(juuDef.body !.length).toEqual(2);
|
||||
@ -1259,7 +1261,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'b', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
|
||||
@ -1269,7 +1271,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'c', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
|
||||
@ -1279,7 +1281,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'd', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toBeNull();
|
||||
@ -1290,8 +1292,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should return the declaration of a locally defined identifier', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const ctrDecorators = host.getConstructorParameters(classNode) !;
|
||||
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
|
||||
local: true,
|
||||
@ -1300,7 +1302,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
}).expression;
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
@ -1310,15 +1312,15 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should return the declaration of an externally defined identifier', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
|
||||
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression)
|
||||
.properties[0] as ts.PropertyAssignment)
|
||||
.initializer as ts.Identifier;
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive', ts.isVariableDeclaration);
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive', isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
@ -1366,14 +1368,15 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const node =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
|
||||
expect(host.isClass(node)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if a given node is a TS function declaration', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
const node =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
expect(host.isClass(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -1385,13 +1388,13 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const dts = makeTestBundleProgram([ARITY_CLASSES[1]]);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dts);
|
||||
const noTypeParamClass =
|
||||
getDeclaration(program, '/src/class.js', 'NoTypeParam', ts.isClassDeclaration);
|
||||
getDeclaration(program, '/src/class.js', 'NoTypeParam', isNamedClassDeclaration);
|
||||
expect(host.getGenericArityOfClass(noTypeParamClass)).toBe(0);
|
||||
const oneTypeParamClass =
|
||||
getDeclaration(program, '/src/class.js', 'OneTypeParam', ts.isClassDeclaration);
|
||||
getDeclaration(program, '/src/class.js', 'OneTypeParam', isNamedClassDeclaration);
|
||||
expect(host.getGenericArityOfClass(oneTypeParamClass)).toBe(1);
|
||||
const twoTypeParamsClass =
|
||||
getDeclaration(program, '/src/class.js', 'TwoTypeParams', ts.isClassDeclaration);
|
||||
getDeclaration(program, '/src/class.js', 'TwoTypeParams', isNamedClassDeclaration);
|
||||
expect(host.getGenericArityOfClass(twoTypeParamsClass)).toBe(2);
|
||||
});
|
||||
});
|
||||
@ -1440,7 +1443,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find the dts declaration that has the same relative path to the source file', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
|
||||
const class1 =
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'Class1', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
@ -1450,7 +1454,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
it('should find the dts declaration for exported functions', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
|
||||
const mooFn =
|
||||
getDeclaration(srcProgram, '/src/func1.js', 'mooFn', isNamedFunctionDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(mooFn);
|
||||
@ -1461,7 +1466,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const missingClass =
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
@ -1471,7 +1476,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const missingClass = getDeclaration(
|
||||
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
|
||||
srcProgram, '/src/missing-class.js', 'MissingClass2', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
@ -1482,7 +1487,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class1 =
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
@ -1493,7 +1498,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class3 =
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class3);
|
||||
@ -1504,8 +1509,8 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const internalClass =
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
|
||||
const internalClass = getDeclaration(
|
||||
srcProgram, '/src/internal.js', 'InternalClass', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(internalClass);
|
||||
@ -1517,9 +1522,9 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class2 =
|
||||
getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isClassDeclaration);
|
||||
getDeclaration(srcProgram, '/src/class2.js', 'Class2', isNamedClassDeclaration);
|
||||
const internalClass2 =
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'Class2', isNamedClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const class2DtsDeclaration = host.getDtsDeclaration(class2);
|
||||
@ -1531,7 +1536,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModuleWithProvidersFunctions', () => {
|
||||
describe('getModuleWithProvidersFunctions()', () => {
|
||||
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
|
||||
import {ClassMemberKind, Import, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
|
||||
import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
@ -123,7 +123,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -147,7 +147,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
@ -163,7 +163,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const host = new Esm5ReflectionHost(true, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -183,7 +183,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -201,7 +201,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
|
||||
@ -215,7 +215,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticMethod = members.find(member => member.name === 'staticMethod') !;
|
||||
@ -228,7 +228,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticProperty = members.find(member => member.name === 'staticProperty') !;
|
||||
@ -245,7 +245,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
|
||||
host.getMembersOfClass(classNode);
|
||||
const identifiers = spy.calls.all().map(call => (call.args[0] as ts.Identifier).text);
|
||||
@ -257,7 +257,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const host = new Esm5ReflectionHost(true, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -272,7 +272,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toBeDefined();
|
||||
@ -295,7 +295,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![2].decorators !;
|
||||
|
||||
@ -334,7 +334,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const ctrDecorators = host.getConstructorParameters(classNode) !;
|
||||
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
|
||||
local: true,
|
||||
@ -343,7 +343,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
}).expression;
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, '/some_directive.js', 'ViewContainerRef', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'ViewContainerRef', isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
@ -354,7 +354,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
const program = makeTestProgram(fileSystem.files[0]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
|
||||
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
const decoratorNode = classDecorators[0].node;
|
||||
|
||||
@ -365,12 +365,24 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
expect(actualDeclaration !.viaModule).toBe('@angular/core');
|
||||
});
|
||||
|
||||
it('should find the "actual" declaration of an aliased variable identifier', () => {
|
||||
const program = makeTestProgram(fileSystem.files[2]);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const ngModuleRef = findIdentifier(
|
||||
program.getSourceFile(fileSystem.files[2].name) !, 'HttpClientXsrfModule_1',
|
||||
isNgModulePropertyAssignment);
|
||||
|
||||
const declaration = host.getDeclarationOfIdentifier(ngModuleRef !);
|
||||
expect(declaration).not.toBe(null);
|
||||
expect(declaration !.node.getText()).toContain('function HttpClientXsrfModule()');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -415,10 +427,26 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) &&
|
||||
node.name.text === variableName) {
|
||||
if (isNamedVariableDeclaration(node) && node.name.text === variableName) {
|
||||
return node;
|
||||
}
|
||||
return node.forEachChild(node => findVariableDeclaration(node, variableName));
|
||||
}
|
||||
});
|
||||
|
||||
function findIdentifier(
|
||||
node: ts.Node | undefined, identifierName: string,
|
||||
requireFn: (node: ts.Identifier) => boolean): ts.Identifier|undefined {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
if (ts.isIdentifier(node) && node.text === identifierName && requireFn(node)) {
|
||||
return node;
|
||||
}
|
||||
return node.forEachChild(node => findIdentifier(node, identifierName, requireFn));
|
||||
}
|
||||
|
||||
function isNgModulePropertyAssignment(identifier: ts.Identifier): boolean {
|
||||
return ts.isPropertyAssignment(identifier.parent) &&
|
||||
identifier.parent.name.getText() === 'ngModule';
|
||||
}
|
@ -8,10 +8,10 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
|
||||
import {ClassMemberKind, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
|
||||
import {getDeclaration, makeTestProgram} from '../helpers/utils';
|
||||
import {Esm5ReflectionHost, getIifeBody} from '../../src/host/esm5_host';
|
||||
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
import {expectTypeValueReferencesForParameters} from './util';
|
||||
|
||||
@ -96,6 +96,13 @@ const ACCESSORS_FILE = {
|
||||
`,
|
||||
};
|
||||
|
||||
const SIMPLE_ES2015_CLASS_FILE = {
|
||||
name: '/simple_es2015_class.d.ts',
|
||||
contents: `
|
||||
export class EmptyClass {}
|
||||
`,
|
||||
};
|
||||
|
||||
const SIMPLE_CLASS_FILE = {
|
||||
name: '/simple_class.js',
|
||||
contents: `
|
||||
@ -529,6 +536,172 @@ const UNWANTED_PROTOTYPE_EXPORT_FILE = {
|
||||
}`
|
||||
};
|
||||
|
||||
const TYPINGS_SRC_FILES = [
|
||||
{
|
||||
name: '/src/index.js',
|
||||
contents:
|
||||
`import {InternalClass} from './internal'; export * from './class1'; export * from './class2';`
|
||||
},
|
||||
{
|
||||
name: '/src/class1.js',
|
||||
contents: `
|
||||
var Class1 = (function() {
|
||||
function Class1() {}
|
||||
return Class1;
|
||||
}());
|
||||
var MissingClass1 = (function() {
|
||||
function MissingClass1() {}
|
||||
return MissingClass1;
|
||||
}());
|
||||
export {Class1, MissingClass1};
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/class2.js',
|
||||
contents: `
|
||||
var Class2 = (function() {
|
||||
function Class2() {}
|
||||
return Class2;
|
||||
}());
|
||||
export {Class2};
|
||||
`
|
||||
},
|
||||
{name: '/src/func1.js', contents: 'function mooFn() {} export {mooFn}'}, {
|
||||
name: '/src/internal.js',
|
||||
contents: `
|
||||
var InternalClass = (function() {
|
||||
function InternalClass() {}
|
||||
return InternalClass;
|
||||
}());
|
||||
var Class2 = (function() {
|
||||
function Class2() {}
|
||||
return Class2;
|
||||
}());
|
||||
export {InternalClass, Class2};
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/missing-class.js',
|
||||
contents: `
|
||||
var MissingClass2 = (function() {
|
||||
function MissingClass2() {}
|
||||
return MissingClass2;
|
||||
}());
|
||||
export {MissingClass2};
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/flat-file.js',
|
||||
contents: `
|
||||
var Class1 = (function() {
|
||||
function Class1() {}
|
||||
return Class1;
|
||||
}());
|
||||
var MissingClass1 = (function() {
|
||||
function MissingClass1() {}
|
||||
return MissingClass1;
|
||||
}());
|
||||
var MissingClass2 = (function() {
|
||||
function MissingClass2() {}
|
||||
return MissingClass2;
|
||||
}());
|
||||
var Class3 = (function() {
|
||||
function Class3() {}
|
||||
return Class3;
|
||||
}());
|
||||
export {Class1, Class3 as xClass3, MissingClass1, MissingClass2};
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const TYPINGS_DTS_FILES = [
|
||||
{
|
||||
name: '/typings/index.d.ts',
|
||||
contents:
|
||||
`import {InternalClass} from './internal'; export * from './class1'; export * from './class2';`
|
||||
},
|
||||
{
|
||||
name: '/typings/class1.d.ts',
|
||||
contents: `export declare class Class1 {}\nexport declare class OtherClass {}`
|
||||
},
|
||||
{
|
||||
name: '/typings/class2.d.ts',
|
||||
contents:
|
||||
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
|
||||
},
|
||||
{name: '/typings/func1.d.ts', contents: 'export declare function mooFn(): void;'},
|
||||
{
|
||||
name: '/typings/internal.d.ts',
|
||||
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
|
||||
},
|
||||
{name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
|
||||
];
|
||||
|
||||
const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||
{
|
||||
name: '/src/functions.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
|
||||
var SomeService = (function() {
|
||||
function SomeService() {}
|
||||
return SomeService;
|
||||
}());
|
||||
|
||||
var InternalModule = (function() {
|
||||
function InternalModule() {}
|
||||
return InternalModule;
|
||||
}());
|
||||
export function aNumber() { return 42; }
|
||||
export function aString() { return 'foo'; }
|
||||
export function emptyObject() { return {}; }
|
||||
export function ngModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||
export function ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||
export function ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||
export function onlyProviders() { return { providers: [SomeService] }; }
|
||||
export function ngModuleNumber() { return { ngModule: 42 }; }
|
||||
export function ngModuleString() { return { ngModule: 'foo' }; }
|
||||
export function ngModuleObject() { return { ngModule: { foo: 42 } }; }
|
||||
export function externalNgModule() { return { ngModule: ExternalModule }; }
|
||||
export {SomeService, InternalModule};
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/methods.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
var SomeService = (function() {
|
||||
function SomeService() {}
|
||||
return SomeService;
|
||||
}());
|
||||
|
||||
var InternalModule = (function() {
|
||||
function InternalModule() {}
|
||||
InternalModule.prototype = {
|
||||
instanceNgModuleIdentifier: function() { return { ngModule: InternalModule }; },
|
||||
instanceNgModuleWithEmptyProviders: function() { return { ngModule: InternalModule, providers: [] }; },
|
||||
instanceNgModuleWithProviders: function() { return { ngModule: InternalModule, providers: [SomeService] }; },
|
||||
instanceExternalNgModule: function() { return { ngModule: ExternalModule }; },
|
||||
};
|
||||
InternalModule.aNumber = function() { return 42; };
|
||||
InternalModule.aString = function() { return 'foo'; };
|
||||
InternalModule.emptyObject = function() { return {}; };
|
||||
InternalModule.ngModuleIdentifier = function() { return { ngModule: InternalModule }; };
|
||||
InternalModule.ngModuleWithEmptyProviders = function() { return { ngModule: InternalModule, providers: [] }; };
|
||||
InternalModule.ngModuleWithProviders = function() { return { ngModule: InternalModule, providers: [SomeService] }; };
|
||||
InternalModule.onlyProviders = function() { return { providers: [SomeService] }; };
|
||||
InternalModule.ngModuleNumber = function() { return { ngModule: 42 }; };
|
||||
InternalModule.ngModuleString = function() { return { ngModule: 'foo' }; };
|
||||
InternalModule.ngModuleObject = function() { return { ngModule: { foo: 42 } }; };
|
||||
InternalModule.externalNgModule = function() { return { ngModule: ExternalModule }; };
|
||||
return InternalModule;
|
||||
}());
|
||||
export {SomeService, InternalModule};
|
||||
`
|
||||
},
|
||||
{name: '/src/module', contents: 'export class ExternalModule {}'},
|
||||
];
|
||||
|
||||
describe('Esm5ReflectionHost', () => {
|
||||
|
||||
describe('getDecoratorsOfDeclaration()', () => {
|
||||
@ -536,7 +709,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators).toBeDefined();
|
||||
@ -554,7 +727,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(functionNode);
|
||||
expect(decorators).toBe(null);
|
||||
});
|
||||
@ -563,7 +736,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode);
|
||||
expect(decorators).toBe(null);
|
||||
});
|
||||
@ -572,7 +745,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode);
|
||||
expect(decorators).toEqual([]);
|
||||
});
|
||||
@ -581,7 +754,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -592,7 +765,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -603,7 +776,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -618,7 +791,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toEqual(1);
|
||||
@ -633,7 +806,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
|
||||
isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -646,7 +820,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -658,7 +832,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
|
||||
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
|
||||
isNamedVariableDeclaration);
|
||||
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
|
||||
expect(decorators.length).toBe(1);
|
||||
@ -673,7 +848,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const input1 = members.find(member => member.name === 'input1') !;
|
||||
@ -691,7 +866,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(ACCESSORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const setter = members.find(member => member.name === 'setter') !;
|
||||
@ -738,7 +913,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
|
||||
@ -752,7 +927,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticMethod = members.find(member => member.name === 'staticMethod') !;
|
||||
@ -766,7 +941,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
const staticProperty = members.find(member => member.name === 'staticProperty') !;
|
||||
@ -780,7 +955,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
expect(() => {
|
||||
host.getMembersOfClass(functionNode);
|
||||
}).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`);
|
||||
@ -790,7 +965,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(members).toEqual([]);
|
||||
@ -802,7 +977,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(members.map(member => member.name)).not.toContain('prop');
|
||||
@ -813,7 +988,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -826,7 +1001,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -839,7 +1014,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
|
||||
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -858,7 +1033,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -875,7 +1050,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -890,7 +1065,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -905,7 +1080,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
const prop = members.find(m => m.name === 'prop') !;
|
||||
const decorators = prop.decorators !;
|
||||
@ -921,18 +1096,18 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(UNWANTED_PROTOTYPE_EXPORT_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, UNWANTED_PROTOTYPE_EXPORT_FILE.name, 'SomeParam', ts.isClassDeclaration);
|
||||
program, UNWANTED_PROTOTYPE_EXPORT_FILE.name, 'SomeParam', isNamedClassDeclaration);
|
||||
const members = host.getMembersOfClass(classNode);
|
||||
expect(members.find(m => m.name === 'prototype')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConstructorParameters', () => {
|
||||
describe('getConstructorParameters()', () => {
|
||||
it('should find the decorated constructor parameters', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toBeDefined();
|
||||
@ -950,7 +1125,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const functionNode =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
expect(() => { host.getConstructorParameters(functionNode); })
|
||||
.toThrowError(
|
||||
'Attempted to get constructor parameters of a non-class: "function foo() {}"');
|
||||
@ -963,7 +1138,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isVariableDeclaration);
|
||||
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass',
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toEqual(jasmine.any(Array));
|
||||
@ -976,7 +1152,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isVariableDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters).toEqual([]);
|
||||
@ -989,7 +1165,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral',
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters !.length).toBe(1);
|
||||
@ -1005,7 +1182,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters !.length).toBe(2);
|
||||
@ -1023,7 +1200,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty',
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1035,7 +1213,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
|
||||
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier',
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1051,7 +1230,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![2].decorators !;
|
||||
|
||||
@ -1078,7 +1257,8 @@ describe('Esm5ReflectionHost', () => {
|
||||
|
||||
const program = makeTestProgram(file);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isVariableDeclaration);
|
||||
const classNode =
|
||||
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||
return host.getConstructorParameters(classNode);
|
||||
}
|
||||
|
||||
@ -1147,7 +1327,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
expect(parameters !.length).toBe(1);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
@ -1162,7 +1342,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1176,7 +1356,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
|
||||
ts.isVariableDeclaration);
|
||||
isNamedVariableDeclaration);
|
||||
const parameters = host.getConstructorParameters(classNode);
|
||||
const decorators = parameters ![0].decorators !;
|
||||
|
||||
@ -1193,7 +1373,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
|
||||
const fooNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !;
|
||||
const fooDef = host.getDefinitionOfFunction(fooNode);
|
||||
expect(fooDef.node).toBe(fooNode);
|
||||
expect(fooDef.body !.length).toEqual(1);
|
||||
@ -1203,7 +1383,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
expect(fooDef.parameters[0].initializer).toBe(null);
|
||||
|
||||
const barNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !;
|
||||
const barDef = host.getDefinitionOfFunction(barNode);
|
||||
expect(barDef.node).toBe(barNode);
|
||||
expect(barDef.body !.length).toEqual(1);
|
||||
@ -1216,7 +1396,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
expect(barDef.parameters[1].initializer !.getText()).toEqual('42');
|
||||
|
||||
const bazNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !;
|
||||
const bazDef = host.getDefinitionOfFunction(bazNode);
|
||||
expect(bazDef.node).toBe(bazNode);
|
||||
expect(bazDef.body !.length).toEqual(3);
|
||||
@ -1225,7 +1405,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
expect(bazDef.parameters[0].initializer).toBe(null);
|
||||
|
||||
const quxNode =
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !;
|
||||
const quxDef = host.getDefinitionOfFunction(quxNode);
|
||||
expect(quxDef.node).toBe(quxNode);
|
||||
expect(quxDef.body !.length).toEqual(2);
|
||||
@ -1235,12 +1415,12 @@ describe('Esm5ReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getImportOfIdentifier', () => {
|
||||
describe('getImportOfIdentifier()', () => {
|
||||
it('should find the import of an identifier', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'b', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
|
||||
@ -1250,7 +1430,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'c', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
|
||||
@ -1260,19 +1440,19 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(...IMPORTS_FILES);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const variableNode =
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration);
|
||||
getDeclaration(program, IMPORTS_FILES[1].name, 'd', isNamedVariableDeclaration);
|
||||
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
|
||||
|
||||
expect(importOfIdent).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDeclarationOfIdentifier', () => {
|
||||
describe('getDeclarationOfIdentifier()', () => {
|
||||
it('should return the declaration of a locally defined identifier', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const ctrDecorators = host.getConstructorParameters(classNode) !;
|
||||
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
|
||||
local: true,
|
||||
@ -1281,7 +1461,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
}).expression;
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
@ -1292,14 +1472,14 @@ describe('Esm5ReflectionHost', () => {
|
||||
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
|
||||
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
|
||||
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
|
||||
const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression)
|
||||
.properties[0] as ts.PropertyAssignment)
|
||||
.initializer as ts.Identifier;
|
||||
|
||||
const expectedDeclarationNode = getDeclaration(
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive', ts.isVariableDeclaration);
|
||||
program, 'node_modules/@angular/core/index.ts', 'Directive', isNamedVariableDeclaration);
|
||||
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective);
|
||||
expect(actualDeclaration).not.toBe(null);
|
||||
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
|
||||
@ -1314,7 +1494,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
|
||||
const outerDeclaration = getDeclaration(
|
||||
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const innerDeclaration = (((outerDeclaration.initializer as ts.ParenthesizedExpression)
|
||||
.expression as ts.CallExpression)
|
||||
.expression as ts.FunctionExpression)
|
||||
@ -1377,105 +1557,97 @@ describe('Esm5ReflectionHost', () => {
|
||||
});
|
||||
|
||||
describe('getClassSymbol()', () => {
|
||||
let superGetClassSymbolSpy: jasmine.Spy;
|
||||
it('should return the class symbol for an ES2015 class', () => {
|
||||
const program = makeTestProgram(SIMPLE_ES2015_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const node = getDeclaration(
|
||||
program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
|
||||
const classSymbol = host.getClassSymbol(node);
|
||||
|
||||
beforeEach(() => {
|
||||
superGetClassSymbolSpy = spyOn(Esm2015ReflectionHost.prototype, 'getClassSymbol');
|
||||
});
|
||||
|
||||
it('should return the class symbol returned by the superclass (if any)', () => {
|
||||
const mockNode = {} as ts.Node;
|
||||
const mockSymbol = {} as ts.Symbol;
|
||||
superGetClassSymbolSpy.and.returnValue(mockSymbol);
|
||||
|
||||
const host = new Esm5ReflectionHost(false, {} as any);
|
||||
|
||||
expect(host.getClassSymbol(mockNode)).toBe(mockSymbol);
|
||||
expect(superGetClassSymbolSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(classSymbol).toBeDefined();
|
||||
expect(classSymbol !.valueDeclaration).toBe(node);
|
||||
});
|
||||
|
||||
it('should return the class symbol for an ES5 class (outer variable declaration)', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const node =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
expect(host.getClassSymbol(node)).toBeDefined();
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const classSymbol = host.getClassSymbol(node);
|
||||
|
||||
expect(classSymbol).toBeDefined();
|
||||
expect(classSymbol !.valueDeclaration).toBe(node);
|
||||
});
|
||||
|
||||
it('should return the class symbol for an ES5 class (inner function declaration)', () => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const outerNode =
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
const innerNode =
|
||||
(((outerNode.initializer as ts.ParenthesizedExpression).expression as ts.CallExpression)
|
||||
.expression as ts.FunctionExpression)
|
||||
.body.statements.find(ts.isFunctionDeclaration) !;
|
||||
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !;
|
||||
const classSymbol = host.getClassSymbol(innerNode);
|
||||
|
||||
expect(host.getClassSymbol(innerNode)).toBeDefined();
|
||||
expect(classSymbol).toBeDefined();
|
||||
expect(classSymbol !.valueDeclaration).toBe(outerNode);
|
||||
});
|
||||
|
||||
it('should return the same class symbol (of the inner declaration) for outer and inner declarations',
|
||||
it('should return the same class symbol (of the outer declaration) for outer and inner declarations',
|
||||
() => {
|
||||
const program = makeTestProgram(SIMPLE_CLASS_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const outerNode = getDeclaration(
|
||||
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
|
||||
const innerNode = (((outerNode.initializer as ts.ParenthesizedExpression)
|
||||
.expression as ts.CallExpression)
|
||||
.expression as ts.FunctionExpression)
|
||||
.body.statements.find(ts.isFunctionDeclaration) !;
|
||||
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
|
||||
const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !;
|
||||
|
||||
expect(host.getClassSymbol(innerNode)).toBe(host.getClassSymbol(outerNode));
|
||||
expect(host.getClassSymbol(innerNode) !.valueDeclaration).toBe(innerNode);
|
||||
});
|
||||
|
||||
it('should return undefined if node is not an ES5 class', () => {
|
||||
const program = makeTestProgram(FOO_FUNCTION_FILE);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
|
||||
expect(host.getClassSymbol(node)).toBeUndefined();
|
||||
const node =
|
||||
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
|
||||
const classSymbol = host.getClassSymbol(node);
|
||||
|
||||
expect(classSymbol).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isClass()', () => {
|
||||
let host: Esm5ReflectionHost;
|
||||
let mockNode: ts.Node;
|
||||
let superIsClassSpy: jasmine.Spy;
|
||||
let getClassSymbolSpy: jasmine.Spy;
|
||||
let getClassDeclarationSpy: jasmine.Spy;
|
||||
let superGetClassDeclarationSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new Esm5ReflectionHost(false, null as any);
|
||||
mockNode = {} as any;
|
||||
|
||||
superIsClassSpy = spyOn(Esm2015ReflectionHost.prototype, 'isClass');
|
||||
getClassSymbolSpy = spyOn(Esm5ReflectionHost.prototype, 'getClassSymbol');
|
||||
getClassDeclarationSpy = spyOn(Esm5ReflectionHost.prototype, 'getClassDeclaration');
|
||||
superGetClassDeclarationSpy = spyOn(Esm2015ReflectionHost.prototype, 'getClassDeclaration');
|
||||
});
|
||||
|
||||
it('should return true if superclass returns true', () => {
|
||||
superIsClassSpy.and.returnValue(true);
|
||||
superGetClassDeclarationSpy.and.returnValue(true);
|
||||
getClassDeclarationSpy.and.callThrough();
|
||||
|
||||
expect(host.isClass(mockNode)).toBe(true);
|
||||
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(getClassSymbolSpy).not.toHaveBeenCalled();
|
||||
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(superGetClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
|
||||
});
|
||||
|
||||
it('should return true if it can find a symbol for the class', () => {
|
||||
superIsClassSpy.and.returnValue(false);
|
||||
getClassSymbolSpy.and.returnValue(true);
|
||||
it('should return true if it can find a declaration for the class', () => {
|
||||
getClassDeclarationSpy.and.returnValue(true);
|
||||
|
||||
expect(host.isClass(mockNode)).toBe(true);
|
||||
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(getClassSymbolSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
|
||||
});
|
||||
|
||||
it('should return false if it cannot find a symbol for the class', () => {
|
||||
superIsClassSpy.and.returnValue(false);
|
||||
getClassSymbolSpy.and.returnValue(false);
|
||||
it('should return false if it cannot find a declaration for the class', () => {
|
||||
getClassDeclarationSpy.and.returnValue(false);
|
||||
|
||||
expect(host.isClass(mockNode)).toBe(false);
|
||||
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(getClassSymbolSpy).toHaveBeenCalledWith(mockNode);
|
||||
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1488,7 +1660,7 @@ describe('Esm5ReflectionHost', () => {
|
||||
|
||||
const program = makeTestProgram(file);
|
||||
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
|
||||
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isVariableDeclaration);
|
||||
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||
return host.hasBaseClass(classNode);
|
||||
}
|
||||
|
||||
@ -1547,4 +1719,142 @@ describe('Esm5ReflectionHost', () => {
|
||||
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDtsDeclarationsOfClass()', () => {
|
||||
it('should find the dts declaration that has the same relative path to the source file', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class1 =
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||
});
|
||||
|
||||
it('should find the dts declaration for exported functions', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(mooFn);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/func1.d.ts');
|
||||
});
|
||||
|
||||
it('should return null if there is no matching class in the matching dts file', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const missingClass =
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null if there is no matching dts file', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const missingClass = getDeclaration(
|
||||
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
});
|
||||
|
||||
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class1 =
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||
});
|
||||
|
||||
it('should find aliased exports', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class3 =
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class3);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
|
||||
});
|
||||
|
||||
it('should find the dts file that contains a matching class declaration, even if the class is not publicly exported',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const internalClass = getDeclaration(
|
||||
srcProgram, '/src/internal.js', 'InternalClass', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(internalClass);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
|
||||
});
|
||||
|
||||
it('should prefer the publicly exported class if there are multiple classes with the same name',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const class2 =
|
||||
getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isVariableDeclaration);
|
||||
const internalClass2 =
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isVariableDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const class2DtsDeclaration = host.getDtsDeclaration(class2);
|
||||
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
|
||||
|
||||
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
|
||||
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
|
||||
.toEqual('/typings/class2.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModuleWithProvidersFunctions', () => {
|
||||
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||
const host = new Esm5ReflectionHost(false, srcProgram.getTypeChecker());
|
||||
const file = srcProgram.getSourceFile('/src/functions.js') !;
|
||||
const fns = host.getModuleWithProvidersFunctions(file);
|
||||
expect(fns.map(info => [info.declaration.name !.getText(), info.ngModule.text])).toEqual([
|
||||
['ngModuleIdentifier', 'InternalModule'],
|
||||
['ngModuleWithEmptyProviders', 'InternalModule'],
|
||||
['ngModuleWithProviders', 'InternalModule'],
|
||||
['externalNgModule', 'ExternalModule'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find every static method on exported classes that return an object that looks like a ModuleWithProviders object',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||
const host = new Esm5ReflectionHost(false, srcProgram.getTypeChecker());
|
||||
const file = srcProgram.getSourceFile('/src/methods.js') !;
|
||||
const fn = host.getModuleWithProvidersFunctions(file);
|
||||
expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.text])).toEqual([
|
||||
[
|
||||
'function() { return { ngModule: InternalModule }; }',
|
||||
'InternalModule',
|
||||
],
|
||||
[
|
||||
'function() { return { ngModule: InternalModule, providers: [] }; }',
|
||||
'InternalModule',
|
||||
],
|
||||
[
|
||||
'function() { return { ngModule: InternalModule, providers: [SomeService] }; }',
|
||||
'InternalModule',
|
||||
],
|
||||
[
|
||||
'function() { return { ngModule: ExternalModule }; }',
|
||||
'ExternalModule',
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
@ -9,7 +9,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CtorParameter} from '../../../ngtsc/reflection';
|
||||
import {CtorParameter} from '../../../src/ngtsc/reflection';
|
||||
|
||||
/**
|
||||
* Check that a given list of `CtorParameter`s has `typeValueReference`s of specific `ts.Identifier`
|
273
packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts
Normal file
@ -0,0 +1,273 @@
|
||||
/**
|
||||
* @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 {existsSync, readFileSync, readdirSync, statSync} from 'fs';
|
||||
import * as mockFs from 'mock-fs';
|
||||
import {join} from 'path';
|
||||
const Module = require('module');
|
||||
|
||||
import {mainNgcc} from '../../src/main';
|
||||
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers';
|
||||
import {EntryPointPackageJson} from '../../src/packages/entry_point';
|
||||
|
||||
describe('ngcc main()', () => {
|
||||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
||||
it('should run ngcc without errors for esm2015', () => {
|
||||
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']}))
|
||||
.not.toThrow();
|
||||
});
|
||||
|
||||
it('should run ngcc without errors for esm5', () => {
|
||||
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm5']}))
|
||||
.not.toThrow();
|
||||
});
|
||||
|
||||
describe('with targetEntryPointPath', () => {
|
||||
it('should only compile the given package entry-point (and its dependencies).', () => {
|
||||
mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http'});
|
||||
|
||||
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
esm2015: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// * `common` is a dependency of `common/http`, so is compiled.
|
||||
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
esm2015: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// * `core` is a dependency of `common`, so is compiled.
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
esm2015: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
|
||||
// * `common/testing` is not a dependency of `common/http` so is not compiled.
|
||||
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should mark a non-Angular package target as processed', () => {
|
||||
mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'});
|
||||
|
||||
// `test-package` has no Angular but is marked as processed.
|
||||
expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
|
||||
// * `core` is a dependency of `test-package`, but it is not processed, since test-package
|
||||
// was not processed.
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with propertiesToConsider', () => {
|
||||
it('should only compile the entry-point formats given in the `propertiesToConsider` list',
|
||||
() => {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
propertiesToConsider: ['main', 'esm5', 'module', 'fesm5']
|
||||
});
|
||||
|
||||
// * the `main` property is UMD, which is not yet supported.
|
||||
// * none of the ES2015 formats are compiled as they are not on the `propertiesToConsider`
|
||||
// list.
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
|
||||
esm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with compileAllFormats set to false', () => {
|
||||
it('should only compile the first matching format', () => {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
propertiesToConsider: ['main', 'module', 'fesm5', 'esm5'],
|
||||
compileAllFormats: false
|
||||
});
|
||||
// * The `main` is UMD, which is not yet supported, and so is not compiled.
|
||||
// * In the Angular packages fesm5 and module have the same underlying format,
|
||||
// so both are marked as compiled.
|
||||
// * The `esm5` is not compiled because we stopped after the `fesm5` format.
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
|
||||
fesm5: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with createNewEntryPointFormats', () => {
|
||||
it('should create new files rather than overwriting the originals', () => {
|
||||
const ANGULAR_CORE_IMPORT_REGEX = /import \* as ɵngcc\d+ from '@angular\/core';/;
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
createNewEntryPointFormats: true,
|
||||
propertiesToConsider: ['esm5']
|
||||
});
|
||||
|
||||
// Updates the package.json
|
||||
expect(loadPackage('@angular/common').esm5).toEqual('./esm5/common.js');
|
||||
expect((loadPackage('@angular/common') as any).esm5_ivy_ngcc)
|
||||
.toEqual('__ivy_ngcc__/esm5/common.js');
|
||||
|
||||
// Doesn't touch original files
|
||||
expect(readFileSync(`/node_modules/@angular/common/esm5/src/common_module.js`, 'utf8'))
|
||||
.not.toMatch(ANGULAR_CORE_IMPORT_REGEX);
|
||||
// Or create a backup of the original
|
||||
expect(existsSync(`/node_modules/@angular/common/esm5/src/common_module.js.__ivy_ngcc_bak`))
|
||||
.toBe(false);
|
||||
|
||||
// Creates new files
|
||||
expect(readFileSync(
|
||||
`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/common_module.js`, 'utf8'))
|
||||
.toMatch(ANGULAR_CORE_IMPORT_REGEX);
|
||||
|
||||
// Copies over files (unchanged) that did not need compiling
|
||||
expect(existsSync(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`));
|
||||
expect(readFileSync(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`, 'utf8'))
|
||||
.toEqual(readFileSync(`/node_modules/@angular/common/esm5/src/version.js`, 'utf8'));
|
||||
|
||||
// Overwrites .d.ts files (as usual)
|
||||
expect(readFileSync(`/node_modules/@angular/common/common.d.ts`, 'utf8'))
|
||||
.toMatch(ANGULAR_CORE_IMPORT_REGEX);
|
||||
expect(existsSync(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function createMockFileSystem() {
|
||||
mockFs({
|
||||
'/node_modules/@angular': loadAngularPackages(),
|
||||
'/node_modules/rxjs': loadDirectory(resolveNpmTreeArtifact('rxjs', 'index.js')),
|
||||
'/node_modules/tslib': loadDirectory(resolveNpmTreeArtifact('tslib', 'tslib.js')),
|
||||
'/node_modules/test-package': {
|
||||
'package.json': '{"name": "test-package", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
'index.js':
|
||||
'import {AppModule} from "@angular/common"; export class MyApp extends AppModule;',
|
||||
'index.d.s':
|
||||
'import {AppModule} from "@angular/common"; export declare class MyApp extends AppModule;',
|
||||
}
|
||||
});
|
||||
spyOn(Module, '_resolveFilename').and.callFake(mockResolve);
|
||||
}
|
||||
|
||||
function restoreRealFileSystem() {
|
||||
mockFs.restore();
|
||||
}
|
||||
|
||||
|
||||
/** Load the built Angular packages into an in-memory structure. */
|
||||
function loadAngularPackages(): Directory {
|
||||
const packagesDirectory: Directory = {};
|
||||
|
||||
getAngularPackagesFromRunfiles().forEach(
|
||||
({name, pkgPath}) => { packagesDirectory[name] = loadDirectory(pkgPath); });
|
||||
|
||||
return packagesDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load real files from the filesystem into an "in-memory" structure,
|
||||
* which can be used with `mock-fs`.
|
||||
* @param directoryPath the path to the directory we want to load.
|
||||
*/
|
||||
function loadDirectory(directoryPath: string): Directory {
|
||||
const directory: Directory = {};
|
||||
|
||||
readdirSync(directoryPath).forEach(item => {
|
||||
const itemPath = join(directoryPath, item);
|
||||
if (statSync(itemPath).isDirectory()) {
|
||||
directory[item] = loadDirectory(itemPath);
|
||||
} else {
|
||||
directory[item] = readFileSync(itemPath, 'utf-8');
|
||||
}
|
||||
});
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
interface Directory {
|
||||
[pathSegment: string]: string|Directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock implementation of the node.js Module._resolveFilename function,
|
||||
* which we are spying on to support mocking out the file-system in these tests.
|
||||
*
|
||||
* @param request the path to a module that needs resolving.
|
||||
*/
|
||||
function mockResolve(request: string): string|null {
|
||||
if (existsSync(request)) {
|
||||
const stat = statSync(request);
|
||||
if (stat.isFile()) {
|
||||
return request;
|
||||
} else if (stat.isDirectory()) {
|
||||
const pIndex = mockResolve(request + '/index');
|
||||
if (pIndex && existsSync(pIndex)) {
|
||||
return pIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const ext of ['.js', '.d.ts']) {
|
||||
if (existsSync(request + ext)) {
|
||||
return request + ext;
|
||||
}
|
||||
}
|
||||
if (request.indexOf('/node_modules') === 0) {
|
||||
// We already tried adding node_modules so give up.
|
||||
return null;
|
||||
} else {
|
||||
return mockResolve(join('/node_modules', request));
|
||||
}
|
||||
}
|
||||
|
||||
function loadPackage(packageName: string): EntryPointPackageJson {
|
||||
return JSON.parse(readFileSync(`/node_modules/${packageName}/package.json`, 'utf8'));
|
||||
}
|
180
packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* @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 {readFileSync, writeFileSync} from 'fs';
|
||||
import * as mockFs from 'mock-fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
|
||||
function createMockFileSystem() {
|
||||
mockFs({
|
||||
'/node_modules/@angular/common': {
|
||||
'package.json': `{
|
||||
"fesm2015": "./fesm2015/common.js",
|
||||
"fesm5": "./fesm5/common.js",
|
||||
"typings": "./common.d.ts"
|
||||
}`,
|
||||
'fesm2015': {
|
||||
'common.js': 'DUMMY CONTENT',
|
||||
'http.js': 'DUMMY CONTENT',
|
||||
'http/testing.js': 'DUMMY CONTENT',
|
||||
'testing.js': 'DUMMY CONTENT',
|
||||
},
|
||||
'http': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../fesm2015/http.js",
|
||||
"fesm5": "../fesm5/http.js",
|
||||
"typings": "./http.d.ts"
|
||||
}`,
|
||||
'testing': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../../fesm2015/http/testing.js",
|
||||
"fesm5": "../../fesm5/http/testing.js",
|
||||
"typings": "../http/testing.d.ts"
|
||||
}`,
|
||||
},
|
||||
},
|
||||
'other': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
'testing': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../fesm2015/testing.js",
|
||||
"fesm5": "../fesm5/testing.js",
|
||||
"typings": "../testing.d.ts"
|
||||
}`,
|
||||
},
|
||||
'node_modules': {
|
||||
'tslib': {
|
||||
'package.json': '{ }',
|
||||
'node_modules': {
|
||||
'other-lib': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/node_modules/@angular/no-typings': {
|
||||
'package.json': `{
|
||||
"fesm2015": "./fesm2015/index.js"
|
||||
}`,
|
||||
'fesm2015': {
|
||||
'index.js': 'DUMMY CONTENT',
|
||||
'index.d.ts': 'DUMMY CONTENT',
|
||||
},
|
||||
},
|
||||
'/node_modules/@angular/other': {
|
||||
'not-package.json': '{ "fesm2015": "./fesm2015/other.js" }',
|
||||
'package.jsonot': '{ "fesm5": "./fesm5/other.js" }',
|
||||
},
|
||||
'/node_modules/@angular/other2': {
|
||||
'node_modules_not': {
|
||||
'lib1': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
},
|
||||
'not_node_modules': {
|
||||
'lib2': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function restoreRealFileSystem() {
|
||||
mockFs.restore();
|
||||
}
|
||||
|
||||
describe('Marker files', () => {
|
||||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
||||
const COMMON_PACKAGE_PATH = AbsoluteFsPath.from('/node_modules/@angular/common/package.json');
|
||||
|
||||
describe('markAsProcessed', () => {
|
||||
it('should write a property in the package.json containing the version placeholder', () => {
|
||||
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'esm5');
|
||||
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
|
||||
it('should update the packageJson object in-place', () => {
|
||||
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasBeenProcessed', () => {
|
||||
it('should return true if the marker exists for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'fesm2015'))
|
||||
.toBe(true);
|
||||
});
|
||||
it('should return false if the marker does not exist for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'module'))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should return false if no markers exist',
|
||||
() => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
|
||||
it('should throw an Error if the format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
});
|
||||
it('should throw an Error if any format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
});
|
||||
});
|
||||
});
|
@ -9,6 +9,8 @@
|
||||
import * as path from 'canonical-path';
|
||||
import * as mockFs from 'mock-fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {DependencyHost} from '../../src/packages/dependency_host';
|
||||
const Module = require('module');
|
||||
|
||||
@ -16,6 +18,8 @@ interface DepMap {
|
||||
[path: string]: {resolved: string[], missing: string[]};
|
||||
}
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
|
||||
describe('DependencyHost', () => {
|
||||
let host: DependencyHost;
|
||||
beforeEach(() => host = new DependencyHost());
|
||||
@ -27,7 +31,8 @@ describe('DependencyHost', () => {
|
||||
it('should not generate a TS AST if the source does not contain any imports or re-exports',
|
||||
() => {
|
||||
spyOn(ts, 'createSourceFile');
|
||||
host.computeDependencies('/no/imports/or/re-exports.js', new Set(), new Set(), new Set());
|
||||
host.computeDependencies(
|
||||
_('/no/imports/or/re-exports.js'), new Set(), new Set(), new Set());
|
||||
expect(ts.createSourceFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -37,7 +42,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/imports.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/external/imports.js'), resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
@ -49,7 +54,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/re-exports.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/external/re-exports.js'), resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
@ -64,7 +69,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/imports-missing.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/external/imports-missing.js'), resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(1);
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(missing.size).toBe(1);
|
||||
@ -84,7 +89,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/deep-import.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/external/deep-import.js'), resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(0);
|
||||
expect(missing.size).toBe(0);
|
||||
expect(deepImports.size).toBe(1);
|
||||
@ -101,7 +106,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/internal/outer.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/internal/outer.js'), resolved, missing, deepImports);
|
||||
expect(getDependenciesSpy)
|
||||
.toHaveBeenCalledWith('/internal/outer.js', resolved, missing, deepImports);
|
||||
expect(getDependenciesSpy)
|
||||
@ -121,7 +126,7 @@ describe('DependencyHost', () => {
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/internal/circular-a.js', resolved, missing, deepImports);
|
||||
host.computeDependencies(_('/internal/circular-a.js'), resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
@ -144,14 +149,15 @@ describe('DependencyHost', () => {
|
||||
|
||||
describe('resolveInternal', () => {
|
||||
it('should resolve the dependency via `Module._resolveFilename`', () => {
|
||||
spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
|
||||
const result = host.resolveInternal('/SOURCE/PATH/FILE', '../TARGET/PATH/FILE');
|
||||
expect(result).toEqual('RESOLVED_PATH');
|
||||
spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
|
||||
const result = host.resolveInternal(
|
||||
_('/SOURCE/PATH/FILE'), PathSegment.fromFsPath('../TARGET/PATH/FILE'));
|
||||
expect(result).toEqual('/RESOLVED_PATH');
|
||||
});
|
||||
|
||||
it('should first resolve the `to` on top of the `from` directory', () => {
|
||||
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
|
||||
host.resolveInternal('/SOURCE/PATH/FILE', '../TARGET/PATH/FILE');
|
||||
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
|
||||
host.resolveInternal(_('/SOURCE/PATH/FILE'), PathSegment.fromFsPath('../TARGET/PATH/FILE'));
|
||||
expect(resolveSpy)
|
||||
.toHaveBeenCalledWith('/SOURCE/TARGET/PATH/FILE', jasmine.any(Object), false, undefined);
|
||||
});
|
||||
@ -159,37 +165,39 @@ describe('DependencyHost', () => {
|
||||
|
||||
describe('tryResolveExternal', () => {
|
||||
it('should call `tryResolve`, appending `package.json` to the target path', () => {
|
||||
const tryResolveSpy = spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
|
||||
host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH');
|
||||
expect(tryResolveSpy).toHaveBeenCalledWith('SOURCE_PATH', 'TARGET_PATH/package.json');
|
||||
const tryResolveSpy = spyOn(host, 'tryResolve').and.returnValue('/PATH/TO/RESOLVED');
|
||||
host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
|
||||
expect(tryResolveSpy).toHaveBeenCalledWith('/SOURCE_PATH', 'TARGET_PATH/package.json');
|
||||
});
|
||||
|
||||
it('should return the directory containing the result from `tryResolve', () => {
|
||||
spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
|
||||
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual('PATH/TO');
|
||||
spyOn(host, 'tryResolve').and.returnValue('/PATH/TO/RESOLVED');
|
||||
expect(host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH')))
|
||||
.toEqual(_('/PATH/TO'));
|
||||
});
|
||||
|
||||
it('should return null if `tryResolve` returns null', () => {
|
||||
spyOn(host, 'tryResolve').and.returnValue(null);
|
||||
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual(null);
|
||||
expect(host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH')))
|
||||
.toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tryResolve()', () => {
|
||||
it('should resolve the dependency via `Module._resolveFilename`, passing the `from` path to the `paths` option',
|
||||
() => {
|
||||
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
|
||||
const result = host.tryResolve('SOURCE_PATH', 'TARGET_PATH');
|
||||
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
|
||||
const result = host.tryResolve(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
|
||||
expect(resolveSpy).toHaveBeenCalledWith('TARGET_PATH', jasmine.any(Object), false, {
|
||||
paths: ['SOURCE_PATH']
|
||||
paths: ['/SOURCE_PATH']
|
||||
});
|
||||
expect(result).toEqual('RESOLVED_PATH');
|
||||
expect(result).toEqual(_('/RESOLVED_PATH'));
|
||||
});
|
||||
|
||||
it('should return null if `Module._resolveFilename` throws an error', () => {
|
||||
const resolveSpy =
|
||||
spyOn(Module, '_resolveFilename').and.throwError(`Cannot find module 'TARGET_PATH'`);
|
||||
const result = host.tryResolve('SOURCE_PATH', 'TARGET_PATH');
|
||||
const result = host.tryResolve(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
});
|
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @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 {resolve} from 'canonical-path';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {DependencyHost} from '../../src/packages/dependency_host';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../../src/packages/dependency_resolver';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
|
||||
describe('DependencyResolver', () => {
|
||||
let host: DependencyHost;
|
||||
let resolver: DependencyResolver;
|
||||
beforeEach(() => {
|
||||
host = new DependencyHost();
|
||||
resolver = new DependencyResolver(host);
|
||||
});
|
||||
describe('sortEntryPointsByDependency()', () => {
|
||||
const first = { path: _('/first'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
|
||||
const second = { path: _('/second'), packageJson: {esm2015: 'sub/index.ts'} } as EntryPoint;
|
||||
const third = { path: _('/third'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
|
||||
const fourth = { path: _('/fourth'), packageJson: {esm2015: 'sub2/index.ts'} } as EntryPoint;
|
||||
const fifth = { path: _('/fifth'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
|
||||
|
||||
const dependencies = {
|
||||
[_('/first/index.ts')]: {resolved: [second.path, third.path, '/ignored-1'], missing: []},
|
||||
[_('/second/sub/index.ts')]: {resolved: [third.path, fifth.path], missing: []},
|
||||
[_('/third/index.ts')]: {resolved: [fourth.path, '/ignored-2'], missing: []},
|
||||
[_('/fourth/sub2/index.ts')]: {resolved: [fifth.path], missing: []},
|
||||
[_('/fifth/index.ts')]: {resolved: [], missing: []},
|
||||
};
|
||||
|
||||
it('should order the entry points by their dependency on each other', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
|
||||
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
|
||||
expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]);
|
||||
});
|
||||
|
||||
it('should remove entry-points that have missing direct dependencies', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
|
||||
[_('/first/index.ts')]: {resolved: [], missing: ['/missing']},
|
||||
[_('/second/sub/index.ts')]: {resolved: [], missing: []},
|
||||
}));
|
||||
const result = resolver.sortEntryPointsByDependency([first, second]);
|
||||
expect(result.entryPoints).toEqual([second]);
|
||||
expect(result.invalidEntryPoints).toEqual([
|
||||
{entryPoint: first, missingDependencies: ['/missing']},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove entry points that depended upon an invalid entry-point', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
|
||||
[_('/first/index.ts')]: {resolved: [second.path], missing: []},
|
||||
[_('/second/sub/index.ts')]: {resolved: [], missing: ['/missing']},
|
||||
[_('/third/index.ts')]: {resolved: [], missing: []},
|
||||
}));
|
||||
// Note that we will process `first` before `second`, which has the missing dependency.
|
||||
const result = resolver.sortEntryPointsByDependency([first, second, third]);
|
||||
expect(result.entryPoints).toEqual([third]);
|
||||
expect(result.invalidEntryPoints).toEqual([
|
||||
{entryPoint: second, missingDependencies: ['/missing']},
|
||||
{entryPoint: first, missingDependencies: ['/missing']},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove entry points that will depend upon an invalid entry-point', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
|
||||
[_('/first/index.ts')]: {resolved: [second.path], missing: []},
|
||||
[_('/second/sub/index.ts')]: {resolved: [], missing: ['/missing']},
|
||||
[_('/third/index.ts')]: {resolved: [], missing: []},
|
||||
}));
|
||||
// Note that we will process `first` after `second`, which has the missing dependency.
|
||||
const result = resolver.sortEntryPointsByDependency([second, first, third]);
|
||||
expect(result.entryPoints).toEqual([third]);
|
||||
expect(result.invalidEntryPoints).toEqual([
|
||||
{entryPoint: second, missingDependencies: ['/missing']},
|
||||
{entryPoint: first, missingDependencies: [second.path]},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should error if the entry point does not have either the esm5 nor esm2015 formats', () => {
|
||||
expect(() => resolver.sortEntryPointsByDependency([
|
||||
{ path: '/first', packageJson: {} } as EntryPoint
|
||||
])).toThrowError(`There is no format with import statements in '/first' entry-point.`);
|
||||
});
|
||||
|
||||
it('should capture any dependencies that were ignored', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
|
||||
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
|
||||
expect(result.ignoredDependencies).toEqual([
|
||||
{entryPoint: first, dependencyPath: '/ignored-1'},
|
||||
{entryPoint: third, dependencyPath: '/ignored-2'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only return dependencies of the target, if provided', () => {
|
||||
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
|
||||
const entryPoints = [fifth, first, fourth, second, third];
|
||||
let sorted: SortedEntryPointsInfo;
|
||||
|
||||
sorted = resolver.sortEntryPointsByDependency(entryPoints, first);
|
||||
expect(sorted.entryPoints).toEqual([fifth, fourth, third, second, first]);
|
||||
sorted = resolver.sortEntryPointsByDependency(entryPoints, second);
|
||||
expect(sorted.entryPoints).toEqual([fifth, fourth, third, second]);
|
||||
sorted = resolver.sortEntryPointsByDependency(entryPoints, third);
|
||||
expect(sorted.entryPoints).toEqual([fifth, fourth, third]);
|
||||
sorted = resolver.sortEntryPointsByDependency(entryPoints, fourth);
|
||||
expect(sorted.entryPoints).toEqual([fifth, fourth]);
|
||||
sorted = resolver.sortEntryPointsByDependency(entryPoints, fifth);
|
||||
expect(sorted.entryPoints).toEqual([fifth]);
|
||||
});
|
||||
|
||||
interface DepMap {
|
||||
[path: string]: {resolved: string[], missing: string[]};
|
||||
}
|
||||
|
||||
function createFakeComputeDependencies(deps: DepMap) {
|
||||
return (entryPoint: string) => {
|
||||
const dependencies = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
deps[entryPoint].resolved.forEach(dep => dependencies.add(dep));
|
||||
deps[entryPoint].missing.forEach(dep => missing.add(dep));
|
||||
return {dependencies, missing, deepImports};
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
@ -7,11 +7,15 @@
|
||||
*/
|
||||
|
||||
import * as mockFs from 'mock-fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {DependencyHost} from '../../src/packages/dependency_host';
|
||||
import {DependencyResolver} from '../../src/packages/dependency_resolver';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
let resolver: DependencyResolver;
|
||||
let finder: EntryPointFinder;
|
||||
@ -25,57 +29,57 @@ describe('findEntryPoints()', () => {
|
||||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/sub_entry_points');
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
['/sub_entry_points/common', '/sub_entry_points/common'],
|
||||
['/sub_entry_points/common', '/sub_entry_points/common/http'],
|
||||
['/sub_entry_points/common', '/sub_entry_points/common/http/testing'],
|
||||
['/sub_entry_points/common', '/sub_entry_points/common/testing'],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http/testing')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find packages inside a namespace', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/namespaced');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/namespaced'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
['/namespaced/@angular/common', '/namespaced/@angular/common'],
|
||||
['/namespaced/@angular/common', '/namespaced/@angular/common/http'],
|
||||
['/namespaced/@angular/common', '/namespaced/@angular/common/http/testing'],
|
||||
['/namespaced/@angular/common', '/namespaced/@angular/common/testing'],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http/testing')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no packages', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/no_packages');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_packages'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no valid entry-points', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/no_valid_entry_points');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_valid_entry_points'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders starting with .', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/dotted_folders');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/dotted_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders that are symlinked', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/symlinked_folders');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/symlinked_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
const {entryPoints} = finder.findEntryPoints('/nested_node_modules');
|
||||
const {entryPoints} = finder.findEntryPoints(_('/nested_node_modules'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
['/nested_node_modules/outer', '/nested_node_modules/outer'],
|
||||
[_('/nested_node_modules/outer'), _('/nested_node_modules/outer')],
|
||||
// Note that the inner entry point does not get included as part of the outer package
|
||||
[
|
||||
'/nested_node_modules/outer/node_modules/inner',
|
||||
'/nested_node_modules/outer/node_modules/inner'
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
],
|
||||
]);
|
||||
});
|