Compare commits
55 Commits
backup-7.2
...
buildkite-
Author | SHA1 | Date | |
---|---|---|---|
8d5ab908fc | |||
1dd11c99cc | |||
784453cf9d | |||
51a0bd2e75 | |||
1b0b36d143 | |||
e268a0579a | |||
1f1e77b641 | |||
880b4aabdb | |||
5fee444fea | |||
1e6c9be86c | |||
a9790018df | |||
1dc95c41eb | |||
a0a3648e7a | |||
7df9040c05 | |||
5638c1d507 | |||
755c8091af | |||
59f64dd361 | |||
27431e0e1e | |||
b7d0ab7de3 | |||
460be795cf | |||
c4f7727408 | |||
d1de9ff313 | |||
707c6828b5 | |||
3f64e87ed1 | |||
62e45cef2d | |||
c34eee4e8e | |||
9b91beed69 | |||
eea2b0f288 | |||
13eb57a59f | |||
0b3ae3d70c | |||
4b67b0af3e | |||
bba5e2632e | |||
c5ce4e62c6 | |||
509aa61619 | |||
cdd737e28b | |||
5da55d6246 | |||
50a91ba28c | |||
077a5fb04b | |||
bc0ee01d09 | |||
326b464d20 | |||
8a7498e0ef | |||
07ada7f3d9 | |||
880e8a5cfc | |||
a0585c9a9a | |||
a833b98fd0 | |||
4b70a4e905 | |||
f2a1c66031 | |||
cfb8c17511 | |||
99d0e27587 | |||
b08f3acf09 | |||
3ab25ab078 | |||
0604527199 | |||
8f8572fd3e | |||
e8f7241366 | |||
a20b2f72f2 |
14
.buildkite/dockerfiles/docker-compose.yml
Normal file
14
.buildkite/dockerfiles/docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
windows-env:
|
||||||
|
build:
|
||||||
|
context: ../../
|
||||||
|
dockerfile: ./.buildkite/dockerfiles/windows-env.Dockerfile
|
||||||
|
windows-test:
|
||||||
|
build:
|
||||||
|
context: ../../
|
||||||
|
dockerfile: ./.buildkite/dockerfiles/windows-test.Dockerfile
|
||||||
|
linux-test:
|
||||||
|
build:
|
||||||
|
context: ../../
|
||||||
|
dockerfile: ./.buildkite/dockerfiles/linux-test.Dockerfile
|
21
.buildkite/dockerfiles/linux-env.Dockerfile
Normal file
21
.buildkite/dockerfiles/linux-env.Dockerfile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
FROM node:10.12
|
||||||
|
|
||||||
|
# Bazel Ubuntu pre-requisites.
|
||||||
|
# https://docs.bazel.build/versions/master/install-ubuntu.html
|
||||||
|
RUN apt-get update;
|
||||||
|
RUN apt-get -y install pkg-config zip g++ zlib1g-dev unzip python
|
||||||
|
|
||||||
|
# Chrome prerequisites.
|
||||||
|
# Based on https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker
|
||||||
|
RUN apt-get install -y wget --no-install-recommends \
|
||||||
|
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||||
|
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& apt-get purge --auto-remove -y curl \
|
||||||
|
&& rm -rf /src/*.deb
|
||||||
|
|
||||||
|
|
||||||
|
# Work back from https://github.com/CircleCI-Public/circleci-dockerfiles/blob/master/node/images/10.12.0-jessie/Dockerfile to get Chrome working properly
|
32
.buildkite/dockerfiles/linux-test.Dockerfile
Normal file
32
.buildkite/dockerfiles/linux-test.Dockerfile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
FROM gcr.io/internal-200822/angular-linux:latest
|
||||||
|
USER root
|
||||||
|
|
||||||
|
# TODO: Delete the above once the bootstrap image is available.
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Copy package.json and yarn.lock before the other files.
|
||||||
|
# This allows docker to cache these steps even if source files change.
|
||||||
|
COPY ./package.json /src/package.json
|
||||||
|
COPY ./yarn.lock /src/yarn.lock
|
||||||
|
COPY ./tools/yarn/check-yarn.js /src/tools/yarn/check-yarn.js
|
||||||
|
COPY ./tools/postinstall-patches.js /src/tools/postinstall-patches.js
|
||||||
|
RUN yarn install --frozen-lockfile --non-interactive --network-timeout 100000
|
||||||
|
|
||||||
|
# Setup files.
|
||||||
|
COPY ./ /src
|
||||||
|
COPY .circleci/bazel.rc /etc/bazel.bazelrc
|
||||||
|
|
||||||
|
# Workaround symlink when building image on Windows.
|
||||||
|
RUN rm /src/packages/upgrade/static/src
|
||||||
|
RUN ln -s ../src /src/packages/upgrade/static/src
|
||||||
|
|
||||||
|
# Run tests.
|
||||||
|
RUN yarn bazel test //tools/ts-api-guardian:all --noshow_progress
|
||||||
|
# RUN yarn bazel build //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-local
|
||||||
|
# RUN yarn bazel build --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot //...
|
||||||
|
# RUN yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-local
|
||||||
|
# RUN yarn test-ivy-aot //...
|
||||||
|
|
||||||
|
# docker build -t angular:latest .
|
||||||
|
# docker build . --build-arg target=angular:latest
|
21
.buildkite/dockerfiles/windows-test.Dockerfile
Normal file
21
.buildkite/dockerfiles/windows-test.Dockerfile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Use the a previous image as source, or bootstrap to the default image.
|
||||||
|
ARG target=filipesilva/node-bazel-windows:0.0.2
|
||||||
|
FROM $target
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Copy package.json and yarn.lock before the other files.
|
||||||
|
# This allows docker to cache these steps even if source files change.
|
||||||
|
COPY ./package.json /src/package.json
|
||||||
|
COPY ./yarn.lock /src/yarn.lock
|
||||||
|
RUN yarn install --frozen-lockfile --non-interactive --network-timeout 100000
|
||||||
|
|
||||||
|
# Copy files.
|
||||||
|
COPY ./ /src
|
||||||
|
|
||||||
|
# Setup.
|
||||||
|
COPY .circleci/bazel.rc /etc/bazel.bazelrc
|
||||||
|
RUN del packages\upgrade\static\src
|
||||||
|
RUN mklink /d packages\upgrade\static\src ..\src
|
||||||
|
|
||||||
|
# Run tests.
|
||||||
|
RUN yarn bazel test //tools/ts-api-guardian:all --noshow_progress
|
@ -1,10 +1,36 @@
|
|||||||
steps:
|
steps:
|
||||||
- label: windows-test
|
- label: windows-test
|
||||||
commands:
|
|
||||||
- "yarn install --frozen-lockfile --non-interactive --network-timeout 100000"
|
|
||||||
- "yarn bazel test //tools/ts-api-guardian:all --noshow_progress"
|
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v2.1.0:
|
- docker-compose#v2.6.0:
|
||||||
image: "filipesilva/node-bazel-windows:0.0.2"
|
build: windows-test
|
||||||
|
config: .buildkite/dockerfiles/docker-compose.yml
|
||||||
|
args:
|
||||||
|
- target=gcr.io/internal-200822/angular-windows:master
|
||||||
agents:
|
agents:
|
||||||
windows: true
|
windows: true
|
||||||
|
- label: linux-test
|
||||||
|
plugins:
|
||||||
|
- docker-compose#v2.6.0:
|
||||||
|
build: linux-test
|
||||||
|
config: .buildkite/dockerfiles/docker-compose.yml
|
||||||
|
# args:
|
||||||
|
# - target=gcr.io/internal-200822/angular-linux:master
|
||||||
|
agents:
|
||||||
|
linux: true
|
||||||
|
- wait
|
||||||
|
# - label: windows-update-image
|
||||||
|
# branches: master
|
||||||
|
# plugins:
|
||||||
|
# - docker-compose#v2.6.0:
|
||||||
|
# push: windows-test:gcr.io/internal-200822/angular-windows:master
|
||||||
|
# config: .buildkite/dockerfiles/docker-compose.yml
|
||||||
|
# agents:
|
||||||
|
# windows: true
|
||||||
|
# - label: linux-update-image
|
||||||
|
# # branches: master
|
||||||
|
# plugins:
|
||||||
|
# - docker-compose#v2.6.0:
|
||||||
|
# push: linux-test:gcr.io/internal-200822/angular-linux:master
|
||||||
|
# config: .buildkite/dockerfiles/docker-compose.yml
|
||||||
|
# agents:
|
||||||
|
# linux: true
|
||||||
|
26
.buildkite/provision/linux-buildkite.md
Normal file
26
.buildkite/provision/linux-buildkite.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Follow https://buildkite.com/docs/agent/v3/gcloud#running-the-agent-on-google-container-engine
|
||||||
|
but :
|
||||||
|
on the cluster creation chose n1-highcpu-16 instances, and give "read write" permissions to Storage
|
||||||
|
|
||||||
|
on the "Create a deployment to start an agent:" step use change
|
||||||
|
```
|
||||||
|
env:
|
||||||
|
- name: BUILDKITE_AGENT_TOKEN
|
||||||
|
valueFrom: {secretKeyRef: {name: buildkite-agent, key: token}}
|
||||||
|
```
|
||||||
|
to
|
||||||
|
```
|
||||||
|
env:
|
||||||
|
- name: BUILDKITE_AGENT_TOKEN
|
||||||
|
valueFrom: {secretKeyRef: {name: buildkite-agent, key: token}}
|
||||||
|
- name: BUILDKITE_AGENT_TAGS
|
||||||
|
value: "linux=true"
|
||||||
|
- name: BUILDKITE_TIMESTAMP_LINES
|
||||||
|
value: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
config kubernetes to access container registry
|
||||||
|
maybe just give write access to storage https://medium.com/google-cloud/updating-google-container-engine-vm-scopes-with-zero-downtime-50bff87e5f80
|
||||||
|
https://cloud.google.com/container-registry/docs/using-with-google-cloud-platform
|
||||||
|
https://container-solutions.com/using-google-container-registry-with-kubernetes/
|
@ -8,6 +8,8 @@
|
|||||||
# We recommend machine type n1-highcpu-16 (16 vCPUs, 14.4 GB memory).
|
# We recommend machine type n1-highcpu-16 (16 vCPUs, 14.4 GB memory).
|
||||||
# Use a windows boot disk with container support such as
|
# Use a windows boot disk with container support such as
|
||||||
# "Windows Server version 1803 Datacenter Core for Containers".
|
# "Windows Server version 1803 Datacenter Core for Containers".
|
||||||
|
# Give it push access to the Container Registry by clicking "Set access for each API" and
|
||||||
|
# setting "Storage" to "Read Write".
|
||||||
# Give it a name, then click "Create".
|
# Give it a name, then click "Create".
|
||||||
|
|
||||||
# VM setup:
|
# VM setup:
|
||||||
@ -56,6 +58,15 @@ Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/
|
|||||||
Add-Path "C:\git\bin"
|
Add-Path "C:\git\bin"
|
||||||
Remove-Item git.exe
|
Remove-Item git.exe
|
||||||
|
|
||||||
|
# Install Docker Compose 1.23.2
|
||||||
|
# https://docs.docker.com/compose/install/#install-compose
|
||||||
|
Invoke-WebRequest "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-Windows-x86_64.exe" -UseBasicParsing -OutFile $Env:ProgramFiles\docker\docker-compose.exe
|
||||||
|
|
||||||
|
# Add the gcloud Docker credential helper to the local system account (used by NSSM)
|
||||||
|
# https://cloud.google.com/container-registry/docs/advanced-authentication
|
||||||
|
gcloud auth configure-docker --quiet
|
||||||
|
Copy-Item C:\Users\angular\.docker -Destination C:\Windows\System32\config\systemprofile\ -Recurse
|
||||||
|
|
||||||
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
|
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
|
||||||
Write-Host "Downloading NSSM."
|
Write-Host "Downloading NSSM."
|
||||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
|
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
|
@ -74,7 +74,10 @@ Command syntax is shown as follows:
|
|||||||
* Option names are prefixed with a double dash (--).
|
* Option names are prefixed with a double dash (--).
|
||||||
Option aliases are prefixed with a single dash (-).
|
Option aliases are prefixed with a single dash (-).
|
||||||
Arguments are not prefixed.
|
Arguments are not prefixed.
|
||||||
For example: `ng build my-app -c production`
|
For example:
|
||||||
|
<code-example format="." language="bash">
|
||||||
|
ng build my-app -c production
|
||||||
|
</code-example>
|
||||||
|
|
||||||
* Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option.
|
* Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option.
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ Other JavaScript modules use *import statements* to access public objects from o
|
|||||||
|
|
||||||
<img src="generated/images/guide/architecture/library-module.png" alt="Component" class="left">
|
<img src="generated/images/guide/architecture/library-module.png" alt="Component" class="left">
|
||||||
|
|
||||||
Angular loads as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the `npm` package manager and import parts of them with JavaScript `import` statements.
|
Angular loads as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the node package manager `npm` and import parts of them with JavaScript `import` statements.
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ The following sets content sets default values for the production build target:
|
|||||||
|
|
||||||
```
|
```
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: true
|
production: true,
|
||||||
apiUrl: 'http://my-prod-url'
|
apiUrl: 'http://my-prod-url'
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -235,31 +235,31 @@ Each budget entry is a JSON object with the following properties:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>baseline</td>
|
<td>baseline</td>
|
||||||
<td>An absolute baseline size for percentage values. </td>
|
<td>The baseline size for comparison.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>maximumWarning</td>
|
<td>maximumWarning</td>
|
||||||
<td>Warns when a size exceeds this threshold percentage of the baseline.</td>
|
<td>The maximum threshold for warning relative to the baseline.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>maximumError</td>
|
<td>maximumError</td>
|
||||||
<td>Reports an error when the size exceeds this threshold percentage of the baseline.</td>
|
<td>The maximum threshold for error relative to the baseline.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>minimumWarning</td>
|
<td>minimumWarning</td>
|
||||||
<td>Warns when the size reaches this threshold percentage of the baseline.</td>
|
<td>The minimum threshold for warning relative to the baseline.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>minimumError</td>
|
<td>minimumError</td>
|
||||||
<td>Reports an error when the size reaches this threshold percentage of the baseline.</td>
|
<td>The minimum threshold for error relative to the baseline.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>warning</td>
|
<td>warning</td>
|
||||||
<td>Warns when the size ??reaches or exceeds?? this threshold percentage of the baseline.</td>
|
<td>The threshold for warning relative to the baseline (min & max).</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>error</td>
|
<td>error</td>
|
||||||
<td>Reports an error when the size ??reaches or exceeds?? this threshold percentage of the baseline.</td>
|
<td>The threshold for error relative to the baseline (min & max).</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
_Angular elements_ are Angular components packaged as _custom elements_, a web standard for defining new HTML elements in a framework-agnostic way.
|
_Angular elements_ are Angular components packaged as _custom elements_, a web standard for defining new HTML elements in a framework-agnostic way.
|
||||||
|
|
||||||
[Custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) are a Web Platform feature currently supported by Chrome, Opera, and Safari, and available in other browsers through polyfills (see [Browser Support](#browser-support)).
|
[Custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) are a Web Platform feature currently supported by Chrome, Firefox, Opera, and Safari, and available in other browsers through polyfills (see [Browser Support](#browser-support)).
|
||||||
A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code.
|
A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code.
|
||||||
The browser maintains a `CustomElementRegistry` of defined custom elements (also called Web Components), which maps an instantiable JavaScript class to an HTML tag.
|
The browser maintains a `CustomElementRegistry` of defined custom elements (also called Web Components), which maps an instantiable JavaScript class to an HTML tag.
|
||||||
|
|
||||||
|
@ -90,6 +90,8 @@ The `installMode` determines how these resources are initially cached. The `inst
|
|||||||
|
|
||||||
* `lazy` does not cache any of the resources up front. Instead, the Angular service worker only caches resources for which it receives requests. This is an on-demand caching mode. Resources that are never requested will not be cached. This is useful for things like images at different resolutions, so the service worker only caches the correct assets for the particular screen and orientation.
|
* `lazy` does not cache any of the resources up front. Instead, the Angular service worker only caches resources for which it receives requests. This is an on-demand caching mode. Resources that are never requested will not be cached. This is useful for things like images at different resolutions, so the service worker only caches the correct assets for the particular screen and orientation.
|
||||||
|
|
||||||
|
Defaults to `prefetch`.
|
||||||
|
|
||||||
### `updateMode`
|
### `updateMode`
|
||||||
|
|
||||||
For resources already in the cache, the `updateMode` determines the caching behavior when a new version of the app is discovered. Any resources in the group that have changed since the previous version are updated in accordance with `updateMode`.
|
For resources already in the cache, the `updateMode` determines the caching behavior when a new version of the app is discovered. Any resources in the group that have changed since the previous version are updated in accordance with `updateMode`.
|
||||||
@ -98,6 +100,8 @@ For resources already in the cache, the `updateMode` determines the caching beha
|
|||||||
|
|
||||||
* `lazy` tells the service worker to not cache those resources. Instead, it treats them as unrequested and waits until they're requested again before updating them. An `updateMode` of `lazy` is only valid if the `installMode` is also `lazy`.
|
* `lazy` tells the service worker to not cache those resources. Instead, it treats them as unrequested and waits until they're requested again before updating them. An `updateMode` of `lazy` is only valid if the `installMode` is also `lazy`.
|
||||||
|
|
||||||
|
Defaults to the value `installMode` is set to.
|
||||||
|
|
||||||
### `resources`
|
### `resources`
|
||||||
|
|
||||||
This section describes the resources to cache, broken up into three groups.
|
This section describes the resources to cache, broken up into three groups.
|
||||||
@ -141,7 +145,7 @@ Occasionally APIs change formats in a way that is not backward-compatible. A new
|
|||||||
|
|
||||||
`version` provides a mechanism to indicate that the resources being cached have been updated in a backwards-incompatible way, and that the old cache entries—those from previous versions—should be discarded.
|
`version` provides a mechanism to indicate that the resources being cached have been updated in a backwards-incompatible way, and that the old cache entries—those from previous versions—should be discarded.
|
||||||
|
|
||||||
`version` is an integer field and defaults to `0`.
|
`version` is an integer field and defaults to `1`.
|
||||||
|
|
||||||
### `cacheConfig`
|
### `cacheConfig`
|
||||||
This section defines the policy by which matching requests will be cached.
|
This section defines the policy by which matching requests will be cached.
|
||||||
|
@ -94,6 +94,16 @@ Notice that all of the files the browser needs to render this application are ca
|
|||||||
* `favicon.ico`.
|
* `favicon.ico`.
|
||||||
* Build artifacts (JS and CSS bundles).
|
* Build artifacts (JS and CSS bundles).
|
||||||
* Anything under `assets`.
|
* Anything under `assets`.
|
||||||
|
* Images and fonts directly under the configured `outputPath` (by default `./dist/<project-name>/`) or `resourcesOutputPath`. See [`ng build`](cli/build) for more information about these options.
|
||||||
|
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
Pay attention to two key points:
|
||||||
|
|
||||||
|
1. The generated `ngsw-config.json` includes a limited list of cachable fonts and images extentions. In some cases, you might want to modify the glob pattern to suit your needs.
|
||||||
|
|
||||||
|
1. If `resourcesOutputPath` or `assets` paths are modified after the generation of configuration file, you need to change the paths manually in `ngsw-config.json`.
|
||||||
|
</div>
|
||||||
|
|
||||||
### Making changes to your application
|
### Making changes to your application
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ Add the following `addHero()` method to the `HeroService` class.
|
|||||||
`HeroService.addHero()` differs from `updateHero` in two ways.
|
`HeroService.addHero()` differs from `updateHero` in two ways.
|
||||||
|
|
||||||
* it calls `HttpClient.post()` instead of `put()`.
|
* it calls `HttpClient.post()` instead of `put()`.
|
||||||
* it expects the server to generates an id for the new hero,
|
* it expects the server to generate an id for the new hero,
|
||||||
which it returns in the `Observable<Hero>` to the caller.
|
which it returns in the `Observable<Hero>` to the caller.
|
||||||
|
|
||||||
Refresh the browser and add some heroes.
|
Refresh the browser and add some heroes.
|
||||||
|
@ -112,7 +112,7 @@
|
|||||||
"cross-spawn": "^5.1.0",
|
"cross-spawn": "^5.1.0",
|
||||||
"css-selector-parser": "^1.3.0",
|
"css-selector-parser": "^1.3.0",
|
||||||
"dgeni": "^0.4.11",
|
"dgeni": "^0.4.11",
|
||||||
"dgeni-packages": "^0.27.0",
|
"dgeni-packages": "^0.27.1",
|
||||||
"entities": "^1.1.1",
|
"entities": "^1.1.1",
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-plugin-jasmine": "^2.2.0",
|
"eslint-plugin-jasmine": "^2.2.0",
|
||||||
|
@ -27,14 +27,16 @@ describe(browser.baseUrl, () => {
|
|||||||
|
|
||||||
describe('(with legacy URLs)', () => {
|
describe('(with legacy URLs)', () => {
|
||||||
page.legacyUrls.forEach(([fromUrl, toUrl], i) => {
|
page.legacyUrls.forEach(([fromUrl, toUrl], i) => {
|
||||||
|
const isExternalUrl = /^https?:/.test(toUrl);
|
||||||
|
|
||||||
it(`should redirect '${fromUrl}' to '${toUrl}' (${i + 1}/${page.legacyUrls.length})`, async () => {
|
it(`should redirect '${fromUrl}' to '${toUrl}' (${i + 1}/${page.legacyUrls.length})`, async () => {
|
||||||
await page.goTo(fromUrl);
|
await page.goTo(fromUrl);
|
||||||
|
|
||||||
const expectedUrl = stripTrailingSlash(/^http/.test(toUrl) ? toUrl : page.baseUrl + toUrl);
|
const expectedUrl = stripTrailingSlash(isExternalUrl ? toUrl : page.baseUrl + toUrl);
|
||||||
const actualUrl = await getCurrentUrl();
|
const actualUrl = await getCurrentUrl();
|
||||||
|
|
||||||
expect(actualUrl).toBe(expectedUrl);
|
expect(actualUrl).toBe(expectedUrl);
|
||||||
});
|
}, isExternalUrl ? 60000 : undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2722,10 +2722,10 @@ devtools-timeline-model@1.1.6:
|
|||||||
chrome-devtools-frontend "1.0.401423"
|
chrome-devtools-frontend "1.0.401423"
|
||||||
resolve "1.1.7"
|
resolve "1.1.7"
|
||||||
|
|
||||||
dgeni-packages@^0.27.0:
|
dgeni-packages@^0.27.1:
|
||||||
version "0.27.0"
|
version "0.27.1"
|
||||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.27.0.tgz#99ddf4c97f75bb1f8deb5658ed7d60f6894e9b9d"
|
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.27.1.tgz#f23d78fd3e222910106e45186e1c2e64649464fc"
|
||||||
integrity sha512-BFWJGZTpLb1xAc/iHq7SOcbkyEoxD57NqVG84azfNu63wAVLxoez/9n8VISWNJkrOIT1ITQS7nacgcGxfl0MIw==
|
integrity sha512-zM2HgMni9FvfBFHv2uhWrWRUV0CpaWl4ggoajbGLMT+TEqxkSPKRkCkCQMHek7ZYSXbPdpVb8DuoEKEem74X4g==
|
||||||
dependencies:
|
dependencies:
|
||||||
canonical-path "^1.0.0"
|
canonical-path "^1.0.0"
|
||||||
catharsis "^0.8.1"
|
catharsis "^0.8.1"
|
||||||
|
@ -8,6 +8,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cli-hello-world-ivy": {
|
||||||
|
"master": {
|
||||||
|
"uncompressed": {
|
||||||
|
"runtime": 1440,
|
||||||
|
"main": 507677,
|
||||||
|
"polyfills": 38390
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"hello_world__closure": {
|
"hello_world__closure": {
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
|
135
integration/bazel-schematics/angular.json.original
Normal file
135
integration/bazel-schematics/angular.json.original
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"demo": {
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"projectType": "application",
|
||||||
|
"prefix": "app",
|
||||||
|
"schematics": {},
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/demo",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"extractCss": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "demo:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "demo:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "demo:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
|
"karmaConfig": "src/karma.conf.js",
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"src/tsconfig.app.json",
|
||||||
|
"src/tsconfig.spec.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"demo-e2e": {
|
||||||
|
"root": "e2e/",
|
||||||
|
"projectType": "application",
|
||||||
|
"prefix": "",
|
||||||
|
"architect": {
|
||||||
|
"e2e": {
|
||||||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
|
"options": {
|
||||||
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
|
"devServerTarget": "demo:serve"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"devServerTarget": "demo:serve:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "demo"
|
||||||
|
}
|
20
integration/bazel-schematics/app.e2e-spec.ts
Normal file
20
integration/bazel-schematics/app.e2e-spec.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { AppPage } from './app.po';
|
||||||
|
import { browser } from 'protractor';
|
||||||
|
|
||||||
|
describe('workspace-project App', () => {
|
||||||
|
let page: AppPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new AppPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display welcome message', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getTitleText()).toEqual('Welcome to demo!');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const logs = await browser.manage().logs().get('browser');
|
||||||
|
expect(logs).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
14
integration/bazel-schematics/index.html
Normal file
14
integration/bazel-schematics/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Demo</title>
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -eux -o pipefail
|
set -eux -o pipefail
|
||||||
|
|
||||||
function test() {
|
function testBazel() {
|
||||||
# Set up
|
# Set up
|
||||||
bazel version
|
bazel version
|
||||||
rm -rf demo
|
rm -rf demo
|
||||||
@ -18,4 +18,20 @@ function test() {
|
|||||||
ng e2e
|
ng e2e
|
||||||
}
|
}
|
||||||
|
|
||||||
test
|
function testNonBazel() {
|
||||||
|
# Replace angular.json that uses Bazel builder with the default generated by CLI
|
||||||
|
cp ../angular.json.original ./angular.json
|
||||||
|
# TODO(kyliau) Remove this once the additional assertion is added to CLI
|
||||||
|
cp ../app.e2e-spec.ts ./e2e/src/
|
||||||
|
# TODO(kyliau) Remove this once web_package rule is in use
|
||||||
|
cp ../index.html ./src/
|
||||||
|
rm -rf dist src/main.dev.ts src/main.prod.ts
|
||||||
|
# Just make a symlink instead of full yarn install to expose node_modules
|
||||||
|
ln -s $(bazel info output_base)/external/npm/node_modules node_modules
|
||||||
|
ng build
|
||||||
|
ng test --watch=false
|
||||||
|
ng e2e
|
||||||
|
}
|
||||||
|
|
||||||
|
testBazel
|
||||||
|
testNonBazel
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Editor configuration, see http://editorconfig.org
|
# Editor configuration, see https://editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# CliHelloWorld
|
# CliHelloWorldIvy
|
||||||
|
|
||||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.6.
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0-rc.0.
|
||||||
|
|
||||||
## Development server
|
## Development server
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ Run `ng generate component component-name` to generate a new component. You can
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||||
|
|
||||||
## Running unit tests
|
## Running unit tests
|
||||||
|
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
{
|
{
|
||||||
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"cli": {
|
"cli": {
|
||||||
"packageManager": "yarn"
|
"packageManager": "yarn"
|
||||||
},
|
},
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
"cli-hello-world": {
|
"cli-hello-world-ivy": {
|
||||||
"root": "",
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
|
"prefix": "app",
|
||||||
|
"schematics": {},
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
@ -19,47 +22,36 @@
|
|||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "src/tsconfig.app.json",
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
"src/favicon.ico",
|
||||||
"glob": "assets",
|
"src/assets"
|
||||||
"input": "/src",
|
|
||||||
"output": "/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "favicon.ico",
|
|
||||||
"input": "/src",
|
|
||||||
"output": "/"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
{
|
"src/styles.css"
|
||||||
"input": "src/styles.css"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"dev": {
|
"production": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
{
|
{
|
||||||
"from": "src/environments/environment.ts",
|
"replace": "src/environments/environment.ts",
|
||||||
"to": "dist/environments/environment.ts"
|
"with": "src/environments/environment.prod.ts"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
|
||||||
"production": {
|
|
||||||
"optimization": true,
|
"optimization": true,
|
||||||
"outputHashing": "all",
|
"outputHashing": "all",
|
||||||
"sourceMap": true,
|
"sourceMap": false,
|
||||||
"extractCss": true,
|
"extractCss": true,
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"aot": true,
|
"aot": true,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
"vendorChunk": false,
|
"vendorChunk": false,
|
||||||
"buildOptimizer": true,
|
"buildOptimizer": true,
|
||||||
"fileReplacements": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"src": "src/environments/environment.ts",
|
"type": "initial",
|
||||||
"replaceWith": "src/environments/environment.prod.ts"
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -68,20 +60,17 @@
|
|||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "cli-hello-world:build"
|
"browserTarget": "cli-hello-world-ivy:build"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"dev": {
|
|
||||||
"browserTarget": "cli-hello-world:build:dev"
|
|
||||||
},
|
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "cli-hello-world:build:production"
|
"browserTarget": "cli-hello-world-ivy:build:production"
|
||||||
},
|
},
|
||||||
"ci": {
|
"ci": {
|
||||||
"progress": false
|
"progress": false
|
||||||
},
|
},
|
||||||
"ci-production": {
|
"ci-production": {
|
||||||
"browserTarget": "cli-hello-world:build:production",
|
"browserTarget": "cli-hello-world-ivy:build:production",
|
||||||
"progress": false
|
"progress": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +78,7 @@
|
|||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "cli-hello-world:build"
|
"browserTarget": "cli-hello-world-ivy:build"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
@ -97,25 +86,15 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"main": "src/test.ts",
|
"main": "src/test.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"karmaConfig": "./karma.conf.js",
|
|
||||||
"tsConfig": "src/tsconfig.spec.json",
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
"scripts": [],
|
"karmaConfig": "src/karma.conf.js",
|
||||||
"styles": [
|
"styles": [
|
||||||
{
|
"src/styles.css"
|
||||||
"input": "src/styles.css"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
"scripts": [],
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
"src/favicon.ico",
|
||||||
"glob": "assets",
|
"src/assets"
|
||||||
"input": "/src",
|
|
||||||
"output": "/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "favicon.ico",
|
|
||||||
"input": "/src",
|
|
||||||
"output": "/"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -133,36 +112,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cli-hello-world-e2e": {
|
"cli-hello-world-ivy-e2e": {
|
||||||
"root": "",
|
"root": "e2e/",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"cli": {},
|
"prefix": "",
|
||||||
"schematics": {},
|
|
||||||
"architect": {
|
"architect": {
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"builder": "@angular-devkit/build-angular:protractor",
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
"options": {
|
"options": {
|
||||||
"protractorConfig": "./protractor.conf.js",
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
"devServerTarget": "cli-hello-world:serve"
|
"devServerTarget": "cli-hello-world-ivy:serve"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"devServerTarget": "cli-hello-world:serve:production"
|
"devServerTarget": "cli-hello-world-ivy:serve:production"
|
||||||
},
|
},
|
||||||
"ci": {
|
"ci": {
|
||||||
"devServerTarget": "cli-hello-world:serve:ci"
|
"devServerTarget": "cli-hello-world-ivy:serve:ci"
|
||||||
},
|
},
|
||||||
"ci-production": {
|
"ci-production": {
|
||||||
"devServerTarget": "cli-hello-world:serve:ci-production"
|
"devServerTarget": "cli-hello-world-ivy:serve:ci-production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
"options": {
|
"options": {
|
||||||
"tsConfig": [
|
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||||
"e2e/tsconfig.e2e.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"**/node_modules/**"
|
"**/node_modules/**"
|
||||||
]
|
]
|
||||||
@ -171,13 +147,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schematics": {
|
"defaultProject": "cli-hello-world-ivy"
|
||||||
"@schematics/angular:component": {
|
|
||||||
"prefix": "app",
|
|
||||||
"styleext": "css"
|
|
||||||
},
|
|
||||||
"@schematics/angular:directive": {
|
|
||||||
"prefix": "app"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ const { SpecReporter } = require('jasmine-spec-reporter');
|
|||||||
exports.config = {
|
exports.config = {
|
||||||
allScriptsTimeout: 11000,
|
allScriptsTimeout: 11000,
|
||||||
specs: [
|
specs: [
|
||||||
'./e2e/**/*.e2e-spec.ts'
|
'./src/**/*.e2e-spec.ts'
|
||||||
],
|
],
|
||||||
capabilities: {
|
capabilities: {
|
||||||
browserName: 'chrome',
|
browserName: 'chrome',
|
||||||
@ -25,7 +25,7 @@ exports.config = {
|
|||||||
},
|
},
|
||||||
onPrepare() {
|
onPrepare() {
|
||||||
require('ts-node').register({
|
require('ts-node').register({
|
||||||
project: 'e2e/tsconfig.e2e.json'
|
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||||
});
|
});
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ describe('cli-hello-world App', () => {
|
|||||||
|
|
||||||
it('should display welcome message', () => {
|
it('should display welcome message', () => {
|
||||||
page.navigateTo();
|
page.navigateTo();
|
||||||
expect(page.getParagraphText()).toEqual('Welcome to app!');
|
expect(page.getParagraphText()).toEqual('Welcome to cli-hello-world-ivy!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('the percent pipe should work', () => {
|
it('the percent pipe should work', () => {
|
@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../out-tsc/e2e",
|
"outDir": "../out-tsc/app",
|
||||||
"baseUrl": "./",
|
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"types": [
|
"types": [
|
||||||
@ -11,4 +10,4 @@
|
|||||||
"node"
|
"node"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "cli-hello-world",
|
"name": "cli-hello-world-ivy",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -29,24 +29,24 @@
|
|||||||
"zone.js": "file:../../node_modules/zone.js"
|
"zone.js": "file:../../node_modules/zone.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~0.10.3",
|
"@angular-devkit/build-angular": "~0.12.0-rc.0",
|
||||||
"@angular/cli": "7.0.3",
|
"@angular/cli": "~7.2.0-rc.0",
|
||||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||||
"@types/jasmine": "~2.8.3",
|
"@types/node": "~8.9.4",
|
||||||
"@types/jasminewd2": "~2.0.4",
|
"@types/jasmine": "~2.8.8",
|
||||||
"@types/node": "~6.0.60",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"codelyzer": "^4.3.0",
|
"codelyzer": "~4.5.0",
|
||||||
"jasmine-core": "~2.8.0",
|
"jasmine-core": "~2.99.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~2.0.0",
|
"karma": "~3.1.1",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||||
"karma-jasmine": "~1.1.0",
|
"karma-jasmine": "~1.1.2",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"protractor": "file:../../node_modules/protractor",
|
"protractor": "file:../../node_modules/protractor",
|
||||||
"ts-node": "~4.1.0",
|
"ts-node": "~7.0.0",
|
||||||
"tslint": "~5.9.1",
|
"tslint": "~5.11.0",
|
||||||
"typescript": "file:../../node_modules/typescript"
|
"typescript": "file:../../node_modules/typescript"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
|
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
|
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
|
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { TestBed, async } from '@angular/core/testing';
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@ -8,20 +9,23 @@ describe('AppComponent', () => {
|
|||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
it('should create the app', async(() => {
|
|
||||||
|
it('should create the app', () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.debugElement.componentInstance;
|
const app = fixture.debugElement.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
}));
|
});
|
||||||
it(`should have as title 'app'`, async(() => {
|
|
||||||
|
it(`should have as title 'cli-hello-world-ivy'`, () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.debugElement.componentInstance;
|
const app = fixture.debugElement.componentInstance;
|
||||||
expect(app.title).toEqual('app');
|
expect(app.title).toEqual('cli-hello-world-ivy');
|
||||||
}));
|
});
|
||||||
it('should render title in a h1 tag', async(() => {
|
|
||||||
|
it('should render title in a h1 tag', () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const compiled = fixture.debugElement.nativeElement;
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to cli-hello-world-ivy!');
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
|||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'app';
|
title = 'cli-hello-world-ivy';
|
||||||
}
|
}
|
||||||
|
11
integration/cli-hello-world-ivy/src/browserslist
Normal file
11
integration/cli-hello-world-ivy/src/browserslist
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
#
|
||||||
|
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
|
||||||
|
|
||||||
|
> 0.5%
|
||||||
|
last 2 versions
|
||||||
|
Firefox ESR
|
||||||
|
not dead
|
||||||
|
not IE 9-11
|
@ -1,8 +1,16 @@
|
|||||||
// The file contents for the current environment will overwrite these during build.
|
// This file can be replaced during build by using the `fileReplacements` array.
|
||||||
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
|
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||||
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
// The list of which env maps to which file can be found in `.angular-cli.json`.
|
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For easier debugging in development mode, you can import the following file
|
||||||
|
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||||
|
*
|
||||||
|
* This import should be commented out in production mode because it will have a negative impact
|
||||||
|
* on performance if an error is thrown.
|
||||||
|
*/
|
||||||
|
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>CliHelloWorld</title>
|
<title>CliHelloWorldIvy</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<script>window['ngDevMode'] = true;</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
@ -12,16 +12,14 @@ module.exports = function (config) {
|
|||||||
require('karma-coverage-istanbul-reporter'),
|
require('karma-coverage-istanbul-reporter'),
|
||||||
require('@angular-devkit/build-angular/plugins/karma')
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
],
|
],
|
||||||
client:{
|
client: {
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
},
|
},
|
||||||
coverageIstanbulReporter: {
|
coverageIstanbulReporter: {
|
||||||
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
dir: require('path').join(__dirname, 'coverage'),
|
||||||
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
fixWebpackSourcePaths: true
|
fixWebpackSourcePaths: true
|
||||||
},
|
},
|
||||||
angularCli: {
|
|
||||||
environment: 'dev'
|
|
||||||
},
|
|
||||||
reporters: ['progress', 'kjhtml'],
|
reporters: ['progress', 'kjhtml'],
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
@ -9,4 +9,4 @@ if (environment.production) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
.catch(err => console.log(err));
|
.catch(err => console.error(err));
|
||||||
|
@ -11,14 +11,17 @@
|
|||||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||||
*
|
*
|
||||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
* Learn more in https://angular.io/guide/browser-support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* BROWSER POLYFILLS
|
* BROWSER POLYFILLS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
|
||||||
|
* This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
|
||||||
|
*/
|
||||||
|
|
||||||
// import 'core-js/es6/symbol';
|
// import 'core-js/es6/symbol';
|
||||||
// import 'core-js/es6/object';
|
// import 'core-js/es6/object';
|
||||||
// import 'core-js/es6/function';
|
// import 'core-js/es6/function';
|
||||||
@ -40,19 +43,36 @@
|
|||||||
/** IE10 and IE11 requires the following for the Reflect API. */
|
/** IE10 and IE11 requires the following for the Reflect API. */
|
||||||
// import 'core-js/es6/reflect';
|
// import 'core-js/es6/reflect';
|
||||||
|
|
||||||
|
|
||||||
/** Evergreen browsers require these. **/
|
|
||||||
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
|
||||||
import 'core-js/es7/reflect';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required to support Web Animations `@angular/platform-browser/animations`.
|
* Web Animations `@angular/platform-browser/animations`
|
||||||
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
|
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||||
**/
|
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||||
|
*/
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||||
|
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||||
|
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||||
|
* will put import in the top of bundle, so user need to create a separate file
|
||||||
|
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||||
|
* into that file, and then add the following code before importing zone.js.
|
||||||
|
* import './zone-flags.ts';
|
||||||
|
*
|
||||||
|
* The flags allowed in zone-flags.ts are listed here.
|
||||||
|
*
|
||||||
|
* The following flags will work for all browsers.
|
||||||
|
*
|
||||||
|
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||||
|
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||||
|
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||||
|
*
|
||||||
|
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||||
|
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||||
|
*
|
||||||
|
* (window as any).__Zone_enable_cross_context_check = true;
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* Zone JS is required by default for Angular itself.
|
* Zone JS is required by default for Angular itself.
|
||||||
@ -60,7 +80,6 @@ import 'core-js/es7/reflect';
|
|||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* APPLICATION IMPORTS
|
* APPLICATION IMPORTS
|
||||||
*/
|
*/
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../out-tsc/app",
|
"outDir": "../out-tsc/app",
|
||||||
"baseUrl": "./",
|
|
||||||
"module": "es2015",
|
|
||||||
"types": []
|
"types": []
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"test.ts",
|
"test.ts",
|
||||||
"**/*.spec.ts"
|
"**/*.spec.ts"
|
||||||
]
|
],
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableIvy": "ngtsc"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../out-tsc/spec",
|
"outDir": "../out-tsc/spec",
|
||||||
"baseUrl": "./",
|
|
||||||
"module": "commonjs",
|
|
||||||
"types": [
|
"types": [
|
||||||
"jasmine",
|
"jasmine",
|
||||||
"node"
|
"node"
|
||||||
@ -17,4 +15,4 @@
|
|||||||
"**/*.spec.ts",
|
"**/*.spec.ts",
|
||||||
"**/*.d.ts"
|
"**/*.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
17
integration/cli-hello-world-ivy/src/tslint.json
Normal file
17
integration/cli-hello-world-ivy/src/tslint.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"directive-selector": [
|
||||||
|
true,
|
||||||
|
"attribute",
|
||||||
|
"app",
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
"app",
|
||||||
|
"kebab-case"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": "ngtsc",
|
|
||||||
},
|
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
"outDir": "./dist/out-tsc",
|
"outDir": "./dist/out-tsc",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
|
"module": "es2015",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"importHelpers": true,
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types"
|
"node_modules/@types"
|
||||||
],
|
],
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2017",
|
"es2018",
|
||||||
"dom"
|
"dom"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"rulesDirectory": [
|
"rulesDirectory": [
|
||||||
"node_modules/codelyzer"
|
"codelyzer"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"arrow-return-shorthand": true,
|
"arrow-return-shorthand": true,
|
||||||
@ -18,7 +18,6 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"import-blacklist": [
|
"import-blacklist": [
|
||||||
true,
|
true,
|
||||||
"rxjs",
|
|
||||||
"rxjs/Rx"
|
"rxjs/Rx"
|
||||||
],
|
],
|
||||||
"import-spacing": true,
|
"import-spacing": true,
|
||||||
@ -66,6 +65,7 @@
|
|||||||
],
|
],
|
||||||
"no-misused-new": true,
|
"no-misused-new": true,
|
||||||
"no-non-null-assertion": true,
|
"no-non-null-assertion": true,
|
||||||
|
"no-redundant-jsdoc": true,
|
||||||
"no-shadowed-variable": true,
|
"no-shadowed-variable": true,
|
||||||
"no-string-literal": false,
|
"no-string-literal": false,
|
||||||
"no-string-throw": true,
|
"no-string-throw": true,
|
||||||
@ -117,18 +117,6 @@
|
|||||||
"check-separator",
|
"check-separator",
|
||||||
"check-type"
|
"check-type"
|
||||||
],
|
],
|
||||||
"directive-selector": [
|
|
||||||
true,
|
|
||||||
"attribute",
|
|
||||||
"app",
|
|
||||||
"camelCase"
|
|
||||||
],
|
|
||||||
"component-selector": [
|
|
||||||
true,
|
|
||||||
"element",
|
|
||||||
"app",
|
|
||||||
"kebab-case"
|
|
||||||
],
|
|
||||||
"no-output-on-prefix": true,
|
"no-output-on-prefix": true,
|
||||||
"use-input-property-decorator": true,
|
"use-input-property-decorator": true,
|
||||||
"use-output-property-decorator": true,
|
"use-output-property-decorator": true,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -48,9 +48,9 @@ for testDir in $(ls | grep -v node_modules) ; do
|
|||||||
yarn install --cache-folder ../$cache
|
yarn install --cache-folder ../$cache
|
||||||
yarn test || exit 1
|
yarn test || exit 1
|
||||||
|
|
||||||
# Track payload size for cli-hello-world and hello_world__closure and the render3 tests
|
# Track payload size for cli-hello-world, cli-hello-world-ivy and hello_world__closure
|
||||||
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__closure ]]); then
|
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]] || [[ $testDir == hello_world__closure ]]); then
|
||||||
if [[ $testDir == cli-hello-world ]]; then
|
if ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]]); then
|
||||||
yarn build
|
yarn build
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
"fs-extra": "4.0.2",
|
"fs-extra": "4.0.2",
|
||||||
"jasmine": "^3.1.0",
|
"jasmine": "^3.1.0",
|
||||||
"jasmine-core": "^3.1.0",
|
"jasmine-core": "^3.1.0",
|
||||||
"karma": "^2.0.4",
|
"karma": "^3.1.4",
|
||||||
"magic-string": "^0.25.0",
|
"magic-string": "^0.25.0",
|
||||||
"minimist": "1.2.0",
|
"minimist": "1.2.0",
|
||||||
"mock-fs": "^4.5.0",
|
"mock-fs": "^4.5.0",
|
||||||
@ -110,7 +110,7 @@
|
|||||||
"firefox-profile": "1.0.3",
|
"firefox-profile": "1.0.3",
|
||||||
"glob": "7.1.2",
|
"glob": "7.1.2",
|
||||||
"gulp": "3.9.1",
|
"gulp": "3.9.1",
|
||||||
"gulp-clang-format": "1.0.27",
|
"gulp-clang-format": "1.0.23",
|
||||||
"gulp-connect": "5.0.0",
|
"gulp-connect": "5.0.0",
|
||||||
"gulp-conventional-changelog": "^2.0.3",
|
"gulp-conventional-changelog": "^2.0.3",
|
||||||
"gulp-filter": "^5.1.0",
|
"gulp-filter": "^5.1.0",
|
||||||
|
@ -19,12 +19,12 @@ def rules_angular_dependencies():
|
|||||||
#
|
#
|
||||||
# Download Bazel toolchain dependencies as needed by build actions
|
# Download Bazel toolchain dependencies as needed by build actions
|
||||||
# Use a SHA to get fix for needing symlink_prefix during npm publishing
|
# Use a SHA to get fix for needing symlink_prefix during npm publishing
|
||||||
# TODO(alexeagle): updated to next tagged rules_typescript release
|
# TODO(alexeagle): update to release later than 0.16.4
|
||||||
_maybe(
|
_maybe(
|
||||||
http_archive,
|
http_archive,
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/ee218e2a98b9f09ba07cecac8496a5918c47bc5d.zip",
|
url = "https://github.com/bazelbuild/rules_nodejs/archive/bbf31af8aafad8dd5193356081c6b233ba143aa3.zip",
|
||||||
strip_prefix = "rules_nodejs-ee218e2a98b9f09ba07cecac8496a5918c47bc5d",
|
strip_prefix = "rules_nodejs-bbf31af8aafad8dd5193356081c6b233ba143aa3",
|
||||||
)
|
)
|
||||||
|
|
||||||
_maybe(
|
_maybe(
|
||||||
|
@ -95,6 +95,8 @@ def _plain_rollup_bundle(ctx):
|
|||||||
run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True)
|
run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True)
|
||||||
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
|
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
|
||||||
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
|
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
|
||||||
|
cjs_rollup_config = write_rollup_config(ctx, filename = "_%s_cjs.rollup.conf.js", output_format = "cjs")
|
||||||
|
run_rollup(ctx, collect_es2015_sources(ctx), cjs_rollup_config, ctx.outputs.build_cjs)
|
||||||
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html)
|
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html)
|
||||||
|
|
||||||
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
|
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
|
||||||
@ -134,6 +136,8 @@ def _ng_rollup_bundle(ctx):
|
|||||||
|
|
||||||
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
|
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
|
||||||
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
|
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
|
||||||
|
cjs_rollup_config = write_rollup_config(ctx, filename = "_%s_cjs.rollup.conf.js", output_format = "cjs")
|
||||||
|
run_rollup(ctx, collect_es2015_sources(ctx), cjs_rollup_config, ctx.outputs.build_cjs)
|
||||||
|
|
||||||
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
|
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
|
||||||
|
|
||||||
|
@ -77,7 +77,10 @@ def _protractor_web_test_impl(ctx):
|
|||||||
output = ctx.outputs.executable,
|
output = ctx.outputs.executable,
|
||||||
is_executable = True,
|
is_executable = True,
|
||||||
content = """#!/usr/bin/env bash
|
content = """#!/usr/bin/env bash
|
||||||
if [ -e "$RUNFILE_MANIFEST_FILE" ]; then
|
# Immediately exit if any command fails.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -e "$RUNFILES_MANIFEST_FILE" ]; then
|
||||||
while read line; do
|
while read line; do
|
||||||
declare -a PARTS=($line)
|
declare -a PARTS=($line)
|
||||||
if [ "${{PARTS[0]}}" == "{TMPL_protractor}" ]; then
|
if [ "${{PARTS[0]}}" == "{TMPL_protractor}" ]; then
|
||||||
@ -85,7 +88,7 @@ if [ -e "$RUNFILE_MANIFEST_FILE" ]; then
|
|||||||
elif [ "${{PARTS[0]}}" == "{TMPL_conf}" ]; then
|
elif [ "${{PARTS[0]}}" == "{TMPL_conf}" ]; then
|
||||||
readonly CONF=${{PARTS[1]}}
|
readonly CONF=${{PARTS[1]}}
|
||||||
fi
|
fi
|
||||||
done < $RUNFILE_MANIFEST_FILE
|
done < $RUNFILES_MANIFEST_FILE
|
||||||
else
|
else
|
||||||
readonly PROTRACTOR=../{TMPL_protractor}
|
readonly PROTRACTOR=../{TMPL_protractor}
|
||||||
readonly CONF=../{TMPL_conf}
|
readonly CONF=../{TMPL_conf}
|
||||||
|
@ -11,7 +11,7 @@ import {defineInjectable, inject} from '@angular/core';
|
|||||||
import {DOCUMENT} from './dom_tokens';
|
import {DOCUMENT} from './dom_tokens';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the scroll position.
|
* Defines a scroll position manager. Implemented by `BrowserViewportScroller`.
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
@ -24,40 +24,40 @@ export abstract class ViewportScroller {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the top offset used when scrolling to an anchor.
|
* Configures the top offset used when scrolling to an anchor.
|
||||||
|
* @param offset A position in screen coordinates (a tuple with x and y values)
|
||||||
|
* or a function that returns the top offset position.
|
||||||
*
|
*
|
||||||
* When given a tuple with two number, the service will always use the numbers.
|
|
||||||
* When given a function, the service will invoke the function every time it restores scroll
|
|
||||||
* position.
|
|
||||||
*/
|
*/
|
||||||
abstract setOffset(offset: [number, number]|(() => [number, number])): void;
|
abstract setOffset(offset: [number, number]|(() => [number, number])): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current scroll position.
|
* Retrieves the current scroll position.
|
||||||
|
* @returns A position in screen coordinates (a tuple with x and y values).
|
||||||
*/
|
*/
|
||||||
abstract getScrollPosition(): [number, number];
|
abstract getScrollPosition(): [number, number];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scroll position.
|
* Scrolls to a specified position.
|
||||||
|
* @param position A position in screen coordinates (a tuple with x and y values).
|
||||||
*/
|
*/
|
||||||
abstract scrollToPosition(position: [number, number]): void;
|
abstract scrollToPosition(position: [number, number]): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls to the provided anchor.
|
* Scrolls to an anchor element.
|
||||||
|
* @param anchor The ID of the anchor element.
|
||||||
*/
|
*/
|
||||||
abstract scrollToAnchor(anchor: string): void;
|
abstract scrollToAnchor(anchor: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Disables automatic scroll restoration provided by the browser.
|
* Disables automatic scroll restoration provided by the browser.
|
||||||
*
|
|
||||||
* See also [window.history.scrollRestoration
|
* See also [window.history.scrollRestoration
|
||||||
* info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)
|
* info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration).
|
||||||
*/
|
*/
|
||||||
abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void;
|
abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the scroll position.
|
* Manages the scroll position for a browser window.
|
||||||
*/
|
*/
|
||||||
export class BrowserViewportScroller implements ViewportScroller {
|
export class BrowserViewportScroller implements ViewportScroller {
|
||||||
private offset: () => [number, number] = () => [0, 0];
|
private offset: () => [number, number] = () => [0, 0];
|
||||||
@ -66,10 +66,9 @@ export class BrowserViewportScroller implements ViewportScroller {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the top offset used when scrolling to an anchor.
|
* Configures the top offset used when scrolling to an anchor.
|
||||||
|
* @param offset A position in screen coordinates (a tuple with x and y values)
|
||||||
|
* or a function that returns the top offset position.
|
||||||
*
|
*
|
||||||
* * When given a number, the service will always use the number.
|
|
||||||
* * When given a function, the service will invoke the function every time it restores scroll
|
|
||||||
* position.
|
|
||||||
*/
|
*/
|
||||||
setOffset(offset: [number, number]|(() => [number, number])): void {
|
setOffset(offset: [number, number]|(() => [number, number])): void {
|
||||||
if (Array.isArray(offset)) {
|
if (Array.isArray(offset)) {
|
||||||
@ -80,7 +79,8 @@ export class BrowserViewportScroller implements ViewportScroller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current scroll position.
|
* Retrieves the current scroll position.
|
||||||
|
* @returns The position in screen coordinates.
|
||||||
*/
|
*/
|
||||||
getScrollPosition(): [number, number] {
|
getScrollPosition(): [number, number] {
|
||||||
if (this.supportScrollRestoration()) {
|
if (this.supportScrollRestoration()) {
|
||||||
@ -92,6 +92,7 @@ export class BrowserViewportScroller implements ViewportScroller {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scroll position.
|
* Sets the scroll position.
|
||||||
|
* @param position The new position in screen coordinates.
|
||||||
*/
|
*/
|
||||||
scrollToPosition(position: [number, number]): void {
|
scrollToPosition(position: [number, number]): void {
|
||||||
if (this.supportScrollRestoration()) {
|
if (this.supportScrollRestoration()) {
|
||||||
@ -100,7 +101,8 @@ export class BrowserViewportScroller implements ViewportScroller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls to the provided anchor.
|
* Scrolls to an anchor element.
|
||||||
|
* @param anchor The ID of the anchor element.
|
||||||
*/
|
*/
|
||||||
scrollToAnchor(anchor: string): void {
|
scrollToAnchor(anchor: string): void {
|
||||||
if (this.supportScrollRestoration()) {
|
if (this.supportScrollRestoration()) {
|
||||||
|
@ -15,6 +15,7 @@ ts_library(
|
|||||||
"//packages/platform-browser",
|
"//packages/platform-browser",
|
||||||
"//packages/platform-browser-dynamic",
|
"//packages/platform-browser-dynamic",
|
||||||
"//packages/platform-browser/testing",
|
"//packages/platform-browser/testing",
|
||||||
|
"//packages/private/testing",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_out
|
|||||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
import {modifiedInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('insert/remove', () => {
|
describe('insert/remove', () => {
|
||||||
|
|
||||||
@ -106,17 +107,20 @@ describe('insert/remove', () => {
|
|||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should resolve a with injector', async(() => {
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.componentInstance.cmpRef = null;
|
modifiedInIvy('Static ViewChild and ContentChild queries are resolved in update mode')
|
||||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
.it('should resolve with an injector', async(() => {
|
||||||
fixture.detectChanges();
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
|
||||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
// We are accessing a ViewChild (ngComponentOutlet) before change detection has run
|
||||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
fixture.componentInstance.cmpRef = null;
|
||||||
expect(cmpRef.instance.testToken).toBeNull();
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
}));
|
fixture.detectChanges();
|
||||||
|
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||||
|
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||||
|
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||||
|
expect(cmpRef.instance.testToken).toBeNull();
|
||||||
|
}));
|
||||||
|
|
||||||
it('should render projectable nodes, if supplied', async(() => {
|
it('should render projectable nodes, if supplied', async(() => {
|
||||||
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* @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 {ReferencesRegistry} from '../../../ngtsc/annotations';
|
||||||
|
import {Declaration} from '../../../ngtsc/host';
|
||||||
|
import {ResolvedReference} from '../../../ngtsc/metadata';
|
||||||
|
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||||
|
import {isDefined} from '../utils';
|
||||||
|
|
||||||
|
export interface ModuleWithProvidersInfo {
|
||||||
|
/**
|
||||||
|
* The declaration (in the .d.ts file) of the function that returns
|
||||||
|
* a `ModuleWithProviders object, but has a signature that needs
|
||||||
|
* a type parameter adding.
|
||||||
|
*/
|
||||||
|
declaration: ts.MethodDeclaration|ts.FunctionDeclaration;
|
||||||
|
/**
|
||||||
|
* The NgModule class declaration (in the .d.ts file) to add as a type parameter.
|
||||||
|
*/
|
||||||
|
ngModule: Declaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleWithProvidersAnalyses = Map<ts.SourceFile, ModuleWithProvidersInfo[]>;
|
||||||
|
export const ModuleWithProvidersAnalyses = Map;
|
||||||
|
|
||||||
|
export class ModuleWithProvidersAnalyzer {
|
||||||
|
constructor(private host: NgccReflectionHost, private referencesRegistry: ReferencesRegistry) {}
|
||||||
|
|
||||||
|
analyzeProgram(program: ts.Program): ModuleWithProvidersAnalyses {
|
||||||
|
const analyses = new ModuleWithProvidersAnalyses();
|
||||||
|
const rootFiles = this.getRootFiles(program);
|
||||||
|
rootFiles.forEach(f => {
|
||||||
|
const fns = this.host.getModuleWithProvidersFunctions(f);
|
||||||
|
fns && fns.forEach(fn => {
|
||||||
|
const dtsFn = this.getDtsDeclaration(fn.declaration);
|
||||||
|
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
|
||||||
|
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
|
||||||
|
null;
|
||||||
|
if (!typeParam || isAnyKeyword(typeParam)) {
|
||||||
|
// Either we do not have a parameterized type or the type is `any`.
|
||||||
|
let ngModule = this.host.getDeclarationOfIdentifier(fn.ngModule);
|
||||||
|
if (!ngModule) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find a declaration for NgModule ${fn.ngModule.text} referenced in ${fn.declaration.getText()}`);
|
||||||
|
}
|
||||||
|
// For internal (non-library) module references, redirect the module's value declaration
|
||||||
|
// to its type declaration.
|
||||||
|
if (ngModule.viaModule === null) {
|
||||||
|
const dtsNgModule = this.host.getDtsDeclaration(ngModule.node);
|
||||||
|
if (!dtsNgModule) {
|
||||||
|
throw new Error(
|
||||||
|
`No typings declaration can be found for the referenced NgModule class in ${fn.declaration.getText()}.`);
|
||||||
|
}
|
||||||
|
if (!ts.isClassDeclaration(dtsNgModule)) {
|
||||||
|
throw new Error(
|
||||||
|
`The referenced NgModule in ${fn.declaration.getText()} is not a class declaration in the typings program; instead we get ${dtsNgModule.getText()}`);
|
||||||
|
}
|
||||||
|
// Record the usage of the internal module as it needs to become an exported symbol
|
||||||
|
this.referencesRegistry.add(new ResolvedReference(ngModule.node, fn.ngModule));
|
||||||
|
|
||||||
|
ngModule = {node: dtsNgModule, viaModule: null};
|
||||||
|
}
|
||||||
|
const dtsFile = dtsFn.getSourceFile();
|
||||||
|
const analysis = analyses.get(dtsFile) || [];
|
||||||
|
analysis.push({declaration: dtsFn, ngModule});
|
||||||
|
analyses.set(dtsFile, analysis);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return analyses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRootFiles(program: ts.Program): ts.SourceFile[] {
|
||||||
|
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDtsDeclaration(fn: ts.SignatureDeclaration) {
|
||||||
|
let dtsFn: ts.Declaration|null = null;
|
||||||
|
const containerClass = this.host.getClassSymbol(fn.parent);
|
||||||
|
const fnName = fn.name && ts.isIdentifier(fn.name) && fn.name.text;
|
||||||
|
if (containerClass && fnName) {
|
||||||
|
const dtsClass = this.host.getDtsDeclaration(containerClass.valueDeclaration);
|
||||||
|
// Get the declaration of the matching static method
|
||||||
|
dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ?
|
||||||
|
dtsClass.members
|
||||||
|
.find(
|
||||||
|
member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
|
||||||
|
member.name.text === fnName) as ts.Declaration :
|
||||||
|
null;
|
||||||
|
} else {
|
||||||
|
dtsFn = this.host.getDtsDeclaration(fn);
|
||||||
|
}
|
||||||
|
if (!dtsFn) {
|
||||||
|
throw new Error(`Matching type declaration for ${fn.getText()} is missing`);
|
||||||
|
}
|
||||||
|
if (!isFunctionOrMethod(dtsFn)) {
|
||||||
|
throw new Error(
|
||||||
|
`Matching type declaration for ${fn.getText()} is not a function: ${dtsFn.getText()}`);
|
||||||
|
}
|
||||||
|
return dtsFn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isFunctionOrMethod(declaration: ts.Declaration): declaration is ts.FunctionDeclaration|
|
||||||
|
ts.MethodDeclaration {
|
||||||
|
return ts.isFunctionDeclaration(declaration) || ts.isMethodDeclaration(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnyKeyword(typeParam: ts.TypeNode): typeParam is ts.KeywordTypeNode {
|
||||||
|
return typeParam.kind === ts.SyntaxKind.AnyKeyword;
|
||||||
|
}
|
@ -15,7 +15,7 @@ import {hasNameIdentifier, isDefined} from '../utils';
|
|||||||
export interface ExportInfo {
|
export interface ExportInfo {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
from: string;
|
from: string;
|
||||||
dtsFrom: string|null;
|
dtsFrom?: string|null;
|
||||||
}
|
}
|
||||||
export type PrivateDeclarationsAnalyses = ExportInfo[];
|
export type PrivateDeclarationsAnalyses = ExportInfo[];
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ export class PrivateDeclarationsAnalyzer {
|
|||||||
return Array.from(privateDeclarations.keys()).map(id => {
|
return Array.from(privateDeclarations.keys()).map(id => {
|
||||||
const from = id.getSourceFile().fileName;
|
const from = id.getSourceFile().fileName;
|
||||||
const declaration = privateDeclarations.get(id) !;
|
const declaration = privateDeclarations.get(id) !;
|
||||||
const dtsDeclaration = this.host.getDtsDeclarationOfClass(declaration.node);
|
const dtsDeclaration = this.host.getDtsDeclaration(declaration.node);
|
||||||
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
|
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
|
||||||
return {identifier: id.text, from, dtsFrom};
|
return {identifier: id.text, from, dtsFrom};
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ import {BundleProgram} from '../packages/bundle_program';
|
|||||||
import {findAll, getNameText, isDefined} from '../utils';
|
import {findAll, getNameText, isDefined} from '../utils';
|
||||||
|
|
||||||
import {DecoratedClass} from './decorated_class';
|
import {DecoratedClass} from './decorated_class';
|
||||||
import {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||||
|
|
||||||
export const DECORATORS = 'decorators' as ts.__String;
|
export const DECORATORS = 'decorators' as ts.__String;
|
||||||
export const PROP_DECORATORS = 'propDecorators' as ts.__String;
|
export const PROP_DECORATORS = 'propDecorators' as ts.__String;
|
||||||
@ -49,10 +49,10 @@ export const CONSTRUCTOR_PARAMS = 'ctorParameters' as ts.__String;
|
|||||||
* a static method called `ctorParameters`.
|
* a static method called `ctorParameters`.
|
||||||
*/
|
*/
|
||||||
export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost {
|
export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost {
|
||||||
protected dtsClassMap: Map<string, ts.ClassDeclaration>|null;
|
protected dtsDeclarationMap: Map<string, ts.Declaration>|null;
|
||||||
constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) {
|
constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) {
|
||||||
super(checker);
|
super(checker);
|
||||||
this.dtsClassMap = dts && this.computeDtsClassMap(dts.path, dts.program) || null;
|
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,15 +327,15 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
* is not a class or has an unknown number of type parameters.
|
* is not a class or has an unknown number of type parameters.
|
||||||
*/
|
*/
|
||||||
getGenericArityOfClass(clazz: ts.Declaration): number|null {
|
getGenericArityOfClass(clazz: ts.Declaration): number|null {
|
||||||
const dtsClass = this.getDtsDeclarationOfClass(clazz);
|
const dtsDeclaration = this.getDtsDeclaration(clazz);
|
||||||
if (dtsClass) {
|
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
|
||||||
return dtsClass.typeParameters ? dtsClass.typeParameters.length : 0;
|
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
|
* Take an exported declaration of a class (maybe down-leveled to a variable) and look up the
|
||||||
* declaration of its type in a separate .d.ts tree.
|
* declaration of its type in a separate .d.ts tree.
|
||||||
*
|
*
|
||||||
* This function is allowed to return `null` if the current compilation unit does not have a
|
* This function is allowed to return `null` if the current compilation unit does not have a
|
||||||
@ -346,19 +346,47 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
|
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
|
||||||
* `ts.Program` as the input declaration.
|
* `ts.Program` as the input declaration.
|
||||||
*/
|
*/
|
||||||
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null {
|
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null {
|
||||||
if (this.dtsClassMap) {
|
if (!this.dtsDeclarationMap) {
|
||||||
if (ts.isClassDeclaration(declaration)) {
|
return null;
|
||||||
if (!declaration.name || !ts.isIdentifier(declaration.name)) {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot get the dts file for a class declaration that has no indetifier: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
|
|
||||||
}
|
|
||||||
return this.dtsClassMap.get(declaration.name.text) || null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
if (!isNamedDeclaration(declaration)) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
|
||||||
|
}
|
||||||
|
return this.dtsDeclarationMap.get(declaration.name.text) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the given source file for exported functions and static class methods that return
|
||||||
|
* ModuleWithProviders objects.
|
||||||
|
* @param f The source file to search for these functions
|
||||||
|
* @returns An array of function declarations that look like they return ModuleWithProviders
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[] {
|
||||||
|
const exports = this.getExportsOfModule(f);
|
||||||
|
if (!exports) return [];
|
||||||
|
const infos: ModuleWithProvidersFunction[] = [];
|
||||||
|
exports.forEach((declaration, name) => {
|
||||||
|
if (this.isClass(declaration.node)) {
|
||||||
|
this.getMembersOfClass(declaration.node).forEach(member => {
|
||||||
|
if (member.isStatic) {
|
||||||
|
const info = this.parseForModuleWithProviders(member.node);
|
||||||
|
if (info) {
|
||||||
|
infos.push(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const info = this.parseForModuleWithProviders(declaration.node);
|
||||||
|
if (info) {
|
||||||
|
infos.push(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
///////////// Protected Helpers /////////////
|
///////////// Protected Helpers /////////////
|
||||||
|
|
||||||
@ -738,7 +766,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
if (isNamedDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
|
if (isNamedDeclaration(node)) {
|
||||||
name = node.name.text;
|
name = node.name.text;
|
||||||
nameNode = node.name;
|
nameNode = node.name;
|
||||||
} else {
|
} else {
|
||||||
@ -846,8 +874,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parameter type and decorators for a class where the information is stored on
|
* Get the parameter type and decorators for a class where the information is stored via
|
||||||
* in calls to `__decorate` helpers.
|
* calls to `__decorate` helpers.
|
||||||
*
|
*
|
||||||
* Reflect over the helpers to find the decorators and types about each of
|
* Reflect over the helpers to find the decorators and types about each of
|
||||||
* the class's constructor parameters.
|
* the class's constructor parameters.
|
||||||
@ -1002,9 +1030,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
* @param dtsProgram The program containing all the typings files.
|
* @param dtsProgram The program containing all the typings files.
|
||||||
* @returns a map of class names to class declarations.
|
* @returns a map of class names to class declarations.
|
||||||
*/
|
*/
|
||||||
protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program):
|
protected computeDtsDeclarationMap(dtsRootFileName: string, dtsProgram: ts.Program):
|
||||||
Map<string, ts.ClassDeclaration> {
|
Map<string, ts.Declaration> {
|
||||||
const dtsClassMap = new Map<string, ts.ClassDeclaration>();
|
const dtsDeclarationMap = new Map<string, ts.Declaration>();
|
||||||
const checker = dtsProgram.getTypeChecker();
|
const checker = dtsProgram.getTypeChecker();
|
||||||
|
|
||||||
// First add all the classes that are publicly exported from the entry-point
|
// First add all the classes that are publicly exported from the entry-point
|
||||||
@ -1012,13 +1040,38 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
if (!rootFile) {
|
if (!rootFile) {
|
||||||
throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`);
|
throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`);
|
||||||
}
|
}
|
||||||
collectExportedClasses(checker, dtsClassMap, rootFile);
|
collectExportedDeclarations(checker, dtsDeclarationMap, rootFile);
|
||||||
|
|
||||||
// Now add any additional classes that are exported from individual dts files,
|
// Now add any additional classes that are exported from individual dts files,
|
||||||
// but are not publicly exported from the entry-point.
|
// but are not publicly exported from the entry-point.
|
||||||
dtsProgram.getSourceFiles().forEach(
|
dtsProgram.getSourceFiles().forEach(
|
||||||
sourceFile => { collectExportedClasses(checker, dtsClassMap, sourceFile); });
|
sourceFile => { collectExportedDeclarations(checker, dtsDeclarationMap, sourceFile); });
|
||||||
return dtsClassMap;
|
return dtsDeclarationMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given node, to see if it is a function that returns a `ModuleWithProviders` object.
|
||||||
|
* @param node a node to check to see if it is a function that returns a `ModuleWithProviders`
|
||||||
|
* object.
|
||||||
|
* @returns info about the function if it does return a `ModuleWithProviders` object; `null`
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
protected parseForModuleWithProviders(node: ts.Node|null): ModuleWithProvidersFunction|null {
|
||||||
|
const declaration =
|
||||||
|
node && (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) ? node : null;
|
||||||
|
const body = declaration ? this.getDefinitionOfFunction(declaration).body : null;
|
||||||
|
const lastStatement = body && body[body.length - 1];
|
||||||
|
const returnExpression =
|
||||||
|
lastStatement && ts.isReturnStatement(lastStatement) && lastStatement.expression || null;
|
||||||
|
const ngModuleProperty = returnExpression && ts.isObjectLiteralExpression(returnExpression) &&
|
||||||
|
returnExpression.properties.find(
|
||||||
|
prop =>
|
||||||
|
!!prop.name && ts.isIdentifier(prop.name) && prop.name.text === 'ngModule') ||
|
||||||
|
null;
|
||||||
|
const ngModule = ngModuleProperty && ts.isPropertyAssignment(ngModuleProperty) &&
|
||||||
|
ts.isIdentifier(ngModuleProperty.initializer) && ngModuleProperty.initializer ||
|
||||||
|
null;
|
||||||
|
return ngModule && declaration && {ngModule, declaration};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1129,8 +1182,10 @@ function isThisAssignment(node: ts.Declaration): node is ts.BinaryExpression&
|
|||||||
node.left.expression.kind === ts.SyntaxKind.ThisKeyword;
|
node.left.expression.kind === ts.SyntaxKind.ThisKeyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration {
|
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration&
|
||||||
return !!(node as any).name;
|
{name: ts.Identifier} {
|
||||||
|
const anyNode: any = node;
|
||||||
|
return !!anyNode.name && ts.isIdentifier(anyNode.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1153,13 +1208,11 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search a source file for exported classes, storing them in the provided `dtsClassMap`.
|
* Collect mappings between exported declarations in a source file and its associated
|
||||||
* @param checker The typechecker for the source program.
|
* declaration in the typings program.
|
||||||
* @param dtsClassMap The map in which to store the collected exported classes.
|
|
||||||
* @param srcFile The source file to search for exported classes.
|
|
||||||
*/
|
*/
|
||||||
function collectExportedClasses(
|
function collectExportedDeclarations(
|
||||||
checker: ts.TypeChecker, dtsClassMap: Map<string, ts.ClassDeclaration>,
|
checker: ts.TypeChecker, dtsDeclarationMap: Map<string, ts.Declaration>,
|
||||||
srcFile: ts.SourceFile): void {
|
srcFile: ts.SourceFile): void {
|
||||||
const srcModule = srcFile && checker.getSymbolAtLocation(srcFile);
|
const srcModule = srcFile && checker.getSymbolAtLocation(srcFile);
|
||||||
const moduleExports = srcModule && checker.getExportsOfModule(srcModule);
|
const moduleExports = srcModule && checker.getExportsOfModule(srcModule);
|
||||||
@ -1170,8 +1223,8 @@ function collectExportedClasses(
|
|||||||
}
|
}
|
||||||
const declaration = exportedSymbol.valueDeclaration;
|
const declaration = exportedSymbol.valueDeclaration;
|
||||||
const name = exportedSymbol.name;
|
const name = exportedSymbol.name;
|
||||||
if (declaration && ts.isClassDeclaration(declaration) && !dtsClassMap.has(name)) {
|
if (declaration && !dtsDeclarationMap.has(name)) {
|
||||||
dtsClassMap.set(name, declaration);
|
dtsDeclarationMap.set(name, declaration);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,21 @@ export function isSwitchableVariableDeclaration(node: ts.Node):
|
|||||||
ts.isIdentifier(node.initializer) && node.initializer.text.endsWith(PRE_R3_MARKER);
|
ts.isIdentifier(node.initializer) && node.initializer.text.endsWith(PRE_R3_MARKER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure returned from `getModuleWithProviderInfo` that describes functions
|
||||||
|
* that return ModuleWithProviders objects.
|
||||||
|
*/
|
||||||
|
export interface ModuleWithProvidersFunction {
|
||||||
|
/**
|
||||||
|
* The declaration of the function that returns the `ModuleWithProviders` object.
|
||||||
|
*/
|
||||||
|
declaration: ts.SignatureDeclaration;
|
||||||
|
/**
|
||||||
|
* The identifier of the `ngModule` property on the `ModuleWithProviders` object.
|
||||||
|
*/
|
||||||
|
ngModule: ts.Identifier;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reflection host that has extra methods for looking at non-Typescript package formats
|
* A reflection host that has extra methods for looking at non-Typescript package formats
|
||||||
*/
|
*/
|
||||||
@ -45,4 +60,13 @@ export interface NgccReflectionHost extends ReflectionHost {
|
|||||||
* @returns An array of decorated classes.
|
* @returns An array of decorated classes.
|
||||||
*/
|
*/
|
||||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
|
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the given source file for exported functions and static class methods that return
|
||||||
|
* ModuleWithProviders objects.
|
||||||
|
* @param f The source file to search for these functions
|
||||||
|
* @returns An array of info items about each of the functions that return ModuleWithProviders
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[];
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {mkdir, mv} from 'shelljs';
|
|||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
|
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
|
||||||
|
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../analysis/module_with_providers_analyzer';
|
||||||
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
|
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
|
||||||
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
|
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
|
||||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
|
||||||
@ -25,6 +26,7 @@ import {EntryPoint} from './entry_point';
|
|||||||
import {EntryPointBundle} from './entry_point_bundle';
|
import {EntryPointBundle} from './entry_point_bundle';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Package is stored in a directory on disk and that directory can contain one or more package
|
* A Package is stored in a directory on disk and that directory can contain one or more package
|
||||||
* formats - e.g. fesm2015, UMD, etc. Additionally, each package provides typings (`.d.ts` files).
|
* formats - e.g. fesm2015, UMD, etc. Additionally, each package provides typings (`.d.ts` files).
|
||||||
@ -59,13 +61,14 @@ export class Transformer {
|
|||||||
const reflectionHost = this.getHost(isCore, bundle);
|
const reflectionHost = this.getHost(isCore, bundle);
|
||||||
|
|
||||||
// Parse and analyze the files.
|
// Parse and analyze the files.
|
||||||
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
this.analyzeProgram(reflectionHost, isCore, bundle);
|
moduleWithProvidersAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle);
|
||||||
|
|
||||||
// Transform the source files and source maps.
|
// Transform the source files and source maps.
|
||||||
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
|
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
|
||||||
const renderedFiles = renderer.renderProgram(
|
const renderedFiles = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
// Write out all the transformed files.
|
// Write out all the transformed files.
|
||||||
renderedFiles.forEach(file => this.writeFile(file));
|
renderedFiles.forEach(file => this.writeFile(file));
|
||||||
@ -102,16 +105,26 @@ export class Transformer {
|
|||||||
ProgramAnalyses {
|
ProgramAnalyses {
|
||||||
const typeChecker = bundle.src.program.getTypeChecker();
|
const typeChecker = bundle.src.program.getTypeChecker();
|
||||||
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
|
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
|
||||||
|
|
||||||
|
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
|
||||||
|
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
||||||
|
|
||||||
const decorationAnalyzer = new DecorationAnalyzer(
|
const decorationAnalyzer = new DecorationAnalyzer(
|
||||||
typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
|
typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
|
||||||
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
|
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
|
||||||
|
|
||||||
|
const moduleWithProvidersAnalyzer =
|
||||||
|
bundle.dts && new ModuleWithProvidersAnalyzer(reflectionHost, referencesRegistry);
|
||||||
|
const moduleWithProvidersAnalyses = moduleWithProvidersAnalyzer &&
|
||||||
|
moduleWithProvidersAnalyzer.analyzeProgram(bundle.src.program);
|
||||||
|
|
||||||
const privateDeclarationsAnalyzer =
|
const privateDeclarationsAnalyzer =
|
||||||
new PrivateDeclarationsAnalyzer(reflectionHost, referencesRegistry);
|
new PrivateDeclarationsAnalyzer(reflectionHost, referencesRegistry);
|
||||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
|
|
||||||
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
|
||||||
const privateDeclarationsAnalyses =
|
const privateDeclarationsAnalyses =
|
||||||
privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program);
|
privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program);
|
||||||
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
|
|
||||||
|
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses};
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFile(file: FileInfo): void {
|
writeFile(file: FileInfo): void {
|
||||||
@ -129,4 +142,5 @@ interface ProgramAnalyses {
|
|||||||
decorationAnalyses: Map<ts.SourceFile, CompiledFile>;
|
decorationAnalyses: Map<ts.SourceFile, CompiledFile>;
|
||||||
switchMarkerAnalyses: SwitchMarkerAnalyses;
|
switchMarkerAnalyses: SwitchMarkerAnalyses;
|
||||||
privateDeclarationsAnalyses: ExportInfo[];
|
privateDeclarationsAnalyses: ExportInfo[];
|
||||||
|
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null;
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,10 @@ import * as ts from 'typescript';
|
|||||||
|
|
||||||
import {Decorator} from '../../../ngtsc/host';
|
import {Decorator} from '../../../ngtsc/host';
|
||||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||||
import {translateStatement, translateType} from '../../../ngtsc/translator';
|
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
|
||||||
import {NgccImportManager} from './ngcc_import_manager';
|
import {NgccImportManager} from './ngcc_import_manager';
|
||||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||||
|
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||||
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||||
import {IMPORT_PREFIX} from '../constants';
|
import {IMPORT_PREFIX} from '../constants';
|
||||||
@ -49,6 +50,20 @@ interface DtsClassInfo {
|
|||||||
compilation: CompileResult[];
|
compilation: CompileResult[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure that captures information about what needs to be rendered
|
||||||
|
* in a typings file.
|
||||||
|
*
|
||||||
|
* It is created as a result of processing the analysis passed to the renderer.
|
||||||
|
*
|
||||||
|
* The `renderDtsFile()` method consumes it when rendering a typings file.
|
||||||
|
*/
|
||||||
|
class DtsRenderInfo {
|
||||||
|
classInfo: DtsClassInfo[] = [];
|
||||||
|
moduleWithProviders: ModuleWithProvidersInfo[] = [];
|
||||||
|
privateExports: ExportInfo[] = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The collected decorators that have become redundant after the compilation
|
* The collected decorators that have become redundant after the compilation
|
||||||
* of Ivy static fields. The map is keyed by the container node, such that we
|
* of Ivy static fields. The map is keyed by the container node, such that we
|
||||||
@ -71,7 +86,8 @@ export abstract class Renderer {
|
|||||||
|
|
||||||
renderProgram(
|
renderProgram(
|
||||||
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
||||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null): FileInfo[] {
|
||||||
const renderedFiles: FileInfo[] = [];
|
const renderedFiles: FileInfo[] = [];
|
||||||
|
|
||||||
// Transform the source files.
|
// Transform the source files.
|
||||||
@ -87,16 +103,16 @@ export abstract class Renderer {
|
|||||||
|
|
||||||
// Transform the .d.ts files
|
// Transform the .d.ts files
|
||||||
if (this.bundle.dts) {
|
if (this.bundle.dts) {
|
||||||
const dtsFiles = this.getTypingsFilesToRender(decorationAnalyses);
|
const dtsFiles = this.getTypingsFilesToRender(
|
||||||
|
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
// If the dts entry-point is not already there (it did not have compiled classes)
|
// If the dts entry-point is not already there (it did not have compiled classes)
|
||||||
// then add it now, to ensure it gets its extra exports rendered.
|
// then add it now, to ensure it gets its extra exports rendered.
|
||||||
if (!dtsFiles.has(this.bundle.dts.file)) {
|
if (!dtsFiles.has(this.bundle.dts.file)) {
|
||||||
dtsFiles.set(this.bundle.dts.file, []);
|
dtsFiles.set(this.bundle.dts.file, new DtsRenderInfo());
|
||||||
}
|
}
|
||||||
dtsFiles.forEach(
|
dtsFiles.forEach(
|
||||||
(classes, file) => renderedFiles.push(
|
(renderInfo, file) => renderedFiles.push(...this.renderDtsFile(file, renderInfo)));
|
||||||
...this.renderDtsFile(file, classes, privateDeclarationsAnalyses)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderedFiles;
|
return renderedFiles;
|
||||||
@ -151,14 +167,12 @@ export abstract class Renderer {
|
|||||||
return this.renderSourceAndMap(sourceFile, input, outputText);
|
return this.renderSourceAndMap(sourceFile, input, outputText);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDtsFile(
|
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
|
||||||
dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[],
|
|
||||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
|
||||||
const input = this.extractSourceMap(dtsFile);
|
const input = this.extractSourceMap(dtsFile);
|
||||||
const outputText = new MagicString(input.source);
|
const outputText = new MagicString(input.source);
|
||||||
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
|
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
|
||||||
|
|
||||||
dtsClasses.forEach(dtsClass => {
|
renderInfo.classInfo.forEach(dtsClass => {
|
||||||
const endOfClass = dtsClass.dtsDeclaration.getEnd();
|
const endOfClass = dtsClass.dtsDeclaration.getEnd();
|
||||||
dtsClass.compilation.forEach(declaration => {
|
dtsClass.compilation.forEach(declaration => {
|
||||||
const type = translateType(declaration.type, importManager);
|
const type = translateType(declaration.type, importManager);
|
||||||
@ -167,26 +181,67 @@ export abstract class Renderer {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
|
||||||
this.addImports(
|
this.addImports(
|
||||||
outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile));
|
outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile));
|
||||||
|
|
||||||
if (dtsFile === this.bundle.dts !.file) {
|
this.addExports(outputText, dtsFile.fileName, renderInfo.privateExports);
|
||||||
const dtsExports = privateDeclarationsAnalyses.map(e => {
|
|
||||||
if (!e.dtsFrom) {
|
|
||||||
throw new Error(
|
|
||||||
`There is no typings path for ${e.identifier} in ${e.from}.\n` +
|
|
||||||
`We need to add an export for this class to a .d.ts typings file because ` +
|
|
||||||
`Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` +
|
|
||||||
`The simplest fix for this is to ensure that this class is exported from the package's entry-point.`);
|
|
||||||
}
|
|
||||||
return {identifier: e.identifier, from: e.dtsFrom};
|
|
||||||
});
|
|
||||||
this.addExports(outputText, dtsFile.fileName, dtsExports);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.renderSourceAndMap(dtsFile, input, outputText);
|
return this.renderSourceAndMap(dtsFile, input, outputText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the type parameters to the appropriate functions that return `ModuleWithProviders`
|
||||||
|
* structures.
|
||||||
|
*
|
||||||
|
* This function only gets called on typings files, so it doesn't need different implementations
|
||||||
|
* for each bundle format.
|
||||||
|
*/
|
||||||
|
protected addModuleWithProvidersParams(
|
||||||
|
outputText: MagicString, moduleWithProviders: ModuleWithProvidersInfo[],
|
||||||
|
importManager: NgccImportManager): void {
|
||||||
|
moduleWithProviders.forEach(info => {
|
||||||
|
const ngModuleName = (info.ngModule.node as ts.ClassDeclaration).name !.text;
|
||||||
|
const declarationFile = info.declaration.getSourceFile().fileName;
|
||||||
|
const ngModuleFile = info.ngModule.node.getSourceFile().fileName;
|
||||||
|
const importPath = info.ngModule.viaModule ||
|
||||||
|
(declarationFile !== ngModuleFile ?
|
||||||
|
stripExtension(`./${relative(dirname(declarationFile), ngModuleFile)}`) :
|
||||||
|
null);
|
||||||
|
const ngModule = getImportString(importManager, importPath, ngModuleName);
|
||||||
|
|
||||||
|
if (info.declaration.type) {
|
||||||
|
const typeName = info.declaration.type && ts.isTypeReferenceNode(info.declaration.type) ?
|
||||||
|
info.declaration.type.typeName :
|
||||||
|
null;
|
||||||
|
if (this.isCoreModuleWithProvidersType(typeName)) {
|
||||||
|
// The declaration already returns `ModuleWithProvider` but it needs the `NgModule` type
|
||||||
|
// parameter adding.
|
||||||
|
outputText.overwrite(
|
||||||
|
info.declaration.type.getStart(), info.declaration.type.getEnd(),
|
||||||
|
`ModuleWithProviders<${ngModule}>`);
|
||||||
|
} else {
|
||||||
|
// The declaration returns an unknown type so we need to convert it to a union that
|
||||||
|
// includes the ngModule property.
|
||||||
|
const originalTypeString = info.declaration.type.getText();
|
||||||
|
outputText.overwrite(
|
||||||
|
info.declaration.type.getStart(), info.declaration.type.getEnd(),
|
||||||
|
`(${originalTypeString})&{ngModule:${ngModule}}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The declaration has no return type so provide one.
|
||||||
|
const lastToken = info.declaration.getLastToken();
|
||||||
|
const insertPoint = lastToken && lastToken.kind === ts.SyntaxKind.SemicolonToken ?
|
||||||
|
lastToken.getStart() :
|
||||||
|
info.declaration.getEnd();
|
||||||
|
outputText.appendLeft(
|
||||||
|
insertPoint,
|
||||||
|
`: ${getImportString(importManager, '@angular/core', 'ModuleWithProviders')}<${ngModule}>`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract addConstants(output: MagicString, constants: string, file: ts.SourceFile):
|
protected abstract addConstants(output: MagicString, constants: string, file: ts.SourceFile):
|
||||||
void;
|
void;
|
||||||
protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void;
|
protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void;
|
||||||
@ -302,22 +357,67 @@ export abstract class Renderer {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getTypingsFilesToRender(analyses: DecorationAnalyses):
|
protected getTypingsFilesToRender(
|
||||||
Map<ts.SourceFile, DtsClassInfo[]> {
|
decorationAnalyses: DecorationAnalyses,
|
||||||
const dtsMap = new Map<ts.SourceFile, DtsClassInfo[]>();
|
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||||
analyses.forEach(compiledFile => {
|
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|
|
||||||
|
null): Map<ts.SourceFile, DtsRenderInfo> {
|
||||||
|
const dtsMap = new Map<ts.SourceFile, DtsRenderInfo>();
|
||||||
|
|
||||||
|
// Capture the rendering info from the decoration analyses
|
||||||
|
decorationAnalyses.forEach(compiledFile => {
|
||||||
compiledFile.compiledClasses.forEach(compiledClass => {
|
compiledFile.compiledClasses.forEach(compiledClass => {
|
||||||
const dtsDeclaration = this.host.getDtsDeclarationOfClass(compiledClass.declaration);
|
const dtsDeclaration = this.host.getDtsDeclaration(compiledClass.declaration);
|
||||||
if (dtsDeclaration) {
|
if (dtsDeclaration) {
|
||||||
const dtsFile = dtsDeclaration.getSourceFile();
|
const dtsFile = dtsDeclaration.getSourceFile();
|
||||||
const classes = dtsMap.get(dtsFile) || [];
|
const renderInfo = dtsMap.get(dtsFile) || new DtsRenderInfo();
|
||||||
classes.push({dtsDeclaration, compilation: compiledClass.compilation});
|
renderInfo.classInfo.push({dtsDeclaration, compilation: compiledClass.compilation});
|
||||||
dtsMap.set(dtsFile, classes);
|
dtsMap.set(dtsFile, renderInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Capture the ModuleWithProviders functions/methods that need updating
|
||||||
|
if (moduleWithProvidersAnalyses !== null) {
|
||||||
|
moduleWithProvidersAnalyses.forEach((moduleWithProvidersToFix, dtsFile) => {
|
||||||
|
const renderInfo = dtsMap.get(dtsFile) || new DtsRenderInfo();
|
||||||
|
renderInfo.moduleWithProviders = moduleWithProvidersToFix;
|
||||||
|
dtsMap.set(dtsFile, renderInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture the private declarations that need to be re-exported
|
||||||
|
if (privateDeclarationsAnalyses.length) {
|
||||||
|
const dtsExports = privateDeclarationsAnalyses.map(e => {
|
||||||
|
if (!e.dtsFrom) {
|
||||||
|
throw new Error(
|
||||||
|
`There is no typings path for ${e.identifier} in ${e.from}.\n` +
|
||||||
|
`We need to add an export for this class to a .d.ts typings file because ` +
|
||||||
|
`Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` +
|
||||||
|
`The simplest fix for this is to ensure that this class is exported from the package's entry-point.`);
|
||||||
|
}
|
||||||
|
return {identifier: e.identifier, from: e.dtsFrom};
|
||||||
|
});
|
||||||
|
const dtsEntryPoint = this.bundle.dts !.file;
|
||||||
|
const renderInfo = dtsMap.get(dtsEntryPoint) || new DtsRenderInfo();
|
||||||
|
renderInfo.privateExports = dtsExports;
|
||||||
|
dtsMap.set(dtsEntryPoint, renderInfo);
|
||||||
|
}
|
||||||
|
|
||||||
return dtsMap;
|
return dtsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given type is the core Angular `ModuleWithProviders` interface.
|
||||||
|
* @param typeName The type to check.
|
||||||
|
* @returns true if the type is the core Angular `ModuleWithProviders` interface.
|
||||||
|
*/
|
||||||
|
private isCoreModuleWithProvidersType(typeName: ts.EntityName|null) {
|
||||||
|
const id =
|
||||||
|
typeName && ts.isIdentifier(typeName) ? this.host.getImportOfIdentifier(typeName) : null;
|
||||||
|
return (
|
||||||
|
id && id.name === 'ModuleWithProviders' && (this.isCore || id.from === '@angular/core'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -386,7 +486,7 @@ export function renderDefinitions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function stripExtension(filePath: string): string {
|
export function stripExtension(filePath: string): string {
|
||||||
return filePath.replace(/\.(js|d\.ts$)/, '');
|
return filePath.replace(/\.(js|d\.ts)$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -399,3 +499,9 @@ function createAssignmentStatement(
|
|||||||
const receiver = new WrappedNodeExpr(receiverName);
|
const receiver = new WrappedNodeExpr(receiverName);
|
||||||
return new WritePropExpr(receiver, propName, initializer).toStmt();
|
return new WritePropExpr(receiver, propName, initializer).toStmt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getImportString(
|
||||||
|
importManager: ImportManager, importPath: string | null, importName: string) {
|
||||||
|
const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null;
|
||||||
|
return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,401 @@
|
|||||||
|
/**
|
||||||
|
* @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 {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||||
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
|
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||||
|
import {BundleProgram} from '../../src/packages/bundle_program';
|
||||||
|
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||||
|
|
||||||
|
const TEST_PROGRAM = [
|
||||||
|
{
|
||||||
|
name: '/src/entry-point.js',
|
||||||
|
contents: `
|
||||||
|
export * from './explicit';
|
||||||
|
export * from './any';
|
||||||
|
export * from './implicit';
|
||||||
|
export * from './no-providers';
|
||||||
|
export * from './module';
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/explicit.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export class ExplicitInternalModule {}
|
||||||
|
export function explicitInternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: ExplicitInternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function explicitExternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function explicitLibraryFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class ExplicitClass {
|
||||||
|
static explicitInternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: ExplicitInternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static explicitExternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static explicitLibraryMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/any.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export class AnyInternalModule {}
|
||||||
|
export function anyInternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: AnyInternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function anyExternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function anyLibraryFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class AnyClass {
|
||||||
|
static anyInternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: AnyInternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static anyExternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static anyLibraryMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/implicit.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export class ImplicitInternalModule {}
|
||||||
|
export function implicitInternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: ImplicitInternalModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function implicitExternalFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function implicitLibraryFunction() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class ImplicitClass {
|
||||||
|
static implicitInternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: ImplicitInternalModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static implicitExternalMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: ExternalModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static implicitLibraryMethod() {
|
||||||
|
return {
|
||||||
|
ngModule: LibraryModule,
|
||||||
|
providers: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/no-providers.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export class NoProvidersInternalModule {}
|
||||||
|
export function noProvExplicitInternalFunction() {
|
||||||
|
return {ngModule: NoProvidersInternalModule};
|
||||||
|
}
|
||||||
|
export function noProvExplicitExternalFunction() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
export function noProvExplicitLibraryFunction() {
|
||||||
|
return {ngModule: LibraryModule};
|
||||||
|
}
|
||||||
|
export function noProvAnyInternalFunction() {
|
||||||
|
return {ngModule: NoProvidersInternalModule};
|
||||||
|
}
|
||||||
|
export function noProvAnyExternalFunction() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
export function noProvAnyLibraryFunction() {
|
||||||
|
return {ngModule: LibraryModule};
|
||||||
|
}
|
||||||
|
export function noProvImplicitInternalFunction() {
|
||||||
|
return {ngModule: NoProvidersInternalModule};
|
||||||
|
}
|
||||||
|
export function noProvImplicitExternalFunction() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
export function noProvImplicitLibraryFunction() {
|
||||||
|
return {ngModule: LibraryModule};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/module.js',
|
||||||
|
contents: `
|
||||||
|
export class ExternalModule {}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/node_modules/some-library/index.d.ts',
|
||||||
|
contents: 'export declare class LibraryModule {}'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const TEST_DTS_PROGRAM = [
|
||||||
|
{
|
||||||
|
name: '/typings/entry-point.d.ts',
|
||||||
|
contents: `
|
||||||
|
export * from './explicit';
|
||||||
|
export * from './any';
|
||||||
|
export * from './implicit';
|
||||||
|
export * from './no-providers';
|
||||||
|
export * from './module';
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/explicit.d.ts',
|
||||||
|
contents: `
|
||||||
|
import {ModuleWithProviders} from './core';
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export declare class ExplicitInternalModule {}
|
||||||
|
export declare function explicitInternalFunction(): ModuleWithProviders<ExplicitInternalModule>;
|
||||||
|
export declare function explicitExternalFunction(): ModuleWithProviders<ExternalModule>;
|
||||||
|
export declare function explicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
|
||||||
|
export declare class ExplicitClass {
|
||||||
|
static explicitInternalMethod(): ModuleWithProviders<ExplicitInternalModule>;
|
||||||
|
static explicitExternalMethod(): ModuleWithProviders<ExternalModule>;
|
||||||
|
static explicitLibraryMethod(): ModuleWithProviders<LibraryModule>;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/any.d.ts',
|
||||||
|
contents: `
|
||||||
|
import {ModuleWithProviders} from './core';
|
||||||
|
export declare class AnyInternalModule {}
|
||||||
|
export declare function anyInternalFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare function anyExternalFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare function anyLibraryFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare class AnyClass {
|
||||||
|
static anyInternalMethod(): ModuleWithProviders<any>;
|
||||||
|
static anyExternalMethod(): ModuleWithProviders<any>;
|
||||||
|
static anyLibraryMethod(): ModuleWithProviders<any>;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/implicit.d.ts',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export declare class ImplicitInternalModule {}
|
||||||
|
export declare function implicitInternalFunction(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
|
||||||
|
export declare function implicitExternalFunction(): { ngModule: typeof ExternalModule; providers: never[]; };
|
||||||
|
export declare function implicitLibraryFunction(): { ngModule: typeof LibraryModule; providers: never[]; };
|
||||||
|
export declare class ImplicitClass {
|
||||||
|
static implicitInternalMethod(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
|
||||||
|
static implicitExternalMethod(): { ngModule: typeof ExternalModule; providers: never[]; };
|
||||||
|
static implicitLibraryMethod(): { ngModule: typeof LibraryModule; providers: never[]; };
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/no-providers.d.ts',
|
||||||
|
contents: `
|
||||||
|
import {ModuleWithProviders} from './core';
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export declare class NoProvidersInternalModule {}
|
||||||
|
export declare function noProvExplicitInternalFunction(): ModuleWithProviders<NoProvidersInternalModule>;
|
||||||
|
export declare function noProvExplicitExternalFunction(): ModuleWithProviders<ExternalModule>;
|
||||||
|
export declare function noProvExplicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
|
||||||
|
export declare function noProvAnyInternalFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare function noProvAnyExternalFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare function noProvAnyLibraryFunction(): ModuleWithProviders<any>;
|
||||||
|
export declare function noProvImplicitInternalFunction(): { ngModule: typeof NoProvidersInternalModule; };
|
||||||
|
export declare function noProvImplicitExternalFunction(): { ngModule: typeof ExternalModule; };
|
||||||
|
export declare function noProvImplicitLibraryFunction(): { ngModule: typeof LibraryModule; };
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/module.d.ts',
|
||||||
|
contents: `
|
||||||
|
export declare class ExternalModule {}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/core.d.ts',
|
||||||
|
contents: `
|
||||||
|
|
||||||
|
export declare interface Type<T> {
|
||||||
|
new (...args: any[]): T
|
||||||
|
}
|
||||||
|
export declare type Provider = any;
|
||||||
|
export declare interface ModuleWithProviders<T> {
|
||||||
|
ngModule: Type<T>
|
||||||
|
providers?: Provider[]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/node_modules/some-library/index.d.ts',
|
||||||
|
contents: 'export declare class LibraryModule {}'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('ModuleWithProvidersAnalyzer', () => {
|
||||||
|
describe('analyzeProgram()', () => {
|
||||||
|
let analyses: ModuleWithProvidersAnalyses;
|
||||||
|
let program: ts.Program;
|
||||||
|
let dtsProgram: BundleProgram;
|
||||||
|
let referencesRegistry: NgccReferencesRegistry;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
program = makeTestProgram(...TEST_PROGRAM);
|
||||||
|
dtsProgram = makeTestBundleProgram(TEST_DTS_PROGRAM);
|
||||||
|
const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dtsProgram);
|
||||||
|
referencesRegistry = new NgccReferencesRegistry(host);
|
||||||
|
|
||||||
|
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry);
|
||||||
|
analyses = analyzer.analyzeProgram(program);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore declarations that already have explicit NgModule type params',
|
||||||
|
() => { expect(getAnalysisDescription(analyses, '/typings/explicit.d.ts')).toEqual([]); });
|
||||||
|
|
||||||
|
it('should find declarations that use `any` for the NgModule type param', () => {
|
||||||
|
const anyAnalysis = getAnalysisDescription(analyses, '/typings/any.d.ts');
|
||||||
|
expect(anyAnalysis).toContain(['anyInternalFunction', 'AnyInternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['anyExternalFunction', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['anyLibraryFunction', 'LibraryModule', 'some-library']);
|
||||||
|
expect(anyAnalysis).toContain(['anyInternalMethod', 'AnyInternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['anyExternalMethod', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['anyLibraryMethod', 'LibraryModule', 'some-library']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should track internal module references in the references registry', () => {
|
||||||
|
const declarations = referencesRegistry.getDeclarationMap();
|
||||||
|
const externalModuleDeclaration =
|
||||||
|
getDeclaration(program, '/src/module.js', 'ExternalModule', ts.isClassDeclaration);
|
||||||
|
const libraryModuleDeclaration = getDeclaration(
|
||||||
|
program, '/node_modules/some-library/index.d.ts', 'LibraryModule', ts.isClassDeclaration);
|
||||||
|
expect(declarations.has(externalModuleDeclaration.name !)).toBe(true);
|
||||||
|
expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find declarations that have implicit return types', () => {
|
||||||
|
const anyAnalysis = getAnalysisDescription(analyses, '/typings/implicit.d.ts');
|
||||||
|
expect(anyAnalysis).toContain(['implicitInternalFunction', 'ImplicitInternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['implicitExternalFunction', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['implicitLibraryFunction', 'LibraryModule', 'some-library']);
|
||||||
|
expect(anyAnalysis).toContain(['implicitInternalMethod', 'ImplicitInternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['implicitExternalMethod', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain(['implicitLibraryMethod', 'LibraryModule', 'some-library']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find declarations that do not specify a `providers` property in the return type',
|
||||||
|
() => {
|
||||||
|
const anyAnalysis = getAnalysisDescription(analyses, '/typings/no-providers.d.ts');
|
||||||
|
expect(anyAnalysis).not.toContain([
|
||||||
|
'noProvExplicitInternalFunction', 'NoProvidersInternalModule'
|
||||||
|
]);
|
||||||
|
expect(anyAnalysis).not.toContain([
|
||||||
|
'noProvExplicitExternalFunction', 'ExternalModule', null
|
||||||
|
]);
|
||||||
|
expect(anyAnalysis).toContain([
|
||||||
|
'noProvAnyInternalFunction', 'NoProvidersInternalModule', null
|
||||||
|
]);
|
||||||
|
expect(anyAnalysis).toContain(['noProvAnyExternalFunction', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain([
|
||||||
|
'noProvAnyLibraryFunction', 'LibraryModule', 'some-library'
|
||||||
|
]);
|
||||||
|
expect(anyAnalysis).toContain([
|
||||||
|
'noProvImplicitInternalFunction', 'NoProvidersInternalModule', null
|
||||||
|
]);
|
||||||
|
expect(anyAnalysis).toContain(['noProvImplicitExternalFunction', 'ExternalModule', null]);
|
||||||
|
expect(anyAnalysis).toContain([
|
||||||
|
'noProvImplicitLibraryFunction', 'LibraryModule', 'some-library'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getAnalysisDescription(analyses: ModuleWithProvidersAnalyses, fileName: string) {
|
||||||
|
const file = dtsProgram.program.getSourceFile(fileName) !;
|
||||||
|
const analysis = analyses.get(file);
|
||||||
|
return analysis ?
|
||||||
|
analysis.map(
|
||||||
|
info =>
|
||||||
|
[info.declaration.name !.getText(),
|
||||||
|
(info.ngModule.node as ts.ClassDeclaration).name !.getText(),
|
||||||
|
info.ngModule.viaModule]) :
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -84,6 +84,8 @@ export function getFakeCore() {
|
|||||||
export class InjectionToken {
|
export class InjectionToken {
|
||||||
constructor(name: string) {}
|
constructor(name: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ModuleWithProviders<T = any> {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -453,6 +453,7 @@ const TYPINGS_SRC_FILES = [
|
|||||||
},
|
},
|
||||||
{name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'},
|
{name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'},
|
||||||
{name: '/src/class2.js', contents: 'export class Class2 {}'},
|
{name: '/src/class2.js', contents: 'export class Class2 {}'},
|
||||||
|
{name: '/src/func1.js', contents: 'export function mooFn() {}'},
|
||||||
{name: '/src/internal.js', contents: 'export class InternalClass {}\nexport class Class2 {}'},
|
{name: '/src/internal.js', contents: 'export class InternalClass {}\nexport class Class2 {}'},
|
||||||
{name: '/src/missing-class.js', contents: 'export class MissingClass2 {}'}, {
|
{name: '/src/missing-class.js', contents: 'export class MissingClass2 {}'}, {
|
||||||
name: '/src/flat-file.js',
|
name: '/src/flat-file.js',
|
||||||
@ -476,6 +477,7 @@ const TYPINGS_DTS_FILES = [
|
|||||||
contents:
|
contents:
|
||||||
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
|
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
|
||||||
},
|
},
|
||||||
|
{name: '/typings/func1.d.ts', contents: 'export declare function mooFn(): void;'},
|
||||||
{
|
{
|
||||||
name: '/typings/internal.d.ts',
|
name: '/typings/internal.d.ts',
|
||||||
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
|
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
|
||||||
@ -483,6 +485,54 @@ const TYPINGS_DTS_FILES = [
|
|||||||
{name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
|
{name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||||
|
{
|
||||||
|
name: '/src/functions.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
export class SomeService {}
|
||||||
|
export class InternalModule {}
|
||||||
|
export function aNumber() { return 42; }
|
||||||
|
export function aString() { return 'foo'; }
|
||||||
|
export function emptyObject() { return {}; }
|
||||||
|
export function ngModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||||
|
export function ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||||
|
export function ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||||
|
export function onlyProviders() { return { providers: [SomeService] }; }
|
||||||
|
export function ngModuleNumber() { return { ngModule: 42 }; }
|
||||||
|
export function ngModuleString() { return { ngModule: 'foo' }; }
|
||||||
|
export function ngModuleObject() { return { ngModule: { foo: 42 } }; }
|
||||||
|
export function externalNgModule() { return { ngModule: ExternalModule }; }
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/methods.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
export class SomeService {}
|
||||||
|
export class InternalModule {
|
||||||
|
static aNumber() { return 42; }
|
||||||
|
static aString() { return 'foo'; }
|
||||||
|
static emptyObject() { return {}; }
|
||||||
|
static ngModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||||
|
static ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||||
|
static ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||||
|
static onlyProviders() { return { providers: [SomeService] }; }
|
||||||
|
static ngModuleNumber() { return { ngModule: 42 }; }
|
||||||
|
static ngModuleString() { return { ngModule: 'foo' }; }
|
||||||
|
static ngModuleObject() { return { ngModule: { foo: 42 } }; }
|
||||||
|
static externalNgModule() { return { ngModule: ExternalModule }; }
|
||||||
|
|
||||||
|
instanceNgModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||||
|
instanceNgModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||||
|
instanceNgModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||||
|
instanceExternalNgModule() { return { ngModule: ExternalModule }; }
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{name: '/src/module', contents: 'export class ExternalModule {}'},
|
||||||
|
];
|
||||||
|
|
||||||
describe('Fesm2015ReflectionHost', () => {
|
describe('Fesm2015ReflectionHost', () => {
|
||||||
|
|
||||||
describe('getDecoratorsOfDeclaration()', () => {
|
describe('getDecoratorsOfDeclaration()', () => {
|
||||||
@ -1286,10 +1336,20 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
|
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
|
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find the dts declaration for exported functions', () => {
|
||||||
|
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||||
|
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||||
|
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
|
||||||
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
|
||||||
|
|
||||||
|
const dtsDeclaration = host.getDtsDeclaration(mooFn);
|
||||||
|
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/func1.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
it('should return null if there is no matching class in the matching dts file', () => {
|
it('should return null if there is no matching class in the matching dts file', () => {
|
||||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||||
@ -1297,7 +1357,7 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
|
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
|
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null if there is no matching dts file', () => {
|
it('should return null if there is no matching dts file', () => {
|
||||||
@ -1307,7 +1367,7 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
|
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
|
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
|
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
|
||||||
@ -1318,7 +1378,7 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
|
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
|
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1329,7 +1389,7 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
|
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class3);
|
const dtsDeclaration = host.getDtsDeclaration(class3);
|
||||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
|
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1341,7 +1401,7 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
|
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass);
|
const dtsDeclaration = host.getDtsDeclaration(internalClass);
|
||||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
|
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1355,12 +1415,42 @@ describe('Fesm2015ReflectionHost', () => {
|
|||||||
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
|
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
|
||||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||||
|
|
||||||
const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2);
|
const class2DtsDeclaration = host.getDtsDeclaration(class2);
|
||||||
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
|
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
|
||||||
|
|
||||||
const internalClass2DtsDeclaration = host.getDtsDeclarationOfClass(internalClass2);
|
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
|
||||||
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
|
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
|
||||||
.toEqual('/typings/class2.d.ts');
|
.toEqual('/typings/class2.d.ts');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getModuleWithProvidersFunctions', () => {
|
||||||
|
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
|
||||||
|
() => {
|
||||||
|
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||||
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker());
|
||||||
|
const file = srcProgram.getSourceFile('/src/functions.js') !;
|
||||||
|
const fns = host.getModuleWithProvidersFunctions(file);
|
||||||
|
expect(fns.map(info => [info.declaration.name !.getText(), info.ngModule.text])).toEqual([
|
||||||
|
['ngModuleIdentifier', 'InternalModule'],
|
||||||
|
['ngModuleWithEmptyProviders', 'InternalModule'],
|
||||||
|
['ngModuleWithProviders', 'InternalModule'],
|
||||||
|
['externalNgModule', 'ExternalModule'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find every static method on exported classes that return an object that looks like a ModuleWithProviders object',
|
||||||
|
() => {
|
||||||
|
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||||
|
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker());
|
||||||
|
const file = srcProgram.getSourceFile('/src/methods.js') !;
|
||||||
|
const fn = host.getModuleWithProvidersFunctions(file);
|
||||||
|
expect(fn.map(fn => [fn.declaration.name !.getText(), fn.ngModule.text])).toEqual([
|
||||||
|
['ngModuleIdentifier', 'InternalModule'],
|
||||||
|
['ngModuleWithEmptyProviders', 'InternalModule'],
|
||||||
|
['ngModuleWithProviders', 'InternalModule'],
|
||||||
|
['externalNgModule', 'ExternalModule'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ import * as ts from 'typescript';
|
|||||||
import {fromObject, generateMapFileComment} from 'convert-source-map';
|
import {fromObject, generateMapFileComment} from 'convert-source-map';
|
||||||
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
|
import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||||
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
||||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||||
@ -47,9 +48,9 @@ class TestRenderer extends Renderer {
|
|||||||
|
|
||||||
function createTestRenderer(
|
function createTestRenderer(
|
||||||
packageName: string, files: {name: string, contents: string}[],
|
packageName: string, files: {name: string, contents: string}[],
|
||||||
dtsFile?: {name: string, contents: string}) {
|
dtsFiles?: {name: string, contents: string}[]) {
|
||||||
const isCore = packageName === '@angular/core';
|
const isCore = packageName === '@angular/core';
|
||||||
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFile && [dtsFile]);
|
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFiles);
|
||||||
const typeChecker = bundle.src.program.getTypeChecker();
|
const typeChecker = bundle.src.program.getTypeChecker();
|
||||||
const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
|
const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
|
||||||
const referencesRegistry = new NgccReferencesRegistry(host);
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||||
@ -57,6 +58,8 @@ function createTestRenderer(
|
|||||||
new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
|
new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
|
||||||
.analyzeProgram(bundle.src.program);
|
.analyzeProgram(bundle.src.program);
|
||||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||||
|
const moduleWithProvidersAnalyses =
|
||||||
|
new ModuleWithProvidersAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||||
const privateDeclarationsAnalyses =
|
const privateDeclarationsAnalyses =
|
||||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||||
const renderer = new TestRenderer(host, isCore, bundle);
|
const renderer = new TestRenderer(host, isCore, bundle);
|
||||||
@ -64,7 +67,8 @@ function createTestRenderer(
|
|||||||
spyOn(renderer, 'addDefinitions').and.callThrough();
|
spyOn(renderer, 'addDefinitions').and.callThrough();
|
||||||
spyOn(renderer, 'removeDecorators').and.callThrough();
|
spyOn(renderer, 'removeDecorators').and.callThrough();
|
||||||
|
|
||||||
return {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
|
return {renderer, decorationAnalyses, switchMarkerAnalyses, moduleWithProvidersAnalyses,
|
||||||
|
privateDeclarationsAnalyses};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -121,10 +125,11 @@ describe('Renderer', () => {
|
|||||||
describe('renderProgram()', () => {
|
describe('renderProgram()', () => {
|
||||||
it('should render the modified contents; and a new map file, if the original provided no map file.',
|
it('should render the modified contents; and a new map file, if the original provided no map file.',
|
||||||
() => {
|
() => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
expect(result[0].path).toEqual('/dist/file.js');
|
expect(result[0].path).toEqual('/dist/file.js');
|
||||||
expect(result[0].contents)
|
expect(result[0].contents)
|
||||||
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
||||||
@ -134,9 +139,11 @@ describe('Renderer', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should render as JavaScript', () => {
|
it('should render as JavaScript', () => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('test-package', [COMPONENT_PROGRAM]);
|
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [COMPONENT_PROGRAM]);
|
||||||
renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
renderer.renderProgram(
|
||||||
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||||
expect(addDefinitionsSpy.calls.first().args[2])
|
expect(addDefinitionsSpy.calls.first().args[2])
|
||||||
.toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{
|
.toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{
|
||||||
@ -154,10 +161,12 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
|
|||||||
describe('calling abstract methods', () => {
|
describe('calling abstract methods', () => {
|
||||||
it('should call addImports with the source code and info about the core Angular library.',
|
it('should call addImports with the source code and info about the core Angular library.',
|
||||||
() => {
|
() => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||||
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||||
expect(addImportsSpy.calls.first().args[1]).toEqual([
|
expect(addImportsSpy.calls.first().args[1]).toEqual([
|
||||||
@ -167,10 +176,12 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
|
|||||||
|
|
||||||
it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.',
|
it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.',
|
||||||
() => {
|
() => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||||
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||||
expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({
|
expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({
|
||||||
@ -187,10 +198,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
|
|
||||||
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
|
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
|
||||||
() => {
|
() => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
|
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
|
||||||
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||||
|
|
||||||
@ -212,14 +225,16 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
describe('source map merging', () => {
|
describe('source map merging', () => {
|
||||||
it('should merge any inline source map from the original file and write the output as an inline source map',
|
it('should merge any inline source map from the original file and write the output as an inline source map',
|
||||||
() => {
|
() => {
|
||||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer(
|
createTestRenderer(
|
||||||
'test-package', [{
|
'test-package', [{
|
||||||
...INPUT_PROGRAM,
|
...INPUT_PROGRAM,
|
||||||
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
|
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
|
||||||
}]);
|
}]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
expect(result[0].path).toEqual('/dist/file.js');
|
expect(result[0].path).toEqual('/dist/file.js');
|
||||||
expect(result[0].contents)
|
expect(result[0].contents)
|
||||||
.toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment());
|
.toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment());
|
||||||
@ -230,14 +245,16 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
() => {
|
() => {
|
||||||
// Mock out reading the map file from disk
|
// Mock out reading the map file from disk
|
||||||
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
|
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
|
||||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer(
|
createTestRenderer(
|
||||||
'test-package', [{
|
'test-package', [{
|
||||||
...INPUT_PROGRAM,
|
...INPUT_PROGRAM,
|
||||||
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
|
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
|
||||||
}]);
|
}]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
expect(result[0].path).toEqual('/dist/file.js');
|
expect(result[0].path).toEqual('/dist/file.js');
|
||||||
expect(result[0].contents)
|
expect(result[0].contents)
|
||||||
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
||||||
@ -259,10 +276,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
contents: `export const NgModule = () => null;`
|
contents: `export const NgModule = () => null;`
|
||||||
};
|
};
|
||||||
// The package name of `@angular/core` indicates that we are compiling the core library.
|
// The package name of `@angular/core` indicates that we are compiling the core library.
|
||||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]);
|
createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]);
|
||||||
renderer.renderProgram(
|
renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||||
expect(addDefinitionsSpy.calls.first().args[2])
|
expect(addDefinitionsSpy.calls.first().args[2])
|
||||||
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
|
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
|
||||||
@ -277,10 +296,11 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
|
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
|
||||||
};
|
};
|
||||||
|
|
||||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('@angular/core', [CORE_FILE]);
|
moduleWithProvidersAnalyses} = createTestRenderer('@angular/core', [CORE_FILE]);
|
||||||
renderer.renderProgram(
|
renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||||
expect(addDefinitionsSpy.calls.first().args[2])
|
expect(addDefinitionsSpy.calls.first().args[2])
|
||||||
.toContain(`/*@__PURE__*/ setClassMetadata(`);
|
.toContain(`/*@__PURE__*/ setClassMetadata(`);
|
||||||
@ -291,10 +311,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
|
|
||||||
describe('rendering typings', () => {
|
describe('rendering typings', () => {
|
||||||
it('should render extract types into typings files', () => {
|
it('should render extract types into typings files', () => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
moduleWithProvidersAnalyses} =
|
||||||
|
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||||
expect(typingsFile.contents)
|
expect(typingsFile.contents)
|
||||||
@ -303,30 +325,195 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render imports into typings files', () => {
|
it('should render imports into typings files', () => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
moduleWithProvidersAnalyses} =
|
||||||
|
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||||
expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`);
|
expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render exports into typings files', () => {
|
it('should render exports into typings files', () => {
|
||||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
moduleWithProvidersAnalyses} =
|
||||||
|
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||||
|
|
||||||
// Add a mock export to trigger export rendering
|
// Add a mock export to trigger export rendering
|
||||||
privateDeclarationsAnalyses.push(
|
privateDeclarationsAnalyses.push(
|
||||||
{identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'});
|
{identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'});
|
||||||
|
|
||||||
const result = renderer.renderProgram(
|
const result = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||||
expect(typingsFile.contents)
|
expect(typingsFile.contents)
|
||||||
.toContain(`// ADD EXPORTS\n\n// ADD IMPORTS\nexport declare class A`);
|
.toContain(`// ADD EXPORTS\n\n// ADD IMPORTS\nexport declare class A`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
||||||
|
const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||||
|
{
|
||||||
|
name: '/src/index.js',
|
||||||
|
contents: `
|
||||||
|
import {ExternalModule} from './module';
|
||||||
|
import {LibraryModule} from 'some-library';
|
||||||
|
export class SomeClass {}
|
||||||
|
export class SomeModule {
|
||||||
|
static withProviders1() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}
|
||||||
|
static withProviders2() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}
|
||||||
|
static withProviders3() {
|
||||||
|
return {ngModule: SomeClass};
|
||||||
|
}
|
||||||
|
static withProviders4() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
static withProviders5() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
static withProviders6() {
|
||||||
|
return {ngModule: LibraryModule};
|
||||||
|
}
|
||||||
|
static withProviders7() {
|
||||||
|
return {ngModule: SomeModule, providers: []};
|
||||||
|
};
|
||||||
|
static withProviders8() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function withProviders1() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}
|
||||||
|
export function withProviders2() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}
|
||||||
|
export function withProviders3() {
|
||||||
|
return {ngModule: SomeClass};
|
||||||
|
}
|
||||||
|
export function withProviders4() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
export function withProviders5() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
export function withProviders6() {
|
||||||
|
return {ngModule: LibraryModule};
|
||||||
|
}
|
||||||
|
export function withProviders7() {
|
||||||
|
return {ngModule: SomeModule, providers: []};
|
||||||
|
};
|
||||||
|
export function withProviders8() {
|
||||||
|
return {ngModule: SomeModule};
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/src/module.js',
|
||||||
|
contents: `
|
||||||
|
export class ExternalModule {
|
||||||
|
static withProviders1() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
static withProviders2() {
|
||||||
|
return {ngModule: ExternalModule};
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/node_modules/some-library/index.d.ts',
|
||||||
|
contents: 'export declare class LibraryModule {}'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const MODULE_WITH_PROVIDERS_DTS_PROGRAM = [
|
||||||
|
{
|
||||||
|
name: '/typings/index.d.ts',
|
||||||
|
contents: `
|
||||||
|
import {ModuleWithProviders} from '@angular/core';
|
||||||
|
export declare class SomeClass {}
|
||||||
|
export interface MyModuleWithProviders extends ModuleWithProviders {}
|
||||||
|
export declare class SomeModule {
|
||||||
|
static withProviders1(): ModuleWithProviders;
|
||||||
|
static withProviders2(): ModuleWithProviders<any>;
|
||||||
|
static withProviders3(): ModuleWithProviders<SomeClass>;
|
||||||
|
static withProviders4(): ModuleWithProviders;
|
||||||
|
static withProviders5();
|
||||||
|
static withProviders6(): ModuleWithProviders;
|
||||||
|
static withProviders7(): {ngModule: SomeModule, providers: any[]};
|
||||||
|
static withProviders8(): MyModuleWithProviders;
|
||||||
|
}
|
||||||
|
export declare function withProviders1(): ModuleWithProviders;
|
||||||
|
export declare function withProviders2(): ModuleWithProviders<any>;
|
||||||
|
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
||||||
|
export declare function withProviders4(): ModuleWithProviders;
|
||||||
|
export declare function withProviders5();
|
||||||
|
export declare function withProviders6(): ModuleWithProviders;
|
||||||
|
export declare function withProviders7(): {ngModule: SomeModule, providers: any[]};
|
||||||
|
export declare function withProviders8(): MyModuleWithProviders;`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/typings/module.d.ts',
|
||||||
|
contents: `
|
||||||
|
export interface ModuleWithProviders {}
|
||||||
|
export declare class ExternalModule {
|
||||||
|
static withProviders1(): ModuleWithProviders;
|
||||||
|
static withProviders2(): ModuleWithProviders;
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '/node_modules/some-library/index.d.ts',
|
||||||
|
contents: 'export declare class LibraryModule {}'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} =
|
||||||
|
createTestRenderer(
|
||||||
|
'test-package', MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
||||||
|
|
||||||
|
const result = renderer.renderProgram(
|
||||||
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
|
const typingsFile = result.find(f => f.path === '/typings/index.d.ts') !;
|
||||||
|
|
||||||
|
expect(typingsFile.contents).toContain(`
|
||||||
|
static withProviders1(): ModuleWithProviders<SomeModule>;
|
||||||
|
static withProviders2(): ModuleWithProviders<SomeModule>;
|
||||||
|
static withProviders3(): ModuleWithProviders<SomeClass>;
|
||||||
|
static withProviders4(): ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||||
|
static withProviders5(): ɵngcc1.ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||||
|
static withProviders6(): ModuleWithProviders<ɵngcc2.LibraryModule>;
|
||||||
|
static withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
||||||
|
static withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
||||||
|
expect(typingsFile.contents).toContain(`
|
||||||
|
export declare function withProviders1(): ModuleWithProviders<SomeModule>;
|
||||||
|
export declare function withProviders2(): ModuleWithProviders<SomeModule>;
|
||||||
|
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
||||||
|
export declare function withProviders4(): ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||||
|
export declare function withProviders5(): ɵngcc1.ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||||
|
export declare function withProviders6(): ModuleWithProviders<ɵngcc2.LibraryModule>;
|
||||||
|
export declare function withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
||||||
|
export declare function withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
||||||
|
|
||||||
|
expect(renderer.addImports).toHaveBeenCalledWith(jasmine.any(MagicString), [
|
||||||
|
{name: './module', as: 'ɵngcc0'},
|
||||||
|
{name: '@angular/core', as: 'ɵngcc1'},
|
||||||
|
{name: 'some-library', as: 'ɵngcc2'},
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// The following expectation checks that we do not mistake `ModuleWithProviders` types
|
||||||
|
// that are not imported from `@angular/core`.
|
||||||
|
const typingsFile2 = result.find(f => f.path === '/typings/module.d.ts') !;
|
||||||
|
expect(typingsFile2.contents).toContain(`
|
||||||
|
static withProviders1(): (ModuleWithProviders)&{ngModule:ExternalModule};
|
||||||
|
static withProviders2(): (ModuleWithProviders)&{ngModule:ExternalModule};`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -109,7 +109,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||||||
const valueContext = node.getSourceFile();
|
const valueContext = node.getSourceFile();
|
||||||
|
|
||||||
let typeContext = valueContext;
|
let typeContext = valueContext;
|
||||||
const typeNode = this.reflector.getDtsDeclarationOfClass(node);
|
const typeNode = this.reflector.getDtsDeclaration(node);
|
||||||
if (typeNode !== null) {
|
if (typeNode !== null) {
|
||||||
typeContext = typeNode.getSourceFile();
|
typeContext = typeNode.getSourceFile();
|
||||||
}
|
}
|
||||||
@ -183,8 +183,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||||||
return toR3Reference(valueRef, valueRef, valueContext, valueContext);
|
return toR3Reference(valueRef, valueRef, valueContext, valueContext);
|
||||||
} else {
|
} else {
|
||||||
let typeRef = valueRef;
|
let typeRef = valueRef;
|
||||||
let typeNode = this.reflector.getDtsDeclarationOfClass(typeRef.node);
|
let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
|
||||||
if (typeNode !== null) {
|
if (typeNode !== null && ts.isClassDeclaration(typeNode)) {
|
||||||
typeRef = new ResolvedReference(typeNode, typeNode.name !);
|
typeRef = new ResolvedReference(typeNode, typeNode.name !);
|
||||||
}
|
}
|
||||||
return toR3Reference(valueRef, typeRef, valueContext, typeContext);
|
return toR3Reference(valueRef, typeRef, valueContext, typeContext);
|
||||||
@ -197,9 +197,20 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||||||
*/
|
*/
|
||||||
private _extractModuleFromModuleWithProvidersFn(node: ts.FunctionDeclaration|
|
private _extractModuleFromModuleWithProvidersFn(node: ts.FunctionDeclaration|
|
||||||
ts.MethodDeclaration): ts.Expression|null {
|
ts.MethodDeclaration): ts.Expression|null {
|
||||||
const type = node.type;
|
const type = node.type || null;
|
||||||
|
return type &&
|
||||||
|
(this._reflectModuleFromTypeParam(type) || this._reflectModuleFromLiteralType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
|
||||||
|
* `ModuleWithProviders<T>`
|
||||||
|
* @param type The type to reflect on.
|
||||||
|
* @returns the identifier of the NgModule type if found, or null otherwise.
|
||||||
|
*/
|
||||||
|
private _reflectModuleFromTypeParam(type: ts.TypeNode): ts.Expression|null {
|
||||||
// Examine the type of the function to see if it's a ModuleWithProviders reference.
|
// Examine the type of the function to see if it's a ModuleWithProviders reference.
|
||||||
if (type === undefined || !ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
|
if (!ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,6 +237,32 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||||||
return typeNodeToValueExpr(arg);
|
return typeNodeToValueExpr(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
|
||||||
|
* `A|B|{ngModule: T}|C`.
|
||||||
|
* @param type The type to reflect on.
|
||||||
|
* @returns the identifier of the NgModule type if found, or null otherwise.
|
||||||
|
*/
|
||||||
|
private _reflectModuleFromLiteralType(type: ts.TypeNode): ts.Expression|null {
|
||||||
|
if (!ts.isIntersectionTypeNode(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (const t of type.types) {
|
||||||
|
if (ts.isTypeLiteralNode(t)) {
|
||||||
|
for (const m of t.members) {
|
||||||
|
const ngModuleType = ts.isPropertySignature(m) && ts.isIdentifier(m.name) &&
|
||||||
|
m.name.text === 'ngModule' && m.type ||
|
||||||
|
null;
|
||||||
|
const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
|
||||||
|
if (ngModuleExpression) {
|
||||||
|
return ngModuleExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a list of `Reference`s from a resolved metadata value.
|
* Compute a list of `Reference`s from a resolved metadata value.
|
||||||
*/
|
*/
|
||||||
|
@ -448,7 +448,7 @@ export interface ReflectionHost {
|
|||||||
getVariableValue(declaration: ts.VariableDeclaration): ts.Expression|null;
|
getVariableValue(declaration: ts.VariableDeclaration): ts.Expression|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
|
* Take an exported declaration (maybe a class down-leveled to a variable) and look up the
|
||||||
* declaration of its type in a separate .d.ts tree.
|
* declaration of its type in a separate .d.ts tree.
|
||||||
*
|
*
|
||||||
* This function is allowed to return `null` if the current compilation unit does not have a
|
* This function is allowed to return `null` if the current compilation unit does not have a
|
||||||
@ -456,8 +456,8 @@ export interface ReflectionHost {
|
|||||||
* are produced only during the emit of such a compilation. When compiling .js code, however,
|
* are produced only during the emit of such a compilation. When compiling .js code, however,
|
||||||
* there is frequently a parallel .d.ts tree which this method exposes.
|
* there is frequently a parallel .d.ts tree which this method exposes.
|
||||||
*
|
*
|
||||||
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
|
* Note that the `ts.Declaration` returned from this function may not be from the same
|
||||||
* `ts.Program` as the input declaration.
|
* `ts.Program` as the input declaration.
|
||||||
*/
|
*/
|
||||||
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null;
|
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
|||||||
return declaration.initializer || null;
|
return declaration.initializer || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDtsDeclarationOfClass(_: ts.Declaration): ts.ClassDeclaration|null { return null; }
|
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
|
* Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AttributeMarker, InitialStylingFlags} from '@angular/compiler/src/core';
|
import {AttributeMarker} from '@angular/compiler/src/core';
|
||||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||||
import {compile, expectEmit} from './mock_compile';
|
import {compile, expectEmit} from './mock_compile';
|
||||||
|
|
||||||
@ -48,17 +48,15 @@ describe('compiler compliance', () => {
|
|||||||
|
|
||||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
const template = `
|
const template = `
|
||||||
const $c1$ = ["title", "Hello"];
|
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
const $c2$ = ["cx", "20", "cy", "30", "r", "50"];
|
||||||
const $c3$ = ["cx", "20", "cy", "30", "r", "50"];
|
|
||||||
…
|
…
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $c1$);
|
$r3$.ɵelementStart(0, "div", $c1$);
|
||||||
$r3$.ɵelementStyling($c2$);
|
|
||||||
$r3$.ɵnamespaceSVG();
|
$r3$.ɵnamespaceSVG();
|
||||||
$r3$.ɵelementStart(1, "svg");
|
$r3$.ɵelementStart(1, "svg");
|
||||||
$r3$.ɵelement(2, "circle", $c3$);
|
$r3$.ɵelement(2, "circle", $c2$);
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
$r3$.ɵnamespaceHTML();
|
$r3$.ɵnamespaceHTML();
|
||||||
$r3$.ɵelementStart(3, "p");
|
$r3$.ɵelementStart(3, "p");
|
||||||
@ -100,13 +98,11 @@ describe('compiler compliance', () => {
|
|||||||
|
|
||||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
const template = `
|
const template = `
|
||||||
const $c1$ = ["title", "Hello"];
|
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
|
||||||
…
|
…
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $c1$);
|
$r3$.ɵelementStart(0, "div", $c1$);
|
||||||
$r3$.ɵelementStyling($c2$);
|
|
||||||
$r3$.ɵnamespaceMathML();
|
$r3$.ɵnamespaceMathML();
|
||||||
$r3$.ɵelementStart(1, "math");
|
$r3$.ɵelementStart(1, "math");
|
||||||
$r3$.ɵelement(2, "infinity");
|
$r3$.ɵelement(2, "infinity");
|
||||||
@ -150,13 +146,11 @@ describe('compiler compliance', () => {
|
|||||||
|
|
||||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
const template = `
|
const template = `
|
||||||
const $c1$ = ["title", "Hello"];
|
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
|
||||||
…
|
…
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $c1$);
|
$r3$.ɵelementStart(0, "div", $c1$);
|
||||||
$r3$.ɵelementStyling($c2$);
|
|
||||||
$r3$.ɵtext(1, "Hello ");
|
$r3$.ɵtext(1, "Hello ");
|
||||||
$r3$.ɵelementStart(2, "b");
|
$r3$.ɵelementStart(2, "b");
|
||||||
$r3$.ɵtext(3, "World");
|
$r3$.ɵtext(3, "World");
|
||||||
@ -486,8 +480,8 @@ describe('compiler compliance', () => {
|
|||||||
const factory =
|
const factory =
|
||||||
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
|
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
|
||||||
const template = `
|
const template = `
|
||||||
const _c0 = ["error"];
|
const $e0_classBindings$ = ["error"];
|
||||||
const _c1 = ["background-color"];
|
const $e0_styleBindings$ = ["background-color"];
|
||||||
…
|
…
|
||||||
MyComponent.ngComponentDef = i0.ɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
|
MyComponent.ngComponentDef = i0.ɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
|
||||||
factory: function MyComponent_Factory(t){
|
factory: function MyComponent_Factory(t){
|
||||||
@ -498,7 +492,7 @@ describe('compiler compliance', () => {
|
|||||||
template: function MyComponent_Template(rf,ctx){
|
template: function MyComponent_Template(rf,ctx){
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div");
|
$r3$.ɵelementStart(0, "div");
|
||||||
$r3$.ɵelementStyling(_c0, _c1);
|
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$);
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
@ -1092,156 +1086,224 @@ describe('compiler compliance', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support content projection in root template', () => {
|
describe('content projection', () => {
|
||||||
const files = {
|
|
||||||
app: {
|
|
||||||
'spec.ts': `
|
|
||||||
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
|
it('should support content projection in root template', () => {
|
||||||
export class SimpleComponent {}
|
const files = {
|
||||||
|
app: {
|
||||||
@Component({
|
'spec.ts': `
|
||||||
selector: 'complex',
|
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
||||||
template: \`
|
|
||||||
<div id="first"><ng-content select="span[title=toFirst]"></ng-content></div>
|
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
|
||||||
<div id="second"><ng-content SELECT="span[title=toSecond]"></ng-content></div>\`
|
export class SimpleComponent {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'complex',
|
||||||
|
template: \`
|
||||||
|
<div id="first"><ng-content select="span[title=toFirst]"></ng-content></div>
|
||||||
|
<div id="second"><ng-content SELECT="span[title=toSecond]"></ng-content></div>\`
|
||||||
|
})
|
||||||
|
export class ComplexComponent { }
|
||||||
|
|
||||||
|
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '<simple>content</simple> <complex></complex>'
|
||||||
})
|
})
|
||||||
export class ComplexComponent { }
|
export class MyApp {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
|
const SimpleComponentDefinition = `
|
||||||
export class MyModule {}
|
SimpleComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: SimpleComponent,
|
||||||
|
selectors: [["simple"]],
|
||||||
|
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
||||||
|
consts: 2,
|
||||||
|
vars: 0,
|
||||||
|
template: function SimpleComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵprojectionDef();
|
||||||
|
$r3$.ɵelementStart(0, "div");
|
||||||
|
$r3$.ɵprojection(1);
|
||||||
|
$r3$.ɵelementEnd();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encapsulation: 2
|
||||||
|
});`;
|
||||||
|
|
||||||
@Component({
|
const ComplexComponentDefinition = `
|
||||||
selector: 'my-app',
|
const $c3$ = ["id","first"];
|
||||||
template: '<simple>content</simple> <complex></complex>'
|
const $c4$ = ["id","second"];
|
||||||
})
|
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
|
||||||
export class MyApp {}
|
const $c2$ = ["span[title=toFirst]", "span[title=toSecond]"];
|
||||||
`
|
…
|
||||||
}
|
ComplexComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
};
|
type: ComplexComponent,
|
||||||
|
selectors: [["complex"]],
|
||||||
|
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
||||||
|
consts: 4,
|
||||||
|
vars: 0,
|
||||||
|
template: function ComplexComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵprojectionDef($c1$, $c2$);
|
||||||
|
$r3$.ɵelementStart(0, "div", $c3$);
|
||||||
|
$r3$.ɵprojection(1, 1);
|
||||||
|
$r3$.ɵelementEnd();
|
||||||
|
$r3$.ɵelementStart(2, "div", $c4$);
|
||||||
|
$r3$.ɵprojection(3, 2);
|
||||||
|
$r3$.ɵelementEnd();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encapsulation: 2
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
const SimpleComponentDefinition = `
|
const result = compile(files, angularFiles);
|
||||||
SimpleComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
const source = result.source;
|
||||||
type: SimpleComponent,
|
|
||||||
selectors: [["simple"]],
|
|
||||||
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
|
||||||
consts: 2,
|
|
||||||
vars: 0,
|
|
||||||
template: function SimpleComponent_Template(rf, ctx) {
|
|
||||||
if (rf & 1) {
|
|
||||||
$r3$.ɵprojectionDef();
|
|
||||||
$r3$.ɵelementStart(0, "div");
|
|
||||||
$r3$.ɵprojection(1);
|
|
||||||
$r3$.ɵelementEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
encapsulation: 2
|
|
||||||
});`;
|
|
||||||
|
|
||||||
const ComplexComponentDefinition = `
|
expectEmit(
|
||||||
const $c3$ = ["id","first"];
|
result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
|
||||||
const $c4$ = ["id","second"];
|
expectEmit(
|
||||||
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
|
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
||||||
const $c2$ = ["span[title=toFirst]", "span[title=toSecond]"];
|
});
|
||||||
…
|
|
||||||
ComplexComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
it('should support content projection in nested templates', () => {
|
||||||
type: ComplexComponent,
|
const files = {
|
||||||
selectors: [["complex"]],
|
app: {
|
||||||
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
'spec.ts': `
|
||||||
consts: 4,
|
import {Component, NgModule} from '@angular/core';
|
||||||
vars: 0,
|
|
||||||
template: function ComplexComponent_Template(rf, ctx) {
|
@Component({
|
||||||
if (rf & 1) {
|
template: \`
|
||||||
$r3$.ɵprojectionDef($c1$, $c2$);
|
<div id="second" *ngIf="visible">
|
||||||
$r3$.ɵelementStart(0, "div", $c3$);
|
<ng-content SELECT="span[title=toFirst]"></ng-content>
|
||||||
|
</div>
|
||||||
|
<div id="third" *ngIf="visible">
|
||||||
|
No ng-content, no instructions generated.
|
||||||
|
</div>
|
||||||
|
<ng-template>
|
||||||
|
'*' selector: <ng-content></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
\`,
|
||||||
|
})
|
||||||
|
class Cmp {}
|
||||||
|
|
||||||
|
@NgModule({ declarations: [Cmp] })
|
||||||
|
class Module {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const output = `
|
||||||
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
|
const $_c1$ = ["id", "second"];
|
||||||
|
function Cmp_div_Template_0(rf, ctx) { if (rf & 1) {
|
||||||
|
$r3$.ɵelementStart(0, "div", $_c1$);
|
||||||
$r3$.ɵprojection(1, 1);
|
$r3$.ɵprojection(1, 1);
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
$r3$.ɵelementStart(2, "div", $c4$);
|
} }
|
||||||
$r3$.ɵprojection(3, 2);
|
const $_c4$ = ["id", "third"];
|
||||||
|
function Cmp_div_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵelementStart(0, "div", $_c4$);
|
||||||
|
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
encapsulation: 2
|
|
||||||
});
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
|
||||||
const source = result.source;
|
|
||||||
|
|
||||||
expectEmit(result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
|
|
||||||
expectEmit(
|
|
||||||
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support content projection in nested templates', () => {
|
|
||||||
const files = {
|
|
||||||
app: {
|
|
||||||
'spec.ts': `
|
|
||||||
import {Component, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: \`
|
|
||||||
<div id="second" *ngIf="visible">
|
|
||||||
<ng-content SELECT="span[title=toFirst]"></ng-content>
|
|
||||||
</div>
|
|
||||||
<div id="third" *ngIf="visible">
|
|
||||||
No ng-content, no instructions generated.
|
|
||||||
</div>
|
|
||||||
<ng-template>
|
|
||||||
'*' selector: <ng-content></ng-content>
|
|
||||||
</ng-template>
|
|
||||||
\`,
|
|
||||||
})
|
|
||||||
class Cmp {}
|
|
||||||
|
|
||||||
@NgModule({ declarations: [Cmp] })
|
|
||||||
class Module {}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const output = `
|
|
||||||
const $_c0$ = [1, "ngIf"];
|
|
||||||
const $_c1$ = ["id", "second"];
|
|
||||||
const $_c2$ = [[["span", "title", "tofirst"]]];
|
|
||||||
const $_c3$ = ["span[title=toFirst]"];
|
|
||||||
function Cmp_div_Template_0(rf, ctx) { if (rf & 1) {
|
|
||||||
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
|
||||||
$r3$.ɵelementStart(0, "div", $_c1$);
|
|
||||||
$r3$.ɵprojection(1, 1);
|
|
||||||
$r3$.ɵelementEnd();
|
|
||||||
} }
|
|
||||||
const $_c4$ = ["id", "third"];
|
|
||||||
function Cmp_div_Template_1(rf, ctx) {
|
|
||||||
if (rf & 1) {
|
|
||||||
$r3$.ɵelementStart(0, "div", $_c4$);
|
|
||||||
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
|
|
||||||
$r3$.ɵelementEnd();
|
|
||||||
}
|
}
|
||||||
}
|
function Cmp_ng_template_Template_2(rf, ctx) {
|
||||||
function Cmp_ng_template_Template_2(rf, ctx) {
|
if (rf & 1) {
|
||||||
if (rf & 1) {
|
$r3$.ɵtext(0, " '*' selector: ");
|
||||||
$r3$.ɵprojectionDef();
|
$r3$.ɵprojection(1);
|
||||||
$r3$.ɵtext(0, " '*' selector: ");
|
}
|
||||||
$r3$.ɵprojection(1);
|
|
||||||
}
|
}
|
||||||
}
|
const $_c2$ = [[["span", "title", "tofirst"]]];
|
||||||
…
|
const $_c3$ = ["span[title=toFirst]"];
|
||||||
template: function Cmp_Template(rf, ctx) {
|
…
|
||||||
if (rf & 1) {
|
template: function Cmp_Template(rf, ctx) {
|
||||||
$r3$.ɵtemplate(0, Cmp_div_Template_0, 2, 0, "div", $_c0$);
|
if (rf & 1) {
|
||||||
$r3$.ɵtemplate(1, Cmp_div_Template_1, 2, 0, "div", $_c0$);
|
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
||||||
$r3$.ɵtemplate(2, Cmp_ng_template_Template_2, 2, 0, "ng-template");
|
$r3$.ɵtemplate(0, Cmp_div_Template_0, 2, 0, "div", $_c0$);
|
||||||
|
$r3$.ɵtemplate(1, Cmp_div_Template_1, 2, 0, "div", $_c0$);
|
||||||
|
$r3$.ɵtemplate(2, Cmp_ng_template_Template_2, 2, 0, "ng-template");
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||||
|
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
`;
|
||||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
|
||||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const {source} = compile(files, angularFiles);
|
const {source} = compile(files, angularFiles);
|
||||||
expectEmit(source, output, 'Invalid content projection instructions generated');
|
expectEmit(source, output, 'Invalid content projection instructions generated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support content projection in both the root and nested templates', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: \`
|
||||||
|
<ng-content select="[id=toMainBefore]"></ng-content>
|
||||||
|
<ng-template>
|
||||||
|
<ng-content select="[id=toTemplate]"></ng-content>
|
||||||
|
<ng-template>
|
||||||
|
<ng-content select="[id=toNestedTemplate]"></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template>
|
||||||
|
'*' selector in a template: <ng-content></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
<ng-content select="[id=toMainAfter]"></ng-content>
|
||||||
|
\`,
|
||||||
|
})
|
||||||
|
class Cmp {}
|
||||||
|
|
||||||
|
@NgModule({ declarations: [Cmp] })
|
||||||
|
class Module {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const output = `
|
||||||
|
function Cmp_ng_template_ng_template_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵprojection(0, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Cmp_ng_template_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵprojection(0, 3);
|
||||||
|
$r3$.ɵtemplate(1, Cmp_ng_template_ng_template_Template_1, 1, 0, "ng-template");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Cmp_ng_template_Template_2(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵtext(0, " '*' selector in a template: ");
|
||||||
|
$r3$.ɵprojection(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const $_c0$ = [[["", "id", "tomainbefore"]], [["", "id", "tomainafter"]], [["", "id", "totemplate"]], [["", "id", "tonestedtemplate"]]];
|
||||||
|
const $_c1$ = ["[id=toMainBefore]", "[id=toMainAfter]", "[id=toTemplate]", "[id=toNestedTemplate]"];
|
||||||
|
…
|
||||||
|
template: function Cmp_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
||||||
|
$r3$.ɵprojection(0, 1);
|
||||||
|
$r3$.ɵtemplate(1, Cmp_ng_template_Template_1, 2, 0, "ng-template");
|
||||||
|
$r3$.ɵtemplate(2, Cmp_ng_template_Template_2, 2, 0, "ng-template");
|
||||||
|
$r3$.ɵprojection(3, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const {source} = compile(files, angularFiles);
|
||||||
|
expectEmit(source, output, 'Invalid content projection instructions generated');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('queries', () => {
|
describe('queries', () => {
|
||||||
|
@ -236,7 +236,7 @@ describe('compiler compliance: directives', () => {
|
|||||||
|
|
||||||
const MyComponentDefinition = `
|
const MyComponentDefinition = `
|
||||||
…
|
…
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $_c1$ = ["directiveA", ""];
|
const $_c1$ = ["directiveA", ""];
|
||||||
function MyComponent_ng_container_Template_0(rf, ctx) {
|
function MyComponent_ng_container_Template_0(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {AttributeMarker} from '@angular/compiler/src/core';
|
||||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||||
|
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../compiler/src/compiler';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../compiler/src/compiler';
|
||||||
@ -393,7 +394,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
|
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||||
/**
|
/**
|
||||||
* @desc d
|
* @desc d
|
||||||
* @meaning m
|
* @meaning m
|
||||||
@ -522,7 +523,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
|
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||||
/**
|
/**
|
||||||
* @desc d
|
* @desc d
|
||||||
* @meaning m
|
* @meaning m
|
||||||
@ -922,7 +923,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", {
|
const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", {
|
||||||
"interpolation": "\uFFFD0\uFFFD",
|
"interpolation": "\uFFFD0\uFFFD",
|
||||||
"startTagDiv": "\uFFFD#3\uFFFD",
|
"startTagDiv": "\uFFFD#3\uFFFD",
|
||||||
@ -976,7 +977,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = ["src", "logo.png"];
|
const $_c0$ = ["src", "logo.png"];
|
||||||
const $_c1$ = [1, "ngIf"];
|
const $_c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
function MyComponent_img_Template_1(rf, ctx) {
|
function MyComponent_img_Template_1(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelement(0, "img", $_c0$);
|
$r3$.ɵelement(0, "img", $_c0$);
|
||||||
@ -1043,7 +1044,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
function MyComponent_div_div_Template_4(rf, ctx) {
|
function MyComponent_div_div_Template_4(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2);
|
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2);
|
||||||
@ -1136,7 +1137,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", {
|
const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", {
|
||||||
"startTagSpan": "\uFFFD#2\uFFFD",
|
"startTagSpan": "\uFFFD#2\uFFFD",
|
||||||
"interpolation": "\uFFFD0\uFFFD",
|
"interpolation": "\uFFFD0\uFFFD",
|
||||||
@ -1259,23 +1260,21 @@ describe('i18n support in the view compiler', () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const output = String.raw `
|
const output = String.raw `
|
||||||
const $_c0$ = ["myClass", 1, "myClass", true];
|
const $_c0$ = [${AttributeMarker.Classes}, "myClass"];
|
||||||
const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$ = goog.getMsg("Text #1");
|
const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$ = goog.getMsg("Text #1");
|
||||||
const $_c1$ = ["padding", 1, "padding", "10px"];
|
const $_c1$ = [${AttributeMarker.Styles}, "padding", "10px"];
|
||||||
const $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$ = goog.getMsg("Text #2");
|
const $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$ = goog.getMsg("Text #2");
|
||||||
…
|
…
|
||||||
consts: 4,
|
consts: 4,
|
||||||
vars: 0,
|
vars: 0,
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "span");
|
$r3$.ɵelementStart(0, "span", $_c0$);
|
||||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$);
|
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$);
|
||||||
$r3$.ɵelementStyling($_c0$);
|
|
||||||
$r3$.ɵi18nEnd();
|
$r3$.ɵi18nEnd();
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
$r3$.ɵelementStart(2, "span");
|
$r3$.ɵelementStart(2, "span", $_c1$);
|
||||||
$r3$.ɵi18nStart(3, $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$);
|
$r3$.ɵi18nStart(3, $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$);
|
||||||
$r3$.ɵelementStyling(null, $_c1$);
|
|
||||||
$r3$.ɵi18nEnd();
|
$r3$.ɵi18nEnd();
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
@ -1701,7 +1700,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$, {
|
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$, {
|
||||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||||
});
|
});
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $_c1$ = ["title", "icu only"];
|
const $_c1$ = ["title", "icu only"];
|
||||||
const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}");
|
const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}");
|
||||||
const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$, {
|
const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$, {
|
||||||
@ -1942,7 +1941,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
const $I18N_APP_SPEC_TS_2$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_2$, {
|
const $I18N_APP_SPEC_TS_2$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_2$, {
|
||||||
"VAR_SELECT": "\uFFFD1\uFFFD"
|
"VAR_SELECT": "\uFFFD1\uFFFD"
|
||||||
});
|
});
|
||||||
const $_c3$ = [1, "ngIf"];
|
const $_c3$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}");
|
const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}");
|
||||||
const $I18N_APP_SPEC_TS__4$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS__4$, {
|
const $I18N_APP_SPEC_TS__4$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS__4$, {
|
||||||
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
||||||
@ -2050,7 +2049,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$, {
|
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$, {
|
||||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||||
});
|
});
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}");
|
const $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}");
|
||||||
const $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$, {
|
const $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$, {
|
||||||
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
||||||
@ -2113,7 +2112,7 @@ describe('i18n support in the view compiler', () => {
|
|||||||
const $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$, {
|
const $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$, {
|
||||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||||
});
|
});
|
||||||
const $_c0$ = [1, "ngIf"];
|
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||||
const $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {$interpolation}}}", {
|
const $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {$interpolation}}}", {
|
||||||
"interpolation": "\uFFFD1:1\uFFFD"
|
"interpolation": "\uFFFD1:1\uFFFD"
|
||||||
});
|
});
|
||||||
|
@ -120,4 +120,70 @@ describe('r3_view_compiler', () => {
|
|||||||
expectEmit(result.source, bV_call, 'Incorrect bV call');
|
expectEmit(result.source, bV_call, 'Incorrect bV call');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('animations', () => {
|
||||||
|
it('should keep @attr but suppress [@attr]', () => {
|
||||||
|
const files: MockDirectory = {
|
||||||
|
app: {
|
||||||
|
'example.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '<div @attrOnly [@myAnimation]="exp"></div>'
|
||||||
|
})
|
||||||
|
export class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyApp]})
|
||||||
|
export class MyModule {}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const _c0 = ["@attrOnly", ""];
|
||||||
|
// ...
|
||||||
|
template: function MyApp_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵelement(0, "div", _c0);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, template, 'Incorrect initialization attributes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dedup multiple [@event] listeners', () => {
|
||||||
|
const files: MockDirectory = {
|
||||||
|
app: {
|
||||||
|
'example.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '<div (@mySelector.start)="false" (@mySelector.done)="false" [@mySelector]="0"></div>'
|
||||||
|
})
|
||||||
|
export class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyApp]})
|
||||||
|
export class MyModule {}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const _c0 = [3, "mySelector"];
|
||||||
|
// ...
|
||||||
|
template: function MyApp_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵelementStart(0, "div", _c0);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, template, 'Incorrect initialization attributes');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AttributeMarker, InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
|
import {AttributeMarker, ViewEncapsulation} from '@angular/compiler/src/core';
|
||||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||||
import {compile, expectEmit} from './mock_compile';
|
import {compile, expectEmit} from './mock_compile';
|
||||||
|
|
||||||
@ -214,7 +214,6 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $e0_attrs$ = ["@foo", ""];
|
|
||||||
const $e1_attrs$ = ["@bar", ""];
|
const $e1_attrs$ = ["@bar", ""];
|
||||||
const $e2_attrs$ = ["@baz", ""];
|
const $e2_attrs$ = ["@baz", ""];
|
||||||
…
|
…
|
||||||
@ -224,7 +223,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
vars: 1,
|
vars: 1,
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelement(0, "div", $e0_attrs$);
|
$r3$.ɵelement(0, "div");
|
||||||
$r3$.ɵelement(1, "div", $e1_attrs$);
|
$r3$.ɵelement(1, "div", $e1_attrs$);
|
||||||
$r3$.ɵelement(2, "div", $e2_attrs$);
|
$r3$.ɵelement(2, "div", $e2_attrs$);
|
||||||
}
|
}
|
||||||
@ -366,8 +365,8 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
|
const $_c0$ = [${AttributeMarker.Styles}, "opacity", "1", ${AttributeMarker.SelectOnly}, "style"];
|
||||||
const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
|
const $_c1$ = ["width", "height"];
|
||||||
…
|
…
|
||||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
@ -379,14 +378,14 @@ describe('compiler compliance: styling', () => {
|
|||||||
vars: 1,
|
vars: 1,
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
$r3$.ɵelementStart(0, "div", $_c0$);
|
||||||
$r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
|
$r3$.ɵelementStyling(null, $_c1$, $r3$.ɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
|
$r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
|
||||||
$r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
|
$r3$.ɵelementStyleProp(0, 0, $ctx$.myWidth);
|
||||||
$r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
|
$r3$.ɵelementStyleProp(0, 1, $ctx$.myHeight);
|
||||||
$r3$.ɵelementStylingApply(0);
|
$r3$.ɵelementStylingApply(0);
|
||||||
$r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
|
$r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
|
||||||
}
|
}
|
||||||
@ -421,7 +420,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const _c0 = ["background-image"];
|
const $_c0$ = ["background-image"];
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.myImage = 'url(foo.jpg)';
|
this.myImage = 'url(foo.jpg)';
|
||||||
@ -456,7 +455,6 @@ describe('compiler compliance: styling', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support [style.foo.suffix] style bindings with a suffix', () => {
|
it('should support [style.foo.suffix] style bindings with a suffix', () => {
|
||||||
|
|
||||||
const files = {
|
const files = {
|
||||||
app: {
|
app: {
|
||||||
'spec.ts': `
|
'spec.ts': `
|
||||||
@ -476,7 +474,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $e0_styles$= ["font-size"];
|
const $e0_styles$ = ["font-size"];
|
||||||
…
|
…
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
@ -564,8 +562,8 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
|
const $e0_attrs$ = [${AttributeMarker.Classes}, "grape", ${AttributeMarker.SelectOnly}, "class"];
|
||||||
const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
|
const $e0_bindings$ = ["apple", "orange"];
|
||||||
…
|
…
|
||||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
@ -578,13 +576,13 @@ describe('compiler compliance: styling', () => {
|
|||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||||
$r3$.ɵelementStyling($e0_cd$);
|
$r3$.ɵelementStyling($e0_bindings$);
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
|
$r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
|
||||||
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
|
$r3$.ɵelementClassProp(0, 0, $ctx$.yesToApple);
|
||||||
$r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
|
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToOrange);
|
||||||
$r3$.ɵelementStylingApply(0);
|
$r3$.ɵelementStylingApply(0);
|
||||||
$r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
|
$r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
|
||||||
}
|
}
|
||||||
@ -606,7 +604,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-component',
|
selector: 'my-component',
|
||||||
template: \`<div class="foo"
|
template: \`<div class=" foo "
|
||||||
style="width:100px"
|
style="width:100px"
|
||||||
[attr.class]="'round'"
|
[attr.class]="'round'"
|
||||||
[attr.style]="'height:100px'"></div>\`
|
[attr.style]="'height:100px'"></div>\`
|
||||||
@ -620,9 +618,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class", "style"];
|
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", ${AttributeMarker.Styles}, "width", "100px", ${AttributeMarker.SelectOnly}, "class", "style"];
|
||||||
const $e0_cd$ = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
|
|
||||||
const $e0_sd$ = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
|
|
||||||
…
|
…
|
||||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
@ -635,7 +631,6 @@ describe('compiler compliance: styling', () => {
|
|||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||||
$r3$.ɵelementStyling($e0_cd$, $e0_sd$);
|
|
||||||
$r3$.ɵelementEnd();
|
$r3$.ɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
@ -765,10 +760,13 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
|
const $e0_classBindings$ = ["foo"];
|
||||||
|
const $e0_styleBindings$ = ["bar", "baz"];
|
||||||
|
…
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStart(0, "div");
|
$r3$.ɵelementStart(0, "div");
|
||||||
$r3$.ɵelementStyling($e0_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
|
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵpipe(1, "pipe");
|
$r3$.ɵpipe(1, "pipe");
|
||||||
$r3$.ɵpipe(2, "pipe");
|
$r3$.ɵpipe(2, "pipe");
|
||||||
$r3$.ɵpipe(3, "pipe");
|
$r3$.ɵpipe(3, "pipe");
|
||||||
@ -828,16 +826,18 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||||
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
const $e0_classBindings$ = ["foo"];
|
||||||
|
const $e0_styleBindings$ = ["color"];
|
||||||
…
|
…
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
$r3$.ɵelementHostAttrs(ctx, $e0_attrs$);
|
||||||
|
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
||||||
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myColorProp, null, ctx);
|
||||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
}
|
}
|
||||||
@ -959,10 +959,10 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const _c0 = ["foo"];
|
const $widthDir_classes$ = ["foo"];
|
||||||
const _c1 = ["width"];
|
const $widthDir_styles$ = ["width"];
|
||||||
const _c2 = ["bar"];
|
const $heightDir_classes$ = ["bar"];
|
||||||
const _c3 = ["height"];
|
const $heightDir_styles$ = ["height"];
|
||||||
…
|
…
|
||||||
function ClassDirective_HostBindings(rf, ctx, elIndex) {
|
function ClassDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
@ -976,7 +976,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
…
|
…
|
||||||
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
|
$r3$.ɵelementStyling($widthDir_classes$, $widthDir_styles$, null, ctx);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
|
||||||
@ -987,7 +987,7 @@ describe('compiler compliance: styling', () => {
|
|||||||
…
|
…
|
||||||
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
|
$r3$.ɵelementStyling($heightDir_classes$, $heightDir_styles$, null, ctx);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
|
||||||
@ -1014,7 +1014,8 @@ describe('compiler compliance: styling', () => {
|
|||||||
template: '',
|
template: '',
|
||||||
host: {
|
host: {
|
||||||
'style': 'width:200px; height:500px',
|
'style': 'width:200px; height:500px',
|
||||||
'class': 'foo baz'
|
'class': 'foo baz',
|
||||||
|
'title': 'foo title'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
@ -1029,6 +1030,9 @@ describe('compiler compliance: styling', () => {
|
|||||||
|
|
||||||
@HostBinding('title')
|
@HostBinding('title')
|
||||||
title = 'some title';
|
title = 'some title';
|
||||||
|
|
||||||
|
@Input('name')
|
||||||
|
name = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({declarations: [MyComponent]})
|
@NgModule({declarations: [MyComponent]})
|
||||||
@ -1038,13 +1042,13 @@ describe('compiler compliance: styling', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $_c0$ = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
const $_c0$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||||
const $_c1$ = ["width", "height", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
|
||||||
…
|
…
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵallocHostVars(2);
|
$r3$.ɵallocHostVars(2);
|
||||||
$r3$.ɵelementStyling($_c0$, $_c1$, $r3$.ɵdefaultStyleSanitizer, ctx);
|
$r3$.ɵelementHostAttrs(ctx, $_c0$);
|
||||||
|
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);
|
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);
|
||||||
|
@ -441,6 +441,39 @@ describe('ngtsc behavioral tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should unwrap a ModuleWithProviders-like function if a matching literal type is provided for it',
|
||||||
|
() => {
|
||||||
|
env.tsconfig();
|
||||||
|
env.write(`test.ts`, `
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from 'router';
|
||||||
|
|
||||||
|
@NgModule({imports: [RouterModule.forRoot()]})
|
||||||
|
export class TestModule {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.write('node_modules/router/index.d.ts', `
|
||||||
|
import {ModuleWithProviders} from '@angular/core';
|
||||||
|
|
||||||
|
export interface MyType extends ModuleWithProviders {}
|
||||||
|
|
||||||
|
declare class RouterModule {
|
||||||
|
static forRoot(): (MyType)&{ngModule:RouterModule};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]');
|
||||||
|
|
||||||
|
const dtsContents = env.getContents('test.d.ts');
|
||||||
|
expect(dtsContents).toContain(`import * as i1 from 'router';`);
|
||||||
|
expect(dtsContents)
|
||||||
|
.toContain(
|
||||||
|
'i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
|
||||||
|
});
|
||||||
|
|
||||||
it('should inject special types according to the metadata', () => {
|
it('should inject special types according to the metadata', () => {
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
env.write(`test.ts`, `
|
env.write(`test.ts`, `
|
||||||
|
@ -134,10 +134,13 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
|||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
viewProviders: Provider[]|null;
|
viewProviders: Provider[]|null;
|
||||||
interpolation?: [string, string];
|
interpolation?: [string, string];
|
||||||
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewEncapsulation = number;
|
export type ViewEncapsulation = number;
|
||||||
|
|
||||||
|
export type ChangeDetectionStrategy = number;
|
||||||
|
|
||||||
export interface R3QueryMetadataFacade {
|
export interface R3QueryMetadataFacade {
|
||||||
propertyName: string;
|
propertyName: string;
|
||||||
first: boolean;
|
first: boolean;
|
||||||
|
@ -379,13 +379,9 @@ export const enum RenderFlags {
|
|||||||
Update = 0b10
|
Update = 0b10
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum InitialStylingFlags {
|
|
||||||
VALUES_MODE = 0b1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pasted from render3/interfaces/node.ts
|
// Pasted from render3/interfaces/node.ts
|
||||||
/**
|
/**
|
||||||
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
|
* A set of marker values to be used in the attributes arrays. These markers indicate that some
|
||||||
* items are not regular attributes and the processing should be adapted accordingly.
|
* items are not regular attributes and the processing should be adapted accordingly.
|
||||||
*/
|
*/
|
||||||
export const enum AttributeMarker {
|
export const enum AttributeMarker {
|
||||||
@ -396,11 +392,48 @@ export const enum AttributeMarker {
|
|||||||
*/
|
*/
|
||||||
NamespaceURI = 0,
|
NamespaceURI = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals class declaration.
|
||||||
|
*
|
||||||
|
* Each value following `Classes` designates a class name to include on the element.
|
||||||
|
* ## Example:
|
||||||
|
*
|
||||||
|
* Given:
|
||||||
|
* ```
|
||||||
|
* <div class="foo bar baz">...<d/vi>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* the generated code is:
|
||||||
|
* ```
|
||||||
|
* var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz'];
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
Classes = 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals style declaration.
|
||||||
|
*
|
||||||
|
* Each pair of values following `Styles` designates a style name and value to include on the
|
||||||
|
* element.
|
||||||
|
* ## Example:
|
||||||
|
*
|
||||||
|
* Given:
|
||||||
|
* ```
|
||||||
|
* <div style="width:100px; height:200px; color:red">...</div>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* the generated code is:
|
||||||
|
* ```
|
||||||
|
* var _c1 = [AttributeMarker.Styles, 'width', '100px', 'height'. '200px', 'color', 'red'];
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
Styles = 2,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This marker indicates that the following attribute names were extracted from bindings (ex.:
|
* This marker indicates that the following attribute names were extracted from bindings (ex.:
|
||||||
* [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
|
* [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
|
||||||
* Taking the above bindings and outputs as an example an attributes array could look as follows:
|
* Taking the above bindings and outputs as an example an attributes array could look as follows:
|
||||||
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
|
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
|
||||||
*/
|
*/
|
||||||
SelectOnly = 1
|
SelectOnly = 3,
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
|||||||
styles: facade.styles || [],
|
styles: facade.styles || [],
|
||||||
encapsulation: facade.encapsulation as any,
|
encapsulation: facade.encapsulation as any,
|
||||||
interpolation: interpolationConfig,
|
interpolation: interpolationConfig,
|
||||||
|
changeDetection: facade.changeDetection,
|
||||||
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
||||||
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
|
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
|
||||||
null,
|
null,
|
||||||
|
@ -43,6 +43,8 @@ export class Identifiers {
|
|||||||
|
|
||||||
static elementStyling: o.ExternalReference = {name: 'ɵelementStyling', moduleName: CORE};
|
static elementStyling: o.ExternalReference = {name: 'ɵelementStyling', moduleName: CORE};
|
||||||
|
|
||||||
|
static elementHostAttrs: o.ExternalReference = {name: 'ɵelementHostAttrs', moduleName: CORE};
|
||||||
|
|
||||||
static elementStylingMap: o.ExternalReference = {name: 'ɵelementStylingMap', moduleName: CORE};
|
static elementStylingMap: o.ExternalReference = {name: 'ɵelementStylingMap', moduleName: CORE};
|
||||||
|
|
||||||
static elementStyleProp: o.ExternalReference = {name: 'ɵelementStyleProp', moduleName: CORE};
|
static elementStyleProp: o.ExternalReference = {name: 'ɵelementStyleProp', moduleName: CORE};
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ViewEncapsulation} from '../../core';
|
import {ChangeDetectionStrategy, ViewEncapsulation} from '../../core';
|
||||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../parse_util';
|
import {ParseSourceSpan} from '../../parse_util';
|
||||||
import * as t from '../r3_ast';
|
import * as t from '../r3_ast';
|
||||||
import {R3DependencyMetadata} from '../r3_factory';
|
import {R3DependencyMetadata} from '../r3_factory';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information needed to compile a directive for the render3 runtime.
|
* Information needed to compile a directive for the render3 runtime.
|
||||||
*/
|
*/
|
||||||
@ -184,14 +185,19 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether translation variable name should contain external message id
|
* Whether translation variable name should contain external message id
|
||||||
* (used by Closure Compiler's output of `goog.getMsg` for transition period)
|
* (used by Closure Compiler's output of `goog.getMsg` for transition period).
|
||||||
*/
|
*/
|
||||||
i18nUseExternalIds: boolean;
|
i18nUseExternalIds: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the default interpolation start and end delimiters ({{ and }})
|
* Overrides the default interpolation start and end delimiters ({{ and }}).
|
||||||
*/
|
*/
|
||||||
interpolation: InterpolationConfig;
|
interpolation: InterpolationConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy used for detecting changes in the component.
|
||||||
|
*/
|
||||||
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +28,7 @@ import {Render3ParseResult} from '../r3_template_transform';
|
|||||||
import {typeWithParameters} from '../util';
|
import {typeWithParameters} from '../util';
|
||||||
|
|
||||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
||||||
import {StylingBuilder, StylingInstruction} from './styling';
|
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
|
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
|
||||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
||||||
|
|
||||||
@ -251,6 +251,7 @@ export function compileComponentFromMetadata(
|
|||||||
|
|
||||||
const directivesUsed = new Set<o.Expression>();
|
const directivesUsed = new Set<o.Expression>();
|
||||||
const pipesUsed = new Set<o.Expression>();
|
const pipesUsed = new Set<o.Expression>();
|
||||||
|
const changeDetection = meta.changeDetection;
|
||||||
|
|
||||||
const template = meta.template;
|
const template = meta.template;
|
||||||
const templateBuilder = new TemplateDefinitionBuilder(
|
const templateBuilder = new TemplateDefinitionBuilder(
|
||||||
@ -313,6 +314,11 @@ export function compileComponentFromMetadata(
|
|||||||
'data', o.literalMap([{key: 'animation', value: meta.animations, quoted: false}]));
|
'data', o.literalMap([{key: 'animation', value: meta.animations, quoted: false}]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only set the change detection flag if it's defined and it's not the default.
|
||||||
|
if (changeDetection != null && changeDetection !== core.ChangeDetectionStrategy.Default) {
|
||||||
|
definitionMap.set('changeDetection', o.literal(changeDetection));
|
||||||
|
}
|
||||||
|
|
||||||
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
||||||
// string literal, which must be on one line.
|
// string literal, which must be on one line.
|
||||||
const selectorForType = (meta.selector || '').replace(/\n/g, '');
|
const selectorForType = (meta.selector || '').replace(/\n/g, '');
|
||||||
@ -703,16 +709,35 @@ function createHostBindingsFunction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styleBuilder.hasBindingsOrInitialValues) {
|
if (styleBuilder.hasBindingsOrInitialValues()) {
|
||||||
const createInstruction = styleBuilder.buildCreateLevelInstruction(null, constantPool);
|
// since we're dealing with directives here and directives have a hostBinding
|
||||||
if (createInstruction) {
|
// function, we need to generate special instructions that deal with styling
|
||||||
const createStmt = createStylingStmt(createInstruction, bindingContext, bindingFn);
|
// (both bindings and initial values). The instruction below will instruct
|
||||||
createStatements.push(createStmt);
|
// all initial styling (styling that is inside of a host binding within a
|
||||||
|
// directive) to be attached to the host element of the directive.
|
||||||
|
const hostAttrsInstruction =
|
||||||
|
styleBuilder.buildDirectiveHostAttrsInstruction(null, constantPool);
|
||||||
|
if (hostAttrsInstruction) {
|
||||||
|
createStatements.push(createStylingStmt(hostAttrsInstruction, bindingContext, bindingFn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
|
||||||
|
// MUST be registered on a given element within the component/directive
|
||||||
|
// templateFn/hostBindingsFn functions. The instruction below will figure out
|
||||||
|
// what all the bindings are and then generate the statements required to register
|
||||||
|
// those bindings to the element via `elementStyling`.
|
||||||
|
const elementStylingInstruction =
|
||||||
|
styleBuilder.buildElementStylingInstruction(null, constantPool);
|
||||||
|
if (elementStylingInstruction) {
|
||||||
|
createStatements.push(
|
||||||
|
createStylingStmt(elementStylingInstruction, bindingContext, bindingFn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally each binding that was registered in the statement above will need to be added to
|
||||||
|
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
||||||
|
// are evaluated and updated for the element.
|
||||||
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => {
|
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => {
|
||||||
const updateStmt = createStylingStmt(instruction, bindingContext, bindingFn);
|
updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn));
|
||||||
updateStatements.push(updateStmt);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,15 @@ const enum Char {
|
|||||||
*
|
*
|
||||||
* @param value string representation of style as used in the `style` attribute in HTML.
|
* @param value string representation of style as used in the `style` attribute in HTML.
|
||||||
* Example: `color: red; height: auto`.
|
* Example: `color: red; height: auto`.
|
||||||
* @returns an object literal. `{ color: 'red', height: 'auto'}`.
|
* @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
|
||||||
|
* 'auto']`
|
||||||
*/
|
*/
|
||||||
export function parse(value: string): {[key: string]: any} {
|
export function parse(value: string): string[] {
|
||||||
const styles: {[key: string]: any} = {};
|
// we use a string array here instead of a string map
|
||||||
|
// because a string-map is not guaranteed to retain the
|
||||||
|
// order of the entries whereas a string array can be
|
||||||
|
// construted in a [key, value, key, value] format.
|
||||||
|
const styles: string[] = [];
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let parenDepth = 0;
|
let parenDepth = 0;
|
||||||
@ -72,7 +77,7 @@ export function parse(value: string): {[key: string]: any} {
|
|||||||
case Char.Semicolon:
|
case Char.Semicolon:
|
||||||
if (currentProp && valueStart > 0 && parenDepth === 0 && quote === Char.QuoteNone) {
|
if (currentProp && valueStart > 0 && parenDepth === 0 && quote === Char.QuoteNone) {
|
||||||
const styleVal = value.substring(valueStart, i - 1).trim();
|
const styleVal = value.substring(valueStart, i - 1).trim();
|
||||||
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
|
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
||||||
propStart = i;
|
propStart = i;
|
||||||
valueStart = 0;
|
valueStart = 0;
|
||||||
currentProp = null;
|
currentProp = null;
|
||||||
@ -84,7 +89,7 @@ export function parse(value: string): {[key: string]: any} {
|
|||||||
|
|
||||||
if (currentProp && valueStart) {
|
if (currentProp && valueStart) {
|
||||||
const styleVal = value.substr(valueStart).trim();
|
const styleVal = value.substr(valueStart).trim();
|
||||||
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
|
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ConstantPool} from '../../constant_pool';
|
import {ConstantPool} from '../../constant_pool';
|
||||||
import {InitialStylingFlags} from '../../core';
|
import {AttributeMarker} from '../../core';
|
||||||
import {AST, BindingType, ParseSpan} from '../../expression_parser/ast';
|
import {AST, BindingType} from '../../expression_parser/ast';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../parse_util';
|
import {ParseSourceSpan} from '../../parse_util';
|
||||||
import * as t from '../r3_ast';
|
import * as t from '../r3_ast';
|
||||||
@ -40,6 +40,10 @@ interface BoundStylingEntry {
|
|||||||
/**
|
/**
|
||||||
* Produces creation/update instructions for all styling bindings (class and style)
|
* Produces creation/update instructions for all styling bindings (class and style)
|
||||||
*
|
*
|
||||||
|
* It also produces the creation instruction to register all initial styling values
|
||||||
|
* (which are all the static class="..." and style="..." attribute values that exist
|
||||||
|
* on an element within a template).
|
||||||
|
*
|
||||||
* The builder class below handles producing instructions for the following cases:
|
* The builder class below handles producing instructions for the following cases:
|
||||||
*
|
*
|
||||||
* - Static style/class attributes (style="..." and class="...")
|
* - Static style/class attributes (style="..." and class="...")
|
||||||
@ -63,25 +67,57 @@ interface BoundStylingEntry {
|
|||||||
* The creation/update methods within the builder class produce these instructions.
|
* The creation/update methods within the builder class produce these instructions.
|
||||||
*/
|
*/
|
||||||
export class StylingBuilder {
|
export class StylingBuilder {
|
||||||
public readonly hasBindingsOrInitialValues = false;
|
/** Whether or not there are any static styling values present */
|
||||||
|
private _hasInitialValues = false;
|
||||||
|
/**
|
||||||
|
* Whether or not there are any styling bindings present
|
||||||
|
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
||||||
|
*/
|
||||||
|
private _hasBindings = false;
|
||||||
|
|
||||||
|
/** the input for [class] (if it exists) */
|
||||||
private _classMapInput: BoundStylingEntry|null = null;
|
private _classMapInput: BoundStylingEntry|null = null;
|
||||||
|
/** the input for [style] (if it exists) */
|
||||||
private _styleMapInput: BoundStylingEntry|null = null;
|
private _styleMapInput: BoundStylingEntry|null = null;
|
||||||
|
/** an array of each [style.prop] input */
|
||||||
private _singleStyleInputs: BoundStylingEntry[]|null = null;
|
private _singleStyleInputs: BoundStylingEntry[]|null = null;
|
||||||
|
/** an array of each [class.name] input */
|
||||||
private _singleClassInputs: BoundStylingEntry[]|null = null;
|
private _singleClassInputs: BoundStylingEntry[]|null = null;
|
||||||
private _lastStylingInput: BoundStylingEntry|null = null;
|
private _lastStylingInput: BoundStylingEntry|null = null;
|
||||||
|
|
||||||
// maps are used instead of hash maps because a Map will
|
// maps are used instead of hash maps because a Map will
|
||||||
// retain the ordering of the keys
|
// retain the ordering of the keys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the location of each style binding in the template
|
||||||
|
* (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
||||||
|
* that `width=0` and `height=1`)
|
||||||
|
*/
|
||||||
private _stylesIndex = new Map<string, number>();
|
private _stylesIndex = new Map<string, number>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the location of each class binding in the template
|
||||||
|
* (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
||||||
|
* that `big=0` and `hidden=1`)
|
||||||
|
*/
|
||||||
private _classesIndex = new Map<string, number>();
|
private _classesIndex = new Map<string, number>();
|
||||||
private _initialStyleValues: {[propName: string]: string} = {};
|
private _initialStyleValues: string[] = [];
|
||||||
private _initialClassValues: {[className: string]: boolean} = {};
|
private _initialClassValues: string[] = [];
|
||||||
|
|
||||||
|
// certain style properties ALWAYS need sanitization
|
||||||
|
// this is checked each time new styles are encountered
|
||||||
private _useDefaultSanitizer = false;
|
private _useDefaultSanitizer = false;
|
||||||
private _applyFnRequired = false;
|
|
||||||
|
|
||||||
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
||||||
|
|
||||||
|
hasBindingsOrInitialValues() { return this._hasBindings || this._hasInitialValues; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given input to the styling builder to be later used when producing AOT code.
|
||||||
|
*
|
||||||
|
* The code below will only accept the input if it is somehow tied to styling (whether it be
|
||||||
|
* style/class bindings or static style/class attributes).
|
||||||
|
*/
|
||||||
registerBoundInput(input: t.BoundAttribute): boolean {
|
registerBoundInput(input: t.BoundAttribute): boolean {
|
||||||
// [attr.style] or [attr.class] are skipped in the code below,
|
// [attr.style] or [attr.class] are skipped in the code below,
|
||||||
// they should not be treated as styling-based bindings since
|
// they should not be treated as styling-based bindings since
|
||||||
@ -117,14 +153,12 @@ export class StylingBuilder {
|
|||||||
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
||||||
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(propertyName);
|
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(propertyName);
|
||||||
registerIntoMap(this._stylesIndex, propertyName);
|
registerIntoMap(this._stylesIndex, propertyName);
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
} else {
|
} else {
|
||||||
this._useDefaultSanitizer = true;
|
this._useDefaultSanitizer = true;
|
||||||
this._styleMapInput = entry;
|
this._styleMapInput = entry;
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
this._lastStylingInput = entry;
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
this._hasBindings = true;
|
||||||
this._applyFnRequired = true;
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,107 +167,152 @@ export class StylingBuilder {
|
|||||||
const entry = { name: className, value, sourceSpan } as BoundStylingEntry;
|
const entry = { name: className, value, sourceSpan } as BoundStylingEntry;
|
||||||
if (className) {
|
if (className) {
|
||||||
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
registerIntoMap(this._classesIndex, className);
|
registerIntoMap(this._classesIndex, className);
|
||||||
} else {
|
} else {
|
||||||
this._classMapInput = entry;
|
this._classMapInput = entry;
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
this._lastStylingInput = entry;
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
this._hasBindings = true;
|
||||||
this._applyFnRequired = true;
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the element's static style string value to the builder.
|
||||||
|
*
|
||||||
|
* @param value the style string (e.g. `width:100px; height:200px;`)
|
||||||
|
*/
|
||||||
registerStyleAttr(value: string) {
|
registerStyleAttr(value: string) {
|
||||||
this._initialStyleValues = parseStyle(value);
|
this._initialStyleValues = parseStyle(value);
|
||||||
Object.keys(this._initialStyleValues).forEach(prop => {
|
this._hasInitialValues = true;
|
||||||
registerIntoMap(this._stylesIndex, prop);
|
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the element's static class string value to the builder.
|
||||||
|
*
|
||||||
|
* @param value the className string (e.g. `disabled gold zoom`)
|
||||||
|
*/
|
||||||
registerClassAttr(value: string) {
|
registerClassAttr(value: string) {
|
||||||
this._initialClassValues = {};
|
this._initialClassValues = value.trim().split(/\s+/g);
|
||||||
value.split(/\s+/g).forEach(className => {
|
this._hasInitialValues = true;
|
||||||
this._initialClassValues[className] = true;
|
|
||||||
registerIntoMap(this._classesIndex, className);
|
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildInitExpr(registry: Map<string, number>, initialValues: {[key: string]: any}):
|
/**
|
||||||
o.Expression|null {
|
* Appends all styling-related expressions to the provided attrs array.
|
||||||
const exprs: o.Expression[] = [];
|
*
|
||||||
const nameAndValueExprs: o.Expression[] = [];
|
* @param attrs an existing array where each of the styling expressions
|
||||||
|
* will be inserted into.
|
||||||
// _c0 = [prop, prop2, prop3, ...]
|
*/
|
||||||
registry.forEach((value, key) => {
|
populateInitialStylingAttrs(attrs: o.Expression[]): void {
|
||||||
const keyLiteral = o.literal(key);
|
// [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
|
||||||
exprs.push(keyLiteral);
|
if (this._initialClassValues.length) {
|
||||||
const initialValue = initialValues[key];
|
attrs.push(o.literal(AttributeMarker.Classes));
|
||||||
if (initialValue) {
|
for (let i = 0; i < this._initialClassValues.length; i++) {
|
||||||
nameAndValueExprs.push(keyLiteral, o.literal(initialValue));
|
attrs.push(o.literal(this._initialClassValues[i]));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (nameAndValueExprs.length) {
|
|
||||||
// _c0 = [... MARKER ...]
|
|
||||||
exprs.push(o.literal(InitialStylingFlags.VALUES_MODE));
|
|
||||||
// _c0 = [prop, VALUE, prop2, VALUE2, ...]
|
|
||||||
exprs.push(...nameAndValueExprs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exprs.length ? o.literalArr(exprs) : null;
|
// [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
|
||||||
|
if (this._initialStyleValues.length) {
|
||||||
|
attrs.push(o.literal(AttributeMarker.Styles));
|
||||||
|
for (let i = 0; i < this._initialStyleValues.length; i += 2) {
|
||||||
|
attrs.push(
|
||||||
|
o.literal(this._initialStyleValues[i]), o.literal(this._initialStyleValues[i + 1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCreateLevelInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
/**
|
||||||
|
* Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
||||||
|
*
|
||||||
|
* The instruction generation code below is used for producing the AOT statement code which is
|
||||||
|
* responsible for registering initial styles (within a directive hostBindings' creation block)
|
||||||
|
* to the directive host element.
|
||||||
|
*/
|
||||||
|
buildDirectiveHostAttrsInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||||
StylingInstruction|null {
|
StylingInstruction|null {
|
||||||
if (this.hasBindingsOrInitialValues) {
|
if (this._hasInitialValues && this._directiveExpr) {
|
||||||
const initialClasses = this._buildInitExpr(this._classesIndex, this._initialClassValues);
|
return {
|
||||||
const initialStyles = this._buildInitExpr(this._stylesIndex, this._initialStyleValues);
|
sourceSpan,
|
||||||
|
reference: R3.elementHostAttrs,
|
||||||
// in the event that a [style] binding is used then sanitization will
|
buildParams: () => {
|
||||||
// always be imported because it is not possible to know ahead of time
|
const attrs: o.Expression[] = [];
|
||||||
// whether style bindings will use or not use any sanitizable properties
|
this.populateInitialStylingAttrs(attrs);
|
||||||
// that isStyleSanitizable() will detect
|
return [this._directiveExpr !, getConstantLiteralFromArray(constantPool, attrs)];
|
||||||
const useSanitizer = this._useDefaultSanitizer;
|
|
||||||
const params: (o.Expression)[] = [];
|
|
||||||
|
|
||||||
if (initialClasses) {
|
|
||||||
// the template compiler handles initial class styling (e.g. class="foo") values
|
|
||||||
// in a special command called `elementClass` so that the initial class
|
|
||||||
// can be processed during runtime. These initial class values are bound to
|
|
||||||
// a constant because the inital class values do not change (since they're static).
|
|
||||||
params.push(constantPool.getConstLiteral(initialClasses, true));
|
|
||||||
} else if (initialStyles || useSanitizer || this._directiveExpr) {
|
|
||||||
// no point in having an extra `null` value unless there are follow-up params
|
|
||||||
params.push(o.NULL_EXPR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initialStyles) {
|
|
||||||
// the template compiler handles initial style (e.g. style="foo") values
|
|
||||||
// in a special command called `elementStyle` so that the initial styles
|
|
||||||
// can be processed during runtime. These initial styles values are bound to
|
|
||||||
// a constant because the inital style values do not change (since they're static).
|
|
||||||
params.push(constantPool.getConstLiteral(initialStyles, true));
|
|
||||||
} else if (useSanitizer || this._directiveExpr) {
|
|
||||||
// no point in having an extra `null` value unless there are follow-up params
|
|
||||||
params.push(o.NULL_EXPR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useSanitizer || this._directiveExpr) {
|
|
||||||
params.push(useSanitizer ? o.importExpr(R3.defaultStyleSanitizer) : o.NULL_EXPR);
|
|
||||||
if (this._directiveExpr) {
|
|
||||||
params.push(this._directiveExpr);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return {sourceSpan, reference: R3.elementStyling, buildParams: () => params};
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildStylingMap(valueConverter: ValueConverter): StylingInstruction|null {
|
/**
|
||||||
|
* Builds an instruction with all the expressions and parameters for `elementStyling`.
|
||||||
|
*
|
||||||
|
* The instruction generation code below is used for producing the AOT statement code which is
|
||||||
|
* responsible for registering style/class bindings to an element.
|
||||||
|
*/
|
||||||
|
buildElementStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||||
|
StylingInstruction|null {
|
||||||
|
if (this._hasBindings) {
|
||||||
|
return {
|
||||||
|
sourceSpan,
|
||||||
|
reference: R3.elementStyling,
|
||||||
|
buildParams: () => {
|
||||||
|
// a string array of every style-based binding
|
||||||
|
const styleBindingProps =
|
||||||
|
this._singleStyleInputs ? this._singleStyleInputs.map(i => o.literal(i.name)) : [];
|
||||||
|
// a string array of every class-based binding
|
||||||
|
const classBindingNames =
|
||||||
|
this._singleClassInputs ? this._singleClassInputs.map(i => o.literal(i.name)) : [];
|
||||||
|
|
||||||
|
// to salvage space in the AOT generated code, there is no point in passing
|
||||||
|
// in `null` into a param if any follow-up params are not used. Therefore,
|
||||||
|
// only when a trailing param is used then it will be filled with nulls in between
|
||||||
|
// (otherwise a shorter amount of params will be filled). The code below helps
|
||||||
|
// determine how many params are required in the expression code.
|
||||||
|
//
|
||||||
|
// min params => elementStyling()
|
||||||
|
// max params => elementStyling(classBindings, styleBindings, sanitizer, directive)
|
||||||
|
let expectedNumberOfArgs = 0;
|
||||||
|
if (this._directiveExpr) {
|
||||||
|
expectedNumberOfArgs = 4;
|
||||||
|
} else if (this._useDefaultSanitizer) {
|
||||||
|
expectedNumberOfArgs = 3;
|
||||||
|
} else if (styleBindingProps.length) {
|
||||||
|
expectedNumberOfArgs = 2;
|
||||||
|
} else if (classBindingNames.length) {
|
||||||
|
expectedNumberOfArgs = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params: o.Expression[] = [];
|
||||||
|
addParam(
|
||||||
|
params, classBindingNames.length > 0,
|
||||||
|
getConstantLiteralFromArray(constantPool, classBindingNames), 1,
|
||||||
|
expectedNumberOfArgs);
|
||||||
|
addParam(
|
||||||
|
params, styleBindingProps.length > 0,
|
||||||
|
getConstantLiteralFromArray(constantPool, styleBindingProps), 2,
|
||||||
|
expectedNumberOfArgs);
|
||||||
|
addParam(
|
||||||
|
params, this._useDefaultSanitizer, o.importExpr(R3.defaultStyleSanitizer), 3,
|
||||||
|
expectedNumberOfArgs);
|
||||||
|
if (this._directiveExpr) {
|
||||||
|
params.push(this._directiveExpr);
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an instruction with all the expressions and parameters for `elementStylingMap`.
|
||||||
|
*
|
||||||
|
* The instruction data will contain all expressions for `elementStylingMap` to function
|
||||||
|
* which include the `[style]` and `[class]` expression params (if they exist) as well as
|
||||||
|
* the sanitizer and directive reference expression.
|
||||||
|
*/
|
||||||
|
buildElementStylingMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
|
||||||
if (this._classMapInput || this._styleMapInput) {
|
if (this._classMapInput || this._styleMapInput) {
|
||||||
const stylingInput = this._classMapInput ! || this._styleMapInput !;
|
const stylingInput = this._classMapInput ! || this._styleMapInput !;
|
||||||
|
|
||||||
@ -332,18 +411,20 @@ export class StylingBuilder {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs all instructions which contain the expressions that will be placed
|
||||||
|
* into the update block of a template function or a directive hostBindings function.
|
||||||
|
*/
|
||||||
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
||||||
const instructions: StylingInstruction[] = [];
|
const instructions: StylingInstruction[] = [];
|
||||||
if (this.hasBindingsOrInitialValues) {
|
if (this._hasBindings) {
|
||||||
const mapInstruction = this._buildStylingMap(valueConverter);
|
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
|
||||||
if (mapInstruction) {
|
if (mapInstruction) {
|
||||||
instructions.push(mapInstruction);
|
instructions.push(mapInstruction);
|
||||||
}
|
}
|
||||||
instructions.push(...this._buildStyleInputs(valueConverter));
|
instructions.push(...this._buildStyleInputs(valueConverter));
|
||||||
instructions.push(...this._buildClassInputs(valueConverter));
|
instructions.push(...this._buildClassInputs(valueConverter));
|
||||||
if (this._applyFnRequired) {
|
instructions.push(this._buildApplyFn());
|
||||||
instructions.push(this._buildApplyFn());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return instructions;
|
return instructions;
|
||||||
}
|
}
|
||||||
@ -363,3 +444,26 @@ function isStyleSanitizable(prop: string): boolean {
|
|||||||
return prop === 'background-image' || prop === 'background' || prop === 'border-image' ||
|
return prop === 'background-image' || prop === 'background' || prop === 'border-image' ||
|
||||||
prop === 'filter' || prop === 'list-style' || prop === 'list-style-image';
|
prop === 'filter' || prop === 'list-style' || prop === 'list-style-image';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple helper function to either provide the constant literal that will house the value
|
||||||
|
* here or a null value if the provided values are empty.
|
||||||
|
*/
|
||||||
|
function getConstantLiteralFromArray(
|
||||||
|
constantPool: ConstantPool, values: o.Expression[]): o.Expression {
|
||||||
|
return values.length ? constantPool.getConstLiteral(o.literalArr(values), true) : o.NULL_EXPR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple helper function that adds a parameter or does nothing at all depending on the provided
|
||||||
|
* predicate and totalExpectedArgs values
|
||||||
|
*/
|
||||||
|
function addParam(
|
||||||
|
params: o.Expression[], predicate: boolean, value: o.Expression, argNumber: number,
|
||||||
|
totalExpectedArgs: number) {
|
||||||
|
if (predicate) {
|
||||||
|
params.push(value);
|
||||||
|
} else if (argNumber < totalExpectedArgs) {
|
||||||
|
params.push(o.NULL_EXPR);
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import {flatten, sanitizeIdentifier} from '../../compile_metadata';
|
|||||||
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||||
import {ConstantPool} from '../../constant_pool';
|
import {ConstantPool} from '../../constant_pool';
|
||||||
import * as core from '../../core';
|
import * as core from '../../core';
|
||||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
import {AST, ASTWithSource, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
||||||
import {Lexer} from '../../expression_parser/lexer';
|
import {Lexer} from '../../expression_parser/lexer';
|
||||||
import {Parser} from '../../expression_parser/parser';
|
import {Parser} from '../../expression_parser/parser';
|
||||||
import * as i18n from '../../i18n/i18n_ast';
|
import * as i18n from '../../i18n/i18n_ast';
|
||||||
@ -35,7 +35,7 @@ import {I18nContext} from './i18n/context';
|
|||||||
import {I18nMetaVisitor} from './i18n/meta';
|
import {I18nMetaVisitor} from './i18n/meta';
|
||||||
import {getSerializedI18nContent} from './i18n/serializer';
|
import {getSerializedI18nContent} from './i18n/serializer';
|
||||||
import {I18N_ICU_MAPPING_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
import {I18N_ICU_MAPPING_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
||||||
import {StylingBuilder, StylingInstruction} from './styling';
|
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
||||||
|
|
||||||
function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined {
|
function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined {
|
||||||
@ -114,6 +114,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// Selectors found in the <ng-content> tags in the template.
|
// Selectors found in the <ng-content> tags in the template.
|
||||||
private _ngContentSelectors: string[] = [];
|
private _ngContentSelectors: string[] = [];
|
||||||
|
|
||||||
|
// Number of non-default selectors found in all parent templates of this template. We need to
|
||||||
|
// track it to properly adjust projection bucket index in the `projection` instruction.
|
||||||
|
private _ngContentSelectorsOffset = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
||||||
private contextName: string|null, private i18nContext: I18nContext|null,
|
private contextName: string|null, private i18nContext: I18nContext|null,
|
||||||
@ -166,7 +170,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTemplateFunction(nodes: t.Node[], variables: t.Variable[], i18n?: i18n.AST): o.FunctionExpr {
|
buildTemplateFunction(
|
||||||
|
nodes: t.Node[], variables: t.Variable[], ngContentSelectorsOffset: number = 0,
|
||||||
|
i18n?: i18n.AST): o.FunctionExpr {
|
||||||
|
this._ngContentSelectorsOffset = ngContentSelectorsOffset;
|
||||||
|
|
||||||
if (this._namespace !== R3.namespaceHTML) {
|
if (this._namespace !== R3.namespaceHTML) {
|
||||||
this.creationInstruction(null, this._namespace);
|
this.creationInstruction(null, this._namespace);
|
||||||
}
|
}
|
||||||
@ -192,8 +200,23 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
||||||
t.visitAll(this, nodes);
|
t.visitAll(this, nodes);
|
||||||
|
|
||||||
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
|
// Add total binding count to pure function count so pure function instructions are
|
||||||
if (this._hasNgContent) {
|
// generated with the correct slot offset when update instructions are processed.
|
||||||
|
this._pureFunctionSlots += this._bindingSlots;
|
||||||
|
|
||||||
|
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
||||||
|
// `pipeBind` update instructions), so we have to update the slot offsets manually
|
||||||
|
// to account for bindings.
|
||||||
|
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
||||||
|
|
||||||
|
// Nested templates must be processed before creation instructions so template()
|
||||||
|
// instructions can be generated with the correct internal const count.
|
||||||
|
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
||||||
|
|
||||||
|
// Output the `projectionDef` instruction when some `<ng-content>` are present.
|
||||||
|
// The `projectionDef` instruction only emitted for the component template and it is skipped for
|
||||||
|
// nested templates (<ng-template> tags).
|
||||||
|
if (this.level === 0 && this._hasNgContent) {
|
||||||
const parameters: o.Expression[] = [];
|
const parameters: o.Expression[] = [];
|
||||||
|
|
||||||
// Only selectors with a non-default value are generated
|
// Only selectors with a non-default value are generated
|
||||||
@ -212,19 +235,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
this.creationInstruction(null, R3.projectionDef, parameters, /* prepend */ true);
|
this.creationInstruction(null, R3.projectionDef, parameters, /* prepend */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add total binding count to pure function count so pure function instructions are
|
|
||||||
// generated with the correct slot offset when update instructions are processed.
|
|
||||||
this._pureFunctionSlots += this._bindingSlots;
|
|
||||||
|
|
||||||
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
|
||||||
// `pipeBind` update instructions), so we have to update the slot offsets manually
|
|
||||||
// to account for bindings.
|
|
||||||
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
|
||||||
|
|
||||||
// Nested templates must be processed before creation instructions so template()
|
|
||||||
// instructions can be generated with the correct internal const count.
|
|
||||||
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
|
||||||
|
|
||||||
if (initI18nContext) {
|
if (initI18nContext) {
|
||||||
this.i18nEnd(null, selfClosingI18nInstruction);
|
this.i18nEnd(null, selfClosingI18nInstruction);
|
||||||
}
|
}
|
||||||
@ -419,7 +429,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
const slot = this.allocateDataSlot();
|
const slot = this.allocateDataSlot();
|
||||||
let selectorIndex = ngContent.selector === DEFAULT_NG_CONTENT_SELECTOR ?
|
let selectorIndex = ngContent.selector === DEFAULT_NG_CONTENT_SELECTOR ?
|
||||||
0 :
|
0 :
|
||||||
this._ngContentSelectors.push(ngContent.selector);
|
this._ngContentSelectors.push(ngContent.selector) + this._ngContentSelectorsOffset;
|
||||||
const parameters: o.Expression[] = [o.literal(slot)];
|
const parameters: o.Expression[] = [o.literal(slot)];
|
||||||
|
|
||||||
const attributeAsList: string[] = [];
|
const attributeAsList: string[] = [];
|
||||||
@ -522,7 +532,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
|
|
||||||
// this will build the instructions so that they fall into the following syntax
|
// this will build the instructions so that they fall into the following syntax
|
||||||
// add attributes for directive matching purposes
|
// add attributes for directive matching purposes
|
||||||
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(allOtherInputs, element.outputs));
|
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(
|
||||||
|
allOtherInputs, element.outputs, stylingBuilder));
|
||||||
parameters.push(this.toAttrsParam(attributes));
|
parameters.push(this.toAttrsParam(attributes));
|
||||||
|
|
||||||
// local refs (ex.: <div #foo #bar="baz">)
|
// local refs (ex.: <div #foo #bar="baz">)
|
||||||
@ -552,11 +563,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
return element.children.length > 0;
|
return element.children.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues &&
|
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues() &&
|
||||||
!isNgContainer && element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren();
|
!isNgContainer && element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren();
|
||||||
|
|
||||||
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
|
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
|
||||||
!stylingBuilder.hasBindingsOrInitialValues && hasTextChildrenOnly(element.children);
|
!stylingBuilder.hasBindingsOrInitialValues() && hasTextChildrenOnly(element.children);
|
||||||
|
|
||||||
if (createSelfClosingInstruction) {
|
if (createSelfClosingInstruction) {
|
||||||
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
|
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
|
||||||
@ -606,10 +617,16 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial styling for static style="..." and class="..." attributes
|
// The style bindings code is placed into two distinct blocks within the template function AOT
|
||||||
|
// code: creation and update. The creation code contains the `elementStyling` instructions
|
||||||
|
// which will apply the collected binding values to the element. `elementStyling` is
|
||||||
|
// designed to run inside of `elementStart` and `elementEnd`. The update instructions
|
||||||
|
// (things like `elementStyleProp`, `elementClassProp`, etc..) are applied later on in this
|
||||||
|
// file
|
||||||
this.processStylingInstruction(
|
this.processStylingInstruction(
|
||||||
implicit,
|
implicit,
|
||||||
stylingBuilder.buildCreateLevelInstruction(element.sourceSpan, this.constantPool), true);
|
stylingBuilder.buildElementStylingInstruction(element.sourceSpan, this.constantPool),
|
||||||
|
true);
|
||||||
|
|
||||||
// Generate Listeners (outputs)
|
// Generate Listeners (outputs)
|
||||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||||
@ -619,6 +636,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the code here will collect all update-level styling instructions and add them to the
|
||||||
|
// update block of the template function AOT code. Instructions like `elementStyleProp`,
|
||||||
|
// `elementStylingMap`, `elementClassProp` and `elementStylingApply` are all generated
|
||||||
|
// and assign in the code below.
|
||||||
stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => {
|
stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => {
|
||||||
this.processStylingInstruction(implicit, instruction, false);
|
this.processStylingInstruction(implicit, instruction, false);
|
||||||
});
|
});
|
||||||
@ -738,8 +759,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
|
// template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
|
||||||
this._nestedTemplateFns.push(() => {
|
this._nestedTemplateFns.push(() => {
|
||||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
|
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
|
||||||
template.children, template.variables, template.i18n);
|
template.children, template.variables,
|
||||||
|
this._ngContentSelectors.length + this._ngContentSelectorsOffset, template.i18n);
|
||||||
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||||
|
if (templateVisitor._hasNgContent) {
|
||||||
|
this._hasNgContent = true;
|
||||||
|
this._ngContentSelectors.push(...templateVisitor._ngContentSelectors);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// e.g. template(1, MyComp_Template_1)
|
// e.g. template(1, MyComp_Template_1)
|
||||||
@ -919,10 +945,51 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareSyntheticAndSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]):
|
/**
|
||||||
o.Expression[] {
|
* Prepares all attribute expression values for the `TAttributes` array.
|
||||||
|
*
|
||||||
|
* The purpose of this function is to properly construct an attributes array that
|
||||||
|
* is passed into the `elementStart` (or just `element`) functions. Because there
|
||||||
|
* are many different types of attributes, the array needs to be constructed in a
|
||||||
|
* special way so that `elementStart` can properly evaluate them.
|
||||||
|
*
|
||||||
|
* The format looks like this:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* attrs = [prop, value, prop2, value2,
|
||||||
|
* CLASSES, class1, class2,
|
||||||
|
* STYLES, style1, value1, style2, value2,
|
||||||
|
* SELECT_ONLY, name1, name2, name2, ...]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private prepareSyntheticAndSelectOnlyAttrs(
|
||||||
|
inputs: t.BoundAttribute[], outputs: t.BoundEvent[],
|
||||||
|
styles?: StylingBuilder): o.Expression[] {
|
||||||
const attrExprs: o.Expression[] = [];
|
const attrExprs: o.Expression[] = [];
|
||||||
const nonSyntheticInputs: t.BoundAttribute[] = [];
|
const nonSyntheticInputs: t.BoundAttribute[] = [];
|
||||||
|
const alreadySeen = new Set<string>();
|
||||||
|
|
||||||
|
function isASTWithSource(ast: AST): ast is ASTWithSource {
|
||||||
|
return ast instanceof ASTWithSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLiteralPrimitive(ast: AST): ast is LiteralPrimitive {
|
||||||
|
return ast instanceof LiteralPrimitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAttrExpr(key: string | number, value?: o.Expression): void {
|
||||||
|
if (typeof key === 'string') {
|
||||||
|
if (!alreadySeen.has(key)) {
|
||||||
|
attrExprs.push(o.literal(key));
|
||||||
|
if (value !== undefined) {
|
||||||
|
attrExprs.push(value);
|
||||||
|
}
|
||||||
|
alreadySeen.add(key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attrExprs.push(o.literal(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (inputs.length) {
|
if (inputs.length) {
|
||||||
const EMPTY_STRING_EXPR = asLiteral('');
|
const EMPTY_STRING_EXPR = asLiteral('');
|
||||||
@ -932,17 +999,30 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// may be supported differently in future versions of angular. However,
|
// may be supported differently in future versions of angular. However,
|
||||||
// @triggers should always just be treated as regular attributes (it's up
|
// @triggers should always just be treated as regular attributes (it's up
|
||||||
// to the renderer to detect and use them in a special way).
|
// to the renderer to detect and use them in a special way).
|
||||||
attrExprs.push(asLiteral(prepareSyntheticAttributeName(input.name)), EMPTY_STRING_EXPR);
|
const valueExp = input.value;
|
||||||
|
if (isASTWithSource(valueExp)) {
|
||||||
|
const literal = valueExp.ast;
|
||||||
|
if (isLiteralPrimitive(literal) && literal.value === undefined) {
|
||||||
|
addAttrExpr(prepareSyntheticAttributeName(input.name), EMPTY_STRING_EXPR);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nonSyntheticInputs.push(input);
|
nonSyntheticInputs.push(input);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// it's important that this occurs before SelectOnly because once `elementStart`
|
||||||
|
// comes across the SelectOnly marker then it will continue reading each value as
|
||||||
|
// as single property value cell by cell.
|
||||||
|
if (styles) {
|
||||||
|
styles.populateInitialStylingAttrs(attrExprs);
|
||||||
|
}
|
||||||
|
|
||||||
if (nonSyntheticInputs.length || outputs.length) {
|
if (nonSyntheticInputs.length || outputs.length) {
|
||||||
attrExprs.push(o.literal(core.AttributeMarker.SelectOnly));
|
addAttrExpr(core.AttributeMarker.SelectOnly);
|
||||||
nonSyntheticInputs.forEach((i: t.BoundAttribute) => attrExprs.push(asLiteral(i.name)));
|
nonSyntheticInputs.forEach((i: t.BoundAttribute) => addAttrExpr(i.name));
|
||||||
outputs.forEach((o: t.BoundEvent) => attrExprs.push(asLiteral(o.name)));
|
outputs.forEach((o: t.BoundEvent) => addAttrExpr(o.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return attrExprs;
|
return attrExprs;
|
||||||
|
@ -53,7 +53,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
function compileApp(): GeneratedFile {
|
function compileApp(): GeneratedFile {
|
||||||
const {genFiles} = compile([rootDir, angularFiles]);
|
const {genFiles} = compile([rootDir, angularFiles]);
|
||||||
return genFiles.find(
|
return genFiles.find(
|
||||||
genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'));
|
genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts')) !;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLineAndColumn(
|
function findLineAndColumn(
|
||||||
@ -321,7 +321,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
const genFilePreamble = '/* Hello world! */';
|
const genFilePreamble = '/* Hello world! */';
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile =
|
const genFile =
|
||||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts')) !;
|
||||||
const genSource = toTypeScript(genFile, genFilePreamble);
|
const genSource = toTypeScript(genFile, genFilePreamble);
|
||||||
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
||||||
});
|
});
|
||||||
@ -440,7 +440,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
||||||
// selector
|
// selector
|
||||||
@ -471,7 +471,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
};
|
};
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile =
|
const genFile =
|
||||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts')) !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
expect(genSource).not.toContain('check(');
|
expect(genSource).not.toContain('check(');
|
||||||
|
|
||||||
@ -510,7 +510,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||||
const {genFiles: appGenFiles} =
|
const {genFiles: appGenFiles} =
|
||||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !;
|
||||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||||
expect(appNgFactoryTs).not.toContain('AType');
|
expect(appNgFactoryTs).not.toContain('AType');
|
||||||
expect(appNgFactoryTs).toContain('AValue');
|
expect(appNgFactoryTs).toContain('AValue');
|
||||||
@ -557,7 +557,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||||
const {genFiles: appGenFiles} =
|
const {genFiles: appGenFiles} =
|
||||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !;
|
||||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||||
|
|
||||||
// metadata of ctor calls is preserved, so we reexport the argument
|
// metadata of ctor calls is preserved, so we reexport the argument
|
||||||
@ -598,7 +598,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||||
const {genFiles: appGenFiles} =
|
const {genFiles: appGenFiles} =
|
||||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !;
|
||||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||||
|
|
||||||
// we don't need to reexport exported symbols via the .ngfactory
|
// we don't need to reexport exported symbols via the .ngfactory
|
||||||
@ -690,7 +690,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const {genFiles} =
|
const {genFiles} =
|
||||||
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts') !;
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
expect(toTypeScript(mainNgFactory))
|
expect(toTypeScript(mainNgFactory))
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
||||||
@ -743,7 +743,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
compile([lib1OutDir, lib2Input, getAngularSummaryFiles()], {useSummaries: true});
|
compile([lib1OutDir, lib2Input, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const {genFiles} =
|
const {genFiles} =
|
||||||
compile([lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts') !;
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
expect(toTypeScript(mainNgFactory))
|
expect(toTypeScript(mainNgFactory))
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
||||||
|
@ -41,7 +41,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
@ -70,7 +70,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
@ -99,7 +99,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
@ -125,7 +125,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
@ -164,7 +164,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
@ -198,7 +198,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
@ -225,7 +225,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(
|
expect(genSource).toMatch(
|
||||||
@ -247,7 +247,7 @@ describe('aot summaries for jit', () => {
|
|||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !;
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(
|
expect(genSource).toMatch(
|
||||||
@ -294,9 +294,10 @@ describe('aot summaries for jit', () => {
|
|||||||
};
|
};
|
||||||
const {outDir: lib3In, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true});
|
const {outDir: lib3In, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true});
|
||||||
|
|
||||||
const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts');
|
const lib2ModuleNgSummary =
|
||||||
|
lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts') !;
|
||||||
const lib2ReexportNgSummary =
|
const lib2ReexportNgSummary =
|
||||||
lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts');
|
lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts') !;
|
||||||
|
|
||||||
// ngsummaries should add reexports for imported NgModules from a direct dependency
|
// ngsummaries should add reexports for imported NgModules from a direct dependency
|
||||||
expect(toTypeScript(lib2ModuleNgSummary))
|
expect(toTypeScript(lib2ModuleNgSummary))
|
||||||
@ -325,9 +326,10 @@ describe('aot summaries for jit', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles;
|
const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles;
|
||||||
const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts');
|
const lib3ModuleNgSummary =
|
||||||
|
lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts') !;
|
||||||
const lib3ReexportNgSummary =
|
const lib3ReexportNgSummary =
|
||||||
lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts');
|
lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts') !;
|
||||||
|
|
||||||
// ngsummary.ts files should use the reexported values from direct and deep deps
|
// ngsummary.ts files should use the reexported values from direct and deep deps
|
||||||
const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary);
|
const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary);
|
||||||
|
@ -422,7 +422,7 @@ export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
|
|||||||
}[] = []) {}
|
}[] = []) {}
|
||||||
addSummary(summary: Summary<StaticSymbol>) { this.summaries.push(summary); }
|
addSummary(summary: Summary<StaticSymbol>) { this.summaries.push(summary); }
|
||||||
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
|
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
|
||||||
return this.summaries.find(summary => summary.symbol === reference);
|
return this.summaries.find(summary => summary.symbol === reference) !;
|
||||||
}
|
}
|
||||||
getSymbolsOf(filePath: string): StaticSymbol[]|null {
|
getSymbolsOf(filePath: string): StaticSymbol[]|null {
|
||||||
const symbols = this.summaries.filter(summary => summary.symbol.filePath === filePath)
|
const symbols = this.summaries.filter(summary => summary.symbol.filePath === filePath)
|
||||||
|
@ -10,55 +10,53 @@ import {hyphenate, parse as parseStyle, stripUnnecessaryQuotes} from '../../src/
|
|||||||
describe('style parsing', () => {
|
describe('style parsing', () => {
|
||||||
it('should parse empty or blank strings', () => {
|
it('should parse empty or blank strings', () => {
|
||||||
const result1 = parseStyle('');
|
const result1 = parseStyle('');
|
||||||
expect(result1).toEqual({});
|
expect(result1).toEqual([]);
|
||||||
|
|
||||||
const result2 = parseStyle(' ');
|
const result2 = parseStyle(' ');
|
||||||
expect(result2).toEqual({});
|
expect(result2).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse a string into a key/value map', () => {
|
it('should parse a string into a key/value map', () => {
|
||||||
const result = parseStyle('width:100px;height:200px;opacity:0');
|
const result = parseStyle('width:100px;height:200px;opacity:0');
|
||||||
expect(result).toEqual({width: '100px', height: '200px', opacity: '0'});
|
expect(result).toEqual(['width', '100px', 'height', '200px', 'opacity', '0']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trim values and properties', () => {
|
it('should trim values and properties', () => {
|
||||||
const result = parseStyle('width :333px ; height:666px ; opacity: 0.5;');
|
const result = parseStyle('width :333px ; height:666px ; opacity: 0.5;');
|
||||||
expect(result).toEqual({width: '333px', height: '666px', opacity: '0.5'});
|
expect(result).toEqual(['width', '333px', 'height', '666px', 'opacity', '0.5']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should chomp out start/end quotes', () => {
|
it('should chomp out start/end quotes', () => {
|
||||||
const result = parseStyle(
|
const result = parseStyle(
|
||||||
'content: "foo"; opacity: \'0.5\'; font-family: "Verdana", Helvetica, "sans-serif"');
|
'content: "foo"; opacity: \'0.5\'; font-family: "Verdana", Helvetica, "sans-serif"');
|
||||||
expect(result).toEqual(
|
expect(result).toEqual(
|
||||||
{content: 'foo', opacity: '0.5', 'font-family': '"Verdana", Helvetica, "sans-serif"'});
|
['content', 'foo', 'opacity', '0.5', 'font-family', '"Verdana", Helvetica, "sans-serif"']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not mess up with quoted strings that contain [:;] values', () => {
|
it('should not mess up with quoted strings that contain [:;] values', () => {
|
||||||
const result = parseStyle('content: "foo; man: guy"; width: 100px');
|
const result = parseStyle('content: "foo; man: guy"; width: 100px');
|
||||||
expect(result).toEqual({content: 'foo; man: guy', width: '100px'});
|
expect(result).toEqual(['content', 'foo; man: guy', 'width', '100px']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not mess up with quoted strings that contain inner quote values', () => {
|
it('should not mess up with quoted strings that contain inner quote values', () => {
|
||||||
const quoteStr = '"one \'two\' three \"four\" five"';
|
const quoteStr = '"one \'two\' three \"four\" five"';
|
||||||
const result = parseStyle(`content: ${quoteStr}; width: 123px`);
|
const result = parseStyle(`content: ${quoteStr}; width: 123px`);
|
||||||
expect(result).toEqual({content: quoteStr, width: '123px'});
|
expect(result).toEqual(['content', quoteStr, 'width', '123px']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should respect parenthesis that are placed within a style', () => {
|
it('should respect parenthesis that are placed within a style', () => {
|
||||||
const result = parseStyle('background-image: url("foo.jpg")');
|
const result = parseStyle('background-image: url("foo.jpg")');
|
||||||
expect(result).toEqual({'background-image': 'url("foo.jpg")'});
|
expect(result).toEqual(['background-image', 'url("foo.jpg")']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should respect multi-level parenthesis that contain special [:;] characters', () => {
|
it('should respect multi-level parenthesis that contain special [:;] characters', () => {
|
||||||
const result = parseStyle('color: rgba(calc(50 * 4), var(--cool), :5;); height: 100px;');
|
const result = parseStyle('color: rgba(calc(50 * 4), var(--cool), :5;); height: 100px;');
|
||||||
expect(result).toEqual({color: 'rgba(calc(50 * 4), var(--cool), :5;)', height: '100px'});
|
expect(result).toEqual(['color', 'rgba(calc(50 * 4), var(--cool), :5;)', 'height', '100px']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hyphenate style properties from camel case', () => {
|
it('should hyphenate style properties from camel case', () => {
|
||||||
const result = parseStyle('borderWidth: 200px');
|
const result = parseStyle('borderWidth: 200px');
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(['border-width', '200px']);
|
||||||
'border-width': '200px',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('quote chomping', () => {
|
describe('quote chomping', () => {
|
||||||
|
@ -17,7 +17,7 @@ export enum ChangeDetectionStrategy {
|
|||||||
/**
|
/**
|
||||||
* Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
|
* Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
|
||||||
* until reactivated by setting the strategy to `Default` (`CheckAlways`).
|
* until reactivated by setting the strategy to `Default` (`CheckAlways`).
|
||||||
* Change detection can still be explictly invoked.
|
* Change detection can still be explicitly invoked.
|
||||||
*/
|
*/
|
||||||
OnPush = 0,
|
OnPush = 0,
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export enum ChangeDetectorStatus {
|
|||||||
Checked,
|
Checked,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A state in which change detection continues automatically until explictly
|
* A state in which change detection continues automatically until explicitly
|
||||||
* deactivated.
|
* deactivated.
|
||||||
*/
|
*/
|
||||||
CheckAlways,
|
CheckAlways,
|
||||||
|
@ -92,6 +92,7 @@ export {
|
|||||||
elementContainerStart as ɵelementContainerStart,
|
elementContainerStart as ɵelementContainerStart,
|
||||||
elementContainerEnd as ɵelementContainerEnd,
|
elementContainerEnd as ɵelementContainerEnd,
|
||||||
elementStyling as ɵelementStyling,
|
elementStyling as ɵelementStyling,
|
||||||
|
elementHostAttrs as ɵelementHostAttrs,
|
||||||
elementStylingMap as ɵelementStylingMap,
|
elementStylingMap as ɵelementStylingMap,
|
||||||
elementStyleProp as ɵelementStyleProp,
|
elementStyleProp as ɵelementStyleProp,
|
||||||
elementStylingApply as ɵelementStylingApply,
|
elementStylingApply as ɵelementStylingApply,
|
||||||
|
@ -12,7 +12,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners,
|
|||||||
import {TNode} from '../render3/interfaces/node';
|
import {TNode} from '../render3/interfaces/node';
|
||||||
import {StylingIndex} from '../render3/interfaces/styling';
|
import {StylingIndex} from '../render3/interfaces/styling';
|
||||||
import {TVIEW} from '../render3/interfaces/view';
|
import {TVIEW} from '../render3/interfaces/view';
|
||||||
import {getProp, getValue, isClassBased} from '../render3/styling/class_and_style_bindings';
|
import {getProp, getValue, isClassBasedValue} from '../render3/styling/class_and_style_bindings';
|
||||||
import {getStylingContext} from '../render3/styling/util';
|
import {getStylingContext} from '../render3/styling/util';
|
||||||
import {DebugContext} from '../view/index';
|
import {DebugContext} from '../view/index';
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
|||||||
if (stylingContext) {
|
if (stylingContext) {
|
||||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||||
i += StylingIndex.Size) {
|
i += StylingIndex.Size) {
|
||||||
if (isClassBased(lNode, i)) {
|
if (isClassBasedValue(lNode, i)) {
|
||||||
const className = getProp(lNode, i);
|
const className = getProp(lNode, i);
|
||||||
const value = getValue(lNode, i);
|
const value = getValue(lNode, i);
|
||||||
if (typeof value == 'boolean') {
|
if (typeof value == 'boolean') {
|
||||||
@ -303,7 +303,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
|||||||
if (stylingContext) {
|
if (stylingContext) {
|
||||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||||
i += StylingIndex.Size) {
|
i += StylingIndex.Size) {
|
||||||
if (!isClassBased(lNode, i)) {
|
if (!isClassBasedValue(lNode, i)) {
|
||||||
const styleName = getProp(lNode, i);
|
const styleName = getProp(lNode, i);
|
||||||
const value = getValue(lNode, i) as string | null;
|
const value = getValue(lNode, i) as string | null;
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
|
@ -11,7 +11,7 @@ import {devModeEqual} from '../change_detection/change_detection_util';
|
|||||||
import {assertDataInRange, assertLessThan, assertNotEqual} from './assert';
|
import {assertDataInRange, assertLessThan, assertNotEqual} from './assert';
|
||||||
import {throwErrorIfNoChangesMode} from './errors';
|
import {throwErrorIfNoChangesMode} from './errors';
|
||||||
import {BINDING_INDEX, LView} from './interfaces/view';
|
import {BINDING_INDEX, LView} from './interfaces/view';
|
||||||
import {getCheckNoChangesMode, getCreationMode} from './state';
|
import {getCheckNoChangesMode, isCreationMode} from './state';
|
||||||
import {NO_CHANGE} from './tokens';
|
import {NO_CHANGE} from './tokens';
|
||||||
import {isDifferent} from './util';
|
import {isDifferent} from './util';
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export function bindingUpdated(lView: LView, bindingIndex: number, value: any):
|
|||||||
} else if (isDifferent(lView[bindingIndex], value)) {
|
} else if (isDifferent(lView[bindingIndex], value)) {
|
||||||
if (ngDevMode && getCheckNoChangesMode()) {
|
if (ngDevMode && getCheckNoChangesMode()) {
|
||||||
if (!devModeEqual(lView[bindingIndex], value)) {
|
if (!devModeEqual(lView[bindingIndex], value)) {
|
||||||
throwErrorIfNoChangesMode(getCreationMode(), lView[bindingIndex], value);
|
throwErrorIfNoChangesMode(isCreationMode(lView), lView[bindingIndex], value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lView[bindingIndex] = value;
|
lView[bindingIndex] = value;
|
||||||
|
@ -22,7 +22,7 @@ import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'
|
|||||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||||
import {PlayerHandler} from './interfaces/player';
|
import {PlayerHandler} from './interfaces/player';
|
||||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
||||||
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
|
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
|
||||||
|
|
||||||
@ -133,7 +133,9 @@ export function renderComponent<T>(
|
|||||||
component = createRootComponent(
|
component = createRootComponent(
|
||||||
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
|
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
|
||||||
|
|
||||||
refreshDescendantViews(rootView, null);
|
refreshDescendantViews(rootView); // creation mode pass
|
||||||
|
rootView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||||
|
refreshDescendantViews(rootView); // update mode pass
|
||||||
} finally {
|
} finally {
|
||||||
leaveView(oldView);
|
leaveView(oldView);
|
||||||
if (rendererFactory.end) rendererFactory.end();
|
if (rendererFactory.end) rendererFactory.end();
|
||||||
|
@ -208,10 +208,9 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||||||
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
||||||
|
|
||||||
addToViewTree(rootLView, HEADER_OFFSET, componentView);
|
addToViewTree(rootLView, HEADER_OFFSET, componentView);
|
||||||
|
refreshDescendantViews(rootLView);
|
||||||
refreshDescendantViews(rootLView, RenderFlags.Create);
|
|
||||||
} finally {
|
} finally {
|
||||||
leaveView(oldLView, true);
|
leaveView(oldLView);
|
||||||
if (rendererFactory.end) rendererFactory.end();
|
if (rendererFactory.end) rendererFactory.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,12 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
import {assertDomNode} from './assert';
|
import {assertDomNode} from './assert';
|
||||||
import {EMPTY_ARRAY} from './definition';
|
import {EMPTY_ARRAY} from './empty';
|
||||||
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
||||||
import {TNode, TNodeFlags} from './interfaces/node';
|
import {TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {RElement} from './interfaces/renderer';
|
import {RElement} from './interfaces/renderer';
|
||||||
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
|
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
|
||||||
import {getComponentViewByIndex, getNativeByTNode, getTNode, readElementValue, readPatchedData} from './util';
|
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
|
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
|
||||||
|
@ -9,22 +9,15 @@
|
|||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||||
import {Provider} from '../di/provider';
|
|
||||||
import {NgModuleDef} from '../metadata/ng_module';
|
import {NgModuleDef} from '../metadata/ng_module';
|
||||||
import {ViewEncapsulation} from '../metadata/view';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {Mutable, Type} from '../type';
|
import {Mutable, Type} from '../type';
|
||||||
import {noSideEffects, stringify} from '../util';
|
import {noSideEffects, stringify} from '../util';
|
||||||
|
import {EMPTY_ARRAY, EMPTY_OBJ} from './empty';
|
||||||
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields';
|
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields';
|
||||||
import {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
|
import {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
|
||||||
import {CssSelectorList, SelectorFlags} from './interfaces/projection';
|
import {CssSelectorList} from './interfaces/projection';
|
||||||
|
|
||||||
export const EMPTY: {} = {};
|
|
||||||
export const EMPTY_ARRAY: any[] = [];
|
|
||||||
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
||||||
Object.freeze(EMPTY);
|
|
||||||
Object.freeze(EMPTY_ARRAY);
|
|
||||||
}
|
|
||||||
let _renderCompCount = 0;
|
let _renderCompCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -389,7 +382,7 @@ export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T>>): nev
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
function invertObject(obj: any, secondary?: any): any {
|
function invertObject(obj: any, secondary?: any): any {
|
||||||
if (obj == null) return EMPTY;
|
if (obj == null) return EMPTY_OBJ;
|
||||||
const newLookup: any = {};
|
const newLookup: any = {};
|
||||||
for (const minifiedKey in obj) {
|
for (const minifiedKey in obj) {
|
||||||
if (obj.hasOwnProperty(minifiedKey)) {
|
if (obj.hasOwnProperty(minifiedKey)) {
|
||||||
|
@ -21,7 +21,8 @@ import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TN
|
|||||||
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LView, TData, TVIEW, TView} from './interfaces/view';
|
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LView, TData, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
|
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
|
||||||
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, stringify} from './util';
|
import {getHostTElementNode, getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, isComponentDef, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines if the call to `inject` should include `viewProviders` in its resolution.
|
* Defines if the call to `inject` should include `viewProviders` in its resolution.
|
||||||
@ -197,7 +198,7 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number {
|
|||||||
*/
|
*/
|
||||||
export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation {
|
export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation {
|
||||||
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
||||||
return tNode.parent.injectorIndex as any; // ViewOffset is 0, AcrossHostBoundary is 0
|
return tNode.parent.injectorIndex as any; // ViewOffset is 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// For most cases, the parent injector index can be found on the host node (e.g. for component
|
// For most cases, the parent injector index can be found on the host node (e.g. for component
|
||||||
@ -210,13 +211,9 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn
|
|||||||
hostTNode = view[HOST_NODE] !;
|
hostTNode = view[HOST_NODE] !;
|
||||||
viewOffset++;
|
viewOffset++;
|
||||||
}
|
}
|
||||||
const acrossHostBoundary = hostTNode && hostTNode.type === TNodeType.Element ?
|
|
||||||
RelativeInjectorLocationFlags.AcrossHostBoundary :
|
|
||||||
0;
|
|
||||||
|
|
||||||
return hostTNode ?
|
return hostTNode ?
|
||||||
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) |
|
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) :
|
||||||
acrossHostBoundary :
|
|
||||||
-1 as any;
|
-1 as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,6 +320,7 @@ export function getOrCreateInjectable<T>(
|
|||||||
let previousTView: TView|null = null;
|
let previousTView: TView|null = null;
|
||||||
let injectorIndex = getInjectorIndex(tNode, lView);
|
let injectorIndex = getInjectorIndex(tNode, lView);
|
||||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||||
|
let hostTElementNode: TNode|null = flags & InjectFlags.Host ? getHostTElementNode(lView) : null;
|
||||||
|
|
||||||
// If we should skip this injector, or if there is no injector on this node, start by searching
|
// If we should skip this injector, or if there is no injector on this node, start by searching
|
||||||
// the parent injector.
|
// the parent injector.
|
||||||
@ -330,7 +328,7 @@ export function getOrCreateInjectable<T>(
|
|||||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
||||||
lView[injectorIndex + PARENT_INJECTOR];
|
lView[injectorIndex + PARENT_INJECTOR];
|
||||||
|
|
||||||
if (!shouldSearchParent(flags, parentLocation)) {
|
if (!shouldSearchParent(flags, false)) {
|
||||||
injectorIndex = -1;
|
injectorIndex = -1;
|
||||||
} else {
|
} else {
|
||||||
previousTView = lView[TVIEW];
|
previousTView = lView[TVIEW];
|
||||||
@ -350,13 +348,14 @@ export function getOrCreateInjectable<T>(
|
|||||||
// At this point, we have an injector which *may* contain the token, so we step through
|
// At this point, we have an injector which *may* contain the token, so we step through
|
||||||
// the providers and directives associated with the injector's corresponding node to get
|
// the providers and directives associated with the injector's corresponding node to get
|
||||||
// the instance.
|
// the instance.
|
||||||
const instance: T|null =
|
const instance: T|null = searchTokensOnInjector<T>(
|
||||||
searchTokensOnInjector<T>(injectorIndex, lView, token, previousTView);
|
injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
||||||
if (instance !== NOT_FOUND) {
|
if (instance !== NOT_FOUND) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldSearchParent(flags, parentLocation) &&
|
if (shouldSearchParent(
|
||||||
|
flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) &&
|
||||||
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
||||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||||
// Traverse up the tree and continue searching.
|
// Traverse up the tree and continue searching.
|
||||||
@ -396,7 +395,7 @@ const NOT_FOUND = {};
|
|||||||
|
|
||||||
function searchTokensOnInjector<T>(
|
function searchTokensOnInjector<T>(
|
||||||
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
|
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
|
||||||
previousTView: TView | null) {
|
previousTView: TView | null, flags: InjectFlags, hostTElementNode: TNode | null) {
|
||||||
const currentTView = lView[TVIEW];
|
const currentTView = lView[TVIEW];
|
||||||
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
|
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
|
||||||
// First, we need to determine if view providers can be accessed by the starting element.
|
// First, we need to determine if view providers can be accessed by the starting element.
|
||||||
@ -418,7 +417,12 @@ function searchTokensOnInjector<T>(
|
|||||||
// into the ViewProviders.
|
// into the ViewProviders.
|
||||||
(previousTView != currentTView && (tNode.type === TNodeType.Element));
|
(previousTView != currentTView && (tNode.type === TNodeType.Element));
|
||||||
|
|
||||||
const injectableIdx = locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders);
|
// This special case happens when there is a @host on the inject and when we are searching
|
||||||
|
// on the host element node.
|
||||||
|
const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
|
||||||
|
|
||||||
|
const injectableIdx =
|
||||||
|
locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders, isHostSpecialCase);
|
||||||
if (injectableIdx !== null) {
|
if (injectableIdx !== null) {
|
||||||
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
|
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
|
||||||
} else {
|
} else {
|
||||||
@ -433,13 +437,13 @@ function searchTokensOnInjector<T>(
|
|||||||
* @param lView The view we are currently processing
|
* @param lView The view we are currently processing
|
||||||
* @param token Provider token or type of a directive to look for.
|
* @param token Provider token or type of a directive to look for.
|
||||||
* @param canAccessViewProviders Whether view providers should be considered.
|
* @param canAccessViewProviders Whether view providers should be considered.
|
||||||
|
* @param isHostSpecialCase Whether the host special case applies.
|
||||||
* @returns Index of a found directive or provider, or null when none found.
|
* @returns Index of a found directive or provider, or null when none found.
|
||||||
*/
|
*/
|
||||||
export function locateDirectiveOrProvider<T>(
|
export function locateDirectiveOrProvider<T>(
|
||||||
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>,
|
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>, canAccessViewProviders: boolean,
|
||||||
canAccessViewProviders: boolean): number|null {
|
isHostSpecialCase: boolean | number): number|null {
|
||||||
const tView = lView[TVIEW];
|
const tView = lView[TVIEW];
|
||||||
const nodeFlags = tNode.flags;
|
|
||||||
const nodeProviderIndexes = tNode.providerIndexes;
|
const nodeProviderIndexes = tNode.providerIndexes;
|
||||||
const tInjectables = tView.data;
|
const tInjectables = tView.data;
|
||||||
|
|
||||||
@ -450,13 +454,21 @@ export function locateDirectiveOrProvider<T>(
|
|||||||
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
||||||
const startingIndex =
|
const startingIndex =
|
||||||
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
||||||
for (let i = startingIndex; i < directiveEnd; i++) {
|
// When the host special case applies, only the viewProviders and the component are visible
|
||||||
|
const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
|
||||||
|
for (let i = startingIndex; i < endIndex; i++) {
|
||||||
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
|
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
|
||||||
if (i < directivesStart && token === providerTokenOrDef ||
|
if (i < directivesStart && token === providerTokenOrDef ||
|
||||||
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isHostSpecialCase) {
|
||||||
|
const dirDef = tInjectables[directivesStart] as DirectiveDef<any>;
|
||||||
|
if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
|
||||||
|
return directivesStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,12 +558,8 @@ export function bloomHasToken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if flags prevent parent injector from being searched for tokens */
|
/** Returns true if flags prevent parent injector from being searched for tokens */
|
||||||
function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjectorLocation): boolean|
|
function shouldSearchParent(flags: InjectFlags, isFirstHostTNode: boolean): boolean|number {
|
||||||
number {
|
return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
|
||||||
return !(
|
|
||||||
flags & InjectFlags.Self ||
|
|
||||||
(flags & InjectFlags.Host &&
|
|
||||||
((parentLocation as any as number) & RelativeInjectorLocationFlags.AcrossHostBoundary)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function injectInjector() {
|
export function injectInjector() {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
|
|
||||||
import {assertDefined} from './assert';
|
import {assertDefined} from './assert';
|
||||||
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
|
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
|
||||||
import {NodeInjector} from './di';
|
import {NodeInjector} from './di';
|
||||||
@ -14,7 +15,8 @@ import {LContext} from './interfaces/context';
|
|||||||
import {DirectiveDef} from './interfaces/definition';
|
import {DirectiveDef} from './interfaces/definition';
|
||||||
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
|
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
|
||||||
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||||
import {readPatchedLView, stringify} from './util';
|
import {readElementValue, readPatchedLView, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,7 +329,7 @@ export function getListeners(element: Element): Listener[] {
|
|||||||
const secondParam = tCleanup[i++];
|
const secondParam = tCleanup[i++];
|
||||||
if (typeof firstParam === 'string') {
|
if (typeof firstParam === 'string') {
|
||||||
const name: string = firstParam;
|
const name: string = firstParam;
|
||||||
const listenerElement: Element = lView[secondParam];
|
const listenerElement = readElementValue(lView[secondParam]) as any as Element;
|
||||||
const callback: (value: any) => any = lCleanup[tCleanup[i++]];
|
const callback: (value: any) => any = lCleanup[tCleanup[i++]];
|
||||||
const useCaptureOrIndx = tCleanup[i++];
|
const useCaptureOrIndx = tCleanup[i++];
|
||||||
// if useCaptureOrIndx is boolean then report it as is.
|
// if useCaptureOrIndx is boolean then report it as is.
|
||||||
|
24
packages/core/src/render3/empty.ts
Normal file
24
packages/core/src/render3/empty.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import './ng_dev_mode';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains reuseable "empty" symbols that can be used as default return values
|
||||||
|
* in different parts of the rendering code. Because the same symbols are returned, this
|
||||||
|
* allows for identity checks against these values to be consistently used by the framework
|
||||||
|
* code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const EMPTY_OBJ: {} = {};
|
||||||
|
export const EMPTY_ARRAY: any[] = [];
|
||||||
|
|
||||||
|
// freezing the values prevents any code from accidentally inserting new values in
|
||||||
|
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
||||||
|
Object.freeze(EMPTY_OBJ);
|
||||||
|
Object.freeze(EMPTY_ARRAY);
|
||||||
|
}
|
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
import {Type} from '../../type';
|
import {Type} from '../../type';
|
||||||
import {fillProperties} from '../../util/property';
|
import {fillProperties} from '../../util/property';
|
||||||
import {EMPTY, EMPTY_ARRAY} from '../definition';
|
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
import {ComponentDef, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
|
|||||||
function maybeUnwrapEmpty<T>(value: T[]): T[];
|
function maybeUnwrapEmpty<T>(value: T[]): T[];
|
||||||
function maybeUnwrapEmpty<T>(value: T): T;
|
function maybeUnwrapEmpty<T>(value: T): T;
|
||||||
function maybeUnwrapEmpty(value: any): any {
|
function maybeUnwrapEmpty(value: any): any {
|
||||||
if (value === EMPTY) {
|
if (value === EMPTY_OBJ) {
|
||||||
return {};
|
return {};
|
||||||
} else if (value === EMPTY_ARRAY) {
|
} else if (value === EMPTY_ARRAY) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -93,9 +93,10 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
|
|||||||
*
|
*
|
||||||
* @param currentView The current view
|
* @param currentView The current view
|
||||||
*/
|
*/
|
||||||
export function executeInitHooks(currentView: LView, tView: TView, creationMode: boolean): void {
|
export function executeInitHooks(
|
||||||
if (currentView[FLAGS] & LViewFlags.RunInit) {
|
currentView: LView, tView: TView, checkNoChangesMode: boolean): void {
|
||||||
executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
|
if (!checkNoChangesMode && currentView[FLAGS] & LViewFlags.RunInit) {
|
||||||
|
executeHooks(currentView, tView.initHooks, tView.checkHooks, checkNoChangesMode);
|
||||||
currentView[FLAGS] &= ~LViewFlags.RunInit;
|
currentView[FLAGS] &= ~LViewFlags.RunInit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,17 +107,19 @@ export function executeInitHooks(currentView: LView, tView: TView, creationMode:
|
|||||||
* @param currentView The current view
|
* @param currentView The current view
|
||||||
*/
|
*/
|
||||||
export function executeHooks(
|
export function executeHooks(
|
||||||
data: LView, allHooks: HookData | null, checkHooks: HookData | null,
|
currentView: LView, allHooks: HookData | null, checkHooks: HookData | null,
|
||||||
creationMode: boolean): void {
|
checkNoChangesMode: boolean): void {
|
||||||
const hooksToCall = creationMode ? allHooks : checkHooks;
|
if (checkNoChangesMode) return;
|
||||||
|
|
||||||
|
const hooksToCall = currentView[FLAGS] & LViewFlags.FirstLViewPass ? allHooks : checkHooks;
|
||||||
if (hooksToCall) {
|
if (hooksToCall) {
|
||||||
callHooks(data, hooksToCall);
|
callHooks(currentView, hooksToCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
* Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
||||||
* creation mode.
|
* the first LView pass.
|
||||||
*
|
*
|
||||||
* @param currentView The current view
|
* @param currentView The current view
|
||||||
* @param arr The array in which the hooks are found
|
* @param arr The array in which the hooks are found
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user