Compare commits

..

192 Commits

Author SHA1 Message Date
5c5fe34241 release: cut the v7.2.2 release 2019-01-22 15:27:48 -08:00
32737a6bf7 fix(router): skipLocationChange with named outlets (#28301)
With #27680, a bug was fixed where multiple redirects using `eager` URL update could cause navigation to fail. However, that fix introduced a problem where with `skipLocationChange` enabled, the URL tree rendered was not properly stored for reference. This specifically caused an issue with named router outlets and subsequent navigations not being recognized.

This PR stores the correct `UrlTree` for reference with later navigations. It fixes the regression introdued with #27680.

Fixes #28200

PR Close #28301
2019-01-22 15:15:34 -08:00
7642308c14 docs: correct array to map (#27379)
PR Close #27379
2019-01-22 12:16:46 -08:00
f1c08d83b0 docs: add input doc (#27379)
PR Close #27379
2019-01-22 12:16:46 -08:00
617412f9c3 docs: add api doc for ng_style directive (#27379)
PR Close #27379
2019-01-22 12:16:46 -08:00
cf716684a9 build: hide vscode settings e.g. debug launch config (#28299)
PR Close #28299
2019-01-22 11:38:16 -08:00
54565ed389 docs: fix crisis-detail milestone files (#27025)
Crisis Detail's template was being added two times and the component's TS none.
PR Close #27025
2019-01-22 11:19:51 -08:00
ab08385336 build: create dist/bin symlink with Bazel outputs (#27781)
Note that we had nasty problems in the past when this was enabled, but those have supposedly been fixed.

PR Close #27781
2019-01-22 09:52:46 -08:00
eb8ccf65d1 fix(docs-infra): removal of the use of the ChildNode.remove() method that it isn't supported by IE (#28188)
fixes #28177

PR Close #28188
2019-01-22 09:48:20 -08:00
cb93027f32 build: Fix gulp format for Node >= 10.14 (#28213)
With Node.js v10.14 and greater, running `yarn gulp format` produces
the following error:

```
$ nvm current
v10.15.0
$ yarn gulp format:changed
yarn run v1.12.3
$ /usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/.bin/gulp format:changed
internal/util/inspect.js:31
const types = internalBinding('types');
              ^

ReferenceError: internalBinding is not defined
    at internal/util/inspect.js:31:15
    at req_ (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:137:5)
    at require (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:110:12)
    at util.js:25:21
    at req_ (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:137:5)
    at require (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:110:12)
    at fs.js:42:21
    at req_ (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:137:5)
    at Object.req [as require] (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/natives/index.js:54:10)
    at Object.<anonymous> (/usr/local/google/home/kyliau/Documents/GitHub/angular/node_modules/vinyl-fs/node_modules/graceful-fs/fs.js:1:99)
```

A search on GitHub reveals this issue is due to natives@1.1.4:
gulpjs/gulp#2246

```
$ yarn why natives
yarn why v1.12.3
[1/4] Why do we have the module "natives"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "natives@1.1.6"
info Reasons this module exists
   - "gulp#vinyl-fs#graceful-fs" depends on it
   - Hoisted from "gulp#vinyl-fs#graceful-fs#natives"
   - Hoisted from "browserstacktunnel-wrapper#unzip#fstream#graceful-fs#natives"
```

The solution is to add a manual resolution for natives@1.1.6

PR Close #28213
2019-01-22 09:45:51 -08:00
607fd8e970 refactor: remove unused case in switch statement (#28253)
PR Close #28253
2019-01-22 09:44:35 -08:00
f4ac96d0ff style(docs-infra): Active tslint rule semicolon (#28282)
PR Close #28282
2019-01-22 09:38:45 -08:00
1c7d156abd ci: add a rebase check to the merge-pr script (#28250)
Adds a check to verify that each PR branch to be merged upstream contains SHAs of commits that significantly changed our CI infrastructure.

This check is used to enforce that we don't merge PRs that have not been rebased recently and could result in merging of non-approved or otherwise bad changes.

PR Close #28250
2019-01-22 09:26:54 -08:00
4c00059260 feat(compiler-cli): resolve generated Sass/Less files to .css inputs (#28166)
Users might have run the CSS Preprocessor tool *before* the Angular
compiler. For example, we do it that way under Bazel. This means that
the design-time reference is different from the compile-time one - the
input to the Angular compiler is a plain .css file.

We assume that the preprocessor does a trivial 1:1 mapping using the same
basename with a different extension.

PR Close #28166
2019-01-18 09:49:19 -08:00
93d78c9c51 build(bazel): Use local rollup & build-optimizer for ng_rollup_bundle (#28215)
The current build workflow depends on cross workspace dependency by
installing angular-cli as a Bazel repository. This is not ideal because
it introduces separate node_module directories other than the one
installed by Angular through the yarn_install rule (ngdeps).

This commit removes angular-cli from the Bazel workspace and installs
rollup and @angular-devkit/build-optimizer locally.

PR Close #28215
2019-01-18 09:07:53 -08:00
49bee4c0d8 ci(docs-infra): do not automatically post preview comments on PRs for team (#28211)
Right now, we post such comments whenever a file has been touched that
could potentially have affected the docs. Since the API docs are built
from comments in the source code, almost all non-docs changes are
generating such preview comments, even though most of the time they are
irrelevant to the author and create unnecessary noise on the PR
(especially for actively worked-on PRs).

This commit removes the `team` GitHub team from the list of teams whose
members will automatically get preview comments.
(Adding the `aio: preview` label would still work on any PR.)

Jira: FW-967

PR Close #28211
2019-01-17 14:11:43 -08:00
d8c75f1bb0 docs: fix comments in services example (#26194)
PR Close #26194
2019-01-16 17:38:24 -08:00
6d40ef2d6b fix(docs-infra): improve a11y (#28086)
Issue #27723

PR Close #28086
2019-01-16 17:37:46 -08:00
4ea6c27dcf build(bazel): Use @angular/cli from root package.json (#28139)
A few integration tests now depend on @angular/cli.

This commit changes the affected tests to use the dependency
on @angular/cli defined at root package.json.

PR Close #28139
2019-01-16 17:37:23 -08:00
a4d9192fbc feat(bazel): Add support for SASS (#28167)
This commit adds the appropriate rules to the WORKSPACE for a project
that requires SASS support.

PR Close #28167
2019-01-16 17:35:55 -08:00
7b772e93a3 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:50:52 -08:00
05168395b0 release: cut the v7.2.1 release 2019-01-16 13:23:06 -08:00
03293c4fec 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 11:56:53 -08:00
479019f457 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 11:56:53 -08:00
68515818b9 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-16 11:56:53 -08:00
8bc369f828 docs(router): add clarification for Router config (#28159)
PR Close #28159
2019-01-16 11:56:53 -08:00
3d1a4d5cc3 docs: add api doc for switch directives (#27378)
PR Close #27378
2019-01-16 11:56:53 -08:00
5c56b778e0 docs(core): fix typo (#28042)
PR Close #28042
2019-01-16 11:56:53 -08:00
585e871c96 fix(service-worker): navigation urls backwards compatibility (#27244)
PR Close #27244
2019-01-16 11:56:53 -08:00
6ae7aee2c3 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-16 11:56:53 -08:00
701270d039 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-16 11:56:53 -08:00
02a852a34a 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-16 11:56:52 -08:00
531f940212 docs(ivy): remove duplicated words in architecture doc (#27471)
PR Close #27471
2019-01-16 11:56:52 -08:00
de80f1b6dd docs: update testing doc example style for HostListener (#26372)
HostListener is preferred over host metadata by official style guide

PR Close #26372
2019-01-16 11:56:52 -08:00
ca3965afe0 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-16 11:56:52 -08:00
f269e433a7 docs: ng config link fix (#28115)
Fixed 'ng config' link to lead to proper CLI command site
PR Close #28115
2019-01-16 11:56:52 -08:00
8750b09fca fix(docs-infra): Add crossed through styling (#28111)
PR Close #28111
2019-01-16 11:56:52 -08:00
ea2eef737b fix(docs-infra): change style of deprecated markers (#28111)
PR Close #28111
2019-01-16 11:56:52 -08:00
0ceab97a03 fix(docs-infra): render deprecated markers for CLI command options (#28111)
fixes #27563
fixes #27423

PR Close #28111
2019-01-16 11:56:52 -08:00
fbbdaaacc0 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-16 11:56:52 -08:00
080de58a88 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-16 11:56:52 -08:00
5390948360 docs: fix typo in singleton services guide (#27948)
PR Close #27948
2019-01-16 11:56:51 -08:00
0d860051af 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-16 11:56:51 -08:00
edbba24b60 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-16 11:56:51 -08:00
6ae8d7691d 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-16 11:56:51 -08:00
af3cf36ce9 docs: ngComponentOutlet doc updated with new Injector creation (#27291)
PR Close #27291
2019-01-16 11:56:51 -08:00
1be2f11965 docs: updates to minor spelling mistakes in pipes guide (#27208)
PR Close #27208
2019-01-16 11:56:51 -08:00
495ce325b2 docs: add browser polyfills for IE11 with hash-based routing (#27135)
Closes #26511

PR Close #27135
2019-01-16 11:56:51 -08:00
92411043d1 docs: fix GitHub pages deployment command (#26896)
closes #26803
PR Close #26896
2019-01-16 11:56:51 -08:00
aa3f75b3c9 docs: minor wording change in code example comment (#26835)
PR Close #26835
2019-01-16 11:56:51 -08:00
d64aadf57a docs: fix typo in testing guide (#26828)
PR Close #26828
2019-01-16 11:56:51 -08:00
51f7f081a3 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-16 11:56:50 -08:00
b9fd62413f docs: fix scripts section and some minor issues in universal documentation (#26444)
PR Close #26444
2019-01-16 11:56:50 -08:00
c5664bf245 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-16 11:56:50 -08:00
c66a076614 docs: add library doc to guides in aio (#27581)
PR Close #27581
2019-01-16 11:56:50 -08:00
28d34b699d 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-16 11:56:50 -08:00
47840bee71 docs(forms): fix up @see tags for AbstractContol (#28069)
PR Close #28069
2019-01-16 11:56:50 -08:00
305331f634 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-16 11:56:50 -08:00
5e6c24cb01 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-16 11:56:40 -08:00
0ec4e1372a 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-16 11:55:47 -08:00
84c1bad3a1 style(service-worker): fix code formatting in service-worker-getting-started example (#28020)
PR Close #28020
2019-01-16 11:55:47 -08:00
1640832f56 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-16 11:55:47 -08:00
8ab036262d 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-16 11:55:47 -08:00
76e8c0ac7b 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-16 11:55:47 -08:00
0e81e418fb docs: add schematics terms to glossary (#27862)
PR Close #27862
2019-01-16 11:55:47 -08:00
60255b68c0 docs: re-write interpolation section and add example (#25170)
PR Close #25170
2019-01-16 11:55:47 -08:00
ae7b3c8d45 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-16 11:55:47 -08:00
9556ba7bca 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-16 11:55:47 -08:00
80994b25b9 docs: fix typo (#27865)
PR Close #27865
2019-01-16 11:55:47 -08:00
2f154b980f 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-16 11:55:46 -08:00
342d352a00 refactor(core): improve type for EventEmitter (#27228)
PR Close #27228
2019-01-16 11:55:46 -08:00
f240ae5084 build(bazel): Bump @bazel/typescript & @bazel/karma to 0.22.1 (#28031)
PR Close #28031
2019-01-16 11:55:46 -08:00
e4fc8bad35 fix(bazel): Add /bazel-out to .gitignore (#27874)
PR Close #27874
2019-01-16 11:55:46 -08:00
5c680d4aa8 test(upgrade): log more info to help debug CI flakes (#28045)
Related Jira issue: FW-939

PR Close #28045
2019-01-16 11:55:46 -08:00
f05c5f82c8 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-16 11:55:46 -08:00
2e0c58ec3e 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-16 11:55:46 -08:00
21093b9090 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-16 11:55:46 -08:00
a6153accf0 docs: remove deprecated fromPromise from RxJS guide (#28015)
Follow-up to #27443.

PR Close #28015
2019-01-16 11:55:46 -08:00
dee789c204 docs(core): add missing closing backtick (`) (#27908)
PR Close #27908
2019-01-16 11:55:46 -08:00
12dd552fcd docs(forms): remove duplicated link (#27884)
PR Close #27884
2019-01-16 11:55:45 -08:00
079bcffe07 docs: fix typo PageNotFoundCompponent (#27799)
closes: #27795

PR Close #27799
2019-01-16 11:55:45 -08:00
30256e8fe8 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-16 11:55:45 -08:00
8ee69831fc 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-16 11:55:45 -08:00
c3d8e2888d 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-16 11:55:45 -08:00
bea677136b 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-16 11:55:45 -08:00
11728bbbd9 build: remove travisci leftovers (#27979)
PR Close #27979
2019-01-16 11:55:45 -08:00
1da4b03940 fix(aio): Treating some deprecated (#27981)
PR Close #27981
2019-01-16 11:55:45 -08:00
54ba0f021f docs(core): fix characters that corrupt link (#27982)
PR Close #27982
2019-01-16 11:55:45 -08:00
836a5c72a0 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-16 11:55:45 -08:00
f589933440 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-16 11:55:45 -08:00
ef3ec34aa3 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-16 11:55:44 -08:00
37a6d2d033 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-16 11:55:44 -08:00
354f3639bb 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-16 11:55:44 -08:00
c4b06868b1 test: fix outDir in TS 3.2 integration test (#27774)
PR Close #27774
2019-01-16 11:55:44 -08:00
e3853e842e 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-16 11:55:44 -08:00
aee5cbd057 test(ivy): re-enable passing animation tests (#27997)
PR Close #27997
2019-01-16 11:55:44 -08:00
e9614eff1a ci: update payload size for cli-hello-world (#27994)
PR Close #27994
2019-01-16 11:55:44 -08:00
80f9f7e8ef docs: group fixes and features for 7.2.0 in changelog desc (#27974)
PR Close #27974
2019-01-16 11:55:37 -08:00
7d2589556f docs: remove Travis status from README (#27973)
we no longer use TravisCI
PR Close #27973
2019-01-16 11:54:04 -08:00
468fcab59d 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-16 11:54:04 -08:00
f720e972d4 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-16 11:54:04 -08:00
b51ae7e59a build: increase parallelism for "test_docs_examples" job (#27937)
PR Close #27937
2019-01-16 11:54:04 -08:00
eedb06936f 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-16 11:54:04 -08:00
ee5a094424 build: bazel integration test using limited resources (#27937)
PR Close #27937
2019-01-16 11:54:04 -08:00
9773b5a173 build: shard integration tests on circleci (#27937)
PR Close #27937
2019-01-16 11:54:04 -08:00
eed171839e 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-16 11:54:04 -08:00
1057b52def build: remove "build.sh" script (#27937)
this script is now obsolete and not needed any more. yay!!!

PR Close #27937
2019-01-16 11:54:04 -08:00
302506e940 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-16 11:54:04 -08:00
cc35feb445 ci: remove travis ci setup (#27937)
we no longer need it... yay!!!

PR Close #27937
2019-01-16 11:54:03 -08:00
21b875d4d0 ci: run legacy e2e tests in parallel (#27937)
PR Close #27937
2019-01-16 11:54:03 -08:00
dac9c09235 ci: move e2e tests from travis to circleci (#27937)
PR Close #27937
2019-01-16 11:54:03 -08:00
5cba438eb5 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-16 11:54:03 -08:00
2b001cb2b1 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-16 11:54:03 -08:00
0be8487f09 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-16 11:54:03 -08:00
e1e69ca828 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-16 11:54:03 -08:00
f376c46d78 test(bazel): fix all ng_package tests not working on windows (#27829)
PR Close #27829
2019-01-16 11:54:03 -08:00
fad4145f48 fix(bazel): packager not properly removing amd directives on windows (#27829)
PR Close #27829
2019-01-16 11:54:03 -08:00
6b394f62be 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-16 11:54:03 -08:00
16aad8b2d8 docs: add Alyle UI to resources (#27954)
PR Close #27954
2019-01-16 11:54:03 -08:00
25bbcbcbe5 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-16 11:54:02 -08:00
0efbb37381 release: cut the v7.2.0 release 2019-01-07 07:37:59 -08:00
6b0c1a71fa test(ivy): turn on platform-browser-dynamic tests on CI (#27940)
We missed removing the `fixme-ivy-aot` bazel tag from the BUILD file
of platform-browser-dynamic, so we weren't running the
`//packages/platform-browser-dynamic/test:test_web_chromium-local`
test target on CI. This commit turns on the tests and adds root causes
where they are known.

PR Close #27940
2019-01-05 17:48:11 -08:00
4c9aff5695 ci: fix typos in codeowners file (#27945)
PR Close #27945
2019-01-05 17:46:27 -08:00
224d78765f 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
7adcbb320f 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
e7f43386a6 build: update to Bazel 0.21.0 (#27935)
PR Close #27935
2019-01-04 18:38:44 -08:00
411de252d7 fix(ivy): disable test which rely on DebugElement in WebWorker (#27895)
PR Close #27895
2019-01-04 18:37:31 -08:00
b61dafaeac refactor: remove redundant error in catch (#25478)
PR Close #25478
2019-01-04 15:42:19 -08:00
929334b0bf fix(ivy): should not throw when getting VCRef.parentInjector on the root view (#27909)
PR Close #27909
2019-01-04 14:57:39 -08:00
fb7816fed4 fix(ivy): element injector on the host of a component should see viewProviders of that component (#27928)
PR Close #27928
2019-01-04 14:46:15 -08:00
5d3dcfc6ad fix(ivy): ensure @animation host bindings/listeners work properly (#27896)
PR Close #27896
2019-01-04 14:12:29 -08:00
0bd9deb9f5 fix(ivy): pipes should overwrite pipes with later entry in the pipes array (#27910)
PR Close #27910
2019-01-04 13:21:46 -08:00
13d23f315b fix(ivy): ngtsc program emit ignoring custom transformers (#27837)
Fixes the `customTransformers` that are passed to the `NgtscProgram.emit` not being passed along.

PR Close #27837
2019-01-04 12:29:15 -08:00
48555f95c6 fix(ivy): ensure unique template and listener function identifiers (#27766)
Previously, there could be identical template/listener function names
for a component's template, if it had multiple similarly structured
nested sub-templates or listeners.
This resulted in build errors:
`Identifier '<SOME_IDENTIFIER>' has already been declared`

This commit fixes this by ensuring that the template index is included
in the `contextName` passed to the `TemplateDefinitionBuilder`
responsible for processing nested sub-templates.
Similarly, the template or element index is included in the listener
names.

PR Close #27766
2019-01-04 12:28:11 -08:00
d026b675be refactor(ivy): minor refactoring / code clean-up (#27766)
PR Close #27766
2019-01-04 12:28:11 -08:00
7eb2c41fb2 fix(ivy): ng-content tags in re-inserted templates should walk declaration tree (#27783)
This PR assures that content projection works if an <ng-content> tag is
placed inside an <ng-template> in one component and that <ng-template>
is inserted into a different component. It fixes a bug where the
projection instruction code would walk up the insertion tree to find
selector data instead of the declaration tree.

PR Close #27783
2019-01-04 12:20:18 -08:00
3f2ebbd7ab fix(ivy): support bindings in ICUs in nested contexts (#27914)
Prior to this change, ICU extraction logic was not taking into account nested bindings (that look like this: �0:1�) and only accounted for top level bindings (like this �0�). As a result, ICUs were not parsed and remained as text in the output. Now the extraction logic (regular expressions) take into account the nested bindings format as well.

PR Close #27914
2019-01-04 12:19:46 -08:00
38b4d15227 fix(ivy): implement 'TestBed.overrideTemplateUsingTestingModule' function (#27717)
Adding 'TestBed.overrideTemplateUsingTestingModule' function that overrides Component template and uses TestingModule as a scope.

PR Close #27717
2019-01-04 12:19:20 -08:00
176b3f12f4 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
8a05199fb9 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:22 -08:00
51a0bd2e75 test(ivy): correct attribute marker in broken test (#27919)
Test incorrectly marks the attribute as a 'Class', when it should be 'SelectOnly'

PR Close #27919
2019-01-03 16:10:00 -08:00
1b0b36d143 fix(forms): match getError and hasError to get method signature (#20211)
Internally getError and hasError call the AbstractControl#get method which takes  `path: Array<string | number> | string` as input, since there are different ways to traverse the AbstractControl tree.
This change matches the method signitures of all methods that use this.

PR Close #20211
2019-01-03 10:16:06 -08:00
e268a0579a docs(forms): update desc for hasError and getError (#27861)
This commit adds docs for the changes made in #20211.

Closes #19734.

PR Close #27861
2019-01-03 10:15:23 -08:00
1f1e77b641 fix(ivy): don’t publish animation bindings as attributes (#27805)
Some of the animation tests have been failing because animation gets
triggered multiple times. The reason for this is that the compiler was
generating static attribute bindings in addition to dynamic bindings.
This created multiple writes to the animation render which failed the
tests.

PR Close #27805
2019-01-03 09:39:25 -08:00
880b4aabdb 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
5fee444fea test(ivy): update test failure root cause (#27855)
Updates the root cause for a couple of the test failures.

PR Close #27855
2019-01-03 09:37:26 -08:00
1e6c9be86c 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
a9790018df fix(ivy): resolve forwardRefs correctly in TestBed (JIT) (#27737)
Forward refs in some places (like imports/export/providers/viewProviders/queries) were not resolved before passing to compilation phase. Now we resolve missing refs before invoking compile function.

PR Close #27737
2019-01-03 09:34:15 -08:00
1dc95c41eb docs: fix typo in TOH http section (#26127)
PR Close #26127
2019-01-03 09:32:28 -08:00
a0a3648e7a docs: add more info on what is being cached (#27343)
PR Close #27343
2019-01-03 09:29:44 -08:00
7df9040c05 docs: add missing comma to environment config in build guide (#27476)
PR Close #27476
2019-01-03 09:29:17 -08:00
5638c1d507 docs: fix npm description in architecture modules guide (#27818)
PR Close #27818
2019-01-03 09:28:45 -08:00
755c8091af docs: improve description of bundle budgets (#27833)
PR Close #27833
2019-01-03 09:28:16 -08:00
59f64dd361 docs(service-worker): update service worker configuration doc (#27868)
PR Close #27868
2019-01-03 09:27:30 -08:00
27431e0e1e 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
b7d0ab7de3 test(ivy): enable more @angular/core tests (#27912)
PR Close #27912
2019-01-03 09:22:22 -08:00
460be795cf test(ivy): add root cause analysis for failing core tests (view injector integration) (#27912)
PR Close #27912
2019-01-03 09:22:22 -08:00
c4f7727408 docs: add api doc for viewport scroller (#27381)
PR Close #27381
2018-12-26 11:47:16 -08:00
d1de9ff313 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
707c6828b5 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
3f64e87ed1 test: check payload size for cli-hello-world-ivy (#27797)
Adds size bundle checking to the integration test `cli-hello-world-ivy`

PR Close #27797
2018-12-26 11:19:19 -08:00
62e45cef2d test: update cli-hello-world-ivy to cli@7.2.0-rc.0 (#27797)
Updates the app itself to reflect the result of using the  `experimentalIvy` flag on the CLI.
The result is similar to:

    npx @angular/cli@next new cli-hello-world-ivy --experimental-ivy --defaults

But replaces the current (cli `7.2.0-rc.0`) `renderComponent` bootstrap with the usual `platformBrowserDynamic` one.
It also keeps what the app did (display a pipe, tests it).

PR Close #27797
2018-12-26 11:19:19 -08:00
c34eee4e8e test(ivy): root cause analysis for @angular/platform-server (#27800)
PR Close #27800
2018-12-26 11:17:45 -08:00
9b91beed69 docs: fix typo explictly (#27798)
Closes #27796

PR Close #27798
2018-12-26 06:57:55 -05:00
eea2b0f288 revert: fix(router): ensure URL is updated after second redirect with UrlUpdateStrategy="eager" (#27523) 2018-12-25 22:14:07 -08:00
13eb57a59f fix(ivy): merge static style rendering across elements, directives and components (#27661)
PR Close #27661
2018-12-21 18:14:44 -05:00
0b3ae3d70c 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
4b67b0af3e fix(ivy): adding TestBed.overrideProvider support (#27693)
Prior to this change, provider overrides defined via TestBed.overrideProvider were not applied to Components/Directives. Now providers are taken into account while compiling Components/Directives (metadata is updated accordingly before being passed to compilation).

PR Close #27693
2018-12-21 16:39:34 -05:00
bba5e2632e docs(forms): clarify the pattern validator behavior (#27560)
PR Close #27560
2018-12-21 15:12:31 -05:00
c5ce4e62c6 docs: fix code example to ensure consistency in file (#26577)
PR Close #26577
2018-12-21 15:12:05 -05:00
509aa61619 fix(ivy): apply all overrides from TestBed, not the last one only (#27734)
In some cases in our tests we can define multiple overrides for a given class. As a result, only the last override is actually applied due to the fact that we store overrides in a Type<->Override map. This update changes the logic to keep all overrides defined in a given test for a Type (i.e. Type<->Override[] map) and applies them one by one at resolution phase. This behavior is more inline with the previous TestBed.

PR Close #27734
2018-12-21 15:09:00 -05:00
cdd737e28b test(ivy): mark tests using getDebugContext as obsolete in ivy (#27790)
PR Close #27790
2018-12-21 15:07:31 -05:00
5da55d6246 test(ivy): add root cause analysis for failing router tests (#27792)
PR Close #27792
2018-12-21 15:06:56 -05:00
50a91ba28c docs(upgrade): add gotchas/tips/example for multiple downgraded modules (#27217)
PR Close #27217
2018-12-20 16:20:41 -05:00
077a5fb04b 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:41 -05:00
bc0ee01d09 fix(upgrade): allow nesting components from different downgraded modules (#27217)
PR Close #27217
2018-12-20 16:20:41 -05:00
326b464d20 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:41 -05:00
8a7498e0ef refactor(upgrade): simplify special handling of ngUpgradeLite in downgradeComponent() (#27217)
PR Close #27217
2018-12-20 16:20:41 -05:00
07ada7f3d9 fix(router): ensure URL is updated after second redirect with UrlUpdateStrategy="eager" (#27523)
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 #27523
2018-12-20 16:18:52 -05:00
880e8a5cfc refactor: fix broken formatting 2018-12-20 09:16:09 -08:00
a0585c9a9a fix(ivy): correct content projection with nested templates (#27755)
Previously ivy code generation was emmiting the projectionDef instruction in
a template where the <ng-content> tag was found. This code generation logic was
incorrect since the ivy runtime expects the projectionDef instruction to be present
in the main template only.

This PR ammends the code generation logic so that the projectionDef instruction is
emmitedin the main template only.

PR Close #27755
2018-12-20 12:01:20 -05:00
a833b98fd0 fix(ivy): change detection strategy not being passed to compiler (#27753)
Fixes the defined change detection strategy not being passed to the compiler when a component is being compiled.

PR Close #27753
2018-12-20 12:00:58 -05:00
4b70a4e905 feat(ivy): support NgModule metadata from calls that do not return ModuleWithProviders types (#27326)
Normally functions that return `ModuleWithProvider` objects should parameterize
the return type to include the type of `NgModule` that is being returned. For
example `forRoot(): ModuleWithProviders<RouterModule>`.

But in some cases, especially those generated by nccc, these functions to not
explicitly declare `ModuleWithProviders` as their return type. Instead they
return a "intersection" type, one of whose members is a type literal that
declares the `NgModule` type returned. For example:
`forRoot(): CustomType&{ngModule:RouterModule}`.

This commit changes the `NgModuleDecoratorHandler` so that it can extract
the `NgModule` type from either kind of declaration.

PR Close #27326
2018-12-20 11:58:50 -05:00
f2a1c66031 feat(ivy): ngcc - add typings to ModuleWithProviders functions (#27326)
Exported functions or static method that return a `ModuleWithProviders`
compatible structure need to provide information about the referenced
`NgModule` type in their return type.

This allows ngtsc to be able to understand the type of `NgModule` that is
being returned from calls to the function, without having to dig into the
internals of the compiled library.

There are two ways to provide this information:

* Add a type parameter to the `ModuleWithProviders` return type. E.g.

```
static forRoot(): ModuleWithProviders<SomeNgModule>;
```

* Convert the return type to a union that includes a literal type. E.g.

```
static forRoot(): (SomeOtherType)&{ngModule:SomeNgModule};
```

This commit updates the rendering of typings files to include this type
information on all matching functions/methods.

PR Close #27326
2018-12-20 11:58:49 -05:00
cfb8c17511 feat(ivy): ngcc - map functions as well as classes from source to typings (#27326)
To support updating `ModuleWithProviders` calls,
we need to be able to map exported functions between
source and typings files, as well as classes.

PR Close #27326
2018-12-20 11:58:49 -05:00
99d0e27587 test(ivy): update root cause for core test (#27768)
PR Close #27768
2018-12-20 11:58:14 -05:00
b08f3acf09 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
3ab25ab078 refactor: fix broken linting rules due to revert 2018-12-19 13:06:43 -08:00
0604527199 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
8f8572fd3e fix(ivy): @Host should behave as in View Engine (#27646)
PR Close #27646
2018-12-19 15:12:35 -05:00
e8f7241366 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
a20b2f72f2 fix(ivy): process creation mode deeply before running update mode (#27744)
Prior to this commit, we had two different modes for change detection
execution for Ivy, depending on whether you called `bootstrap()` or
`renderComponent()`. In the former case, we would complete creation
mode for all components in the tree before beginning update mode for
any component. In the latter case, we would run creation mode and
update mode together for each component individually.

Maintaining code to support these two different execution orders was
unnecessarily complex, so this commit aligns the two bootstrapping
mechanisms to execute in the same order. Now creation mode always
runs for all components before update mode begins.

This change also simplifies our rendering logic so that we use
`LView` flags as the source of truth for rendering mode instead of
`rf` function arguments. This fixed some related bugs (e.g. calling
`ViewRef.detectChanges` synchronously after the view's creation
would create view nodes twice, view queries would execute twice, etc).

PR Close #27744
2018-12-19 15:10:16 -05:00
211 changed files with 12642 additions and 7584 deletions

View File

@ -22,12 +22,18 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
# Filesystem interactions #
###############################
# Don't create symlinks like bazel-out in the project.
# These cause VSCode to traverse a massive tree, opening file handles and
# Create symlinks in the project:
# - dist/bin for outputs
# - dist/testlogs, dist/genfiles
# - bazel-out
# NB: bazel-out should be excluded from the editor configuration.
# The checked-in /.vscode/settings.json does this for VSCode.
# Other editors may require manual config to ignore this directory.
# In the past, we say a problem where VSCode traversed a massive tree, opening file handles and
# eventually a surprising failure with auto-discovery of the C++ toolchain in
# MacOS High Sierra.
# See https://github.com/bazelbuild/bazel/issues/4603
build --symlink_prefix=/
build --symlink_prefix=dist/
# Performance: avoid stat'ing input files
build --watchfs

4
.gitignore vendored
View File

@ -1,7 +1,7 @@
.DS_STORE
/dist/
/bazel-*
/bazel-out/
/integration/bazel/bazel-*
e2e_test.*
node_modules
@ -13,9 +13,9 @@ pubspec.lock
.c9
.idea/
.settings/
.vscode/launch.json
*.swo
modules/.settings
.vscode
modules/.vscode
# Don't check in secret files

15
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/bazel-out/**": true,
"**/dist/**": true,
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/bazel-out": true,
"**/dist": true,
},
}

View File

@ -1,3 +1,47 @@
<a name="7.2.2"></a>
## [7.2.2](https://github.com/angular/angular/compare/7.2.1...7.2.2) (2019-01-22)
### Bug Fixes
* **bazel:** Fix integration test after v8 bump ([#28194](https://github.com/angular/angular/issues/28194)) ([7b772e9](https://github.com/angular/angular/commit/7b772e9)), closes [#28142](https://github.com/angular/angular/issues/28142)
* **router:** `skipLocationChange` with named outlets ([#28301](https://github.com/angular/angular/issues/28301)) ([32737a6](https://github.com/angular/angular/commit/32737a6)), closes [#27680](https://github.com/angular/angular/issues/27680) [#28200](https://github.com/angular/angular/issues/28200)
### Features
* **bazel:** Add support for SASS ([#28167](https://github.com/angular/angular/issues/28167)) ([a4d9192](https://github.com/angular/angular/commit/a4d9192))
* **compiler-cli:** resolve generated Sass/Less files to .css inputs ([#28166](https://github.com/angular/angular/issues/28166)) ([4c00059](https://github.com/angular/angular/commit/4c00059))
<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

@ -26,8 +26,8 @@ ARG AIO_GITHUB_ORGANIZATION=angular
ARG TEST_AIO_GITHUB_ORGANIZATION=test-org
ARG AIO_GITHUB_REPO=angular
ARG TEST_AIO_GITHUB_REPO=test-repo
ARG AIO_GITHUB_TEAM_SLUGS=team,aio-contributors
ARG TEST_AIO_GITHUB_TEAM_SLUGS=team,aio-contributors
ARG AIO_GITHUB_TEAM_SLUGS=aio-contributors
ARG TEST_AIO_GITHUB_TEAM_SLUGS=aio-contributors
ARG AIO_NGINX_HOSTNAME=$AIO_DOMAIN_NAME
ARG TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_DOMAIN_NAME
ARG AIO_NGINX_PORT_HTTP=80

View File

@ -16,7 +16,7 @@ const ITEMS: Item[] = [
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves crises from a server */
/** Simulate a data service that retrieves items from a server */
@Injectable()
export class ItemService implements OnDestroy {

View File

@ -3796,7 +3796,7 @@ The relevant *Crisis Center* code for this milestone follows.
</code-pane>
<code-pane header="crisis-detail.component.html" path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.html">
<code-pane header="crisis-detail.component.ts" path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts">
</code-pane>

View File

@ -8,7 +8,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Cross Platform</div>
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px" alt="">
</div>
<div class="feature-row">
@ -34,7 +34,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Speed and Performance</div>
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px" alt="">
</div>
<div class="feature-row">
@ -59,7 +59,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Productivity</div>
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px" alt="">
</div>
<div class="feature-row">
@ -84,7 +84,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Full Development Story</div>
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px" alt="">
</div>
<div class="feature-row">

View File

@ -9,7 +9,7 @@
<!-- LOGO -->
<div class="hero-logo">
<img src="assets/images/logos/angular/angular.svg"/>
<img src="assets/images/logos/angular/angular.svg" alt="Angular"/>
</div>
<!-- CONTAINER -->
@ -104,7 +104,7 @@
<div layout="row" layout-xs="column" class="home-row">
<a href="guide/quickstart">
<div class="card">
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
<img src="generated/images/marketing/home/code-icon.svg" height="70px" alt="Angular quickstart">
<div class="card-text-container">
<div class="text-headline">Get Started</div>
<p>Start building your Angular application.</p>

View File

@ -20,7 +20,7 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="assets/images/logos/angular/angular.svg" alt="Angular">
<img src="assets/images/logos/angular/angular.svg" alt="Full color logo Angular">
</div>
<div>
<h3 class="l-space-left-3">FULL COLOR LOGO</h3>
@ -40,7 +40,7 @@
<div class="presskit-inner">
<div class="presskit-image-container">
<div>
<img src="assets/images/logos/angular/angular_solidBlack.svg">
<img src="assets/images/logos/angular/angular_solidBlack.svg" alt="Black logo Angular">
</div>
</div>
<div>
@ -61,7 +61,7 @@
<div class="presskit-inner">
<div class="presskit-image-container">
<div>
<img src="assets/images/logos/angular/angular_whiteTransparent.svg" class="transparent-img-bg">
<img src="assets/images/logos/angular/angular_whiteTransparent.svg" class="transparent-img-bg" alt="Transparent logo Angular">
</div>
</div>
<div>

View File

@ -23,7 +23,7 @@
<mat-sidenav-container class="sidenav-container mat-drawer-container mat-sidenav-container" role="main">
<mat-sidenav-content class="mat-drawer-content mat-sidenav-content">
<section class="sidenav-content" role="content">
<section class="sidenav-content" role="article">
<aio-doc-viewer>
<div class="content">
<div class="nf-container l-flex-wrap flex-center">

View File

@ -52,7 +52,7 @@
</div>
</mat-sidenav>
<section class="sidenav-content" [id]="pageId" role="content">
<main class="sidenav-content" [id]="pageId" role="main">
<aio-mode-banner [mode]="deployment.mode" [version]="versionInfo"></aio-mode-banner>
<aio-doc-viewer [class.no-animations]="isStarting"
[doc]="currentDocument"
@ -62,7 +62,7 @@
(docRendered)="onDocRendered()">
</aio-doc-viewer>
<aio-dt [on]="dtOn" [(doc)]="currentDocument"></aio-dt>
</section>
</main>
</mat-sidenav-container>

View File

@ -52,7 +52,7 @@ describe('AppComponent', () => {
await newDocPromise; // Wait for the new document to be fetched.
fixture.detectChanges(); // Propagate document change to the view (i.e to `DocViewer`).
await docRenderedPromise; // Wait for the `docRendered` event.
};
}
function initializeTest(waitForDoc = true) {
fixture = TestBed.createComponent(AppComponent);
@ -73,7 +73,7 @@ describe('AppComponent', () => {
tocService = de.injector.get<TocService>(TocService);
return waitForDoc && awaitDocRendered();
};
}
describe('with proper DocViewer', () => {
@ -1071,7 +1071,7 @@ describe('AppComponent', () => {
it('should set the id of the doc viewer container based on the current doc', () => {
initializeTest(false);
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
const container = fixture.debugElement.query(By.css('main.sidenav-content'));
navigateTo('guide/pipes');
expect(component.pageId).toEqual('guide-pipes');
@ -1088,7 +1088,7 @@ describe('AppComponent', () => {
it('should not be affected by changes to the query', () => {
initializeTest(false);
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
const container = fixture.debugElement.query(By.css('main.sidenav-content'));
navigateTo('guide/pipes');
navigateTo('guide/other?search=http');

View File

@ -46,7 +46,7 @@ export interface Announcement {
template: `
<div class="homepage-container" *ngIf="announcement">
<div class="announcement-bar">
<img [src]="announcement.imageUrl">
<img [src]="announcement.imageUrl" alt="">
<p [innerHTML]="announcement.message"></p>
<a class="button" [href]="announcement.linkUrl">Learn More</a>
</div>

View File

@ -125,7 +125,7 @@ export class ApiListComponent implements OnInit {
return status === 'all' ||
status === item.stability ||
(status === 'security-risk' && item.securityRisk);
};
}
function matchesType() {
return type === 'all' || type === item.docType;

View File

@ -54,7 +54,7 @@ export class ApiService implements OnDestroy {
section.items.every(item => item.stability === 'deprecated');
});
}));
};
}
constructor(private http: HttpClient, private logger: Logger) { }

View File

@ -79,7 +79,7 @@ describe('ContributorListComponent', () => {
return comp;
}
interface SearchResult { [index: string]: string; };
interface SearchResult { [index: string]: string; }
class TestLocationService {
searchResult: SearchResult = {};

View File

@ -245,7 +245,7 @@ class FakeComponentFactory extends ComponentFactory<any> {
rootSelectorOrNode?: string | any,
ngModule?: NgModuleRef<any>): ComponentRef<any> {
return jasmine.createSpy('ComponentRef') as any;
};
}
}
class FakeComponentFactoryResolver extends ComponentFactoryResolver {

View File

@ -37,6 +37,6 @@ describe('Getting Started NgFor Component', () => {
component.parseError$.subscribe(error => {
expect(error).toBeTruthy();
})
});
});
});

View File

@ -46,6 +46,6 @@ describe('Getting Started NgIf Component', () => {
component.parseError$.subscribe(error => {
expect(error).toBeTruthy();
})
});
});
});

View File

@ -26,7 +26,7 @@ export class ResourceService {
(categories as ConnectableObservable<Category[]>).connect();
return categories;
};
}
}
// Extract sorted Category[] from resource JSON data

View File

@ -100,9 +100,10 @@ export class DocViewerComponent implements OnDestroy {
if (needsToc && !embeddedToc) {
// Add an embedded ToC if it's needed and there isn't one in the content already.
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
} else if (!needsToc && embeddedToc) {
} else if (!needsToc && embeddedToc && embeddedToc.parentNode !== null) {
// Remove the embedded Toc if it's there and not needed.
embeddedToc.remove();
// We cannot use ChildNode.remove() because of IE11
embeddedToc.parentNode.removeChild(embeddedToc);
}
return () => {

View File

@ -14,4 +14,4 @@ export class Deployment {
mode: string = this.location.search()['mode'] || environment.mode;
constructor(private location: LocationService) {}
};
}

View File

@ -68,7 +68,10 @@ export class TocService {
}
}
// now remove the anchor
anchorLink.remove();
if (anchorLink.parentNode !== null) {
// We cannot use ChildNode.remove() because of IE11
anchorLink.parentNode.removeChild(anchorLink);
}
}
// security: the document element which provides this heading content
// is always authored by the documentation team and is considered to be safe

View File

@ -93,7 +93,7 @@
<div class="background-sky hero"></div>
<section id="intro" style="text-shadow: 1px 1px #1976d2;">
<div class="hero-logo">
<img src="assets/images/logos/angular/angular.svg" width="250" height="250">
<img src="assets/images/logos/angular/angular.svg" width="250" height="250" alt="Angular">
</div>
<div class="homepage-container">
<div class="hero-headline">One framework.<br>Mobile &amp; desktop.</div>

View File

@ -268,13 +268,13 @@ section#intro {
aio-shell {
&.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute {
section {
main {
padding: 0rem 0rem 3rem;
}
}
&.page-home {
section {
main {
padding: 0;
}
}

View File

@ -89,7 +89,7 @@ aio-notification {
}
&.page-home, &.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute {
section {
main {
padding-top: $notificationHeight;
}
}

View File

@ -75,6 +75,7 @@
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [

View File

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

View File

@ -13,3 +13,6 @@ build --local_resources=14336,8.0,1.0
# Use the Angular 6 compiler
build --define=compile=legacy
# Don't create symlinks
build --symlink_prefix=/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>

View File

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

View File

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

View File

@ -0,0 +1,11 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11

View File

@ -1,8 +1,16 @@
// 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`.
// 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`.
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,12 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>CliHelloWorld</title>
<title>CliHelloWorldIvy</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

@ -12,16 +12,14 @@ 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' ],
dir: require('path').join(__dirname, 'coverage'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,

View File

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

View File

@ -11,14 +11,17 @@
* 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/docs/ts/latest/guide/browser-support.html
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
* This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
*/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
@ -40,19 +43,36 @@
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
/**
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
* 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`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
@ -60,7 +80,6 @@ import 'core-js/es7/reflect';
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

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

View File

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

View File

@ -0,0 +1,17 @@
{
"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": [
"es2017",
"es2018",
"dom"
]
}

View File

@ -1,6 +1,6 @@
{
"rulesDirectory": [
"node_modules/codelyzer"
"codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
@ -18,7 +18,6 @@
"forin": true,
"import-blacklist": [
true,
"rxjs",
"rxjs/Rx"
],
"import-spacing": true,
@ -66,6 +65,7 @@
],
"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,18 +117,6 @@
"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

@ -28,7 +28,7 @@
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.10.3",
"@angular/cli": "7.0.3",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "~2.8.3",

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
import nodeResolve from 'rollup-plugin-node-resolve';
export default {
entry: 'dist/src/main.js',
sourceMap: true,
input: 'dist/src/main.js',
output: {
sourceMap: true,
},
treeshake: true,
moduleName: 'main',
plugins: [
nodeResolve()
]

View File

@ -4,10 +4,11 @@ import commonjs from 'rollup-plugin-commonjs';
// a real app should make a common bundle for libraries instead of bundling them
// in both the main module & the lazy module, but we don't care about size here
export default {
entry: 'dist/src/lazy.module.js',
sourceMap: true,
input: 'dist/src/lazy.module.js',
output: {
sourceMap: true,
},
treeshake: true,
moduleName: 'lazy',
plugins: [
nodeResolve()
]

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 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
# 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
yarn build
fi

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "7.2.0",
"version": "7.2.2",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -32,6 +32,7 @@
"// 1": "dependencies are used locally and by bazel",
"dependencies": {
"@angular-devkit/architect": "^0.10.6",
"@angular-devkit/build-optimizer": "^0.12.2",
"@angular-devkit/core": "^7.0.4",
"@angular-devkit/schematics": "^7.0.4",
"@bazel/karma": "~0.22.1",
@ -73,6 +74,9 @@
"node-uuid": "1.4.8",
"protractor": "^5.4.2",
"reflect-metadata": "^0.1.3",
"rollup": "^1.1.0",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-sourcemaps": "^0.4.2",
"selenium-webdriver": "3.5.0",
"shelljs": "^0.8.1",
"source-map": "^0.6.1",
@ -129,10 +133,7 @@
"madge": "0.5.0",
"mutation-observer": "^1.0.3",
"rewire": "2.5.2",
"rollup": "0.47.4",
"rollup-plugin-commonjs": "8.1.0",
"rollup-plugin-node-resolve": "3.0.0",
"rollup-plugin-sourcemaps": "0.4.2",
"rxjs": "^6.3.0",
"sauce-connect": "https://saucelabs.com/downloads/sc-4.5.2-linux.tar.gz",
"semver": "5.4.1",
@ -145,5 +146,9 @@
"vlq": "0.2.2",
"vrsource-tslint-rules": "5.1.1",
"webpack": "1.12.9"
},
"// 4": "natives is needed for gulp to work with node >= 10.13, see #28213",
"resolutions": {
"natives": "1.1.6"
}
}

View File

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

View File

@ -53,19 +53,6 @@ def rules_angular_dev_dependencies():
shorter.
"""
# We have a source dependency on the Devkit repository, because it's built with
# Bazel.
# This allows us to edit sources and have the effect appear immediately without
# re-packaging or "npm link"ing.
# Even better, things like aspects will visit the entire graph including
# ts_library rules in the devkit repository.
http_archive(
name = "angular_cli",
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
strip_prefix = "angular-cli-6.1.0-rc.0",
url = "https://github.com/angular/angular-cli/archive/v6.1.0-rc.0.zip",
)
http_archive(
name = "org_brotli",
sha256 = "774b893a0700b0692a76e2e5b7e7610dbbe330ffbe3fe864b4b52ca718061d5a",

View File

@ -13,13 +13,15 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
nodejs_binary(
name = "rollup_with_build_optimizer",
data = ["@angular_cli//packages/angular_devkit/build_optimizer:lib"],
# Since our rule extends the one in rules_nodejs, we use the same runtime
# dependency @build_bazel_rules_nodejs_rollup_deps. We don't need any
# additional npm dependencies when we run rollup or uglify.
entry_point = "build_bazel_rules_nodejs_rollup_deps/node_modules/rollup/bin/rollup",
data = [
"@ngdeps//@angular-devkit/build-optimizer",
"@ngdeps//is-builtin-module",
"@ngdeps//rollup",
"@ngdeps//rollup-plugin-node-resolve",
"@ngdeps//rollup-plugin-sourcemaps",
],
entry_point = "ngdeps/node_modules/rollup/bin/rollup",
install_source_map_support = False,
node_modules = "@build_bazel_rules_nodejs_rollup_deps//:node_modules",
)
nodejs_binary(

View File

@ -31,7 +31,7 @@ PACKAGES = ["packages/core/src", "packages/common/src", "packages/compiler/src",
PLUGIN_CONFIG = "{sideEffectFreeModules: [\n%s]}" % ",\n".join(
[" '.esm5/{0}'".format(p) for p in PACKAGES],
)
BO_ROLLUP = "angular_cli/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js"
BO_ROLLUP = "ngdeps/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js"
BO_PLUGIN = "require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG)
def _use_plain_rollup(ctx):

View File

@ -41,7 +41,7 @@ http_archive(
url = "https://registry.yarnpkg.com/rxjs/-/rxjs-%s.tgz" % RXJS_VERSION,
strip_prefix = "package/src",
)
<% if (sass) { %>
# Rules for compiling sass
RULES_SASS_VERSION = "<%= RULES_SASS_VERSION %>"
http_archive(
@ -49,7 +49,7 @@ http_archive(
url = "https://github.com/bazelbuild/rules_sass/archive/%s.zip" % RULES_SASS_VERSION,
strip_prefix = "rules_sass-%s" % RULES_SASS_VERSION,
)
<% } %>
####################################
# Load and install our dependencies downloaded above.
@ -85,9 +85,9 @@ browser_repositories(
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
ts_setup_workspace()
<% if (sass) { %>
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
<% } %>
load("@angular//:index.bzl", "ng_setup_workspace")
ng_setup_workspace()

View File

@ -5,7 +5,17 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_library", "ts_web_test_suit
load("@build_bazel_rules_nodejs//:defs.bzl", "rollup_bundle", "history_server")
load("@build_bazel_rules_nodejs//internal/web_package:web_package.bzl", "web_package")
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
<% if (sass) { %>load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
[
sass_binary(
name = "style_" + x,
src = x,
deps = [],
)
for x in glob(["**/*.scss"])
]
<% } %>
ng_module(
name = "src",
srcs = glob(
@ -20,7 +30,7 @@ ng_module(
assets = glob([
"**/*.css",
"**/*.html",
]),
])<% if (sass) { %> + [":style_" + x for x in glob(["**/*.scss"])]<% } %>,
deps = [
"@angular//packages/core",
"@angular//packages/platform-browser",<% if (routing) { %>

View File

@ -60,6 +60,16 @@ function hasRoutingModule(host: Tree) {
return hasRouting;
}
/**
* Returns true if project uses SASS stylesheets, false otherwise.
*/
function hasSassStylesheet(host: Tree) {
let hasSass = false;
// The proper extension for SASS is .scss
host.visit((file: string) => { hasSass = hasSass || file.endsWith('.scss'); });
return hasSass;
}
export default function(options: BazelWorkspaceOptions): Rule {
return (host: Tree, context: SchematicContext) => {
if (!options.name) {
@ -103,6 +113,7 @@ export default function(options: BazelWorkspaceOptions): Rule {
...options,
'dot': '.', ...workspaceVersions,
routing: hasRoutingModule(host),
sass: hasSassStylesheet(host),
}),
move(appDir),
]));

View File

@ -12,7 +12,7 @@ import {clean} from './index';
describe('Bazel-workspace Schematic', () => {
const schematicRunner =
new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'), );
new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'));
const defaultOptions = {
name: 'demo',
};
@ -79,6 +79,46 @@ describe('Bazel-workspace Schematic', () => {
expect(content).toContain('workspace(name = "demo_project"');
});
});
describe('SASS', () => {
let host = new UnitTestTree(new HostTree);
beforeAll(() => {
host.create('/demo/src/app/app.component.scss', '');
expect(host.files).toContain('/demo/src/app/app.component.scss');
const options = {...defaultOptions};
host = schematicRunner.runSchematic('bazel-workspace', options, host);
expect(host.files).toContain('/demo/WORKSPACE');
expect(host.files).toContain('/demo/src/BUILD.bazel');
});
it('should download rules_sass in WORKSPACE', () => {
const content = host.readContent('/demo/WORKSPACE');
expect(content).toContain('RULES_SASS_VERSION');
expect(content).toContain('io_bazel_rules_sass');
});
it('should load sass_repositories in WORKSPACE', () => {
const content = host.readContent('/demo/WORKSPACE');
expect(content).toContain(
'load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")');
expect(content).toContain('sass_repositories()');
});
it('should add sass_binary rules in src/BUILD', () => {
const content = host.readContent('/demo/src/BUILD.bazel');
expect(content).toContain('load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")');
expect(content).toMatch(/sass_binary\((.*\n)+\)/);
});
it('should add SASS targets to assets of ng_module in src/BUILD', () => {
const content = host.readContent('/demo/src/BUILD.bazel');
expect(content).toContain(`
assets = glob([
"**/*.css",
"**/*.html",
]) + [":style_" + x for x in glob(["**/*.scss"])],`);
});
});
});
describe('clean', () => {

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 (e) {
} catch {
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

@ -12,21 +12,35 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer,
* @ngModule CommonModule
*
* @usageNotes
*
* Set the font of the containing element to the result of an expression.
*
* ```
* <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
* ```
*
* Set the width of the containing element to a pixel value returned by an expression.
*
* ```
* <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element>
* ```
*
* Set a collection of style values using an expression that returns key-value pairs.
*
* ```
* <some-element [ngStyle]="objExp">...</some-element>
* ```
*
* @description
*
* Update an HTML element styles.
*
* The styles are updated according to the value of the expression evaluation:
* - keys are style names with an optional `.<unit>` suffix (ie 'top.px', 'font-style.em'),
* - values are the values assigned to those properties (expressed in the given unit).
* An attribute directive that updates styles for the containing HTML element.
* Sets one or more style properties, specified as colon-separated key-value pairs.
* The key is a style name, with an optional `.<unit>` suffix
* (such as 'top.px', 'font-style.em').
* The value is an expression to be evaluated.
* The resulting non-null value, expressed in the given unit,
* is assigned to the given style property.
* If the result of evaluation is null, the corresponding style is removed.
*
* @publicApi
*/
@ -41,13 +55,24 @@ export class NgStyle implements DoCheck {
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Input()
set ngStyle(values: {[key: string]: string}) {
set ngStyle(
/**
* A map of style properties, specified as colon-separated
* key-value pairs.
* * The key is a style name, with an optional `.<unit>` suffix
* (such as 'top.px', 'font-style.em').
* * The value is an expression to be evaluated.
*/
values: {[key: string]: string}) {
this._ngStyle = values;
if (!this._differ && values) {
this._differ = this._differs.find(values).create();
}
}
/**
* Applies the new styles if needed.
*/
ngDoCheck() {
if (this._differ) {
const changes = this._differ.diff(this._ngStyle);

View File

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

View File

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

View File

@ -11,6 +11,7 @@ import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_out
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {modifiedInIvy} from '@angular/private/testing';
describe('insert/remove', () => {
@ -106,17 +107,20 @@ describe('insert/remove', () => {
}));
it('should resolve a with injector', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.componentInstance.cmpRef = null;
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();
}));
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();
}));
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): number {
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers): number {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) {
@ -32,8 +32,12 @@ 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)});
const {diagnostics: compileDiags} = performCompilation({
rootNames,
options,
emitFlags,
emitCallback: createEmitCallback(options), customTransformers
});
return reportErrorsAndExit(compileDiags, options, consoleError);
}

View File

@ -0,0 +1,118 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {ReferencesRegistry} from '../../../ngtsc/annotations';
import {Declaration} from '../../../ngtsc/host';
import {ResolvedReference} from '../../../ngtsc/metadata';
import {NgccReflectionHost} from '../host/ngcc_host';
import {isDefined} from '../utils';
export interface ModuleWithProvidersInfo {
/**
* The declaration (in the .d.ts file) of the function that returns
* a `ModuleWithProviders object, but has a signature that needs
* a type parameter adding.
*/
declaration: ts.MethodDeclaration|ts.FunctionDeclaration;
/**
* The NgModule class declaration (in the .d.ts file) to add as a type parameter.
*/
ngModule: Declaration;
}
export type ModuleWithProvidersAnalyses = Map<ts.SourceFile, ModuleWithProvidersInfo[]>;
export const ModuleWithProvidersAnalyses = Map;
export class ModuleWithProvidersAnalyzer {
constructor(private host: NgccReflectionHost, private referencesRegistry: ReferencesRegistry) {}
analyzeProgram(program: ts.Program): ModuleWithProvidersAnalyses {
const analyses = new ModuleWithProvidersAnalyses();
const rootFiles = this.getRootFiles(program);
rootFiles.forEach(f => {
const fns = this.host.getModuleWithProvidersFunctions(f);
fns && fns.forEach(fn => {
const dtsFn = this.getDtsDeclaration(fn.declaration);
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
null;
if (!typeParam || isAnyKeyword(typeParam)) {
// Either we do not have a parameterized type or the type is `any`.
let ngModule = this.host.getDeclarationOfIdentifier(fn.ngModule);
if (!ngModule) {
throw new Error(
`Cannot find a declaration for NgModule ${fn.ngModule.text} referenced in ${fn.declaration.getText()}`);
}
// For internal (non-library) module references, redirect the module's value declaration
// to its type declaration.
if (ngModule.viaModule === null) {
const dtsNgModule = this.host.getDtsDeclaration(ngModule.node);
if (!dtsNgModule) {
throw new Error(
`No typings declaration can be found for the referenced NgModule class in ${fn.declaration.getText()}.`);
}
if (!ts.isClassDeclaration(dtsNgModule)) {
throw new Error(
`The referenced NgModule in ${fn.declaration.getText()} is not a class declaration in the typings program; instead we get ${dtsNgModule.getText()}`);
}
// Record the usage of the internal module as it needs to become an exported symbol
this.referencesRegistry.add(new ResolvedReference(ngModule.node, fn.ngModule));
ngModule = {node: dtsNgModule, viaModule: null};
}
const dtsFile = dtsFn.getSourceFile();
const analysis = analyses.get(dtsFile) || [];
analysis.push({declaration: dtsFn, ngModule});
analyses.set(dtsFile, analysis);
}
});
});
return analyses;
}
private getRootFiles(program: ts.Program): ts.SourceFile[] {
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
}
private getDtsDeclaration(fn: ts.SignatureDeclaration) {
let dtsFn: ts.Declaration|null = null;
const containerClass = this.host.getClassSymbol(fn.parent);
const fnName = fn.name && ts.isIdentifier(fn.name) && fn.name.text;
if (containerClass && fnName) {
const dtsClass = this.host.getDtsDeclaration(containerClass.valueDeclaration);
// Get the declaration of the matching static method
dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ?
dtsClass.members
.find(
member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
member.name.text === fnName) as ts.Declaration :
null;
} else {
dtsFn = this.host.getDtsDeclaration(fn);
}
if (!dtsFn) {
throw new Error(`Matching type declaration for ${fn.getText()} is missing`);
}
if (!isFunctionOrMethod(dtsFn)) {
throw new Error(
`Matching type declaration for ${fn.getText()} is not a function: ${dtsFn.getText()}`);
}
return dtsFn;
}
}
function isFunctionOrMethod(declaration: ts.Declaration): declaration is ts.FunctionDeclaration|
ts.MethodDeclaration {
return ts.isFunctionDeclaration(declaration) || ts.isMethodDeclaration(declaration);
}
function isAnyKeyword(typeParam: ts.TypeNode): typeParam is ts.KeywordTypeNode {
return typeParam.kind === ts.SyntaxKind.AnyKeyword;
}

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.getDtsDeclarationOfClass(declaration.node);
const dtsDeclaration = this.host.getDtsDeclaration(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 {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
export const DECORATORS = 'decorators' as ts.__String;
export const 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 dtsClassMap: Map<string, ts.ClassDeclaration>|null;
protected dtsDeclarationMap: Map<string, ts.Declaration>|null;
constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) {
super(checker);
this.dtsClassMap = dts && this.computeDtsClassMap(dts.path, dts.program) || null;
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
}
/**
@ -327,15 +327,15 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* is not a class or has an unknown number of type parameters.
*/
getGenericArityOfClass(clazz: ts.Declaration): number|null {
const dtsClass = this.getDtsDeclarationOfClass(clazz);
if (dtsClass) {
return dtsClass.typeParameters ? dtsClass.typeParameters.length : 0;
const dtsDeclaration = this.getDtsDeclaration(clazz);
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
}
return null;
}
/**
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
* Take an exported declaration of a class (maybe down-leveled to a variable) and look up the
* declaration of its type in a separate .d.ts tree.
*
* This function is allowed to return `null` if the current compilation unit does not have a
@ -346,19 +346,47 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
* `ts.Program` as the input declaration.
*/
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;
}
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null {
if (!this.dtsDeclarationMap) {
return null;
}
return null;
if (!isNamedDeclaration(declaration)) {
throw new Error(
`Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
}
return this.dtsDeclarationMap.get(declaration.name.text) || null;
}
/**
* Search the given source file for exported functions and static class methods that return
* ModuleWithProviders objects.
* @param f The source file to search for these functions
* @returns An array of function declarations that look like they return ModuleWithProviders
* objects.
*/
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[] {
const exports = this.getExportsOfModule(f);
if (!exports) return [];
const infos: ModuleWithProvidersFunction[] = [];
exports.forEach((declaration, name) => {
if (this.isClass(declaration.node)) {
this.getMembersOfClass(declaration.node).forEach(member => {
if (member.isStatic) {
const info = this.parseForModuleWithProviders(member.node);
if (info) {
infos.push(info);
}
}
});
} else {
const info = this.parseForModuleWithProviders(declaration.node);
if (info) {
infos.push(info);
}
}
});
return infos;
}
///////////// Protected Helpers /////////////
@ -738,7 +766,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
if (!name) {
if (isNamedDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
if (isNamedDeclaration(node)) {
name = node.name.text;
nameNode = node.name;
} else {
@ -846,8 +874,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
/**
* Get the parameter type and decorators for a class where the information is stored on
* in calls to `__decorate` helpers.
* Get the parameter type and decorators for a class where the information is stored via
* calls to `__decorate` helpers.
*
* Reflect over the helpers to find the decorators and types about each of
* the class's constructor parameters.
@ -1002,9 +1030,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @param dtsProgram The program containing all the typings files.
* @returns a map of class names to class declarations.
*/
protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program):
Map<string, ts.ClassDeclaration> {
const dtsClassMap = new Map<string, ts.ClassDeclaration>();
protected computeDtsDeclarationMap(dtsRootFileName: string, dtsProgram: ts.Program):
Map<string, ts.Declaration> {
const dtsDeclarationMap = new Map<string, ts.Declaration>();
const checker = dtsProgram.getTypeChecker();
// First add all the classes that are publicly exported from the entry-point
@ -1012,13 +1040,38 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (!rootFile) {
throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`);
}
collectExportedClasses(checker, dtsClassMap, rootFile);
collectExportedDeclarations(checker, dtsDeclarationMap, rootFile);
// Now add any additional classes that are exported from individual dts files,
// but are not publicly exported from the entry-point.
dtsProgram.getSourceFiles().forEach(
sourceFile => { collectExportedClasses(checker, dtsClassMap, sourceFile); });
return dtsClassMap;
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};
}
}
@ -1129,8 +1182,10 @@ 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 {
return !!(node as any).name;
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration&
{name: ts.Identifier} {
const anyNode: any = node;
return !!anyNode.name && ts.isIdentifier(anyNode.name);
}
@ -1153,13 +1208,11 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
}
/**
* Search a source file for exported classes, storing them in the provided `dtsClassMap`.
* @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.
* Collect mappings between exported declarations in a source file and its associated
* declaration in the typings program.
*/
function collectExportedClasses(
checker: ts.TypeChecker, dtsClassMap: Map<string, ts.ClassDeclaration>,
function collectExportedDeclarations(
checker: ts.TypeChecker, dtsDeclarationMap: Map<string, ts.Declaration>,
srcFile: ts.SourceFile): void {
const srcModule = srcFile && checker.getSymbolAtLocation(srcFile);
const moduleExports = srcModule && checker.getExportsOfModule(srcModule);
@ -1170,8 +1223,8 @@ function collectExportedClasses(
}
const declaration = exportedSymbol.valueDeclaration;
const name = exportedSymbol.name;
if (declaration && ts.isClassDeclaration(declaration) && !dtsClassMap.has(name)) {
dtsClassMap.set(name, declaration);
if (declaration && !dtsDeclarationMap.has(name)) {
dtsDeclarationMap.set(name, declaration);
}
});
}

View File

@ -19,6 +19,21 @@ 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
*/
@ -45,4 +60,13 @@ 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,6 +11,7 @@ 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';
@ -25,6 +26,7 @@ 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).
@ -59,13 +61,14 @@ export class Transformer {
const reflectionHost = this.getHost(isCore, bundle);
// Parse and analyze the files.
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
this.analyzeProgram(reflectionHost, isCore, bundle);
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle);
// Transform the source files and source maps.
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
const renderedFiles = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
// Write out all the transformed files.
renderedFiles.forEach(file => this.writeFile(file));
@ -102,16 +105,26 @@ 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 switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
const moduleWithProvidersAnalyzer =
bundle.dts && new ModuleWithProvidersAnalyzer(reflectionHost, referencesRegistry);
const moduleWithProvidersAnalyses = moduleWithProvidersAnalyzer &&
moduleWithProvidersAnalyzer.analyzeProgram(bundle.src.program);
const privateDeclarationsAnalyzer =
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};
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses};
}
writeFile(file: FileInfo): void {
@ -129,4 +142,5 @@ interface ProgramAnalyses {
decorationAnalyses: Map<ts.SourceFile, CompiledFile>;
switchMarkerAnalyses: SwitchMarkerAnalyses;
privateDeclarationsAnalyses: ExportInfo[];
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null;
}

View File

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

@ -0,0 +1,401 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {BundleProgram} from '../../src/packages/bundle_program';
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
const TEST_PROGRAM = [
{
name: '/src/entry-point.js',
contents: `
export * from './explicit';
export * from './any';
export * from './implicit';
export * from './no-providers';
export * from './module';
`
},
{
name: '/src/explicit.js',
contents: `
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export class ExplicitInternalModule {}
export function explicitInternalFunction() {
return {
ngModule: ExplicitInternalModule,
providers: []
};
}
export function explicitExternalFunction() {
return {
ngModule: ExternalModule,
providers: []
};
}
export function explicitLibraryFunction() {
return {
ngModule: LibraryModule,
providers: []
};
}
export class ExplicitClass {
static explicitInternalMethod() {
return {
ngModule: ExplicitInternalModule,
providers: []
};
}
static explicitExternalMethod() {
return {
ngModule: ExternalModule,
providers: []
};
}
static explicitLibraryMethod() {
return {
ngModule: LibraryModule,
providers: []
};
}
}
`
},
{
name: '/src/any.js',
contents: `
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export class AnyInternalModule {}
export function anyInternalFunction() {
return {
ngModule: AnyInternalModule,
providers: []
};
}
export function anyExternalFunction() {
return {
ngModule: ExternalModule,
providers: []
};
}
export function anyLibraryFunction() {
return {
ngModule: LibraryModule,
providers: []
};
}
export class AnyClass {
static anyInternalMethod() {
return {
ngModule: AnyInternalModule,
providers: []
};
}
static anyExternalMethod() {
return {
ngModule: ExternalModule,
providers: []
};
}
static anyLibraryMethod() {
return {
ngModule: LibraryModule,
providers: []
};
}
}
`
},
{
name: '/src/implicit.js',
contents: `
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export class ImplicitInternalModule {}
export function implicitInternalFunction() {
return {
ngModule: ImplicitInternalModule,
providers: [],
};
}
export function implicitExternalFunction() {
return {
ngModule: ExternalModule,
providers: [],
};
}
export function implicitLibraryFunction() {
return {
ngModule: LibraryModule,
providers: [],
};
}
export class ImplicitClass {
static implicitInternalMethod() {
return {
ngModule: ImplicitInternalModule,
providers: [],
};
}
static implicitExternalMethod() {
return {
ngModule: ExternalModule,
providers: [],
};
}
static implicitLibraryMethod() {
return {
ngModule: LibraryModule,
providers: [],
};
}
}
`
},
{
name: '/src/no-providers.js',
contents: `
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export class NoProvidersInternalModule {}
export function noProvExplicitInternalFunction() {
return {ngModule: NoProvidersInternalModule};
}
export function noProvExplicitExternalFunction() {
return {ngModule: ExternalModule};
}
export function noProvExplicitLibraryFunction() {
return {ngModule: LibraryModule};
}
export function noProvAnyInternalFunction() {
return {ngModule: NoProvidersInternalModule};
}
export function noProvAnyExternalFunction() {
return {ngModule: ExternalModule};
}
export function noProvAnyLibraryFunction() {
return {ngModule: LibraryModule};
}
export function noProvImplicitInternalFunction() {
return {ngModule: NoProvidersInternalModule};
}
export function noProvImplicitExternalFunction() {
return {ngModule: ExternalModule};
}
export function noProvImplicitLibraryFunction() {
return {ngModule: LibraryModule};
}
`
},
{
name: '/src/module.js',
contents: `
export class ExternalModule {}
`
},
{
name: '/node_modules/some-library/index.d.ts',
contents: 'export declare class LibraryModule {}'
},
];
const TEST_DTS_PROGRAM = [
{
name: '/typings/entry-point.d.ts',
contents: `
export * from './explicit';
export * from './any';
export * from './implicit';
export * from './no-providers';
export * from './module';
`
},
{
name: '/typings/explicit.d.ts',
contents: `
import {ModuleWithProviders} from './core';
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export declare class ExplicitInternalModule {}
export declare function explicitInternalFunction(): ModuleWithProviders<ExplicitInternalModule>;
export declare function explicitExternalFunction(): ModuleWithProviders<ExternalModule>;
export declare function explicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
export declare class ExplicitClass {
static explicitInternalMethod(): ModuleWithProviders<ExplicitInternalModule>;
static explicitExternalMethod(): ModuleWithProviders<ExternalModule>;
static explicitLibraryMethod(): ModuleWithProviders<LibraryModule>;
}
`
},
{
name: '/typings/any.d.ts',
contents: `
import {ModuleWithProviders} from './core';
export declare class AnyInternalModule {}
export declare function anyInternalFunction(): ModuleWithProviders<any>;
export declare function anyExternalFunction(): ModuleWithProviders<any>;
export declare function anyLibraryFunction(): ModuleWithProviders<any>;
export declare class AnyClass {
static anyInternalMethod(): ModuleWithProviders<any>;
static anyExternalMethod(): ModuleWithProviders<any>;
static anyLibraryMethod(): ModuleWithProviders<any>;
}
`
},
{
name: '/typings/implicit.d.ts',
contents: `
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export declare class ImplicitInternalModule {}
export declare function implicitInternalFunction(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
export declare function implicitExternalFunction(): { ngModule: typeof ExternalModule; providers: never[]; };
export declare function implicitLibraryFunction(): { ngModule: typeof LibraryModule; providers: never[]; };
export declare class ImplicitClass {
static implicitInternalMethod(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
static implicitExternalMethod(): { ngModule: typeof ExternalModule; providers: never[]; };
static implicitLibraryMethod(): { ngModule: typeof LibraryModule; providers: never[]; };
}
`
},
{
name: '/typings/no-providers.d.ts',
contents: `
import {ModuleWithProviders} from './core';
import {ExternalModule} from './module';
import {LibraryModule} from 'some-library';
export declare class NoProvidersInternalModule {}
export declare function noProvExplicitInternalFunction(): ModuleWithProviders<NoProvidersInternalModule>;
export declare function noProvExplicitExternalFunction(): ModuleWithProviders<ExternalModule>;
export declare function noProvExplicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
export declare function noProvAnyInternalFunction(): ModuleWithProviders<any>;
export declare function noProvAnyExternalFunction(): ModuleWithProviders<any>;
export declare function noProvAnyLibraryFunction(): ModuleWithProviders<any>;
export declare function noProvImplicitInternalFunction(): { ngModule: typeof NoProvidersInternalModule; };
export declare function noProvImplicitExternalFunction(): { ngModule: typeof ExternalModule; };
export declare function noProvImplicitLibraryFunction(): { ngModule: typeof LibraryModule; };
`
},
{
name: '/typings/module.d.ts',
contents: `
export declare class ExternalModule {}
`
},
{
name: '/typings/core.d.ts',
contents: `
export declare interface Type<T> {
new (...args: any[]): T
}
export declare type Provider = any;
export declare interface ModuleWithProviders<T> {
ngModule: Type<T>
providers?: Provider[]
}
`
},
{
name: '/node_modules/some-library/index.d.ts',
contents: 'export declare class LibraryModule {}'
},
];
describe('ModuleWithProvidersAnalyzer', () => {
describe('analyzeProgram()', () => {
let analyses: ModuleWithProvidersAnalyses;
let program: ts.Program;
let dtsProgram: BundleProgram;
let referencesRegistry: NgccReferencesRegistry;
beforeAll(() => {
program = makeTestProgram(...TEST_PROGRAM);
dtsProgram = makeTestBundleProgram(TEST_DTS_PROGRAM);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dtsProgram);
referencesRegistry = new NgccReferencesRegistry(host);
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry);
analyses = analyzer.analyzeProgram(program);
});
it('should ignore declarations that already have explicit NgModule type params',
() => { expect(getAnalysisDescription(analyses, '/typings/explicit.d.ts')).toEqual([]); });
it('should find declarations that use `any` for the NgModule type param', () => {
const anyAnalysis = getAnalysisDescription(analyses, '/typings/any.d.ts');
expect(anyAnalysis).toContain(['anyInternalFunction', 'AnyInternalModule', null]);
expect(anyAnalysis).toContain(['anyExternalFunction', 'ExternalModule', null]);
expect(anyAnalysis).toContain(['anyLibraryFunction', 'LibraryModule', 'some-library']);
expect(anyAnalysis).toContain(['anyInternalMethod', 'AnyInternalModule', null]);
expect(anyAnalysis).toContain(['anyExternalMethod', 'ExternalModule', null]);
expect(anyAnalysis).toContain(['anyLibraryMethod', 'LibraryModule', 'some-library']);
});
it('should track internal module references in the references registry', () => {
const declarations = referencesRegistry.getDeclarationMap();
const externalModuleDeclaration =
getDeclaration(program, '/src/module.js', 'ExternalModule', ts.isClassDeclaration);
const libraryModuleDeclaration = getDeclaration(
program, '/node_modules/some-library/index.d.ts', 'LibraryModule', ts.isClassDeclaration);
expect(declarations.has(externalModuleDeclaration.name !)).toBe(true);
expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false);
});
it('should find declarations that have implicit return types', () => {
const anyAnalysis = getAnalysisDescription(analyses, '/typings/implicit.d.ts');
expect(anyAnalysis).toContain(['implicitInternalFunction', 'ImplicitInternalModule', null]);
expect(anyAnalysis).toContain(['implicitExternalFunction', 'ExternalModule', null]);
expect(anyAnalysis).toContain(['implicitLibraryFunction', 'LibraryModule', 'some-library']);
expect(anyAnalysis).toContain(['implicitInternalMethod', 'ImplicitInternalModule', null]);
expect(anyAnalysis).toContain(['implicitExternalMethod', 'ExternalModule', null]);
expect(anyAnalysis).toContain(['implicitLibraryMethod', 'LibraryModule', 'some-library']);
});
it('should find declarations that do not specify a `providers` property in the return type',
() => {
const anyAnalysis = getAnalysisDescription(analyses, '/typings/no-providers.d.ts');
expect(anyAnalysis).not.toContain([
'noProvExplicitInternalFunction', 'NoProvidersInternalModule'
]);
expect(anyAnalysis).not.toContain([
'noProvExplicitExternalFunction', 'ExternalModule', null
]);
expect(anyAnalysis).toContain([
'noProvAnyInternalFunction', 'NoProvidersInternalModule', null
]);
expect(anyAnalysis).toContain(['noProvAnyExternalFunction', 'ExternalModule', null]);
expect(anyAnalysis).toContain([
'noProvAnyLibraryFunction', 'LibraryModule', 'some-library'
]);
expect(anyAnalysis).toContain([
'noProvImplicitInternalFunction', 'NoProvidersInternalModule', null
]);
expect(anyAnalysis).toContain(['noProvImplicitExternalFunction', 'ExternalModule', null]);
expect(anyAnalysis).toContain([
'noProvImplicitLibraryFunction', 'LibraryModule', 'some-library'
]);
});
function getAnalysisDescription(analyses: ModuleWithProvidersAnalyses, fileName: string) {
const file = dtsProgram.program.getSourceFile(fileName) !;
const analysis = analyses.get(file);
return analysis ?
analysis.map(
info =>
[info.declaration.name !.getText(),
(info.ngModule.node as ts.ClassDeclaration).name !.getText(),
info.ngModule.viaModule]) :
[];
}
});
});

View File

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

View File

@ -453,6 +453,7 @@ 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',
@ -476,6 +477,7 @@ 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 {}`
@ -483,6 +485,54 @@ 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()', () => {
@ -1286,10 +1336,20 @@ describe('Fesm2015ReflectionHost', () => {
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
const dtsDeclaration = host.getDtsDeclaration(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
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);
@ -1297,7 +1357,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
});
it('should return null if there is no matching dts file', () => {
@ -1307,7 +1367,7 @@ describe('Fesm2015ReflectionHost', () => {
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
});
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
@ -1318,7 +1378,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
const dtsDeclaration = host.getDtsDeclaration(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
@ -1329,7 +1389,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclarationOfClass(class3);
const dtsDeclaration = host.getDtsDeclaration(class3);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
});
@ -1341,7 +1401,7 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass);
const dtsDeclaration = host.getDtsDeclaration(internalClass);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
});
@ -1355,12 +1415,42 @@ describe('Fesm2015ReflectionHost', () => {
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2);
const class2DtsDeclaration = host.getDtsDeclaration(class2);
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
const internalClass2DtsDeclaration = host.getDtsDeclarationOfClass(internalClass2);
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
.toEqual('/typings/class2.d.ts');
});
});
describe('getModuleWithProvidersFunctions', () => {
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
() => {
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
const host = new 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,6 +11,7 @@ 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';
@ -47,9 +48,9 @@ class TestRenderer extends Renderer {
function createTestRenderer(
packageName: string, files: {name: string, contents: string}[],
dtsFile?: {name: string, contents: string}) {
dtsFiles?: {name: string, contents: string}[]) {
const isCore = packageName === '@angular/core';
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFile && [dtsFile]);
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFiles);
const typeChecker = bundle.src.program.getTypeChecker();
const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
const referencesRegistry = new NgccReferencesRegistry(host);
@ -57,6 +58,8 @@ 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);
@ -64,7 +67,8 @@ function createTestRenderer(
spyOn(renderer, 'addDefinitions').and.callThrough();
spyOn(renderer, 'removeDecorators').and.callThrough();
return {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
return {renderer, decorationAnalyses, switchMarkerAnalyses, moduleWithProvidersAnalyses,
privateDeclarationsAnalyses};
}
@ -121,10 +125,11 @@ describe('Renderer', () => {
describe('renderProgram()', () => {
it('should render the modified contents; and a new map file, if the original provided no map file.',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
@ -134,9 +139,11 @@ describe('Renderer', () => {
it('should render as JavaScript', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [COMPONENT_PROGRAM]);
renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [COMPONENT_PROGRAM]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{
@ -154,10 +161,12 @@ 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} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const addImportsSpy = renderer.addImports as jasmine.Spy;
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
expect(addImportsSpy.calls.first().args[1]).toEqual([
@ -167,10 +176,12 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
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({
@ -187,10 +198,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
() => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
@ -212,14 +225,16 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
describe('source map merging', () => {
it('should merge any inline source map from the original file and write the output as an inline source map',
() => {
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer(
'test-package', [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
}]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment());
@ -230,14 +245,16 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
() => {
// Mock out reading the map file from disk
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer(
'test-package', [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
}]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
expect(result[0].path).toEqual('/dist/file.js');
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
@ -259,10 +276,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
contents: `export const NgModule = () => null;`
};
// The package name of `@angular/core` indicates that we are compiling the core library.
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
@ -277,10 +296,11 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
};
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('@angular/core', [CORE_FILE]);
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} = createTestRenderer('@angular/core', [CORE_FILE]);
renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[2])
.toContain(`/*@__PURE__*/ setClassMetadata(`);
@ -291,10 +311,12 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
describe('rendering typings', () => {
it('should render extract types into typings files', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
expect(typingsFile.contents)
@ -303,30 +325,195 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
});
it('should render imports into typings files', () => {
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
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} =
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
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);
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
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, animations} = parseHostBindings(hostMetadata);
const {attributes, listeners, properties} = 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.getDtsDeclarationOfClass(node);
const typeNode = this.reflector.getDtsDeclaration(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.getDtsDeclarationOfClass(typeRef.node);
if (typeNode !== null) {
let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
if (typeNode !== null && ts.isClassDeclaration(typeNode)) {
typeRef = new ResolvedReference(typeNode, typeNode.name !);
}
return toR3Reference(valueRef, typeRef, valueContext, typeContext);
@ -197,9 +197,20 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
*/
private _extractModuleFromModuleWithProvidersFn(node: ts.FunctionDeclaration|
ts.MethodDeclaration): ts.Expression|null {
const type = node.type;
const type = node.type || null;
return type &&
(this._reflectModuleFromTypeParam(type) || this._reflectModuleFromLiteralType(type));
}
/**
* Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
* `ModuleWithProviders<T>`
* @param type The type to reflect on.
* @returns the identifier of the NgModule type if found, or null otherwise.
*/
private _reflectModuleFromTypeParam(type: ts.TypeNode): ts.Expression|null {
// Examine the type of the function to see if it's a ModuleWithProviders reference.
if (type === undefined || !ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
if (!ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
return null;
}
@ -226,6 +237,32 @@ 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 of a class (maybe downleveled to a variable) and look up the
* Take an exported declaration (maybe a class down-leveled to a variable) and look up the
* declaration of its type in a separate .d.ts tree.
*
* 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.ClassDeclaration` returned from this function may not be from the same
* Note that the `ts.Declaration` returned from this function may not be from the same
* `ts.Program` as the input declaration.
*/
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null;
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null;
}

View File

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

View File

@ -208,14 +208,21 @@ export class NgtscProgram implements api.Program {
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
};
const transforms =
const customTransforms = opts && opts.customTransformers;
const beforeTransforms =
[ivyTransformFactory(this.compilation !, this.reflector, this.coreImportsFrom)];
if (this.factoryToSourceInfo !== null) {
transforms.push(generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
beforeTransforms.push(
generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
}
if (this.isCore) {
transforms.push(ivySwitchTransform);
beforeTransforms.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,
@ -223,7 +230,8 @@ export class NgtscProgram implements api.Program {
options: this.options,
emitOnlyDtsFiles: false, writeFile,
customTransformers: {
before: transforms,
before: beforeTransforms,
after: customTransforms && customTransforms.afterTs,
},
});
return emitResult;

View File

@ -19,6 +19,7 @@ import {DTS, GENERATED_FILES, isInRootDir, relativeToRootDirs} from './util';
const NODE_MODULES_PACKAGE_NAME = /node_modules\/((\w|-|\.)+|(@(\w|-|\.)+\/(\w|-|\.)+))/;
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const CSS_PREPROCESSOR_EXT = /(\.scss|\.less|\.styl)$/;
export function createCompilerHost(
{options, tsHost = ts.createCompilerHost(options, true)}:
@ -246,7 +247,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
if (packageTypings === originalImportedFile) {
moduleName = importedFilePackageName;
}
} catch (e) {
} catch {
// 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
@ -270,8 +271,15 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
} else if (firstChar !== '.') {
resourceName = `./${resourceName}`;
}
const filePathWithNgResource =
let filePathWithNgResource =
this.moduleNameToFileName(addNgResourceSuffix(resourceName), containingFile);
// If the user specified styleUrl pointing to *.scss, but the Sass compiler was run before
// Angular, then the resource may have been generated as *.css. Simply try the resolution again.
if (!filePathWithNgResource && CSS_PREPROCESSOR_EXT.test(resourceName)) {
const fallbackResourceName = resourceName.replace(CSS_PREPROCESSOR_EXT, '.css');
filePathWithNgResource =
this.moduleNameToFileName(addNgResourceSuffix(fallbackResourceName), containingFile);
}
const result = filePathWithNgResource ? stripNgResourceSuffix(filePathWithNgResource) : null;
// Used under Bazel to report more specific error with remediation advice
if (!result && (this.context as any).reportMissingResource) {
@ -582,7 +590,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
result = false;
}
}
} catch (e) {
} catch {
// 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, InitialStylingFlags} from '@angular/compiler/src/core';
import {AttributeMarker} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@ -48,17 +48,15 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
const $c3$ = ["cx", "20", "cy", "30", "r", "50"];
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
const $c2$ = ["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", $c3$);
$r3$.ɵelement(2, "circle", $c2$);
$r3$.ɵelementEnd();
$r3$.ɵnamespaceHTML();
$r3$.ɵelementStart(3, "p");
@ -100,13 +98,11 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
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");
@ -150,13 +146,11 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
const $c1$ = ["title", "Hello"];
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
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");
@ -440,14 +434,14 @@ describe('compiler compliance', () => {
$r3$.ɵallocHostVars(14);
}
if (rf & 2) {
$r3$elementProperty(elIndex, "expansionHeight",
$r3$componentHostSyntheticProperty(elIndex, "@expansionHeight",
$r3$.ɵbind(
$r3$.ɵpureFunction2(5, $_c1$, ctx.getExpandedState(),
$r3$.ɵpureFunction2(2, $_c0$, ctx.collapsedHeight, ctx.expandedHeight)
)
), null, true
);
$r3$elementProperty(elIndex, "expansionWidth",
$r3$componentHostSyntheticProperty(elIndex, "@expansionWidth",
$r3$.ɵbind(
$r3$.ɵpureFunction2(11, $_c1$, ctx.getExpandedState(),
$r3$.ɵpureFunction2(8, $_c2$, ctx.collapsedWidth, ctx.expandedWidth)
@ -486,8 +480,8 @@ describe('compiler compliance', () => {
const factory =
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
const _c0 = ["error"];
const _c1 = ["background-color"];
const $e0_classBindings$ = ["error"];
const $e0_styleBindings$ = ["background-color"];
MyComponent.ngComponentDef = i0.ɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
factory: function MyComponent_Factory(t){
@ -498,7 +492,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf,ctx){
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStyling(_c0, _c1);
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -773,7 +767,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $c1$ = ["foo", ""];
const $c2$ = ["if", ""];
function MyComponent_li_Template_2(rf, ctx) {
function MyComponent_li_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -795,7 +789,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul", null, $c1$);
$r3$.ɵtemplate(2, MyComponent_li_Template_2, 2, 2, "li", $c2$);
$r3$.ɵtemplate(2, MyComponent_li_2_Template, 2, 2, "li", $c2$);
$r3$.ɵelementEnd();
}
},
@ -1092,156 +1086,224 @@ describe('compiler compliance', () => {
});
});
it('should support content projection in root template', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
describe('content projection', () => {
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
export class SimpleComponent {}
it('should support content projection in root template', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
@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>\`
@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>'
})
export class ComplexComponent { }
export class MyApp {}
`
}
};
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
export class MyModule {}
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
});`;
@Component({
selector: 'my-app',
template: '<simple>content</simple> <complex></complex>'
})
export class MyApp {}
`
}
};
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
});
`;
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
});`;
const result = compile(files, angularFiles);
const source = result.source;
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$);
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$);
$r3$.ɵprojection(1, 1);
$r3$.ɵelementEnd();
$r3$.ɵelementStart(2, "div", $c4$);
$r3$.ɵprojection(3, 2);
} }
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$.ɵelementEnd();
}
},
encapsulation: 2
});
`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
expectEmit(
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
});
it('should support content projection in nested templates', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
template: \`
<div id="second" *ngIf="visible">
<ng-content SELECT="span[title=toFirst]"></ng-content>
</div>
<div id="third" *ngIf="visible">
No ng-content, no instructions generated.
</div>
<ng-template>
'*' selector: <ng-content></ng-content>
</ng-template>
\`,
})
class Cmp {}
@NgModule({ declarations: [Cmp] })
class Module {}
`
}
};
const output = `
const $_c0$ = [1, "ngIf"];
const $_c1$ = ["id", "second"];
const $_c2$ = [[["span", "title", "tofirst"]]];
const $_c3$ = ["span[title=toFirst]"];
function Cmp_div_Template_0(rf, ctx) { if (rf & 1) {
$r3$.ɵprojectionDef($_c2$, $_c3$);
$r3$.ɵelementStart(0, "div", $_c1$);
$r3$.ɵprojection(1, 1);
$r3$.ɵelementEnd();
} }
const $_c4$ = ["id", "third"];
function Cmp_div_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c4$);
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
$r3$.ɵelementEnd();
}
}
function Cmp_ng_template_Template_2(rf, ctx) {
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: ");
$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 $_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));
}
}
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');
});
it('should support content projection in both the root and nested templates', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
template: \`
<ng-content select="[id=toMainBefore]"></ng-content>
<ng-template>
<ng-content select="[id=toTemplate]"></ng-content>
<ng-template>
<ng-content select="[id=toNestedTemplate]"></ng-content>
</ng-template>
</ng-template>
<ng-template>
'*' selector in a template: <ng-content></ng-content>
</ng-template>
<ng-content select="[id=toMainAfter]"></ng-content>
\`,
})
class Cmp {}
@NgModule({ declarations: [Cmp] })
class Module {}
`
}
};
const output = `
function Cmp_ng_template_1_ng_template_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵprojection(0, 4);
}
}
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_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtext(0, " '*' selector in a template: ");
$r3$.ɵprojection(1);
}
}
const $_c0$ = [[["", "id", "tomainbefore"]], [["", "id", "tomainafter"]], [["", "id", "totemplate"]], [["", "id", "tonestedtemplate"]]];
const $_c1$ = ["[id=toMainBefore]", "[id=toMainAfter]", "[id=toTemplate]", "[id=toNestedTemplate]"];
template: function Cmp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵprojectionDef($_c2$, $_c3$);
$r3$.ɵprojection(0, 1);
$r3$.ɵtemplate(1, Cmp_ng_template_1_Template, 2, 0, "ng-template");
$r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
$r3$.ɵprojection(3, 2);
}
}
`;
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
});
describe('queries', () => {
@ -1849,7 +1911,7 @@ describe('compiler compliance', () => {
const $c2$ = ["if", ""];
const $c3$ = ["baz", ""];
const $c4$ = ["bar", ""];
function MyComponent_div_span_Template_2(rf, ctx) {
function MyComponent_div_3_span_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "span");
$r3$.ɵtext(1);
@ -1864,11 +1926,11 @@ describe('compiler compliance', () => {
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation3("", $foo$, "-", $bar$, "-", $baz$, ""));
}
}
function MyComponent_div_Template_3(rf, ctx) {
function MyComponent_div_3_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵtext(1);
$r3$.ɵtemplate(2, MyComponent_div_span_Template_2, 2, 3, "span", $c2$);
$r3$.ɵtemplate(2, MyComponent_div_3_span_2_Template, 2, 3, "span", $c2$);
$r3$.ɵelement(3, "span", null, $c4$);
$r3$.ɵelementEnd();
}
@ -1890,7 +1952,7 @@ describe('compiler compliance', () => {
if (rf & 1) {
$r3$.ɵelement(0, "div", null, $c1$);
$r3$.ɵtext(2);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 5, 2, "div", $c2$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 5, 2, "div", $c2$);
$r3$.ɵelement(4, "div", null, $c3$);
}
if (rf & 2) {
@ -1938,7 +2000,7 @@ describe('compiler compliance', () => {
const $c1$ = ["foo", ""];
const $c2$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_span_Template_3(rf, ctx) {
function MyComponent_div_0_span_3_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "span");
$i0$.ɵtext(1);
@ -1951,11 +2013,11 @@ describe('compiler compliance', () => {
}
}
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵelement(1, "div", null, $c1$);
$i0$.ɵtemplate(3, MyComponent_div_span_Template_3, 2, 2, "span", $c2$);
$i0$.ɵtemplate(3, MyComponent_div_0_span_3_Template, 2, 2, "span", $c2$);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -1967,7 +2029,7 @@ describe('compiler compliance', () => {
// ...
template:function MyComponent_Template(rf, ctx){
if (rf & 1) {
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 4, 1, "div", $c0$);
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 4, 1, "div", $c0$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@ -2162,7 +2224,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent__svg_g_Template_1(rf, ctx) {
function MyComponent__svg_g_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(0,"g");
@ -2181,7 +2243,7 @@ describe('compiler compliance', () => {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(0,"svg");
$r3$.ɵtemplate(1, MyComponent__svg_g_Template_1, 2, 0, ":svg:g", $t1_attrs$);
$r3$.ɵtemplate(1, MyComponent__svg_g_1_Template, 2, 0, ":svg:g", $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) { $r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items)); }
@ -2238,7 +2300,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_Template_1(rf, ctx) {
function MyComponent_li_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -2259,7 +2321,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul");
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 2, 1, "li", $t1_attrs$);
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 2, 1, "li", $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -2318,7 +2380,7 @@ describe('compiler compliance', () => {
const MyComponentDefinition = `
const $t4_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_li_Template_4(rf, ctx) {
function MyComponent_li_1_li_4_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
$r3$.ɵtext(1);
@ -2331,14 +2393,14 @@ describe('compiler compliance', () => {
}
}
function MyComponent_li_Template_1(rf, ctx) {
function MyComponent_li_1_Template(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_li_Template_4, 2, 2, "li", $t4_attrs$);
$r3$.ɵtemplate(4, MyComponent_li_1_li_4_Template, 2, 2, "li", $t4_attrs$);
$r3$.ɵelementEnd();
$r3$.ɵelementEnd();
}
@ -2359,7 +2421,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul");
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 5, 2, "li", $c1$);
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(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_Template_0, 1, 0, "ng-template", $_c0$);
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", $_c0$);
}
},
@ -236,9 +236,9 @@ describe('compiler compliance: directives', () => {
const MyComponentDefinition = `
const $_c0$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $_c1$ = ["directiveA", ""];
function MyComponent_ng_container_Template_0(rf, ctx) {
function MyComponent_ng_container_0_Template(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_Template_0, 2, 0, "ng-container", $_c0$);
$r3$.ɵtemplate(0, MyComponent_ng_container_0_Template, 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_Template_0, 0, 0, "ng-template", $c0_a0$);
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 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_Template_0, 1, 0, "div", $c0_a0$);
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 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_listener($event) { return ctx.noop(); });
$r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_0_listener($event) { return ctx.noop(); });
$r3$.ɵelementEnd();
}
},

View File

@ -6,6 +6,7 @@
* 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';
@ -393,7 +394,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
/**
* @desc d
* @meaning m
@ -402,7 +403,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -422,7 +423,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
@ -522,7 +523,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
/**
* @desc d
* @meaning m
@ -531,7 +532,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -551,7 +552,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
@ -922,7 +923,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", {
"interpolation": "\uFFFD0\uFFFD",
"startTagDiv": "\uFFFD#3\uFFFD",
@ -930,7 +931,7 @@ describe('i18n support in the view compiler', () => {
"closeTagDiv": "\uFFFD/#3\uFFFD"
});
function MyComponent_div_Template_2(rf, ctx) {
function MyComponent_div_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStart(1, "div");
@ -955,7 +956,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_Template_2, 5, 4, "div", $_c0$);
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 4, "div", $_c0$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@ -976,8 +977,8 @@ describe('i18n support in the view compiler', () => {
const output = String.raw `
const $_c0$ = ["src", "logo.png"];
const $_c1$ = [1, "ngIf"];
function MyComponent_img_Template_1(rf, ctx) {
const $_c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_img_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "img", $_c0$);
}
@ -986,7 +987,7 @@ describe('i18n support in the view compiler', () => {
"interpolation": "\uFFFD0\uFFFD"
});
const $_c2$ = ["title", $MSG_EXTERNAL_2367729185105559721$];
function MyComponent_img_Template_2(rf, ctx) {
function MyComponent_img_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "img", $_c0$);
$r3$.ɵi18nAttributes(1, $_c2$);
@ -1004,8 +1005,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_Template_1, 1, 0, "img", $_c1$);
$r3$.ɵtemplate(2, MyComponent_img_Template_2, 2, 1, "img", $_c1$);
$r3$.ɵtemplate(1, MyComponent_img_1_Template, 1, 0, "img", $_c1$);
$r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c1$);
}
if (rf & 2) {
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
@ -1043,8 +1044,8 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [1, "ngIf"];
function MyComponent_div_div_Template_4(rf, ctx) {
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_2_div_4_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2);
$r3$.ɵelementStart(1, "div");
@ -1059,13 +1060,13 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵi18nApply(0);
}
}
function MyComponent_div_Template_2(rf, ctx) {
function MyComponent_div_2_Template(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_div_Template_4, 3, 2, "div", $_c1$);
$r3$.ɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", $_c1$);
$r3$.ɵelementEnd();
$r3$.ɵelementEnd();
$r3$.ɵi18nEnd();
@ -1092,7 +1093,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_Template_3(rf, ctx) {
function MyComponent_div_3_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 3);
$r3$.ɵelementStart(1, "div");
@ -1115,8 +1116,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_Template_2, 5, 5, "div", $_c1$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 4, "div", $_c1$);
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", $_c1$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", $_c1$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1136,14 +1137,14 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", {
"startTagSpan": "\uFFFD#2\uFFFD",
"interpolation": "\uFFFD0\uFFFD",
"closeTagSpan": "\uFFFD/#2\uFFFD"
});
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$);
@ -1162,7 +1163,7 @@ describe('i18n support in the view compiler', () => {
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 3, 1, "div", $_c0$);
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 1, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
@ -1233,7 +1234,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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$);
}
@ -1241,7 +1242,7 @@ describe('i18n support in the view compiler', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template");
$r3$.ɵelementContainerStart(1);
$r3$.ɵi18n(2, $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$);
$r3$.ɵelementContainerEnd();
@ -1259,23 +1260,21 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
const $_c0$ = ["myClass", 1, "myClass", true];
const $_c0$ = [${AttributeMarker.Classes}, "myClass"];
const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$ = goog.getMsg("Text #1");
const $_c1$ = ["padding", 1, "padding", "10px"];
const $_c1$ = [${AttributeMarker.Styles}, "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");
$r3$.ɵelementStart(0, "span", $_c0$);
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$);
$r3$.ɵelementStyling($_c0$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
$r3$.ɵelementStart(2, "span");
$r3$.ɵelementStart(2, "span", $_c1$);
$r3$.ɵi18nStart(3, $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$);
$r3$.ɵelementStyling(null, $_c1$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1325,7 +1324,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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$);
$r3$.ɵpipe(1, "uppercase");
@ -1340,7 +1339,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_Template_0, 2, 3, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 2, 3, "ng-template");
}
}
`;
@ -1365,7 +1364,7 @@ describe('i18n support in the view compiler', () => {
"closeTagNgContainer": "\uFFFD/#3\uFFFD",
"interpolation": "\uFFFD0:1\uFFFD"
});
function MyComponent_ng_template_Template_2(rf, ctx) {
function MyComponent_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$, 1);
$r3$.ɵpipe(1, "uppercase");
@ -1383,7 +1382,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_Template_2, 2, 3, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 2, 3, "ng-template");
$r3$.ɵelementContainerStart(3);
$r3$.ɵpipe(4, "uppercase");
$r3$.ɵelementContainerEnd();
@ -1415,7 +1414,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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$);
}
@ -1430,7 +1429,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_Template_0, 1, 1, "ng-template");
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 1, "ng-template");
$r3$.ɵelementContainerStart(1);
$r3$.ɵi18n(2, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$);
$r3$.ɵelementContainerEnd();
@ -1461,7 +1460,7 @@ describe('i18n support in the view compiler', () => {
`;
const output = String.raw `
function MyComponent_ng_template_ng_template_ng_template_Template_1(rf, ctx) {
function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 3);
}
@ -1471,10 +1470,10 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵi18nApply(0);
}
}
function MyComponent_ng_template_ng_template_Template_2(rf, ctx) {
function MyComponent_ng_template_2_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 2);
$r3$.ɵtemplate(1, MyComponent_ng_template_ng_template_ng_template_Template_1, 1, 1, "ng-template");
$r3$.ɵtemplate(1, MyComponent_ng_template_2_ng_template_2_ng_template_1_Template, 1, 1, "ng-template");
$r3$.ɵi18nEnd();
}
if (rf & 2) {
@ -1491,11 +1490,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_Template_2(rf, ctx) {
function MyComponent_ng_template_2_Template(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_ng_template_Template_2, 2, 1, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_2_ng_template_2_Template, 2, 1, "ng-template");
$r3$.ɵi18nEnd();
}
if (rf & 2) {
@ -1511,7 +1510,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_Template_2, 3, 3, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 3, 3, "ng-template");
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -1536,7 +1535,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_Template_2(rf, ctx) {
function MyComponent_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18n(0, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$);
}
@ -1554,7 +1553,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_Template_2, 1, 1, "ng-template");
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template");
}
if (rf & 2) {
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
@ -1584,7 +1583,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_Template_3(rf, ctx) {
function MyComponent_ng_template_3_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$);
$r3$.ɵelement(1, "img", $_c0$);
@ -1599,7 +1598,7 @@ describe('i18n support in the view compiler', () => {
$r3$.ɵelement(2, "img", $_c0$);
$r3$.ɵi18nEnd();
$r3$.ɵelementContainerEnd();
$r3$.ɵtemplate(3, MyComponent_ng_template_Template_3, 2, 0, "ng-template");
$r3$.ɵtemplate(3, MyComponent_ng_template_3_Template, 2, 0, "ng-template");
}
}
`;
@ -1701,13 +1700,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$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "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_Template_2(rf, ctx) {
function MyComponent_div_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c1$);
$r3$.ɵi18n(1, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$);
@ -1726,7 +1725,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_Template_3(rf, ctx) {
function MyComponent_div_3_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $_c2$);
$r3$.ɵtext(1, " You have ");
@ -1749,8 +1748,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_Template_2, 2, 1, "div", $_c0$);
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 2, "div", $_c0$);
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 2, 1, "div", $_c0$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c0$);
}
if (rf & 2) {
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
@ -1942,7 +1941,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$ = [1, "ngIf"];
const $_c3$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}");
const $I18N_APP_SPEC_TS__4$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS__4$, {
"VAR_SELECT": "\uFFFD0:1\uFFFD"
@ -1956,7 +1955,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_Template_3(rf, ctx) {
function MyComponent_div_3_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $I18N_APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "div");
@ -1976,7 +1975,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_Template_3, 2, 1, "div", $_c3$);
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", $_c3$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -2050,7 +2049,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
const $_c0$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}");
const $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$, {
"VAR_SELECT": "\uFFFD0:1\uFFFD"
@ -2061,7 +2060,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_Template_2(rf, ctx) {
function MyComponent_span_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "span");
@ -2080,7 +2079,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_Template_2, 2, 1, "span", $_c2$);
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", $_c2$);
$r3$.ɵi18nEnd();
$r3$.ɵelementEnd();
}
@ -2113,7 +2112,7 @@ describe('i18n support in the view compiler', () => {
const $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$, {
"VAR_SELECT": "\uFFFD0\uFFFD"
});
const $_c0$ = [1, "ngIf"];
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
const $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {$interpolation}}}", {
"interpolation": "\uFFFD1:1\uFFFD"
});
@ -2126,7 +2125,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_Template_2(rf, ctx) {
function MyComponent_span_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$, 1);
$r3$.ɵelement(1, "span");
@ -2146,7 +2145,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_Template_2, 2, 2, "span", $_c2$);
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 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_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_div_click_0_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_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_my_app_click_0_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_Template_0(rf, ctx) {
function MyComponent_div_0_Template(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_Template_0_div_click_listener($event) {
$r3$.ɵlistener("click", function MyComponent_div_0_Template_div_click_1_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_Template_0_button_click_listener($event) {
$r3$.ɵlistener("click", function MyComponent_div_0_Template_button_click_2_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_Template_0, 3, 0, "div", $c0$);
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 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_listener($event) {
$r3$.ɵlistener("click", function MyComponent_Template_button_click_0_listener($event) {
$r3$.ɵrestoreView($s$);
const $user$ = $r3$.ɵreference(3);
return ctx.onClick($user$.value);

View File

@ -120,4 +120,70 @@ 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, InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
import {AttributeMarker, ViewEncapsulation} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@ -214,7 +214,6 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = ["@foo", ""];
const $e1_attrs$ = ["@bar", ""];
const $e2_attrs$ = ["@baz", ""];
@ -224,7 +223,7 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelement(0, "div", $e0_attrs$);
$r3$.ɵelement(0, "div");
$r3$.ɵelement(1, "div", $e1_attrs$);
$r3$.ɵelement(2, "div", $e2_attrs$);
}
@ -282,8 +281,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__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$.ɵ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$.ɵelementEnd();
} if (rf & 2) {
$r3$.ɵelementProperty(0, "@myAnimation", $r3$.ɵbind(ctx.exp));
@ -297,6 +296,64 @@ 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]', () => {
@ -366,8 +423,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
const $_c0$ = [${AttributeMarker.Styles}, "opacity", "1", ${AttributeMarker.SelectOnly}, "style"];
const $_c1$ = ["width", "height"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@ -379,14 +436,14 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementStart(0, "div", $_c0$);
$r3$.ɵelementStyling(null, $_c1$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementEnd();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
$r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
$r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
$r3$.ɵelementStyleProp(0, 0, $ctx$.myWidth);
$r3$.ɵelementStyleProp(0, 1, $ctx$.myHeight);
$r3$.ɵelementStylingApply(0);
$r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
}
@ -421,7 +478,7 @@ describe('compiler compliance: styling', () => {
};
const template = `
const _c0 = ["background-image"];
const $_c0$ = ["background-image"];
export class MyComponent {
constructor() {
this.myImage = 'url(foo.jpg)';
@ -456,7 +513,6 @@ describe('compiler compliance: styling', () => {
});
it('should support [style.foo.suffix] style bindings with a suffix', () => {
const files = {
app: {
'spec.ts': `
@ -476,7 +532,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) {
@ -564,8 +620,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
const $e0_attrs$ = [${AttributeMarker.Classes}, "grape", ${AttributeMarker.SelectOnly}, "class"];
const $e0_bindings$ = ["apple", "orange"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@ -578,13 +634,13 @@ describe('compiler compliance: styling', () => {
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵelementStyling($e0_cd$);
$r3$.ɵelementStyling($e0_bindings$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
$r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
$r3$.ɵelementClassProp(0, 0, $ctx$.yesToApple);
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToOrange);
$r3$.ɵelementStylingApply(0);
$r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
}
@ -606,7 +662,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>\`
@ -620,9 +676,7 @@ describe('compiler compliance: styling', () => {
};
const template = `
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"];
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", ${AttributeMarker.Styles}, "width", "100px", ${AttributeMarker.SelectOnly}, "class", "style"];
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@ -635,7 +689,6 @@ 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) {
@ -765,10 +818,13 @@ 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_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵpipe(1, "pipe");
$r3$.ɵpipe(2, "pipe");
$r3$.ɵpipe(3, "pipe");
@ -828,16 +884,18 @@ describe('compiler compliance: styling', () => {
};
const template = `
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
const $e0_classBindings$ = ["foo"];
const $e0_styleBindings$ = ["color"];
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostAttrs(ctx, $e0_attrs$);
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer, ctx);
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myColorProp, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
}
@ -959,10 +1017,10 @@ describe('compiler compliance: styling', () => {
};
const template = `
const _c0 = ["foo"];
const _c1 = ["width"];
const _c2 = ["bar"];
const _c3 = ["height"];
const $widthDir_classes$ = ["foo"];
const $widthDir_styles$ = ["width"];
const $heightDir_classes$ = ["bar"];
const $heightDir_styles$ = ["height"];
function ClassDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
@ -976,7 +1034,7 @@ describe('compiler compliance: styling', () => {
function WidthDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
$r3$.ɵelementStyling($widthDir_classes$, $widthDir_styles$, null, ctx);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
@ -987,7 +1045,7 @@ describe('compiler compliance: styling', () => {
function HeightDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
$r3$.ɵelementStyling($heightDir_classes$, $heightDir_styles$, null, ctx);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
@ -1014,7 +1072,8 @@ describe('compiler compliance: styling', () => {
template: '',
host: {
'style': 'width:200px; height:500px',
'class': 'foo baz'
'class': 'foo baz',
'title': 'foo title'
}
})
export class MyComponent {
@ -1029,6 +1088,9 @@ describe('compiler compliance: styling', () => {
@HostBinding('title')
title = 'some title';
@Input('name')
name = '';
}
@NgModule({declarations: [MyComponent]})
@ -1038,13 +1100,13 @@ describe('compiler compliance: styling', () => {
};
const template = `
const $_c0$ = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
const $_c1$ = ["width", "height", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
const $_c0$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵallocHostVars(2);
$r3$.ɵelementStyling($_c0$, $_c1$, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostAttrs(ctx, $_c0$);
$r3$.ɵelementStyling(null, null, $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_li_div_Template_1(rf, ctx) {
function MyComponent_ul_0_li_1_div_1_Template(rf, ctx) {
if (rf & 1) {
const $s$ = $i0$.ɵgetCurrentView();
$i0$.ɵelementStart(0, "div", $e0_attrs$);
$i0$.ɵlistener("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
$i0$.ɵlistener("click", function MyComponent_ul_0_li_1_div_1_Template_div_click_0_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_li_Template_1(rf, ctx) {
function MyComponent_ul_0_li_1_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "li");
$i0$.ɵtemplate(1, MyComponent_ul_li_div_Template_1, 2, 2, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_div_1_Template, 2, 2, "div", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -91,10 +91,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_ul_Template_0(rf, ctx) {
function MyComponent_ul_0_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "ul");
$i0$.ɵtemplate(1, MyComponent_ul_li_Template_1, 2, 1, "li", _c0);
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_Template, 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_Template_0, 2, 1, "ul", _c0);
$i0$.ɵtemplate(0, MyComponent_ul_0_Template, 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_Template_0(rf, ctx) {
function MyComponent_span_0_Template(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_Template_0, 2, 2, "span", _c0);
$i0$.ɵtemplate(0, MyComponent_span_0_Template, 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_span_Template_1(rf, ctx) {
function MyComponent_div_0_span_1_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "span");
$i0$.ɵtext(1);
@ -209,10 +209,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_span_Template_1, 2, 2, "span", $c1$);
$i0$.ɵtemplate(1, MyComponent_div_0_span_1_Template, 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_Template_0, 2, 1, "div", $c0$);
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 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_div_div_Template_1(rf, ctx) {
function MyComponent_div_0_div_1_div_1_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtext(1);
@ -277,10 +277,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_div_Template_1(rf, ctx) {
function MyComponent_div_0_div_1_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_div_div_Template_1, 2, 2, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_div_1_Template, 2, 2, "div", _c0);
$i0$.ɵelementEnd();
}
if (rf & 2) {
@ -289,10 +289,10 @@ describe('compiler compliance: template', () => {
}
}
function MyComponent_div_Template_0(rf, ctx) {
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
$i0$.ɵtemplate(1, MyComponent_div_div_Template_1, 2, 1, "div", _c0);
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_Template, 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_Template_0, 2, 1, "div", _c0);
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(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_Template_0, 1, 0, "ng-template", $c0$);
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 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_Template_0(rf, ctx) {
function MyComponent_ng_template_0_Template(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_Template_0, 1, 0, "ng-template", null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 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_Template_0(rf, ctx) { }
function MyComponent_ng_template_0_Template(rf, ctx) { }
// ...
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$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(); });
$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(); });
}
}`;
@ -440,4 +440,122 @@ 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,6 +6,7 @@
* 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';
@ -111,9 +112,9 @@ export class NgtscTestEnvironment {
/**
* Run the compiler to completion, and assert that no errors occurred.
*/
driveMain(): void {
driveMain(customTransformers?: CustomTransformers): void {
const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error);
const exitCode = main(['-p', this.basePath], errorSpy);
const exitCode = main(['-p', this.basePath], errorSpy, undefined, customTransformers);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
}

View File

@ -441,6 +441,39 @@ describe('ngtsc behavioral tests', () => {
});
});
it('should unwrap a ModuleWithProviders-like function if a matching literal type is provided for it',
() => {
env.tsconfig();
env.write(`test.ts`, `
import {NgModule} from '@angular/core';
import {RouterModule} from 'router';
@NgModule({imports: [RouterModule.forRoot()]})
export class TestModule {}
`);
env.write('node_modules/router/index.d.ts', `
import {ModuleWithProviders} from '@angular/core';
export interface MyType extends ModuleWithProviders {}
declare class RouterModule {
static forRoot(): (MyType)&{ngModule:RouterModule};
}
`);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]');
const dtsContents = env.getContents('test.d.ts');
expect(dtsContents).toContain(`import * as i1 from 'router';`);
expect(dtsContents)
.toContain(
'i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
});
it('should inject special types according to the metadata', () => {
env.tsconfig();
env.write(`test.ts`, `
@ -1194,4 +1227,32 @@ 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

@ -196,7 +196,21 @@ describe('NgCompilerHost', () => {
const host = createHost({ngHost});
expect(host.resourceNameToFileName('a', 'b')).toBe('someResult');
});
it('should resolve Sass imports to generated .css files', () => {
const host = createHost({files: {'tmp': {'src': {'a': {'style.css': 'h1: bold'}}}}});
expect(host.resourceNameToFileName('./a/style.scss', '/tmp/src/index.ts'))
.toBe('/tmp/src/a/style.css');
});
it('should resolve Less imports to generated .css files', () => {
const host = createHost({files: {'tmp': {'src': {'a': {'style.css': 'h1: bold'}}}}});
expect(host.resourceNameToFileName('./a/style.less', '/tmp/src/index.ts'))
.toBe('/tmp/src/a/style.css');
});
it('should resolve Stylus imports to generated .css files', () => {
const host = createHost({files: {'tmp': {'src': {'a': {'style.css': 'h1: bold'}}}}});
expect(host.resourceNameToFileName('./a/style.styl', '/tmp/src/index.ts'))
.toBe('/tmp/src/a/style.css');
});
});
describe('getSourceFile', () => {

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