Compare commits

..

130 Commits

Author SHA1 Message Date
279c57f38d fix(bazel): Fix integration test after v8 bump (#28194)
The integration test for bazel-schematics installs Angular in
two different locations:

1. Bazel workspace
2. package.json -> fetched from npm

Pull request #28142 changes the test to always install (1) from
source. This breaks when there's a major version bump since the
versions locally and the version in package.json no longer match.

This change updates package.json to fetch @angular/* packages
locally as well.

PR Close #28194
2019-01-16 16:09:58 -08:00
35ec2dc9bc fix(ivy): normalize summary and factory shim files paths (#28173)
At the moment, paths stored in `maps` are not normalized and in Windows is causing files not to be found when enabling factory shimming.

For example, the map contents will be
```
Map {
  'C:\\git\\cli-repos\\ng-factory-shims\\index.ngfactory.ts' => 'C:\\git\\cli-repos\\ng-factory-shims\\index.ts' }
```

However, ts compiler normalized the paths and is causing;
```
error TS6053: File 'C:/git/cli-repos/ng-factory-shims/index.ngfactory.ts' not found.
error TS6053: File 'C:/git/cli-repos/ng-factory-shims/index.ngsummary.ts' not found.
```

The changes normalized the paths that are stored within the factory and summary maps.

PR Close #28173
2019-01-16 09:47:27 -08:00
332dcb2d47 test(upgrade): clean up global state after test (#28181)
In ngUpgradeLite, when a downgraded component
[requests its downgraded module][1], the AngularJS injector is
[temporarily stored][2] with the intention of grabbing it
[once the module has been bootstrapped][3] (which also cleans up the
[temporary injector reference][4]).

In [some tests][5], bootstrapping a component might throw an error,
which causes the test to fail immediatelly and move on to the next
test. In slow browsers (e.g. on CI/Saucelabs), the (successful)
bootstrapping of another downgraded module might not have been
completed in time and thus the temporary injector reference not cleaned
up.

In such a case, if the following test (in our randomized test suite)
happens to rely on the temporary injector reference's being initially
unset (such as [this test][6]), it will fail. This might appear as a
flake on CI, because it depends on a race condition and specific order
of tests, so it usually goes away after a rerun.

This commit fixes it by ensuring the temporary injector reference is
manually cleaned up, when necessary.

Jira issue: FW-939

[1]: f983e99fb2/packages/upgrade/src/common/downgrade_component.ts (L120)
[2]: f983e99fb2/packages/upgrade/src/static/downgrade_module.ts (L165)
[3]: f983e99fb2/packages/upgrade/src/static/downgrade_module.ts (L169)
[4]: f983e99fb2/packages/upgrade/src/static/angular1_providers.ts (L25)
[5]: f983e99fb2/packages/upgrade/test/static/integration/downgrade_module_spec.ts (L1331-L1381)
[6]: f983e99fb2/packages/upgrade/test/static/angular1_providers_spec.ts (L31-L45)

PR Close #28181
2019-01-16 09:45:01 -08:00
2c3b084817 perf(platform-server): use shared DomElementSchemaRegistry instance (#28150) (#28151)
Right now the `ServerRendererFactory2` creates a new instance of the
`DomElementSchemaRegistry` for each and every request, which is quite
costly (for the Tour of Heroes SSR this takes around **30%** of the
overall execution time). Since the schema is never modified, but only
used in a read-only fashion, it should be possible to re-use a single
instance instead.

Naive performance testing with 100 concurrent connections and 1000
requests in total shows an approximate **33%** improvement in Req/Sec
on the Tour of Heroes SSR example.

PR Close #28150

PR Close #28151
2019-01-15 11:27:55 -08:00
918d906a1e docs(router): add clarification for Router config (#28159)
PR Close #28159
2019-01-15 10:54:50 -08:00
12e3b71740 docs: add api doc for switch directives (#27378)
PR Close #27378
2019-01-15 10:53:03 -08:00
9faad4b2b0 docs(core): fix typo (#28042)
PR Close #28042
2019-01-15 09:52:49 -08:00
42ccea547e fix(service-worker): navigation urls backwards compatibility (#27244)
PR Close #27244
2019-01-15 09:50:32 -08:00
67e6b63165 fix(router): ensure URL is updated after second redirect with UrlUpdateStrategy="eager" (#27680)
Navigating to a route such as `/users`, you may get redirected to `/login`. Previously, if you go then route to `/users` again the URL will end up showing `/users` after the second redirect. This only happened in `UrlUpdateStrategy="eager"`. This is now fixed so after the second redirect, the URL shows the correct page.

Fixes #27116

PR Close #27680
2019-01-14 17:19:37 -08:00
88d415515a test(bazel): Use local_repository to load Angular (#28142)
The current integration test for Bazel schematics downloads a
published version of Angular as required by the http_archive
rule in the CLI created WORKSPACE.
However, this makes the test less useful because it does not
actually test any changes to the Angular repo at source.
This PR replaces the http_archive method in the WORSPACE
with local_repository so that any local changes to the Angular
repo are tested accordingly.

With Typescript 3.2, the file e2e/src/app.po.ts generated by CLI
no longer compiles under Bazel due to missing type annotations.
A temporary file is placed in the integration/bazel-schematics
directory while the change is pending in CLI repo.
https://github.com/angular/angular-cli/pull/13406

PR Close #28142
2019-01-14 17:16:53 -08:00
3bc02a3652 fix(bazel): Bazel schematics should add router package (#28141)
This commit fixes a bug whereby a Bazel project created by the
schematics would not compiled if project contains routing module.

It is missing a dependency on the router package.

PR Close #28141
2019-01-14 17:12:01 -08:00
a3f72d92f2 docs(ivy): remove duplicated words in architecture doc (#27471)
PR Close #27471
2019-01-14 17:06:43 -08:00
f86eb9a133 docs: update testing doc example style for HostListener (#26372)
HostListener is preferred over host metadata by official style guide

PR Close #26372
2019-01-14 17:04:47 -08:00
ac3c9c1e6e fix(bazel): incorrectly always uses ngc-wrapped from "npm" workspace (#28137)
* This is a follow-up to cd0451305a which fixes that "ngc-wrapped" from the "npm" workspace is always used if "angular" is fetched as an external dependency.

PR Close #28137
2019-01-14 14:12:19 -08:00
c9f213c0ea docs: ng config link fix (#28115)
Fixed 'ng config' link to lead to proper CLI command site
PR Close #28115
2019-01-14 10:47:06 -08:00
23d8edb263 fix(docs-infra): Add crossed through styling (#28111)
PR Close #28111
2019-01-14 10:45:47 -08:00
23e2127cee fix(docs-infra): change style of deprecated markers (#28111)
PR Close #28111
2019-01-14 10:45:47 -08:00
0af5b8eb7a fix(docs-infra): render deprecated markers for CLI command options (#28111)
fixes #27563
fixes #27423

PR Close #28111
2019-01-14 10:45:47 -08:00
17403a2b1d fix(bazel): replay compilation uses wrong compiler for building esm5 (#28053)
With the update to TypeScript 3.2.x, a big issue seems to have appeared for downstream Bazel users. If the downstream user still uses a lower TypeScript version, normal Bazel targets using the `ng_module` rule are still compiled with the correct/old TypeScript version (assuming they set the `node_modules` attribute properly).

But, if they build the previous Bazel targets by specifying them within a `ng_package` rule, the TypeScript version from the Angular `workspace` is being used for the replayed ESM5 compilation. This is because we resolve the replay compiler to `ngc_wrapped` or `tsc_wrapped` Bazel executables which are defined as part of the `angular` workspace. This means that the compilers are different if the downstream user uses `ngc-wrapped` from the `@npm` repository because the replayed compilation would use the compiler with `@ngdeps//typescript`.

In order to fix this, we should just use the compiler that is defined in the `@angular//BUILD.bazel` file. This target by defaults to the "@npm" workspace which is working for downstream users. This is similar to how it is handled for `tsc-wrapped`. `tsc-wrapped` works as expected for downstream users.

**Note**: This is not the ideal solution because ideally we would
completely respect the `compiler` option from the base `ng_module`, but
this is not possible in a hermetic way, unless we somehow accept the
`compiler` as an attribute that builds all transitive deps. This is
something we should explore in the future. For now, we just fix this in
a reasonable way that is also used for `tsc_wrapped` from the TypeScript
rules.

PR Close #28053
2019-01-14 10:44:25 -08:00
325bf558ba refactor(bazel): use web_package rule for index.html (#27995)
index.html needs to have the zone.js and the project bundle injected
using script tags. This used to be done explicitly by specifying a
new index.html but with `web_package` rule introduced in rules_nodejs,
it is now possible to perform the injection dynamically.

PR Close #27995
2019-01-14 10:42:14 -08:00
21a9fe64d6 docs: fix typo in singleton services guide (#27948)
PR Close #27948
2019-01-14 10:41:31 -08:00
961d55395d docs: change in(what should I import? answer) NgModule FAQs (#27677)
I think only should be after BrowserModule , because we can import more than BrowserModule and I think we need to import other modules to AppModule in most of cases and we should import BrowserModule only in AppModule,so that thing seems okay.

PR Close #27677
2019-01-14 10:39:04 -08:00
c7fe8de182 docs: fix typo in dependency injection guide (#27616)
~~`HeroService` must provided in some parent injector~~
*`HeroService` must be provided in some parent injector*

PR Close #27616
2019-01-14 10:38:05 -08:00
7928356a62 docs: fix typo in docs.md (#27484)
Added a period to the end of the last sentence in the first paragraph
PR Close #27484
2019-01-14 10:37:22 -08:00
0316fda529 docs: ngComponentOutlet doc updated with new Injector creation (#27291)
PR Close #27291
2019-01-14 10:36:03 -08:00
6650e525ec docs: updates to minor spelling mistakes in pipes guide (#27208)
PR Close #27208
2019-01-14 10:34:18 -08:00
2558e6843c docs: add browser polyfills for IE11 with hash-based routing (#27135)
Closes #26511

PR Close #27135
2019-01-14 10:33:24 -08:00
1b2ba78090 docs: fix GitHub pages deployment command (#26896)
closes #26803
PR Close #26896
2019-01-14 10:32:31 -08:00
5f0d845ca9 docs: minor wording change in code example comment (#26835)
PR Close #26835
2019-01-14 10:31:24 -08:00
5f616ca613 docs: fix typo in testing guide (#26828)
PR Close #26828
2019-01-14 10:30:34 -08:00
b47ea97a48 docs: update link in universal guide (#26628)
Link to the document "53 percent of mobile site visits" was changed, updated link. Old link led to a page that didn't have the statistics on it.

PR Close #26628
2019-01-14 10:12:53 -08:00
f8101804a2 docs: fix scripts section and some minor issues in universal documentation (#26444)
PR Close #26444
2019-01-14 10:11:09 -08:00
5a8c14b6d7 test(docs-infra): increase timeout for all redirection tests (#28103)
Occasionally, URLs take longer to load, which causes CI flakes.
In #27903, the timeout for external URLs was increased, but internal
URLs turned out to be affected as well.

PR Close #28103
2019-01-14 10:04:59 -08:00
31556fa7b0 docs: add library doc to guides in aio (#27581)
PR Close #27581
2019-01-11 16:22:20 -08:00
01d475d6f3 fix(bazel): Add ibazel to deps of Bazel project (#28090)
Incremental rebuilds is a fundamental part of the development
workflow. `@bazel/ibazel` should be added to the dev dependencies
of a Bazel project.

PR Close #28090
2019-01-11 15:58:38 -08:00
3c08b86e27 docs(forms): fix up @see tags for AbstractContol (#28069)
PR Close #28069
2019-01-11 14:35:24 -08:00
b102835294 build(docs-infra): render @see information in members (#28069)
Previously `@see` tags were only rendered for top level class-like
docs. Now these tags are rendered for methods and properties too.

PR Close #28069
2019-01-11 14:35:24 -08:00
0465dc6637 build: update version in package.json (#28075)
The version was updated on master (with 0efbb3738), but the commit was
not backported to 7.2.x. As a result, the version on angular.io appears
as `7.2.0-rc.0` (instead of 7.2.0).

PR Close #28075
2019-01-11 11:20:42 -08:00
937a856b47 docs(service-worker): fix example of manually checking for updates (#28020)
Poll for updates in a way that does not prevent the SW from being
registered. Discussed in https://github.com/angular/angular/pull/27332#pullrequestreview-179504620.

PR Close #28020
2019-01-11 11:17:19 -08:00
eeb4c87dc0 style(service-worker): fix code formatting in service-worker-getting-started example (#28020)
PR Close #28020
2019-01-11 11:17:19 -08:00
a82ba5d4dc docs(service-worker): fix the service-worker-getting-started docs example (#28020)
Changes:
- Change the project type to `service-worker`, so that it gets
  appropriate `package.json` (with `@angular/service-worker` dependency)
  and `angular.json` (with `serviceWorker: true` in production config).
- Move `ngsw-config.json` to the correct directory.
- Specify custom test commands for aio's `yarn example-e2e` to also
  verify that the ServiceWorker bits are set up correctly.

PR Close #28020
2019-01-11 11:17:19 -08:00
28c6e9d684 feat(docs-infra): add support for custom test commands in cli-based docs examples (#28020)
Previously, cli-based docs examples were tested using `yarn e2e ...`. In
some cases, it might make sense to run different or additional checks
for a docs example (when running `yarn example-e2e` in `aio/`).

Currently, the only option is to define a custom project type and
overwrite the `e2e` yarn script in `package.json`. Doing so (in addition
to being cumbersome and verbose) would also end up in the `.zip` archive
that users can download to run the example locally. This would be
confusing, if these custom tests are specific to our CI needs.

This commit adds support for defining a custom list of commands per
example. These commands (if specified) would be run instead of the
default `yarn e2e ...`, when testing the docs examples on CI (via
`yarn example-e2e`).

(This feature will be used to verify that the
`service-worker-getting-started` example is set up correctly in a
subsequent commit, but can be useful in other cases as well.)

PR Close #28020
2019-01-11 11:17:19 -08:00
9ea8f7197f feat(docs-infra): add service-worker project type (#28020)
File overwrites:
- **angular.json**: Add `serviceWorker: true` to production config.
- **package.json**: Add `@angular/service-worker` to dependencies.

This will make any `service-worker` examples work out-of-the-box, when
downloading and running locally from the `.zip` archives.

PR Close #28020
2019-01-11 11:17:19 -08:00
0d9afd85f1 docs: add schematics terms to glossary (#27862)
PR Close #27862
2019-01-11 11:15:09 -08:00
d2eea769f6 docs: re-write interpolation section and add example (#25170)
PR Close #25170
2019-01-11 11:12:44 -08:00
7bafe180fb docs(router): fixed a typo in CanLoad title text (#27894)
* Interface that a class can implement to be a guard deciding if a children can be loaded.
'...if a children...' changed to '...if children...'
 * Interface that a class can implement to be a guard deciding if children can be loaded.

PR Close #27894
2019-01-10 17:06:55 -08:00
457781906b docs(router): fix misnamed path (#27879)
This change matches the routes found in the following code example
for auth.guard.ts as well as the login.component.1.ts in the "Add
the LoginComponent" section.

PR Close #27879
2019-01-10 17:06:06 -08:00
2530c9d2dd docs: fix typo (#27865)
PR Close #27865
2019-01-10 17:04:54 -08:00
c18c46a976 docs: fix broken html for deployment.md (#27851)
without the closing quote the text and link are not displayed correctly

PR Close #27851
2019-01-10 17:03:55 -08:00
9447b852a4 refactor(core): improve type for EventEmitter (#27228)
PR Close #27228
2019-01-10 17:02:53 -08:00
11d184aaae build(bazel): Bump @bazel/typescript & @bazel/karma to 0.22.1 (#28031)
PR Close #28031
2019-01-10 16:49:56 -08:00
d4b476fb45 fix(bazel): Add /bazel-out to .gitignore (#27874)
PR Close #27874
2019-01-10 13:40:29 -08:00
a64a7a4041 test(upgrade): log more info to help debug CI flakes (#28045)
Related Jira issue: FW-939

PR Close #28045
2019-01-10 11:03:26 -08:00
9bda4460fa fix(bazel): protractor rule does not run spec files with underscore (#28022)
There are various e2e tests with the `_spec.ts` suffix in the Angular project. Currently the protractor Bazel rule does not pick up these files and just ignores them. Since underscore is commonly used, we should support this.

Needed for the conversion fo the `examples` to Bazel.

PR Close #28022
2019-01-10 10:53:26 -08:00
acb69e8341 docs: document why not using compare-url orb (#28010)
Discussed in
https://github.com/angular/angular/pull/27775#issuecomment-452565603.

PR Close #28010
2019-01-10 10:50:31 -08:00
69ca7cd381 fix(bazel): Add @bazel/bazel to dev deps (#28032)
Project created by @angular/cli depends on Bazel at build time and
we should not assume that Bazel is available globally.
Instead, the project should specify an explicit dev dependency on
`@bazel/bazel`.

PR Close #28032
2019-01-09 17:45:17 -08:00
e5f5ad34a2 docs: remove deprecated fromPromise from RxJS guide (#28015)
Follow-up to #27443.

PR Close #28015
2019-01-09 15:37:53 -08:00
51f8892d9b docs(core): add missing closing backtick (`) (#27908)
PR Close #27908
2019-01-09 12:44:13 -08:00
a06469c2f5 docs(forms): remove duplicated link (#27884)
PR Close #27884
2019-01-09 12:43:29 -08:00
eb3345b950 docs: fix typo PageNotFoundCompponent (#27799)
closes: #27795

PR Close #27799
2019-01-09 12:42:40 -08:00
ecc304adac ci: add brandonroberts to @angular/docs-infra codeowners (#28023)
Brandon is joining the docs-infra team in addition to the docs team.

PR Close #28023
2019-01-09 11:50:41 -08:00
49138bd114 build(bazel): List explicit dependencies in WORKSPACE (#28000)
Instead of relying on implicit dependencies through Angular, the WORKSPACE
of the project should explicitly add rules_nodejs and rules_typescript so
it can better control the versions.

PR Close #28000
2019-01-09 11:49:09 -08:00
aa7f2c8dc7 fix(bazel): flat module misses AMD module name on windows (#27839)
* Fixes that the flat module out files do not have a proper AMD module name on Windows. This is currently blocking serving a `ng_module` using the Bazel TypeScript `devserver` on Windows.

PR Close #27839
2019-01-09 10:42:38 -08:00
84857a267c build: re-enable disabled e2e tests (#27979)
Re-enables a few e2e tests which have been disabled a long time ago. Since these pass now, we should re-enable them.

PR Close #27979
2019-01-09 10:41:17 -08:00
e0ed78c2d0 build: remove travisci leftovers (#27979)
PR Close #27979
2019-01-09 10:41:17 -08:00
b00efa3399 fix(aio): Treating some deprecated (#27981)
PR Close #27981
2019-01-09 10:39:25 -08:00
cdfca90053 docs(core): fix characters that corrupt link (#27982)
PR Close #27982
2019-01-09 10:38:23 -08:00
0edca7997f build(docs-infra): remove unnecessary script to workaround cli issue (#28012)
The cli issue has been fixed and we no longer need to work around it.
This is essentially reverting #23470.

PR Close #28012
2019-01-09 10:29:43 -08:00
2ce90323b8 test(upgrade): properly clean up after tests to avoid errors in unrelated tests (#28013)
Many `ngUpgrade` tests need to manually [bootstrap modules][1] (instead
of using `TestBed` which automatically cleans up) and thus need to also
manually clean up afterwards (e.g. by calling [destroyPlatform()][2]
after each test).

Failing to destroy the platform is usually not a problem, unless the
next test tries to manually destroy it (as a precaution), as happens
[here][3] (among other places).

More specifically, the problem happens, because (as part of the clean-up
happening on platform destruction) upgraded components will try to
[call a method][4] on `angular.element` after `angular` has been
[set to `undefined`][5] (assuming the last test was using the
[withEachNg1Version()][6] helper).

Because the test order is pseudo-random and thus different on each run,
these errors did not always come up and - when they did- they would go
away after a couple of reruns, making them appear as flakes on CI.

(For reference, the issue was introduced in 43c33d566.)

This commit eliminates the issue by always destroying the platform after
each `ngUpgrade` test.

Jira issue: FW-924

[1]: c3aa24c3f9/packages/upgrade/test/static/test_helpers.ts (L21)
[2]: c3aa24c3f9/packages/upgrade/test/static/integration/upgrade_component_spec.ts (L24)
[3]: c3aa24c3f9/packages/elements/test/create-custom-element_spec.ts (L31)
[4]: c3aa24c3f9/packages/upgrade/src/common/upgrade_helper.ts (L134-L135)
[5]: c3aa24c3f9/packages/upgrade/test/common/test_helpers.ts (L115)
[6]: c3aa24c3f9/packages/upgrade/test/common/test_helpers.ts (L31)

PR Close #28013
2019-01-09 10:27:47 -08:00
7227b4aa62 docs: use static zip function as an Observable creator. (#26790)
The existing example makes it seem like zip is a pipeable operator. It can be used this way, but I think that is for backwards compatibility. You can achieve the same functionality by using it as an Observable creator. I think this also makes the example clearer.

PR Close #26790
2019-01-08 17:16:46 -08:00
5a7dd73b2c Revert "test(ivy): re-enable passing animation tests (#27997)"
This reverts commit 097af5ae2b.
It was accidentally merged on both master and patch branches when
it should have only been merged on master.
2019-01-08 16:19:44 -08:00
e1538fdd63 docs: update rxjs example and change fromPromise to from (#27443)
the first example at https://angular.io/guide/rx-library,
https://github.com/ReactiveX/rxjs-tslint/issues/7

PR Close #27443
2019-01-08 16:15:54 -08:00
81da0fe91f test: fix outDir in TS 3.2 integration test (#27774)
PR Close #27774
2019-01-08 16:00:26 -08:00
fa377b350f ci: fix public api rule in codeowners (#27999)
@alxhub spotted that the public api rule in codeowners is being overriden by the Build & CI Owners rule.

swapping the two sections fixes the problem.

PR Close #27999
2019-01-08 15:56:13 -08:00
097af5ae2b test(ivy): re-enable passing animation tests (#27997)
PR Close #27997
2019-01-08 14:07:32 -08:00
f1c3eab1e0 ci: update payload size for cli-hello-world (#27994)
PR Close #27994
2019-01-08 11:58:40 -08:00
1edb8af20f docs: group fixes and features for 7.2.0 in changelog desc (#27974)
PR Close #27974
2019-01-08 11:31:59 -08:00
0f34e3ac15 docs: remove Travis status from README (#27973)
we no longer use TravisCI
PR Close #27973
2019-01-08 10:38:35 -08:00
1cca9cc9d1 ci: compute commit range for rerun workflows (#27775)
On push builds, CircleCI provides `CIRCLE_COMPARE_URL`, which we use to
extract the commit range for a given build. When a workflow is rerun
(e.g. to recover from a flaked job), `CIRCLE_COMPARE_URL` is not
defined, causing some jobs to fail.

This commit fixes it by retrieving the compare URL from the original
workflow. It uses a slow process involving a (potentially large) number
of requests to CircleCI API.
It depends on the (undocumented) fact, that the `workspace_id` is the
same on all rerun workflows and the same as the original `workflow_id`.

PR Close #27775
2019-01-08 10:38:00 -08:00
575cf3da0f ci: remove required ci status for travis from angular-robot.yaml (#27970)
we missed this one!! oops

the robot should not expect travis status check on PRs any more.

PR Close #27970
2019-01-07 15:42:21 -08:00
11dfdf7f57 build: increase parallelism for "test_docs_examples" job (#27937)
PR Close #27937
2019-01-07 15:35:12 -08:00
0bb03aaab1 build: group sharded "test_docs_examples" jobs within circleci (#27937)
* Groups the two sharded `test_docs_examples` job using CircleCI's `parallelism` feature.  This makes the amount of jobs that show up on a PR, more reduced and also reduces code duplication for maintaining the Circle job definition.

PR Close #27937
2019-01-07 15:35:12 -08:00
df2704dc12 build: bazel integration test using limited resources (#27937)
PR Close #27937
2019-01-07 15:35:11 -08:00
7714998869 build: shard integration tests on circleci (#27937)
PR Close #27937
2019-01-07 15:35:11 -08:00
c9c06a014f test: fix integration/platform-server test which had missing @types/node devDep (#27937)
I'm not sure why this problem is visible only now or how this worked before, but the CI
is now failing because @types/node is missing.

I also added the yarn.lock file which was previously omitted. We want the yarn.lock file in so that
our deps don't change over time without us knowing.

PR Close #27937
2019-01-07 15:35:11 -08:00
204b675ea4 build: remove "build.sh" script (#27937)
this script is now obsolete and not needed any more. yay!!!

PR Close #27937
2019-01-07 15:35:11 -08:00
23c666bdf5 build: remove "test.sh" script (#27937)
test.sh is no longer needed... all the tests should now be executed via bazel.

if for whatever reason we need to run the legacy unit test setup, we should should follow the commands that we use to execute those tests in .circle/config.yaml

PR Close #27937
2019-01-07 15:35:11 -08:00
9202a4de81 ci: remove travis ci setup (#27937)
we no longer need it... yay!!!

PR Close #27937
2019-01-07 15:35:11 -08:00
8c51e83a46 ci: run legacy e2e tests in parallel (#27937)
PR Close #27937
2019-01-07 15:35:11 -08:00
cbc45fd59b ci: move e2e tests from travis to circleci (#27937)
PR Close #27937
2019-01-07 15:35:11 -08:00
11fe52b805 ci: remove build steps that are no longer needed (#27937)
the metadata build seems to be needed only by the offline compiler tests which is currently disabled

PR Close #27937
2019-01-07 15:35:11 -08:00
50fadfaca3 ci: move local and saucelabs unit tests to circle (#27937)
Moving the tests over to CircleCI in pretty much "as-is" state just so that we can drop the dependency on Travis.

In the followup changes we plan to migrate these tests to run on sauce under bazel. @gregmagolan is working on that.

I've previously verified that all the tests executed in legacy-unit-tests-local already under bazel.
Therefore the legacy-unit-tests-local job is strictly not necessary any more, but given how flaky legacy-unit-tests-saucelabs is,
it is good to have the -local job just so that we can quickly determine if any failure is a flake or legit issue
(the bazel version of these tests could theoretically run in a slightly different way and fail or not fail in a different way, so having -lcoal job is just an extra safety check).

This change was coauthored with @devversion

PR Close #27937
2019-01-07 15:35:10 -08:00
9e04284bb3 fix(bazel): protractor utils cannot start server on windows (#27915)
* Currently the protractor utils assume that the specified Bazel server runfile can be resolved by just using the real file system. This is not the case on Windows because the runfiles are not symlinked into the working directory and need to be resolved through the runfile manifest.

PR Close #27915
2019-01-07 14:49:44 -08:00
216bbd09ef test(bazel): re-enable ng_package golden testing on ci (#27829)
* Enables the ng_package golden testing on the CI
* Fixes the ng_package golden testing for Windows

PR Close #27829
2019-01-07 14:46:48 -08:00
0c683a73e2 test(bazel): fix all ng_package tests not working on windows (#27829)
PR Close #27829
2019-01-07 14:46:48 -08:00
998bb8382e fix(bazel): packager not properly removing amd directives on windows (#27829)
PR Close #27829
2019-01-07 14:46:47 -08:00
f893e2e502 fix(bazel): ng_package creates invalid typings reexport on windows (#27829)
Currently when building a package on Windows, the typings re-export for secondary entry-points is not valid TypeScript. Similarly the metadata and the "package.json" files use non-posix paths and cause inconsistency within the NPM package.

For example:

_package.json_
```
  "esm5": "./esm5\\core.js",
  "esm2015": "./esm2015\\core.js",
```

_testing.d.t.s_ (of the `core` package)
```
export * from './testing\testing';
```

PR Close #27829
2019-01-07 14:46:47 -08:00
bbe656e634 docs: add Alyle UI to resources (#27954)
PR Close #27954
2019-01-07 08:35:41 -08:00
17825e8f55 ci: add brandonroberts to @angular/framework-global-approvers-for-docs-only-changes (#27949)
I’ve observed that Brandon reviews many docs-only PRs and then we still need me or Jeniffer to approve them.

In most cases, Brandon is perfectly qualified to approve these, so I’m proposing that Brandon is added to the framework-global-approvers-for-docs-only-changes group.

PR Close #27949
2019-01-07 08:35:23 -08:00
7242551213 ci: fix typos in codeowners file (#27945)
PR Close #27945
2019-01-05 17:46:28 -08:00
1ad88e0e59 ci: replace our pullapprove setup with GitHub CODEOWNERS (#27690)
Summary of changes:
- created .github/CODEOWNERS with docs and config similar to the one in .pullapprove.yml
- updated docs
- updated .github/angular-robot.yml to not expect pullapprove status
- removed .pullapprove.yml

The primary motivations behind this change are:
- CODEOWNERS didn't exist when we introduced pullapprove
- CODEOWNERS is a functionality tightly integrated with github which results in better DX
- pullapprove v2 has been very unstable recently causing productivity loss
- pullapprove v2 has been deprecated in favor of v3, which requires and migration

PR Close #27690
2019-01-05 17:39:00 -08:00
6cc864a771 build: enforce minimum Bazel version of 0.21.0 in WORKSPACE so cache works with yarn bazel (#27935)
PR Close #27935
2019-01-04 18:38:44 -08:00
3fc02ce60b build: update to Bazel 0.21.0 (#27935)
PR Close #27935
2019-01-04 18:38:44 -08:00
0a54825b29 ci: make integration_test job logs less verbose (#27934)
The build and test progress logs make the CI log output so long that it
can't be displayed in the UI and one has to download and view the file
locally instead. This makes it harder to get to the interesting lines,
such as error messages.

Similar to #26869, but for the `bazel-schematics` integration project.

PR Close #27934
2019-01-04 12:18:39 -08:00
970e3a0f79 test: remove bower and polymer benchmarks (#27931)
the polymer benchmarks are super old and not relevant any more

and these benchmarks were the only reason why we needed bower at all

so long, bower. thanks for all the fish.

PR Close #27931
2019-01-04 12:02:23 -08:00
791d8656fd fix(docs-infra): upgrade to latest dgeni-packages to fix linking problem (#27864)
This new version of dgeni-packages gives the main (implemented)
overload of a method the correct id and aliases, which allow it to be
automatically linked.

See 398f35da30

Fixes #27820
Closes #27821

PR Close #27864
2019-01-03 09:38:06 -08:00
7e98eedf5e fix(bazel): unable to launch protractor test on windows (#27850)
Due to an incorrect environment variable name, it's currently not possible to launch Protractor on Windows using the Bazel protractor rule.

PR Close #27850
2019-01-03 09:36:37 -08:00
5aecb2d43d docs: fix typo in TOH http section (#26127)
PR Close #26127
2019-01-03 09:32:28 -08:00
9a15a42f29 docs: add more info on what is being cached (#27343)
PR Close #27343
2019-01-03 09:29:44 -08:00
fdd8b0402c docs: add missing comma to environment config in build guide (#27476)
PR Close #27476
2019-01-03 09:29:18 -08:00
d8aeb59c1c docs: fix npm description in architecture modules guide (#27818)
PR Close #27818
2019-01-03 09:28:45 -08:00
679235a44a docs: improve description of bundle budgets (#27833)
PR Close #27833
2019-01-03 09:28:16 -08:00
e9de47c969 docs(service-worker): update service worker configuration doc (#27868)
PR Close #27868
2019-01-03 09:27:31 -08:00
061615c16e test(docs-infra): increase timeout for redirection to external URL (#27903)
Occasionally, external URLs take longer to load, which causes CI flakes.

PR Close #27903
2019-01-03 09:26:54 -08:00
213e01bd38 docs: add api doc for viewport scroller (#27381)
PR Close #27381
2018-12-26 11:47:16 -08:00
92d3942691 build: update to latest rules_nodejs (#27764)
This includes a performance fix for module resolution in the common case under Ivy

PR Close #27764
2018-12-26 11:31:37 -08:00
4ad7e21ce8 docs(elements): add "Firefox" for support of custom elements (#27789)
Firefox now supports custom elements: https://caniuse.com/#feat=custom-elementsv1

PR Close #27789
2018-12-26 11:27:45 -08:00
4b78c23c65 docs: fix typo explictly (#27798)
Closes #27796

PR Close #27798
2018-12-26 06:57:55 -05:00
70c184b4bd build: update to latest karma version (#27735)
Updates to the latest Karma version that includes karma-runner/karma@cc2eff2 and should be able to properly restart disconnected browsers. This was a long-term Karma bug and affected CI flakiness significantly.

PR Close #27735
2018-12-21 17:02:50 -05:00
98a617e415 docs(forms): clarify the pattern validator behavior (#27560)
PR Close #27560
2018-12-21 15:12:31 -05:00
0ae1e93eed docs: fix code example to ensure consistency in file (#26577)
PR Close #26577
2018-12-21 15:12:05 -05:00
dd59e9a343 docs(upgrade): add gotchas/tips/example for multiple downgraded modules (#27217)
PR Close #27217
2018-12-20 16:20:42 -05:00
89fd1822d4 test(upgrade): test injector tree traversal for downgraded components (#27217)
This commit adds tests that verify the current behavior wrt injector
tree traversal for downgraded components, so that it is easier to
contrast with changed behavior is future commits (should we decide
to actually change it).

PR Close #27217
2018-12-20 16:20:42 -05:00
4ae1880642 fix(upgrade): allow nesting components from different downgraded modules (#27217)
PR Close #27217
2018-12-20 16:20:42 -05:00
c51b0b9564 fix(upgrade): correctly handle nested downgraded components with downgradeModule() (#27217)
Previously, nested downgraded components would not be created/destroyed
inside the Angular zone (as they should) and they would not be wired up
correctly for change detection.

This commit ensures that ngUpgrade correctly detects whether this is an
ngUpgradeLite app (i.e. one using `downgradeModule()` instead of
`UpgradeModule`) and appropriately handles components, even if they are
nested inside other downgraded components.

Fixes #22581
Closes #22869
Closes #27083

PR Close #27217
2018-12-20 16:20:42 -05:00
7bb760f6ff refactor(upgrade): simplify special handling of ngUpgradeLite in downgradeComponent() (#27217)
PR Close #27217
2018-12-20 16:20:42 -05:00
2013ef7ed0 test(bazel): Make sure CLI project created with Bazel works with original workflow (#27741)
Bazel bits added to a CLI project should not be destructive.
The project should still work under the original CLI workflow.

PR Close #27741
2018-12-19 18:24:25 -05:00
6a13c107cf refactor: fix broken linting rules due to revert 2018-12-19 12:59:22 -08:00
a3debf6a72 Revert "build: update gulp-clang-format dependency (#27712)" (#27759)
This reverts commit d766ad01db.

As discussed per chat, we want to temporarily revert that change because `gulp-clang-format` expects a more recent version of `clang-format` which comes with new style updates. In order to make sure that the formatting will be enforced in the meanwhile, we need to revert the update.

PR Close #27759
2018-12-19 15:13:36 -05:00
b1560f7357 build: fix our copy of Array#find typing (#27742)
It should be nullable, matching the lib.es2015.d.ts from TypeScript

PR Close #27742
2018-12-19 15:11:52 -05:00
168 changed files with 7117 additions and 11140 deletions

View File

@ -1,30 +1,3 @@
<a name="7.2.1"></a>
## [7.2.1](https://github.com/angular/angular/compare/7.2.0...7.2.1) (2019-01-16)
### Bug Fixes
* **bazel:** Add [@bazel](https://github.com/bazel)/bazel to dev deps ([#28032](https://github.com/angular/angular/issues/28032)) ([21093b9](https://github.com/angular/angular/commit/21093b9))
* **bazel:** Add /bazel-out to .gitignore ([#27874](https://github.com/angular/angular/issues/27874)) ([e4fc8ba](https://github.com/angular/angular/commit/e4fc8ba))
* **bazel:** Add ibazel to deps of Bazel project ([#28090](https://github.com/angular/angular/issues/28090)) ([28d34b6](https://github.com/angular/angular/commit/28d34b6))
* **bazel:** Bazel schematics should add router package ([#28141](https://github.com/angular/angular/issues/28141)) ([02a852a](https://github.com/angular/angular/commit/02a852a))
* **bazel:** flat module misses AMD module name on windows ([#27839](https://github.com/angular/angular/issues/27839)) ([c3d8e28](https://github.com/angular/angular/commit/c3d8e28))
* **bazel:** incorrectly always uses ngc-wrapped from "npm" workspace ([#28137](https://github.com/angular/angular/issues/28137)) ([ca3965a](https://github.com/angular/angular/commit/ca3965a))
* **bazel:** ng_package creates invalid typings reexport on windows ([#27829](https://github.com/angular/angular/issues/27829)) ([6b394f6](https://github.com/angular/angular/commit/6b394f6))
* **bazel:** packager not properly removing amd directives on windows ([#27829](https://github.com/angular/angular/issues/27829)) ([fad4145](https://github.com/angular/angular/commit/fad4145))
* **bazel:** protractor rule does not run spec files with underscore ([#28022](https://github.com/angular/angular/issues/28022)) ([f05c5f8](https://github.com/angular/angular/commit/f05c5f8))
* **bazel:** protractor utils cannot start server on windows ([#27915](https://github.com/angular/angular/issues/27915)) ([0be8487](https://github.com/angular/angular/commit/0be8487))
* **bazel:** replay compilation uses wrong compiler for building esm5 ([#28053](https://github.com/angular/angular/issues/28053)) ([fbbdaaa](https://github.com/angular/angular/commit/fbbdaaa))
* **router:** ensure URL is updated after second redirect with UrlUpdateStrategy="eager" ([#27680](https://github.com/angular/angular/issues/27680)) ([6ae7aee](https://github.com/angular/angular/commit/6ae7aee)), closes [#27116](https://github.com/angular/angular/issues/27116)
* **service-worker:** navigation urls backwards compatibility ([#27244](https://github.com/angular/angular/issues/27244)) ([585e871](https://github.com/angular/angular/commit/585e871))
### Performance Improvements
* **platform-server:** use shared `DomElementSchemaRegistry` instance ([#28150](https://github.com/angular/angular/issues/28150)) ([#28151](https://github.com/angular/angular/issues/28151)) ([6851581](https://github.com/angular/angular/commit/6851581))
<a name="7.2.0"></a>
# [7.2.0](https://github.com/angular/angular/compare/7.1.4...7.2.0) (2019-01-07)

View File

@ -8,15 +8,6 @@
}
}
},
"cli-hello-world-ivy": {
"master": {
"uncompressed": {
"runtime": 1440,
"main": 507677,
"polyfills": 38390
}
}
},
"hello_world__closure": {
"master": {
"uncompressed": {

View File

@ -0,0 +1,53 @@
{
"name": "demo",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "file:../angular/dist/packages-dist/animations",
"@angular/common": "file:../angular/dist/packages-dist/common",
"@angular/compiler": "file:../angular/dist/packages-dist/compiler",
"@angular/core": "file:../angular/dist/packages-dist/core",
"@angular/forms": "file:../angular/dist/packages-dist/forms",
"@angular/platform-browser": "file:../angular/dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../angular/dist/packages-dist/platform-browser-dynamic",
"@angular/router": "file:../angular/dist/packages-dist/router",
"core-js": "^2.5.4",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.12.0",
"@angular/bazel": "file:../angular/dist/packages-dist/bazel",
"@angular/cli": "~7.2.1",
"@angular/compiler-cli": "file:../angular/dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../angular/dist/packages-dist/language-service",
"@bazel/bazel": "^0.21.0",
"@bazel/ibazel": "^0.9.0",
"@bazel/karma": "^0.22.1",
"@bazel/typescript": "^0.22.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
}
}

View File

@ -13,6 +13,7 @@ function testBazel() {
# TODO(kyliau) Remove this once the type annotations are added to AppPage
# https://github.com/angular/angular-cli/pull/13406
cp ../app.po.ts ./e2e/src/
cp ../package.json.replace ./package.json
# Run build
# TODO(kyliau): Use `bazel build` for now. Running `ng build` requires
# node_modules to be available in project directory.

View File

@ -73,7 +73,7 @@
"@angular-devkit/architect" "^0.10.6"
"@angular-devkit/core" "^7.0.4"
"@angular-devkit/schematics" "^7.0.4"
"@bazel/typescript" "^0.21.0"
"@bazel/typescript" "^0.22.1"
"@schematics/angular" "^7.0.4"
"@types/node" "6.0.84"
semver "^5.6.0"
@ -115,12 +115,13 @@
"@bazel/bazel-linux_x64" "0.21.0"
"@bazel/bazel-win32_x64" "0.21.0"
"@bazel/typescript@^0.21.0":
version "0.21.0"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.21.0.tgz#41c304f77a42c6a016280d0f4c20e0749c3f4b2a"
integrity sha512-ASXj0RFybmqoa3LwqkTU3gNkX9bY9wL/VDNo5hlp9pynYWl4RMpe9V3m/qDIdtSuLJ+qD+Z3FKT/OcpWQHMlYA==
"@bazel/typescript@^0.22.1":
version "0.22.1"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.22.1.tgz#b52c00e8560019e2f9d273d45c04785e0ec9d9bd"
integrity sha512-88DaCCnNg8rPlKP0eAQEZuoiJkEPeiItpUS3oBR1sFQNBRJb56D25ahK8+N6LJk4qaH+ZQ1/AHOPDhfEEWvDzA==
dependencies:
protobufjs "5.0.3"
semver "5.6.0"
source-map-support "0.5.9"
tsutils "2.27.2"
@ -1907,7 +1908,7 @@ semver-intersect@1.4.0:
dependencies:
semver "^5.0.0"
"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
"semver@2 || 3 || 4 || 5", semver@5.6.0, semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==

View File

@ -1,4 +1,4 @@
# Editor configuration, see https://editorconfig.org
# Editor configuration, see http://editorconfig.org
root = true
[*]

View File

@ -1,6 +1,6 @@
# CliHelloWorldIvy
# CliHelloWorld
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0-rc.0.
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.6.
## Development server
@ -12,7 +12,7 @@ Run `ng generate component component-name` to generate a new component. You can
## 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

View File

@ -1,17 +1,14 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
"version": 1,
"cli": {
"packageManager": "yarn"
},
"newProjectRoot": "projects",
"projects": {
"cli-hello-world-ivy": {
"cli-hello-world": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
@ -22,36 +19,47 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
{
"glob": "assets",
"input": "/src",
"output": "/"
},
{
"glob": "favicon.ico",
"input": "/src",
"output": "/"
}
],
"styles": [
"src/styles.css"
{
"input": "src/styles.css"
}
],
"scripts": []
},
"configurations": {
"production": {
"dev": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
"from": "src/environments/environment.ts",
"to": "dist/environments/environment.ts"
}
],
]
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
"fileReplacements": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.prod.ts"
}
]
}
@ -60,17 +68,20 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "cli-hello-world-ivy:build"
"browserTarget": "cli-hello-world:build"
},
"configurations": {
"dev": {
"browserTarget": "cli-hello-world:build:dev"
},
"production": {
"browserTarget": "cli-hello-world-ivy:build:production"
"browserTarget": "cli-hello-world:build:production"
},
"ci": {
"progress": false
},
"ci-production": {
"browserTarget": "cli-hello-world-ivy:build:production",
"browserTarget": "cli-hello-world:build:production",
"progress": false
}
}
@ -78,7 +89,7 @@
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "cli-hello-world-ivy:build"
"browserTarget": "cli-hello-world:build"
}
},
"test": {
@ -86,15 +97,25 @@
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"karmaConfig": "./karma.conf.js",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.css"
],
"scripts": [],
"styles": [
{
"input": "src/styles.css"
}
],
"assets": [
"src/favicon.ico",
"src/assets"
{
"glob": "assets",
"input": "/src",
"output": "/"
},
{
"glob": "favicon.ico",
"input": "/src",
"output": "/"
}
]
}
},
@ -112,33 +133,36 @@
}
}
},
"cli-hello-world-ivy-e2e": {
"root": "e2e/",
"cli-hello-world-e2e": {
"root": "",
"projectType": "application",
"prefix": "",
"cli": {},
"schematics": {},
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "cli-hello-world-ivy:serve"
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "cli-hello-world:serve"
},
"configurations": {
"production": {
"devServerTarget": "cli-hello-world-ivy:serve:production"
"devServerTarget": "cli-hello-world:serve:production"
},
"ci": {
"devServerTarget": "cli-hello-world-ivy:serve:ci"
"devServerTarget": "cli-hello-world:serve:ci"
},
"ci-production": {
"devServerTarget": "cli-hello-world-ivy:serve:ci-production"
"devServerTarget": "cli-hello-world:serve:ci-production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
@ -147,5 +171,13 @@
}
}
},
"defaultProject": "cli-hello-world-ivy"
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}

View File

@ -9,7 +9,7 @@ describe('cli-hello-world App', () => {
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to cli-hello-world-ivy!');
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
it('the percent pipe should work', () => {

View File

@ -1,7 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
@ -10,4 +11,4 @@
"node"
]
}
}
}

View File

@ -12,14 +12,16 @@ module.exports = function (config) {
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'),
reports: ['html', 'lcovonly', 'text-summary'],
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,

View File

@ -1,5 +1,5 @@
{
"name": "cli-hello-world-ivy",
"name": "cli-hello-world",
"version": "0.0.0",
"license": "MIT",
"scripts": {
@ -29,24 +29,24 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.12.0-rc.0",
"@angular/cli": "~7.2.0-rc.0",
"@angular-devkit/build-angular": "~0.10.3",
"@angular/cli": "7.0.3",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.4",
"@types/node": "~6.0.60",
"codelyzer": "^4.3.0",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "file:../../node_modules/protractor",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "file:../../node_modules/typescript"
}
}

View File

@ -6,7 +6,7 @@ const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome',
@ -25,7 +25,7 @@ exports.config = {
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}

View File

@ -12,7 +12,7 @@
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>

View File

@ -1,6 +1,5 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
@ -9,23 +8,20 @@ describe('AppComponent', () => {
],
}).compileComponents();
}));
it('should create the app', () => {
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'cli-hello-world-ivy'`, () => {
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('cli-hello-world-ivy');
});
it('should render title in a h1 tag', () => {
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to cli-hello-world-ivy!');
});
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});

View File

@ -6,5 +6,5 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'cli-hello-world-ivy';
title = 'app';
}

View File

@ -1,11 +0,0 @@
# 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

View File

@ -1,16 +1,8 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
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.

View File

@ -2,11 +2,12 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>CliHelloWorldIvy</title>
<title>CliHelloWorld</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script>window['ngDevMode'] = true;</script>
</head>
<body>
<app-root></app-root>

View File

@ -9,4 +9,4 @@ if (environment.production) {
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
.catch(err => console.log(err));

View File

@ -11,17 +11,14 @@
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER 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
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
@ -43,36 +40,19 @@
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/**
* Web Animations `@angular/platform-browser/animations`
* 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`.
/** 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';
/**
* 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;
*
*/
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
@ -80,6 +60,7 @@
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@ -2,13 +2,12 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
"angularCompilerOptions": {
"enableIvy": "ngtsc"
}
]
}

View File

@ -2,6 +2,8 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"types": [
"jasmine",
"node"
@ -15,4 +17,4 @@
"**/*.spec.ts",
"**/*.d.ts"
]
}
}

View File

@ -1,17 +0,0 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

View File

@ -1,21 +1,21 @@
{
"angularCompilerOptions": {
"enableIvy": "ngtsc",
},
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"es2017",
"dom"
]
}

View File

@ -1,6 +1,6 @@
{
"rulesDirectory": [
"codelyzer"
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
@ -18,6 +18,7 @@
"forin": true,
"import-blacklist": [
true,
"rxjs",
"rxjs/Rx"
],
"import-spacing": true,
@ -65,7 +66,6 @@
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
@ -117,6 +117,18 @@
"check-separator",
"check-type"
],
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,

File diff suppressed because it is too large Load Diff

View File

@ -61,9 +61,9 @@ for testDir in ${TEST_DIRS}; do
yarn install --cache-folder ../$cache
yarn test || exit 1
# Track payload size for cli-hello-world, cli-hello-world-ivy and hello_world__closure
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]] || [[ $testDir == hello_world__closure ]]); then
if ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]]); then
# Track payload size for cli-hello-world and hello_world__closure and the render3 tests
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__closure ]]); then
if [[ $testDir == cli-hello-world ]]; then
yarn build
fi

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "7.2.1",
"version": "7.2.0",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",

View File

@ -23,7 +23,7 @@ export function supportsAnimationEventCreation() {
try {
makeAnimationEvent('end', 'test', 0);
supported = true;
} catch {
} catch (e) {
}
return supported;
}

View File

@ -149,7 +149,7 @@ export class NgForOf<T> implements DoCheck {
if (!this._differ && value) {
try {
this._differ = this._differs.find(value).create(this.ngForTrackBy);
} catch {
} catch (e) {
throw new Error(
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
}

View File

@ -150,7 +150,7 @@ export class BrowserViewportScroller implements ViewportScroller {
private supportScrollRestoration(): boolean {
try {
return !!this.window && !!this.window.scrollTo;
} catch {
} catch (e) {
return false;
}
}

View File

@ -15,7 +15,6 @@ ts_library(
"//packages/platform-browser",
"//packages/platform-browser-dynamic",
"//packages/platform-browser/testing",
"//packages/private/testing",
],
)

View File

@ -11,7 +11,6 @@ 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 {TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {modifiedInIvy} from '@angular/private/testing';
describe('insert/remove', () => {
@ -107,20 +106,17 @@ describe('insert/remove', () => {
}));
it('should resolve a with injector', async(() => {
let fixture = TestBed.createComponent(TestComponent);
modifiedInIvy('Static ViewChild and ContentChild queries are resolved in update mode')
.it('should resolve with an injector', async(() => {
let fixture = TestBed.createComponent(TestComponent);
// We are accessing a ViewChild (ngComponentOutlet) before change detection has run
fixture.componentInstance.cmpRef = null;
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();
}));
fixture.componentInstance.cmpRef = null;
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(() => {
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;

View File

@ -22,7 +22,7 @@ import {performWatchCompilation, createPerformWatchHost} from './perform_watch'
export function main(
args: string[], consoleError: (s: string) => void = console.error,
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers): number {
config?: NgcParsedConfiguration): number {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) {
@ -32,12 +32,8 @@ export function main(
const result = watchMode(project, options, consoleError);
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
}
const {diagnostics: compileDiags} = performCompilation({
rootNames,
options,
emitFlags,
emitCallback: createEmitCallback(options), customTransformers
});
const {diagnostics: compileDiags} = performCompilation(
{rootNames, options, emitFlags, emitCallback: createEmitCallback(options)});
return reportErrorsAndExit(compileDiags, options, consoleError);
}

View File

@ -1,118 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as 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;
}

View File

@ -15,7 +15,7 @@ import {hasNameIdentifier, isDefined} from '../utils';
export interface ExportInfo {
identifier: string;
from: string;
dtsFrom?: string|null;
dtsFrom: string|null;
}
export type PrivateDeclarationsAnalyses = ExportInfo[];
@ -52,7 +52,7 @@ export class PrivateDeclarationsAnalyzer {
return Array.from(privateDeclarations.keys()).map(id => {
const from = id.getSourceFile().fileName;
const declaration = privateDeclarations.get(id) !;
const dtsDeclaration = this.host.getDtsDeclaration(declaration.node);
const dtsDeclaration = this.host.getDtsDeclarationOfClass(declaration.node);
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
return {identifier: id.text, from, dtsFrom};
});

View File

@ -14,7 +14,7 @@ import {BundleProgram} from '../packages/bundle_program';
import {findAll, getNameText, isDefined} from '../utils';
import {DecoratedClass} from './decorated_class';
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
import {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
export const DECORATORS = 'decorators' 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`.
*/
export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost {
protected dtsDeclarationMap: Map<string, ts.Declaration>|null;
protected dtsClassMap: Map<string, ts.ClassDeclaration>|null;
constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) {
super(checker);
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
this.dtsClassMap = dts && this.computeDtsClassMap(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.
*/
getGenericArityOfClass(clazz: ts.Declaration): number|null {
const dtsDeclaration = this.getDtsDeclaration(clazz);
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
const dtsClass = this.getDtsDeclarationOfClass(clazz);
if (dtsClass) {
return dtsClass.typeParameters ? dtsClass.typeParameters.length : 0;
}
return null;
}
/**
* Take an exported declaration of a class (maybe down-leveled to a variable) and look up the
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
* 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
@ -346,47 +346,19 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
* `ts.Program` as the input declaration.
*/
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null {
if (!this.dtsDeclarationMap) {
return null;
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null {
if (this.dtsClassMap) {
if (ts.isClassDeclaration(declaration)) {
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;
}
}
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;
return 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 /////////////
@ -766,7 +738,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
if (!name) {
if (isNamedDeclaration(node)) {
if (isNamedDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
name = node.name.text;
nameNode = node.name;
} else {
@ -874,8 +846,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
/**
* Get the parameter type and decorators for a class where the information is stored via
* calls to `__decorate` helpers.
* Get the parameter type and decorators for a class where the information is stored on
* in calls to `__decorate` helpers.
*
* Reflect over the helpers to find the decorators and types about each of
* the class's constructor parameters.
@ -1030,9 +1002,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @param dtsProgram The program containing all the typings files.
* @returns a map of class names to class declarations.
*/
protected computeDtsDeclarationMap(dtsRootFileName: string, dtsProgram: ts.Program):
Map<string, ts.Declaration> {
const dtsDeclarationMap = new Map<string, ts.Declaration>();
protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program):
Map<string, ts.ClassDeclaration> {
const dtsClassMap = new Map<string, ts.ClassDeclaration>();
const checker = dtsProgram.getTypeChecker();
// First add all the classes that are publicly exported from the entry-point
@ -1040,38 +1012,13 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (!rootFile) {
throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`);
}
collectExportedDeclarations(checker, dtsDeclarationMap, rootFile);
collectExportedClasses(checker, dtsClassMap, rootFile);
// Now add any additional classes that are exported from individual dts files,
// but are not publicly exported from the entry-point.
dtsProgram.getSourceFiles().forEach(
sourceFile => { collectExportedDeclarations(checker, dtsDeclarationMap, sourceFile); });
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};
sourceFile => { collectExportedClasses(checker, dtsClassMap, sourceFile); });
return dtsClassMap;
}
}
@ -1182,10 +1129,8 @@ function isThisAssignment(node: ts.Declaration): node is ts.BinaryExpression&
node.left.expression.kind === ts.SyntaxKind.ThisKeyword;
}
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration&
{name: ts.Identifier} {
const anyNode: any = node;
return !!anyNode.name && ts.isIdentifier(anyNode.name);
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration {
return !!(node as any).name;
}
@ -1208,11 +1153,13 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
}
/**
* Collect mappings between exported declarations in a source file and its associated
* declaration in the typings program.
* Search a source file for exported classes, storing them in the provided `dtsClassMap`.
* @param checker The typechecker for the source program.
* @param dtsClassMap The map in which to store the collected exported classes.
* @param srcFile The source file to search for exported classes.
*/
function collectExportedDeclarations(
checker: ts.TypeChecker, dtsDeclarationMap: Map<string, ts.Declaration>,
function collectExportedClasses(
checker: ts.TypeChecker, dtsClassMap: Map<string, ts.ClassDeclaration>,
srcFile: ts.SourceFile): void {
const srcModule = srcFile && checker.getSymbolAtLocation(srcFile);
const moduleExports = srcModule && checker.getExportsOfModule(srcModule);
@ -1223,8 +1170,8 @@ function collectExportedDeclarations(
}
const declaration = exportedSymbol.valueDeclaration;
const name = exportedSymbol.name;
if (declaration && !dtsDeclarationMap.has(name)) {
dtsDeclarationMap.set(name, declaration);
if (declaration && ts.isClassDeclaration(declaration) && !dtsClassMap.has(name)) {
dtsClassMap.set(name, declaration);
}
});
}

View File

@ -19,21 +19,6 @@ export function isSwitchableVariableDeclaration(node: ts.Node):
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
*/
@ -60,13 +45,4 @@ export interface NgccReflectionHost extends ReflectionHost {
* @returns An array of decorated classes.
*/
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[];
}

View File

@ -11,7 +11,6 @@ import {mkdir, mv} from 'shelljs';
import * as ts from 'typescript';
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../analysis/module_with_providers_analyzer';
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
@ -26,7 +25,6 @@ import {EntryPoint} from './entry_point';
import {EntryPointBundle} from './entry_point_bundle';
/**
* 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).
@ -61,14 +59,13 @@ export class Transformer {
const reflectionHost = this.getHost(isCore, bundle);
// Parse and analyze the files.
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle);
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
this.analyzeProgram(reflectionHost, isCore, bundle);
// Transform the source files and source maps.
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
const renderedFiles = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
// Write out all the transformed files.
renderedFiles.forEach(file => this.writeFile(file));
@ -105,26 +102,16 @@ export class Transformer {
ProgramAnalyses {
const typeChecker = bundle.src.program.getTypeChecker();
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
const decorationAnalyzer = new DecorationAnalyzer(
typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
const moduleWithProvidersAnalyzer =
bundle.dts && new ModuleWithProvidersAnalyzer(reflectionHost, referencesRegistry);
const moduleWithProvidersAnalyses = moduleWithProvidersAnalyzer &&
moduleWithProvidersAnalyzer.analyzeProgram(bundle.src.program);
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
const privateDeclarationsAnalyzer =
new PrivateDeclarationsAnalyzer(reflectionHost, referencesRegistry);
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
const privateDeclarationsAnalyses =
privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program);
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses};
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
}
writeFile(file: FileInfo): void {
@ -142,5 +129,4 @@ interface ProgramAnalyses {
decorationAnalyses: Map<ts.SourceFile, CompiledFile>;
switchMarkerAnalyses: SwitchMarkerAnalyses;
privateDeclarationsAnalyses: ExportInfo[];
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null;
}

View File

@ -15,10 +15,9 @@ import * as ts from 'typescript';
import {Decorator} from '../../../ngtsc/host';
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
import {translateStatement, translateType} from '../../../ngtsc/translator';
import {NgccImportManager} from './ngcc_import_manager';
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 {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
import {IMPORT_PREFIX} from '../constants';
@ -50,20 +49,6 @@ interface DtsClassInfo {
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
* of Ivy static fields. The map is keyed by the container node, such that we
@ -86,8 +71,7 @@ export abstract class Renderer {
renderProgram(
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null): FileInfo[] {
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
const renderedFiles: FileInfo[] = [];
// Transform the source files.
@ -103,16 +87,16 @@ export abstract class Renderer {
// Transform the .d.ts files
if (this.bundle.dts) {
const dtsFiles = this.getTypingsFilesToRender(
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
const dtsFiles = this.getTypingsFilesToRender(decorationAnalyses);
// If the dts entry-point is not already there (it did not have compiled classes)
// then add it now, to ensure it gets its extra exports rendered.
if (!dtsFiles.has(this.bundle.dts.file)) {
dtsFiles.set(this.bundle.dts.file, new DtsRenderInfo());
dtsFiles.set(this.bundle.dts.file, []);
}
dtsFiles.forEach(
(renderInfo, file) => renderedFiles.push(...this.renderDtsFile(file, renderInfo)));
(classes, file) => renderedFiles.push(
...this.renderDtsFile(file, classes, privateDeclarationsAnalyses)));
}
return renderedFiles;
@ -167,12 +151,14 @@ export abstract class Renderer {
return this.renderSourceAndMap(sourceFile, input, outputText);
}
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
renderDtsFile(
dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[],
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
const input = this.extractSourceMap(dtsFile);
const outputText = new MagicString(input.source);
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
renderInfo.classInfo.forEach(dtsClass => {
dtsClasses.forEach(dtsClass => {
const endOfClass = dtsClass.dtsDeclaration.getEnd();
dtsClass.compilation.forEach(declaration => {
const type = translateType(declaration.type, importManager);
@ -181,67 +167,26 @@ export abstract class Renderer {
});
});
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
this.addImports(
outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile));
this.addExports(outputText, dtsFile.fileName, renderInfo.privateExports);
if (dtsFile === this.bundle.dts !.file) {
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);
}
/**
* 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):
void;
protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void;
@ -357,67 +302,22 @@ export abstract class Renderer {
return result;
}
protected getTypingsFilesToRender(
decorationAnalyses: DecorationAnalyses,
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|
null): Map<ts.SourceFile, DtsRenderInfo> {
const dtsMap = new Map<ts.SourceFile, DtsRenderInfo>();
// Capture the rendering info from the decoration analyses
decorationAnalyses.forEach(compiledFile => {
protected getTypingsFilesToRender(analyses: DecorationAnalyses):
Map<ts.SourceFile, DtsClassInfo[]> {
const dtsMap = new Map<ts.SourceFile, DtsClassInfo[]>();
analyses.forEach(compiledFile => {
compiledFile.compiledClasses.forEach(compiledClass => {
const dtsDeclaration = this.host.getDtsDeclaration(compiledClass.declaration);
const dtsDeclaration = this.host.getDtsDeclarationOfClass(compiledClass.declaration);
if (dtsDeclaration) {
const dtsFile = dtsDeclaration.getSourceFile();
const renderInfo = dtsMap.get(dtsFile) || new DtsRenderInfo();
renderInfo.classInfo.push({dtsDeclaration, compilation: compiledClass.compilation});
dtsMap.set(dtsFile, renderInfo);
const classes = dtsMap.get(dtsFile) || [];
classes.push({dtsDeclaration, compilation: compiledClass.compilation});
dtsMap.set(dtsFile, classes);
}
});
});
// 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;
}
/**
* 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'));
}
}
/**
@ -486,7 +386,7 @@ export function renderDefinitions(
}
export function stripExtension(filePath: string): string {
return filePath.replace(/\.(js|d\.ts)$/, '');
return filePath.replace(/\.(js|d\.ts$)/, '');
}
/**
@ -499,9 +399,3 @@ function createAssignmentStatement(
const receiver = new WrappedNodeExpr(receiverName);
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}`;
}

View File

@ -1,401 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as 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]) :
[];
}
});
});

View File

@ -84,8 +84,6 @@ export function getFakeCore() {
export class InjectionToken {
constructor(name: string) {}
}
export interface ModuleWithProviders<T = any> {}
`
};
}

View File

@ -453,7 +453,6 @@ const TYPINGS_SRC_FILES = [
},
{name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'},
{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/missing-class.js', contents: 'export class MissingClass2 {}'}, {
name: '/src/flat-file.js',
@ -477,7 +476,6 @@ const TYPINGS_DTS_FILES = [
contents:
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
},
{name: '/typings/func1.d.ts', contents: 'export declare function mooFn(): void;'},
{
name: '/typings/internal.d.ts',
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
@ -485,54 +483,6 @@ const TYPINGS_DTS_FILES = [
{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('getDecoratorsOfDeclaration()', () => {
@ -1336,20 +1286,10 @@ describe('Fesm2015ReflectionHost', () => {
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
it('should find the dts declaration for exported functions', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
const dtsDeclaration = host.getDtsDeclaration(mooFn);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/func1.d.ts');
});
it('should return null if there is no matching class in the matching dts file', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
@ -1357,7 +1297,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
});
it('should return null if there is no matching dts file', () => {
@ -1367,7 +1307,7 @@ describe('Fesm2015ReflectionHost', () => {
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
});
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
@ -1378,7 +1318,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
@ -1389,7 +1329,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class3);
const dtsDeclaration = host.getDtsDeclarationOfClass(class3);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
});
@ -1401,7 +1341,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(internalClass);
const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
});
@ -1415,42 +1355,12 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const class2DtsDeclaration = host.getDtsDeclaration(class2);
const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2);
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
const internalClass2DtsDeclaration = host.getDtsDeclarationOfClass(internalClass2);
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
.toEqual('/typings/class2.d.ts');
});
});
describe('getModuleWithProvidersFunctions', () => {
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
() => {
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
const host = new 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'],
]);
});
});
});

View File

@ -11,7 +11,6 @@ import * as ts from 'typescript';
import {fromObject, generateMapFileComment} from 'convert-source-map';
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
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 {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
@ -48,9 +47,9 @@ class TestRenderer extends Renderer {
function createTestRenderer(
packageName: string, files: {name: string, contents: string}[],
dtsFiles?: {name: string, contents: string}[]) {
dtsFile?: {name: string, contents: string}) {
const isCore = packageName === '@angular/core';
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFiles);
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFile && [dtsFile]);
const typeChecker = bundle.src.program.getTypeChecker();
const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
const referencesRegistry = new NgccReferencesRegistry(host);
@ -58,8 +57,6 @@ function createTestRenderer(
new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
.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 =
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
const renderer = new TestRenderer(host, isCore, bundle);
@ -67,8 +64,7 @@ function createTestRenderer(
spyOn(renderer, 'addDefinitions').and.callThrough();
spyOn(renderer, 'removeDecorators').and.callThrough();
return {renderer, decorationAnalyses, switchMarkerAnalyses, moduleWithProvidersAnalyses,
privateDeclarationsAnalyses};
return {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
}
@ -125,11 +121,10 @@ describe('Renderer', () => {
describe('renderProgram()', () => {
it('should render the modified contents; and a new map file, if the original provided no map file.',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
@ -139,11 +134,9 @@ describe('Renderer', () => {
it('should render as JavaScript', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [COMPONENT_PROGRAM]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [COMPONENT_PROGRAM]);
renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{
@ -161,12 +154,10 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
describe('calling abstract methods', () => {
it('should call addImports with the source code and info about the core Angular library.',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const addImportsSpy = renderer.addImports as jasmine.Spy;
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
expect(addImportsSpy.calls.first().args[1]).toEqual([
@ -176,12 +167,10 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({
@ -198,12 +187,10 @@ 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',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
@ -225,16 +212,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
describe('source map merging', () => {
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,
moduleWithProvidersAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer(
'test-package', [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
}]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment());
@ -245,16 +230,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
() => {
// Mock out reading the map file from disk
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer(
'test-package', [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
}]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
@ -276,12 +259,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
contents: `export const NgModule = () => null;`
};
// The package name of `@angular/core` indicates that we are compiling the core library.
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
@ -296,11 +277,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
};
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('@angular/core', [CORE_FILE]);
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('@angular/core', [CORE_FILE]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toContain(`/*@__PURE__*/ setClassMetadata(`);
@ -311,12 +291,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
describe('rendering typings', () => {
it('should render extract types into typings files', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
expect(typingsFile.contents)
@ -325,195 +303,30 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
});
it('should render imports into typings files', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`);
});
it('should render exports into typings files', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
// Add a mock export to trigger export rendering
privateDeclarationsAnalyses.push(
{identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'});
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
expect(typingsFile.contents)
.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};`);
});
});
});
});

View File

@ -434,7 +434,7 @@ function extractHostBindings(
});
}
const {attributes, listeners, properties} = parseHostBindings(hostMetadata);
const {attributes, listeners, properties, animations} = parseHostBindings(hostMetadata);
filterToMembersWithDecorator(members, 'HostBinding', coreModule)
.forEach(({member, decorators}) => {

View File

@ -109,7 +109,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
const valueContext = node.getSourceFile();
let typeContext = valueContext;
const typeNode = this.reflector.getDtsDeclaration(node);
const typeNode = this.reflector.getDtsDeclarationOfClass(node);
if (typeNode !== null) {
typeContext = typeNode.getSourceFile();
}
@ -183,8 +183,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
return toR3Reference(valueRef, valueRef, valueContext, valueContext);
} else {
let typeRef = valueRef;
let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
if (typeNode !== null && ts.isClassDeclaration(typeNode)) {
let typeNode = this.reflector.getDtsDeclarationOfClass(typeRef.node);
if (typeNode !== null) {
typeRef = new ResolvedReference(typeNode, typeNode.name !);
}
return toR3Reference(valueRef, typeRef, valueContext, typeContext);
@ -197,20 +197,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
*/
private _extractModuleFromModuleWithProvidersFn(node: ts.FunctionDeclaration|
ts.MethodDeclaration): ts.Expression|null {
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 {
const type = node.type;
// Examine the type of the function to see if it's a ModuleWithProviders reference.
if (!ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
if (type === undefined || !ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
return null;
}
@ -237,32 +226,6 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
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.
*/

View File

@ -448,7 +448,7 @@ export interface ReflectionHost {
getVariableValue(declaration: ts.VariableDeclaration): ts.Expression|null;
/**
* Take an exported declaration (maybe a class down-leveled to a variable) and look up the
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
* 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
@ -456,8 +456,8 @@ export interface ReflectionHost {
* 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.
*
* Note that the `ts.Declaration` 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.
*/
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null;
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null;
}

View File

@ -188,7 +188,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return declaration.initializer || null;
}
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; }
getDtsDeclarationOfClass(_: ts.Declaration): ts.ClassDeclaration|null { return null; }
/**
* Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.

View File

@ -208,21 +208,14 @@ export class NgtscProgram implements api.Program {
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
};
const customTransforms = opts && opts.customTransformers;
const beforeTransforms =
const transforms =
[ivyTransformFactory(this.compilation !, this.reflector, this.coreImportsFrom)];
if (this.factoryToSourceInfo !== null) {
beforeTransforms.push(
generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
transforms.push(generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
}
if (this.isCore) {
beforeTransforms.push(ivySwitchTransform);
transforms.push(ivySwitchTransform);
}
if (customTransforms && customTransforms.beforeTs) {
beforeTransforms.push(...customTransforms.beforeTs);
}
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
const emitResult = emitCallback({
program: this.tsProgram,
@ -230,8 +223,7 @@ export class NgtscProgram implements api.Program {
options: this.options,
emitOnlyDtsFiles: false, writeFile,
customTransformers: {
before: beforeTransforms,
after: customTransforms && customTransforms.afterTs,
before: transforms,
},
});
return emitResult;

View File

@ -246,7 +246,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
if (packageTypings === originalImportedFile) {
moduleName = importedFilePackageName;
}
} catch {
} catch (e) {
// the above require() will throw if there is no package.json file
// and this is safe to ignore and correct to keep the longer
// moduleName in this case
@ -582,7 +582,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
result = false;
}
}
} catch {
} catch (e) {
// If we encounter any errors assume we this isn't a bundle index.
result = false;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker} from '@angular/compiler/src/core';
import {AttributeMarker, InitialStylingFlags} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@ -48,15 +48,17 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
const $c2$ = ["cx", "20", "cy", "30", "r", "50"];
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
const $c3$ = ["cx", "20", "cy", "30", "r", "50"];
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $c1$);
$r3$.ɵelementStyling($c2$);
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(1, "svg");
$r3$.ɵelement(2, "circle", $c2$);
$r3$.ɵelement(2, "circle", $c3$);
$r3$.ɵelementEnd();
$r3$.ɵnamespaceHTML();
$r3$.ɵelementStart(3, "p");
@ -98,11 +100,13 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $c1$);
$r3$.ɵelementStyling($c2$);
$r3$.ɵnamespaceMathML();
$r3$.ɵelementStart(1, "math");
$r3$.ɵelement(2, "infinity");
@ -146,11 +150,13 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $c1$);
$r3$.ɵelementStyling($c2$);
$r3$.ɵtext(1, "Hello ");
$r3$.ɵelementStart(2, "b");
$r3$.ɵtext(3, "World");
@ -434,14 +440,14 @@ describe('compiler compliance', () => {
$r3$.ɵallocHostVars(14);
}
if (rf & 2) {
$r3$componentHostSyntheticProperty(elIndex, "@expansionHeight",
$r3$elementProperty(elIndex, "expansionHeight",
$r3$.ɵbind(
$r3$.ɵpureFunction2(5, $_c1$, ctx.getExpandedState(),
$r3$.ɵpureFunction2(2, $_c0$, ctx.collapsedHeight, ctx.expandedHeight)
)
), null, true
);
$r3$componentHostSyntheticProperty(elIndex, "@expansionWidth",
$r3$elementProperty(elIndex, "expansionWidth",
$r3$.ɵbind(
$r3$.ɵpureFunction2(11, $_c1$, ctx.getExpandedState(),
$r3$.ɵpureFunction2(8, $_c2$, ctx.collapsedWidth, ctx.expandedWidth)
@ -480,8 +486,8 @@ describe('compiler compliance', () => {
const factory =
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
const $e0_classBindings$ = ["error"];
const $e0_styleBindings$ = ["background-color"];
const _c0 = ["error"];
const _c1 = ["background-color"];
MyComponent.ngComponentDef = i0.ɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
factory: function MyComponent_Factory(t){
@ -492,7 +498,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf,ctx){
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$);
$r3$.ɵelementStyling(_c0, _c1);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -767,7 +773,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $c1$ = ["foo", ""];
const $c2$ = ["if", ""];
function MyComponent_li_2_Template(rf, ctx) {
function MyComponent_li_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -789,7 +795,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul", null, $c1$);
$r3$.ɵtemplate(2, MyComponent_li_2_Template, 2, 2, "li", $c2$);
$r3$.ɵtemplate(2, MyComponent_li_Template_2, 2, 2, "li", $c2$);
$r3$.ɵelementEnd();
}
},
@ -1086,224 +1092,156 @@ describe('compiler compliance', () => {
});
});
describe('content projection', () => {
it('should support content projection in root template', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
it('should support content projection in root template', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
export class SimpleComponent {}
@Component({selector: 'simple', template: '<div><ng-content></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>'
@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 MyApp {}
`
}
};
export class ComplexComponent { }
const SimpleComponentDefinition = `
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
});`;
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
export class MyModule {}
const ComplexComponentDefinition = `
const $c3$ = ["id","first"];
const $c4$ = ["id","second"];
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
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
});
`;
@Component({
selector: 'my-app',
template: '<simple>content</simple> <complex></complex>'
})
export class MyApp {}
`
}
};
const result = compile(files, angularFiles);
const source = result.source;
const SimpleComponentDefinition = `
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
});`;
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$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c1$ = ["id", "second"];
function Cmp_div_0_Template(rf, ctx) { if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c1$);
const ComplexComponentDefinition = `
const $c3$ = ["id","first"];
const $c4$ = ["id","second"];
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
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();
} }
const $_c4$ = ["id", "third"];
function Cmp_div_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c4$);
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
$r3$.ɵelementStart(2, "div", $c4$);
$r3$.ɵprojection(3, 2);
$r3$.ɵelementEnd();
}
}
function Cmp_ng_template_2_Template(rf, ctx) {
if (rf & 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) {
$r3$.ɵprojectionDef($_c2$, $_c3$);
$r3$.ɵtemplate(0, Cmp_div_0_Template, 2, 0, "div", $_c0$);
$r3$.ɵtemplate(1, Cmp_div_1_Template, 2, 0, "div", $_c0$);
$r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
}
}
`;
},
encapsulation: 2
});
`;
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
const result = compile(files, angularFiles);
const source = result.source;
it('should support content projection in both the root and nested templates', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
expectEmit(result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
expectEmit(
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
});
@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 {}
it('should support content projection in nested templates', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@NgModule({ declarations: [Cmp] })
class Module {}
`
}
};
@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 {}
const output = `
function Cmp_ng_template_1_ng_template_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵprojection(0, 4);
}
@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_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵprojection(0, 3);
$r3$template(1, Cmp_ng_template_1_ng_template_1_Template, 1, 0, "ng-template");
}
}
function Cmp_ng_template_Template_2(rf, ctx) {
if (rf & 1) {
$r3$projectionDef();
$r3$.ɵtext(0, " '*' selector: ");
$r3$.ɵprojection(1);
}
function Cmp_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtext(0, " '*' selector in a template: ");
$r3$.ɵprojection(1);
}
}
template: function Cmp_Template(rf, ctx) {
if (rf & 1) {
$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");
}
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_1_Template, 2, 0, "ng-template");
$r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
$r3$.ɵprojection(3, 2);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
}
`;
}
`;
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
describe('queries', () => {
@ -1911,7 +1849,7 @@ describe('compiler compliance', () => {
const $c2$ = ["if", ""];
const $c3$ = ["baz", ""];
const $c4$ = ["bar", ""];
function MyComponent_div_3_span_2_Template(rf, ctx) {
function MyComponent_div_span_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "span");
$r3$.ɵtext(1);
@ -1926,11 +1864,11 @@ describe('compiler compliance', () => {
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation3("", $foo$, "-", $bar$, "-", $baz$, ""));
}
}
function MyComponent_div_3_Template(rf, ctx) {
function MyComponent_div_Template_3(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵtext(1);
$r3$.ɵtemplate(2, MyComponent_div_3_span_2_Template, 2, 3, "span", $c2$);
$r3$.ɵtemplate(2, MyComponent_div_span_Template_2, 2, 3, "span", $c2$);
$r3$.ɵelement(3, "span", null, $c4$);
$r3$.ɵelementEnd();
}
@ -1952,7 +1890,7 @@ describe('compiler compliance', () => {
if (rf & 1) {
$r3$.ɵelement(0, "div", null, $c1$);
$r3$.ɵtext(2);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 5, 2, "div", $c2$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 5, 2, "div", $c2$);
$r3$.ɵelement(4, "div", null, $c3$);
}
if (rf & 2) {
@ -2000,7 +1938,7 @@ describe('compiler compliance', () => {
const $c1$ = ["foo", ""];
const $c2$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_0_span_3_Template(rf, ctx) {
function MyComponent_div_span_Template_3(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "span");
$i0$.ɵtext(1);
@ -2013,11 +1951,11 @@ describe('compiler compliance', () => {
}
}
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵelement(1, "div", null, $c1$);
$i0$.ɵtemplate(3, MyComponent_div_0_span_3_Template, 2, 2, "span", $c2$);
$i0$.ɵtemplate(3, MyComponent_div_span_Template_3, 2, 2, "span", $c2$);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -2029,7 +1967,7 @@ describe('compiler compliance', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 4, 1, "div", $c0$);
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 4, 1, "div", $c0$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -2224,7 +2162,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent__svg_g_1_Template(rf, ctx) {
function MyComponent__svg_g_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(0,"g");
@ -2243,7 +2181,7 @@ describe('compiler compliance', () => {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(0,"svg");
$r3$.ɵtemplate(1, MyComponent__svg_g_1_Template, 2, 0, ":svg:g", $t1_attrs$);
$r3$.ɵtemplate(1, MyComponent__svg_g_Template_1, 2, 0, ":svg:g", $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) { $r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items)); }
@ -2300,7 +2238,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_1_Template(rf, ctx) {
function MyComponent_li_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -2321,7 +2259,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul");
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 2, 1, "li", $t1_attrs$);
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 2, 1, "li", $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -2380,7 +2318,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t4_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_1_li_4_Template(rf, ctx) {
function MyComponent_li_li_Template_4(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -2393,14 +2331,14 @@ describe('compiler compliance', () => {
}
}
function MyComponent_li_1_Template(rf, ctx) {
function MyComponent_li_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵelementStart(1, "div");
$r3$.ɵtext(2);
$r3$.ɵelementEnd();
$r3$.ɵelementStart(3, "ul");
$r3$.ɵtemplate(4, MyComponent_li_1_li_4_Template, 2, 2, "li", $t4_attrs$);
$r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 2, "li", $t4_attrs$);
$r3$.ɵelementEnd();
$r3$.ɵelementEnd();
}
@ -2421,7 +2359,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul");
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 5, 2, "li", $c1$);
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 5, 2, "li", $c1$);
$r3$.ɵelementEnd();
}
if (rf & 2) {

View File

@ -24,13 +24,13 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Directive({selector: '[i18n]'})
export class I18nDirective {}
@Component({selector: 'my-component', template: '<div i18n></div>'})
export class MyComponent {}
@NgModule({declarations: [I18nDirective, MyComponent]})
export class MyModule{}`
}
@ -39,11 +39,11 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "div");
@ -64,7 +64,7 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Directive({selector: '[i18n]'})
export class I18nDirective {}
@ -73,10 +73,10 @@ describe('compiler compliance: directives', () => {
@Directive({selector: '[foo]'})
export class FooDirective {}
@Component({selector: 'my-component', template: '<div i18n-foo></div>'})
export class MyComponent {}
@NgModule({declarations: [I18nDirective, I18nFooDirective, FooDirective, MyComponent]})
export class MyModule{}`
}
@ -85,11 +85,11 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "div");
@ -111,15 +111,15 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, Input, NgModule} from '@angular/core';
@Directive({selector: '[someDirective]'})
export class SomeDirective {
@Input() someDirective;
}
@Component({selector: 'my-component', template: '<div [someDirective]="true"></div>'})
export class MyComponent {}
@NgModule({declarations: [SomeDirective, MyComponent]})
export class MyModule{}
`
@ -129,7 +129,7 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
const _c0 = [${AttributeMarker.SelectOnly}, "someDirective"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
@ -184,7 +184,7 @@ describe('compiler compliance: directives', () => {
const MyComponentDefinition = `
const $_c0$ = ["directiveA", ""];
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵtext(0, "Some content");
}
@ -194,7 +194,7 @@ describe('compiler compliance: directives', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", $_c0$);
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", $_c0$);
}
},
@ -236,9 +236,9 @@ describe('compiler compliance: directives', () => {
const MyComponentDefinition = `
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
const $_c1$ = ["directiveA", ""];
function MyComponent_ng_container_0_Template(rf, ctx) {
function MyComponent_ng_container_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementContainerStart(0, $_c1$);
$r3$.ɵtext(1, "Some content");
@ -250,7 +250,7 @@ describe('compiler compliance: directives', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_container_0_Template, 2, 0, "ng-container", $_c0$);
$r3$.ɵtemplate(0, MyComponent_ng_container_Template_0, 2, 0, "ng-container", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.showing));
@ -272,15 +272,15 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, Input, NgModule} from '@angular/core';
@Directive({selector: '[someDirective]'})
export class SomeDirective {
@Input() someDirective;
}
@Component({selector: 'my-component', template: '<ng-template [someDirective]="true"></ng-template>'})
export class MyComponent {}
@NgModule({declarations: [SomeDirective, MyComponent]})
export class MyModule{}
`
@ -290,14 +290,14 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", $c0_a0$);
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 0, 0, "ng-template", $c0_a0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
@ -321,15 +321,15 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, Input, NgModule} from '@angular/core';
@Directive({selector: '[someDirective]'})
export class SomeDirective {
@Input() someDirective;
}
@Component({selector: 'my-component', template: '<div *someDirective></div>'})
export class MyComponent {}
@NgModule({declarations: [SomeDirective, MyComponent]})
export class MyModule{}
`
@ -338,14 +338,14 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
const $c0_a0$ = ["someDirective", ""];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 1, 0, "div", $c0_a0$);
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 1, 0, "div", $c0_a0$);
}
},
@ -367,17 +367,17 @@ describe('compiler compliance: directives', () => {
app: {
'spec.ts': `
import {Component, Directive, Output, EventEmitter, NgModule} from '@angular/core';
@Directive({selector: '[someDirective]'})
export class SomeDirective {
@Output() someDirective = new EventEmitter();
}
@Component({selector: 'my-component', template: '<div (someDirective)="noop()"></div>'})
export class MyComponent {
noop() {}
}
@NgModule({declarations: [SomeDirective, MyComponent]})
export class MyModule{}
`
@ -387,7 +387,7 @@ describe('compiler compliance: directives', () => {
// MyComponent definition should be:
const MyComponentDefinition = `
const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
@ -395,7 +395,7 @@ describe('compiler compliance: directives', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $c0_a0$);
$r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_0_listener($event) { return ctx.noop(); });
$r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_listener($event) { return ctx.noop(); });
$r3$.ɵelementEnd();
}
},

View File

@ -6,7 +6,6 @@
* 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 {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../compiler/src/compiler';
@ -394,7 +393,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
/**
* @desc d
* @meaning m
@ -403,7 +402,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -423,7 +422,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
@ -523,7 +522,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
/**
* @desc d
* @meaning m
@ -532,7 +531,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -552,7 +551,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
@ -923,7 +922,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
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",
"startTagDiv": "\uFFFD#3\uFFFD",
@ -931,7 +930,7 @@ describe('i18n support in the view compiler', () => {
"closeTagDiv": "\uFFFD/#3\uFFFD"
});
function MyComponent_div_2_Template(rf, ctx) {
function MyComponent_div_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -956,7 +955,7 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵtext(1, " Some content ");
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 4, "div", $_c0$);
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 5, 4, "div", $_c0$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -977,8 +976,8 @@ describe('i18n support in the view compiler', () => {
const output = String.raw `
const $_c0$ = ["src", "logo.png"];
const $_c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_img_1_Template(rf, ctx) {
const $_c1$ = [1, "ngIf"];
function MyComponent_img_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "img", $_c0$);
}
@ -987,7 +986,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c2$ = ["title", $MSG_EXTERNAL_2367729185105559721$];
function MyComponent_img_2_Template(rf, ctx) {
function MyComponent_img_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "img", $_c0$);
$r3$.ɵi18nAttributes(1, $_c2$);
@ -1005,8 +1004,8 @@ describe('i18n support in the view compiler', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "img", $_c0$);
$r3$.ɵtemplate(1, MyComponent_img_1_Template, 1, 0, "img", $_c1$);
$r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c1$);
$r3$.ɵtemplate(1, MyComponent_img_Template_1, 1, 0, "img", $_c1$);
$r3$.ɵtemplate(2, MyComponent_img_Template_2, 2, 1, "img", $_c1$);
}
if (rf & 2) {
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
@ -1044,8 +1043,8 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_2_div_4_Template(rf, ctx) {
const $_c0$ = [1, "ngIf"];
function MyComponent_div_div_Template_4(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2);
$r3$.ɵelementStart(1, "div");
@ -1060,13 +1059,13 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵi18nApply(0);
}
}
function MyComponent_div_2_Template(rf, ctx) {
function MyComponent_div_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 1);
$r3$.ɵelementStart(1, "div");
$r3$.ɵelementStart(2, "div");
$r3$.ɵpipe(3, "uppercase");
$r3$.ɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", $_c1$);
$r3$.ɵtemplate(4, MyComponent_div_div_Template_4, 3, 2, "div", $_c1$);
$r3$.ɵelementEnd();
$r3$.ɵelementEnd();
$r3$.ɵi18nEnd();
@ -1093,7 +1092,7 @@ describe('i18n support in the view compiler', () => {
"interpolation_5": "\uFFFD1:3\uFFFD"
});
const $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$);
function MyComponent_div_3_Template(rf, ctx) {
function MyComponent_div_Template_3(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 3);
$r3$.ɵelementStart(1, "div");
@ -1116,8 +1115,8 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$);
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", $_c1$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", $_c1$);
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 5, 5, "div", $_c1$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 4, "div", $_c1$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1137,14 +1136,14 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", {
"startTagSpan": "\uFFFD#2\uFFFD",
"interpolation": "\uFFFD0\uFFFD",
"closeTagSpan": "\uFFFD/#2\uFFFD"
});
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$);
@ -1163,7 +1162,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 1, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 3, 1, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
@ -1234,7 +1233,7 @@ describe('i18n support in the view compiler', () => {
const output = String.raw `
const $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #2");
const $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$ = goog.getMsg("My i18n block #1");
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$);
}
@ -1242,7 +1241,7 @@ describe('i18n support in the view compiler', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template");
$r3$.ɵelementContainerStart(1);
$r3$.ɵi18n(2, $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$);
$r3$.ɵelementContainerEnd();
@ -1260,21 +1259,23 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [${AttributeMarker.Classes}, "myClass"];
const $_c0$ = ["myClass", 1, "myClass", true];
const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$ = goog.getMsg("Text #1");
const $_c1$ = [${AttributeMarker.Styles}, "padding", "10px"];
const $_c1$ = ["padding", 1, "padding", "10px"];
const $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$ = goog.getMsg("Text #2");
consts: 4,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "span", $_c0$);
$r3$.ɵelementStart(0, "span");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$);
$r3$.ɵelementStyling($_c0$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
$r3$.ɵelementStart(2, "span", $_c1$);
$r3$.ɵelementStart(2, "span");
$r3$.ɵi18nStart(3, $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$);
$r3$.ɵelementStyling(null, $_c1$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1324,7 +1325,7 @@ describe('i18n support in the view compiler', () => {
const $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$ = goog.getMsg("Some content: {$interpolation}", {
"interpolation": "\uFFFD0\uFFFD"
});
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$);
$r3$.ɵpipe(1, "uppercase");
@ -1339,7 +1340,7 @@ describe('i18n support in the view compiler', () => {
vars: 0,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 2, 3, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 2, 3, "ng-template");
}
}
`;
@ -1364,7 +1365,7 @@ describe('i18n support in the view compiler', () => {
"closeTagNgContainer": "\uFFFD/#3\uFFFD",
"interpolation": "\uFFFD0:1\uFFFD"
});
function MyComponent_ng_template_2_Template(rf, ctx) {
function MyComponent_ng_template_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$, 1);
$r3$.ɵpipe(1, "uppercase");
@ -1382,7 +1383,7 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$);
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 2, 3, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 2, 3, "ng-template");
$r3$.ɵelementContainerStart(3);
$r3$.ɵpipe(4, "uppercase");
$r3$.ɵelementContainerEnd();
@ -1414,7 +1415,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$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$);
}
@ -1429,7 +1430,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 1, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 1, "ng-template");
$r3$.ɵelementContainerStart(1);
$r3$.ɵi18n(2, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$);
$r3$.ɵelementContainerEnd();
@ -1460,7 +1461,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) {
function MyComponent_ng_template_ng_template_ng_template_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 3);
}
@ -1470,10 +1471,10 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵi18nApply(0);
}
}
function MyComponent_ng_template_2_ng_template_2_Template(rf, ctx) {
function MyComponent_ng_template_ng_template_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 2);
$r3$.ɵtemplate(1, MyComponent_ng_template_2_ng_template_2_ng_template_1_Template, 1, 1, "ng-template");
$r3$.ɵtemplate(1, MyComponent_ng_template_ng_template_ng_template_Template_1, 1, 1, "ng-template");
$r3$.ɵi18nEnd();
}
if (rf & 2) {
@ -1490,11 +1491,11 @@ describe('i18n support in the view compiler', () => {
"interpolation_2": "\uFFFD0:3\uFFFD"
});
const $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$);
function MyComponent_ng_template_2_Template(rf, ctx) {
function MyComponent_ng_template_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 1);
$r3$.ɵpipe(1, "uppercase");
$r3$.ɵtemplate(2, MyComponent_ng_template_2_ng_template_2_Template, 2, 1, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_ng_template_Template_2, 2, 1, "ng-template");
$r3$.ɵi18nEnd();
}
if (rf & 2) {
@ -1510,7 +1511,7 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$);
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 3, 3, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 3, 3, "ng-template");
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1535,7 +1536,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
function MyComponent_ng_template_2_Template(rf, ctx) {
function MyComponent_ng_template_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$);
}
@ -1553,7 +1554,7 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵelementContainerStart(0);
$r3$.ɵi18n(1, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$);
$r3$.ɵelementContainerEnd();
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 1, 1, "ng-template");
}
if (rf & 2) {
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
@ -1583,7 +1584,7 @@ describe('i18n support in the view compiler', () => {
const $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$ = goog.getMsg("{$tagImg} is my logo #2 ", {
"tagImg": "\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"
});
function MyComponent_ng_template_3_Template(rf, ctx) {
function MyComponent_ng_template_Template_3(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$);
$r3$.ɵelement(1, "img", $_c0$);
@ -1598,7 +1599,7 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵelement(2, "img", $_c0$);
$r3$.ɵi18nEnd();
$r3$.ɵelementContainerEnd();
$r3$.ɵtemplate(3, MyComponent_ng_template_3_Template, 2, 0, "ng-template");
$r3$.ɵtemplate(3, MyComponent_ng_template_Template_3, 2, 0, "ng-template");
}
}
`;
@ -1700,13 +1701,13 @@ describe('i18n support in the view compiler', () => {
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
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 $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
function MyComponent_div_2_Template(rf, ctx) {
function MyComponent_div_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c1$);
$r3$.ɵi18n(1, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$);
@ -1725,7 +1726,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
function MyComponent_div_3_Template(rf, ctx) {
function MyComponent_div_Template_3(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c2$);
$r3$.ɵtext(1, " You have ");
@ -1748,8 +1749,8 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18n(1, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$);
$r3$.ɵelementEnd();
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 2, 1, "div", $_c0$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c0$);
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 2, 1, "div", $_c0$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 2, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
@ -1941,7 +1942,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_APP_SPEC_TS_2$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_2$, {
"VAR_SELECT": "\uFFFD1\uFFFD"
});
const $_c3$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c3$ = [1, "ngIf"];
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$, {
"VAR_SELECT": "\uFFFD0:1\uFFFD"
@ -1955,7 +1956,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_0$, {
"ICU": [$I18N_APP_SPEC_TS_1$, $I18N_APP_SPEC_TS_2$, $I18N_APP_SPEC_TS__4$]
});
function MyComponent_div_3_Template(rf, ctx) {
function MyComponent_div_Template_3(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "div");
@ -1975,7 +1976,7 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $I18N_APP_SPEC_TS_0$);
$r3$.ɵelement(2, "div");
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", $_c3$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 2, 1, "div", $_c3$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -2049,7 +2050,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$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
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$, {
"VAR_SELECT": "\uFFFD0:1\uFFFD"
@ -2060,7 +2061,7 @@ describe('i18n support in the view compiler', () => {
"icu": $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$,
"icu_1": $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$
});
function MyComponent_span_2_Template(rf, ctx) {
function MyComponent_span_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "span");
@ -2079,7 +2080,7 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$);
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", $_c2$);
$r3$.ɵtemplate(2, MyComponent_span_Template_2, 2, 1, "span", $_c2$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -2112,7 +2113,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$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c0$ = [1, "ngIf"];
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"
});
@ -2125,7 +2126,7 @@ describe('i18n support in the view compiler', () => {
"icu": $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$,
"icu_1": $I18N_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$
});
function MyComponent_span_2_Template(rf, ctx) {
function MyComponent_span_Template_2(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "span");
@ -2145,7 +2146,7 @@ describe('i18n support in the view compiler', () => {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$);
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 2, 2, "span", $_c2$);
$r3$.ɵtemplate(2, MyComponent_span_Template_2, 2, 2, "span", $_c2$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}

View File

@ -46,7 +46,7 @@ describe('compiler compliance: listen()', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵlistener("click", function MyComponent_Template_div_click_0_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_div_click_listener($event) {
ctx.onClick($event);
return (1 == 2);
});
@ -92,7 +92,7 @@ describe('compiler compliance: listen()', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "my-app", $e0_attrs$);
$r3$.ɵlistener("click", function MyComponent_Template_my_app_click_0_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_my_app_click_listener($event) {
return ctx.onClick($event);
});
$r3$.ɵelementEnd();
@ -136,19 +136,19 @@ describe('compiler compliance: listen()', () => {
const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $e_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
const $s$ = $r3$.ɵgetCurrentView();
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div", $e_attrs$);
$r3$.ɵlistener("click", function MyComponent_div_0_Template_div_click_1_listener($event) {
$r3$.ɵlistener("click", function MyComponent_div_Template_0_div_click_listener($event) {
$r3$.ɵrestoreView($s$);
const $comp$ = $r3$.ɵnextContext();
return $comp$.onClick($comp$.foo);
});
$r3$.ɵelementEnd();
$r3$.ɵelementStart(2, "button", $e_attrs$);
$r3$.ɵlistener("click", function MyComponent_div_0_Template_button_click_2_listener($event) {
$r3$.ɵlistener("click", function MyComponent_div_Template_0_button_click_listener($event) {
$r3$.ɵrestoreView($s$);
const $comp2$ = $r3$.ɵnextContext();
return $comp2$.onClick2($comp2$.bar);
@ -160,7 +160,7 @@ describe('compiler compliance: listen()', () => {
// ...
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 0, "div", $c0$);
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 3, 0, "div", $c0$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngIf", $i0$.ɵbind(ctx.showing));
@ -208,7 +208,7 @@ describe('compiler compliance: listen()', () => {
if (rf & 1) {
const $s$ = $r3$.ɵgetCurrentView();
$r3$.ɵelementStart(0, "button", $e0_attrs$);
$r3$.ɵlistener("click", function MyComponent_Template_button_click_0_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_button_click_listener($event) {
$r3$.ɵrestoreView($s$);
const $user$ = $r3$.ɵreference(3);
return ctx.onClick($user$.value);

View File

@ -120,70 +120,4 @@ describe('r3_view_compiler', () => {
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');
});
});
});

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker, ViewEncapsulation} from '@angular/compiler/src/core';
import {AttributeMarker, InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@ -214,6 +214,7 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = ["@foo", ""];
const $e1_attrs$ = ["@bar", ""];
const $e2_attrs$ = ["@baz", ""];
@ -223,7 +224,7 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelement(0, "div");
$r3$.ɵelement(0, "div", $e0_attrs$);
$r3$.ɵelement(1, "div", $e1_attrs$);
$r3$.ɵelement(2, "div", $e2_attrs$);
}
@ -281,8 +282,8 @@ describe('compiler compliance: styling', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", _c0);
$r3$.ɵlistener("@myAnimation.start", function MyComponent_Template_div_animation_myAnimation_start_0_listener($event) { return ctx.onStart($event); });
$r3$.ɵlistener("@myAnimation.done", function MyComponent_Template_div_animation_myAnimation_done_0_listener($event) { return ctx.onDone($event); });
$r3$.ɵlistener("@myAnimation.start", function MyComponent_Template_div__myAnimation_start_listener($event) { return ctx.onStart($event); });
$r3$.ɵlistener("@myAnimation.done", function MyComponent_Template_div__myAnimation_done_listener($event) { return ctx.onDone($event); });
$r3$.ɵelementEnd();
} if (rf & 2) {
$r3$.ɵelementProperty(0, "@myAnimation", $r3$.ɵbind(ctx.exp));
@ -296,64 +297,6 @@ describe('compiler compliance: styling', () => {
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should generate animation host binding and listener code for directives', () => {
const files = {
app: {
'spec.ts': `
import {Directive, Component, NgModule} from '@angular/core';
@Directive({
selector: '[my-anim-dir]',
animations: [
{name: 'myAnim'}
],
host: {
'[@myAnim]': 'myAnimState',
'(@myAnim.start)': 'onStart()',
'(@myAnim.done)': 'onDone()'
}
})
class MyAnimDir {
onStart() {}
onDone() {}
myAnimState = '123';
}
@Component({
selector: 'my-cmp',
template: \`
<div my-anim-dir></div>
\`
})
class MyComponent {
}
@NgModule({declarations: [MyComponent, MyAnimDir]})
export class MyModule {}
`
}
};
const template = `
MyAnimDir.ngDirectiveDef = $r3$.ɵdefineDirective({
hostBindings: function MyAnimDir_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵallocHostVars(1);
$r3$.ɵlistener("@myAnim.start", function MyAnimDir_animation_myAnim_start_HostBindingHandler($event) { return ctx.onStart(); });
$r3$.ɵlistener("@myAnim.done", function MyAnimDir_animation_myAnim_done_HostBindingHandler($event) { return ctx.onDone(); });
} if (rf & 2) {
$r3$.ɵcomponentHostSyntheticProperty(elIndex, "@myAnim", $r3$.ɵbind(ctx.myAnimState), null, true);
}
}
});
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
});
describe('[style] and [style.prop]', () => {
@ -423,8 +366,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $_c0$ = [${AttributeMarker.Styles}, "opacity", "1", ${AttributeMarker.SelectOnly}, "style"];
const $_c1$ = ["width", "height"];
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@ -436,14 +379,14 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c0$);
$r3$.ɵelementStyling(null, $_c1$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementEnd();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
$r3$.ɵelementStyleProp(0, 0, $ctx$.myWidth);
$r3$.ɵelementStyleProp(0, 1, $ctx$.myHeight);
$r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
$r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
$r3$.ɵelementStylingApply(0);
$r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
}
@ -478,7 +421,7 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $_c0$ = ["background-image"];
const _c0 = ["background-image"];
export class MyComponent {
constructor() {
this.myImage = 'url(foo.jpg)';
@ -513,6 +456,7 @@ describe('compiler compliance: styling', () => {
});
it('should support [style.foo.suffix] style bindings with a suffix', () => {
const files = {
app: {
'spec.ts': `
@ -532,7 +476,7 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_styles$ = ["font-size"];
const $e0_styles$= ["font-size"];
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
@ -620,8 +564,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = [${AttributeMarker.Classes}, "grape", ${AttributeMarker.SelectOnly}, "class"];
const $e0_bindings$ = ["apple", "orange"];
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@ -634,13 +578,13 @@ describe('compiler compliance: styling', () => {
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵelementStyling($e0_bindings$);
$r3$.ɵelementStyling($e0_cd$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
$r3$.ɵelementClassProp(0, 0, $ctx$.yesToApple);
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToOrange);
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
$r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
$r3$.ɵelementStylingApply(0);
$r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
}
@ -662,7 +606,7 @@ describe('compiler compliance: styling', () => {
@Component({
selector: 'my-component',
template: \`<div class=" foo "
template: \`<div class="foo"
style="width:100px"
[attr.class]="'round'"
[attr.style]="'height:100px'"></div>\`
@ -676,7 +620,9 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", ${AttributeMarker.Styles}, "width", "100px", ${AttributeMarker.SelectOnly}, "class", "style"];
const $e0_attrs$ = [${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({
type: MyComponent,
@ -689,6 +635,7 @@ describe('compiler compliance: styling', () => {
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵelementStyling($e0_cd$, $e0_sd$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -818,13 +765,10 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_classBindings$ = ["foo"];
const $e0_styleBindings$ = ["bar", "baz"];
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementStyling($e0_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵpipe(1, "pipe");
$r3$.ɵpipe(2, "pipe");
$r3$.ɵpipe(3, "pipe");
@ -884,18 +828,16 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
const $e0_classBindings$ = ["foo"];
const $e0_styleBindings$ = ["color"];
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementHostAttrs(ctx, $e0_attrs$);
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myColorProp, null, ctx);
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
}
@ -1017,10 +959,10 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $widthDir_classes$ = ["foo"];
const $widthDir_styles$ = ["width"];
const $heightDir_classes$ = ["bar"];
const $heightDir_styles$ = ["height"];
const _c0 = ["foo"];
const _c1 = ["width"];
const _c2 = ["bar"];
const _c3 = ["height"];
function ClassDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
@ -1034,7 +976,7 @@ describe('compiler compliance: styling', () => {
function WidthDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling($widthDir_classes$, $widthDir_styles$, null, ctx);
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
@ -1045,7 +987,7 @@ describe('compiler compliance: styling', () => {
function HeightDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling($heightDir_classes$, $heightDir_styles$, null, ctx);
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
@ -1072,8 +1014,7 @@ describe('compiler compliance: styling', () => {
template: '',
host: {
'style': 'width:200px; height:500px',
'class': 'foo baz',
'title': 'foo title'
'class': 'foo baz'
}
})
export class MyComponent {
@ -1088,9 +1029,6 @@ describe('compiler compliance: styling', () => {
@HostBinding('title')
title = 'some title';
@Input('name')
name = '';
}
@NgModule({declarations: [MyComponent]})
@ -1100,13 +1038,13 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $_c0$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
const $_c0$ = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
const $_c1$ = ["width", "height", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵallocHostVars(2);
$r3$.ɵelementHostAttrs(ctx, $_c0$);
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementStyling($_c0$, $_c1$, $r3$.ɵdefaultStyleSanitizer, ctx);
}
if (rf & 2) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);

View File

@ -52,12 +52,12 @@ describe('compiler compliance: template', () => {
const template = `
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "title", "click"];
function MyComponent_ul_0_li_1_div_1_Template(rf, ctx) {
function MyComponent_ul_li_div_Template_1(rf, ctx) {
if (rf & 1) {
const $s$ = $i0$.ɵgetCurrentView();
$i0$.ɵelementStart(0, "div", $e0_attrs$);
$i0$.ɵlistener("click", function MyComponent_ul_0_li_1_div_1_Template_div_click_0_listener($event){
$i0$.ɵlistener("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
$i0$.ɵrestoreView($s$);
const $inner$ = ctx.$implicit;
const $middle$ = $i0$.ɵnextContext().$implicit;
@ -79,10 +79,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_ul_0_li_1_Template(rf, ctx) {
function MyComponent_ul_li_Template_1(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "li");
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_div_1_Template, 2, 2, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_ul_li_div_Template_1, 2, 2, "div", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -91,10 +91,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_ul_0_Template(rf, ctx) {
function MyComponent_ul_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "ul");
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_Template, 2, 1, "li", _c0);
$i0$.ɵtemplate(1, MyComponent_ul_li_Template_1, 2, 1, "li", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -105,7 +105,7 @@ describe('compiler compliance: template', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_ul_0_Template, 2, 1, "ul", _c0);
$i0$.ɵtemplate(0, MyComponent_ul_Template_0, 2, 1, "ul", _c0);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -141,7 +141,7 @@ describe('compiler compliance: template', () => {
const template = `
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
function MyComponent_span_0_Template(rf, ctx) {
function MyComponent_span_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "span");
$i0$.ɵtext(1);
@ -156,7 +156,7 @@ describe('compiler compliance: template', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_span_0_Template, 2, 2, "span", _c0);
$i0$.ɵtemplate(0, MyComponent_span_Template_0, 2, 2, "span", _c0);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -195,7 +195,7 @@ describe('compiler compliance: template', () => {
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
const $c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_0_span_1_Template(rf, ctx) {
function MyComponent_div_span_Template_1(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "span");
$i0$.ɵtext(1);
@ -209,10 +209,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_0_span_1_Template, 2, 2, "span", $c1$);
$i0$.ɵtemplate(1, MyComponent_div_span_Template_1, 2, 2, "span", $c1$);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -224,7 +224,7 @@ describe('compiler compliance: template', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", $c0$);
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 2, 1, "div", $c0$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -264,7 +264,7 @@ describe('compiler compliance: template', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
function MyComponent_div_0_div_1_div_1_Template(rf, ctx) {
function MyComponent_div_div_div_Template_1(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtext(1);
@ -277,10 +277,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_0_div_1_Template(rf, ctx) {
function MyComponent_div_div_Template_1(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_div_1_Template, 2, 2, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_div_div_div_Template_1, 2, 2, "div", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -289,10 +289,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_0_Template(rf, ctx) {
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_Template, 2, 1, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_div_div_Template_1, 2, 1, "div", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -303,7 +303,7 @@ describe('compiler compliance: template', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", _c0);
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 2, 1, "div", _c0);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -339,7 +339,7 @@ describe('compiler compliance: template', () => {
const template = `
const $c0$ = ["attr", "l", ${AttributeMarker.SelectOnly}, "boundAttr"];
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵtext(0, " some-content ");
}
@ -349,7 +349,7 @@ describe('compiler compliance: template', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", $c0$);
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", $c0$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "boundAttr", $i0$.ɵbind(ctx.b));
@ -370,7 +370,7 @@ describe('compiler compliance: template', () => {
@Component({
selector: 'my-component',
template: '<ng-template #foo>some-content</ng-template>',
template: '<ng-template #foo>some-content</ng-template>';
})
export class MyComponent {}
@ -383,7 +383,7 @@ describe('compiler compliance: template', () => {
const template = `
const $t0_refs$ = ["foo", ""];
function MyComponent_ng_template_0_Template(rf, ctx) {
function MyComponent_ng_template_Template_0(rf, ctx) {
if (rf & 1) {
$i0$.ɵtext(0, "some-content");
}
@ -393,7 +393,7 @@ describe('compiler compliance: template', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
}
}`;
@ -411,7 +411,7 @@ describe('compiler compliance: template', () => {
@Component({
selector: 'my-component',
template: '<ng-template (outDirective)="$event.doSth()"></ng-template>',
template: '<ng-template (outDirective)="$event.doSth()"></ng-template>';
})
export class MyComponent {}
@ -424,14 +424,14 @@ describe('compiler compliance: template', () => {
const template = `
const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "outDirective"];
function MyComponent_ng_template_0_Template(rf, ctx) { }
function MyComponent_ng_template_Template_0(rf, ctx) { }
// ...
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", $t0_attrs$);
$i0$.ɵlistener("outDirective", function MyComponent_Template_ng_template_outDirective_0_listener($event) { return $event.doSth(); });
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 0, 0, "ng-template", $t0_attrs$);
$i0$.ɵlistener("outDirective", function MyComponent_Template_ng_template_outDirective_listener($event) { return $event.doSth(); });
}
}`;
@ -440,122 +440,4 @@ describe('compiler compliance: template', () => {
expectEmit(result.source, template, 'Incorrect template');
});
it('should create unique template function names even for similar nested template structures',
() => {
const files = {
app: {
'spec1.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'a-component',
template: \`
<div *ngFor="let item of items">
<p *ngIf="item < 10">less than 10</p>
<p *ngIf="item < 10">less than 10</p>
</div>
<div *ngFor="let item of items">
<p *ngIf="item > 10">more than 10</p>
</div>
\`,
})
export class AComponent {
items = [4, 2];
}
@NgModule({declarations: [AComponent]})
export class AModule {}
`,
'spec2.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'b-component',
template: \`
<div *ngFor="let item of items">
<ng-container *ngFor="let subitem of item.subitems">
<p *ngIf="subitem < 10">less than 10</p>
<p *ngIf="subitem < 10">less than 10</p>
</ng-container>
<ng-container *ngFor="let subitem of item.subitems">
<p *ngIf="subitem < 10">less than 10</p>
</ng-container>
</div>
<div *ngFor="let item of items">
<ng-container *ngFor="let subitem of item.subitems">
<p *ngIf="subitem > 10">more than 10</p>
</ng-container>
</div>
\`,
})
export class BComponent {
items = [
{subitems: [1, 3]},
{subitems: [3, 7]},
];
}
@NgModule({declarations: [BComponent]})
export class BModule {}
`,
},
};
const result = compile(files, angularFiles);
const allTemplateFunctionsNames = (result.source.match(/function ([^\s(]+)/g) || [])
.map(x => x.slice(9))
.filter(x => x.includes('Template'))
.sort();
const uniqueTemplateFunctionNames = Array.from(new Set(allTemplateFunctionsNames));
// Expected template function:
// - 5 for AComponent's template.
// - 9 for BComponent's template.
// - 2 for the two components.
expect(allTemplateFunctionsNames.length).toBe(5 + 9 + 2);
expect(allTemplateFunctionsNames).toEqual(uniqueTemplateFunctionNames);
});
it('should create unique listener function names even for similar nested template structures',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
<div *ngFor="let item of items">
<p (click)="$event">{{ item }}</p>
<p (click)="$event">{{ item }}</p>
</div>
<div *ngFor="let item of items">
<p (click)="$event">{{ item }}</p>
</div>
\`,
})
export class MyComponent {
items = [4, 2];
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`,
},
};
const result = compile(files, angularFiles);
const allListenerFunctionsNames = (result.source.match(/function ([^\s(]+)/g) || [])
.map(x => x.slice(9))
.filter(x => x.includes('listener'))
.sort();
const uniqueListenerFunctionNames = Array.from(new Set(allListenerFunctionsNames));
expect(allListenerFunctionsNames.length).toBe(3);
expect(allListenerFunctionsNames).toEqual(uniqueListenerFunctionNames);
});
});

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CustomTransformers} from '@angular/compiler-cli';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
@ -112,9 +111,9 @@ export class NgtscTestEnvironment {
/**
* Run the compiler to completion, and assert that no errors occurred.
*/
driveMain(customTransformers?: CustomTransformers): void {
driveMain(): void {
const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error);
const exitCode = main(['-p', this.basePath], errorSpy, undefined, customTransformers);
const exitCode = main(['-p', this.basePath], errorSpy);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
}

View File

@ -441,39 +441,6 @@ 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', () => {
env.tsconfig();
env.write(`test.ts`, `
@ -1227,32 +1194,4 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain('/// <amd-module name="@mymodule" />');
});
});
it('should execute custom transformers', () => {
let beforeCount = 0;
let afterCount = 0;
env.tsconfig();
env.write('test.ts', `
import {NgModule} from '@angular/core';
@NgModule({})
class Module {}
`);
env.driveMain({
beforeTs: [() => sourceFile => {
beforeCount++;
return sourceFile;
}],
afterTs: [() => sourceFile => {
afterCount++;
return sourceFile;
}],
});
expect(beforeCount).toBe(1);
expect(afterCount).toBe(1);
});
});

View File

@ -134,13 +134,10 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
encapsulation: ViewEncapsulation;
viewProviders: Provider[]|null;
interpolation?: [string, string];
changeDetection?: ChangeDetectionStrategy;
}
export type ViewEncapsulation = number;
export type ChangeDetectionStrategy = number;
export interface R3QueryMetadataFacade {
propertyName: string;
first: boolean;

View File

@ -379,9 +379,13 @@ export const enum RenderFlags {
Update = 0b10
}
export const enum InitialStylingFlags {
VALUES_MODE = 0b1,
}
// Pasted from render3/interfaces/node.ts
/**
* A set of marker values to be used in the attributes arrays. These markers indicate that some
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
* items are not regular attributes and the processing should be adapted accordingly.
*/
export const enum AttributeMarker {
@ -392,48 +396,11 @@ export const enum AttributeMarker {
*/
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.:
* [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:
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
*/
SelectOnly = 3,
}
SelectOnly = 1
}

View File

@ -130,7 +130,6 @@ export class CompilerFacadeImpl implements CompilerFacade {
styles: facade.styles || [],
encapsulation: facade.encapsulation as any,
interpolation: interpolationConfig,
changeDetection: facade.changeDetection,
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
null,
@ -250,7 +249,11 @@ function extractHostBindings(host: {[key: string]: string}, propMetadata: {[key:
properties: StringMap,
} {
// First parse the declarations from the metadata.
const {attributes, listeners, properties} = parseHostBindings(host || {});
const {attributes, listeners, properties, animations} = parseHostBindings(host || {});
if (Object.keys(animations).length > 0) {
throw new Error(`Animation bindings are as-of-yet unsupported in Ivy`);
}
// Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
for (const field in propMetadata) {

View File

@ -334,7 +334,7 @@ class _Tokenizer {
try {
const charCode = parseInt(strNum, isHex ? 16 : 10);
return String.fromCharCode(charCode);
} catch {
} catch (e) {
const entity = this._input.substring(start.offset + 1, this._index - 1);
throw this._createError(_unknownEntityErrorMsg(entity), this._getSpan(start));
}

View File

@ -31,9 +31,6 @@ export class Identifiers {
static elementProperty: o.ExternalReference = {name: 'ɵelementProperty', moduleName: CORE};
static componentHostSyntheticProperty:
o.ExternalReference = {name: 'ɵcomponentHostSyntheticProperty', moduleName: CORE};
static elementAttribute: o.ExternalReference = {name: 'ɵelementAttribute', moduleName: CORE};
static elementClassProp: o.ExternalReference = {name: 'ɵelementClassProp', moduleName: CORE};
@ -46,8 +43,6 @@ export class Identifiers {
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 elementStyleProp: o.ExternalReference = {name: 'ɵelementStyleProp', moduleName: CORE};

View File

@ -52,31 +52,3 @@ export interface R3Reference {
value: o.Expression;
type: o.Expression;
}
const ANIMATE_SYMBOL_PREFIX = '@';
export function prepareSyntheticPropertyName(name: string) {
return `${ANIMATE_SYMBOL_PREFIX}${name}`;
}
export function prepareSyntheticListenerName(name: string, phase: string) {
return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
}
export function isSyntheticPropertyOrListener(name: string) {
return name.charAt(0) == ANIMATE_SYMBOL_PREFIX;
}
export function getSyntheticPropertyName(name: string) {
// this will strip out listener phase values...
// @foo.start => @foo
const i = name.indexOf('.');
name = i > 0 ? name.substring(0, i) : name;
if (name.charAt(0) !== ANIMATE_SYMBOL_PREFIX) {
name = ANIMATE_SYMBOL_PREFIX + name;
}
return name;
}
export function prepareSyntheticListenerFunctionName(name: string, phase: string) {
return `animation_${name}_${phase}`;
}

View File

@ -6,14 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectionStrategy, ViewEncapsulation} from '../../core';
import {ViewEncapsulation} from '../../core';
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import * as t from '../r3_ast';
import {R3DependencyMetadata} from '../r3_factory';
/**
* Information needed to compile a directive for the render3 runtime.
*/
@ -185,19 +184,14 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
/**
* 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;
/**
* Overrides the default interpolation start and end delimiters ({{ and }}).
* Overrides the default interpolation start and end delimiters ({{ and }})
*/
interpolation: InterpolationConfig;
/**
* Strategy used for detecting changes in the component.
*/
changeDetection?: ChangeDetectionStrategy;
}
/**

View File

@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
import {BindingForm, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {ConstantPool, DefinitionKind} from '../../constant_pool';
import * as core from '../../core';
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
import {AST, ParsedEvent} from '../../expression_parser/ast';
import {LifecycleHooks} from '../../lifecycle_reflector';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
import * as o from '../../output/output_ast';
@ -25,10 +25,10 @@ import {OutputContext, error} from '../../util';
import {compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
import {Identifiers as R3} from '../r3_identifiers';
import {Render3ParseResult} from '../r3_template_transform';
import {prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
import {typeWithParameters} from '../util';
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
import {StylingBuilder, StylingInstruction} from './styling_builder';
import {StylingBuilder, StylingInstruction} from './styling';
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
@ -251,7 +251,6 @@ export function compileComponentFromMetadata(
const directivesUsed = new Set<o.Expression>();
const pipesUsed = new Set<o.Expression>();
const changeDetection = meta.changeDetection;
const template = meta.template;
const templateBuilder = new TemplateDefinitionBuilder(
@ -314,11 +313,6 @@ export function compileComponentFromMetadata(
'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
// string literal, which must be on one line.
const selectorForType = (meta.selector || '').replace(/\n/g, '');
@ -697,7 +691,7 @@ function createHostBindingsFunction(
const value = binding.expression.visit(valueConverter);
const bindingExpr = bindingFn(bindingContext, value);
const {bindingName, instruction, extraParams} = getBindingNameAndInstruction(binding);
const {bindingName, instruction, extraParams} = getBindingNameAndInstruction(name);
const instructionParams: o.Expression[] = [
elVarExp, o.literal(bindingName), o.importExpr(R3.bind).callFn([bindingExpr.currValExpr])
@ -709,35 +703,16 @@ function createHostBindingsFunction(
}
}
if (styleBuilder.hasBindingsOrInitialValues()) {
// since we're dealing with directives here and directives have a hostBinding
// function, we need to generate special instructions that deal with styling
// (both bindings and initial values). The instruction below will instruct
// 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));
if (styleBuilder.hasBindingsOrInitialValues) {
const createInstruction = styleBuilder.buildCreateLevelInstruction(null, constantPool);
if (createInstruction) {
const createStmt = createStylingStmt(createInstruction, bindingContext, bindingFn);
createStatements.push(createStmt);
}
// 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 => {
updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn));
const updateStmt = createStylingStmt(instruction, bindingContext, bindingFn);
updateStatements.push(updateStmt);
});
}
}
@ -775,9 +750,8 @@ function createStylingStmt(
.toStmt();
}
function getBindingNameAndInstruction(binding: ParsedProperty):
function getBindingNameAndInstruction(bindingName: string):
{bindingName: string, instruction: o.ExternalReference, extraParams: o.Expression[]} {
let bindingName = binding.name;
let instruction !: o.ExternalReference;
const extraParams: o.Expression[] = [];
@ -787,15 +761,7 @@ function getBindingNameAndInstruction(binding: ParsedProperty):
bindingName = attrMatches[1];
instruction = R3.elementAttribute;
} else {
if (binding.isAnimation) {
bindingName = prepareSyntheticPropertyName(bindingName);
// host bindings that have a synthetic property (e.g. @foo) should always be rendered
// in the context of the component and not the parent. Therefore there is a special
// compatibility instruction available for this purpose.
instruction = R3.componentHostSyntheticProperty;
} else {
instruction = R3.elementProperty;
}
instruction = R3.elementProperty;
extraParams.push(
o.literal(null), // TODO: This should be a sanitizer fn (FW-785)
o.literal(true) // host bindings must have nativeOnly prop set to true
@ -811,19 +777,14 @@ function createHostListeners(
return eventBindings.map(binding => {
const bindingExpr = convertActionBinding(
null, bindingContext, binding.handler, 'b', () => error('Unexpected interpolation'));
let bindingName = binding.name && sanitizeIdentifier(binding.name);
let bindingFnName = bindingName;
if (binding.type === ParsedEventType.Animation) {
bindingFnName = prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase);
bindingName = prepareSyntheticListenerName(bindingName, binding.targetOrPhase);
}
const bindingName = binding.name && sanitizeIdentifier(binding.name);
const typeName = meta.name;
const functionName =
typeName && bindingName ? `${typeName}_${bindingFnName}_HostBindingHandler` : null;
typeName && bindingName ? `${typeName}_${bindingName}_HostBindingHandler` : null;
const handler = o.fn(
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...bindingExpr.render3Stmts], o.INFERRED_TYPE,
null, functionName);
return o.importExpr(R3.listener).callFn([o.literal(bindingName), handler]).toStmt();
return o.importExpr(R3.listener).callFn([o.literal(binding.name), handler]).toStmt();
});
}
@ -846,24 +807,30 @@ function typeMapToExpressionMap(
return new Map(entries);
}
const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
// Represents the groups in the above regex.
const enum HostBindingGroup {
// group 1: "prop" from "[prop]", or "attr.role" from "[attr.role]", or @anim from [@anim]
// group 1: "prop" from "[prop]", or "attr.role" from "[attr.role]"
Binding = 1,
// group 2: "event" from "(event)"
Event = 2,
// group 3: "@trigger" from "@trigger"
Animation = 3,
}
export function parseHostBindings(host: {[key: string]: string}): {
attributes: {[key: string]: string},
listeners: {[key: string]: string},
properties: {[key: string]: string},
animations: {[key: string]: string},
} {
const attributes: {[key: string]: string} = {};
const listeners: {[key: string]: string} = {};
const properties: {[key: string]: string} = {};
const animations: {[key: string]: string} = {};
Object.keys(host).forEach(key => {
const value = host[key];
@ -871,16 +838,15 @@ export function parseHostBindings(host: {[key: string]: string}): {
if (matches === null) {
attributes[key] = value;
} else if (matches[HostBindingGroup.Binding] != null) {
// synthetic properties (the ones that have a `@` as a prefix)
// are still treated the same as regular properties. Therefore
// there is no point in storing them in a separate map.
properties[matches[HostBindingGroup.Binding]] = value;
} else if (matches[HostBindingGroup.Event] != null) {
listeners[matches[HostBindingGroup.Event]] = value;
} else if (matches[HostBindingGroup.Animation] != null) {
animations[matches[HostBindingGroup.Animation]] = value;
}
});
return {attributes, listeners, properties};
return {attributes, listeners, properties, animations};
}
function compileStyles(styles: string[], selector: string, hostSelector: string): string[] {

View File

@ -23,15 +23,10 @@ const enum Char {
*
* @param value string representation of style as used in the `style` attribute in HTML.
* Example: `color: red; height: auto`.
* @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
* 'auto']`
* @returns an object literal. `{ color: 'red', height: 'auto'}`.
*/
export function parse(value: string): string[] {
// 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[] = [];
export function parse(value: string): {[key: string]: any} {
const styles: {[key: string]: any} = {};
let i = 0;
let parenDepth = 0;
@ -77,7 +72,7 @@ export function parse(value: string): string[] {
case Char.Semicolon:
if (currentProp && valueStart > 0 && parenDepth === 0 && quote === Char.QuoteNone) {
const styleVal = value.substring(valueStart, i - 1).trim();
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
propStart = i;
valueStart = 0;
currentProp = null;
@ -89,7 +84,7 @@ export function parse(value: string): string[] {
if (currentProp && valueStart) {
const styleVal = value.substr(valueStart).trim();
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
}
return styles;

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool} from '../../constant_pool';
import {AttributeMarker} from '../../core';
import {AST, BindingType} from '../../expression_parser/ast';
import {InitialStylingFlags} from '../../core';
import {AST, BindingType, ParseSpan} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import * as t from '../r3_ast';
@ -40,10 +40,6 @@ interface BoundStylingEntry {
/**
* 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:
*
* - Static style/class attributes (style="..." and class="...")
@ -67,57 +63,25 @@ interface BoundStylingEntry {
* The creation/update methods within the builder class produce these instructions.
*/
export class StylingBuilder {
/** 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;
public readonly hasBindingsOrInitialValues = false;
/** the input for [class] (if it exists) */
private _classMapInput: BoundStylingEntry|null = null;
/** the input for [style] (if it exists) */
private _styleMapInput: BoundStylingEntry|null = null;
/** an array of each [style.prop] input */
private _singleStyleInputs: BoundStylingEntry[]|null = null;
/** an array of each [class.name] input */
private _singleClassInputs: BoundStylingEntry[]|null = null;
private _lastStylingInput: BoundStylingEntry|null = null;
// maps are used instead of hash maps because a Map will
// 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>();
/**
* 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 _initialStyleValues: string[] = [];
private _initialClassValues: string[] = [];
// certain style properties ALWAYS need sanitization
// this is checked each time new styles are encountered
private _initialStyleValues: {[propName: string]: string} = {};
private _initialClassValues: {[className: string]: boolean} = {};
private _useDefaultSanitizer = false;
private _applyFnRequired = false;
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 {
// [attr.style] or [attr.class] are skipped in the code below,
// they should not be treated as styling-based bindings since
@ -153,12 +117,14 @@ export class StylingBuilder {
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(propertyName);
registerIntoMap(this._stylesIndex, propertyName);
(this as any).hasBindingsOrInitialValues = true;
} else {
this._useDefaultSanitizer = true;
this._styleMapInput = entry;
}
this._lastStylingInput = entry;
this._hasBindings = true;
(this as any).hasBindingsOrInitialValues = true;
this._applyFnRequired = true;
return entry;
}
@ -167,152 +133,107 @@ export class StylingBuilder {
const entry = { name: className, value, sourceSpan } as BoundStylingEntry;
if (className) {
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
(this as any).hasBindingsOrInitialValues = true;
registerIntoMap(this._classesIndex, className);
} else {
this._classMapInput = entry;
}
this._lastStylingInput = entry;
this._hasBindings = true;
(this as any).hasBindingsOrInitialValues = true;
this._applyFnRequired = true;
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) {
this._initialStyleValues = parseStyle(value);
this._hasInitialValues = true;
Object.keys(this._initialStyleValues).forEach(prop => {
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) {
this._initialClassValues = value.trim().split(/\s+/g);
this._hasInitialValues = true;
this._initialClassValues = {};
value.split(/\s+/g).forEach(className => {
this._initialClassValues[className] = true;
registerIntoMap(this._classesIndex, className);
(this as any).hasBindingsOrInitialValues = true;
});
}
/**
* Appends all styling-related expressions to the provided attrs array.
*
* @param attrs an existing array where each of the styling expressions
* will be inserted into.
*/
populateInitialStylingAttrs(attrs: o.Expression[]): void {
// [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
if (this._initialClassValues.length) {
attrs.push(o.literal(AttributeMarker.Classes));
for (let i = 0; i < this._initialClassValues.length; i++) {
attrs.push(o.literal(this._initialClassValues[i]));
private _buildInitExpr(registry: Map<string, number>, initialValues: {[key: string]: any}):
o.Expression|null {
const exprs: o.Expression[] = [];
const nameAndValueExprs: o.Expression[] = [];
// _c0 = [prop, prop2, prop3, ...]
registry.forEach((value, key) => {
const keyLiteral = o.literal(key);
exprs.push(keyLiteral);
const initialValue = initialValues[key];
if (initialValue) {
nameAndValueExprs.push(keyLiteral, o.literal(initialValue));
}
});
if (nameAndValueExprs.length) {
// _c0 = [... MARKER ...]
exprs.push(o.literal(InitialStylingFlags.VALUES_MODE));
// _c0 = [prop, VALUE, prop2, VALUE2, ...]
exprs.push(...nameAndValueExprs);
}
// [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]));
}
}
return exprs.length ? o.literalArr(exprs) : null;
}
/**
* 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):
buildCreateLevelInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
StylingInstruction|null {
if (this._hasInitialValues && this._directiveExpr) {
return {
sourceSpan,
reference: R3.elementHostAttrs,
buildParams: () => {
const attrs: o.Expression[] = [];
this.populateInitialStylingAttrs(attrs);
return [this._directiveExpr !, getConstantLiteralFromArray(constantPool, attrs)];
if (this.hasBindingsOrInitialValues) {
const initialClasses = this._buildInitExpr(this._classesIndex, this._initialClassValues);
const initialStyles = this._buildInitExpr(this._stylesIndex, this._initialStyleValues);
// in the event that a [style] binding is used then sanitization will
// always be imported because it is not possible to know ahead of time
// whether style bindings will use or not use any sanitizable properties
// that isStyleSanitizable() will detect
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;
}
/**
* 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 {
private _buildStylingMap(valueConverter: ValueConverter): StylingInstruction|null {
if (this._classMapInput || this._styleMapInput) {
const stylingInput = this._classMapInput ! || this._styleMapInput !;
@ -411,20 +332,18 @@ 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) {
const instructions: StylingInstruction[] = [];
if (this._hasBindings) {
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
if (this.hasBindingsOrInitialValues) {
const mapInstruction = this._buildStylingMap(valueConverter);
if (mapInstruction) {
instructions.push(mapInstruction);
}
instructions.push(...this._buildStyleInputs(valueConverter));
instructions.push(...this._buildClassInputs(valueConverter));
instructions.push(this._buildApplyFn());
if (this._applyFnRequired) {
instructions.push(this._buildApplyFn());
}
}
return instructions;
}
@ -444,26 +363,3 @@ function isStyleSanitizable(prop: string): boolean {
return prop === 'background-image' || prop === 'background' || prop === 'border-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);
}
}

View File

@ -10,7 +10,7 @@ import {flatten, sanitizeIdentifier} from '../../compile_metadata';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {ConstantPool} from '../../constant_pool';
import * as core from '../../core';
import {AST, ASTWithSource, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEvent, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
import {Lexer} from '../../expression_parser/lexer';
import {Parser} from '../../expression_parser/parser';
import * as i18n from '../../i18n/i18n_ast';
@ -29,14 +29,13 @@ import {error} from '../../util';
import * as t from '../r3_ast';
import {Identifiers as R3} from '../r3_identifiers';
import {htmlAstToRender3Ast} from '../r3_template_transform';
import {getSyntheticPropertyName, prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName} from '../util';
import {R3QueryMetadata} from './api';
import {I18nContext} from './i18n/context';
import {I18nMetaVisitor} from './i18n/meta';
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 {StylingBuilder, StylingInstruction} from './styling_builder';
import {StylingBuilder, StylingInstruction} from './styling';
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 {
@ -115,10 +114,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// Selectors found in the <ng-content> tags in the template.
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(
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
private contextName: string|null, private i18nContext: I18nContext|null,
@ -171,11 +166,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
});
}
buildTemplateFunction(
nodes: t.Node[], variables: t.Variable[], ngContentSelectorsOffset: number = 0,
i18n?: i18n.AST): o.FunctionExpr {
this._ngContentSelectorsOffset = ngContentSelectorsOffset;
buildTemplateFunction(nodes: t.Node[], variables: t.Variable[], i18n?: i18n.AST): o.FunctionExpr {
if (this._namespace !== R3.namespaceHTML) {
this.creationInstruction(null, this._namespace);
}
@ -201,23 +192,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
t.visitAll(this, nodes);
// 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());
// 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) {
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
if (this._hasNgContent) {
const parameters: o.Expression[] = [];
// Only selectors with a non-default value are generated
@ -236,6 +212,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
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) {
this.i18nEnd(null, selfClosingI18nInstruction);
}
@ -430,7 +419,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const slot = this.allocateDataSlot();
let selectorIndex = ngContent.selector === DEFAULT_NG_CONTENT_SELECTOR ?
0 :
this._ngContentSelectors.push(ngContent.selector) + this._ngContentSelectorsOffset;
this._ngContentSelectors.push(ngContent.selector);
const parameters: o.Expression[] = [o.literal(slot)];
const attributeAsList: string[] = [];
@ -491,9 +480,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const {name, value} = attr;
if (name === NON_BINDABLE_ATTR) {
isNonBindableMode = true;
} else if (name === 'style') {
} else if (name == 'style') {
stylingBuilder.registerStyleAttr(value);
} else if (name === 'class') {
} else if (name == 'class') {
stylingBuilder.registerClassAttr(value);
} else if (attr.i18n) {
i18nAttrs.push(attr);
@ -517,7 +506,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
element.inputs.forEach((input: t.BoundAttribute) => {
if (!stylingBuilder.registerBoundInput(input)) {
if (input.type === BindingType.Property) {
if (input.type == BindingType.Property) {
if (input.i18n) {
i18nAttrs.push(input);
} else {
@ -533,8 +522,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// this will build the instructions so that they fall into the following syntax
// add attributes for directive matching purposes
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(
allOtherInputs, element.outputs, stylingBuilder));
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(allOtherInputs, element.outputs));
parameters.push(this.toAttrsParam(attributes));
// local refs (ex.: <div #foo #bar="baz">)
@ -564,11 +552,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
return element.children.length > 0;
};
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues() &&
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues &&
!isNgContainer && element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren();
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
!stylingBuilder.hasBindingsOrInitialValues() && hasTextChildrenOnly(element.children);
!stylingBuilder.hasBindingsOrInitialValues && hasTextChildrenOnly(element.children);
if (createSelfClosingInstruction) {
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
@ -618,29 +606,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
}
// 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
// initial styling for static style="..." and class="..." attributes
this.processStylingInstruction(
implicit,
stylingBuilder.buildElementStylingInstruction(element.sourceSpan, this.constantPool),
true);
stylingBuilder.buildCreateLevelInstruction(element.sourceSpan, this.constantPool), true);
// Generate Listeners (outputs)
element.outputs.forEach((outputAst: t.BoundEvent) => {
this.creationInstruction(
outputAst.sourceSpan, R3.listener,
this.prepareListenerParameter(element.name, outputAst, elementIndex));
this.prepareListenerParameter(element.name, outputAst));
});
}
// 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 => {
this.processStylingInstruction(implicit, instruction, false);
});
@ -652,12 +630,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const value = input.value.visit(this._valueConverter);
// setProperty without a value doesn't make any sense
if (value.name || value.value) {
const bindingName = prepareSyntheticPropertyName(input.name);
this.allocateBindingSlots(value);
const name = prepareSyntheticAttributeName(input.name);
this.updateInstruction(input.sourceSpan, R3.elementProperty, () => {
return [
o.literal(elementIndex), o.literal(bindingName),
this.convertPropertyBinding(implicit, value)
o.literal(elementIndex), o.literal(name), this.convertPropertyBinding(implicit, value)
];
});
}
@ -709,8 +686,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
const tagName = sanitizeIdentifier(template.tagName || '');
const contextName = `${tagName ? this.contextName + '_' + tagName : ''}_${templateIndex}`;
const templateName = `${contextName}_Template`;
const contextName = tagName ? `${this.contextName}_${tagName}` : '';
const templateName =
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
const parameters: o.Expression[] = [
o.literal(templateIndex),
@ -734,7 +712,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
parameters.push(o.importExpr(R3.templateRefExtractor));
}
// handle property bindings e.g. p(1, 'ngForOf', ɵbind(ctx.items));
// handle property bindings e.g. p(1, 'forOf', ɵbind(ctx.items));
const context = o.variable(CONTEXT_NAME);
template.inputs.forEach(input => {
const value = input.value.visit(this._valueConverter);
@ -757,16 +735,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// Nested templates must not be visited until after their parent templates have completed
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
// be able to support bindings in nested templates to local refs that occur after the
// 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(() => {
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
template.children, template.variables,
this._ngContentSelectors.length + this._ngContentSelectorsOffset, template.i18n);
template.children, template.variables, template.i18n);
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)
@ -781,7 +754,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
template.outputs.forEach((outputAst: t.BoundEvent) => {
this.creationInstruction(
outputAst.sourceSpan, R3.listener,
this.prepareListenerParameter('ng_template', outputAst, templateIndex));
this.prepareListenerParameter('ng_template', outputAst));
});
}
@ -946,51 +919,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
}
/**
* 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[] {
private prepareSyntheticAndSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]):
o.Expression[] {
const attrExprs: o.Expression[] = [];
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) {
const EMPTY_STRING_EXPR = asLiteral('');
@ -1000,34 +932,17 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// may be supported differently in future versions of angular. However,
// @triggers should always just be treated as regular attributes (it's up
// to the renderer to detect and use them in a special way).
const valueExp = input.value;
if (isASTWithSource(valueExp)) {
const literal = valueExp.ast;
if (isLiteralPrimitive(literal) && literal.value === undefined) {
addAttrExpr(prepareSyntheticPropertyName(input.name), EMPTY_STRING_EXPR);
}
}
attrExprs.push(asLiteral(prepareSyntheticAttributeName(input.name)), EMPTY_STRING_EXPR);
} else {
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) {
addAttrExpr(core.AttributeMarker.SelectOnly);
nonSyntheticInputs.forEach((i: t.BoundAttribute) => addAttrExpr(i.name));
outputs.forEach((o: t.BoundEvent) => {
const name =
o.type === ParsedEventType.Animation ? getSyntheticPropertyName(o.name) : o.name;
addAttrExpr(name);
});
attrExprs.push(o.literal(core.AttributeMarker.SelectOnly));
nonSyntheticInputs.forEach((i: t.BoundAttribute) => attrExprs.push(asLiteral(i.name)));
outputs.forEach((o: t.BoundEvent) => attrExprs.push(asLiteral(o.name)));
}
return attrExprs;
@ -1067,22 +982,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
return this.constantPool.getConstLiteral(asLiteral(refsParam), true);
}
private prepareListenerParameter(tagName: string, outputAst: t.BoundEvent, index: number):
() => o.Expression[] {
private prepareListenerParameter(tagName: string, outputAst: t.BoundEvent): () => o.Expression[] {
let eventName: string = outputAst.name;
let bindingFnName;
if (outputAst.type === ParsedEventType.Animation) {
// synthetic @listener.foo values are treated the exact same as are standard listeners
bindingFnName = prepareSyntheticListenerFunctionName(eventName, outputAst.phase !);
eventName = prepareSyntheticListenerName(eventName, outputAst.phase !);
} else {
bindingFnName = sanitizeIdentifier(eventName);
eventName = prepareSyntheticAttributeName(`${outputAst.name}.${outputAst.phase}`);
}
const evNameSanitized = sanitizeIdentifier(eventName);
const tagNameSanitized = sanitizeIdentifier(tagName);
const functionName =
`${this.templateName}_${tagNameSanitized}_${bindingFnName}_${index}_listener`;
const functionName = `${this.templateName}_${tagNameSanitized}_${evNameSanitized}_listener`;
return () => {
const listenerScope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
@ -1465,7 +1373,7 @@ function createCssSelector(tag: string, attributes: {[name: string]: string}): C
cssSelector.addAttribute(name, value);
if (name.toLowerCase() === 'class') {
const classes = value.trim().split(/\s+/);
const classes = value.trim().split(/\s+/g);
classes.forEach(className => cssSelector.addClassName(className));
}
});
@ -1574,14 +1482,16 @@ function resolveSanitizationFn(input: t.BoundAttribute, context: core.SecurityCo
}
}
function prepareSyntheticAttributeName(name: string) {
return '@' + name;
}
function isSingleElementTemplate(children: t.Node[]): children is[t.Element] {
return children.length === 1 && children[0] instanceof t.Element;
}
function isTextNode(node: t.Node): boolean {
return node instanceof t.Text || node instanceof t.BoundText || node instanceof t.Icu;
}
function hasTextChildrenOnly(children: t.Node[]): boolean {
return children.every(isTextNode);
return !children.find(
child =>
!(child instanceof t.Text || child instanceof t.BoundText || child instanceof t.Icu));
}

View File

@ -440,4 +440,4 @@ export function calcPossibleSecurityContexts(
elementName => registry.securityContext(elementName, propName, isAttribute)));
});
return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
}
}

View File

@ -10,53 +10,55 @@ import {hyphenate, parse as parseStyle, stripUnnecessaryQuotes} from '../../src/
describe('style parsing', () => {
it('should parse empty or blank strings', () => {
const result1 = parseStyle('');
expect(result1).toEqual([]);
expect(result1).toEqual({});
const result2 = parseStyle(' ');
expect(result2).toEqual([]);
expect(result2).toEqual({});
});
it('should parse a string into a key/value map', () => {
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', () => {
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', () => {
const result = parseStyle(
'content: "foo"; opacity: \'0.5\'; font-family: "Verdana", Helvetica, "sans-serif"');
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', () => {
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', () => {
const quoteStr = '"one \'two\' three \"four\" five"';
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', () => {
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', () => {
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', () => {
const result = parseStyle('borderWidth: 200px');
expect(result).toEqual(['border-width', '200px']);
expect(result).toEqual({
'border-width': '200px',
});
});
describe('quote chomping', () => {

View File

@ -83,7 +83,6 @@ export {
loadQueryList as ɵloadQueryList,
elementEnd as ɵelementEnd,
elementProperty as ɵelementProperty,
componentHostSyntheticProperty as ɵcomponentHostSyntheticProperty,
projectionDef as ɵprojectionDef,
reference as ɵreference,
enableBindings as ɵenableBindings,
@ -93,7 +92,6 @@ export {
elementContainerStart as ɵelementContainerStart,
elementContainerEnd as ɵelementContainerEnd,
elementStyling as ɵelementStyling,
elementHostAttrs as ɵelementHostAttrs,
elementStylingMap as ɵelementStylingMap,
elementStyleProp as ɵelementStyleProp,
elementStylingApply as ɵelementStylingApply,

View File

@ -12,7 +12,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners,
import {TNode} from '../render3/interfaces/node';
import {StylingIndex} from '../render3/interfaces/styling';
import {TVIEW} from '../render3/interfaces/view';
import {getProp, getValue, isClassBasedValue} from '../render3/styling/class_and_style_bindings';
import {getProp, getValue, isClassBased} from '../render3/styling/class_and_style_bindings';
import {getStylingContext} from '../render3/styling/util';
import {DebugContext} from '../view/index';
@ -273,7 +273,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
if (stylingContext) {
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
i += StylingIndex.Size) {
if (isClassBasedValue(lNode, i)) {
if (isClassBased(lNode, i)) {
const className = getProp(lNode, i);
const value = getValue(lNode, i);
if (typeof value == 'boolean') {
@ -303,7 +303,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
if (stylingContext) {
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
i += StylingIndex.Size) {
if (!isClassBasedValue(lNode, i)) {
if (!isClassBased(lNode, i)) {
const styleName = getProp(lNode, i);
const value = getValue(lNode, i) as string | null;
if (value !== null) {

View File

@ -11,7 +11,7 @@ import {devModeEqual} from '../change_detection/change_detection_util';
import {assertDataInRange, assertLessThan, assertNotEqual} from './assert';
import {throwErrorIfNoChangesMode} from './errors';
import {BINDING_INDEX, LView} from './interfaces/view';
import {getCheckNoChangesMode, isCreationMode} from './state';
import {getCheckNoChangesMode, getCreationMode} from './state';
import {NO_CHANGE} from './tokens';
import {isDifferent} from './util';
@ -44,7 +44,7 @@ export function bindingUpdated(lView: LView, bindingIndex: number, value: any):
} else if (isDifferent(lView[bindingIndex], value)) {
if (ngDevMode && getCheckNoChangesMode()) {
if (!devModeEqual(lView[bindingIndex], value)) {
throwErrorIfNoChangesMode(isCreationMode(lView), lView[bindingIndex], value);
throwErrorIfNoChangesMode(getCreationMode(), lView[bindingIndex], value);
}
}
lView[bindingIndex] = value;

View File

@ -22,7 +22,7 @@ import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
@ -133,9 +133,7 @@ export function renderComponent<T>(
component = createRootComponent(
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
refreshDescendantViews(rootView); // creation mode pass
rootView[FLAGS] &= ~LViewFlags.CreationMode;
refreshDescendantViews(rootView); // update mode pass
refreshDescendantViews(rootView, null);
} finally {
leaveView(oldView);
if (rendererFactory.end) rendererFactory.end();

View File

@ -208,9 +208,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
addToViewTree(rootLView, HEADER_OFFSET, componentView);
refreshDescendantViews(rootLView);
refreshDescendantViews(rootLView, RenderFlags.Create);
} finally {
leaveView(oldLView);
leaveView(oldLView, true);
if (rendererFactory.end) rendererFactory.end();
}

View File

@ -7,13 +7,12 @@
*/
import './ng_dev_mode';
import {assertDomNode} from './assert';
import {EMPTY_ARRAY} from './empty';
import {EMPTY_ARRAY} from './definition';
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {TNode, TNodeFlags} from './interfaces/node';
import {RElement} from './interfaces/renderer';
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
import {getComponentViewByIndex, getNativeByTNode, getTNode, readElementValue, readPatchedData} from './util';
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.

View File

@ -9,15 +9,22 @@
import './ng_dev_mode';
import {ChangeDetectionStrategy} from '../change_detection/constants';
import {Provider} from '../di/provider';
import {NgModuleDef} from '../metadata/ng_module';
import {ViewEncapsulation} from '../metadata/view';
import {Mutable, Type} from '../type';
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 {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
import {CssSelectorList} from './interfaces/projection';
import {CssSelectorList, SelectorFlags} 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;
/**
@ -382,7 +389,7 @@ export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T>>): nev
*/
function invertObject(obj: any, secondary?: any): any {
if (obj == null) return EMPTY_OBJ;
if (obj == null) return EMPTY;
const newLookup: any = {};
for (const minifiedKey in obj) {
if (obj.hasOwnProperty(minifiedKey)) {

View File

@ -21,8 +21,7 @@ import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TN
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LView, TData, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
import {findComponentView, getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, isComponentDef, stringify} from './util';
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, stringify} from './util';
/**
* Defines if the call to `inject` should include `viewProviders` in its resolution.
@ -60,7 +59,7 @@ import {findComponentView, getParentInjectorIndex, getParentInjectorView, hasPar
*
* ```
*/
let includeViewProviders = true;
let includeViewProviders = false;
function setIncludeViewProviders(v: boolean): boolean {
const oldValue = includeViewProviders;
@ -198,7 +197,7 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number {
*/
export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation {
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
return tNode.parent.injectorIndex as any; // ViewOffset is 0
return tNode.parent.injectorIndex as any; // ViewOffset is 0, AcrossHostBoundary is 0
}
// For most cases, the parent injector index can be found on the host node (e.g. for component
@ -208,12 +207,16 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn
let viewOffset = 1;
while (hostTNode && hostTNode.injectorIndex === -1) {
view = view[DECLARATION_VIEW] !;
hostTNode = view ? view[HOST_NODE] : null;
hostTNode = view[HOST_NODE] !;
viewOffset++;
}
const acrossHostBoundary = hostTNode && hostTNode.type === TNodeType.Element ?
RelativeInjectorLocationFlags.AcrossHostBoundary :
0;
return hostTNode ?
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) :
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) |
acrossHostBoundary :
-1 as any;
}
@ -292,86 +295,79 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
* @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
*/
export function getOrCreateInjectable<T>(
tNode: TElementNode | TContainerNode | TElementContainerNode | null, lView: LView,
tNode: TElementNode | TContainerNode | TElementContainerNode, lView: LView,
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default,
notFoundValue?: any): T|null {
if (tNode) {
const bloomHash = bloomHashBitOrFactory(token);
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
// so just call the factory function to create it.
if (typeof bloomHash === 'function') {
const savePreviousOrParentTNode = getPreviousOrParentTNode();
const saveLView = getLView();
setTNodeAndViewData(tNode, lView);
try {
const value = bloomHash();
if (value == null && !(flags & InjectFlags.Optional)) {
throw new Error(`No provider for ${stringify(token)}!`);
} else {
return value;
}
} finally {
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
const bloomHash = bloomHashBitOrFactory(token);
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
// so just call the factory function to create it.
if (typeof bloomHash === 'function') {
const savePreviousOrParentTNode = getPreviousOrParentTNode();
const saveLView = getLView();
setTNodeAndViewData(tNode, lView);
try {
const value = bloomHash();
if (value == null && !(flags & InjectFlags.Optional)) {
throw new Error(`No provider for ${stringify(token)}!`);
} else {
return value;
}
} else if (typeof bloomHash == 'number') {
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
} finally {
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
}
} else if (typeof bloomHash == 'number') {
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
// A reference to the previous injector TView that was found while climbing the element
// injector tree. This is used to know if viewProviders can be accessed on the current
// injector.
let previousTView: TView|null = null;
let injectorIndex = getInjectorIndex(tNode, lView);
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
let hostTElementNode: TNode|null =
flags & InjectFlags.Host ? findComponentView(lView)[HOST_NODE] : null;
// A reference to the previous injector TView that was found while climbing the element injector
// tree. This is used to know if viewProviders can be accessed on the current injector.
let previousTView: TView|null = null;
let injectorIndex = getInjectorIndex(tNode, lView);
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
// If we should skip this injector, or if there is no injector on this node, start by
// searching
// the parent injector.
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
lView[injectorIndex + PARENT_INJECTOR];
// If we should skip this injector, or if there is no injector on this node, start by searching
// the parent injector.
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
lView[injectorIndex + PARENT_INJECTOR];
if (!shouldSearchParent(flags, false)) {
injectorIndex = -1;
} else {
previousTView = lView[TVIEW];
injectorIndex = getParentInjectorIndex(parentLocation);
lView = getParentInjectorView(parentLocation, lView);
if (!shouldSearchParent(flags, parentLocation)) {
injectorIndex = -1;
} else {
previousTView = lView[TVIEW];
injectorIndex = getParentInjectorIndex(parentLocation);
lView = getParentInjectorView(parentLocation, lView);
}
}
// Traverse up the injector tree until we find a potential match or until we know there
// *isn't* a match.
while (injectorIndex !== -1) {
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
// Check the current injector. If it matches, see if it contains token.
const tView = lView[TVIEW];
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
// 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 instance.
const instance: T|null =
searchTokensOnInjector<T>(injectorIndex, lView, token, previousTView);
if (instance !== NOT_FOUND) {
return instance;
}
}
// Traverse up the injector tree until we find a potential match or until we know there
// *isn't* a match.
while (injectorIndex !== -1) {
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
// Check the current injector. If it matches, see if it contains token.
const tView = lView[TVIEW];
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
// 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 instance.
const instance: T|null = searchTokensOnInjector<T>(
injectorIndex, lView, token, previousTView, flags, hostTElementNode);
if (instance !== NOT_FOUND) {
return instance;
}
}
if (shouldSearchParent(
flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) &&
bloomHasToken(bloomHash, injectorIndex, lView)) {
// The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching.
previousTView = tView;
injectorIndex = getParentInjectorIndex(parentLocation);
lView = getParentInjectorView(parentLocation, lView);
} else {
// If we should not search parent OR If the ancestor bloom filter value does not have the
// bit corresponding to the directive we can give up on traversing up to find the specific
// injector.
injectorIndex = -1;
}
if (shouldSearchParent(flags, parentLocation) &&
bloomHasToken(bloomHash, injectorIndex, lView)) {
// The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching.
previousTView = tView;
injectorIndex = getParentInjectorIndex(parentLocation);
lView = getParentInjectorView(parentLocation, lView);
} else {
// If we should not search parent OR If the ancestor bloom filter value does not have the
// bit corresponding to the directive we can give up on traversing up to find the specific
// injector.
injectorIndex = -1;
}
}
}
@ -400,7 +396,7 @@ const NOT_FOUND = {};
function searchTokensOnInjector<T>(
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
previousTView: TView | null, flags: InjectFlags, hostTElementNode: TNode | null) {
previousTView: TView | null) {
const currentTView = lView[TVIEW];
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
// First, we need to determine if view providers can be accessed by the starting element.
@ -422,12 +418,7 @@ function searchTokensOnInjector<T>(
// into the ViewProviders.
(previousTView != currentTView && (tNode.type === TNodeType.Element));
// 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);
const injectableIdx = locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders);
if (injectableIdx !== null) {
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
} else {
@ -442,13 +433,13 @@ function searchTokensOnInjector<T>(
* @param lView The view we are currently processing
* @param token Provider token or type of a directive to look for.
* @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.
*/
export function locateDirectiveOrProvider<T>(
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>, canAccessViewProviders: boolean,
isHostSpecialCase: boolean | number): number|null {
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>,
canAccessViewProviders: boolean): number|null {
const tView = lView[TVIEW];
const nodeFlags = tNode.flags;
const nodeProviderIndexes = tNode.providerIndexes;
const tInjectables = tView.data;
@ -459,21 +450,13 @@ export function locateDirectiveOrProvider<T>(
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
const startingIndex =
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
// 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++) {
for (let i = startingIndex; i < directiveEnd; i++) {
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
if (i < directivesStart && token === providerTokenOrDef ||
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
return i;
}
}
if (isHostSpecialCase) {
const dirDef = tInjectables[directivesStart] as DirectiveDef<any>;
if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
return directivesStart;
}
}
return null;
}
@ -563,8 +546,12 @@ export function bloomHasToken(
}
/** Returns true if flags prevent parent injector from being searched for tokens */
function shouldSearchParent(flags: InjectFlags, isFirstHostTNode: boolean): boolean|number {
return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjectorLocation): boolean|
number {
return !(
flags & InjectFlags.Self ||
(flags & InjectFlags.Host &&
((parentLocation as any as number) & RelativeInjectorLocationFlags.AcrossHostBoundary)));
}
export function injectInjector() {
@ -574,8 +561,7 @@ export function injectInjector() {
export class NodeInjector implements Injector {
constructor(
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,
private _lView: LView) {}
private _tNode: TElementNode|TContainerNode|TElementContainerNode, private _lView: LView) {}
get(token: any, notFoundValue?: any): any {
return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);

View File

@ -7,7 +7,6 @@
*/
import {Injector} from '../di/injector';
import {assertDefined} from './assert';
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
import {NodeInjector} from './di';
@ -15,8 +14,7 @@ import {LContext} from './interfaces/context';
import {DirectiveDef} from './interfaces/definition';
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
import {readElementValue, readPatchedLView, stringify} from './util';
import {readPatchedLView, stringify} from './util';
/**
@ -329,7 +327,7 @@ export function getListeners(element: Element): Listener[] {
const secondParam = tCleanup[i++];
if (typeof firstParam === 'string') {
const name: string = firstParam;
const listenerElement = readElementValue(lView[secondParam]) as any as Element;
const listenerElement: Element = lView[secondParam];
const callback: (value: any) => any = lCleanup[tCleanup[i++]];
const useCaptureOrIndx = tCleanup[i++];
// if useCaptureOrIndx is boolean then report it as is.

View File

@ -1,24 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import './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);
}

View File

@ -8,8 +8,8 @@
import {Type} from '../../type';
import {fillProperties} from '../../util/property';
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
import {ComponentDef, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
import {EMPTY, EMPTY_ARRAY} from '../definition';
import {ComponentDef, ComponentTemplate, 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(value: any): any {
if (value === EMPTY_OBJ) {
if (value === EMPTY) {
return {};
} else if (value === EMPTY_ARRAY) {
return [];

View File

@ -93,10 +93,9 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
*
* @param currentView The current view
*/
export function executeInitHooks(
currentView: LView, tView: TView, checkNoChangesMode: boolean): void {
if (!checkNoChangesMode && currentView[FLAGS] & LViewFlags.RunInit) {
executeHooks(currentView, tView.initHooks, tView.checkHooks, checkNoChangesMode);
export function executeInitHooks(currentView: LView, tView: TView, creationMode: boolean): void {
if (currentView[FLAGS] & LViewFlags.RunInit) {
executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
currentView[FLAGS] &= ~LViewFlags.RunInit;
}
}
@ -107,19 +106,17 @@ export function executeInitHooks(
* @param currentView The current view
*/
export function executeHooks(
currentView: LView, allHooks: HookData | null, checkHooks: HookData | null,
checkNoChangesMode: boolean): void {
if (checkNoChangesMode) return;
const hooksToCall = currentView[FLAGS] & LViewFlags.FirstLViewPass ? allHooks : checkHooks;
data: LView, allHooks: HookData | null, checkHooks: HookData | null,
creationMode: boolean): void {
const hooksToCall = creationMode ? allHooks : checkHooks;
if (hooksToCall) {
callHooks(currentView, hooksToCall);
callHooks(data, hooksToCall);
}
}
/**
* Calls lifecycle hooks with their contexts, skipping init hooks if it's not
* the first LView pass.
* creation mode.
*
* @param currentView The current view
* @param arr The array in which the hooks are found

View File

@ -25,11 +25,11 @@ import {NO_CHANGE} from './tokens';
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
const MARKER = `<EFBFBD>`;
const ICU_BLOCK_REGEX = /^\s*(<28>\d+:?\d*<EFBFBD>)\s*,\s*(select|plural)\s*,/;
const ICU_BLOCK_REGEX = /^\s*(<28>\d+<2B>)\s*,\s*(select|plural)\s*,/;
const SUBTEMPLATE_REGEXP = /<2F>\/?\*(\d+:\d+)<29>/gi;
const PH_REGEXP = /<2F>(\/?[#*]\d+):?\d*<2A>/gi;
const BINDING_REGEXP = /<2F>(\d+):?\d*<2A>/gi;
const ICU_REGEXP = /({\s*<2A>\d+:?\d*<EFBFBD>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
const ICU_REGEXP = /({\s*<2A>\d+<2B>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
// i18nPostproocess regexps
const PP_PLACEHOLDERS = /\[(<28>.+?<3F>?)\]/g;

View File

@ -44,13 +44,12 @@ export {
elementClassProp,
elementEnd,
elementProperty,
componentHostSyntheticProperty,
elementStart,
elementContainerStart,
elementContainerEnd,
elementStyling,
elementHostAttrs,
elementStylingMap,
elementStyleProp,
elementStylingApply,
@ -80,7 +79,7 @@ export {
directiveInject,
injectAttribute,
getCurrentView
} from './instructions';

View File

@ -15,7 +15,6 @@ import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {Type} from '../type';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../util/ng_reflect';
import {assertDataInRange, assertDefined, assertEqual, assertHasParent, assertLessThan, assertNotEqual, assertPreviousIsParent} from './assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from './bindings';
import {attachPatchData, getComponentViewByInstance} from './context_discovery';
@ -23,7 +22,7 @@ import {diPublicInInjector, getNodeInjectable, getOrCreateInjectable, getOrCreat
import {throwMultipleComponentError} from './errors';
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
import {PlayerFactory} from './interfaces/player';
@ -31,17 +30,17 @@ import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'
import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {SanitizerFn} from './interfaces/sanitization';
import {StylingIndex} from './interfaces/styling';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCreationMode, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
import {createStylingContextTemplate, renderStyleAndClassBindings, setStyle, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {BoundPlayerFactory} from './styling/player_factory';
import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util';
import {getStylingContext, isAnimationProp} from './styling/util';
import {NO_CHANGE} from './tokens';
import {findComponentView, getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
/**
@ -61,30 +60,36 @@ const enum BindingDirection {
* bindings, refreshes child components.
* Note: view hooks are triggered later when leaving the view.
*/
export function refreshDescendantViews(lView: LView) {
export function refreshDescendantViews(lView: LView, rf: RenderFlags | null) {
const tView = lView[TVIEW];
// This needs to be set before children are processed to support recursive components
tView.firstTemplatePass = false;
setFirstTemplatePass(false);
// If this is a creation pass, we should not call lifecycle hooks or evaluate bindings.
// This will be done in the update pass.
if (!isCreationMode(lView)) {
// Dynamically created views must run first only in creation mode. If this is a
// creation-only pass, we should not call lifecycle hooks or evaluate bindings.
// This will be done in the update-only pass.
if (rf !== RenderFlags.Create) {
const creationMode = getCreationMode();
const checkNoChangesMode = getCheckNoChangesMode();
executeInitHooks(lView, tView, checkNoChangesMode);
if (!checkNoChangesMode) {
executeInitHooks(lView, tView, creationMode);
}
refreshDynamicEmbeddedViews(lView);
// Content query results must be refreshed before content hooks are called.
refreshContentQueries(tView);
executeHooks(lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode);
if (!checkNoChangesMode) {
executeHooks(lView, tView.contentHooks, tView.contentCheckHooks, creationMode);
}
setHostBindings(tView, lView);
}
refreshChildComponents(tView.components);
refreshChildComponents(tView.components, rf);
}
@ -142,10 +147,10 @@ function refreshContentQueries(tView: TView): void {
}
/** Refreshes child components in the current view. */
function refreshChildComponents(components: number[] | null): void {
function refreshChildComponents(components: number[] | null, rf: RenderFlags | null): void {
if (components != null) {
for (let i = 0; i < components.length; i++) {
componentRefresh(components[i]);
componentRefresh(components[i], rf);
}
}
}
@ -155,8 +160,7 @@ export function createLView<T>(
rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null,
sanitizer?: Sanitizer | null, injector?: Injector | null): LView {
const lView = tView.blueprint.slice() as LView;
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit |
LViewFlags.FirstLViewPass;
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
lView[CONTEXT] = context;
lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !;
@ -299,7 +303,8 @@ export function renderTemplate<T>(
renderer, sanitizer);
hostView[HOST_NODE] = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null);
}
renderComponentOrTemplate(hostView, context, templateFn);
renderComponentOrTemplate(hostView, context, null, templateFn);
return hostView;
}
@ -343,7 +348,8 @@ export function createEmbeddedViewAndNode<T>(
* can't store TViews in the template function itself (as we do for comps). Instead, we store the
* TView for dynamically created views on their host TNode, which only has one instance.
*/
export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, context: T) {
export function renderEmbeddedTemplate<T>(
viewToRender: LView, tView: TView, context: T, rf: RenderFlags) {
const _isParent = getIsParent();
const _previousOrParentTNode = getPreviousOrParentTNode();
setIsParent(true);
@ -359,17 +365,22 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
oldView = enterView(viewToRender, viewToRender[HOST_NODE]);
namespaceHTML();
tView.template !(getRenderFlags(viewToRender), context);
// This must be set to false immediately after the first creation run because in an
// ngFor loop, all the views will be created together before update mode runs and turns
// off firstTemplatePass. If we don't set it here, instances will perform directive
// matching, etc again and again.
viewToRender[TVIEW].firstTemplatePass = false;
setFirstTemplatePass(false);
refreshDescendantViews(viewToRender);
tView.template !(rf, context);
if (rf & RenderFlags.Update) {
refreshDescendantViews(viewToRender, null);
} else {
// This must be set to false immediately after the first creation run because in an
// ngFor loop, all the views will be created together before update mode runs and turns
// off firstTemplatePass. If we don't set it here, instances will perform directive
// matching, etc again and again.
viewToRender[TVIEW].firstTemplatePass = false;
setFirstTemplatePass(false);
}
} finally {
leaveView(oldView !);
// renderEmbeddedTemplate() is called twice, once for creation only and then once for
// update. When for creation only, leaveView() must not trigger view hooks, nor clean flags.
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
leaveView(oldView !, isCreationOnly);
setIsParent(_isParent);
setPreviousOrParentTNode(_previousOrParentTNode);
}
@ -391,31 +402,21 @@ export function nextContext<T = any>(level: number = 1): T {
}
function renderComponentOrTemplate<T>(
hostView: LView, context: T, templateFn?: ComponentTemplate<T>) {
hostView: LView, componentOrContext: T, rf: RenderFlags | null,
templateFn?: ComponentTemplate<T>) {
const rendererFactory = hostView[RENDERER_FACTORY];
const oldView = enterView(hostView, hostView[HOST_NODE]);
const normalExecutionPath = !getCheckNoChangesMode();
try {
if (normalExecutionPath && rendererFactory.begin) {
if (rendererFactory.begin) {
rendererFactory.begin();
}
if (isCreationMode(hostView)) {
// creation mode pass
if (templateFn) {
namespaceHTML();
templateFn(RenderFlags.Create, context !);
}
refreshDescendantViews(hostView);
hostView[FLAGS] &= ~LViewFlags.CreationMode;
if (templateFn) {
namespaceHTML();
templateFn(rf || getRenderFlags(hostView), componentOrContext !);
}
// update mode pass
templateFn && templateFn(RenderFlags.Update, context !);
refreshDescendantViews(hostView);
refreshDescendantViews(hostView, rf);
} finally {
if (normalExecutionPath && rendererFactory.end) {
if (rendererFactory.end) {
rendererFactory.end();
}
leaveView(oldView);
@ -424,11 +425,16 @@ function renderComponentOrTemplate<T>(
/**
* This function returns the default configuration of rendering flags depending on when the
* template is in creation mode or update mode. Update block and create block are
* always run separately.
* template is in creation mode or update mode. By default, the update block is run with the
* creation block when the view is in creation mode. Otherwise, the update block is run
* alone.
*
* Dynamically created views do NOT use this configuration (update block and create block are
* always run separately).
*/
function getRenderFlags(view: LView): RenderFlags {
return isCreationMode(view) ? RenderFlags.Create : RenderFlags.Update;
return view[FLAGS] & LViewFlags.CreationMode ? RenderFlags.Create | RenderFlags.Update :
RenderFlags.Update;
}
//////////////////////////
@ -458,8 +464,7 @@ export function namespaceHTML() {
*
* @param index Index of the element in the data array
* @param name Name of the DOM Node
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
* @param attrs Statically bound set of attributes to be written into the DOM element on creation.
* @param localRefs A set of local reference bindings on the element.
*/
export function element(
@ -529,8 +534,7 @@ export function elementContainerEnd(): void {
*
* @param index Index of the element in the LView array
* @param name Name of the DOM Node
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
* @param attrs Statically bound set of attributes to be written into the DOM element on creation.
* @param localRefs A set of local reference bindings on the element.
*
* Attributes and localRefs are passed as an array of strings where elements with an even index
@ -554,14 +558,6 @@ export function elementStart(
const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null);
if (attrs) {
// it's important to only prepare styling-related datastructures once for a given
// tNode and not each time an element is created. Also, the styling code is designed
// to be patched and constructed at various points, but only up until the first element
// is created. Then the styling context is locked and can only be instantiated for each
// successive element that is created.
if (tView.firstTemplatePass && !tNode.stylingTemplate && hasStyling(attrs)) {
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
}
setUpAttributes(native, attrs);
}
@ -575,23 +571,6 @@ export function elementStart(
attachPatchData(native, lView);
}
increaseElementDepthCount();
// if a directive contains a host binding for "class" then all class-based data will
// flow through that (except for `[class.prop]` bindings). This also includes initial
// static class values as well. (Note that this will be fixed once map-based `[style]`
// and `[class]` bindings work for multiple directives.)
if (tView.firstTemplatePass) {
const inputData = initializeTNodeInputs(tNode);
if (inputData && inputData.hasOwnProperty('class')) {
tNode.flags |= TNodeFlags.hasClassInput;
}
}
// There is no point in rendering styles when a class directive is present since
// it will take that over for us (this will be removed once #FW-882 is in).
if (tNode.stylingTemplate && (tNode.flags & TNodeFlags.hasClassInput) === 0) {
renderInitialStylesAndClasses(native, tNode.stylingTemplate, lView[RENDERER]);
}
}
/**
@ -750,28 +729,25 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
let i = 0;
while (i < attrs.length) {
const attrName = attrs[i++];
if (typeof attrName == 'number') {
const attrName = attrs[i];
if (attrName === AttributeMarker.SelectOnly) break;
if (attrName === NG_PROJECT_AS_ATTR_NAME) {
i += 2;
} else {
ngDevMode && ngDevMode.rendererSetAttribute++;
if (attrName === AttributeMarker.NamespaceURI) {
// Namespaced attributes
const namespaceURI = attrs[i++] as string;
const attrName = attrs[i++] as string;
const attrVal = attrs[i++] as string;
ngDevMode && ngDevMode.rendererSetAttribute++;
const namespaceURI = attrs[i + 1] as string;
const attrName = attrs[i + 2] as string;
const attrVal = attrs[i + 3] as string;
isProc ?
(renderer as ProceduralRenderer3)
.setAttribute(native, attrName, attrVal, namespaceURI) :
native.setAttributeNS(namespaceURI, attrName, attrVal);
i += 4;
} else {
// All other `AttributeMarker`s are ignored here.
break;
}
} else {
/// attrName is string;
const attrVal = attrs[i++];
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
// Standard attributes
ngDevMode && ngDevMode.rendererSetAttribute++;
const attrVal = attrs[i + 1];
if (isAnimationProp(attrName)) {
if (isProc) {
(renderer as ProceduralRenderer3).setProperty(native, attrName, attrVal);
@ -782,6 +758,7 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
.setAttribute(native, attrName as string, attrVal as string) :
native.setAttribute(attrName as string, attrVal as string);
}
i += 2;
}
}
}
@ -933,15 +910,6 @@ export function elementEnd(): void {
queueLifecycleHooks(getLView()[TVIEW], previousOrParentTNode);
decreaseElementDepthCount();
// this is fired at the end of elementEnd because ALL of the stylingBindings code
// (for directives and the template) have now executed which means the styling
// context can be instantiated properly.
if (hasClassInput(previousOrParentTNode)) {
const stylingContext = getStylingContext(previousOrParentTNode.index, lView);
setInputsForProperty(
lView, previousOrParentTNode.inputs !['class'] !, getInitialClassNameValue(stylingContext));
}
}
/**
@ -987,48 +955,10 @@ export function elementAttribute(
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
*/
export function elementProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): void {
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly);
}
/**
* Updates a synthetic host binding (e.g. `[@foo]`) on a component.
*
* This instruction is for compatibility purposes and is designed to ensure that a
* synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
* the component's renderer. Normally all host bindings are evaluated with the parent
* component's renderer, but, in the case of animation @triggers, they need to be
* evaluated with the sub components renderer (because that's where the animation
* triggers are defined).
*
* Do not use this instruction as a replacement for `elementProperty`. This instruction
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
*
* @param index The index of the element to update in the data array
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
*/
export function componentHostSyntheticProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean) {
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly, loadComponentRenderer);
}
function loadComponentRenderer(tNode: TNode, lView: LView): Renderer3 {
const componentLView = lView[tNode.index] as LView;
return componentLView[RENDERER];
}
function elementPropertyInternal<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean,
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
if (value === NO_CHANGE) return;
const lView = getLView();
const element = getNativeByIndex(index, lView) as RElement | RComment;
@ -1045,7 +975,7 @@ function elementPropertyInternal<T>(
}
}
} else if (tNode.type === TNodeType.Element) {
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
const renderer = lView[RENDERER];
// It is assumed that the sanitizer is only added when the compiler determines that the property
// is risky, so sanitization can be done without further checks.
value = sanitizer != null ? (sanitizer(value) as any) : value;
@ -1174,84 +1104,119 @@ function generatePropertyAliases(tNode: TNode, direction: BindingDirection): Pro
return propStore;
}
/**
* Add or remove a class in a `classList` on a DOM element.
*
* This instruction is meant to handle the [class.foo]="exp" case
*
* @param index The index of the element to update in the data array
* @param classIndex Index of class to toggle. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value A value indicating if a given class should be added or removed.
* @param directive the ref to the directive that is attempting to change styling.
*/
export function elementClassProp(
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {}): void {
if (directive != undefined) {
return hackImplementationOfElementClassProp(
index, classIndex, value, directive); // proper supported in next PR
}
const val =
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
updateElementClassProp(getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, val);
}
/**
* Assign any inline style values to the element during creation mode.
*
* This instruction is meant to be called during creation mode to register all
* dynamic style and class bindings on the element. Note for static values (no binding)
* see `elementStart` and `elementHostAttrs`.
* This instruction is meant to be called during creation mode to apply all styling
* (e.g. `style="..."`) values to the element. This is also where the provided index
* value is allocated for the styling details for its corresponding element (the element
* index is the previous index value from this one).
*
* @param classBindingNames An array containing bindable class names.
* The `elementClassProp` refers to the class name by index in this array.
* (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
* @param styleBindingNames An array containing bindable style properties.
* The `elementStyleProp` refers to the class name by index in this array.
* (i.e. `['width', 'height']` means `width=0` and `height=1`).
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
* property values that are applied to the element (during rendering).
* Note that the sanitizer instance itself is tied to the `directive` (if provided).
* @param directive A directive instance the styling is associated with. If not provided
* current view's controller instance is assumed.
* (Note this function calls `elementStylingApply` immediately when called.)
*
* @publicApi
*
* @param index Index value which will be allocated to store styling data for the element.
* (Note that this is not the element index, but rather an index value allocated
* specifically for element styling--the index must be the next index after the element
* index.)
* @param classDeclarations A key/value array of CSS classes that will be registered on the element.
* Each individual style will be used on the element as long as it is not overridden
* by any classes placed on the element by multiple (`[class]`) or singular (`[class.named]`)
* bindings. If a class binding changes its value to a falsy value then the matching initial
* class value that are passed in here will be applied to the element (if matched).
* @param styleDeclarations A key/value array of CSS styles that will be registered on the element.
* Each individual style will be used on the element as long as it is not overridden
* by any styles placed on the element by multiple (`[style]`) or singular (`[style.prop]`)
* bindings. If a style binding changes its value to null then the initial styling
* values that are passed in here will be applied to the element (if matched).
* @param styleSanitizer An optional sanitizer function that will be used (if provided)
* to sanitize the any CSS property values that are applied to the element (during rendering).
* @param directive the ref to the directive that is attempting to change styling.
*/
export function elementStyling(
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
styleSanitizer?: StyleSanitizeFn | null, directive?: {}): void {
const tNode = getPreviousOrParentTNode();
if (!tNode.stylingTemplate) {
tNode.stylingTemplate = createEmptyStylingContext();
if (directive != undefined) {
getCreationMode() &&
hackImplementationOfElementStyling(
classDeclarations || null, styleDeclarations || null, styleSanitizer || null,
directive); // supported in next PR
return;
}
const tNode = getPreviousOrParentTNode();
const inputData = initializeTNodeInputs(tNode);
if (!tNode.stylingTemplate) {
const hasClassInput = inputData && inputData.hasOwnProperty('class') ? true : false;
if (hasClassInput) {
tNode.flags |= TNodeFlags.hasClassInput;
}
// initialize the styling template.
tNode.stylingTemplate = createStylingContextTemplate(
classDeclarations, styleDeclarations, styleSanitizer, hasClassInput);
}
if (styleDeclarations && styleDeclarations.length ||
classDeclarations && classDeclarations.length) {
const index = tNode.index;
if (delegateToClassInput(tNode)) {
const lView = getLView();
const stylingContext = getStylingContext(index, lView);
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
setInputsForProperty(lView, tNode.inputs !['class'] !, initialClasses);
}
elementStylingApply(index - HEADER_OFFSET);
}
updateContextWithBindings(
tNode.stylingTemplate !, directive || null, classBindingNames, styleBindingNames,
styleSanitizer, hasClassInput(tNode));
}
/**
* Assign static styling values to a host element.
*
* NOTE: This instruction is meant to used from `hostBindings` function only.
*
* @param directive A directive instance the styling is associated with.
* @param attrs An array containing class and styling information. The values must be marked with
* `AttributeMarker`.
*
* ```
* var attrs = [AttributeMarker.Classes, 'foo', 'bar',
* AttributeMarker.Styles, 'width', '100px', 'height, '200px']
* elementHostAttrs(directive, attrs);
* ```
*
* @publicApi
*/
export function elementHostAttrs(directive: any, attrs: TAttributes) {
const tNode = getPreviousOrParentTNode();
if (!tNode.stylingTemplate) {
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
}
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, directive);
}
/**
* Apply styling binding to the element.
* Apply all styling values to the element which have been queued by any styling instructions.
*
* This instruction is meant to be run after `elementStyle` and/or `elementStyleProp`.
* if any styling bindings have changed then the changes are flushed to the element.
* This instruction is meant to be run once one or more `elementStyle` and/or `elementStyleProp`
* have been issued against the element. This function will also determine if any styles have
* changed and will then skip the operation if there is nothing new to render.
*
* Once called then all queued styles will be flushed.
*
* @param index Index of the element's with which styling is associated.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
components
*
* @publicApi
* @param index Index of the element's styling storage that will be rendered.
* (Note that this is not the element index, but rather an index value allocated
* specifically for element styling--the index must be the next index after the element
* index.)
* @param directive the ref to the directive that is attempting to change styling.
*/
export function elementStylingApply(index: number, directive?: any): void {
export function elementStylingApply(index: number, directive?: {}): void {
if (directive != undefined) {
return hackImplementationOfElementStylingApply(index, directive); // supported in next PR
}
const lView = getLView();
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
const totalPlayersQueued = renderStyling(
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender, null,
null, directive);
const isFirstRender = (lView[FLAGS] & LViewFlags.CreationMode) !== 0;
const totalPlayersQueued = renderStyleAndClassBindings(
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(lView);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
@ -1259,34 +1224,29 @@ export function elementStylingApply(index: number, directive?: any): void {
}
/**
* Update a style bindings value on an element.
* Queue a given style to be rendered on an Element.
*
* If the style value is `null` then it will be removed from the element
* (or assigned a different value depending if there are any styles placed
* on the element with `elementStyle` or any styles that are present
* from when the element was created (with `elementStyling`).
*
* (Note that the styling element is updated as part of `elementStylingApply`.)
* (Note that the styling instruction will not be applied until `elementStylingApply` is called.)
*
* @param index Index of the element's with which styling is associated.
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `elementStlyingBindings`.
* @param value New value to write (null to remove). Note that if a directive also
* attempts to write to the same binding value then it will only be able to
* do so if the template binding value is `null` (or doesn't exist at all).
* @param index Index of the element's styling storage to change in the data array.
* (Note that this is not the element index, but rather an index value allocated
* specifically for element styling--the index must be the next index after the element
* index.)
* @param styleIndex Index of the style property on this element. (Monotonically increasing.)
* @param value New value to write (null to remove).
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
components
*
* @publicApi
* @param directive the ref to the directive that is attempting to change styling.
*/
export function elementStyleProp(
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
suffix?: string | null, directive?: {}): void {
suffix?: string, directive?: {}): void {
let valueToAdd: string|null = null;
if (value !== null) {
if (suffix) {
@ -1301,59 +1261,35 @@ export function elementStyleProp(
valueToAdd = value as any as string;
}
}
updateElementStyleProp(
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd, directive);
if (directive != undefined) {
hackImplementationOfElementStyleProp(index, styleIndex, valueToAdd, suffix, directive);
} else {
updateElementStyleProp(
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd);
}
}
/**
* Add or remove a class via a class binding on a DOM element.
* Queue a key/value map of styles to be rendered on an Element.
*
* This instruction is meant to handle the [class.foo]="exp" case and, therefore,
* the class itself must already be applied using `elementStyling` within
* the creation block.
*
* @param index Index of the element's with which styling is associated.
* @param classIndex Index of class to toggle. This index value refers to the
* index of the class in the class bindings array that was passed into
* `elementStlyingBindings` (which is meant to be called before this
* function is).
* @param value A true/false value which will turn the class on or off.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
components
*
* @publicApi
*/
export function elementClassProp(
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {}): void {
const onOrOffClassValue =
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
updateElementClassProp(
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, onOrOffClassValue,
directive);
}
/**
* Update style and/or class bindings using object literal.
*
* This instruction is meant apply styling via the `[style]="exp"` and `[class]="exp"` template
* bindings. When styles are applied to the Element they will then be placed with respect to
* any styles set with `elementStyleProp`. If any styles are set to `null` then they will be
* removed from the element.
* This instruction is meant to handle the `[style]="exp"` usage. When styles are applied to
* the Element they will then be placed with respect to any styles set with `elementStyleProp`.
* If any styles are set to `null` then they will be removed from the element (unless the same
* style properties have been assigned to the element during creation using `elementStyling`).
*
* (Note that the styling instruction will not be applied until `elementStylingApply` is called.)
*
* @param index Index of the element's with which styling is associated.
* @param index Index of the element's styling storage to change in the data array.
* (Note that this is not the element index, but rather an index value allocated
* specifically for element styling--the index must be the next index after the element
* index.)
* @param classes A key/value style map of CSS classes that will be added to the given element.
* Any missing classes (that have already been applied to the element beforehand) will be
* removed (unset) from the element's list of CSS classes.
* @param styles A key/value style map of the styles that will be applied to the given element.
* Any missing styles (that have already been applied to the element beforehand) will be
* removed (unset) from the element's styling.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
*
* @publicApi
* @param directive the ref to the directive that is attempting to change styling.
*/
export function elementStylingMap<T>(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
@ -1364,24 +1300,113 @@ export function elementStylingMap<T>(
const lView = getLView();
const tNode = getTNode(index, lView);
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
const initialClasses = getInitialClassNameValue(stylingContext);
if (delegateToClassInput(tNode) && classes !== NO_CHANGE) {
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
const classInputVal =
(initialClasses.length ? (initialClasses + ' ') : '') + (classes as string);
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
} else {
updateStylingMap(stylingContext, classes, styles);
}
updateStylingMap(stylingContext, classes, styles);
}
/* START OF HACK BLOCK */
/*
* HACK
* The code below is a quick and dirty implementation of the host style binding so that we can make
* progress on TestBed. Once the correct implementation is created this code should be removed.
*/
interface HostStylingHack {
classDeclarations: string[];
styleDeclarations: string[];
styleSanitizer: StyleSanitizeFn|null;
}
type HostStylingHackMap = Map<{}, HostStylingHack>;
function hackImplementationOfElementStyling(
classDeclarations: (string | boolean | InitialStylingFlags)[] | null,
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView()) as RElement;
ngDevMode && assertDefined(node, 'expecting parent DOM node');
const hostStylingHackMap: HostStylingHackMap =
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
const squashedClassDeclarations = hackSquashDeclaration(classDeclarations);
hostStylingHackMap.set(directive, {
classDeclarations: squashedClassDeclarations,
styleDeclarations: hackSquashDeclaration(styleDeclarations), styleSanitizer
});
hackSetStaticClasses(node, squashedClassDeclarations);
}
function hackSetStaticClasses(node: RElement, classDeclarations: (string | boolean)[]) {
// Static classes need to be set here because static classes don't generate
// elementClassProp instructions.
const lView = getLView();
const staticClassStartIndex =
classDeclarations.indexOf(InitialStylingFlags.VALUES_MODE as any) + 1;
const renderer = lView[RENDERER];
for (let i = staticClassStartIndex; i < classDeclarations.length; i += 2) {
const className = classDeclarations[i] as string;
const value = classDeclarations[i + 1];
// if value is true, then this is a static class and we should set it now.
// class bindings are set separately in elementClassProp.
if (value === true) {
if (isProceduralRenderer(renderer)) {
renderer.addClass(node, className);
} else {
const classList = (node as HTMLElement).classList;
classList.add(className);
}
}
}
}
function hackSquashDeclaration(declarations: (string | boolean | InitialStylingFlags)[] | null):
string[] {
// assume the array is correct. This should be fine for View Engine compatibility.
return declarations || [] as any;
}
function hackImplementationOfElementClassProp(
index: number, classIndex: number, value: boolean | PlayerFactory, directive: {}): void {
const lView = getLView();
const node = getNativeByIndex(index, lView);
ngDevMode && assertDefined(node, 'could not locate node');
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
const className = hostStylingHack.classDeclarations[classIndex];
const renderer = lView[RENDERER];
if (isProceduralRenderer(renderer)) {
value ? renderer.addClass(node, className) : renderer.removeClass(node, className);
} else {
const classList = (node as HTMLElement).classList;
value ? classList.add(className) : classList.remove(className);
}
}
function hackImplementationOfElementStylingApply(index: number, directive?: {}): void {
// Do nothing because the hack implementation is eager.
}
function hackImplementationOfElementStyleProp(
index: number, styleIndex: number, value: string | null, suffix?: string,
directive?: {}): void {
const lView = getLView();
const node = getNativeByIndex(index, lView);
ngDevMode && assertDefined(node, 'could not locate node');
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
const styleName = hostStylingHack.styleDeclarations[styleIndex];
const renderer = lView[RENDERER];
setStyle(node, styleName, value as string, renderer, null);
}
function hackImplementationOfElementStylingMap<T>(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null, directive?: {}): void {
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
}
/* END OF HACK BLOCK */
/* END OF HACK BLOCK */
//////////////////////////
//// Text
//////////////////////////
@ -1967,9 +1992,11 @@ export function containerRefreshStart(index: number): void {
lView[index + HEADER_OFFSET][ACTIVE_INDEX] = 0;
// We need to execute init hooks here so ngOnInit hooks are called in top level views
// before they are called in embedded views (for backwards compatibility).
executeInitHooks(lView, tView, getCheckNoChangesMode());
if (!getCheckNoChangesMode()) {
// We need to execute init hooks here so ngOnInit hooks are called in top level views
// before they are called in embedded views (for backwards compatibility).
executeInitHooks(lView, tView, getCreationMode());
}
}
/**
@ -2014,7 +2041,9 @@ function refreshDynamicEmbeddedViews(lView: LView) {
const dynamicViewData = container[VIEWS][i];
// The directives and pipes are not needed here as an existing view is only being refreshed.
ngDevMode && assertDefined(dynamicViewData[TVIEW], 'TView must be allocated');
renderEmbeddedTemplate(dynamicViewData, dynamicViewData[TVIEW], dynamicViewData[CONTEXT] !);
renderEmbeddedTemplate(
dynamicViewData, dynamicViewData[TVIEW], dynamicViewData[CONTEXT] !,
RenderFlags.Update);
}
}
}
@ -2089,14 +2118,13 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
enterView(viewToRender, viewToRender[TVIEW].node);
}
if (lContainer) {
if (isCreationMode(viewToRender)) {
if (getCreationMode()) {
// it is a new view, insert it into collection of views for a given container
insertView(viewToRender, lContainer, lView, lContainer[ACTIVE_INDEX] !, -1);
}
lContainer[ACTIVE_INDEX] !++;
}
return isCreationMode(viewToRender) ? RenderFlags.Create | RenderFlags.Update :
RenderFlags.Update;
return getRenderFlags(viewToRender);
}
/**
@ -2130,12 +2158,7 @@ function getOrCreateEmbeddedTView(
export function embeddedViewEnd(): void {
const lView = getLView();
const viewHost = lView[HOST_NODE];
if (isCreationMode(lView)) {
refreshDescendantViews(lView); // creation mode pass
lView[FLAGS] &= ~LViewFlags.CreationMode;
}
refreshDescendantViews(lView); // update mode pass
refreshDescendantViews(lView, null);
leaveView(lView[PARENT] !);
setPreviousOrParentTNode(viewHost !);
setIsParent(false);
@ -2147,8 +2170,9 @@ export function embeddedViewEnd(): void {
* Refreshes components by entering the component view and processing its bindings, queries, etc.
*
* @param adjustedElementIndex Element index in LView[] (adjusted for HEADER_OFFSET)
* @param rf The render flags that should be used to process this template
*/
export function componentRefresh<T>(adjustedElementIndex: number): void {
export function componentRefresh<T>(adjustedElementIndex: number, rf: RenderFlags | null): void {
const lView = getLView();
ngDevMode && assertDataInRange(lView, adjustedElementIndex);
const hostView = getComponentViewByIndex(adjustedElementIndex, lView);
@ -2157,7 +2181,7 @@ export function componentRefresh<T>(adjustedElementIndex: number): void {
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
syncViewWithBlueprint(hostView);
checkView(hostView, hostView[CONTEXT]);
detectChangesInternal(hostView, hostView[CONTEXT], rf);
}
}
@ -2437,7 +2461,7 @@ export function tick<T>(component: T): void {
function tickRootContext(rootContext: RootContext) {
for (let i = 0; i < rootContext.components.length; i++) {
const rootComponent = rootContext.components[i];
renderComponentOrTemplate(readPatchedLView(rootComponent) !, rootComponent);
renderComponentOrTemplate(readPatchedLView(rootComponent) !, rootComponent, RenderFlags.Update);
}
}
@ -2455,21 +2479,7 @@ function tickRootContext(rootContext: RootContext) {
* @param component The component which the change detection should be performed on.
*/
export function detectChanges<T>(component: T): void {
const view = getComponentViewByInstance(component) !;
detectChangesInternal<T>(view, component);
}
export function detectChangesInternal<T>(view: LView, context: T) {
const rendererFactory = view[RENDERER_FACTORY];
if (rendererFactory.begin) rendererFactory.begin();
if (isCreationMode(view)) {
checkView(view, context); // creation mode pass
}
checkView(view, context); // update mode pass
if (rendererFactory.end) rendererFactory.end();
detectChangesInternal(getComponentViewByInstance(component) !, component, null);
}
/**
@ -2516,7 +2526,7 @@ export function checkNoChangesInRootView(lView: LView): void {
}
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
export function checkView<T>(hostView: LView, component: T) {
export function detectChangesInternal<T>(hostView: LView, component: T, rf: RenderFlags | null) {
const hostTView = hostView[TVIEW];
const oldView = enterView(hostView, hostView[HOST_NODE]);
const templateFn = hostTView.template !;
@ -2524,23 +2534,27 @@ export function checkView<T>(hostView: LView, component: T) {
try {
namespaceHTML();
createViewQuery(viewQuery, hostView, component);
templateFn(getRenderFlags(hostView), component);
refreshDescendantViews(hostView);
updateViewQuery(viewQuery, hostView, component);
createViewQuery(viewQuery, rf, hostView[FLAGS], component);
templateFn(rf || getRenderFlags(hostView), component);
refreshDescendantViews(hostView, rf);
updateViewQuery(viewQuery, hostView[FLAGS], component);
} finally {
leaveView(oldView);
leaveView(oldView, rf === RenderFlags.Create);
}
}
function createViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
if (viewQuery && isCreationMode(view)) {
function createViewQuery<T>(
viewQuery: ComponentQuery<{}>| null, renderFlags: RenderFlags | null, viewFlags: LViewFlags,
component: T): void {
if (viewQuery && (renderFlags === RenderFlags.Create ||
(renderFlags === null && (viewFlags & LViewFlags.CreationMode)))) {
viewQuery(RenderFlags.Create, component);
}
}
function updateViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
if (viewQuery && !isCreationMode(view)) {
function updateViewQuery<T>(
viewQuery: ComponentQuery<{}>| null, flags: LViewFlags, component: T): void {
if (viewQuery && flags & RenderFlags.Update) {
viewQuery(RenderFlags.Update, component);
}
}
@ -2868,6 +2882,10 @@ function initializeTNodeInputs(tNode: TNode | null) {
return null;
}
export function delegateToClassInput(tNode: TNode) {
return tNode.flags & TNodeFlags.hasClassInput;
}
/**
* Returns the current OpaqueViewState instance.

View File

@ -23,14 +23,6 @@ export const VIEWS = 1;
// As we already have these constants in LView, we don't need to re-create them.
export const NATIVE = 6;
export const RENDER_PARENT = 7;
// Because interfaces in TS/JS cannot be instanceof-checked this means that we
// need to rely on predictable characteristics of data-structures to check if they
// are what we expect for them to be. The `LContainer` interface code below has a
// fixed length and the constant value below references that. Using the length value
// below we can predictably gaurantee that we are dealing with an `LContainer` array.
// This value MUST be kept up to date with the length of the `LContainer` array
// interface below so that runtime type checking can work.
export const LCONTAINER_LENGTH = 8;
/**
* The state associated with a container.

View File

@ -344,4 +344,8 @@ export type PipeTypeList =
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;
export const unusedValueExportToPlacateAjd = 1;
export const enum InitialStylingFlags {
VALUES_MODE = 0b1,
}

View File

@ -26,6 +26,7 @@ export interface RelativeInjectorLocation { __brand__: 'RelativeInjectorLocation
export const enum RelativeInjectorLocationFlags {
InjectorIndexMask = 0b111111111111111,
AcrossHostBoundary = 0b1000000000000000,
ViewOffsetShift = 16,
NO_PARENT = -1,
}

View File

@ -54,7 +54,7 @@ export const enum TNodeProviderIndexes {
CptViewProvidersCountShifter = 0b00000000000000010000000000000000,
}
/**
* A set of marker values to be used in the attributes arrays. These markers indicate that some
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
* items are not regular attributes and the processing should be adapted accordingly.
*/
export const enum AttributeMarker {
@ -65,50 +65,13 @@ export const enum AttributeMarker {
*/
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.:
* [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:
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
*/
SelectOnly = 3,
SelectOnly = 1
}
/**

View File

@ -9,199 +9,130 @@ import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {RElement} from '../interfaces/renderer';
import {PlayerContext} from './player';
/**
* The styling context acts as a styling manifest (shaped as an array) for determining which
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
* and `updateClassProp` functions. It also stores the static style/class values that were
* extracted from the template by the compiler.
* and `updateClassProp` functions. There are also two initialization functions
* `allocStylingContext` and `createStylingContextTemplate` which are used to initialize
* and/or clone the context.
*
* A context is created by Angular when:
* 1. An element contains static styling values (like style="..." or class="...")
* 2. An element contains single property binding values (like [style.prop]="x" or
* [class.prop]="y")
* 3. An element contains multi property binding values (like [style]="x" or [class]="y")
* 4. A directive contains host bindings for static, single or multi styling properties/bindings.
* 5. An animation player is added to an element via `addPlayer`
* The context is an array where the first two cells are used for static data (initial styling)
* and dirty flags / index offsets). The remaining set of cells is used for multi (map) and single
* (prop) style values.
*
* Note that even if an element contains static styling then this context will be created and
* attached to it. The reason why this happens (instead of treating styles/classes as regular
* HTML attributes) is because the style/class bindings must be able to default themselves back
* to their respective static values when they are set to null.
* each value from here onwards is mapped as so:
* [i] = mutation/type flag for the style/class value
* [i + 1] = prop string (or null incase it has been removed)
* [i + 2] = value string (or null incase it has been removed)
*
* There are three types of styling types stored in this context:
* initial: any styles that are passed in once the context is created
* (these are stored in the first cell of the array and the first
* value of this array is always `null` even if no initial styling exists.
* the `null` value is there so that any new styles have a parent to point
* to. This way we can always assume that there is a parent.)
*
* single: any styles that are updated using `updateStyleProp` or `updateClassProp` (fixed set)
*
* multi: any styles that are updated using `updateStylingMap` (dynamic set)
*
* Note that context is only used to collect style information. Only when `renderStyling`
* is called is when the styling payload will be rendered (or built as a key/value map).
*
* When the context is created, depending on what initial styling values are passed in, the
* context itself will be pre-filled with slots based on the initial style properties. Say
* for example we have a series of initial styles that look like so:
*
* style="width:100px; height:200px;"
* class="foo"
*
* Then the initial state of the context (once initialized) will look like so:
*
* Say for example we have this:
* ```
* <!-- when myWidthExp=null then a width of "100px"
* will be used a default value for width -->
* <div style="width:100px" [style.width]="myWidthExp"></div>
* ```
*
* Even in the situation where there are no bindings, the static styling is still placed into the
* context because there may be another directive on the same element that has styling.
*
* When Angular initializes styling data for an element then it will first register the static
* styling values on the element using one of these two instructions:
*
* 1. elementStart or element (within the template function of a component)
* 2. elementHostAttrs (for directive host bindings)
*
* In either case, a styling context will be created and stored within an element's LViewData. Once
* the styling context is created then single and multi properties can stored within it. For this to
* happen, the following function needs to be called:
*
* `elementStyling` (called with style properties, class properties and a sanitizer + a directive
* instance).
*
* When this instruction is called it will populate the styling context with the provided style
* and class names into the context.
*
* The context itself looks like this:
*
* context = [
* // 0-8: header values (about 8 entries of configuration data)
* // 9+: this is where each entry is stored:
* element,
* playerContext | null,
* styleSanitizer | null,
* [null, '100px', '200px', true], // property names are not needed since they have already been
* written to DOM.
*
* configMasterVal,
* 1, // this instructs how many `style` values there are so that class index values can be
* offsetted
* { classOne: true, classTwo: false } | 'classOne classTwo' | null // last class value provided
* into updateStylingMap
* { styleOne: '100px', styleTwo: 0 } | null // last style value provided into updateStylingMap
*
* // 8
* 'width',
* pointers(1, 15); // Point to static `width`: `100px` and multi `width`.
* null,
*
* // 11
* 'height',
* pointers(2, 18); // Point to static `height`: `200px` and multi `height`.
* null,
*
* // 14
* 'foo',
* pointers(1, 21); // Point to static `foo`: `true` and multi `foo`.
* null,
*
* // 17
* 'width',
* pointers(1, 6); // Point to static `width`: `100px` and single `width`.
* null,
*
* // 21
* 'height',
* pointers(2, 9); // Point to static `height`: `200px` and single `height`.
* null,
*
* // 24
* 'foo',
* pointers(3, 12); // Point to static `foo`: `true` and single `foo`.
* null,
* ]
*
* Let's say we have the following template code:
*
* ```
* <div class="foo bar"
* style="width:200px; color:red"
* [style.width]="myWidthExp"
* [style.height]="myHeightExp"
* [class.baz]="myBazExp">
* ```
*
* The context generated from these values will look like this (note that
* for each binding name (the class and style bindings) the values will
* be inserted twice into the array (once for single property entries) and
* another for multi property entries).
*
* context = [
* // 0-8: header values (about 8 entries of configuration data)
* // 9+: this is where each entry is stored:
*
* // SINGLE PROPERTIES
* configForWidth,
* 'width'
* myWidthExp, // the binding value not the binding itself
* 0, // the directive owner
*
* configForHeight,
* 'height'
* myHeightExp, // the binding value not the binding itself
* 0, // the directive owner
*
* configForBazClass,
* 'baz
* myBazClassExp, // the binding value not the binding itself
* 0, // the directive owner
*
* // MULTI PROPERTIES
* configForWidth,
* 'width'
* myWidthExp, // the binding value not the binding itself
* 0, // the directive owner
*
* configForHeight,
* 'height'
* myHeightExp, // the binding value not the binding itself
* 0, // the directive owner
*
* configForBazClass,
* 'baz
* myBazClassExp, // the binding value not the binding itself
* 0, // the directive owner
* ]
*
* The configuration values are left out of the example above because
* the ordering of them could change between code patches. Please read the
* documentation below to get a better understand of what the configuration
* values are and how they work.
*
* Each time a binding property is updated (whether it be through a single
* property instruction like `elementStyleProp`, `elementClassProp` or
* `elementStylingMap`) then the values in the context will be updated as
* well.
*
* If for example `[style.width]` updates to `555px` then its value will be reflected
* in the context as so:
*
* context = [
* // ...
* configForWidth, // this will be marked DIRTY
* 'width'
* '555px',
* 0,
* //..
* ]
*
* The context and directive data will also be marked dirty.
*
* Despite the context being updated, nothing has been rendered on screen (not styles or
* classes have been set on the element). To kick off rendering for an element the following
* function needs to be run `elementStylingApply`.
*
* `elementStylingApply` will run through the context and find each dirty value and render them onto
* the element. Once complete, all styles/classes will be set to clean. Because of this, the render
* function will now know not to rerun itself again if called again unless new style/class values
* have changed.
*
* ## Directives
* Directives style values (which are provided through host bindings) are also supported and
* housed within the same styling context as are template-level style/class properties/bindings.
* Both directive-level and template-level styling bindings share the same context.
*
* Each of the following instructions supports accepting a directive instance as an input parameter:
*
* - `elementHostAttrs`
* - `elementStyling`
* - `elementStyleProp`
* - `elementClassProp`
* - `elementStylingMap`
* - `elementStylingApply`
*
* Each time a directiveRef is passed in, it will be converted into an index by examining the
* directive registry (which lives in the context configuration area). The index is then used
* to help single style properties figure out where a value is located in the context.
*
* If two directives or a directive + a template binding both write to the same style/class
* binding then the styling context code will decide which one wins based on the following
* rule:
*
* 1. If the template binding has a value then it always wins
* 2. If not then whichever first-registered directive that has that value first will win
*
* The code example helps make this clear:
*
* ```
* <div [style.width]="myWidth" [my-width-directive]="'600px">
* @Directive({ selector: '[my-width-directive' ]})
* class MyWidthDirective {
* @Input('my-width-directive')
* @HostBinding('style.width')
* public width = null;
* function pointers(staticIndex: number, dynamicIndex: number) {
* // combine the two indices into a single word.
* return (staticIndex << StylingFlags.BitCountSize) |
* (dynamicIndex << (StylingIndex.BitCountSize + StylingFlags.BitCountSize));
* }
* ```
*
* Since there is a style binding for width present on the element (`[style.width]`) then
* it will always win over the width binding that is present as a host binding within
* the `MyWidthDirective`. However, if `[style.width]` renders as `null` (so `myWidth=null`)
* then the `MyWidthDirective` will be able to write to the `width` style within the context.
* Simply put, whichever directive writes to a value ends up having ownership of it.
* The values are duplicated so that space is set aside for both multi ([style] and [class])
* and single ([style.prop] or [class.named]) values. The respective config values
* (configValA, configValB, etc...) are a combination of the StylingFlags with two index
* values: the `initialIndex` (which points to the index location of the style value in
* the initial styles array in slot 0) and the `dynamicIndex` (which points to the
* matching single/multi index position in the context array for the same prop).
*
* The way in which the ownership is facilitated is through index value. The earliest directives
* get the smallest index values (with 0 being reserved for the template element bindings). Each
* time a value is written from a directive or the template bindings, the value itself gets
* assigned the directive index value in its data. If another directive writes a value again then
* its directive index gets compared against the directive index that exists on the element. Only
* when the new value's directive index is less than the existing directive index then the new
* value will be written to the context.
*
* Each directive also has its own sanitizer and dirty flags. These values are consumed within the
* rendering function.
* This means that every time `updateStyleProp` or `updateClassProp` are called then they
* must be called using an index value (not a property string) which references the index
* value of the initial style prop/class when the context was created. This also means that
* `updateStyleProp` or `updateClassProp` cannot be called with a new property (only
* `updateStylingMap` can include new CSS properties that will be added to the context).
*/
export interface StylingContext extends
Array<{[key: string]: any}|number|string|boolean|RElement|StyleSanitizeFn|PlayerContext|null> {
export interface StylingContext extends Array<InitialStyles|{[key: string]: any}|number|string|
boolean|RElement|StyleSanitizeFn|PlayerContext|null> {
/**
* Location of animation context (which contains the active players) for this element styling
* context.
*/
[StylingIndex.PlayerContext]: PlayerContext|null;
/**
* The style sanitizer that is used within this context
*/
[StylingIndex.StyleSanitizerPosition]: StyleSanitizeFn|null;
/**
* Location of initial data shared by all instances of this style.
*/
[StylingIndex.InitialStylesPosition]: InitialStyles;
/**
* A numeric value representing the configuration status (whether the context is dirty or not)
* mixed together (using bit shifting) with a index value which tells the starting index value
@ -209,27 +140,12 @@ export interface StylingContext extends
*/
[StylingIndex.MasterFlagPosition]: number;
/**
* Location of the collection of directives for this context
*/
[StylingIndex.DirectiveRegistryPosition]: DirectiveRegistryValues;
/**
* Location of all static styles values
*/
[StylingIndex.InitialStyleValuesPosition]: InitialStylingValues;
/**
* Location of all static class values
*/
[StylingIndex.InitialClassValuesPosition]: InitialStylingValues;
/**
* A numeric value representing the class index offset value. Whenever a single class is
* applied (using `elementClassProp`) it should have an styling index value that doesn't
* need to take into account any style values that exist in the context.
*/
[StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues;
[StylingIndex.ClassOffsetPosition]: number;
/**
* Location of element that is used as a target for this context.
@ -240,206 +156,24 @@ export interface StylingContext extends
* The last class value that was interpreted by elementStylingMap. This is cached
* So that the algorithm can exit early incase the value has not changed.
*/
[StylingIndex.CachedClassValueOrInitialClassString]: {[key: string]: any}|string|(string)[]|null;
[StylingIndex.PreviousOrCachedMultiClassValue]: {[key: string]: any}|string|null;
/**
* The last style value that was interpreted by elementStylingMap. This is cached
* So that the algorithm can exit early incase the value has not changed.
*/
[StylingIndex.CachedStyleValue]: {[key: string]: any}|(string)[]|null;
/**
* Location of animation context (which contains the active players) for this element styling
* context.
*/
[StylingIndex.PlayerContext]: PlayerContext|null;
[StylingIndex.PreviousMultiStyleValue]: {[key: string]: any}|null;
}
/**
* Used as a styling array to house static class and style values that were extracted
* by the compiler and placed in the animation context via `elementStart` and
* `elementHostAttrs`.
* The initial styles is populated whether or not there are any initial styles passed into
* the context during allocation. The 0th value must be null so that index values of `0` within
* the context flags can always point to a null value safely when nothing is set.
*
* See [InitialStylingValuesIndex] for a breakdown of how all this works.
* All other entries in this array are of `string` value and correspond to the values that
* were extracted from the `style=""` attribute in the HTML code for the provided template.
*/
export interface InitialStylingValues extends Array<string|boolean|null> { [0]: null; }
/**
* Used as an offset/position index to figure out where initial styling
* values are located.
*
* Used as a reference point to provide markers to all static styling
* values (the initial style and class values on an element) within an
* array within the StylingContext. This array contains key/value pairs
* where the key is the style property name or className and the value is
* the style value or whether or not a class is present on the elment.
*
* The first value is also always null so that a initial index value of
* `0` will always point to a null value.
*
* If a <div> elements contains a list of static styling values like so:
*
* <div class="foo bar baz" style="width:100px; height:200px;">
*
* Then the initial styles for that will look like so:
*
* Styles:
* StylingContext[InitialStylesIndex] = [
* null, 'width', '100px', height, '200px'
* ]
*
* Classes:
* StylingContext[InitialStylesIndex] = [
* null, 'foo', true, 'bar', true, 'baz', true
* ]
*
* Initial style and class entries have their own arrays. This is because
* it's easier to add to the end of one array and not then have to update
* every context entries' pointer index to the newly offseted values.
*
* When property bindinds are added to a context then initial style/class
* values will also be inserted into the array. This is to create a space
* in the situation when a follow-up directive inserts static styling into
* the array. By default style values are `null` and class values are
* `false` when inserted by property bindings.
*
* For example:
* <div class="foo bar baz"
* [class.car]="myCarExp"
* style="width:100px; height:200px;"
* [style.opacity]="myOpacityExp">
*
* Will construct initial styling values that look like:
*
* Styles:
* StylingContext[InitialStylesIndex] = [
* null, 'width', '100px', height, '200px', 'opacity', null
* ]
*
* Classes:
* StylingContext[InitialStylesIndex] = [
* null, 'foo', true, 'bar', true, 'baz', true, 'car', false
* ]
*
* Now if a directive comes along and introduces `car` as a static
* class value or `opacity` then those values will be filled into
* the initial styles array.
*
* For example:
*
* @Directive({
* selector: 'opacity-car-directive',
* host: {
* 'style': 'opacity:0.5',
* 'class': 'car'
* }
* })
* class OpacityCarDirective {}
*
* This will render itself as:
*
* Styles:
* StylingContext[InitialStylesIndex] = [
* null, 'width', '100px', height, '200px', 'opacity', null
* ]
*
* Classes:
* StylingContext[InitialStylesIndex] = [
* null, 'foo', true, 'bar', true, 'baz', true, 'car', false
* ]
*/
export const enum InitialStylingValuesIndex {
KeyValueStartPosition = 1,
PropOffset = 0,
ValueOffset = 1,
Size = 2
}
/**
* An array located in the StylingContext that houses all directive instances and additional
* data about them.
*
* Each entry in this array represents a source of where style/class binding values could
* come from. By default, there is always at least one directive here with a null value and
* that represents bindings that live directly on an element (not host bindings).
*
* Each successive entry in the array is an actual instance of an array as well as some
* additional info.
*
* An entry within this array has the following values:
* [0] = The instance of the directive (or null when it is not a directive, but a template binding
* source)
* [1] = The pointer that tells where the single styling (stuff like [class.foo] and [style.prop])
* offset values are located. This value will allow for a binding instruction to find exactly
* where a style is located.
* [2] = Whether or not the directive has any styling values that are dirty. This is used as
* reference within the renderClassAndStyleBindings function to decide whether to skip
* iterating through the context when rendering is executed.
* [3] = The styleSanitizer instance that is assigned to the directive. Although it's unlikely,
* a directive could introduce its own special style sanitizer and for this reach each
* directive will get its own space for it (if null then the very first sanitizer is used).
*
* Each time a new directive is added it will insert these four values at the end of the array.
* When this array is examined (using indexOf) then the resulting directiveIndex will be resolved
* by dividing the index value by the size of the array entries (so if DirA is at spot 8 then its
* index will be 2).
*/
export interface DirectiveRegistryValues extends Array<null|{}|boolean|number|StyleSanitizeFn> {
[DirectiveRegistryValuesIndex.DirectiveValueOffset]: null;
[DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset]: number;
[DirectiveRegistryValuesIndex.DirtyFlagOffset]: boolean;
[DirectiveRegistryValuesIndex.StyleSanitizerOffset]: StyleSanitizeFn|null;
}
/**
* An enum that outlines the offset/position values for each directive entry and its data
* that are housed inside of [DirectiveRegistryValues].
*/
export const enum DirectiveRegistryValuesIndex {
DirectiveValueOffset = 0,
SinglePropValuesIndexOffset = 1,
DirtyFlagOffset = 2,
StyleSanitizerOffset = 3,
Size = 4
}
/**
* An array that contains the index pointer values for every single styling property
* that exists in the context and for every directive. It also contains the total
* single styles and single classes that exists in the context as the first two values.
*
* Let's say we have the following template code:
*
* <div [style.width]="myWidth"
* [style.height]="myHeight"
* [class.flipped]="flipClass"
* directive-with-opacity>
* directive-with-foo-bar-classes>
*
* We have two directive and template-binding sources,
* 2 + 1 styles and 1 + 1 classes. When the bindings are
* registered the SinglePropOffsets array will look like so:
*
* s_0/c_0 = template directive value
* s_1/c_1 = directive one (directive-with-opacity)
* s_2/c_2 = directive two (directive-with-foo-bar-classes)
*
* [3, 2, 2, 1, s_00, s01, c_01, 1, 0, s_10, 0, 1, c_20
*/
export interface SinglePropOffsetValues extends Array<number> {
[SinglePropOffsetValuesIndex.StylesCountPosition]: number;
[SinglePropOffsetValuesIndex.ClassesCountPosition]: number;
}
/**
* An enum that outlines the offset/position values for each single prop/class entry
* that are housed inside of [SinglePropOffsetValues].
*/
export const enum SinglePropOffsetValuesIndex {
StylesCountPosition = 0,
ClassesCountPosition = 1,
ValueStartPosition = 2
}
export interface InitialStyles extends Array<string|null|boolean> { [0]: null; }
/**
* Used to set the context to be dirty or not both on the master flag (position 1)
@ -447,49 +181,47 @@ export const enum SinglePropOffsetValuesIndex {
*/
export const enum StylingFlags {
// Implies no configurations
None = 0b000000,
None = 0b00000,
// Whether or not the entry or context itself is dirty
Dirty = 0b000001,
Dirty = 0b00001,
// Whether or not this is a class-based assignment
Class = 0b000010,
Class = 0b00010,
// Whether or not a sanitizer was applied to this property
Sanitize = 0b000100,
Sanitize = 0b00100,
// Whether or not any player builders within need to produce new players
PlayerBuildersDirty = 0b001000,
PlayerBuildersDirty = 0b01000,
// If NgClass is present (or some other class handler) then it will handle the map expressions and
// initial classes
OnlyProcessSingleClasses = 0b010000,
OnlyProcessSingleClasses = 0b10000,
// The max amount of bits used to represent these configuration values
BindingAllocationLocked = 0b100000,
BitCountSize = 6,
// There are only six bits here
BitMask = 0b111111
BitCountSize = 5,
// There are only five bits here
BitMask = 0b11111
}
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
export const enum StylingIndex {
// Index of location where the start of single properties are stored. (`updateStyleProp`)
MasterFlagPosition = 0,
// Position of where the registered directives exist for this styling context
DirectiveRegistryPosition = 1,
// Position of where the initial styles are stored in the styling context
InitialStyleValuesPosition = 2,
InitialClassValuesPosition = 3,
PlayerContext = 0,
// Position of where the style sanitizer is stored within the styling context
StyleSanitizerPosition = 1,
// Position of where the initial styles are stored in the styling context
InitialStylesPosition = 2,
// Index of location where the start of single properties are stored. (`updateStyleProp`)
MasterFlagPosition = 3,
// Index of location where the class index offset value is located
SinglePropOffsetPositions = 4,
ClassOffsetPosition = 4,
// Position of where the initial styles are stored in the styling context
// This index must align with HOST, see interfaces/view.ts
ElementPosition = 5,
// Position of where the last string-based CSS class value was stored (or a cached version of the
// initial styles when a [class] directive is present)
CachedClassValueOrInitialClassString = 6,
PreviousOrCachedMultiClassValue = 6,
// Position of where the last string-based CSS class value was stored
CachedStyleValue = 7,
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
// Position of where the initial styles are stored in the styling context
PlayerContext = 8,
PreviousMultiStyleValue = 7,
// Location of single (prop) value entries are stored within the context
SingleStylesStartPosition = 9,
SingleStylesStartPosition = 8,
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
FlagsOffset = 0,
PropertyOffset = 1,
ValueOffset = 2,
@ -501,16 +233,3 @@ export const enum StylingIndex {
// The binary digit value as a mask
BitMask = 0b11111111111111, // 14 bits
}
/**
* An enum that outlines the bit flag data for directive owner and player index
* values that exist within en entry that lives in the StylingContext.
*
* The values here split a number value into two sets of bits:
* - The first 16 bits are used to store the directiveIndex that owns this style value
* - The other 16 bits are used to store the playerBuilderIndex that is attached to this style
*/
export const enum DirectiveOwnerAndPlayerBuilderIndex {
BitCountSize = 16,
BitMask = 0b1111111111111111
}

View File

@ -221,25 +221,16 @@ export const enum LViewFlags {
* back into the parent view, `data` will be defined and `creationMode` will be
* improperly reported as false.
*/
CreationMode = 0b000000001,
/**
* Whether or not this LView instance is on its first processing pass.
*
* An LView instance is considered to be on its "first pass" until it
* has completed one creation mode run and one update mode run. At this
* time, the flag is turned off.
*/
FirstLViewPass = 0b000000010,
CreationMode = 0b0000001,
/** Whether this view has default change detection strategy (checks always) or onPush */
CheckAlways = 0b000000100,
CheckAlways = 0b0000010,
/** Whether or not this view is currently dirty (needing check) */
Dirty = 0b000001000,
Dirty = 0b0000100,
/** Whether or not this view is currently attached to change detection tree. */
Attached = 0b000010000,
Attached = 0b0001000,
/**
* Whether or not the init hooks have run.
@ -248,13 +239,13 @@ export const enum LViewFlags {
* runs OR the first cR() instruction that runs (so inits are run for the top level view before
* any embedded views).
*/
RunInit = 0b000100000,
RunInit = 0b0010000,
/** Whether or not this view is destroyed. */
Destroyed = 0b001000000,
Destroyed = 0b0100000,
/** Whether or not this view is the root view */
IsRoot = 0b010000000,
IsRoot = 0b1000000,
}
/**

View File

@ -134,13 +134,10 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
encapsulation: ViewEncapsulation;
viewProviders: Provider[]|null;
interpolation?: [string, string];
changeDetection?: ChangeDetectionStrategy;
}
export type ViewEncapsulation = number;
export type ChangeDetectionStrategy = number;
export interface R3QueryMetadataFacade {
propertyName: string;
first: boolean;

View File

@ -7,15 +7,14 @@
*/
import {ComponentType} from '..';
import {resolveForwardRef} from '../../di/forward_ref';
import {Query} from '../../metadata/di';
import {Component, Directive} from '../../metadata/directives';
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
import {ViewEncapsulation} from '../../metadata/view';
import {Type} from '../../type';
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
import {stringify} from '../../util';
import {EMPTY_ARRAY} from '../definition';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from '../fields';
import {stringify} from '../util';
import {R3DirectiveMetadataFacade, getCompilerFacade} from './compiler_facade';
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from './compiler_facade_interface';
@ -62,7 +61,6 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
animations: metadata.animations,
viewQueries: extractQueriesMetadata(type, getReflect().propMetadata(type), isViewQuery),
directives: [],
changeDetection: metadata.changeDetection,
pipes: new Map(),
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated,
interpolation: metadata.interpolation,
@ -155,8 +153,10 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
};
}
const EMPTY_OBJ = {};
function convertToR3QueryPredicate(selector: any): any|string[] {
return typeof selector === 'string' ? splitByComma(selector) : resolveForwardRef(selector);
return typeof selector === 'string' ? splitByComma(selector) : selector;
}
export function convertToR3QueryMetadata(propertyName: string, ann: Query): R3QueryMetadataFacade {

View File

@ -78,7 +78,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵload': r3.load,
'ɵprojection': r3.projection,
'ɵelementProperty': r3.elementProperty,
'ɵcomponentHostSyntheticProperty': r3.componentHostSyntheticProperty,
'ɵpipeBind1': r3.pipeBind1,
'ɵpipeBind2': r3.pipeBind2,
'ɵpipeBind3': r3.pipeBind3,
@ -91,7 +90,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵregisterContentQuery': r3.registerContentQuery,
'ɵreference': r3.reference,
'ɵelementStyling': r3.elementStyling,
'ɵelementHostAttrs': r3.elementHostAttrs,
'ɵelementStylingMap': r3.elementStylingMap,
'ɵelementStyleProp': r3.elementStyleProp,
'ɵelementStylingApply': r3.elementStylingApply,

View File

@ -107,12 +107,10 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
ngModuleDef = getCompilerFacade().compileNgModule(
angularCoreEnv, `ng://${moduleType.name}/ngModuleDef.js`, {
type: moduleType,
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY, resolveForwardRef),
declarations: declarations.map(resolveForwardRef),
imports: flatten(ngModule.imports || EMPTY_ARRAY, resolveForwardRef)
.map(expandModuleWithProviders),
exports: flatten(ngModule.exports || EMPTY_ARRAY, resolveForwardRef)
.map(expandModuleWithProviders),
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY),
declarations: declarations,
imports: flatten(ngModule.imports || EMPTY_ARRAY).map(expandModuleWithProviders),
exports: flatten(ngModule.exports || EMPTY_ARRAY).map(expandModuleWithProviders),
emitInline: true,
});
}
@ -134,8 +132,8 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
deps: reflectDependencies(moduleType),
providers: ngModule.providers || EMPTY_ARRAY,
imports: [
(ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef),
(ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef),
ngModule.imports || EMPTY_ARRAY,
ngModule.exports || EMPTY_ARRAY,
],
};
ngInjectorDef = getCompilerFacade().compileInjector(
@ -156,8 +154,8 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void {
const errors: string[] = [];
ngModuleDef.declarations.forEach(verifyDeclarationsHaveDefinitions);
const combinedDeclarations: Type<any>[] = [
...ngModuleDef.declarations.map(resolveForwardRef), //
...flatten(ngModuleDef.imports.map(computeCombinedExports), resolveForwardRef),
...ngModuleDef.declarations, //
...flatten(ngModuleDef.imports.map(computeCombinedExports)),
];
ngModuleDef.exports.forEach(verifyExportsAreDeclaredOrReExported);
ngModuleDef.declarations.forEach(verifyDeclarationIsUnique);

View File

@ -15,7 +15,7 @@ import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeType} from './node_assert';
import {findComponentView, getNativeByTNode, isLContainer, isRootView, readElementValue, stringify} from './util';
import {getNativeByTNode, isLContainer, isRootView, readElementValue, stringify} from './util';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
@ -195,6 +195,24 @@ function walkTNodeTree(
}
}
/**
* Given a current view, finds the nearest component's host (LElement).
*
* @param lView LView for which we want a host element node
* @returns The host node
*/
export function findComponentView(lView: LView): LView {
let rootTNode = lView[HOST_NODE];
while (rootTNode && rootTNode.type === TNodeType.View) {
ngDevMode && assertDefined(lView[PARENT], 'lView.parent');
lView = lView[PARENT] !;
rootTNode = lView[HOST_NODE];
}
return lView;
}
/**
* NOTE: for performance reasons, the possible actions are inlined within the function instead of
* being passed as an argument.

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