Compare commits

...

46 Commits

Author SHA1 Message Date
c34c223122 fix(ivy): avoid type coercion in binding-related instruction asserts (#29470)
ivy's bindingUpdated instruction is using the assertNotEqual check to make
sure that NO_CHANGE value (of type Object) is not passed as a value to be
dirty-checked. In practice it means that any value passed as a binding
value would be compared to the NO_CHANGE object.

It turns out that the assertNotEqual is using == and given
that binding values are of different type and we always compare it to the
NO_CHANGE object we were doing lots of type coercion. It resulted in calls
to expensive types conversions and calls to Object.toString().

A profiler reported ~15% of the self time spent in the assertNotEqual
but it turns out that removing type coercion speeds up Material Chips with
input scenario much more (~40ms down to ~20ms).

This PR introduces new assert method `assertNotSame` that uses strict equality
check. The new assertion is used in binding instructions to compare to
NO_CHANGE object reference.

PR Close #29470
2019-03-25 09:34:13 -07:00
26a8c5961e fix(router): support NgFactory promise in loadChildren typings (#29392)
The router loadChildren property already supports a promise that returns a NgModuleFactory, but the typings cause the compilation to fail.

PR Close #29392
2019-03-25 09:30:28 -07:00
41225289d7 ci: add codefresh badge (#29475)
Fix link in Codefresh ci readme
Add a public badge to show the last status of the last build

fix: cleanup

docs: move badge to the head

PR Close #29475
2019-03-25 09:23:48 -07:00
3612ddb433 refactor: remove extra line break in gulpfile (#29489)
PR Close #29489
2019-03-25 09:22:37 -07:00
09fab58109 fix(core): static-query schematic should detect queries in "ngDoCheck" and "ngOnChanges" (#29492)
Queries can be also used statically within the "ngDoCheck" and "ngOnChanges" lifecylce hook.
In order to properly detect all queries, we need to also respect these lifecycle hooks.

Resolves FW-1192

PR Close #29492
2019-03-25 09:21:35 -07:00
b3102b9de1 docs: changing "struture" to "structure" (#29497)
PR Close #29497
2019-03-25 09:21:10 -07:00
cfe6581fc8 test(ivy): remove passing test from blocklist (#29484)
Removes the test that was fixed by https://github.com/angular/material2/pull/15555 from the blocklist.

PR Close #29484
2019-03-22 16:46:00 -07:00
bef5043a5a fix(ivy): TestBed overriding custom ErrorHandler (#29482)
Fixes TestBed's default ErrorHandler overriding the one provided by the consumer via an `import`.

This PR resolves FW-1193.

PR Close #29482
2019-03-22 16:45:33 -07:00
b5295ad277 test(bazel): Add router to bazel integration test (#29459)
PR Close #29459
2019-03-22 13:17:01 -07:00
769d960db1 fix(bazel): workaround problem reading summary files from node_modules (#29459)
PR Close #29459
2019-03-22 13:17:00 -07:00
21be0fb926 fix(bazel): allow ng_module users to set createExternalSymbolFactoryReexports (#29459)
PR Close #29459
2019-03-22 13:17:00 -07:00
9bcc1e8dce ci: add .codefresh/ to the fw-dev-infra group (#29478)
PR Close #29478
2019-03-22 13:16:16 -07:00
a06f0340d2 docs(core): close tags in example (#29474)
PR Close #29474
2019-03-22 13:15:03 -07:00
66b72bfa58 fix(ivy): ViewContainerRef.destroy should properly clean the DOM (#29414)
PR Close #29414
2019-03-22 13:13:12 -07:00
00075647be fix(compiler): inherit param types when class has a constructor which takes no declared parameters and delegates up (#29232)
In ReflectionCapabilities, when checking for own parameters of a type, inherit the types properly for classes that do have a constructor, but the constructor takes no declared parameters and just delegates to super(...arguments). This removes the need to declare trivial constructors in such classes to make them work properly in JIT mode. Without this, DI fails and injectables are undefined.

PR Close #29232
2019-03-22 13:12:25 -07:00
ce789b75a4 docs: update developer guide for testing and IntelliJ (#29048)
provide the command for running all test suites
- don't make new contributors read the whole Bazel doc
add info about ClangFormatIJ plugin for IntelliJ
add info about Bazel plugin for IntelliJ
fix inconsistent casing of GitHub

PR Close #29048
2019-03-22 13:05:40 -07:00
7baf45fe88 docs: fix minor typo in testing.md (#29464)
PR Close #29464
2019-03-22 10:59:21 -07:00
f3e0cc89ed fix(ivy): properly check LView array size in binding asserts (#29476)
This fix corrects a bug where we were passing a binding _value_
in place of an expected binding index. This reulted in the binding
value being compared to an array length and buggy type coercion.

Fixing this bug speeds up test scenario by ~10-15%.

PR Close #29476
2019-03-22 10:59:00 -07:00
cfa6e8e008 docs: add Siddharth Ajmera to GDE resources (#28456)
PR Close #28456
2019-03-21 15:57:16 -07:00
02debebcff docs: add NG-DE 2019 to events page (#29079)
PR Close #29079
2019-03-21 15:55:15 -07:00
ed8d60d060 docs: add app shell guide (#28591)
PR Close #28591
2019-03-21 15:54:26 -07:00
9ea0d64d8b docs(core): indicate OnPush applies to children (#28320)
Indicate the OnPush change detection strategy applies to all child directives.
Resolves confusion from #12480.
PR Close #28320
2019-03-21 15:48:46 -07:00
dfcf759e33 docs: drop Coding Conventions section from style guide (#29331)
Closes #24153

PR Close #29331
2019-03-21 15:40:20 -07:00
21835af70c fix(ivy): handle class declarations consistently in ES5 code (#29209)
PR Close #29209
2019-03-21 22:20:24 +00:00
2790352d04 refactor(ivy): use ClassDeclaration in more ReflectionHost methods (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
bb6a3632f6 refactor(ivy): correctly type class declarations in ngtsc/ngcc (#29209)
Previously, several `ngtsc` and `ngcc` APIs dealing with class
declaration nodes used inconsistent types. For example, some methods of
the `DecoratorHandler` interface expected a `ts.Declaration` argument,
but actual `DecoratorHandler` implementations specified a stricter
`ts.ClassDeclaration` type.

As a result, the stricter methods would operate under the incorrect
assumption that their arguments were of type `ts.ClassDeclaration`,
while the actual arguments might be of different types (e.g. `ngcc`
would call them with `ts.FunctionDeclaration` or
`ts.VariableDeclaration` arguments, when compiling ES5 code).

Additionally, since we need those class declarations to be referenced in
other parts of the program, `ngtsc`/`ngcc` had to either repeatedly
check for `ts.isIdentifier(node.name)` or assume there was a `name`
identifier and use `node.name!`. While this assumption happens to be
true in the current implementation, working around type-checking is
error-prone (e.g. the assumption might stop being true in the future).

This commit fixes this by introducing a new type to be used for such
class declarations (`ts.Declaration & {name: ts.Identifier}`) and using
it consistently throughput the code.

PR Close #29209
2019-03-21 22:20:23 +00:00
2d859a8c3a refactor(ivy): implement DtsModuleScopeResolver from MetadataDtsModuleScopeResolver (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
70fffba054 refactor(ivy): remove unused code from TypeCheckContext (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
e185d3a4ad ci: add codefresh (#29305)
PR Close #29305
2019-03-21 22:19:19 +00:00
6f3052b799 ci: disable sauce-connect ssl bumping (#29447)
By default we disable SSL bumping for all requests. This is because SSL
bumping is not needed for our test setup and in order to perform the SSL
bumping, Saucelabs intercepts all HTTP requests in the tunnel VM and modifies
them. This can cause flakiness as it makes all requests dependent on the SSL bumping
middleware.

See: https://wiki.saucelabs.com/display/DOCS/Troubleshooting+Sauce+Connect#TroubleshootingSauceConnect-DisablingSSLBumping

PR Close #29447
2019-03-21 22:18:18 +00:00
c18fa7b5bd build(docs-infra): upgrade cli command docs sources to 18d979cdc (#29437)
Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master).
Relevant changes in [commit range](cafa558cf...18d979cdc):

**Added**
- help/analytics.json

**Modified**
- help/add.json
- help/build.json
- help/generate.json
- help/new.json
- help/serve.json

PR Close #29437
2019-03-21 15:32:03 -04:00
693b350567 build: add @npm//jasmine-core dep back to jasmine_node_test in defaults.bzl (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
0b27c09b51 build(bazel): also back out of jasmine bootstrap simplification (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
861d6f1523 build(bazel): back out of @bazel/jasmine 0.27.7 with shard count (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
dc10355d61 build(compiler-cli): enable full TypeScript strictness (#29436)
This commit enables strict: true in TypeScript builds of
//packages/compiler-cli.

PR Close #29436
2019-03-21 12:14:39 -04:00
6cd3743b44 refactor(service-worker): use Adapter#parseUrl() for all URL parsing (#27080)
This commit also ensures that the correct implementation is used on
environments that do not support `URL` (e.g. Node.js).

PR Close #27080
2019-03-21 12:07:56 -04:00
a24f4b51b3 test(service-worker): test support for multiple apps on different subpaths of a domain (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
84baa0bb08 test(service-worker): make it easy to use a different SW scope in tests (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
e721c08c7f feat(service-worker): support multiple apps on different subpaths of a domain (#27080)
Previously, it was not possible to have multiple apps (using
`@angular/service-worker`) on different subpaths of the same domain,
because each SW would overwrite the caches of the others (even though
their scope was different).

This commit fixes it by ensuring that the cache names created by the SW
are different for each scope.

Fixes #21388

PR Close #27080
2019-03-21 12:07:56 -04:00
37a154e4e6 test(service-worker): do not create testing artifacts on environments that do not support SW (#27080)
The tests will not be run anyway, so the artifacts are never used and
there might be errors if creating the testing artifacts relies on APIs
that are not available in that environment (e.g. `URL`).

PR Close #27080
2019-03-21 12:07:56 -04:00
415de9a291 test(service-worker): ensure SwTestHarness#parseUrl() behaves the same on browser and Node.js (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
b3dda0ebc1 refactor(service-worker): make second parameter to Adapter#parseUrl() optional (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
41737bb4d3 docs: fix html tags in changelog (#29411)
PR Close #29411
2019-03-21 02:46:41 -04:00
afe3e72601 docs: add Luis Aviles to GDE resources (#29405)
PR Close #29405
2019-03-20 19:06:23 -04:00
5e3bbf79a6 build(bazel): add comment about advancing the version of yarn used under Bazel in the future (#29431)
PR Close #29431
2019-03-20 18:36:13 -04:00
d2b64cc008 build(bazel): revert back to yarn 1.12.1 under Bazel to fix Windows file-in-use issues (#29431)
PR Close #29431
2019-03-20 18:36:13 -04:00
124 changed files with 2313 additions and 1707 deletions

View 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
View File

@ -0,0 +1,33 @@
# CodeFresh configuration
[![Codefresh build status](https://g.codefresh.io/api/badges/pipeline/angular/angular%2Fangular%2Fangular?type=cf-1)](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
View 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
View 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

View 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."

1
.github/CODEOWNERS vendored
View File

@ -821,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

View File

@ -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>`.

View File

@ -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(

View 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.

View File

@ -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).

View File

@ -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>

View File

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -673,21 +673,20 @@
"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":
{
"jschwarty": {
"name": "Justin Schwartzenberger",
"picture": "justinschwartzenberger.jpg",
"twitter": "schwarty",
@ -739,5 +738,21 @@
"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"
}
}
}

View File

@ -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>

View File

@ -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",

View File

@ -19,7 +19,7 @@
"build-local": "yarn ~~build",
"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",

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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*"
]
}

View File

@ -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",
],
)

View 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 { }

View File

@ -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(['...']));
}

View File

@ -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 {
}

View File

@ -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';

View File

@ -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",
@ -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"
}
}
}

View File

@ -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==

View File

@ -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"],
)

View File

@ -36,7 +36,7 @@
"@angular-devkit/core": "^7.3.2",
"@angular-devkit/schematics": "^7.3.2",
"@angular/bazel": "file:./tools/npm/@angular_bazel",
"@bazel/jasmine": "0.27.7",
"@bazel/jasmine": "0.26.0",
"@bazel/karma": "0.27.7",
"@bazel/typescript": "0.27.7",
"@microsoft/api-extractor": "^7.0.21",
@ -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",

View File

@ -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);

View File

@ -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);

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {Decorator} from '../../../src/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[]) {}
}

View File

@ -8,9 +8,9 @@
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, CtorParameter, Decorator, Import, TypeScriptReflectionHost, reflectObjectLiteral} from '../../../src/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;
@ -396,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);
@ -405,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) {
@ -443,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);
}
@ -489,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 => {
@ -501,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);
@ -563,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 => {
@ -862,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) {
@ -891,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) :
@ -965,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');
@ -1012,7 +1032,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* 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))
@ -1028,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);
}

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, reflectObjectLiteral} from '../../../src/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';
@ -36,21 +36,23 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
/**
* 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;
@ -59,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;
}
/**
@ -113,12 +116,7 @@ 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);
if (outerClassNode && hasNameIdentifier(outerClassNode)) {
id = outerClassNode.name;
}
const declaration = super.getDeclarationOfIdentifier(id);
const declaration = super.getDeclarationOfIdentifier(outerClassNode ? outerClassNode.name : id);
if (!declaration || !ts.isVariableDeclaration(declaration.node) ||
declaration.node.initializer !== undefined ||
@ -170,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.
@ -310,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) : [];
}
}
@ -388,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...
@ -413,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;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {ReflectionHost} from '../../../src/ngtsc/reflection';
import {ClassSymbol, ReflectionHost} from '../../../src/ngtsc/reflection';
import {DecoratedClass} from './decorated_class';
export const PRE_R3_MARKER = '__PRE_R3__';
@ -52,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

View File

@ -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);
}

View File

@ -481,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 =

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../src/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);

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../src/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';
@ -559,8 +559,8 @@ describe('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
}).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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
() => {
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('Esm2015ReflectionHost', () => {
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('Esm2015ReflectionHost', () => {
});
});
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);

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../src/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,7 +365,7 @@ 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);
@ -427,8 +427,7 @@ 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));

View File

@ -8,9 +8,9 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../src/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 {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: `
@ -702,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();
@ -720,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);
});
@ -729,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);
});
@ -738,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([]);
});
@ -747,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);
@ -758,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);
@ -769,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);
@ -784,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);
@ -799,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);
@ -812,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);
@ -824,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);
@ -839,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') !;
@ -857,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') !;
@ -904,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') !;
@ -918,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') !;
@ -932,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') !;
@ -946,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() {}"`);
@ -956,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([]);
@ -968,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');
@ -979,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 !;
@ -992,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 !;
@ -1005,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 !;
@ -1024,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();
@ -1041,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 !;
@ -1056,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 !;
@ -1071,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 !;
@ -1087,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();
@ -1116,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() {}"');
@ -1129,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));
@ -1142,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([]);
@ -1155,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);
@ -1171,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);
@ -1189,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 !;
@ -1201,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 !;
@ -1217,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 !;
@ -1244,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);
}
@ -1313,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 !;
@ -1328,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 !;
@ -1342,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 !;
@ -1359,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);
@ -1369,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);
@ -1382,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);
@ -1391,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);
@ -1401,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'});
@ -1416,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'});
@ -1426,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,
@ -1447,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);
@ -1458,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);
@ -1480,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)
@ -1543,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);
});
});
@ -1654,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);
}

View File

@ -258,28 +258,16 @@ SOME DEFINITION TEXT
const {renderer, host, sourceFile, program} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
const badSymbolDeclaration =
getDeclaration(program, sourceFile.fileName, 'A', ts.isVariableDeclaration);
const badSymbol: any = {name: 'BadSymbol', declaration: badSymbolDeclaration};
const hostSpy = spyOn(host, 'getClassSymbol').and.returnValue(null);
expect(() => renderer.addDefinitions(output, badSymbol, 'SOME DEFINITION TEXT'))
.toThrowError('Compiled class does not have a valid symbol: BadSymbol in /some/file.js');
const noIifeDeclaration =
getDeclaration(program, sourceFile.fileName, 'NoIife', ts.isFunctionDeclaration);
const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'};
hostSpy.and.returnValue({valueDeclaration: noIifeDeclaration});
expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT'))
.toThrowError(
'Compiled class declaration is not inside an IIFE: NoIife in /some/file.js');
const badIifeWrapper: any =
getDeclaration(program, sourceFile.fileName, 'BadIife', ts.isVariableDeclaration);
const badIifeDeclaration =
badIifeWrapper.initializer.expression.expression.body.statements[0];
getDeclaration(program, sourceFile.fileName, 'BadIife', ts.isVariableDeclaration);
const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'};
hostSpy.and.returnValue({valueDeclaration: badIifeDeclaration});
expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT'))
.toThrowError(
'Compiled class wrapper IIFE does not have a return statement: BadIife in /some/file.js');

View File

@ -7,10 +7,9 @@
*/
import {R3BaseRefMetaData, compileBaseDefFromMetadata} from '@angular/compiler';
import * as ts from 'typescript';
import {PartialEvaluator} from '../../partial_evaluator';
import {ClassMember, Decorator, ReflectionHost} from '../../reflection';
import {ClassDeclaration, ClassMember, Decorator, ReflectionHost} from '../../reflection';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
import {isAngularDecorator} from './util';
@ -33,7 +32,7 @@ export class BaseDefDecoratorHandler implements
readonly precedence = HandlerPrecedence.WEAK;
detect(node: ts.ClassDeclaration, decorators: Decorator[]|null):
detect(node: ClassDeclaration, decorators: Decorator[]|null):
DetectResult<R3BaseRefDecoratorDetection>|undefined {
if (containsNgTopLevelDecorator(decorators, this.isCore)) {
// If the class is already decorated by @Component or @Directive let that
@ -70,7 +69,7 @@ export class BaseDefDecoratorHandler implements
}
}
analyze(node: ts.ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
analyze(node: ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
AnalysisOutput<R3BaseRefMetaData> {
const analysis: R3BaseRefMetaData = {};
if (metadata.inputs) {
@ -114,7 +113,7 @@ export class BaseDefDecoratorHandler implements
return {analysis};
}
compile(node: ts.Declaration, analysis: R3BaseRefMetaData): CompileResult[]|CompileResult {
compile(node: ClassDeclaration, analysis: R3BaseRefMetaData): CompileResult[]|CompileResult {
const {expression, type} = compileBaseDefFromMetadata(analysis);
return {

View File

@ -14,7 +14,7 @@ import {CycleAnalyzer} from '../../cycles';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports';
import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection';
import {ClassDeclaration, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection';
import {LocalModuleScopeRegistry, ScopeDirective, extractDirectiveGuards} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../transform';
import {TypeCheckContext} from '../../typecheck';
@ -60,7 +60,7 @@ export class ComponentDecoratorHandler implements
readonly precedence = HandlerPrecedence.PRIMARY;
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
return undefined;
}
@ -75,7 +75,7 @@ export class ComponentDecoratorHandler implements
}
}
preanalyze(node: ts.ClassDeclaration, decorator: Decorator): Promise<void>|undefined {
preanalyze(node: ClassDeclaration, decorator: Decorator): Promise<void>|undefined {
// In preanalyze, resource URLs associated with the component are asynchronously preloaded via
// the resourceLoader. This is the only time async operations are allowed for a component.
// These resources are:
@ -127,7 +127,7 @@ export class ComponentDecoratorHandler implements
}
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<ComponentHandlerData> {
analyze(node: ClassDeclaration, decorator: Decorator): AnalysisOutput<ComponentHandlerData> {
const containingFile = node.getSourceFile().fileName;
const meta = this._resolveLiteral(decorator);
this.literalCache.delete(decorator);
@ -213,7 +213,7 @@ export class ComponentDecoratorHandler implements
const ref = new Reference(node);
this.scopeRegistry.registerDirective({
ref,
name: node.name !.text,
name: node.name.text,
selector: metadata.selector,
exportAs: metadata.exportAs,
inputs: metadata.inputs,
@ -297,7 +297,7 @@ export class ComponentDecoratorHandler implements
return output;
}
typeCheck(ctx: TypeCheckContext, node: ts.Declaration, meta: ComponentHandlerData): void {
typeCheck(ctx: TypeCheckContext, node: ClassDeclaration, meta: ComponentHandlerData): void {
if (!ts.isClassDeclaration(node)) {
return;
}
@ -312,7 +312,7 @@ export class ComponentDecoratorHandler implements
}
}
resolve(node: ts.ClassDeclaration, analysis: ComponentHandlerData): ResolveResult {
resolve(node: ClassDeclaration, analysis: ComponentHandlerData): ResolveResult {
const context = node.getSourceFile();
// Check whether this component was registered with an NgModule. If so, it should be compiled
// under that module's compilation scope.
@ -345,9 +345,9 @@ export class ComponentDecoratorHandler implements
if (!cycleDetected) {
const wrapDirectivesAndPipesInClosure =
directives.some(
dir => isExpressionForwardReference(dir.expression, node.name !, context)) ||
dir => isExpressionForwardReference(dir.expression, node.name, context)) ||
Array.from(pipes.values())
.some(pipe => isExpressionForwardReference(pipe, node.name !, context));
.some(pipe => isExpressionForwardReference(pipe, node.name, context));
metadata.directives = directives;
metadata.pipes = pipes;
metadata.wrapDirectivesAndPipesInClosure = wrapDirectivesAndPipesInClosure;
@ -374,7 +374,7 @@ export class ComponentDecoratorHandler implements
return {};
}
compile(node: ts.ClassDeclaration, analysis: ComponentHandlerData, pool: ConstantPool):
compile(node: ClassDeclaration, analysis: ComponentHandlerData, pool: ConstantPool):
CompileResult {
const res = compileComponentFromMetadata(analysis.meta, pool, makeBindingParser());

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, Reference} from '../../imports';
import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {ClassMember, ClassMemberKind, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection';
import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection';
import {LocalModuleScopeRegistry} from '../../scope/src/local';
import {extractDirectiveGuards} from '../../scope/src/util';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
@ -35,7 +35,7 @@ export class DirectiveDecoratorHandler implements
readonly precedence = HandlerPrecedence.PRIMARY;
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
return undefined;
}
@ -50,7 +50,7 @@ export class DirectiveDecoratorHandler implements
}
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<DirectiveHandlerData> {
analyze(node: ClassDeclaration, decorator: Decorator): AnalysisOutput<DirectiveHandlerData> {
const directiveResult = extractDirectiveMetadata(
node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore);
const analysis = directiveResult && directiveResult.metadata;
@ -61,7 +61,7 @@ export class DirectiveDecoratorHandler implements
const ref = new Reference(node);
this.scopeRegistry.registerDirective({
ref,
name: node.name !.text,
name: node.name.text,
selector: analysis.selector,
exportAs: analysis.exportAs,
inputs: analysis.inputs,
@ -84,7 +84,7 @@ export class DirectiveDecoratorHandler implements
};
}
compile(node: ts.ClassDeclaration, analysis: DirectiveHandlerData, pool: ConstantPool):
compile(node: ClassDeclaration, analysis: DirectiveHandlerData, pool: ConstantPool):
CompileResult {
const res = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser());
const statements = res.statements;
@ -104,7 +104,7 @@ export class DirectiveDecoratorHandler implements
* Helper function to extract metadata from a `Directive` or `Component`.
*/
export function extractDirectiveMetadata(
clazz: ts.ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
clazz: ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
defaultSelector: string | null = null): {
decorator: Map<string, ts.Expression>,
@ -189,7 +189,7 @@ export function extractDirectiveMetadata(
selector = resolved === '' ? defaultSelector : resolved;
}
if (!selector) {
throw new Error(`Directive ${clazz.name !.text} has no selector, please add it!`);
throw new Error(`Directive ${clazz.name.text} has no selector, please add it!`);
}
const host = extractHostBindings(directive, decoratedElements, evaluator, coreModule);
@ -217,14 +217,14 @@ export function extractDirectiveMetadata(
// Detect if the component inherits from another class
const usesInheritance = reflector.hasBaseClass(clazz);
const metadata: R3DirectiveMetadata = {
name: clazz.name !.text,
name: clazz.name.text,
deps: getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore), host,
lifecycle: {
usesOnChanges,
},
inputs: {...inputsFromMeta, ...inputsFromFields},
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector,
type: new WrappedNodeExpr(clazz.name !),
type: new WrappedNodeExpr(clazz.name),
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
typeSourceSpan: null !, usesInheritance, exportAs, providers
};

View File

@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder} from '../../imports';
import {Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
import {generateSetClassMetadataCall} from './metadata';
@ -33,7 +33,7 @@ export class InjectableDecoratorHandler implements
readonly precedence = HandlerPrecedence.SHARED;
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
return undefined;
}
@ -48,7 +48,7 @@ export class InjectableDecoratorHandler implements
}
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<InjectableHandlerData> {
analyze(node: ClassDeclaration, decorator: Decorator): AnalysisOutput<InjectableHandlerData> {
return {
analysis: {
meta: extractInjectableMetadata(
@ -60,7 +60,7 @@ export class InjectableDecoratorHandler implements
};
}
compile(node: ts.ClassDeclaration, analysis: InjectableHandlerData): CompileResult {
compile(node: ClassDeclaration, analysis: InjectableHandlerData): CompileResult {
const res = compileIvyInjectable(analysis.meta);
const statements = res.statements;
if (analysis.metadataStmt !== null) {
@ -81,13 +81,9 @@ export class InjectableDecoratorHandler implements
* A `null` return value indicates this is @Injectable has invalid data.
*/
function extractInjectableMetadata(
clazz: ts.ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
clazz: ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
strictCtorDeps: boolean): R3InjectableMetadata {
if (clazz.name === undefined) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_ON_ANONYMOUS_CLASS, decorator.node, `@Injectable on anonymous class`);
}
const name = clazz.name.text;
const type = new WrappedNodeExpr(clazz.name);
const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;

View File

@ -25,7 +25,7 @@ import {valueReferenceToExpression} from './util';
export function generateSetClassMetadataCall(
clazz: ts.Declaration, reflection: ReflectionHost, defaultImportRecorder: DefaultImportRecorder,
isCore: boolean): Statement|null {
if (!reflection.isClass(clazz) || clazz.name === undefined || !ts.isIdentifier(clazz.name)) {
if (!reflection.isClass(clazz)) {
return null;
}
const id = ts.updateIdentifier(clazz.name);

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports';
import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
import {Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
import {NgModuleRouteAnalyzer} from '../../routing';
import {LocalModuleScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../transform';
@ -26,7 +26,7 @@ export interface NgModuleAnalysis {
ngModuleDef: R3NgModuleMetadata;
ngInjectorDef: R3InjectorMetadata;
metadataStmt: Statement|null;
declarations: Reference<ts.Declaration>[];
declarations: Reference<ClassDeclaration>[];
}
/**
@ -44,7 +44,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
readonly precedence = HandlerPrecedence.PRIMARY;
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
return undefined;
}
@ -59,8 +59,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
}
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<NgModuleAnalysis> {
const name = node.name !.text;
analyze(node: ClassDeclaration, decorator: Decorator): AnalysisOutput<NgModuleAnalysis> {
const name = node.name.text;
if (decorator.args === null || decorator.args.length > 1) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_ARITY_WRONG, decorator.node,
@ -90,20 +90,20 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
]);
// Extract the module declarations, imports, and exports.
let declarationRefs: Reference<ts.Declaration>[] = [];
let declarationRefs: Reference<ClassDeclaration>[] = [];
if (ngModule.has('declarations')) {
const expr = ngModule.get('declarations') !;
const declarationMeta = this.evaluator.evaluate(expr, forwardRefResolver);
declarationRefs = this.resolveTypeList(expr, declarationMeta, name, 'declarations');
}
let importRefs: Reference<ts.Declaration>[] = [];
let importRefs: Reference<ClassDeclaration>[] = [];
let rawImports: ts.Expression|null = null;
if (ngModule.has('imports')) {
rawImports = ngModule.get('imports') !;
const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
}
let exportRefs: Reference<ts.Declaration>[] = [];
let exportRefs: Reference<ClassDeclaration>[] = [];
let rawExports: ts.Expression|null = null;
if (ngModule.has('exports')) {
rawExports = ngModule.get('exports') !;
@ -111,7 +111,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
this.referencesRegistry.add(node, ...exportRefs);
}
let bootstrapRefs: Reference<ts.Declaration>[] = [];
let bootstrapRefs: Reference<ClassDeclaration>[] = [];
if (ngModule.has('bootstrap')) {
const expr = ngModule.get('bootstrap') !;
const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
@ -146,7 +146,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
exports.some(isForwardReference);
const ngModuleDef: R3NgModuleMetadata = {
type: new WrappedNodeExpr(node.name !),
type: new WrappedNodeExpr(node.name),
bootstrap,
declarations,
exports,
@ -176,7 +176,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
const ngInjectorDef: R3InjectorMetadata = {
name,
type: new WrappedNodeExpr(node.name !),
type: new WrappedNodeExpr(node.name),
deps: getValidConstructorDependencies(
node, this.reflector, this.defaultImportRecorder, this.isCore),
providers,
@ -191,11 +191,11 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
metadataStmt: generateSetClassMetadataCall(
node, this.reflector, this.defaultImportRecorder, this.isCore),
},
factorySymbolName: node.name !== undefined ? node.name.text : undefined,
factorySymbolName: node.name.text,
};
}
resolve(node: ts.Declaration, analysis: NgModuleAnalysis): ResolveResult {
resolve(node: ClassDeclaration, analysis: NgModuleAnalysis): ResolveResult {
const scope = this.scopeRegistry.getScopeOfModule(node);
const diagnostics = this.scopeRegistry.getDiagnosticsOfModule(node) || undefined;
if (scope === null || scope.reexports === null) {
@ -208,7 +208,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
}
}
compile(node: ts.ClassDeclaration, analysis: NgModuleAnalysis): CompileResult[] {
compile(node: ClassDeclaration, analysis: NgModuleAnalysis): CompileResult[] {
const ngInjectorDef = compileInjector(analysis.ngInjectorDef);
const ngModuleDef = compileNgModule(analysis.ngModuleDef);
const ngModuleStatements = ngModuleDef.additionalStatements;
@ -218,8 +218,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
const context = getSourceFile(node);
for (const decl of analysis.declarations) {
if (this.scopeRegistry.getRequiresRemoteScope(decl.node)) {
const scope =
this.scopeRegistry.getScopeOfModule(ts.getOriginalNode(node) as ts.Declaration);
const scope = this.scopeRegistry.getScopeOfModule(ts.getOriginalNode(node) as typeof node);
if (scope === null) {
continue;
}
@ -340,13 +339,19 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
return null;
}
// Verify that a `ts.Declaration` reference is a `ClassDeclaration` reference.
private isClassDeclarationReference(ref: Reference<ts.Declaration>):
ref is Reference<ClassDeclaration> {
return this.reflector.isClass(ref.node);
}
/**
* Compute a list of `Reference`s from a resolved metadata value.
*/
private resolveTypeList(
expr: ts.Node, resolvedList: ResolvedValue, className: string,
arrayName: string): Reference<ts.Declaration>[] {
const refList: Reference<ts.Declaration>[] = [];
arrayName: string): Reference<ClassDeclaration>[] {
const refList: Reference<ClassDeclaration>[] = [];
if (!Array.isArray(resolvedList)) {
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, expr,
@ -364,7 +369,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
// Recurse into nested arrays.
refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
} else if (isDeclarationReference(entry)) {
if (!this.reflector.isClass(entry.node)) {
if (!this.isClassDeclarationReference(entry)) {
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, entry.node,
`Value at position ${idx} in the NgModule.${arrayName}s of ${className} is not a class`);

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {LiteralExpr, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler';
import {R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler';
import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, Reference} from '../../imports';
import {PartialEvaluator} from '../../partial_evaluator';
import {Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {LocalModuleScopeRegistry} from '../../scope/src/local';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
@ -32,7 +32,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<PipeHandlerData, D
readonly precedence = HandlerPrecedence.PRIMARY;
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
return undefined;
}
@ -47,11 +47,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<PipeHandlerData, D
}
}
analyze(clazz: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<PipeHandlerData> {
if (clazz.name === undefined) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_ON_ANONYMOUS_CLASS, clazz, `@Pipes must have names`);
}
analyze(clazz: ClassDeclaration, decorator: Decorator): AnalysisOutput<PipeHandlerData> {
const name = clazz.name.text;
const type = new WrappedNodeExpr(clazz.name);
if (decorator.args === null) {
@ -109,7 +105,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<PipeHandlerData, D
};
}
compile(node: ts.ClassDeclaration, analysis: PipeHandlerData): CompileResult {
compile(node: ClassDeclaration, analysis: PipeHandlerData): CompileResult {
const res = compilePipeFromMetadata(analysis.meta);
const statements = res.statements;
if (analysis.metadataStmt !== null) {

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, ImportMode, Reference, ReferenceEmitter} from '../../imports';
import {ForeignFunctionResolver} from '../../partial_evaluator';
import {ClassMemberKind, CtorParameter, Decorator, Import, ReflectionHost, TypeValueReference} from '../../reflection';
import {ClassDeclaration, CtorParameter, Decorator, Import, ReflectionHost, TypeValueReference} from '../../reflection';
export enum ConstructorDepErrorKind {
NO_SUITABLE_TOKEN,
@ -33,7 +33,7 @@ export interface ConstructorDepError {
}
export function getConstructorDependencies(
clazz: ts.ClassDeclaration, reflector: ReflectionHost,
clazz: ClassDeclaration, reflector: ReflectionHost,
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): ConstructorDeps|null {
const deps: R3DependencyMetadata[] = [];
const errors: ConstructorDepError[] = [];
@ -128,14 +128,14 @@ export function valueReferenceToExpression(
}
export function getValidConstructorDependencies(
clazz: ts.ClassDeclaration, reflector: ReflectionHost,
clazz: ClassDeclaration, reflector: ReflectionHost,
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): R3DependencyMetadata[]|null {
return validateConstructorDependencies(
clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
}
export function validateConstructorDependencies(
clazz: ts.ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null {
clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null {
if (deps === null) {
return null;
} else if (deps.deps !== null) {

View File

@ -21,7 +21,6 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/scope",
"//packages/compiler-cli/src/ngtsc/testing",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//typescript",
],
)

View File

@ -12,7 +12,7 @@ import {CycleAnalyzer, ImportGraph} from '../../cycles';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost} from '../../reflection';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {ResourceLoader} from '../src/api';
@ -56,7 +56,7 @@ describe('ComponentDecoratorHandler', () => {
const handler = new ComponentDecoratorHandler(
reflectionHost, evaluator, scopeRegistry, false, new NoopResourceLoader(), [''], false,
true, moduleResolver, cycleAnalyzer, refEmitter, NOOP_DEFAULT_IMPORT_RECORDER);
const TestCmp = getDeclaration(program, 'entry.ts', 'TestCmp', ts.isClassDeclaration);
const TestCmp = getDeclaration(program, 'entry.ts', 'TestCmp', isNamedClassDeclaration);
const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp));
if (detected === undefined) {
return fail('Failed to recognize @Component');

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost} from '../../reflection';
import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {DirectiveDecoratorHandler} from '../src/directive';
@ -47,7 +47,7 @@ describe('DirectiveDecoratorHandler', () => {
reflectionHost, evaluator, scopeRegistry, NOOP_DEFAULT_IMPORT_RECORDER, false);
const analyzeDirective = (dirName: string) => {
const DirNode = getDeclaration(program, 'entry.ts', dirName, ts.isClassDeclaration);
const DirNode = getDeclaration(program, 'entry.ts', dirName, isNamedClassDeclaration);
const detected = handler.detect(DirNode, reflectionHost.getDecoratorsOfDeclaration(DirNode));
if (detected === undefined) {
@ -78,5 +78,5 @@ describe('DirectiveDecoratorHandler', () => {
class TestReflectionHost extends TypeScriptReflectionHost {
hasBaseClassReturnValue = false;
hasBaseClass(node: ts.Declaration): boolean { return this.hasBaseClassReturnValue; }
hasBaseClass(clazz: ClassDeclaration): boolean { return this.hasBaseClassReturnValue; }
}

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost} from '../../reflection';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {NgModuleDecoratorHandler} from '../src/ng_module';
@ -63,7 +63,7 @@ describe('NgModuleDecoratorHandler', () => {
const handler = new NgModuleDecoratorHandler(
reflectionHost, evaluator, scopeRegistry, referencesRegistry, false, null, refEmitter,
NOOP_DEFAULT_IMPORT_RECORDER);
const TestModule = getDeclaration(program, 'entry.ts', 'TestModule', ts.isClassDeclaration);
const TestModule = getDeclaration(program, 'entry.ts', 'TestModule', isNamedClassDeclaration);
const detected =
handler.detect(TestModule, reflectionHost.getDecoratorsOfDeclaration(TestModule));
if (detected === undefined) {

View File

@ -11,6 +11,7 @@ ts_library(
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",
"@npm//typescript",

View File

@ -9,6 +9,7 @@
import {Expression, ExternalExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {ClassDeclaration} from '../../reflection';
import {FileToModuleHost, ReferenceEmitStrategy} from './emitter';
import {ImportMode, Reference} from './references';
@ -18,20 +19,16 @@ const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g;
export class AliasGenerator {
constructor(private fileToModuleHost: FileToModuleHost) {}
aliasSymbolName(decl: ts.Declaration, context: ts.SourceFile): string {
if (!ts.isClassDeclaration(decl)) {
throw new Error(`Attempt to write an alias to something which isn't a class`);
}
aliasSymbolName(decl: ClassDeclaration, context: ts.SourceFile): string {
// The declared module is used to get the name of the alias.
const declModule =
this.fileToModuleHost.fileNameToModuleName(decl.getSourceFile().fileName, context.fileName);
const replaced = declModule.replace(CHARS_TO_ESCAPE, '_').replace(/\//g, '$');
return 'ɵng$' + replaced + '$$' + decl.name !.text;
return 'ɵng$' + replaced + '$$' + decl.name.text;
}
aliasTo(decl: ts.Declaration, via: ts.SourceFile): Expression {
aliasTo(decl: ClassDeclaration, via: ts.SourceFile): Expression {
const name = this.aliasSymbolName(decl, via);
// viaModule is the module it'll actually be imported from.
const moduleName = this.fileToModuleHost.fileNameToModuleName(via.fileName, via.fileName);

View File

@ -267,7 +267,7 @@ export class NgtscProgram implements api.Program {
const writeFile: ts.WriteFileCallback =
(fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void) | undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>) => {
sourceFiles: ReadonlyArray<ts.SourceFile>| undefined) => {
if (this.closureCompilerEnabled && fileName.endsWith('.js')) {
data = nocollapseHack(data);
}

View File

@ -8,4 +8,5 @@
export * from './src/host';
export {typeNodeToValueExpr} from './src/type_to_value';
export {TypeScriptReflectionHost, filterToMembersWithDecorator, reflectIdentifierOfDeclaration, reflectNameOfDeclaration, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/typescript';
export {TypeScriptReflectionHost, filterToMembersWithDecorator, reflectIdentifierOfDeclaration, reflectNameOfDeclaration, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/typescript';
export {isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from './src/util';

View File

@ -42,6 +42,29 @@ export interface Decorator {
args: ts.Expression[]|null;
}
/**
* The `ts.Declaration` of a "class".
*
* Classes are represented differently in different code formats:
* - In TS code, they are typically defined using the `class` keyword.
* - In ES2015 code, they are usually defined using the `class` keyword, but they can also be
* variable declarations, which are initialized to a class expression (e.g.
* `let Foo = Foo1 = class Foo {}`).
* - In ES5 code, they are typically defined as variable declarations being assigned the return
* value of an IIFE. The actual "class" is implemented as a constructor function inside the IIFE,
* but the outer variable declaration represents the "class" to the rest of the program.
*
* For `ReflectionHost` purposes, a class declaration should always have a `name` identifier,
* because we need to be able to reference it in other parts of the program.
*/
export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T & {name: ts.Identifier};
/**
* The symbol corresponding to a "class" declaration. I.e. a `ts.Symbol` whose `valueDeclaration` is
* a `ClassDeclaration`.
*/
export type ClassSymbol = ts.Symbol & {valueDeclaration: ClassDeclaration};
/**
* An enumeration of possible kinds of class members.
*/
@ -339,16 +362,13 @@ export interface ReflectionHost {
* 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[];
getMembersOfClass(clazz: ClassDeclaration): ClassMember[];
/**
* Reflect over the constructor of a class and return metadata about its parameters.
@ -356,16 +376,13 @@ export interface ReflectionHost {
* 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.
* If the class has no constructor, this method returns `null`.
*/
getConstructorParameters(declaration: ts.Declaration): CtorParameter[]|null;
getConstructorParameters(clazz: ClassDeclaration): CtorParameter[]|null;
/**
* Reflect over a function and return metadata about its parameters and body.
@ -452,20 +469,24 @@ export interface ReflectionHost {
/**
* Check whether the given node actually represents a class.
*/
isClass(node: ts.Node): node is ts.NamedDeclaration;
isClass(node: ts.Node): node is ClassDeclaration;
/**
* Determines whether the given declaration has a base class.
* Determines whether the given declaration, which should be a class, has a base class.
*
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
*/
hasBaseClass(node: ts.Declaration): boolean;
hasBaseClass(clazz: ClassDeclaration): boolean;
/**
* 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;
/**
* Find the assigned value of a variable declaration.

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost, TypeValueReference} from './host';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost} from './host';
import {typeToValue} from './type_to_value';
/**
@ -26,17 +26,17 @@ export class TypeScriptReflectionHost implements ReflectionHost {
.filter((dec): dec is Decorator => dec !== null);
}
getMembersOfClass(declaration: ts.Declaration): ClassMember[] {
const clazz = castDeclarationToClassOrDie(declaration);
return clazz.members.map(member => this._reflectMember(member))
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
const tsClazz = castDeclarationToClassOrDie(clazz);
return tsClazz.members.map(member => this._reflectMember(member))
.filter((member): member is ClassMember => member !== null);
}
getConstructorParameters(declaration: ts.Declaration): CtorParameter[]|null {
const clazz = castDeclarationToClassOrDie(declaration);
getConstructorParameters(clazz: ClassDeclaration): CtorParameter[]|null {
const tsClazz = castDeclarationToClassOrDie(clazz);
// First, find the constructor.
const ctor = clazz.members.find(ts.isConstructorDeclaration);
const ctor = tsClazz.members.find(ts.isConstructorDeclaration);
if (ctor === undefined) {
return null;
}
@ -133,14 +133,15 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return map;
}
isClass(node: ts.Node): node is ts.NamedDeclaration {
isClass(node: ts.Node): node is ClassDeclaration {
// In TypeScript code, classes are ts.ClassDeclarations.
return ts.isClassDeclaration(node);
// (`name` can be undefined in unnamed default exports: `default export class { ... }`)
return ts.isClassDeclaration(node) && (node.name !== undefined) && ts.isIdentifier(node.name);
}
hasBaseClass(node: ts.Declaration): boolean {
return ts.isClassDeclaration(node) && node.heritageClauses !== undefined &&
node.heritageClauses.some(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
hasBaseClass(clazz: ClassDeclaration): boolean {
return ts.isClassDeclaration(clazz) && clazz.heritageClauses !== undefined &&
clazz.heritageClauses.some(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
}
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
@ -165,7 +166,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
};
}
getGenericArityOfClass(clazz: ts.Declaration): number|null {
getGenericArityOfClass(clazz: ClassDeclaration): number|null {
if (!ts.isClassDeclaration(clazz)) {
return null;
}
@ -418,7 +419,8 @@ export function reflectObjectLiteral(node: ts.ObjectLiteralExpression): Map<stri
return map;
}
function castDeclarationToClassOrDie(declaration: ts.Declaration): ts.ClassDeclaration {
function castDeclarationToClassOrDie(declaration: ClassDeclaration):
ClassDeclaration<ts.ClassDeclaration> {
if (!ts.isClassDeclaration(declaration)) {
throw new Error(
`Reflecting on a ${ts.SyntaxKind[declaration.kind]} instead of a ClassDeclaration.`);

View File

@ -0,0 +1,27 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {ClassDeclaration} from './host';
export function isNamedClassDeclaration(node: ts.Node):
node is ClassDeclaration<ts.ClassDeclaration>&{name: ts.Identifier} {
return ts.isClassDeclaration(node) && (node.name !== undefined);
}
export function isNamedFunctionDeclaration(node: ts.Node):
node is ClassDeclaration<ts.FunctionDeclaration>&{name: ts.Identifier} {
return ts.isFunctionDeclaration(node) && (node.name !== undefined);
}
export function isNamedVariableDeclaration(node: ts.Node):
node is ClassDeclaration<ts.VariableDeclaration>&{name: ts.Identifier} {
return ts.isVariableDeclaration(node) && (node.name !== undefined);
}

View File

@ -11,6 +11,7 @@ import * as ts from 'typescript';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {CtorParameter} from '../src/host';
import {TypeScriptReflectionHost} from '../src/typescript';
import {isNamedClassDeclaration} from '../src/util';
describe('reflector', () => {
describe('ctor params', () => {
@ -25,7 +26,7 @@ describe('reflector', () => {
}
`
}]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -54,7 +55,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -83,7 +84,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -111,7 +112,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -139,7 +140,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -166,7 +167,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -198,7 +199,7 @@ describe('reflector', () => {
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', isNamedClassDeclaration);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const args = host.getConstructorParameters(clazz) !;
@ -292,4 +293,4 @@ function argExpressionToString(name: ts.Node | null): string {
} else {
throw new Error(`Unexpected node in arg expression: ${ts.SyntaxKind[name.kind]}.`);
}
}
}

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {Reference} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {TypeCheckableDirectiveMeta} from '../../typecheck';
/**
@ -51,6 +50,6 @@ export interface ScopeDirective extends TypeCheckableDirectiveMeta {
* Metadata for a given pipe within an NgModule's scope.
*/
export interface ScopePipe {
ref: Reference<ts.Declaration>;
ref: Reference<ClassDeclaration>;
name: string;
}

View File

@ -9,13 +9,13 @@
import * as ts from 'typescript';
import {AliasGenerator, Reference} from '../../imports';
import {ReflectionHost} from '../../reflection';
import {ClassDeclaration, ReflectionHost} from '../../reflection';
import {ExportScope, ScopeDirective, ScopePipe} from './api';
import {extractDirectiveGuards, extractReferencesFromType, readStringArrayType, readStringMapType, readStringType} from './util';
export interface DtsModuleScopeResolver {
resolve(ref: Reference<ts.ClassDeclaration>): ExportScope|null;
resolve(ref: Reference<ClassDeclaration>): ExportScope|null;
}
/**
@ -25,11 +25,11 @@ export interface DtsModuleScopeResolver {
* of all of the directives/pipes it exports. It does this by reading metadata off of Ivy static
* fields on directives, components, pipes, and NgModules.
*/
export class MetadataDtsModuleScopeResolver {
export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver {
/**
* Cache which holds fully resolved scopes for NgModule classes from .d.ts files.
*/
private cache = new Map<ts.ClassDeclaration, ExportScope|null>();
private cache = new Map<ClassDeclaration, ExportScope|null>();
constructor(
private checker: ts.TypeChecker, private reflector: ReflectionHost,
@ -42,7 +42,7 @@ export class MetadataDtsModuleScopeResolver {
* This operation relies on a `Reference` instead of a direct TypeScrpt node as the `Reference`s
* produced depend on how the original NgModule was imported.
*/
resolve(ref: Reference<ts.ClassDeclaration>): ExportScope|null {
resolve(ref: Reference<ClassDeclaration>): ExportScope|null {
const clazz = ref.node;
const sourceFile = clazz.getSourceFile();
if (!sourceFile.isDeclarationFile) {
@ -64,7 +64,7 @@ export class MetadataDtsModuleScopeResolver {
return null;
}
const declarations = new Set<ts.Declaration>();
const declarations = new Set<ClassDeclaration>();
for (const declRef of meta.declarations) {
declarations.add(declRef.node);
}
@ -139,7 +139,8 @@ export class MetadataDtsModuleScopeResolver {
*
* @param ref `Reference` to the class of interest, with the context of how it was obtained.
*/
private readModuleMetadataFromClass(ref: Reference<ts.Declaration>): RawDependencyMetadata|null {
private readModuleMetadataFromClass(ref: Reference<ClassDeclaration>): RawDependencyMetadata
|null {
const clazz = ref.node;
const resolutionContext = clazz.getSourceFile().fileName;
// This operation is explicitly not memoized, as it depends on `ref.ownedByModuleGuess`.
@ -171,7 +172,7 @@ export class MetadataDtsModuleScopeResolver {
/**
* Read directive (or component) metadata from a referenced class in a .d.ts file.
*/
private readScopeDirectiveFromClassWithDef(ref: Reference<ts.ClassDeclaration>): ScopeDirective
private readScopeDirectiveFromClassWithDef(ref: Reference<ClassDeclaration>): ScopeDirective
|null {
const clazz = ref.node;
const def = this.reflector.getMembersOfClass(clazz).find(
@ -193,7 +194,7 @@ export class MetadataDtsModuleScopeResolver {
return {
ref,
name: clazz.name !.text,
name: clazz.name.text,
isComponent: def.name === 'ngComponentDef', selector,
exportAs: readStringArrayType(def.type.typeArguments[2]),
inputs: readStringMapType(def.type.typeArguments[3]),
@ -206,7 +207,7 @@ export class MetadataDtsModuleScopeResolver {
/**
* Read pipe metadata from a referenced class in a .d.ts file.
*/
private readScopePipeFromClassWithDef(ref: Reference<ts.ClassDeclaration>): ScopePipe|null {
private readScopePipeFromClassWithDef(ref: Reference<ClassDeclaration>): ScopePipe|null {
const def = this.reflector.getMembersOfClass(ref.node).find(
field => field.isStatic && field.name === 'ngPipeDef');
if (def === undefined) {
@ -248,7 +249,7 @@ export class MetadataDtsModuleScopeResolver {
* Raw metadata read from the .d.ts info of an ngModuleDef field on a compiled NgModule class.
*/
interface RawDependencyMetadata {
declarations: Reference<ts.ClassDeclaration>[];
imports: Reference<ts.ClassDeclaration>[];
exports: Reference<ts.ClassDeclaration>[];
declarations: Reference<ClassDeclaration>[];
imports: Reference<ClassDeclaration>[];
exports: Reference<ClassDeclaration>[];
}

View File

@ -11,15 +11,16 @@ import * as ts from 'typescript';
import {ErrorCode, makeDiagnostic} from '../../diagnostics';
import {AliasGenerator, Reexport, Reference, ReferenceEmitter} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {identifierOfNode, nodeNameForError} from '../../util/src/typescript';
import {ExportScope, ScopeData, ScopeDirective, ScopePipe} from './api';
import {DtsModuleScopeResolver} from './dependency';
export interface LocalNgModuleData {
declarations: Reference<ts.Declaration>[];
imports: Reference<ts.Declaration>[];
exports: Reference<ts.Declaration>[];
declarations: Reference<ClassDeclaration>[];
imports: Reference<ClassDeclaration>[];
exports: Reference<ClassDeclaration>[];
}
export interface LocalModuleScope extends ExportScope {
@ -57,17 +58,17 @@ export class LocalModuleScopeRegistry {
/**
* Metadata for each local NgModule registered.
*/
private ngModuleData = new Map<ts.Declaration, LocalNgModuleData>();
private ngModuleData = new Map<ClassDeclaration, LocalNgModuleData>();
/**
* Metadata for each local directive registered.
*/
private directiveData = new Map<ts.Declaration, ScopeDirective>();
private directiveData = new Map<ClassDeclaration, ScopeDirective>();
/**
* Metadata for each local pipe registered.
*/
private pipeData = new Map<ts.Declaration, ScopePipe>();
private pipeData = new Map<ClassDeclaration, ScopePipe>();
/**
* A map of components from the current compilation unit to the NgModule which declared them.
@ -76,7 +77,7 @@ export class LocalModuleScopeRegistry {
* contain directives. This doesn't cause any problems but isn't useful as there is no concept of
* a directive's compilation scope.
*/
private declarationToModule = new Map<ts.Declaration, ts.Declaration>();
private declarationToModule = new Map<ClassDeclaration, ClassDeclaration>();
/**
* A cache of calculated `LocalModuleScope`s for each NgModule declared in the current program.
@ -84,7 +85,7 @@ export class LocalModuleScopeRegistry {
* A value of `undefined` indicates the scope was invalid and produced errors (therefore,
* diagnostics should exist in the `scopeErrors` map).
*/
private cache = new Map<ts.Declaration, LocalModuleScope|undefined>();
private cache = new Map<ClassDeclaration, LocalModuleScope|undefined>();
/**
* Tracks whether a given component requires "remote scoping".
@ -94,12 +95,12 @@ export class LocalModuleScopeRegistry {
* around cyclic import issues). This is not used in calculation of `LocalModuleScope`s, but is
* tracked here for convenience.
*/
private remoteScoping = new Set<ts.Declaration>();
private remoteScoping = new Set<ClassDeclaration>();
/**
* Tracks errors accumulated in the processing of scopes for each module declaration.
*/
private scopeErrors = new Map<ts.Declaration, ts.Diagnostic[]>();
private scopeErrors = new Map<ClassDeclaration, ts.Diagnostic[]>();
constructor(
private dependencyScopeReader: DtsModuleScopeResolver, private refEmitter: ReferenceEmitter,
@ -108,7 +109,7 @@ export class LocalModuleScopeRegistry {
/**
* Add an NgModule's data to the registry.
*/
registerNgModule(clazz: ts.Declaration, data: LocalNgModuleData): void {
registerNgModule(clazz: ClassDeclaration, data: LocalNgModuleData): void {
this.assertCollecting();
this.ngModuleData.set(clazz, data);
for (const decl of data.declarations) {
@ -126,7 +127,7 @@ export class LocalModuleScopeRegistry {
this.pipeData.set(pipe.ref.node, pipe);
}
getScopeForComponent(clazz: ts.ClassDeclaration): LocalModuleScope|null {
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null {
if (!this.declarationToModule.has(clazz)) {
return null;
}
@ -141,7 +142,7 @@ export class LocalModuleScopeRegistry {
* `LocalModuleScope` for the given NgModule if one can be produced, and `null` if no scope is
* available or the scope contains errors.
*/
getScopeOfModule(clazz: ts.Declaration): LocalModuleScope|null {
getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope|null {
const scope = this.getScopeOfModuleInternal(clazz);
// Translate undefined -> null.
return scope !== undefined ? scope : null;
@ -151,7 +152,7 @@ export class LocalModuleScopeRegistry {
* Retrieves any `ts.Diagnostic`s produced during the calculation of the `LocalModuleScope` for
* the given NgModule, or `null` if no errors were present.
*/
getDiagnosticsOfModule(clazz: ts.Declaration): ts.Diagnostic[]|null {
getDiagnosticsOfModule(clazz: ClassDeclaration): ts.Diagnostic[]|null {
// Required to ensure the errors are populated for the given class. If it has been processed
// before, this will be a no-op due to the scope cache.
this.getScopeOfModule(clazz);
@ -167,7 +168,7 @@ export class LocalModuleScopeRegistry {
* Implementation of `getScopeOfModule` which differentiates between no scope being available
* (returns `null`) and a scope being produced with errors (returns `undefined`).
*/
private getScopeOfModuleInternal(clazz: ts.Declaration): LocalModuleScope|null|undefined {
private getScopeOfModuleInternal(clazz: ClassDeclaration): LocalModuleScope|null|undefined {
// Seal the registry to protect the integrity of the `LocalModuleScope` cache.
this.sealed = true;
@ -218,8 +219,7 @@ export class LocalModuleScopeRegistry {
for (const decl of ngModule.declarations) {
if (this.directiveData.has(decl.node)) {
const directive = this.directiveData.get(decl.node) !;
compilationDirectives.set(
decl.node, {...directive, ref: decl as Reference<ts.ClassDeclaration>});
compilationDirectives.set(decl.node, {...directive, ref: decl});
} else if (this.pipeData.has(decl.node)) {
const pipe = this.pipeData.get(decl.node) !;
compilationPipes.set(decl.node, {...pipe, ref: decl});
@ -303,7 +303,7 @@ export class LocalModuleScopeRegistry {
let reexports: Reexport[]|null = null;
if (this.aliasGenerator !== null) {
reexports = [];
const addReexport = (ref: Reference<ts.Declaration>) => {
const addReexport = (ref: Reference<ClassDeclaration>) => {
if (!declared.has(ref.node) && ref.node.getSourceFile() !== sourceFile) {
const exportName = this.aliasGenerator !.aliasSymbolName(ref.node, sourceFile);
if (ref.alias && ref.alias instanceof ExternalExpr) {
@ -362,12 +362,14 @@ export class LocalModuleScopeRegistry {
/**
* Check whether a component requires remote scoping.
*/
getRequiresRemoteScope(node: ts.Declaration): boolean { return this.remoteScoping.has(node); }
getRequiresRemoteScope(node: ClassDeclaration): boolean { return this.remoteScoping.has(node); }
/**
* Set a component as requiring remote scoping.
*/
setComponentAsRequiringRemoteScoping(node: ts.Declaration): void { this.remoteScoping.add(node); }
setComponentAsRequiringRemoteScoping(node: ClassDeclaration): void {
this.remoteScoping.add(node);
}
/**
* Look up the `ExportScope` of a given `Reference` to an NgModule.
@ -380,8 +382,8 @@ export class LocalModuleScopeRegistry {
* array parameter.
*/
private getExportedScope(
ref: Reference<ts.Declaration>, diagnostics: ts.Diagnostic[], ownerForErrors: ts.Declaration,
type: 'import'|'export'): ExportScope|null|undefined {
ref: Reference<ClassDeclaration>, diagnostics: ts.Diagnostic[],
ownerForErrors: ts.Declaration, type: 'import'|'export'): ExportScope|null|undefined {
if (ref.node.getSourceFile().isDeclarationFile) {
// The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`.
if (!ts.isClassDeclaration(ref.node)) {
@ -394,7 +396,7 @@ export class LocalModuleScopeRegistry {
`Appears in the NgModule.${type}s of ${nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`));
return undefined;
}
return this.dependencyScopeReader.resolve(ref as Reference<ts.ClassDeclaration>);
return this.dependencyScopeReader.resolve(ref);
} else {
// The NgModule is declared locally in the current program. Resolve it from the registry.
return this.getScopeOfModuleInternal(ref.node);

View File

@ -9,12 +9,12 @@
import * as ts from 'typescript';
import {Reference} from '../../imports';
import {ClassMemberKind, ReflectionHost, reflectTypeEntityToDeclaration} from '../../reflection';
import {ClassDeclaration, ClassMemberKind, ReflectionHost, isNamedClassDeclaration, reflectTypeEntityToDeclaration} from '../../reflection';
import {nodeDebugInfo} from '../../util/src/typescript';
export function extractReferencesFromType(
checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string | null,
resolutionContext: string): Reference<ts.ClassDeclaration>[] {
resolutionContext: string): Reference<ClassDeclaration>[] {
if (!ts.isTupleTypeNode(def)) {
return [];
}
@ -24,8 +24,8 @@ export function extractReferencesFromType(
}
const type = element.exprName;
const {node, from} = reflectTypeEntityToDeclaration(type, checker);
if (!ts.isClassDeclaration(node)) {
throw new Error(`Expected ClassDeclaration: ${nodeDebugInfo(node)}`);
if (!isNamedClassDeclaration(node)) {
throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
}
const specifier = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
if (specifier !== null) {
@ -77,7 +77,7 @@ export function readStringArrayType(type: ts.TypeNode): string[] {
}
export function extractDirectiveGuards(node: ts.Declaration, reflector: ReflectionHost): {
export function extractDirectiveGuards(node: ClassDeclaration, reflector: ReflectionHost): {
ngTemplateGuards: string[],
hasNgTemplateContextGuard: boolean,
} {
@ -88,7 +88,7 @@ export function extractDirectiveGuards(node: ts.Declaration, reflector: Reflecti
return {hasNgTemplateContextGuard, ngTemplateGuards};
}
function nodeStaticMethodNames(node: ts.Declaration, reflector: ReflectionHost): string[] {
function nodeStaticMethodNames(node: ClassDeclaration, reflector: ReflectionHost): string[] {
return reflector.getMembersOfClass(node)
.filter(member => member.kind === ClassMemberKind.Method && member.isStatic)
.map(member => member.name);

View File

@ -10,7 +10,7 @@ import {ExternalExpr, ExternalReference} from '@angular/compiler';
import * as ts from 'typescript';
import {AliasGenerator, FileToModuleHost, Reference} from '../../imports';
import {TypeScriptReflectionHost} from '../../reflection';
import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {makeProgram} from '../../testing/in_memory_typescript';
import {ExportScope} from '../src/api';
import {MetadataDtsModuleScopeResolver} from '../src/dependency';
@ -42,7 +42,7 @@ export declare type PipeMeta<A, B> = never;
*/
function makeTestEnv(
modules: {[module: string]: string}, aliasGenerator: AliasGenerator | null = null): {
refs: {[name: string]: Reference<ts.ClassDeclaration>},
refs: {[name: string]: Reference<ClassDeclaration>},
resolver: MetadataDtsModuleScopeResolver,
} {
// Map the modules object to an array of files for `makeProgram`.
@ -123,7 +123,7 @@ describe('MetadataDtsModuleScopeResolver', () => {
export declare class Dir {
static ngDirectiveDef: DirectiveMeta<Dir, '[dir]', never, never, never, never>;
}
export declare class ModuleA {
static ngModuleDef: ModuleMeta<ModuleA, [typeof Dir], never, [typeof Dir]>;
}
@ -270,13 +270,13 @@ describe('MetadataDtsModuleScopeResolver', () => {
});
});
function scopeToRefs(scope: ExportScope): Reference<ts.ClassDeclaration>[] {
function scopeToRefs(scope: ExportScope): Reference<ClassDeclaration>[] {
const directives = scope.exported.directives.map(dir => dir.ref);
const pipes = scope.exported.pipes.map(pipe => pipe.ref as Reference<ts.ClassDeclaration>);
const pipes = scope.exported.pipes.map(pipe => pipe.ref);
return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !));
}
function getAlias(ref: Reference<ts.ClassDeclaration>): ExternalReference|null {
function getAlias(ref: Reference<ClassDeclaration>): ExternalReference|null {
if (ref.alias === null) {
return null;
} else {

View File

@ -9,16 +9,17 @@
import * as ts from 'typescript';
import {Reference, ReferenceEmitter} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {ScopeData, ScopeDirective, ScopePipe} from '../src/api';
import {DtsModuleScopeResolver} from '../src/dependency';
import {LocalModuleScopeRegistry} from '../src/local';
function registerFakeRefs(registry: LocalModuleScopeRegistry):
{[name: string]: Reference<ts.ClassDeclaration>} {
const get = (target: {}, name: string): Reference<ts.ClassDeclaration> => {
{[name: string]: Reference<ClassDeclaration>} {
const get = (target: {}, name: string): Reference<ClassDeclaration> => {
const sf = ts.createSourceFile(
name + '.ts', `export class ${name} {}`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
const clazz = sf.statements[0] as ts.ClassDeclaration;
const clazz = sf.statements[0] as unknown as ClassDeclaration;
const ref = new Reference(clazz);
if (name.startsWith('Dir') || name.startsWith('Cmp')) {
registry.registerDirective(fakeDirective(ref));
@ -136,7 +137,7 @@ describe('LocalModuleScopeRegistry', () => {
});
});
function fakeDirective(ref: Reference<ts.ClassDeclaration>): ScopeDirective {
function fakeDirective(ref: Reference<ClassDeclaration>): ScopeDirective {
const name = ref.debugName !;
return {
ref,
@ -152,16 +153,16 @@ function fakeDirective(ref: Reference<ts.ClassDeclaration>): ScopeDirective {
};
}
function fakePipe(ref: Reference<ts.ClassDeclaration>): ScopePipe {
function fakePipe(ref: Reference<ClassDeclaration>): ScopePipe {
const name = ref.debugName !;
return {ref, name};
}
class MockDtsModuleScopeResolver implements DtsModuleScopeResolver {
resolve(ref: Reference<ts.ClassDeclaration>): null { return null; }
resolve(ref: Reference<ClassDeclaration>): null { return null; }
}
function scopeToRefs(scopeData: ScopeData): Reference<ts.Declaration>[] {
function scopeToRefs(scopeData: ScopeData): Reference<ClassDeclaration>[] {
const directives = scopeData.directives.map(dir => dir.ref);
const pipes = scopeData.pipes.map(pipe => pipe.ref);
return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !));

View File

@ -81,7 +81,7 @@ export class GeneratedShimsHostWrapper implements ts.CompilerHost {
writeFile(
fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void)|undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>): void {
sourceFiles: ReadonlyArray<ts.SourceFile>|undefined): void {
return this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
}

View File

@ -10,7 +10,7 @@ import {ConstantPool, Expression, Statement, Type} from '@angular/compiler';
import * as ts from 'typescript';
import {Reexport} from '../../imports';
import {Decorator} from '../../reflection';
import {ClassDeclaration, Decorator} from '../../reflection';
import {TypeCheckContext} from '../../typecheck';
export enum HandlerPrecedence {
@ -58,7 +58,7 @@ export interface DecoratorHandler<A, M> {
* Scan a set of reflected decorators and determine if this handler is responsible for compilation
* of one of them.
*/
detect(node: ts.Declaration, decorators: Decorator[]|null): DetectResult<M>|undefined;
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<M>|undefined;
/**
@ -67,14 +67,14 @@ export interface DecoratorHandler<A, M> {
* `preAnalyze` is optional and is not guaranteed to be called through all compilation flows. It
* will only be called if asynchronicity is supported in the CompilerHost.
*/
preanalyze?(node: ts.Declaration, metadata: M): Promise<void>|undefined;
preanalyze?(node: ClassDeclaration, metadata: M): Promise<void>|undefined;
/**
* Perform analysis on the decorator/class combination, producing instructions for compilation
* if successful, or an array of diagnostic messages if the analysis fails or the decorator
* isn't valid.
*/
analyze(node: ts.Declaration, metadata: M): AnalysisOutput<A>;
analyze(node: ClassDeclaration, metadata: M): AnalysisOutput<A>;
/**
* Perform resolution on the given decorator along with the result of analysis.
@ -83,15 +83,15 @@ export interface DecoratorHandler<A, M> {
* `DecoratorHandler` a chance to leverage information from the whole compilation unit to enhance
* the `analysis` before the emit phase.
*/
resolve?(node: ts.Declaration, analysis: A): ResolveResult;
resolve?(node: ClassDeclaration, analysis: A): ResolveResult;
typeCheck?(ctx: TypeCheckContext, node: ts.Declaration, metadata: A): void;
typeCheck?(ctx: TypeCheckContext, node: ClassDeclaration, metadata: A): void;
/**
* Generate a description of the field which should be added to the class, including any
* initialization code to be generated.
*/
compile(node: ts.Declaration, analysis: A, constantPool: ConstantPool): CompileResult
compile(node: ClassDeclaration, analysis: A, constantPool: ConstantPool): CompileResult
|CompileResult[];
}

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, ExternalExpr} from '@angular/compiler';
import {ConstantPool} from '@angular/compiler';
import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {ImportRewriter} from '../../imports';
import {ReflectionHost, reflectNameOfDeclaration} from '../../reflection';
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration, reflectNameOfDeclaration} from '../../reflection';
import {TypeCheckContext} from '../../typecheck';
import {getSourceFile} from '../../util/src/typescript';
@ -48,7 +48,7 @@ export class IvyCompilation {
* Tracks classes which have been analyzed and found to have an Ivy decorator, and the
* information recorded about them for later compilation.
*/
private ivyClasses = new Map<ts.Declaration, IvyClass>();
private ivyClasses = new Map<ClassDeclaration, IvyClass>();
/**
* Tracks factory information which needs to be generated.
@ -84,7 +84,7 @@ export class IvyCompilation {
analyzeAsync(sf: ts.SourceFile): Promise<void>|undefined { return this.analyze(sf, true); }
private detectHandlersForClass(node: ts.Declaration): IvyClass|null {
private detectHandlersForClass(node: ClassDeclaration): IvyClass|null {
// The first step is to reflect the decorators.
const classDecorators = this.reflector.getDecoratorsOfDeclaration(node);
let ivyClass: IvyClass|null = null;
@ -169,7 +169,7 @@ export class IvyCompilation {
private analyze(sf: ts.SourceFile, preanalyze: boolean): Promise<void>|undefined {
const promises: Promise<void>[] = [];
const analyzeClass = (node: ts.Declaration): void => {
const analyzeClass = (node: ClassDeclaration): void => {
const ivyClass = this.detectHandlersForClass(node);
// If the class has no Ivy behavior (or had errors), skip it.
@ -227,7 +227,7 @@ export class IvyCompilation {
const visit = (node: ts.Node): void => {
// Process nodes recursively, and look for class declarations with decorators.
if (ts.isClassDeclaration(node)) {
if (isNamedClassDeclaration(node)) {
analyzeClass(node);
}
ts.forEachChild(node, visit);
@ -291,8 +291,8 @@ export class IvyCompilation {
*/
compileIvyFieldFor(node: ts.Declaration, constantPool: ConstantPool): CompileResult[]|undefined {
// Look to see whether the original node was analyzed. If not, there's nothing to do.
const original = ts.getOriginalNode(node) as ts.Declaration;
if (!this.ivyClasses.has(original)) {
const original = ts.getOriginalNode(node) as typeof node;
if (!isNamedClassDeclaration(original) || !this.ivyClasses.has(original)) {
return undefined;
}
@ -305,7 +305,8 @@ export class IvyCompilation {
continue;
}
const compileMatchRes = match.handler.compile(node, match.analyzed.analysis, constantPool);
const compileMatchRes =
match.handler.compile(node as ClassDeclaration, match.analyzed.analysis, constantPool);
if (!Array.isArray(compileMatchRes)) {
res.push(compileMatchRes);
} else {
@ -327,9 +328,9 @@ export class IvyCompilation {
* Lookup the `ts.Decorator` which triggered transformation of a particular class declaration.
*/
ivyDecoratorsFor(node: ts.Declaration): ts.Decorator[] {
const original = ts.getOriginalNode(node) as ts.Declaration;
const original = ts.getOriginalNode(node) as typeof node;
if (!this.ivyClasses.has(original)) {
if (!isNamedClassDeclaration(original) || !this.ivyClasses.has(original)) {
return EMPTY_ARRAY;
}
const ivyClass = this.ivyClasses.get(original) !;

View File

@ -67,12 +67,11 @@ export class NgTscPlugin implements TscPlugin {
const afterDeclarations: Array<ts.TransformerFactory<ts.SourceFile|ts.Bundle>> =
[(context: ts.TransformationContext) => (sf: ts.SourceFile | ts.Bundle) => {
const visitor = (node: ts.Node): ts.Node => {
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
const clz = node as ts.ClassDeclaration;
if (ts.isClassDeclaration(node)) {
// For demo purposes, transform the class name in the .d.ts output
return ts.updateClassDeclaration(
clz, clz.decorators, node.modifiers, ts.createIdentifier('NEWNAME'),
clz.typeParameters, clz.heritageClauses, clz.members);
node, node.decorators, node.modifiers, ts.createIdentifier('NEWNAME'),
node.typeParameters, node.heritageClauses, node.members);
}
return ts.visitEachChild(node, visitor, context);
};

View File

@ -9,6 +9,7 @@ ts_library(
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//typescript",

View File

@ -7,16 +7,16 @@
*/
import {BoundTarget, DirectiveMeta} from '@angular/compiler';
import * as ts from 'typescript';
import {Reference} from '../../imports';
import {ClassDeclaration} from '../../reflection';
/**
* Extension of `DirectiveMeta` that includes additional information required to type-check the
* usage of a particular directive.
*/
export interface TypeCheckableDirectiveMeta extends DirectiveMeta {
ref: Reference<ts.ClassDeclaration>;
ref: Reference<ClassDeclaration>;
queries: string[];
ngTemplateGuards: string[];
hasNgTemplateContextGuard: boolean;

View File

@ -10,6 +10,7 @@ import {BoundTarget} from '@angular/compiler';
import * as ts from 'typescript';
import {NoopImportRewriter, ReferenceEmitter} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {ImportManager} from '../../translator';
import {TypeCheckBlockMetadata, TypeCheckableDirectiveMeta, TypeCtorMetadata} from './api';
@ -28,11 +29,6 @@ import {generateTypeCtor} from './type_constructor';
export class TypeCheckContext {
constructor(private refEmitter: ReferenceEmitter) {}
/**
* A `Set` of classes which will be used to generate type constructors.
*/
private typeCtors = new Set<ts.ClassDeclaration>();
/**
* A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
* or type-check blocks) that need to be eventually performed on that file.
@ -47,16 +43,12 @@ export class TypeCheckContext {
* @param template AST nodes of the template being recorded.
* @param matcher `SelectorMatcher` which tracks directives that are in scope for this template.
*/
addTemplate(node: ts.ClassDeclaration, boundTarget: BoundTarget<TypeCheckableDirectiveMeta>):
void {
// Only write TCBs for named classes.
if (node.name === undefined) {
throw new Error(`Assertion: class must be named`);
}
addTemplate(
node: ClassDeclaration<ts.ClassDeclaration>,
boundTarget: BoundTarget<TypeCheckableDirectiveMeta>): void {
// Get all of the directives used in the template and record type constructors for all of them.
boundTarget.getUsedDirectives().forEach(dir => {
const dirNode = dir.ref.node;
const dirNode = dir.ref.node as ClassDeclaration<ts.ClassDeclaration>;
// Add a type constructor operation for the directive.
this.addTypeCtor(dirNode.getSourceFile(), dirNode, {
fnName: 'ngTypeCtor',
@ -82,10 +74,9 @@ export class TypeCheckContext {
/**
* Record a type constructor for the given `node` with the given `ctorMetadata`.
*/
addTypeCtor(sf: ts.SourceFile, node: ts.ClassDeclaration, ctorMeta: TypeCtorMetadata): void {
if (this.hasTypeCtor(node)) {
return;
}
addTypeCtor(
sf: ts.SourceFile, node: ClassDeclaration<ts.ClassDeclaration>,
ctorMeta: TypeCtorMetadata): void {
// Lazily construct the operation map.
if (!this.opMap.has(sf)) {
this.opMap.set(sf, []);
@ -143,13 +134,9 @@ export class TypeCheckContext {
return ts.createSourceFile(sf.fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
}
/**
* Whether the given `node` has a type constructor already.
*/
private hasTypeCtor(node: ts.ClassDeclaration): boolean { return this.typeCtors.has(node); }
private addTypeCheckBlock(
sf: ts.SourceFile, node: ts.ClassDeclaration, tcbMeta: TypeCheckBlockMetadata): void {
sf: ts.SourceFile, node: ClassDeclaration<ts.ClassDeclaration>,
tcbMeta: TypeCheckBlockMetadata): void {
if (!this.opMap.has(sf)) {
this.opMap.set(sf, []);
}
@ -165,7 +152,7 @@ interface Op {
/**
* The node in the file which will have code generated for it.
*/
readonly node: ts.ClassDeclaration;
readonly node: ClassDeclaration<ts.ClassDeclaration>;
/**
* Index into the source text where the code generated by the operation should be inserted.
@ -183,7 +170,9 @@ interface Op {
* A type check block operation which produces type check code for a particular component.
*/
class TcbOp implements Op {
constructor(readonly node: ts.ClassDeclaration, readonly meta: TypeCheckBlockMetadata) {}
constructor(
readonly node: ClassDeclaration<ts.ClassDeclaration>, readonly meta: TypeCheckBlockMetadata) {
}
/**
* Type check blocks are inserted immediately after the end of the component class.
@ -201,7 +190,8 @@ class TcbOp implements Op {
* A type constructor operation which produces type constructor code for a particular directive.
*/
class TypeCtorOp implements Op {
constructor(readonly node: ts.ClassDeclaration, readonly meta: TypeCtorMetadata) {}
constructor(
readonly node: ClassDeclaration<ts.ClassDeclaration>, readonly meta: TypeCtorMetadata) {}
/**
* Type constructor operations are inserted immediately before the end of the directive class.

View File

@ -75,7 +75,7 @@ export class TypeCheckProgramHost implements ts.CompilerHost {
writeFile(
fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void)|undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>): void {
sourceFiles: ReadonlyArray<ts.SourceFile>|undefined): void {
return this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
}

View File

@ -10,6 +10,7 @@ import {AST, BindingType, BoundTarget, ImplicitReceiver, PropertyRead, TmplAstBo
import * as ts from 'typescript';
import {NOOP_DEFAULT_IMPORT_RECORDER, Reference, ReferenceEmitter} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {ImportManager, translateExpression} from '../../translator';
import {TypeCheckBlockMetadata, TypeCheckableDirectiveMeta} from './api';
@ -29,8 +30,8 @@ import {astToTypescript} from './expression';
* @param importManager an `ImportManager` for the file into which the TCB will be written.
*/
export function generateTypeCheckBlock(
node: ts.ClassDeclaration, meta: TypeCheckBlockMetadata, importManager: ImportManager,
refEmitter: ReferenceEmitter): ts.FunctionDeclaration {
node: ClassDeclaration<ts.ClassDeclaration>, meta: TypeCheckBlockMetadata,
importManager: ImportManager, refEmitter: ReferenceEmitter): ts.FunctionDeclaration {
const tcb = new Context(meta.boundTarget, node.getSourceFile(), importManager, refEmitter);
const scope = new Scope(tcb);
tcbProcessNodes(meta.boundTarget.target.template !, tcb, scope);

View File

@ -8,6 +8,7 @@
import * as ts from 'typescript';
import {ClassDeclaration} from '../../reflection';
import {TypeCtorMetadata} from './api';
/**
@ -29,19 +30,20 @@ import {TypeCtorMetadata} from './api';
*
* NgForOf.ngTypeCtor(init: {ngForOf: ['foo', 'bar']}); // Infers a type of NgForOf<string>.
*
* @param node the `ts.ClassDeclaration` for which a type constructor will be generated.
* @param node the `ClassDeclaration<ts.ClassDeclaration>` for which a type constructor will be
* generated.
* @param meta additional metadata required to generate the type constructor.
* @returns a `ts.MethodDeclaration` for the type constructor.
*/
export function generateTypeCtor(
node: ts.ClassDeclaration, meta: TypeCtorMetadata): ts.MethodDeclaration {
node: ClassDeclaration<ts.ClassDeclaration>, meta: TypeCtorMetadata): ts.MethodDeclaration {
// Build rawType, a `ts.TypeNode` of the class with its generic parameters passed through from
// the definition without any type bounds. For example, if the class is
// `FooDirective<T extends Bar>`, its rawType would be `FooDirective<T>`.
const rawTypeArgs = node.typeParameters !== undefined ?
node.typeParameters.map(param => ts.createTypeReferenceNode(param.name, undefined)) :
undefined;
const rawType: ts.TypeNode = ts.createTypeReferenceNode(node.name !, rawTypeArgs);
const rawType: ts.TypeNode = ts.createTypeReferenceNode(node.name, rawTypeArgs);
// initType is the type of 'init', the single argument to the type constructor method.
// If the Directive has any inputs, outputs, or queries, its initType will be:

View File

@ -12,6 +12,7 @@ ts_library(
"//packages:types",
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/testing",
"//packages/compiler-cli/src/ngtsc/typecheck",
"//packages/compiler-cli/src/ngtsc/util",

View File

@ -10,6 +10,7 @@ import * as ts from 'typescript';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitter} from '../../imports';
import {LogicalFileSystem} from '../../path';
import {isNamedClassDeclaration} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {getRootDirs} from '../../util/src/typescript';
import {TypeCheckContext} from '../src/context';
@ -47,7 +48,7 @@ TestClass.ngTypeCtor({value: 'test'});
new LogicalProjectStrategy(checker, logicalFs),
]);
const ctx = new TypeCheckContext(emitter);
const TestClass = getDeclaration(program, 'main.ts', 'TestClass', ts.isClassDeclaration);
const TestClass = getDeclaration(program, 'main.ts', 'TestClass', isNamedClassDeclaration);
ctx.addTypeCtor(program.getSourceFile('main.ts') !, TestClass, {
fnName: 'ngTypeCtor',
body: true,

View File

@ -54,7 +54,10 @@ export class InlineResourcesMetadataTransformer implements MetadataTransformer {
isMetadataImportedSymbolReferenceExpression(d.expression) &&
d.expression.module === '@angular/core' && d.expression.name === 'Component' &&
d.arguments) {
d.arguments = d.arguments.map(this.updateDecoratorMetadata.bind(this, loader));
// Arguments to an @Component that was compiled successfully are always
// MetadataObject(s).
d.arguments = (d.arguments as MetadataObject[])
.map(this.updateDecoratorMetadata.bind(this, loader));
}
});
}
@ -95,8 +98,8 @@ export function getInlineResourcesTransformFactory(
// Decorator case - before or without decorator downleveling
// @Component()
const newDecorators = ts.visitNodes(node.decorators, (node: ts.Decorator) => {
if (isComponentDecorator(node, program.getTypeChecker())) {
const newDecorators = ts.visitNodes(node.decorators, (node: ts.Node) => {
if (ts.isDecorator(node) && isComponentDecorator(node, program.getTypeChecker())) {
return updateDecorator(node, loader);
}
return node;
@ -104,9 +107,13 @@ export function getInlineResourcesTransformFactory(
// Annotation case - after decorator downleveling
// static decorators: {type: Function, args?: any[]}[]
const newMembers = ts.visitNodes(
node.members,
(node: ts.ClassElement) => updateAnnotations(node, loader, program.getTypeChecker()));
const newMembers = ts.visitNodes(node.members, (node: ts.Node) => {
if (ts.isClassElement(node)) {
return updateAnnotations(node, loader, program.getTypeChecker());
} else {
return node;
}
});
// Create a new AST subtree with our modifications
return ts.updateClassDeclaration(

View File

@ -21,7 +21,6 @@ jasmine_node_test(
data = [
"//packages/compiler-cli/test/ngtsc/fake_core:npm_package",
],
shard_count = 4,
tags = [
"ivy-only",
],

View File

@ -23,7 +23,6 @@ jasmine_node_test(
data = [
"//packages/compiler-cli/test/ngtsc/fake_core:npm_package",
],
shard_count = 4,
deps = [
":ngtsc_lib",
"//tools/testing:node_no_angular",

View File

@ -13,6 +13,7 @@
"paths": {
"@angular/compiler": ["../../dist/packages/compiler"]
},
"strict": true,
"types": [
"node"
],

View File

@ -5,6 +5,7 @@
"lib": [
"es2015"
],
"strict": true,
"types": [
"jasmine"
]

View File

@ -19,8 +19,9 @@ import {NgQueryDefinition, QueryTiming, QueryType} from './query-definition';
* could be used to access such a query statically.
*/
const STATIC_QUERY_LIFECYCLE_HOOKS = {
[QueryType.ViewChild]: ['ngOnInit', 'ngAfterContentInit', 'ngAfterContentChecked'],
[QueryType.ContentChild]: ['ngOnInit'],
[QueryType.ViewChild]:
['ngOnChanges', 'ngOnInit', 'ngDoCheck', 'ngAfterContentInit', 'ngAfterContentChecked'],
[QueryType.ContentChild]: ['ngOnChanges', 'ngOnInit', 'ngDoCheck'],
};
/**

View File

@ -161,6 +161,26 @@ describe('static-queries migration', () => {
.toContain(`@${queryType}('dynamic', { static: false }) dynamic: any`);
});
it('should mark queries used in "ngOnChanges" as static', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngOnChanges() {
this.query.classList.add('test');
}
}
`);
runMigration();
expect(tree.readContent('/index.ts'))
.toContain(`@${queryType}('test', { static: true }) query: any;`);
});
it('should mark queries used in "ngOnInit" as static', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@ -181,6 +201,26 @@ describe('static-queries migration', () => {
.toContain(`@${queryType}('test', { static: true }) query: any;`);
});
it('should mark queries used in "ngDoCheck" as static', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngDoCheck() {
this.query.classList.add('test');
}
}
`);
runMigration();
expect(tree.readContent('/index.ts'))
.toContain(`@${queryType}('test', { static: true }) query: any;`);
});
it('should keep existing query options when updating timing', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';

View File

@ -18,6 +18,7 @@ export enum ChangeDetectionStrategy {
* Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
* until reactivated by setting the strategy to `Default` (`CheckAlways`).
* Change detection can still be explicitly invoked.
* This strategy applies to all child directives and cannot be overridden.
*/
OnPush = 0,

View File

@ -23,6 +23,8 @@ export const DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*arg
export const INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
export const INHERITED_CLASS_WITH_CTOR =
/^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
export const INHERITED_CLASS_WITH_DELEGATE_CTOR =
/^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s+super\(\.\.\.arguments\)/;
export class ReflectionCapabilities implements PlatformReflectionCapabilities {
private _reflect: any;
@ -70,7 +72,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
// This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
// that sets 'design:paramtypes' to []
// if a class inherits from another class but has no ctor declared itself.
if (DELEGATE_CTOR.exec(typeStr) ||
if (DELEGATE_CTOR.exec(typeStr) || INHERITED_CLASS_WITH_DELEGATE_CTOR.exec(typeStr) ||
(INHERITED_CLASS.exec(typeStr) && !INHERITED_CLASS_WITH_CTOR.exec(typeStr))) {
return null;
}

View File

@ -7,7 +7,7 @@
*/
import {devModeEqual} from '../change_detection/change_detection_util';
import {assertDataInRange, assertLessThan, assertNotEqual} from '../util/assert';
import {assertDataInRange, assertLessThan, assertNotSame} from '../util/assert';
import {throwErrorIfNoChangesMode} from './errors';
import {LView} from './interfaces/view';
import {getCheckNoChangesMode} from './state';
@ -25,15 +25,15 @@ export function updateBinding(lView: LView, bindingIndex: number, value: any): a
/** Gets the current binding value. */
export function getBinding(lView: LView, bindingIndex: number): any {
ngDevMode && assertDataInRange(lView, lView[bindingIndex]);
ngDevMode && assertDataInRange(lView, bindingIndex);
ngDevMode &&
assertNotEqual(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
return lView[bindingIndex];
}
/** Updates binding if changed, then returns whether it was updated. */
export function bindingUpdated(lView: LView, bindingIndex: number, value: any): boolean {
ngDevMode && assertNotEqual(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
ngDevMode &&
assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`);

View File

@ -1952,7 +1952,7 @@ function generateInitialInputs(
*/
export function createLContainer(
hostNative: RElement | RComment | StylingContext | LView, currentView: LView, native: RComment,
isForViewContainerRef?: boolean): LContainer {
tNode: TNode, isForViewContainerRef?: boolean): LContainer {
ngDevMode && assertDomNode(native);
ngDevMode && assertLView(currentView);
const lContainer: LContainer = [
@ -1962,8 +1962,9 @@ export function createLContainer(
currentView, // parent
null, // next
null, // queries
[], // views
tNode, // t_host
native, // native
[], // views
];
ngDevMode && attachLContainerDebug(lContainer);
return lContainer;
@ -2037,7 +2038,8 @@ function containerInternal(
const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : '');
ngDevMode && ngDevMode.rendererCreateComment++;
const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs);
const lContainer = lView[adjustedIndex] = createLContainer(lView[adjustedIndex], lView, comment);
const lContainer = lView[adjustedIndex] =
createLContainer(lView[adjustedIndex], lView, comment, tNode);
appendChild(comment, tNode, lView);

View File

@ -6,10 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {TNode} from './node';
import {LQueries} from './query';
import {RComment, RElement} from './renderer';
import {StylingContext} from './styling';
import {HOST, LView, NEXT, PARENT, QUERIES} from './view';
import {HOST, LView, NEXT, PARENT, QUERIES, T_HOST} from './view';
/**
* Special location which allows easy identification of type. If we have an array which was
@ -23,10 +25,10 @@ export const TYPE = 1;
* Uglify will inline these when minifying so there shouldn't be a cost.
*/
export const ACTIVE_INDEX = 2;
// PARENT, NEXT, and QUERIES are indices 3, 4, and 5.
// PARENT, NEXT, QUERIES and T_HOST are indices 3, 4, 5 and 6.
// As we already have these constants in LView, we don't need to re-create them.
export const VIEWS = 6;
export const NATIVE = 7;
export const VIEWS = 8;
/**
* The state associated with a container.
@ -83,17 +85,22 @@ export interface LContainer extends Array<any> {
// `[QUERIES]` in it which are not needed for `LContainer` (only needed for Template)
/**
* A list of the container's currently active child views. Views will be inserted
* here as they are added and spliced from here when they are removed. We need
* to keep a record of current views so we know which views are already in the DOM
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
* Pointer to the `TNode` which represents the host of the container.
*/
[VIEWS]: LView[];
[T_HOST]: TNode;
/** The comment element that serves as an anchor for this LContainer. */
readonly[NATIVE]:
RComment; // TODO(misko): remove as this value can be gotten by unwrapping `[HOST]`
/**
*A list of the container's currently active child views. Views will be inserted
*here as they are added and spliced from here when they are removed. We need
*to keep a record of current views so we know which views are already in the DOM
*(and don't need to be re-added) and so we can remove views from the DOM when they
*are no longer required.
*/
[VIEWS]: LView[];
}
// Note: This hack is necessary so we don't erroneously get a circular dependency

View File

@ -91,7 +91,7 @@ function walkTNodeTree(
let tNode: TNode|null = rootTNode.child as TNode;
while (tNode) {
let nextTNode: TNode|null = null;
if (tNode.type === TNodeType.Element) {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
executeNodeAction(
action, renderer, renderParent, getNativeByTNode(tNode, currentView), tNode, beforeNode);
const nodeOrContainer = currentView[tNode.index];
@ -99,6 +99,14 @@ function walkTNodeTree(
// This element has an LContainer, and its comment needs to be handled
executeNodeAction(
action, renderer, renderParent, nodeOrContainer[NATIVE], tNode, beforeNode);
if (nodeOrContainer[VIEWS].length) {
currentView = nodeOrContainer[VIEWS][0];
nextTNode = currentView[TVIEW].node;
// When the walker enters a container, then the beforeNode has to become the local native
// comment node.
beforeNode = nodeOrContainer[NATIVE];
}
}
} else if (tNode.type === TNodeType.Container) {
const lContainer = currentView ![tNode.index] as LContainer;
@ -133,9 +141,8 @@ function walkTNodeTree(
nextTNode = currentView[TVIEW].data[head.index] as TNode;
}
}
} else {
// Otherwise, this is a View or an ElementContainer
// Otherwise, this is a View
nextTNode = tNode.child;
}
@ -145,7 +152,14 @@ function walkTNodeTree(
currentView = projectionNodeStack[projectionNodeIndex--] as LView;
tNode = projectionNodeStack[projectionNodeIndex--] as TNode;
}
nextTNode = (tNode.flags & TNodeFlags.isProjected) ? tNode.projectionNext : tNode.next;
if (tNode.flags & TNodeFlags.isProjected) {
nextTNode = tNode.projectionNext;
} else if (tNode.type === TNodeType.ElementContainer) {
nextTNode = tNode.child || tNode.next;
} else {
nextTNode = tNode.next;
}
/**
* Find the next node in the TNode tree, taking into account the place where a node is
@ -172,19 +186,26 @@ function walkTNodeTree(
* chain until:
* - we find an lView with a next pointer
* - or find a tNode with a parent that has a next pointer
* - or find a lContainer
* - or reach root TNode (in which case we exit, since we traversed all nodes)
*/
while (!currentView[NEXT] && currentView[PARENT] &&
!(tNode.parent && tNode.parent.next)) {
if (tNode === rootTNode) return;
currentView = currentView[PARENT] as LView;
if (isLContainer(currentView)) {
tNode = currentView[T_HOST] !;
currentView = currentView[PARENT];
beforeNode = currentView[tNode.index][NATIVE];
break;
}
tNode = currentView[T_HOST] !;
}
if (currentView[NEXT]) {
currentView = currentView[NEXT] as LView;
nextTNode = currentView[T_HOST];
} else {
nextTNode = tNode.next;
nextTNode = tNode.type === TNodeType.ElementContainer && tNode.child || tNode.next;
}
} else {
nextTNode = tNode.next;

View File

@ -321,7 +321,7 @@ export function createContainerRef(
}
hostView[hostTNode.index] = lContainer =
createLContainer(slotValue, hostView, commentNode, true);
createLContainer(slotValue, hostView, commentNode, hostTNode, true);
addToViewTree(hostView, lContainer);
}

View File

@ -34,6 +34,12 @@ export function assertSame<T>(actual: T, expected: T, msg: string) {
}
}
export function assertNotSame<T>(actual: T, expected: T, msg: string) {
if (actual === expected) {
throwError(msg);
}
}
export function assertLessThan<T>(actual: T, expected: T, msg: string) {
if (actual >= expected) {
throwError(msg);

View File

@ -58,7 +58,6 @@ ts_library(
jasmine_node_test(
name = "test",
bootstrap = ["angular/tools/testing/init_node_spec.js"],
shard_count = 4,
deps = [
":test_lib",
":test_node_only_lib",

View File

@ -6,15 +6,25 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {Component, Directive, NO_ERRORS_SCHEMA, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
import {ivyEnabled, onlyInIvy, polyfillGoogGetMsg} from '@angular/private/testing';
describe('ViewContainerRef', () => {
const TRANSLATIONS: any = {
'Bar': 'o',
'{$startTagBefore}{$closeTagBefore}{$startTagDiv}{$startTagInside}{$closeTagInside}{$closeTagDiv}{$startTagAfter}{$closeTagAfter}':
'F{$startTagDiv}{$closeTagDiv}o',
'{$startTagBefore}{$closeTagBefore}{$startTagDiv}{$startTagIn}{$closeTagIn}{$closeTagDiv}{$startTagAfter}{$closeTagAfter}':
'{$startTagDiv}{$closeTagDiv}{$startTagBefore}{$closeTagBefore}'
};
beforeEach(() => {
TestBed.configureTestingModule({declarations: [ViewContainerRefComp, ViewContainerRefApp]});
polyfillGoogGetMsg(TRANSLATIONS);
TestBed.configureTestingModule(
{declarations: [StructDir, ViewContainerRefComp, ViewContainerRefApp, DestroyCasesComp]});
});
describe('insert', () => {
@ -80,6 +90,142 @@ describe('ViewContainerRef', () => {
});
});
describe('destroy should clean the DOM in all cases:', () => {
function executeTest(template: string) {
TestBed.overrideTemplate(DestroyCasesComp, template).configureTestingModule({
schemas: [NO_ERRORS_SCHEMA]
});
const fixture = TestBed.createComponent(DestroyCasesComp);
fixture.detectChanges();
const initial = fixture.nativeElement.innerHTML;
const structDirs = fixture.componentInstance.structDirs.toArray();
structDirs.forEach(structDir => structDir.create());
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('Foo');
structDirs.forEach(structDir => structDir.destroy());
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual(initial);
}
it('when nested ng-container', () => {
executeTest(`
<ng-template structDir>
<before></before>
<ng-container>
<before></before>
<ng-container>
<inside>Foo</inside>
</ng-container>
<after></after>
</ng-container>
<after></after>
</ng-template>`);
});
it('when ViewContainerRef is on a ng-container', () => {
executeTest(`
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template structDir>
<before></before>
<ng-container [ngTemplateOutlet]="foo">
<inside></inside>
</ng-container>
<after></after>
</ng-template>`);
});
it('when ViewContainerRef is on an element', () => {
executeTest(`
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template structDir>
<before></before>
<div [ngTemplateOutlet]="foo">
<inside></inside>
</div>
<after></after>
</ng-template>`);
});
it('when ViewContainerRef is on a ng-template', () => {
executeTest(`
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template structDir>
<before></before>
<ng-template [ngTemplateOutlet]="foo"></ng-template>
<after></after>
</ng-template>`);
});
it('when ViewContainerRef is on an element inside a ng-container', () => {
executeTest(`
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template structDir>
<before></before>
<ng-container>
<before></before>
<div [ngTemplateOutlet]="foo">
<inside></inside>
</div>
<after></after>
</ng-container>
<after></after>
</ng-template>`);
});
onlyInIvy('Ivy i18n logic')
.it('when ViewContainerRef is on an element inside a ng-container with i18n', () => {
executeTest(`
<ng-template #foo>
<span i18n>Bar</span>
</ng-template>
<ng-template structDir>
<before></before>
<ng-container i18n>
<before></before>
<div [ngTemplateOutlet]="foo">
<inside></inside>
</div>
<after></after>
</ng-container>
<after></after>
</ng-template>`);
});
onlyInIvy('Ivy i18n logic')
.it('when ViewContainerRef is on an element, and i18n is on the parent ViewContainerRef',
() => {
executeTest(`
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template structDir i18n>
<before></before>
<div [ngTemplateOutlet]="foo">
<in></in>
</div>
<after></after>
</ng-template>`);
});
});
});
@Component({
@ -105,3 +251,17 @@ class ViewContainerRefComp {
class ViewContainerRefApp {
@ViewChild(ViewContainerRefComp) vcrComp !: ViewContainerRefComp;
}
@Directive({selector: '[structDir]'})
export class StructDir {
constructor(private vcref: ViewContainerRef, private tplRef: TemplateRef<any>) {}
create() { this.vcref.createEmbeddedView(this.tplRef); }
destroy() { this.vcref.clear(); }
}
@Component({selector: 'destroy-cases', template: ` `})
class DestroyCasesComp {
@ViewChildren(StructDir) structDirs !: QueryList<StructDir>;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {createLContainer, createLView, createTView} from '@angular/core/src/render3/instructions/all';
import {createLContainer, createLView, createTNode, createTView} from '@angular/core/src/render3/instructions/all';
import {createEmptyStylingContext} from '@angular/core/src/render3/styling/util';
import {isLContainer, isLView, isStylingContext, unwrapLContainer, unwrapLView, unwrapRNode, unwrapStylingContext} from '@angular/core/src/render3/util/view_utils';
@ -15,7 +15,8 @@ describe('view_utils', () => {
const div = document.createElement('div');
const tView = createTView(0, null, 0, 0, null, null, null, null);
const lView = createLView(null, tView, {}, 0, div, null, {} as any, {} as any, null, null);
const lContainer = createLContainer(lView, lView, div, true);
const tNode = createTNode(null, 3, 0, 'div', []);
const lContainer = createLContainer(lView, lView, div, tNode, true);
const styleContext = createEmptyStylingContext(lContainer, null, null, null);
expect(unwrapRNode(styleContext)).toBe(div);

Some files were not shown because too many files have changed in this diff Show More