Compare commits

..

295 Commits

Author SHA1 Message Date
e81982ef90 release: cut the v6.0.6 release 2018-06-20 15:57:10 -07:00
776bc38999 test(animations): properly reference body node for SSR environments (#23300)
PR Close #23300
2018-06-20 11:00:42 -07:00
7e7ef8ee25 docs: More edits (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
dd90d92573 docs: More form control API edits (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
1b862820e9 docs: Added multicast to observable descriptions (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
1a642231cd docs: formatting (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
138e0d79cd docs: More form control API references fixes (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
f0a43716e2 docs(docs-infra): Update with review changes (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
2be27fb888 docs(forms): update API reference for FormControl (#24255)
PR Close #24255
2018-06-19 10:53:14 -07:00
74f07f40e0 docs: Remove outdated Webpack guide and example (#24478)
A supporting Webpack guide will be introduced as part of the guidance
for ejecting from the Angular CLI.

Closes #23937

PR Close #24478
2018-06-18 15:03:22 -07:00
aa66e84e44 docs: push animation api doc changes back to stable (#24525)
PR Close #24525
2018-06-15 09:55:58 -07:00
e1bc3f5c1a build(docs-infra): ensure all headings are sentence cased (#24527)
PR Close #24527
2018-06-15 09:13:46 -07:00
f5b366147b fix(core): Injector correctly honors the @Self flag (#24520)
Injector was incorrectly returning instance from parent injector even
when `@Self` was specified.

PR Close #24520
2018-06-14 16:42:08 -07:00
3147a92ee9 build: fix broken circleci build (#24513)
Bazel 0.14 allocates memory differently and runs out of RAM on a 4G circleci worker
so use an xlarge machine for the integration_test job.
Cherry-pick of #24512 onto patch branch.

PR Close #24513
2018-06-14 14:15:20 -07:00
ffedbde0b3 test(bazel): fix flakey bazel integration e2e test (#24522)
PR Close #24522
2018-06-14 14:15:00 -07:00
e543c734ab fix(compiler): support . in import statements. (#20634)
fix #20363

PR Close #20634
2018-06-13 20:29:22 -07:00
fbe6871a94 build(aio): add github links to API doc members (#24000)
This change adds Github edit and view links to methods
and decorator options.

It is possible to add these to properties also but the
UI is rather tight as these are displayed in a table.

PR Close #24000
2018-06-13 16:51:59 -07:00
f43cb9398c build(aio): compute breadcrums for all API doc types (#24000)
PR Close #24000
2018-06-13 16:51:59 -07:00
1e04df9ac7 build(aio): fix decorator doc "inherited from" heading (#24000)
We should not include the package path in the inherited
from heading for decorator API docs

PR Close #24000
2018-06-13 16:51:59 -07:00
840ca05fe8 fix(aio): tidy up API doc styles (#24000)
* Code anchors should inherit the font size from their container
* Table headings should align with content

PR Close #24000
2018-06-13 16:51:59 -07:00
facc9d498d build(aio): fix broken doc-gen unit test (#24000)
PR Close #24000
2018-06-13 16:51:59 -07:00
9d0999027f build(aio): remove invalid H3 usage notes heading (#24000)
This heading is too high for the section because the
method name is a H3 but it cannot be a H4 because
usage notes may contain H4 headings.

PR Close #24000
2018-06-13 16:51:59 -07:00
c00dd5a3f3 build(aio): rearrange processors to ensure we catch all content errors (#24000)
PR Close #24000
2018-06-13 16:51:59 -07:00
3908a63381 docs: fix invalid headings (#24000)
PR Close #24000
2018-06-13 16:51:54 -07:00
20f9e51b8f build(aio): map H3 headings into H4 headings for certain templates (#24000)
The sections such as methods and decorator options are already headed
by a H3 heading so we need to map the H3 headings in the API doc source
down to H4 headings.

This commit includes general heading mapping functionality accessible via
the `marked` Nunjucks filter.

PR Close #24000
2018-06-13 16:47:42 -07:00
232203242a build(aio): rearrange decorator API doc template (#24000)
The overview of the decorator options is now a table.
The detailed description of each option is now a full section.

PR Close #24000
2018-06-13 16:47:42 -07:00
0ca634e697 build(aio): remove unused @linkDocs alias for @link jsdoc tag (#24000)
PR Close #24000
2018-06-13 16:47:42 -07:00
7fd9918024 docs: remove unnecessary @linkDocs tags (#24000)
It is cleaner and simpler to use just a straightforward link.

PR Close #24000
2018-06-13 16:47:42 -07:00
b0aacb81d4 build(aio): improve automatic linking of code items (#24000)
This commit adds new link disambiguators that mean that more
code links can be generated automatically in a sensible way.

The best example is the use of properties within class, interface and
enum documentation.

PR Close #24000
2018-06-13 16:47:42 -07:00
6c850eb031 docs(aio): remove unused guide doc (#24000)
This was erroneously committed into master, when it was really only
supposed to demo what the pages might look like.

PR Close #24000
2018-06-13 16:47:42 -07:00
0df61ad107 build(aio): refactor the decorator doc processing (#24000)
PR Close #24000
2018-06-13 16:47:42 -07:00
fa985ac067 build(aio): do not allow @usageNotes on properties (#24000)
PR Close #24000
2018-06-13 16:47:42 -07:00
06f9197361 build(aio): update to latest dgeni-packages@0.26.2 (#24000)
This update allows us to autolink to methods and properties, which
means that we can change things like `{@link transition transition()}`
to just `transition()`.

PR Close #24000
2018-06-13 16:47:42 -07:00
c2bad1249e build(aio): add content rule to prevent usageNotes in non-export API docs (#24000)
This commit also factors out `API_CONTAINED_DOC_TYPES` to be used by
both `filterContainedDocs` and `addAllowedPropertiesRules`.

PR Close #24000
2018-06-13 16:47:42 -07:00
9d69ff8ddd build(aio): don't constrain checkContentRules to run before another processor (#24000)
We don't really care when this processor runs as long as it happens
after the tags have been extracted.
By not constraining its `runBefore` property we can ensure that other
processors can be run before it.

PR Close #24000
2018-06-13 16:47:42 -07:00
3fd33f8eb0 build(aio): do not try to auto-link to internal API items (#24000)
This would cause dangling links since the target, being internal,
would not exist in the docs.

PR Close #24000
2018-06-13 16:47:42 -07:00
501a243b3f build(aio): don't render @Annotation tags (#24000)
Because we were "ignoring" these tags they were being
rendered as part of the previous tag.
What we really want to do is know about them, so that we
don't break the doc-gen but then ignore them when rendering.

PR Close #24000
2018-06-13 16:47:42 -07:00
b91b9efc91 release: cut the v6.0.5 release 2018-06-13 15:00:28 -07:00
03718c95ce docs: change capitalization for css hex color values (#23511)
PR Close #23511
2018-06-13 13:31:30 -07:00
0834710c18 docs(aio): remove links to outdated live examples from the API documenation (#23966)
Closes #21525

PR Close #23966
2018-06-13 13:29:13 -07:00
9a9a7de9a4 docs: add message property to compose-message component (#24310)
PR Close #24310
2018-06-13 13:28:47 -07:00
ff51691221 docs: fix typo (#24470)
PR Close #24470
2018-06-13 11:54:26 -07:00
2ad90ab0d3 docs: fix wording in 4-10 (#24385)
PR Close #24385
2018-06-13 11:53:20 -07:00
c5872e6782 docs(aio): Reorganize style guide sections on prefixing components/directives (#22571)
Closes https://github.com/angular/angular/issues/22081

PR Close #22571
2018-06-13 11:20:43 -07:00
d20877bf3f fix(router): fix lazy loading of aux routes (#23459)
Fixes #10981

PR Close #23459
2018-06-13 11:20:20 -07:00
b3089b4963 build: update buildifier to latest (#24296) (#24453)
this matches the version in ngcontainer:0.3.1

PR Close #24296

PR Close #24453
2018-06-13 11:19:59 -07:00
880b03101e build: update to Bazel 0.14.0 (#24296) (#24453)
Also update usage of the ctx.actions.args to a newer preferred API

PR Close #24296

PR Close #24453
2018-06-13 11:19:59 -07:00
451d996414 docs: change aio scope to docs-infra (#24410)
Related to #24295

PR Close #24410
2018-06-12 11:36:14 -07:00
ea3669e334 fix(bazel): Allow ng_module to depend on targets w no deps (#24446)
PR Close #24446
2018-06-12 11:35:53 -07:00
ea2987c7af fix(service-worker): fix SwPush.unsubscribe() (#24162)
Fixes #24095

PR Close #24162
2018-06-11 14:04:31 -04:00
5b823dedcc test(service-worker): allow SwPush tests to run on Node.js (#24162)
PR Close #24162
2018-06-11 14:04:30 -04:00
80f7c83bdd test(service-worker): add tests for SwPush (#24162)
PR Close #24162
2018-06-11 14:04:30 -04:00
53072b10da refactor(service-worker): minor mocks refactoring (#24162)
PR Close #24162
2018-06-11 14:04:30 -04:00
30c2f560b1 build(docs-infra): ensure dist/ directory is cleaned before running tsc --watch (#24372)
PR Close #24372
2018-06-11 09:18:46 -07:00
18c4976f3b build(docs-infra): upgrade preview server to latest @types/shelljs (#24372)
PR Close #24372
2018-06-11 09:18:46 -07:00
0139173c7c fix(animations): always render end-state styles for orphaned DOM nodes (#24236)
This patch ensures that any destination animation styling (state values)
are always applied even if the DOM node is not apart of the DOM.

PR Close #24236
2018-06-08 16:35:27 -07:00
f3c9954a68 docs: adds information about the VSCode clang-format extension (#24351)
PR Close #24351
2018-06-08 16:35:06 -07:00
e8765352e8 fix(docs-infra): use script nomodule to load IE polyfills, skip other polyfills (#24317)
This commit includes two changes:
1. It changes the unreliable dynamic way of loading IE polyfills to use
   `<script nomodule>` instead - for IE it's equivalent to a regular script tag
   while modern browsers will ignore it.
2. It removes other polyfills for browsers not supporting `Object.assign` as
   this API is supported by Chrome 45+, Firefox 34+ and Safari 9+ i.e. it's been
   supported for some time.

Note that as of June 2018 Googlebot uses Chrome 41 to render sites to be
indexed. Chrome 41 doesn't support `Object.assign` but it also doesn't support
ES6 modules so it'll load polyfills meant for IE - which it should do anyway
as it doesn't support most of ES6.

Fixes #23647

PR Close #24317
2018-06-08 13:34:06 -07:00
9e61ad897e build(docs-infra): ensure stability is computed before the API list (#24356)
Previously the API list was being generated before the stability had
been computed. This meant that the API list page showed no API docs
when filtering by `stable` stability status.

Closes #24329

PR Close #24356
2018-06-08 13:33:32 -07:00
0d81151cbc docs(aio): add mix and connect to front page campaigns (#24357)
PR Close #24357
2018-06-08 13:31:28 -07:00
70319722a1 docs(aio): Added resource link to Amexio Canvas Web Based IDE (#24336)
PR Close #24336
2018-06-07 18:46:32 -04:00
cfa5b6567d build(bazel): fix ng_package rollup root dir for fesm2015 output (#24298)
PR Close #24298
2018-06-07 17:56:10 -04:00
41698bf5fd release: cut the v6.0.4 release 2018-06-06 11:49:25 -07:00
23c50e27fc build(docs-infra): log warning rather than error if content errors are not fatal (#24320)
PR Close #24320
2018-06-06 10:25:05 -07:00
0ae8ea254a build(aio): ensure the correct decorator properties are merged (#24289)
Previously only the `description` and `usageNotes` were being copied over
from the call-member of the decorator interface. Important properties such
as `shortDescription` were missed.

These are now added and the code has been refactored to make it simpler and
clearer to update which properties get copied as the requirements change.

PR Close #24289
2018-06-06 10:23:47 -07:00
19deca159b fix(animations): retain trigger-state for nodes that are moved around (#24238)
This patch ensures that if a list of nodes (that contain
animation triggers) are moved around then they will retain their
trigger-value state when animated again at a later point.

PR Close #24238
2018-06-05 18:29:47 -07:00
dc3e8aa0fb fix(forms): properly handle special properties in FormGroup.get (#22249)
closes #17195

PR Close #22249
2018-06-05 18:28:13 -07:00
a9222c0ade docs(aio): Add null type to form validation example (#23949)
Closes #20282

PR Close #23949
2018-06-05 17:32:36 -07:00
ea3127e3f9 build(bazel): ran format (#24279)
PR Close #24279
2018-06-05 13:36:27 -07:00
8e30f7b1aa build(bazel): ran buildifier (#24279)
PR Close #24279
2018-06-05 13:36:27 -07:00
46d81b48ac build(bazel): fix //packages/platform-browser/test:test_web (#24279)
PR Close #24279
2018-06-05 13:36:27 -07:00
3e51a2cb26 build(bazel): enable manual ts_web_test_suite tests that require static_files (#24279)
PR Close #24279
2018-06-05 13:36:27 -07:00
faf60d9310 docs: rename the "aio" component to "docs-infra" (#24295)
The legacy "aio" is still active for currently pending PRs,
The GH label has been renamed as well

PR Close #24295
2018-06-04 17:25:13 -07:00
0639cb9de1 fix(aio): remove unnecessary scrollbar in code-tabs (#24207)
PR Close #24207
2018-06-04 12:07:25 -07:00
810d025488 fix(aio): add right-margin to .home link (#24207)
PR Close #24207
2018-06-04 12:07:25 -07:00
efe49c141a docs(aio): clean up frequent ng-modules (#24025)
Closes #24017

PR Close #24025
2018-06-04 10:13:19 -07:00
a3d9878c9d docs(aio): remove an extraneous apostrophe (#24293)
PR Close #24293
2018-06-04 10:11:28 -07:00
10213d0ca0 docs(common): improve deprecation notices to be parsed by tslint
Closes: #24237
2018-06-04 09:37:05 -07:00
0e1919c2db build(bazel): update bazel integration test to test secondary angular imports such as @angular/common/http (#24170)
PR Close #24170
2018-06-01 13:40:48 -07:00
d579b8ce05 build(bazel): fix bazel built es5 ngfactory with secondary entry-point angular imports (#24170)
PR Close #24170
2018-06-01 13:40:48 -07:00
d69ba735ee feat(compiler-cli): update tsickle to 0.29.x (#24241)
PR Close #24241
2018-06-01 08:37:46 -07:00
2991b1b217 fix(platform-server): avoid dependency cycle when using http interceptor (#24229)
Fixes #23023.

When a HTTP Interceptor injects HttpClient it causes a DI cycle. This fix is to use Injector to lazily inject HTTP_INTERCEPTORS while setting up the HttpHandler on the server so as to break the cycle.

PR Close #24229
2018-06-01 08:33:46 -07:00
b18cf21e99 build(bazel): re-enable packages/upgrade/test:test_web test with static_files in ts_web_test_suite (#24214)
PR Close #24214
2018-05-31 16:13:06 -07:00
c17098dae6 fix(platform-server): don't reflect innerHTML property to attibute (#24213)
Fixes #19278.

innerHTML is conservatively marked as an attribute for security purpose so that it's sanitized when set. However this same mapping is used by the server renderer to decide whether the `innerHTML` property needs to be reflected to the `innerhtml` attribute. The fix is to just skip the property to attribute reflection for `innerHTML`.

PR Close #24213
2018-05-31 10:08:29 -07:00
43e3073687 build: update to rules_nodejs 0.9.1 and rules_typescript 0.15.0 (#24212)
PR Close #24212
2018-05-31 10:08:07 -07:00
f28878f92f build: sync g3 exclude list from copybara to ngbot (#24224)
PR Close #24224
2018-05-31 10:07:45 -07:00
a10bdefa8b docs(aio): Add GDE Kim Maida to contributors 2018-05-30 17:34:15 -07:00
2f377dbcdd style(compiler-cli): fix typo error (#23897)
PR Close #23897
2018-05-30 17:29:04 -07:00
e2e7b4943e docs: fix typo (#24210)
closes #24191

PR Close #24210
2018-05-30 17:06:13 -07:00
b8a1363bb2 docs(forms): fix API doc (#24210)
closes #24090

PR Close #24210
2018-05-30 17:06:13 -07:00
e9e6a58dd0 docs: fix typo (#24210)
closes #23891

PR Close #24210
2018-05-30 17:06:12 -07:00
5932a79713 refactor(animations): fix typo (#24210)
closes #22459

PR Close #24210
2018-05-30 17:06:12 -07:00
7c6a082afd docs: fix typo if FAQ section (#24210)
closes #22360

PR Close #24210
2018-05-30 17:06:12 -07:00
8a2fe5b7c9 docs: fix WebStorm name (#24210)
closes #21900

PR Close #24210
2018-05-30 17:06:12 -07:00
c9eb4910eb fix(animations): Fix browser detection logic (#24188)
Element type is being polyfilled on the server now and cannot be used to detect browser environment.

PR Close #24188
2018-05-30 16:39:09 -07:00
a634a5abbc Revert "docs: update docs to use HttpClientModule instead of HttpModule (#22727)"
This reverts commit b5533e0ee5.
2018-05-30 16:13:59 -07:00
41225026e4 docs: remove unfinished observables file (#23801)
PR Close #23801
2018-05-30 14:44:29 -07:00
e9f2203347 fix(platform-server): avoid clash between server and client style encapsulation attributes (#24158)
Previously the style encapsulation attributes(_nghost-* and _ngcontent-*) created on the server could overlap with the attributes and styles created by the client side app when it botstraps. In case the client is bootstrapping a lazy route, the client side styles are added before the server-side styles are removed. If the components on the client are bootstrapped in a different order than on the server, the styles generated by the client will cause the elements on the server to have the wrong styles.

The fix puts the styles and attributes generated on the server in a completely differemt space so that they are not affected by the client generated styles. The client generated styles will only affect elements bootstrapped on the client.

PR Close #24158
2018-05-30 14:39:10 -07:00
906b3ec8e7 fix(platform-server): provide Domino DOM types globally (#24116)
Fixes #23280, #23133.

This fix lets code access DOM types like Node, HTMLElement in the code. These are invariant across requests and the corresponding classes from Domino can be safely provided during platform initialization.

This is needed for the current sanitizer to work properly on platform-server. Also allows HTML types in injection - Ex. `@inject(DOCUMENT) doc: Document`.

PR Close #24116
2018-05-30 14:38:57 -07:00
1cb0c4644a feat(platform-server): use EventManagerPlugin on the server (#24132)
Previously event handlers on the server were setup directly. This change makes it so that the event registration on the server go through EventManagerPlugin just like on client. This allows us to add custom event registration handlers on the server which allows us to hook up preboot event handlers cleanly.

PR Close #24132
2018-05-30 14:38:39 -07:00
9d28a27215 test(animations): fix Node.js detection in animation tests (#24139)
PR Close #24139
2018-05-30 14:36:35 -07:00
f04aef48f2 build: replace hard-coded master branch with the variable (#24199)
PR Close #24199
2018-05-30 11:31:39 -07:00
d249852f4a build: update rules_webtesting (#24198)
this includes a fix for spammy browser installs that makes our CI logs hard to read

PR Close #24198
2018-05-30 11:31:03 -07:00
707dd59767 build: update brotli version in WORKSPACE (#24194)
The updated version includes the fix for google/brotli#671.

PR Close #24194
2018-05-30 11:30:40 -07:00
4d2570576a docs(aio): fix typo for @NgModuledecorator to @NgModule decorator (#24201)
closes #23974

PR Close #24201
2018-05-30 11:24:12 -07:00
76e58e6cca docs: fix typo (#24201)
closes #23853

PR Close #24201
2018-05-30 11:24:12 -07:00
a27066b9d2 style(compiler): fix up grammar in error message (#24201)
closes #22746

PR Close #24201
2018-05-30 11:24:12 -07:00
29dfc8f3ac docs(bazel): improve error message for ng_package with no metadata (#22964)
PR Close #22964
2018-05-30 10:04:35 -07:00
b5533e0ee5 docs: update docs to use HttpClientModule instead of HttpModule (#22727)
Updated most examples to use HttpClientModule instead of deprecated HttpModule

fix #19280

PR Close #22727
2018-05-30 10:03:14 -07:00
b275d378df docs(aio): add blox material library to resources (#20539)
PR Close #20539
2018-05-30 10:02:32 -07:00
96655f7aac style(compiler): fix typo and formatting (#23729)
PR Close #23729
2018-05-29 18:48:55 -04:00
253f509493 refactor(core): use Partial<T> for MetadataOverride (#24103)
Allows to write:

const fixture = TestBed
      .overridePipe(DisplayNamePipe, { set: { pure: false } })
      .createComponent(MenuComponent);

when you only want to set the `pure` metadata,
instead of currently:

const fixture = TestBed
      .overridePipe(DisplayNamePipe, { set: { name: 'displayName', pure: false } })
      .createComponent(MenuComponent);

which forces you to redefine the name of the pipe even if it is useless.

Fixes #24102

PR Close #24103
2018-05-29 18:40:05 -04:00
5a02ae2f84 docs: update lts and labs practices (#23922)
PR Close #23922
2018-05-29 18:01:31 -04:00
f860752902 refactor(aio): rename method (loadContainingCustomElements --> loadContainedCustomElements) (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
efa126f157 fix(aio): avoid loading the ToC component until it is necessary (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
715135b117 fix(aio): show embedded ToC (#23944)
On narrow screens (where there is not enough room on the right to show
the floating ToC), an embedded ToC is shown (via an `<aio-toc embedded>`
element in the document). Since ToC was not a custom element, the
component was not instantiated for the embedded element.

This commit fixes it by making `aio-toc` a custom element and loading it
manually for the floating ToC (if necessary).

PR Close #23944
2018-05-29 18:00:34 -04:00
c30fc52d99 feat(aio): add component for lazy-loading custom element (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
434eb971e4 refactor(aio): order custom elements by selector (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
6e31e22d41 fix(aio): do not load custom elements again while already loading (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
8fef926cd2 build(aio): update payload size limits (#23944)
PR Close #23944
2018-05-29 18:00:34 -04:00
7698afedb1 docs(http): correct spelling error (#23675)
Correct a spelling error. I changed HttpParms to HttpParams
PR Close #23675
2018-05-29 16:55:07 -04:00
a583d12660 feat(aio): display code-tabs in a Material Design "card" (#24027)
This helps to connect the "content" of the tab to its label.

Closes #23985

PR Close #24027
2018-05-29 16:52:06 -04:00
a867d71ece feat(aio): render hover status on code-tabs (#24027)
The Material Design spec states that there should be a change
of background when hovering over a tab label.

See https://material.io/design/components/tabs.html#states

Related to #23985

PR Close #24027
2018-05-29 16:52:06 -04:00
84b43daf65 build: pick up rules_typescript karma fix (#24184)
Error was Cannot find module 'karma/bin/karma'

PR Close #24184
2018-05-29 16:50:11 -04:00
9b3423b50d docs(aio): fix link to correct bio image (#24150)
PR Close #24150
2018-05-29 16:27:49 -04:00
d6041d83ec docs(aio): Added Mashhood as GDE in contributors (#24157)
PR Close #24157
2018-05-29 16:20:05 -04:00
a638f504eb docs(aio): add material community components (#24042)
PR Close #24042
2018-05-25 13:45:40 -04:00
a29c756251 refactor(aio): improve logging output in update-preview-server.sh (#24071)
PR Close #24071
2018-05-25 13:44:44 -04:00
f206eb94bb docs(aio): add Juri Strumpflohner to GDE resources (#24086)
PR Close #24086
2018-05-25 13:43:50 -04:00
3d6e82eccd docs(aio): remove outdated rangle link (#24108)
PR Close #24108
2018-05-25 13:41:59 -04:00
154289305e release: cut the v6.0.3 release 2018-05-22 16:16:26 -07:00
469b1e4a9a docs: add doc to event-management api (#23656)
PR Close #23656
2018-05-22 17:33:49 -04:00
7fee1fd442 docs(aio): fix typo (#23925)
PR Close #23925
2018-05-22 16:35:18 -04:00
0ee5b7efa5 fix(service-worker): check platformBrowser before accessing navigator.serviceWorker (#21231)
PR Close #21231
2018-05-22 15:09:32 -04:00
afff84c03f docs(aio): Remove outdated README.md from cli-quickstart zip (#23947)
Closes #23936

PR Close #23947
2018-05-22 13:37:15 -04:00
23f2a7069f docs: fix typo (#23998)
"Made" doesn't make sense (redoing and closing #23940)
PR Close #23998
2018-05-22 13:35:13 -04:00
42260702f7 docs(aio): Add missing dependencies and files to testing zip file download (#23948)
Closes #23060

PR Close #23948
2018-05-21 16:12:40 -04:00
eec231d21e docs(aio): add ant design of angular in resources (#23953)
PR Close #23953
2018-05-21 16:11:13 -04:00
95344d6039 fix(aio): avoid unnecessary re-calculations in live-examples (#23960)
With `plnkrs`, we used to choose a different plnkr mode (normal vs
embedded) based on the size of the screen. This affected the layout of
the plnkr page ("embedded" plnkr mode was usable on small screens, while
"normal" mode wasn't). This is not to be confused with the live-example
mode we use today to determine whether the live-example should be a link
(that open StackBlitz on a new page) or embedded into the document
(using an iframe).

Since we no longer need to change the live-example URL based on the
screen size, there is no need to listen for rezise events on Window. The
necessary properties can be computed once and certain variables are
obsolete.

PR Close #23960
2018-05-21 16:10:12 -04:00
1c9839e91d fix(aio): allow setting live-example title from content (#23960)
Previously, it was possible to set the live-example title as content in
the `<live-example>` element. This relied on our custom loader
functionality that extracted the content from the DOM element before
passing it to the Angular compiler and stored it on a property for later
retrieval.
Since we switched to custom elements (and got rid of the custom loader),
the property is no longer populated with the contents. As a result, many
live examples show the default title ("live example") instead of the one
specified as content.

This commit fixes it by projecting the content into an invisible node
for later retrieval  (similar to what we do in other components, such as
the `CodeExampleComponent`).

PR Close #23960
2018-05-21 16:10:12 -04:00
bc1a66e907 refactor(aio): clean up attribute-utils (#23960)
PR Close #23960
2018-05-21 16:10:12 -04:00
2a83dbb0d8 style(aio): remove background from lazy-loading concept icon (#23950)
Fixes #23938

PR Close #23950
2018-05-21 16:08:08 -04:00
4007d00403 docs(aio): add mhartington to gde (#23777)
PR Close #23777
2018-05-21 16:05:18 -04:00
61e32ac3c4 docs(aio): fix typo (#23990)
are are -> are
PR Close #23990
2018-05-21 16:04:09 -04:00
43ee10fbbd docs(aio): changed 'onVoted' output property to 'voted' to be in line with the styleguide (#23832)
PR Close #23832
2018-05-16 17:23:05 -04:00
eb90039ea1 docs(aio): Expose server and CLI configuration for universal in guide (#23842)
Closes #23795

PR Close #23842
2018-05-16 17:21:44 -04:00
5d318ff234 ci: ensure github-robot listens on circleci builds (#23863)
PR Close #23863
2018-05-16 17:20:44 -04:00
f3361abdd7 test: don't run unit tests on Firefox (#23942)
PR Close #23942
2018-05-16 17:19:45 -04:00
52cbe894e9 docs(aio): add Angular Conf Australia to events (#22929)
Angular Conf Australia 2018 will be held at June 22 in Melbourne, Australia! 

https://www.angularconf.com.au/
PR Close #22929
2018-05-16 17:18:47 -04:00
41c2030534 docs(aio): fix path to observables guide (#23858)
PR Close #23858
2018-05-16 17:16:48 -04:00
daee41a40a docs(aio): update HTTP error test example (#22844)
Update the example to match the description preceding it, which refers to the
use of the error method and ErrorEvent rather than the flush method with a
non-2xx status as shown previously.

PR Close #22844
2018-05-16 17:15:20 -04:00
68bd45ba87 docs(bazel): add a link to the Bazel doc (#22940)
The developer doc mentions but doesn't link to BAZEL.md. Add link and fix capitalization.
PR Close #22940
2018-05-16 17:14:04 -04:00
0f6407750b docs(aio): Remove Intertech with no courses scheduled (#22867)
PR Close #22867
2018-05-16 17:09:44 -04:00
95fca24fd8 feat(aio): add brand and concept icons, img style class more flexible (#23589)
PR Close #23589
2018-05-15 15:36:07 -07:00
a8f6542115 release: cut the v6.0.2 release 2018-05-15 12:34:04 -07:00
c6b618d020 fix(service-worker): deprecate versionedFiles in asset-group resources (#23584)
Since `versionedFiles` behaves in the exact same way as `files`, there
is no reaason to have both. Users should use `files` instead.

This commit deprecates the property and prints a warning when coming
across an asset-group that uses it. It should be completely removed in
a future version.

Note, it has also been removed from the default `ngsw-config.json`
template in angular/devkit#754.

PR Close #23584
2018-05-15 12:19:08 -07:00
ad6052e397 test: switch to ts_web_test_suite (#23859)
Unit tests now run on Firefox too

PR Close #23859
2018-05-15 11:41:58 -07:00
e9d1709156 build: only match version tags for BUILD_SCM_VERSION (#23903)
PR Close #23903
2018-05-14 12:44:26 -07:00
b5d3de50cc docs(aio): use heroesUrl (#23884)
PR Close #23884
2018-05-14 10:38:15 -07:00
734d37b231 build(aio): improve enum API rendering (#23872)
* The member details section is now called "Members", rather
than "Properties".
* The property table now displays appropriate table headings:
"Member", "Value", "Description".
* The "Value" column is not shown if none of the members have
a value.

Closes #22678

PR Close #23872
2018-05-14 10:37:42 -07:00
bc2063807c build(aio): ensure usageNotes are copied into decorator API docs (#23901)
PR Close #23901
2018-05-14 10:35:33 -07:00
2a528fcb15 build(aio): display types of API const docs correctly (#23850)
Previously these docs always displayed `any` as the type
of the const export. Now the type is computed correctly from
the declared type or initializer of the constant.

PR Close #23850
2018-05-11 16:44:51 -04:00
752b83ac81 fix(animations): do not throw errors when a destroyed component is animated (#23836)
PR Close #23836
2018-05-11 16:08:15 -04:00
56be3375ec fix(aio): make background transparent in 144x144 PWA icon (#23851)
Fixes #23827

PR Close #23851
2018-05-11 12:38:21 -04:00
1b83b3fb15 docs(aio): add Cory Rylan to GDE resources (#23840)
PR Close #23840
2018-05-11 12:32:38 -04:00
14d4625ede docs: update version to 6 in language-service (#20795)
PR Close #20795
2018-05-11 12:22:51 -04:00
fd880a8de4 build: replace the old publish script with a new bazel-based one 2018-05-10 23:01:41 -07:00
bd3dddce4b release: cut the v6.0.1 release 2018-05-10 22:22:49 -07:00
1336a9451f build: update to latest TypeScript rules (#23828)
Fixes #23810

PR Close #23828
2018-05-10 16:45:38 -07:00
d280077412 fix(elements): always check to create strategy (#23825)
PR Close #23825
2018-05-10 16:07:11 -07:00
2b578f5c61 docs(elements): add angular element term to glossary (#23807)
PR Close #23807
2018-05-10 15:50:00 -07:00
12dcb313af style: remove empty comments (#23404)
PR Close #23404
2018-05-10 15:48:13 -07:00
f576851ecc docs(elements): emphasize future direction, update link (#23806)
PR Close #23806
2018-05-10 15:46:53 -07:00
ca6cb66c32 docs: change release_schedule.md to link to new angular release page in docs (#23808)
PR Close #23808
2018-05-10 15:45:28 -07:00
484233f6c1 docs(aio): add Alain Chautard in GDE list (#23783)
PR Close #23783
2018-05-10 12:07:10 -07:00
3d8799b3a2 fix(router): avoid freezing queryParams in-place (#22663)
The recognizer code used to call Object.freeze() on queryParams before
using them to construct ActivatedRoutes, with the intent being to help
avoid common invalid usage. Unfortunately, Object.freeze() works
in-place, so this was also freezing the queryParams on the actual
UrlTree object, making it more difficult to manipulate UrlTrees in
things like UrlHandlingStrategy.

This change simply shallow-copies the queryParams before freezing them.

Fixes #22617

PR Close #22663
2018-05-10 07:54:11 -07:00
8733843c11 fix(router): correct the segment parsing so it won't break on ampersand (#23684)
PR Close #23684
2018-05-10 07:53:54 -07:00
f1097914c5 ci: add config for size plugin of the github rebot (#23665)
PR Close #23665
2018-05-10 07:53:34 -07:00
06776d1d10 fix(aio): fix error in import after RxJS 6 migration (#22886)
PR Close #22886
2018-05-09 11:52:04 -07:00
2b31b6dc3f refactor(service-worker): sort manifest url/hashTable entries (#23586)
This makes it easier to quickly check whether a specific file ended up
in the manifest, for example when debugging.

PR Close #23586
2018-05-09 11:51:23 -07:00
2254ac23e4 fix(service-worker): correctly handle requests with empty clientId (#23625)
Requests from clients that are not assigned a client ID by the browser
will produce `fetch` events with `null` or empty (`''`) `clientId`s.

Previously, the ServiceWorker only handled `null` values correctly. Yet
empty strings are also valid (see for example [here][1] and [there][2]).
With this commit, the SW will interpret _all_ falsy `clientId` values
the same (i.e. "no client ID assigned") and handle them appropriately.

Related Chromium issue/discussion: [#832105][3]

[1]: 4cc72bd0f1/docs/index.bs (L1392)
[2]: https://w3c.github.io/ServiceWorker/#fetchevent-interface
[3]: https://bugs.chromium.org/p/chromium/issues/detail?id=832105

Fixes #23526

PR Close #23625
2018-05-09 11:50:03 -07:00
38c678fdcd test(service-worker): support mock requests with null/empty client ID (#23625)
PR Close #23625
2018-05-09 11:50:03 -07:00
1a655836cb test(service-worker): improve adding clients in SwTestHarness (#23625)
This commits changes how clients are added in `SwTestHarness`, so that
the behavior in tests closer mimics what would happen in an actual
ServiceWorker.
It also removes auto-adding clients when calling `clients.get()`, which
could hide bugs related to non-existing clients.

PR Close #23625
2018-05-09 11:50:03 -07:00
2e466f4bea build(bazel): update to rules_typescript 0.12.3 (#23617)
PR Close #23617
2018-05-09 11:47:11 -07:00
7b06fa88ab build(aio): include navigation.json changes in docs-watch (#23698)
Closes #23582

PR Close #23698
2018-05-09 11:45:19 -07:00
3d712894ae fix(aio): add link to v5 docs (#23794)
Fixes #23781

PR Close #23794
2018-05-09 11:44:45 -07:00
75b8edae03 ci: Remove Chuck from pullapprove (#23798)
Jason takes over his role on core, Keen for everything else

PR Close #23798
2018-05-09 11:43:47 -07:00
fe7f48c1d5 docs(aio): Upgrade example dependencies to Angular V6 (#23660)
PR Close #23660
2018-05-08 13:56:48 -07:00
29600cbb19 docs(aio): Update i18n example to Angular V6 (#23660)
PR Close #23660
2018-05-08 13:56:48 -07:00
5581e97d2a fix(core): call ngOnDestroy on all services that have it (#23755)
Previously, ngOnDestroy was only called on services which were statically
determined to have ngOnDestroy methods. In some cases, such as with services
instantiated via factory functions, it's not statically known that the service
has an ngOnDestroy method.

This commit changes the runtime to look for ngOnDestroy when instantiating
all DI tokens, and to call the method if it's present.

Fixes #22466
Fixes #22240
Fixes #14818

PR Close #23755
2018-05-08 13:55:29 -07:00
19262d9f90 Revert "style(animations): fix short param names (#23668)"
This reverts commit e3518967ad.

This PR accidentaly introduces a breaking change:
https://github.com/angular/angular/pull/23668#discussion_r186265055
2018-05-05 08:40:35 -07:00
1eb1c6315d Revert "docs(animations): fix content errors (#23668)"
This reverts commit 005dc8f68b.

The PR accidently introduced a breaking change
https://github.com/angular/angular/pull/23668#discussion_r186265055
2018-05-05 08:38:13 -07:00
3807599a4d style(animations): fix short param names (#23668)
PR Close #23668
2018-05-05 08:17:02 -07:00
2ed41d9e38 docs(animations): fix content errors (#23668)
PR Close #23668
2018-05-05 08:17:02 -07:00
5eb9c01bb6 ci: hide encryption key from circleci logs (#23585)
PR Close #23585
2018-05-04 16:33:59 -07:00
09d9662386 build: serve ivy todo app with real http-server (#23446)
PR Close #23446
2018-05-04 16:33:53 -07:00
844cbd9774 ci: publish build snapshots from Bazel/CircleCI (#23512)
This uses a new script and CircleCI job called "build-packages-dist"
which shims the new Bazel build to produce outputs matching the legacy
build. We'll use this to get AIO testing onto CircleCI as well.

We move the integration tests to a new circleCI job that depends on this
one, as well as the build publishing job.

Note that every PR will have a trivial green publishing status, because
we always create this job even for PRs. We'd rather not - see
https://discuss.circleci.com/t/workflows-pull-request-filter/14396/4

PR Close #23512
2018-05-04 16:33:39 -07:00
373a47dda9 test: fix firebase deployment script test
When I fixed the project id in 2c4850dc58,
I didn't realize we had a test that verified the wrong behavior.
2018-05-04 15:40:50 -07:00
83f12f3047 build: update to latest nodejs bazel rules (#23683)
PR Close #23683
2018-05-04 15:29:03 -07:00
bbc416cdcd build: update bazel to 0.13 (#23623)
PR Close #23623
2018-05-04 15:23:56 -07:00
07f2098655 feat(aio): add v6 release notification (#23690)
PR Close #23690
2018-05-04 15:23:36 -07:00
9b53a6e779 fix(aio): remove main background color when printing (#23538)
PR Close #23538
2018-05-04 15:21:14 -07:00
65f8505fb6 fix(aio): fix code-example print styles when printing backgrounds (#23538)
Fixes #23431

PR Close #23538
2018-05-04 15:21:14 -07:00
5a5ea45c40 refactor(aio): use the same selectors for screen and print styles (#23538)
PR Close #23538
2018-05-04 15:21:14 -07:00
52a3657b48 refactor(aio): include print styles last to overwrite other styles (#23538)
PR Close #23538
2018-05-04 15:21:14 -07:00
3824e3f858 fix(animations): properly clean up queried element styles in safari/edge (#23686)
Prior to this patch, if an element is queried and animated for 0 seconds
(just a style() call and nothing else) then the styles applied would not
be properly cleaned up due to their camelCased nature.

PR Close #23686
2018-05-04 15:04:20 -07:00
05aa5e0179 fix(animations): retain state styling for nodes that are moved around (#23686)
PR Close #23686
2018-05-04 15:04:20 -07:00
4ddeb030e7 build(aio): use Angular 6.0.0 (#23687)
PR Close #23687
2018-05-03 16:05:34 -07:00
afe6380429 build(aio): update to Angular CLI 6.0.0 (#23687)
PR Close #23687
2018-05-03 16:05:34 -07:00
947ea17a09 build: update the scripts/release/post-check script for 6.0.x 2018-05-03 15:38:52 -07:00
a190c45a64 fix(aio): correct project id for deployment of archive sites 2018-05-03 15:05:38 -07:00
902781803f docs(aio): Upgrade server-side rendering example to Angular V6 (#23649)
PR Close #23649
2018-05-03 14:24:44 -07:00
0d480ac0dc docs: add new info about angular update policies and resources (#23551)
PR Close #23551
2018-05-03 14:24:31 -07:00
6934bf4863 docs: add information on when not to use tree-shakable providers (#23634)
PR Close #23634
2018-05-03 14:24:16 -07:00
7e7ea33fb0 docs: add doc to include updates to the index.html with the new ng add command (#23616)
PR Close #23616
2018-05-03 14:23:30 -07:00
5bd8c6887e docs: improve the GitHub README.md, update links, etc 2018-05-03 13:26:52 -07:00
d28ab372c8 docs: add link to the v6 release announcement to our changelog 2018-05-03 13:26:46 -07:00
d0ccf5f169 release: cut the v6.0.0 release 2018-05-03 12:17:26 -07:00
ecde15298a build: update to rxjs@6.0.0 (#23679)
PR Close #23679
2018-05-03 10:53:39 -07:00
983e5f2d7e fix(aio): correctly route embedded live-example URLs from SW (#23637)
Partially addresses #23626.

PR Close #23637
2018-05-02 15:55:23 -07:00
5fc4299e0a refactor(aio): move right margin from .home image to .home anchor (#23624)
This makes the outline of `.home` symmetric.

PR Close #23624
2018-05-02 15:54:15 -07:00
1823d5dd1c style(aio): add space between .home and .hamburger (#23624)
When the `.hamburger` icon is clicked, it's background is drawn until
the very edge of `.home`'s image, leaving no space.

PR Close #23624
2018-05-02 15:54:15 -07:00
91d4da0d2f docs: add missing link to bootstrapping section (#23214)
PR Close #23214
2018-05-02 15:53:02 -07:00
22eb8e26fc build(aio): align stackblitz files with Angular CLI V6 (#23521)
Also cleans up legacy references to `.angular-cli.json`

PR Close #23521
2018-05-02 15:00:57 -07:00
f6002c1702 docs(forms): Fixed a typo in the reactive form (From 'address' to 'secretLairs') section (#23221)
PR Close #23221
2018-05-02 15:00:28 -07:00
14138f6382 docs(elements): add intro connecting angular elements to custom elements (#23638)
PR Close #23638
2018-05-02 14:57:20 -07:00
f11daa2031 docs(aio): update Egghead.io URL (#23598)
Closes #23597
PR Close #23598
2018-05-01 10:27:16 -07:00
31a435ef5b docs: fix typo in tag name (my-child --> app-child) (#23606)
Fixes #23599

PR Close #23606
2018-05-01 10:26:50 -07:00
3e92b22258 test: add i18n to cli-hello-world integration test (#23527)
PR Close #23527
2018-04-27 11:26:50 -07:00
2e5457c824 docs(aio): update docs error in guide/http (#23567)
Updates documentation to include examples for both req.flush and
req.error in http testing examples.

PR Close #23567
2018-04-27 11:26:27 -07:00
1ab5fba92e build(aio): add support for faster, unoptimized serve (#23569)
When running `yarn start` and `yarn serve-and-sync`, we are usually
more interested in faster re-build times than optimized builds. This was
also the behavior, before upgrading to @angular/cli@6 (fc5af69fb).

This commit introduces a new configuration (`fast`), which is used by
`yarn start` and `yarn serve-and-sync` to restore the faster,
unoptimized builds.
Other commands, such as `ng serve` and `ng e2e`, remain unchanged (using
slower, optimized builds).

PR Close #23569
2018-04-27 11:26:22 -07:00
e1e57ddaa7 docs: correct more typos (#23565)
PR Close #23565
2018-04-27 11:26:17 -07:00
ee7cb48877 docs: correct typos (#23565)
PR Close #23565
2018-04-27 11:26:10 -07:00
a30c57090a docs: correct node.js version and usage (#23565)
PR Close #23565
2018-04-27 11:26:04 -07:00
8a49ec4f27 ci: add Brandon Roberts as an aio approver (#23417)
PR Close #23417
2018-04-27 11:25:59 -07:00
697b6c040c fix(core): avoid eager providers re-initialization (#23559)
Fix a corner case where eager providers were getting constructed twice if the provider was requested before the initialization of the NgModule is complete.

PR Close #23559
2018-04-27 11:25:47 -07:00
4008e36e80 release: cut the v6.0.0-rc.6 release 2018-04-27 10:47:56 -07:00
e47bb52084 Revert "refactor(core): tree-shake application_module providers (#23477)"
This reverts commit ac2b530f4b.

The change is breaking targets in g3 see cl/194336387.
2018-04-27 07:13:56 -07:00
ac2b530f4b refactor(core): tree-shake application_module providers (#23477)
PR Close #23477
2018-04-25 15:54:41 -07:00
64bf6edf00 docs: update glossary architectural terms (#23045)
PR Close #23045
2018-04-25 13:21:52 -07:00
adf6235479 docs: corrected spelling of "ambient". 2018-04-24 15:05:31 -07:00
04c18ac1aa docs: fix typo (#23514)
PR Close #23514
2018-04-24 14:43:34 -07:00
1b26dd8cdb docs(benchpress): fix typo in README (#23471) (#23488)
PR Close #23488
2018-04-24 14:37:03 -07:00
f721b06bde style: format code 2018-04-24 14:36:30 -07:00
0bc8443e12 fix(compiler): avoid a crash in ngc-wrapped. (#23468)
`ng.performCompilation` can return an `undefined` program, which is not handled by ngc-wrapped.

Avoid crashing by checking for the error return and returning the diagnostics.
PR Close #23468
2018-04-24 13:57:04 -07:00
db17231597 ci(aio): fix deploy-to-firebase script (#23470)
Temporary workaround for angular/angular-cli#10398.
The behavior of `yarn build` remains the same, but building for a
specific deployment env (e.g. archive, next) requires
`yarn build-for $deployEnv`.

PR Close #23470
2018-04-24 11:15:35 -07:00
540626a3a6 build(common): mark locales files as side-effect-full (#23509)
Fixes https://github.com/angular/angular-cli/issues/10322
PR Close #23509
2018-04-24 11:14:52 -07:00
391bfcede5 docs(aio): Add UpgradingAngularJS to education resources (#23169)
PR Close #23169
2018-04-23 13:36:48 -07:00
d8de6488dd fix(router): cache route handle if found (#22475)
When asking the route reuse strategy to retrieve a detached route handle, store the
return value in a local variable for further processing instead of asking again later.

resolves #22474

PR Close #22475
2018-04-23 13:35:59 -07:00
151fb66848 ci: add alxhub as owner to a few packages (#23510)
PR Close #23510
2018-04-23 10:08:25 -07:00
02424ff0d0 Revert "style(compiler): fix lint issues (#23480)"
This reverts commit f0925d9705.
2018-04-22 12:21:24 -07:00
f0925d9705 style(compiler): fix lint issues (#23480)
PR Close #23480
2018-04-22 11:55:33 -07:00
212b806eda build: make commit validation accept typical Revert messages (#23480)
fixes #23479

PR Close #23480
2018-04-22 11:49:49 -07:00
b9431e88fb fix(compiler): handle undefined annotation metadata (#23349)
In certain cases seen in production, simplify() can returned
undefined when simplifying decorator metadata. This has proven tricky
to reproduce in an isolated test, but the fix is simple and low-risk:
don't attempt to spread an undefined set of annotations in the first
place.

PR Close #23349
2018-04-19 18:57:22 -07:00
7790cfa0d0 Revert "fix(compiler): Pretty print object instead of [Object object] (#22689)" (#23442)
This reverts commit 8555a3a3cd.

Reverted because of https://github.com/angular/angular/issues/23440

PR Close #23442
2018-04-19 14:52:49 -07:00
41b5149509 docs(aio): add front page campaign for the ng-conf live stream (#23391)
PR Close #23391
2018-04-17 14:13:43 -07:00
06f865640d docs(aio): Cleanup examples with edits from Igor/George (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
824f74f27b build(aio): turn on webpack's stats.json generation for debugging purposes (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
5301c43eed build(aio): add @angular/language-service (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
f280d1aef1 docs(aio): Bump shared yarn.lock file for examples (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
5e741d42a6 docs(aio): Bump shared dependencies to RC5 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
0035d41030 docs(aio): Fix failing upgrade-module tests (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
08f447ceec docs(aio): Fix failing boilerplate tests (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
8953f123e3 docs(aio): Upgrade examples to Angular 6 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
6274007e3b test(aio): fix failing tests (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
ee76be7783 docs(aio): update yarn test command in README.md (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
a8c720bc3a build(aio): update to @angular/material@6.0.0-rc.11 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
ac47a3cd93 test(aio): move reflect-metadata polyfills to test.ts (#23234)
This resolves https://github.com/angular/angular-cli/issues/10333 and nicely cleans up the code.

PR Close #23234
2018-04-17 14:09:04 -07:00
041458a3d2 build(aio): update to angular/core@6.0.0-rc.5 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
2cb74b748f style(aio): lint fixes for examples (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
cb0bfe7a43 test(aio): fix tests and update testing infra (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
8ee11aeaa6 build(aio): update tslint and codelyzer (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
bf89fcb361 build(aio): fix deployment script (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
4f70c5b6f7 build(aio): upgrade @angular/cli to 6.0.0-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
ad3ebec2a5 build(aio): upgrade @angular/* to 6.0.0-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
3f5d61f2dd build(aio): remove redundant flags from cli commands (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
11ada7f78b build(aio): switch to webpack-cli for IE polyfills (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
0b96bc7456 build(aio): upgrade rxjs to 6.0.0-turbo-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
fefadadff3 ci: chown bazel-built packages when running integration tests (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
946057ae29 build: update to rxjs@6.0.0-uncanny-rc.7 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
d4293aaaaa build: remove a postinstall-patch to fix rxjs (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
e3dcc227f6 build(aio): reorder entries in package.json (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
c9230dd90e build: fix angular.json that was missing keys due to cli bugs (#23234)
https://github.com/angular/angular-cli/issues/10225
https://github.com/angular/angular-cli/issues/10226

PR Close #23234
2018-04-17 14:09:04 -07:00
234af9ba59 build(aio): update to @angular/material@6.0.0-rc.1 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
7204028d3e build(aio): update to @angular/material@5.2.4 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
f7c55952bf build: update to rxjs@6.0.0-tactical-rc.1 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
e38e3bd135 test: simplify config for cli-hello-world (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
cd20c01ba1 test: update cli-hello-world to cli@6.0.0-rc.2 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
12a191ef3f build(aio): update to @angular/cli@6.0.0-rc.2 + project layout update (#23234)
project layout was updated using:
yarn ng update @angular/cli --migrate-only --from=1.7.3

PR Close #23234
2018-04-17 14:09:04 -07:00
65e67b3c3a build(aio): upgrade to @angular/*@6.0.0-rc.3 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
32e57f6197 build(aio): upgrade to @angular/*@6.0.0-rc.2 (#23234)
PR Close #23234
2018-04-17 14:09:04 -07:00
7de69ba29b ci(aio): fix aio-monitoring tests (#23390)
Previously, we were running the e2e tests from master against
`https://angular.io` (deployed from the stable branch). Often the e2e
tests from master do not apply to the stable branch, since the app has
deviated slightly.

This commit fixes this by stop running the full e2e tests against the
deployed versions, but a smaller set of "smoke tests", which check basic
functionality that is less likely to change between versions.

PR Close #23390
2018-04-17 13:45:38 -07:00
44193c0b94 refactor(aio): rename directory (tests/deployment-config --> tests/deployment) (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
6db8241ffa refactor(aio): rename spec file (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
33c594516c refactor(aio): rename yarn script (deployment-config-test --> redirects-test) (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
290 changed files with 5622 additions and 23015 deletions

View File

@ -85,7 +85,7 @@ jobs:
# This avoids waiting for the slowest build target to finish before running the first test
# See https://github.com/bazelbuild/bazel/issues/4257
# NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/...
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only
- run: bazel query --output=label //... | xargs bazel test
# CircleCI will allow us to go back and view/download these artifacts from past builds.
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
@ -111,42 +111,6 @@ jobs:
paths:
- "node_modules"
- "~/bazel_repository_cache"
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_jit:
<<: *job_defaults
resource_class: xlarge
steps:
- *define_env_vars
- checkout:
<<: *post_checkout
# See remote cache documentation in /docs/BAZEL.md
- run: .circleci/setup_cache.sh
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- *setup-bazel-remote-cache
- restore_cache:
key: *cache_key
- run: bazel run @yarn//:yarn
- run: bazel query --output=label //... | xargs bazel test --define=compile=jit --build_tag_filters=ivy-jit --test_tag_filters=-manual,ivy-jit
test_ivy_aot:
<<: *job_defaults
resource_class: xlarge
steps:
- *define_env_vars
- checkout:
<<: *post_checkout
# See remote cache documentation in /docs/BAZEL.md
- run: .circleci/setup_cache.sh
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- *setup-bazel-remote-cache
- restore_cache:
key: *cache_key
- run: bazel run @yarn//:yarn
- run: bazel query --output=label //... | xargs bazel test --define=compile=local --build_tag_filters=ivy-local --test_tag_filters=-manual,ivy-local
# This job exists only for backwards-compatibility with old scripts and tests
# that rely on the pre-Bazel dist/packages-dist layout.
@ -176,8 +140,6 @@ jobs:
root: dist
paths:
- packages-dist
- packages-dist-ivy-jit
- packages-dist-ivy-local
# We run the integration tests outside of Bazel for now.
# They are a separate workflow job so that they can be easily re-run.
@ -242,8 +204,6 @@ workflows:
jobs:
- lint
- test
- test_ivy_jit
- test_ivy_aot
- build-packages-dist
- integration_test:
requires:
@ -256,8 +216,6 @@ workflows:
requires:
# Only publish if tests and integration tests pass
- test
- test_ivy_jit
- test_ivy_aot
- integration_test
# Get the artifacts to publish from the build-packages-dist job
# since the publishing script expects the legacy outputs layout.

View File

@ -163,7 +163,7 @@ groups:
files:
- "packages/compiler/*"
users:
- alxhub #primary
- chuckjaz #primary
- vicb
- mhevery
- IgorMinar #fallback

View File

@ -1,14 +1,3 @@
<a name="6.1.0-beta.2"></a>
# [6.1.0-beta.2](https://github.com/angular/angular/compare/6.1.0-beta.1...6.1.0-beta.2) (2018-06-20)
### Bug Fixes
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
<a name="6.0.6"></a>
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)
@ -24,40 +13,8 @@
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
<a name="6.1.0-beta.1"></a>
# [6.1.0-beta.1](https://github.com/angular/angular/compare/6.1.0-beta.0...6.1.0-beta.1) (2018-06-13)
### Bug Fixes
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([dc4a3d0](https://github.com/angular/angular/commit/dc4a3d0))
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([282d351](https://github.com/angular/angular/commit/282d351))
* **docs-infra:** use script nomodule to load IE polyfills, skip other polyfills ([#24317](https://github.com/angular/angular/issues/24317)) ([8be6892](https://github.com/angular/angular/commit/8be6892)), closes [#23647](https://github.com/angular/angular/issues/23647)
* **ivy:** compute transitive scopes from NgModuleDef only ([#24334](https://github.com/angular/angular/issues/24334)) ([1135563](https://github.com/angular/angular/commit/1135563))
* **ivy:** correctly handle queries with embedded views ([#24418](https://github.com/angular/angular/issues/24418)) ([014949f](https://github.com/angular/angular/commit/014949f))
* **ivy:** remove debugger statement ([#24480](https://github.com/angular/angular/issues/24480)) ([70ef061](https://github.com/angular/angular/commit/70ef061))
* **ivy:** special case [style] and [class] bindings for future use ([#23232](https://github.com/angular/angular/issues/23232)) ([1b253e1](https://github.com/angular/angular/commit/1b253e1))
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
### Features
* **common:** introduce KeyValuePipe ([#24319](https://github.com/angular/angular/issues/24319)) ([2b49bf7](https://github.com/angular/angular/commit/2b49bf7))
* **core:** export defaultKeyValueDiffers to private api ([#24319](https://github.com/angular/angular/issues/24319)) ([92b278c](https://github.com/angular/angular/commit/92b278c))
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
* **ivy:** a generic visitor which allows prefixing nodes for ngtsc ([#24230](https://github.com/angular/angular/issues/24230)) ([ca79e11](https://github.com/angular/angular/commit/ca79e11))
* **ivy:** add support of ApplicationRef.bootstrapModuleFactory ([#23811](https://github.com/angular/angular/issues/23811)) ([e3759f7](https://github.com/angular/angular/commit/e3759f7))
* **ivy:** namespaced attributes added to output instructions ([#24386](https://github.com/angular/angular/issues/24386)) ([82c5313](https://github.com/angular/angular/commit/82c5313))
* **ivy:** now supports SVG and MathML elements ([#24377](https://github.com/angular/angular/issues/24377)) ([8c1ac28](https://github.com/angular/angular/commit/8c1ac28))
* **router:** implement scrolling restoration service ([#20030](https://github.com/angular/angular/issues/20030)) ([49c5234](https://github.com/angular/angular/commit/49c5234)), closes [#13636](https://github.com/angular/angular/issues/13636) [#10929](https://github.com/angular/angular/issues/10929) [#7791](https://github.com/angular/angular/issues/7791) [#6595](https://github.com/angular/angular/issues/6595)
<a name="6.0.5"></a>
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([0139173](https://github.com/angular/angular/commit/0139173))
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([ea3669e](https://github.com/angular/angular/commit/ea3669e))
* **docs-infra:** use script nomodule to load IE polyfills, skip other polyfills ([#24317](https://github.com/angular/angular/issues/24317)) ([e876535](https://github.com/angular/angular/commit/e876535)), closes [#23647](https://github.com/angular/angular/issues/23647)
@ -66,51 +23,6 @@
<a name="6.1.0-beta.0"></a>
## [6.1.0-beta.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0-beta.0) (2018-06-06)
### Bug Fixes
* **animations:** do not throw errors when a destroyed component is animated ([#23836](https://github.com/angular/angular/issues/23836)) ([d2a8687](https://github.com/angular/angular/commit/d2a8687))
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([b492b9e](https://github.com/angular/angular/commit/b492b9e))
* **animations:** properly clean up queried element styles in safari/edge ([#23633](https://github.com/angular/angular/issues/23633)) ([da9ff25](https://github.com/angular/angular/commit/da9ff25))
* **animations:** retain state styling for nodes that are moved around ([#23534](https://github.com/angular/angular/issues/23534)) ([65211f4](https://github.com/angular/angular/commit/65211f4))
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([8db928d](https://github.com/angular/angular/commit/8db928d))
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
* **elements:** always check to create strategy ([#23825](https://github.com/angular/angular/issues/23825)) ([b1cda36](https://github.com/angular/angular/commit/b1cda36))
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([b96a3c8](https://github.com/angular/angular/commit/b96a3c8))
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([60aa943](https://github.com/angular/angular/commit/60aa943)), closes [#23023](https://github.com/angular/angular/issues/23023)
* **platform-server:** don't reflect innerHTML property to attibute ([#24213](https://github.com/angular/angular/issues/24213)) ([6a663a4](https://github.com/angular/angular/commit/6a663a4)), closes [#19278](https://github.com/angular/angular/issues/19278)
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
* **service-worker:** correctly handle requests with empty `clientId` ([#23625](https://github.com/angular/angular/issues/23625)) ([e0ed59e](https://github.com/angular/angular/commit/e0ed59e)), closes [#23526](https://github.com/angular/angular/issues/23526)
* **service-worker:** deprecate `versionedFiles` in asset-group resources ([#23584](https://github.com/angular/angular/issues/23584)) ([1d378e2](https://github.com/angular/angular/commit/1d378e2))
### Features
* **compiler:** support `// ...` and `// TODO` in mock compiler expectations ([#23441](https://github.com/angular/angular/issues/23441)) ([c6b206e](https://github.com/angular/angular/commit/c6b206e))
* **compiler-cli:** update `tsickle` to `0.29.x` ([#24233](https://github.com/angular/angular/issues/24233)) ([f69ac67](https://github.com/angular/angular/commit/f69ac67))
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
* **platform-server:** use EventManagerPlugin on the server ([#24132](https://github.com/angular/angular/issues/24132)) ([d6595eb](https://github.com/angular/angular/commit/d6595eb))
* **router:** add navigation execution context info to activation hooks ([#24204](https://github.com/angular/angular/issues/24204)) ([20c463e](https://github.com/angular/angular/commit/20c463e)), closes [#24202](https://github.com/angular/angular/issues/24202)
<a name="6.0.4"></a>
## [6.0.4](https://github.com/angular/angular/compare/6.0.3...6.0.4) (2018-06-06)

View File

@ -1,13 +1,10 @@
# Supported Public API Surface of Angular
Our semver, timed-release cycle and deprecation policy currently applies to these npm packages:
Our SemVer, timed-release cycle and deprecation policy currently applies to these npm packages:
- `@angular/animations`
- `@angular/core`
- `@angular/common`
- `@angular/elements`
- `@angular/forms`
- `@angular/http`
- `@angular/platform-browser`
- `@angular/platform-browser-dynamic`
- `@angular/platform-server`
@ -15,13 +12,12 @@ Our semver, timed-release cycle and deprecation policy currently applies to thes
- `@angular/platform-webworker-dynamic`
- `@angular/upgrade`
- `@angular/router`
- `@angular/service-worker`
- `@angular/forms`
- `@angular/http`
One intentional omission from this list is `@angular/compiler`, which is currently considered a low level api and is subject to internal changes. These changes will not affect any applications or libraries using the higher-level apis (the command line interface or JIT compilation via `@angular/platform-browser-dynamic`). Only very specific use-cases require direct access to the compiler API (mostly tooling integration for IDEs, linters, etc). If you are working on this kind of integration, please reach out to us first.
Package `@angular/bazel` is currently an Angular Labs project and not covered by the public API guarantees.
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
Other projects developed by the Angular team like angular-cli, Angular Material, will be covered by these or similar guarantees in the future as they mature.
@ -35,7 +31,7 @@ Within the supported packages, we provide guarantees for:
We explicitly don't consider the following to be our public API surface:
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*` and other documented package entry-points.
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*`
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
- any class members or symbols marked as `private`, or prefixed with underscore (`_`) and [barred latin o](https://en.wikipedia.org/wiki/%C6%9F) (`ɵ`)
- extending any of our classes unless the support for this is specifically documented in the API docs

View File

@ -3,13 +3,13 @@
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod --progress false",
"e2e": "ng e2e",
"test": "ng test && ng e2e --webdriver-update=false && ng e2e --prod --webdriver-update=false",
"lint": "ng lint",
"ng": "ng",
"postinstall": "webdriver-manager update --gecko false --standalone false $CHROMEDRIVER_VERSION_ARG",
"start": "ng serve",
"test": "ng test && ng e2e --webdriver-update=false && ng e2e --prod --webdriver-update=false"
"postinstall": "webdriver-manager update --gecko false --standalone false $CHROMEDRIVER_VERSION_ARG"
},
"private": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "angular-integration",
"description": "Assert that users with TypeScript 2.7 can type-check an Angular application",
"description": "Assert that users with TypeScript 2.6 can type-check an Angular application",
"version": "0.0.0",
"license": "MIT",
"dependencies": {

View File

@ -1,47 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as compiler from '@angular/compiler';
import * as compilerTesting from '@angular/compiler/testing';
import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as forms from '@angular/forms';
import * as http from '@angular/http';
import * as httpTesting from '@angular/http/testing';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserTesting from '@angular/platform-browser/testing';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformServer from '@angular/platform-server';
import * as platformServerTesting from '@angular/platform-server/testing';
import * as platformWebworker from '@angular/platform-webworker';
import * as platformWebworkerDynamic from '@angular/platform-webworker-dynamic';
import * as router from '@angular/router';
import * as routerTesting from '@angular/router/testing';
import * as serviceWorker from '@angular/service-worker';
import * as upgrade from '@angular/upgrade';
export default {
compiler,
compilerTesting,
core,
coreTesting,
forms,
http,
httpTesting,
platformBrowser,
platformBrowserTesting,
platformBrowserDynamic,
platformServer,
platformServerTesting,
platformWebworker,
platformWebworkerDynamic,
router,
routerTesting,
serviceWorker,
upgrade,
};

View File

@ -1,30 +0,0 @@
{
"name": "angular-integration",
"description": "Assert that users with TypeScript 2.8 can type-check an Angular application",
"version": "0.0.0",
"license": "MIT",
"dependencies": {
"@angular/animations": "file:../../dist/packages-dist/animations",
"@angular/common": "file:../../dist/packages-dist/common",
"@angular/compiler": "file:../../dist/packages-dist/compiler",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/forms": "file:../../dist/packages-dist/forms",
"@angular/http": "file:../../dist/packages-dist/http",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/platform-webworker": "file:../../dist/packages-dist/platform-webworker",
"@angular/platform-webworker-dynamic": "file:../../dist/packages-dist/platform-webworker-dynamic",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/service-worker": "file:../../dist/packages-dist/service-worker",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "2.5.41",
"rxjs": "file:../../node_modules/rxjs",
"typescript": "2.8.x",
"zone.js": "file:../../node_modules/zone.js"
},
"scripts": {
"test": "tsc"
}
}

View File

@ -1,24 +0,0 @@
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../../dist/typings_test_ts28/",
"rootDir": ".",
"target": "es5",
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.iterable",
"es2015.promise"
],
"types": [],
"strictNullChecks": true
},
"files": [
"include-all.ts",
"node_modules/@types/jasmine/index.d.ts"
]
}

View File

@ -65,8 +65,6 @@ module.exports = function(config) {
'dist/all/@angular/**/*node_only_spec.js',
'dist/all/@angular/benchpress/**',
'dist/all/@angular/compiler-cli/**',
'dist/all/@angular/compiler-cli/src/ngtsc/**',
'dist/all/@angular/compiler-cli/test/ngtsc/**',
'dist/all/@angular/compiler/test/aot/**',
'dist/all/@angular/compiler/test/render3/**',
'dist/all/@angular/core/test/bundling/**',

View File

@ -7,7 +7,7 @@
*/
import {ɵC as C, ɵE as E, ɵRenderFlags as RenderFlags, ɵT as T, ɵV as V, ɵb as b, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as detectChanges, ɵe as e, ɵsn as sn, ɵt as t, ɵv as v} from '@angular/core';
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
import {TableCell, buildTable, emptyTable} from '../util';
@ -15,7 +15,7 @@ export class LargeTableComponent {
data: TableCell[][] = emptyTable;
/** @nocollapse */
static ngComponentDef: ComponentDefInternal<LargeTableComponent> = defineComponent({
static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({
type: LargeTableComponent,
selectors: [['largetable']],
template: function(rf: RenderFlags, ctx: LargeTableComponent) {

View File

@ -7,7 +7,7 @@
*/
import {ɵC as C, ɵE as E, ɵRenderFlags as RenderFlags, ɵT as T, ɵV as V, ɵb as b, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as _detectChanges, ɵe as e, ɵi1 as i1, ɵp as p, ɵsn as sn, ɵt as t, ɵv as v} from '@angular/core';
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
import {TreeNode, buildTree, emptyTree} from '../util';
@ -35,7 +35,7 @@ export class TreeComponent {
data: TreeNode = emptyTree;
/** @nocollapse */
static ngComponentDef: ComponentDefInternal<TreeComponent> = defineComponent({
static ngComponentDef: ComponentDef<TreeComponent> = defineComponent({
type: TreeComponent,
selectors: [['tree']],
template: function(rf: RenderFlags, ctx: TreeComponent) {
@ -95,7 +95,7 @@ export class TreeFunction {
data: TreeNode = emptyTree;
/** @nocollapse */
static ngComponentDef: ComponentDefInternal<TreeFunction> = defineComponent({
static ngComponentDef: ComponentDef<TreeFunction> = defineComponent({
type: TreeFunction,
selectors: [['tree']],
template: function(rf: RenderFlags, ctx: TreeFunction) {

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "6.1.0-beta.2",
"version": "6.0.6",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -114,7 +114,7 @@
"tslint": "5.7.0",
"tslint-eslint-rules": "4.1.1",
"tsutils": "2.20.0",
"typescript": "2.8.x",
"typescript": "2.7.x",
"uglify-es": "^3.3.9",
"universal-analytics": "0.4.15",
"vlq": "0.2.2",

View File

@ -24,10 +24,7 @@ ng_package(
"//packages/animations/browser/testing:package.json",
],
entry_point = "packages/animations/index.js",
tags = [
"ivy-jit",
"release-with-framework",
],
tags = ["release-with-framework"],
deps = [
":animations",
"//packages/animations/browser",

View File

@ -15,7 +15,12 @@ const DEFAULT_FILL_MODE = 'forwards';
const DEFAULT_EASING = 'linear';
const ANIMATION_END_EVENT = 'animationend';
export const enum AnimatorControlState {INITIALIZED = 1, STARTED = 2, FINISHED = 3, DESTROYED = 4}
export enum AnimatorControlState {
INITIALIZED = 1,
STARTED = 2,
FINISHED = 3,
DESTROYED = 4
}
export class CssKeyframesPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = [];
@ -29,8 +34,7 @@ export class CssKeyframesPlayer implements AnimationPlayer {
public readonly totalTime: number;
public readonly easing: string;
public currentSnapshot: {[key: string]: string} = {};
private _state: AnimatorControlState = 0;
public state = 0;
constructor(
public readonly element: any, public readonly keyframes: {[key: string]: string | number}[],
@ -50,8 +54,8 @@ export class CssKeyframesPlayer implements AnimationPlayer {
destroy() {
this.init();
if (this._state >= AnimatorControlState.DESTROYED) return;
this._state = AnimatorControlState.DESTROYED;
if (this.state >= AnimatorControlState.DESTROYED) return;
this.state = AnimatorControlState.DESTROYED;
this._styler.destroy();
this._flushStartFns();
this._flushDoneFns();
@ -71,8 +75,8 @@ export class CssKeyframesPlayer implements AnimationPlayer {
finish() {
this.init();
if (this._state >= AnimatorControlState.FINISHED) return;
this._state = AnimatorControlState.FINISHED;
if (this.state >= AnimatorControlState.FINISHED) return;
this.state = AnimatorControlState.FINISHED;
this._styler.finish();
this._flushStartFns();
this._flushDoneFns();
@ -82,10 +86,10 @@ export class CssKeyframesPlayer implements AnimationPlayer {
getPosition(): number { return this._styler.getPosition(); }
hasStarted(): boolean { return this._state >= AnimatorControlState.STARTED; }
hasStarted(): boolean { return this.state >= AnimatorControlState.STARTED; }
init(): void {
if (this._state >= AnimatorControlState.INITIALIZED) return;
this._state = AnimatorControlState.INITIALIZED;
if (this.state >= AnimatorControlState.INITIALIZED) return;
this.state = AnimatorControlState.INITIALIZED;
const elm = this.element;
this._styler.apply();
if (this._delay) {
@ -97,7 +101,7 @@ export class CssKeyframesPlayer implements AnimationPlayer {
this.init();
if (!this.hasStarted()) {
this._flushStartFns();
this._state = AnimatorControlState.STARTED;
this.state = AnimatorControlState.STARTED;
}
this._styler.resume();
}
@ -133,7 +137,7 @@ export class CssKeyframesPlayer implements AnimationPlayer {
this.init();
const styles: {[key: string]: string} = {};
if (this.hasStarted()) {
const finished = this._state >= AnimatorControlState.FINISHED;
const finished = this.state >= AnimatorControlState.FINISHED;
Object.keys(this._finalStyles).forEach(prop => {
if (prop != 'offset') {
styles[prop] = finished ? this._finalStyles[prop] : computeStyle(this.element, prop);

View File

@ -13,7 +13,7 @@
},
"peerDependencies": {
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
"typescript": ">=2.7.2 <2.9"
"typescript": ">=2.7.2 <2.8"
},
"repository": {
"type": "git",

View File

@ -14,90 +14,6 @@ load(":rules_typescript.bzl",
"ts_providers_dict_to_struct",
)
def _compile_strategy(ctx):
"""Detect which strategy should be used to implement ng_module.
Depending on the value of the 'compile' define flag or the '_global_mode' attribute, ng_module
can be implemented in various ways. This function reads the configuration passed by the user and
determines which mode is active.
Args:
ctx: skylark rule execution context
Returns:
one of 'legacy', 'local', 'jit', or 'global' depending on the configuration in ctx
"""
strategy = 'legacy'
if 'compile' in ctx.var:
strategy = ctx.var['compile']
if strategy not in ['legacy', 'local', 'jit']:
fail("Unknown --define=compile value '%s'" % strategy)
if strategy == 'legacy' and hasattr(ctx.attr, '_global_mode') and ctx.attr._global_mode:
strategy = 'global'
return strategy
def _compiler_name(ctx):
"""Selects a user-visible name depending on the current compilation strategy.
Args:
ctx: skylark rule execution context
Returns:
the name of the current compiler to be displayed in build output
"""
strategy = _compile_strategy(ctx)
if strategy == 'legacy':
return 'ngc'
elif strategy == 'global':
return 'ngc.ivy'
elif strategy == 'local':
return 'ngtsc'
elif strategy == 'jit':
return 'tsc'
else:
fail('unreachable')
def _enable_ivy_value(ctx):
"""Determines the value of the enableIvy option in the generated tsconfig.
Args:
ctx: skylark rule execution context
Returns:
the value of enableIvy that needs to be set in angularCompilerOptions in the generated tsconfig
"""
strategy = _compile_strategy(ctx)
if strategy == 'legacy':
return False
elif strategy == 'global':
return True
elif strategy == 'local':
return 'ngtsc'
elif strategy == 'jit':
return 'tsc'
else:
fail('unreachable')
def _include_ng_files(ctx):
"""Determines whether Angular outputs will be produced by the current compilation strategy.
Args:
ctx: skylark rule execution context
Returns:
true iff the current compilation strategy will produce View Engine compilation outputs (such as
factory files), false otherwise
"""
strategy = _compile_strategy(ctx)
return strategy == 'legacy' or strategy == 'global'
def _basename_of(ctx, file):
ext_len = len(".ts")
if file.short_path.endswith(".ng.html"):
@ -145,8 +61,6 @@ def _should_produce_flat_module_outs(ctx):
# in the library. Most of these will be produced as empty files but it is
# unknown, without parsing, which will be empty.
def _expected_outs(ctx):
include_ng_files = _include_ng_files(ctx)
devmode_js_files = []
closure_js_files = []
declaration_files = []
@ -164,7 +78,7 @@ def _expected_outs(ctx):
if short_path.endswith(".ts") and not short_path.endswith(".d.ts"):
basename = short_path[len(package_prefix):-len(".ts")]
if include_ng_files and (len(factory_basename_set) == 0 or basename in factory_basename_set):
if len(factory_basename_set) == 0 or basename in factory_basename_set:
devmode_js = [
".ngfactory.js",
".ngsummary.js",
@ -176,7 +90,7 @@ def _expected_outs(ctx):
devmode_js = [".js"]
summaries = []
metadata = []
elif include_ng_files and short_path.endswith(".css"):
elif short_path.endswith(".css"):
basename = short_path[len(package_prefix):-len(".css")]
devmode_js = [
".css.shim.ngstyle.js",
@ -199,7 +113,7 @@ def _expected_outs(ctx):
metadata_files += [ctx.actions.declare_file(basename + ext) for ext in metadata]
# We do this just when producing a flat module index for a publishable ng_module
if include_ng_files and _should_produce_flat_module_outs(ctx):
if _should_produce_flat_module_outs(ctx):
flat_module_out = _flat_module_out_file(ctx)
devmode_js_files.append(ctx.actions.declare_file("%s.js" % flat_module_out))
closure_js_files.append(ctx.actions.declare_file("%s.closure.js" % flat_module_out))
@ -209,12 +123,7 @@ def _expected_outs(ctx):
else:
bundle_index_typings = None
# TODO(alxhub): i18n is only produced by the legacy compiler currently. This should be re-enabled
# when ngtsc can extract messages
if include_ng_files:
i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")]
else:
i18n_messages_files = []
return struct(
closure_js = closure_js_files,
@ -226,9 +135,14 @@ def _expected_outs(ctx):
i18n_messages = i18n_messages_files,
)
def _ivy_tsconfig(ctx, files, srcs, **kwargs):
return _ngc_tsconfig_helper(ctx, files, srcs, True, **kwargs)
def _ngc_tsconfig(ctx, files, srcs, **kwargs):
return _ngc_tsconfig_helper(ctx, files, srcs, False, **kwargs)
def _ngc_tsconfig_helper(ctx, files, srcs, enable_ivy, **kwargs):
outs = _expected_outs(ctx)
include_ng_files = _include_ng_files(ctx)
if "devmode_manifest" in kwargs:
expected_outs = outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
else:
@ -238,9 +152,8 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs):
"enableResourceInlining": ctx.attr.inline_resources,
"generateCodeForLibraries": False,
"allowEmptyCodegenFiles": True,
# Summaries are only enabled if Angular outputs are to be produced.
"enableSummariesForJit": include_ng_files,
"enableIvy": _enable_ivy_value(ctx),
"enableSummariesForJit": True,
"enableIvy": enable_ivy,
"fullTemplateTypeCheck": ctx.attr.type_check,
# FIXME: wrong place to de-dupe
"expectedOut": depset([o.path for o in expected_outs]).to_list()
@ -303,10 +216,8 @@ def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file,
the parameters of the compilation which will be used to replay the ngc action for i18N.
"""
include_ng_files = _include_ng_files(ctx)
mnemonic = "AngularTemplateCompile"
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
progress_message = "Compiling Angular templates (ngc) %s" % label
if locale:
mnemonic = "AngularI18NMerging"
@ -340,7 +251,7 @@ def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file,
},
)
if include_ng_files and messages_out != None:
if messages_out != None:
ctx.actions.run(
inputs = list(inputs),
outputs = messages_out,
@ -402,7 +313,7 @@ def _ts_expected_outs(ctx, label):
_ignored = [label]
return _expected_outs(ctx)
def ng_module_impl(ctx, ts_compile_actions):
def ng_module_impl(ctx, ts_compile_actions, ivy = False):
"""Implementation function for the ng_module rule.
This is exposed so that google3 can have its own entry point that re-uses this
@ -411,30 +322,29 @@ def ng_module_impl(ctx, ts_compile_actions):
Args:
ctx: the skylark rule context
ts_compile_actions: generates all the actions to run an ngc compilation
ivy: if True, run the compiler in Ivy mode (internal only)
Returns:
the result of the ng_module rule as a dict, suitable for
conversion by ts_providers_dict_to_struct
"""
include_ng_files = _include_ng_files(ctx)
tsconfig = _ngc_tsconfig if not ivy else _ivy_tsconfig
providers = ts_compile_actions(
ctx, is_library=True, compile_action=_prodmode_compile_action,
devmode_compile_action=_devmode_compile_action,
tsc_wrapped_tsconfig=_ngc_tsconfig,
tsc_wrapped_tsconfig=tsconfig,
outputs = _ts_expected_outs)
outs = _expected_outs(ctx)
if include_ng_files:
providers["angular"] = {
"summaries": outs.summaries,
"metadata": outs.metadata
}
providers["ngc_messages"] = outs.i18n_messages
if include_ng_files and _should_produce_flat_module_outs(ctx):
if _should_produce_flat_module_outs(ctx):
if len(outs.metadata) > 1:
fail("expecting exactly one metadata output for " + str(ctx.label))
@ -450,6 +360,9 @@ def ng_module_impl(ctx, ts_compile_actions):
def _ng_module_impl(ctx):
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
def _ivy_module_impl(ctx):
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts, True))
NG_MODULE_ATTRIBUTES = {
"srcs": attr.label_list(allow_files = [".ts"]),
@ -516,16 +429,11 @@ ng_module = rule(
outputs = COMMON_OUTPUTS,
)
# TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information.
# It exists to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used
# instead, and should be removed once ngtsc is capable of fulfilling the same requirements.
internal_global_ng_module = rule(
implementation = _ng_module_impl,
attrs = dict(NG_MODULE_RULE_ATTRS, **{
"_global_mode": attr.bool(
default = True,
),
}),
# TODO(alxhub): this rule exists to allow early testing of the Ivy compiler within angular/angular,
# and should not be made public. When ng_module() supports Ivy-mode outputs, this rule should be
# removed and its usages refactored to use ng_module() directly.
internal_ivy_ng_module = rule(
implementation = _ivy_module_impl,
attrs = NG_MODULE_RULE_ATTRS,
outputs = COMMON_OUTPUTS,
)

View File

@ -27,29 +27,6 @@ PLUGIN_CONFIG="{sideEffectFreeModules: [\n%s]}" % ",\n".join(
BO_ROLLUP="angular_devkit/packages/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):
"""Determine whether to use the Angular or upstream versions of the rollup_bundle rule.
In legacy mode, the Angular version of rollup is used. This runs build optimizer as part of its
processing, which affects decorators and annotations.
In other modes, an emulation of the upstream rollup_bundle rule is used. This avoids running
build optimizer on code which isn't designed to be optimized by it.
Args:
ctx: skylark rule execution context
Returns:
true iff the Angular version of rollup with build optimizer should be used, false otherwise
"""
if 'compile' not in ctx.var:
return False
strategy = ctx.var['compile']
return strategy != 'legacy'
def run_brotli(ctx, input, output):
ctx.actions.run(
executable = ctx.executable._brotli,
@ -58,41 +35,7 @@ def run_brotli(ctx, input, output):
arguments = ["--output=%s" % output.path, input.path],
)
# Borrowed from bazelbuild/rules_nodejs
def _run_tsc(ctx, input, output):
args = ctx.actions.args()
args.add("--target", "es5")
args.add("--allowJS")
args.add(input)
args.add("--outFile", output)
ctx.action(
executable = ctx.executable._tsc,
inputs = [input],
outputs = [output],
arguments = [args]
)
# Borrowed from bazelbuild/rules_nodejs, with the addition of brotli compression output
def _plain_rollup_bundle(ctx):
rollup_config = write_rollup_config(ctx)
run_rollup(ctx, collect_es2015_sources(ctx), rollup_config, ctx.outputs.build_es6)
_run_tsc(ctx, ctx.outputs.build_es6, ctx.outputs.build_es5)
source_map = run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min)
run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True)
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html)
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
files = [ctx.outputs.build_es5_min, source_map]
return DefaultInfo(files = depset(files), runfiles = ctx.runfiles(files))
def _ng_rollup_bundle(ctx):
# Escape and use the plain rollup rule if the compilation strategy requires it
if _use_plain_rollup(ctx):
return _plain_rollup_bundle(ctx)
# We don't expect anyone to make use of this bundle yet, but it makes this rule
# compatible with rollup_bundle which allows them to be easily swapped back and
# forth.

View File

@ -119,15 +119,6 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
compilerOpts.annotationsAs = 'static fields';
}
// Detect from compilerOpts whether the entrypoint is being invoked in Ivy mode.
const isInIvyMode = compilerOpts.enableIvy === 'ngtsc' || compilerOpts.enableIvy === 'tsc';
// Disable downleveling and Closure annotation if in Ivy mode.
if (isInIvyMode) {
compilerOpts.annotateForClosureCompiler = false;
compilerOpts.annotationsAs = 'decorators';
}
if (!compilerOpts.rootDirs) {
throw new Error('rootDirs is not set!');
}
@ -181,12 +172,6 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
const bazelHost = new CompilerHost(
files, compilerOpts, bazelOpts, tsHost, fileLoader, allowNonHermeticReads,
generatedFileModuleResolver);
// Also need to disable decorator downleveling in the BazelHost in Ivy mode.
if (isInIvyMode) {
bazelHost.transformDecorators = false;
}
// Prevent tsickle adding any types at all if we don't want closure compiler annotations.
bazelHost.transformTypesToClosure = compilerOpts.annotateForClosureCompiler;
const origBazelHostFileExist = bazelHost.fileExists;

View File

@ -51,12 +51,12 @@ export class ChromeDriverExtension extends WebDriverExtension {
gc() { return this._driver.executeScript('window.gc()'); }
async timeBegin(name: string): Promise<any> {
timeBegin(name: string): Promise<any> {
if (this._firstRun) {
this._firstRun = false;
// Before the first run, read out the existing performance logs
// so that the chrome buffer does not fill up.
await this._driver.logs('performance');
this._driver.logs('performance');
}
return this._driver.executeScript(`console.time('${name}');`);
}

View File

@ -27,10 +27,7 @@ ng_package(
],
entry_point = "packages/common/index.js",
packages = ["//packages/common/locales:package"],
tags = [
"ivy-jit",
"release-with-framework",
],
tags = ["release-with-framework"],
deps = [
"//packages/common",
"//packages/common/http",

View File

@ -21,8 +21,7 @@ export {parseCookieValue as ɵparseCookieValue} from './cookie';
export {CommonModule, DeprecatedI18NPipesModule} from './common_module';
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
export {DOCUMENT} from './dom_tokens';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
export {DeprecatedDatePipe, DeprecatedCurrencyPipe, DeprecatedDecimalPipe, DeprecatedPercentPipe} from './pipes/deprecated/index';
export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id';
export {VERSION} from './version';
export {ViewportScroller, NullViewportScroller as ɵNullViewportScroller} from './viewport_scroller';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core';
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, OnChanges, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core';
export class NgForOfContext<T> {
constructor(
@ -93,12 +93,8 @@ export class NgForOfContext<T> {
*
*/
@Directive({selector: '[ngFor][ngForOf]'})
export class NgForOf<T> implements DoCheck {
@Input()
set ngForOf(ngForOf: NgIterable<T>) {
this._ngForOf = ngForOf;
this._ngForOfDirty = true;
}
export class NgForOf<T> implements DoCheck, OnChanges {
@Input() ngForOf: NgIterable<T>;
@Input()
set ngForTrackBy(fn: TrackByFunction<T>) {
if (isDevMode() && fn != null && typeof fn !== 'function') {
@ -114,8 +110,6 @@ export class NgForOf<T> implements DoCheck {
get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; }
private _ngForOf: NgIterable<T>;
private _ngForOfDirty: boolean = true;
private _differ: IterableDiffer<T>|null = null;
private _trackByFn: TrackByFunction<T>;
@ -133,11 +127,10 @@ export class NgForOf<T> implements DoCheck {
}
}
ngDoCheck(): void {
if (this._ngForOfDirty) {
this._ngForOfDirty = false;
ngOnChanges(changes: SimpleChanges): void {
if ('ngForOf' in changes) {
// React on ngForOf changes only once all inputs have been initialized
const value = this._ngForOf;
const value = changes['ngForOf'].currentValue;
if (!this._differ && value) {
try {
this._differ = this._differs.find(value).create(this.ngForTrackBy);
@ -147,8 +140,11 @@ export class NgForOf<T> implements DoCheck {
}
}
}
}
ngDoCheck(): void {
if (this._differ) {
const changes = this._differ.diff(this._ngForOf);
const changes = this._differ.diff(this.ngForOf);
if (changes) this._applyChanges(changes);
}
}
@ -159,7 +155,7 @@ export class NgForOf<T> implements DoCheck {
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
if (item.previousIndex == null) {
const view = this._viewContainer.createEmbeddedView(
this._template, new NgForOfContext<T>(null !, this._ngForOf, -1, -1), currentIndex);
this._template, new NgForOfContext<T>(null !, this.ngForOf, -1, -1), currentIndex);
const tuple = new RecordViewTuple<T>(item, view);
insertTuples.push(tuple);
} else if (currentIndex == null) {

View File

@ -17,7 +17,6 @@ import {DatePipe} from './date_pipe';
import {I18nPluralPipe} from './i18n_plural_pipe';
import {I18nSelectPipe} from './i18n_select_pipe';
import {JsonPipe} from './json_pipe';
import {KeyValue, KeyValuePipe} from './keyvalue_pipe';
import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
import {SlicePipe} from './slice_pipe';
@ -26,8 +25,6 @@ export {
CurrencyPipe,
DatePipe,
DecimalPipe,
KeyValue,
KeyValuePipe,
I18nPluralPipe,
I18nSelectPipe,
JsonPipe,
@ -35,7 +32,7 @@ export {
PercentPipe,
SlicePipe,
TitleCasePipe,
UpperCasePipe,
UpperCasePipe
};
@ -55,5 +52,4 @@ export const COMMON_PIPES = [
DatePipe,
I18nPluralPipe,
I18nSelectPipe,
KeyValuePipe,
];

View File

@ -1,110 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Pipe, PipeTransform} from '@angular/core';
function makeKeyValuePair<K, V>(key: K, value: V): KeyValue<K, V> {
return {key: key, value: value};
}
/**
* A key value pair.
* Usually used to represent the key value pairs from a Map or Object.
*/
export interface KeyValue<K, V> {
key: K;
value: V;
}
/**
* @ngModule CommonModule
* @description
*
* Transforms Object or Map into an array of key value pairs.
*
* The output array will be ordered by keys.
* By default the comparator will be by Unicode point value.
* You can optionally pass a compareFn if your keys are complex types.
*
* ## Examples
*
* This examples show how an Object or a Map and be iterated by ngFor with the use of this keyvalue
* pipe.
*
* {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'}
*/
@Pipe({name: 'keyvalue', pure: false})
export class KeyValuePipe implements PipeTransform {
constructor(private readonly differs: KeyValueDiffers) {}
private differ: KeyValueDiffer<any, any>;
private keyValues: Array<KeyValue<any, any>>;
transform<K, V>(input: null, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): null;
transform<V>(
input: {[key: string]: V}|Map<string, V>,
compareFn?: (a: KeyValue<string, V>, b: KeyValue<string, V>) => number):
Array<KeyValue<string, V>>;
transform<V>(
input: {[key: number]: V}|Map<number, V>,
compareFn?: (a: KeyValue<number, V>, b: KeyValue<number, V>) => number):
Array<KeyValue<number, V>>;
transform<K, V>(input: Map<K, V>, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number):
Array<KeyValue<K, V>>;
transform<K, V>(
input: null|{[key: string]: V, [key: number]: V}|Map<K, V>,
compareFn: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number = defaultComparator):
Array<KeyValue<K, V>>|null {
if (!input || (!(input instanceof Map) && typeof input !== 'object')) {
return null;
}
if (!this.differ) {
// make a differ for whatever type we've been passed in
this.differ = this.differs.find(input).create();
}
const differChanges: KeyValueChanges<K, V>|null = this.differ.diff(input as any);
if (differChanges) {
this.keyValues = [];
differChanges.forEachItem((r: KeyValueChangeRecord<K, V>) => {
this.keyValues.push(makeKeyValuePair(r.key, r.currentValue !));
});
this.keyValues.sort(compareFn);
}
return this.keyValues;
}
}
export function defaultComparator<K, V>(
keyValueA: KeyValue<K, V>, keyValueB: KeyValue<K, V>): number {
const a = keyValueA.key;
const b = keyValueB.key;
// if same exit with 0;
if (a === b) return 0;
// make sure that undefined are at the end of the sort.
if (a === undefined) return 1;
if (b === undefined) return -1;
// make sure that nulls are at the end of the sort.
if (a === null) return 1;
if (b === null) return -1;
if (typeof a == 'string' && typeof b == 'string') {
return a < b ? -1 : 1;
}
if (typeof a == 'number' && typeof b == 'number') {
return a - b;
}
if (typeof a == 'boolean' && typeof b == 'boolean') {
return a < b ? -1 : 1;
}
// `a` and `b` are of different types. Compare their string values.
const aString = String(a);
const bString = String(b);
return aString == bString ? 0 : aString < bString ? -1 : 1;
}

View File

@ -1,183 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {defineInjectable, inject} from '@angular/core';
import {DOCUMENT} from './dom_tokens';
/**
* @whatItDoes Manages the scroll position.
*/
export abstract class ViewportScroller {
// De-sugared tree-shakable injection
// See #23917
/** @nocollapse */
static ngInjectableDef = defineInjectable(
{providedIn: 'root', factory: () => new BrowserViewportScroller(inject(DOCUMENT), window)});
/**
* @whatItDoes Configures the top offset used when scrolling to an anchor.
*
* When given a tuple with two number, the service will always use the numbers.
* When given a function, the service will invoke the function every time it restores scroll
* position.
*/
abstract setOffset(offset: [number, number]|(() => [number, number])): void;
/**
* @whatItDoes Returns the current scroll position.
*/
abstract getScrollPosition(): [number, number];
/**
* @whatItDoes Sets the scroll position.
*/
abstract scrollToPosition(position: [number, number]): void;
/**
* @whatItDoes Scrolls to the provided anchor.
*/
abstract scrollToAnchor(anchor: string): void;
/**
* @whatItDoes Disables automatic scroll restoration provided by the browser.
* See also [window.history.scrollRestoration
* info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)
*/
abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void;
}
/**
* @whatItDoes Manages the scroll position.
*/
export class BrowserViewportScroller implements ViewportScroller {
private offset: () => [number, number] = () => [0, 0];
constructor(private document: any, private window: any) {}
/**
* @whatItDoes Configures the top offset used when scrolling to an anchor.
*
* * When given a number, the service will always use the number.
* * When given a function, the service will invoke the function every time it restores scroll
* position.
*/
setOffset(offset: [number, number]|(() => [number, number])): void {
if (Array.isArray(offset)) {
this.offset = () => offset;
} else {
this.offset = offset;
}
}
/**
* @whatItDoes Returns the current scroll position.
*/
getScrollPosition(): [number, number] {
if (this.supportScrollRestoration()) {
return [this.window.scrollX, this.window.scrollY];
} else {
return [0, 0];
}
}
/**
* @whatItDoes Sets the scroll position.
*/
scrollToPosition(position: [number, number]): void {
if (this.supportScrollRestoration()) {
this.window.scrollTo(position[0], position[1]);
}
}
/**
* @whatItDoes Scrolls to the provided anchor.
*/
scrollToAnchor(anchor: string): void {
if (this.supportScrollRestoration()) {
const elSelectedById = this.document.querySelector(`#${anchor}`);
if (elSelectedById) {
this.scrollToElement(elSelectedById);
return;
}
const elSelectedByName = this.document.querySelector(`[name='${anchor}']`);
if (elSelectedByName) {
this.scrollToElement(elSelectedByName);
return;
}
}
}
/**
* @whatItDoes Disables automatic scroll restoration provided by the browser.
*/
setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void {
if (this.supportScrollRestoration()) {
const history = this.window.history;
if (history && history.scrollRestoration) {
history.scrollRestoration = scrollRestoration;
}
}
}
private scrollToElement(el: any): void {
const rect = el.getBoundingClientRect();
const left = rect.left + this.window.pageXOffset;
const top = rect.top + this.window.pageYOffset;
const offset = this.offset();
this.window.scrollTo(left - offset[0], top - offset[1]);
}
/**
* We only support scroll restoration when we can get a hold of window.
* This means that we do not support this behavior when running in a web worker.
*
* Lifting this restriction right now would require more changes in the dom adapter.
* Since webworkers aren't widely used, we will lift it once RouterScroller is
* battle-tested.
*/
private supportScrollRestoration(): boolean {
try {
return !!this.window && !!this.window.scrollTo;
} catch (e) {
return false;
}
}
}
/**
* @whatItDoes Provides an empty implementation of the viewport scroller. This will
* live in @angular/common as it will be used by both platform-server and platform-webworker.
*/
export class NullViewportScroller implements ViewportScroller {
/**
* @whatItDoes empty implementation
*/
setOffset(offset: [number, number]|(() => [number, number])): void {}
/**
* @whatItDoes empty implementation
*/
getScrollPosition(): [number, number] { return [0, 0]; }
/**
* @whatItDoes empty implementation
*/
scrollToPosition(position: [number, number]): void {}
/**
* @whatItDoes empty implementation
*/
scrollToAnchor(anchor: string): void {}
/**
* @whatItDoes empty implementation
*/
setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void {}
}

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {CommonModule, NgForOf} from '@angular/common';
import {Component, Directive} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/src/matchers';

View File

@ -1,152 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {KeyValuePipe} from '@angular/common';
import {EventEmitter, KeyValueDiffers, WrappedValue, ɵdefaultKeyValueDiffers as defaultKeyValueDiffers} from '@angular/core';
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
import {defaultComparator} from '../../src/pipes/keyvalue_pipe';
import {SpyChangeDetectorRef} from '../spies';
describe('KeyValuePipe', () => {
it('should return null when given null', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform(null)).toEqual(null);
});
it('should return null when given undefined', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform(undefined as any)).toEqual(null);
});
it('should return null for an unsupported type', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const fn = () => {};
expect(pipe.transform(fn as any)).toEqual(null);
});
describe('object dictionary', () => {
it('should transform a basic dictionary', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform({1: 2})).toEqual([{key: '1', value: 2}]);
});
it('should order by alpha', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform({'b': 1, 'a': 1})).toEqual([
{key: 'a', value: 1}, {key: 'b', value: 1}
]);
});
it('should order by numerical', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform({2: 1, 1: 1})).toEqual([{key: '1', value: 1}, {key: '2', value: 1}]);
});
it('should order by numerical and alpha', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const input = {2: 1, 1: 1, 'b': 1, 0: 1, 3: 1, 'a': 1};
expect(pipe.transform(input)).toEqual([
{key: '0', value: 1}, {key: '1', value: 1}, {key: '2', value: 1}, {key: '3', value: 1},
{key: 'a', value: 1}, {key: 'b', value: 1}
]);
});
it('should return the same ref if nothing changes', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const transform1 = pipe.transform({1: 2});
const transform2 = pipe.transform({1: 2});
expect(transform1 === transform2).toEqual(true);
});
it('should return a new ref if something changes', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const transform1 = pipe.transform({1: 2});
const transform2 = pipe.transform({1: 3});
expect(transform1 !== transform2).toEqual(true);
});
});
describe('Map', () => {
it('should transform a basic Map', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform(new Map([[1, 2]]))).toEqual([{key: 1, value: 2}]);
});
it('should order by alpha', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform(new Map([['b', 1], ['a', 1]]))).toEqual([
{key: 'a', value: 1}, {key: 'b', value: 1}
]);
});
it('should order by numerical', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
expect(pipe.transform(new Map([[2, 1], [1, 1]]))).toEqual([
{key: 1, value: 1}, {key: 2, value: 1}
]);
});
it('should order by numerical and alpha', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const input = [[2, 1], [1, 1], ['b', 1], [0, 1], [3, 1], ['a', 1]];
expect(pipe.transform(new Map(input as any))).toEqual([
{key: 0, value: 1}, {key: 1, value: 1}, {key: 2, value: 1}, {key: 3, value: 1},
{key: 'a', value: 1}, {key: 'b', value: 1}
]);
});
it('should order by complex types with compareFn', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const input = new Map([[{id: 1}, 1], [{id: 0}, 1]]);
expect(pipe.transform<{id: number}, number>(input, (a, b) => a.key.id > b.key.id ? 1 : -1))
.toEqual([
{key: {id: 0}, value: 1},
{key: {id: 1}, value: 1},
]);
});
it('should return the same ref if nothing changes', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const transform1 = pipe.transform(new Map([[1, 2]]));
const transform2 = pipe.transform(new Map([[1, 2]]));
expect(transform1 === transform2).toEqual(true);
});
it('should return a new ref if something changes', () => {
const pipe = new KeyValuePipe(defaultKeyValueDiffers);
const transform1 = pipe.transform(new Map([[1, 2]]));
const transform2 = pipe.transform(new Map([[1, 3]]));
expect(transform1 !== transform2).toEqual(true);
});
});
});
describe('defaultComparator', () => {
it('should remain the same order when keys are equal', () => {
const key = 1;
const values = [{key, value: 2}, {key, value: 1}];
expect(values.sort(defaultComparator)).toEqual(values);
});
it('should sort undefined keys to the end', () => {
const values = [{key: 3, value: 1}, {key: undefined, value: 3}, {key: 1, value: 2}];
expect(values.sort(defaultComparator)).toEqual([
{key: 1, value: 2}, {key: 3, value: 1}, {key: undefined, value: 3}
]);
});
it('should sort null keys to the end', () => {
const values = [{key: 3, value: 1}, {key: null, value: 3}, {key: 1, value: 2}];
expect(values.sort(defaultComparator)).toEqual([
{key: 1, value: 2}, {key: 3, value: 1}, {key: null, value: 3}
]);
});
it('should sort strings in alpha ascending', () => {
const values = [{key: 'b', value: 1}, {key: 'a', value: 3}];
expect(values.sort(defaultComparator)).toEqual([{key: 'a', value: 3}, {key: 'b', value: 1}]);
});
it('should sort numbers in numerical ascending', () => {
const values = [{key: 2, value: 1}, {key: 1, value: 3}];
expect(values.sort(defaultComparator)).toEqual([{key: 1, value: 3}, {key: 2, value: 1}]);
});
it('should sort boolean in false (0) -> true (1)', () => {
const values = [{key: true, value: 3}, {key: false, value: 1}];
expect(values.sort(defaultComparator)).toEqual([{key: false, value: 1}, {key: true, value: 3}]);
});
it('should sort numbers as strings in numerical ascending', () => {
const values = [{key: '2', value: 1}, {key: 1, value: 3}];
expect(values.sort(defaultComparator)).toEqual([{key: 1, value: 3}, {key: '2', value: 1}]);
});
});

View File

@ -25,8 +25,6 @@ ts_library(
tsconfig = ":tsconfig",
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/annotations",
"//packages/compiler-cli/src/ngtsc/transform",
],
)
@ -35,9 +33,6 @@ npm_package(
srcs = [
"package.json",
],
tags = [
"ivy-jit",
"release-with-framework",
],
tags = ["release-with-framework"],
deps = [":compiler-cli"],
)

View File

@ -1,6 +1,6 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ivy_ng_module")
load("//tools:defaults.bzl", "ivy_ng_module", "ts_library")
load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle")
ivy_ng_module(

View File

@ -15,7 +15,7 @@
"chokidar": "^1.4.2"
},
"peerDependencies": {
"typescript": ">=2.7.2 <2.9",
"typescript": ">=2.7.2 <2.8",
"@angular/compiler": "0.0.0-PLACEHOLDER"
},
"engines" : {

View File

@ -40,8 +40,7 @@ export function main(
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' &&
options.annotationsAs !== 'decorators';
const transformDecorators = options.annotationsAs !== 'decorators';
const transformTypesToClosure = options.annotateForClosureCompiler;
if (!transformDecorators && !transformTypesToClosure) {
return undefined;

View File

@ -1,17 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "annotations",
srcs = glob([
"index.ts",
"src/**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/annotations",
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
],
)

View File

@ -1,13 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export {ComponentDecoratorHandler} from './src/component';
export {DirectiveDecoratorHandler} from './src/directive';
export {InjectableDecoratorHandler} from './src/injectable';
export {NgModuleDecoratorHandler} from './src/ng_module';
export {CompilationScope, SelectorScopeRegistry} from './src/selector_scope';

View File

@ -1,117 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, Expression, R3ComponentMetadata, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator, reflectNonStaticField, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
import {extractDirectiveMetadata} from './directive';
import {SelectorScopeRegistry} from './selector_scope';
const EMPTY_MAP = new Map<string, Expression>();
/**
* `DecoratorHandler` which handles the `@Component` annotation.
*/
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata> {
constructor(private checker: ts.TypeChecker, private scopeRegistry: SelectorScopeRegistry) {}
detect(decorators: Decorator[]): Decorator|undefined {
return decorators.find(
decorator => decorator.name === 'Component' && decorator.from === '@angular/core');
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3ComponentMetadata> {
const meta = decorator.args[0];
if (!ts.isObjectLiteralExpression(meta)) {
throw new Error(`Decorator argument must be literal.`);
}
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
// on it.
const directiveMetadata = extractDirectiveMetadata(node, decorator, this.checker);
if (directiveMetadata === undefined) {
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
// case, compilation of the decorator is skipped. Returning an empty object signifies
// that no analysis was produced.
return {};
}
// Next, read the `@Component`-specific fields.
const component = reflectObjectLiteral(meta);
// Resolve and parse the template.
if (!component.has('template')) {
throw new Error(`For now, components must directly have a template.`);
}
const templateExpr = component.get('template') !;
const templateStr = staticallyResolve(templateExpr, this.checker);
if (typeof templateStr !== 'string') {
throw new Error(`Template must statically resolve to a string: ${node.name!.text}`);
}
let preserveWhitespaces: boolean = false;
if (component.has('preserveWhitespaces')) {
const value = staticallyResolve(component.get('preserveWhitespaces') !, this.checker);
if (typeof value !== 'boolean') {
throw new Error(`preserveWhitespaces must resolve to a boolean if present`);
}
preserveWhitespaces = value;
}
const template = parseTemplate(
templateStr, `${node.getSourceFile().fileName}#${node.name!.text}/template.html`,
{preserveWhitespaces});
if (template.errors !== undefined) {
throw new Error(
`Errors parsing template: ${template.errors.map(e => e.toString()).join(', ')}`);
}
// If the component has a selector, it should be registered with the `SelectorScopeRegistry` so
// when this component appears in an `@NgModule` scope, its selector can be determined.
if (directiveMetadata.selector !== null) {
this.scopeRegistry.registerSelector(node, directiveMetadata.selector);
}
return {
analysis: {
...directiveMetadata,
template,
viewQueries: [],
// These will be replaced during the compilation step, after all `NgModule`s have been
// analyzed and the full compilation scope for the component can be realized.
pipes: EMPTY_MAP,
directives: EMPTY_MAP,
}
};
}
compile(node: ts.ClassDeclaration, analysis: R3ComponentMetadata): CompileResult {
const pool = new ConstantPool();
// Check whether this component was registered with an NgModule. If so, it should be compiled
// under that module's compilation scope.
const scope = this.scopeRegistry.lookupCompilationScope(node);
if (scope !== null) {
// Replace the empty components and directives from the analyze() step with a fully expanded
// scope. This is possible now because during compile() the whole compilation unit has been
// fully analyzed.
analysis = {...analysis, ...scope};
}
const res = compileComponentFromMetadata(analysis, pool, makeBindingParser());
return {
field: 'ngComponentDef',
initializer: res.expression,
statements: pool.statements,
type: res.type,
};
}
}

View File

@ -1,212 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator, staticallyResolve} from '../../metadata';
import {DecoratedNode, getDecoratedClassElements, reflectNonStaticField, reflectObjectLiteral} from '../../metadata/src/reflector';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
import {SelectorScopeRegistry} from './selector_scope';
import {getConstructorDependencies} from './util';
const EMPTY_OBJECT: {[key: string]: string} = {};
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata> {
constructor(private checker: ts.TypeChecker, private scopeRegistry: SelectorScopeRegistry) {}
detect(decorators: Decorator[]): Decorator|undefined {
return decorators.find(
decorator => decorator.name === 'Directive' && decorator.from === '@angular/core');
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3DirectiveMetadata> {
const analysis = extractDirectiveMetadata(node, decorator, this.checker);
// If the directive has a selector, it should be registered with the `SelectorScopeRegistry` so
// when this directive appears in an `@NgModule` scope, its selector can be determined.
if (analysis && analysis.selector !== null) {
this.scopeRegistry.registerSelector(node, analysis.selector);
}
return {analysis};
}
compile(node: ts.ClassDeclaration, analysis: R3DirectiveMetadata): CompileResult {
const pool = new ConstantPool();
const res = compileDirectiveFromMetadata(analysis, pool, makeBindingParser());
return {
field: 'ngDirectiveDef',
initializer: res.expression,
statements: pool.statements,
type: res.type,
};
}
}
/**
* Helper function to extract metadata from a `Directive` or `Component`.
*/
export function extractDirectiveMetadata(
clazz: ts.ClassDeclaration, decorator: Decorator, checker: ts.TypeChecker): R3DirectiveMetadata|
undefined {
const meta = decorator.args[0];
if (!ts.isObjectLiteralExpression(meta)) {
throw new Error(`Decorator argument must be literal.`);
}
const directive = reflectObjectLiteral(meta);
if (directive.has('jit')) {
// The only allowed value is true, so there's no need to expand further.
return undefined;
}
// Precompute a list of ts.ClassElements that have decorators. This includes things like @Input,
// @Output, @HostBinding, etc.
const decoratedElements = getDecoratedClassElements(clazz, checker);
// Construct the map of inputs both from the @Directive/@Component decorator, and the decorated
// fields.
const inputsFromMeta = parseFieldToPropertyMapping(directive, 'inputs', checker);
const inputsFromFields = parseDecoratedFields(
findDecoratedFields(decoratedElements, '@angular/core', 'Input'), checker);
// And outputs.
const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', checker);
const outputsFromFields = parseDecoratedFields(
findDecoratedFields(decoratedElements, '@angular/core', 'Output'), checker);
// Parse the selector.
let selector = '';
if (directive.has('selector')) {
const resolved = staticallyResolve(directive.get('selector') !, checker);
if (typeof resolved !== 'string') {
throw new Error(`Selector must be a string`);
}
selector = resolved;
}
// Determine if `ngOnChanges` is a lifecycle hook defined on the component.
const usesOnChanges = reflectNonStaticField(clazz, 'ngOnChanges') !== null;
return {
name: clazz.name !.text,
deps: getConstructorDependencies(clazz, checker),
host: {
attributes: {},
listeners: {},
properties: {},
},
lifecycle: {
usesOnChanges,
},
inputs: {...inputsFromMeta, ...inputsFromFields},
outputs: {...outputsFromMeta, ...outputsFromFields},
queries: [], selector,
type: new WrappedNodeExpr(clazz.name !),
typeSourceSpan: null !,
};
}
function assertIsStringArray(value: any[]): value is string[] {
for (let i = 0; i < value.length; i++) {
if (typeof value[i] !== 'string') {
throw new Error(`Failed to resolve @Directive.inputs[${i}] to a string`);
}
}
return true;
}
type DecoratedProperty = DecoratedNode<ts.PropertyDeclaration|ts.AccessorDeclaration>;
/**
* Find all fields in the array of `DecoratedNode`s that have a decorator of the given type.
*/
function findDecoratedFields(
elements: DecoratedNode<ts.ClassElement>[], decoratorModule: string,
decoratorName: string): DecoratedProperty[] {
return elements
.map(entry => {
const element = entry.element;
// Only consider properties and accessors. Filter out everything else.
if (!ts.isPropertyDeclaration(element) && !ts.isAccessor(element)) {
return null;
}
// Extract the array of matching decorators (there could be more than one).
const decorators = entry.decorators.filter(
decorator => decorator.name === decoratorName && decorator.from === decoratorModule);
if (decorators.length === 0) {
// No matching decorators, don't include this element.
return null;
}
return {element, decorators};
})
// Filter out nulls.
.filter(entry => entry !== null) as DecoratedProperty[];
}
/**
* Interpret property mapping fields on the decorator (e.g. inputs or outputs) and return the
* correctly shaped metadata object.
*/
function parseFieldToPropertyMapping(
directive: Map<string, ts.Expression>, field: string,
checker: ts.TypeChecker): {[field: string]: string} {
if (!directive.has(field)) {
return EMPTY_OBJECT;
}
// Resolve the field of interest from the directive metadata to a string[].
const metaValues = staticallyResolve(directive.get(field) !, checker);
if (!Array.isArray(metaValues) || !assertIsStringArray(metaValues)) {
throw new Error(`Failed to resolve @Directive.${field}`);
}
return metaValues.reduce(
(results, value) => {
// Either the value is 'field' or 'field: property'. In the first case, `property` will
// be undefined, in which case the field name should also be used as the property name.
const [field, property] = value.split(':', 2).map(str => str.trim());
results[field] = property || field;
return results;
},
{} as{[field: string]: string});
}
/**
* Parse property decorators (e.g. `Input` or `Output`) and return the correctly shaped metadata
* object.
*/
function parseDecoratedFields(
fields: DecoratedProperty[], checker: ts.TypeChecker): {[field: string]: string} {
return fields.reduce(
(results, field) => {
const fieldName = (field.element.name as ts.Identifier).text;
field.decorators.forEach(decorator => {
// The decorator either doesn't have an argument (@Input()) in which case the property
// name is used, or it has one argument (@Output('named')).
if (decorator.args.length === 0) {
results[fieldName] = fieldName;
} else if (decorator.args.length === 1) {
const property = staticallyResolve(decorator.args[0], checker);
if (typeof property !== 'string') {
throw new Error(`Decorator argument must resolve to a string`);
}
results[fieldName] = property;
} else {
// Too many arguments.
throw new Error(
`Decorator must have 0 or 1 arguments, got ${decorator.args.length} argument(s)`);
}
});
return results;
},
{} as{[field: string]: string});
}

View File

@ -1,156 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, LiteralExpr, R3DependencyMetadata, R3InjectableMetadata, R3ResolvedDependencyType, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator} from '../../metadata';
import {reflectConstructorParameters, reflectImportedIdentifier, reflectObjectLiteral} from '../../metadata/src/reflector';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform/src/api';
import {getConstructorDependencies} from './util';
/**
* Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
*/
export class InjectableDecoratorHandler implements DecoratorHandler<R3InjectableMetadata> {
constructor(private checker: ts.TypeChecker) {}
detect(decorator: Decorator[]): Decorator|undefined {
return decorator.find(dec => dec.name === 'Injectable' && dec.from === '@angular/core');
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3InjectableMetadata> {
return {
analysis: extractInjectableMetadata(node, decorator, this.checker),
};
}
compile(node: ts.ClassDeclaration, analysis: R3InjectableMetadata): CompileResult {
const res = compileIvyInjectable(analysis);
return {
field: 'ngInjectableDef',
initializer: res.expression,
statements: [],
type: res.type,
};
}
}
/**
* Read metadata from the `@Injectable` decorator and produce the `IvyInjectableMetadata`, the input
* metadata needed to run `compileIvyInjectable`.
*/
function extractInjectableMetadata(
clazz: ts.ClassDeclaration, decorator: Decorator,
checker: ts.TypeChecker): R3InjectableMetadata {
if (clazz.name === undefined) {
throw new Error(`@Injectables must have names`);
}
const name = clazz.name.text;
const type = new WrappedNodeExpr(clazz.name);
if (decorator.args.length === 0) {
return {
name,
type,
providedIn: new LiteralExpr(null),
deps: getConstructorDependencies(clazz, checker),
};
} else if (decorator.args.length === 1) {
const metaNode = decorator.args[0];
// Firstly make sure the decorator argument is an inline literal - if not, it's illegal to
// transport references from one location to another. This is the problem that lowering
// used to solve - if this restriction proves too undesirable we can re-implement lowering.
if (!ts.isObjectLiteralExpression(metaNode)) {
throw new Error(`In Ivy, decorator metadata must be inline.`);
}
// Resolve the fields of the literal into a map of field name to expression.
const meta = reflectObjectLiteral(metaNode);
let providedIn: Expression = new LiteralExpr(null);
if (meta.has('providedIn')) {
providedIn = new WrappedNodeExpr(meta.get('providedIn') !);
}
if (meta.has('useValue')) {
return {name, type, providedIn, useValue: new WrappedNodeExpr(meta.get('useValue') !)};
} else if (meta.has('useExisting')) {
return {name, type, providedIn, useExisting: new WrappedNodeExpr(meta.get('useExisting') !)};
} else if (meta.has('useClass')) {
return {name, type, providedIn, useClass: new WrappedNodeExpr(meta.get('useClass') !)};
} else if (meta.has('useFactory')) {
// useFactory is special - the 'deps' property must be analyzed.
const factory = new WrappedNodeExpr(meta.get('useFactory') !);
const deps: R3DependencyMetadata[] = [];
if (meta.has('deps')) {
const depsExpr = meta.get('deps') !;
if (!ts.isArrayLiteralExpression(depsExpr)) {
throw new Error(`In Ivy, deps metadata must be inline.`);
}
if (depsExpr.elements.length > 0) {
throw new Error(`deps not yet supported`);
}
deps.push(...depsExpr.elements.map(dep => getDep(dep, checker)));
}
return {name, type, providedIn, useFactory: factory, deps};
} else {
const deps = getConstructorDependencies(clazz, checker);
return {name, type, providedIn, deps};
}
} else {
throw new Error(`Too many arguments to @Injectable`);
}
}
function getDep(dep: ts.Expression, checker: ts.TypeChecker): R3DependencyMetadata {
const meta: R3DependencyMetadata = {
token: new WrappedNodeExpr(dep),
host: false,
resolved: R3ResolvedDependencyType.Token,
optional: false,
self: false,
skipSelf: false,
};
function maybeUpdateDecorator(dec: ts.Identifier, token?: ts.Expression): void {
const source = reflectImportedIdentifier(dec, checker);
if (source === null || source.from !== '@angular/core') {
return;
}
switch (source.name) {
case 'Inject':
if (token !== undefined) {
meta.token = new WrappedNodeExpr(token);
}
break;
case 'Optional':
meta.optional = true;
break;
case 'SkipSelf':
meta.skipSelf = true;
break;
case 'Self':
meta.self = true;
break;
}
}
if (ts.isArrayLiteralExpression(dep)) {
dep.elements.forEach(el => {
if (ts.isIdentifier(el)) {
maybeUpdateDecorator(el);
} else if (ts.isNewExpression(el) && ts.isIdentifier(el.expression)) {
const token = el.arguments && el.arguments.length > 0 && el.arguments[0] || undefined;
maybeUpdateDecorator(el.expression, token);
}
});
}
return meta;
}

View File

@ -1,116 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, Expression, R3DirectiveMetadata, R3NgModuleMetadata, WrappedNodeExpr, compileNgModule, makeBindingParser, parseTemplate} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator, Reference, ResolvedValue, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
import {SelectorScopeRegistry} from './selector_scope';
import {referenceToExpression} from './util';
/**
* Compiles @NgModule annotations to ngModuleDef fields.
*
* TODO(alxhub): handle injector side of things as well.
*/
export class NgModuleDecoratorHandler implements DecoratorHandler<R3NgModuleMetadata> {
constructor(private checker: ts.TypeChecker, private scopeRegistry: SelectorScopeRegistry) {}
detect(decorators: Decorator[]): Decorator|undefined {
return decorators.find(
decorator => decorator.name === 'NgModule' && decorator.from === '@angular/core');
}
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3NgModuleMetadata> {
const meta = decorator.args[0];
if (!ts.isObjectLiteralExpression(meta)) {
throw new Error(`Decorator argument must be literal.`);
}
const ngModule = reflectObjectLiteral(meta);
if (ngModule.has('jit')) {
// The only allowed value is true, so there's no need to expand further.
return {};
}
// Extract the module declarations, imports, and exports.
let declarations: Reference[] = [];
if (ngModule.has('declarations')) {
const declarationMeta = staticallyResolve(ngModule.get('declarations') !, this.checker);
declarations = resolveTypeList(declarationMeta, 'declarations');
}
let imports: Reference[] = [];
if (ngModule.has('imports')) {
const importsMeta = staticallyResolve(ngModule.get('imports') !, this.checker);
imports = resolveTypeList(importsMeta, 'imports');
}
let exports: Reference[] = [];
if (ngModule.has('exports')) {
const exportsMeta = staticallyResolve(ngModule.get('exports') !, this.checker);
exports = resolveTypeList(exportsMeta, 'exports');
}
// Register this module's information with the SelectorScopeRegistry. This ensures that during
// the compile() phase, the module's metadata is available for selector scope computation.
this.scopeRegistry.registerModule(node, {declarations, imports, exports});
const context = node.getSourceFile();
return {
analysis: {
type: new WrappedNodeExpr(node.name !),
bootstrap: [],
declarations: declarations.map(decl => referenceToExpression(decl, context)),
exports: exports.map(exp => referenceToExpression(exp, context)),
imports: imports.map(imp => referenceToExpression(imp, context)),
emitInline: false,
},
};
}
compile(node: ts.ClassDeclaration, analysis: R3NgModuleMetadata): CompileResult {
const res = compileNgModule(analysis);
return {
field: 'ngModuleDef',
initializer: res.expression,
statements: [],
type: res.type,
};
}
}
/**
* Compute a list of `Reference`s from a resolved metadata value.
*/
function resolveTypeList(resolvedList: ResolvedValue, name: string): Reference[] {
const refList: Reference[] = [];
if (!Array.isArray(resolvedList)) {
throw new Error(`Expected array when reading property ${name}`);
}
resolvedList.forEach((entry, idx) => {
if (Array.isArray(entry)) {
// Recurse into nested arrays.
refList.push(...resolveTypeList(entry, name));
} else if (entry instanceof Reference) {
if (!entry.expressable) {
throw new Error(`Value at position ${idx} in ${name} array is not expressable`);
} else if (!ts.isClassDeclaration(entry.node)) {
throw new Error(`Value at position ${idx} in ${name} array is not a class declaration`);
}
refList.push(entry);
} else {
// TODO(alxhub): expand ModuleWithProviders.
throw new Error(`Value at position ${idx} in ${name} array is not a reference`);
}
});
return refList;
}

View File

@ -1,353 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, ExternalExpr, ExternalReference} from '@angular/compiler';
import * as ts from 'typescript';
import {AbsoluteReference, Reference, reflectStaticField, reflectTypeEntityToDeclaration} from '../../metadata';
import {referenceToExpression} from './util';
/**
* Metadata extracted for a given NgModule that can be used to compute selector scopes.
*/
export interface ModuleData {
declarations: Reference[];
imports: Reference[];
exports: Reference[];
}
/**
* Transitively expanded maps of directives and pipes visible to a component being compiled in the
* context of some module.
*/
export interface CompilationScope<T> {
directives: Map<string, T>;
pipes: Map<string, T>;
}
/**
* Both transitively expanded scopes for a given NgModule.
*/
interface SelectorScopes {
/**
* Set of components, directives, and pipes visible to all components being compiled in the
* context of some module.
*/
compilation: Reference[];
/**
* Set of components, directives, and pipes added to the compilation scope of any module importing
* some module.
*/
exported: Reference[];
}
/**
* Registry which records and correlates static analysis information of Angular types.
*
* Once a compilation unit's information is fed into the SelectorScopeRegistry, it can be asked to
* produce transitive `CompilationScope`s for components.
*/
export class SelectorScopeRegistry {
/**
* Map of modules declared in the current compilation unit to their (local) metadata.
*/
private _moduleToData = new Map<ts.ClassDeclaration, ModuleData>();
/**
* Map of modules to their cached `CompilationScope`s.
*/
private _compilationScopeCache = new Map<ts.ClassDeclaration, CompilationScope<Reference>>();
/**
* Map of components/directives to their selector.
*/
private _directiveToSelector = new Map<ts.ClassDeclaration, string>();
/**
* Map of pipes to their name.
*/
private _pipeToName = new Map<ts.ClassDeclaration, string>();
/**
* Map of components/directives/pipes to their module.
*/
private _declararedTypeToModule = new Map<ts.ClassDeclaration, ts.ClassDeclaration>();
constructor(private checker: ts.TypeChecker) {}
/**
* Register a module's metadata with the registry.
*/
registerModule(node: ts.ClassDeclaration, data: ModuleData): void {
node = ts.getOriginalNode(node) as ts.ClassDeclaration;
if (this._moduleToData.has(node)) {
throw new Error(`Module already registered: ${node.name!.text}`);
}
this._moduleToData.set(node, data);
// Register all of the module's declarations in the context map as belonging to this module.
data.declarations.forEach(decl => {
this._declararedTypeToModule.set(ts.getOriginalNode(decl.node) as ts.ClassDeclaration, node);
});
}
/**
* Register the selector of a component or directive with the registry.
*/
registerSelector(node: ts.ClassDeclaration, selector: string): void {
node = ts.getOriginalNode(node) as ts.ClassDeclaration;
if (this._directiveToSelector.has(node)) {
throw new Error(`Selector already registered: ${node.name!.text} ${selector}`);
}
this._directiveToSelector.set(node, selector);
}
/**
* Register the name of a pipe with the registry.
*/
registerPipe(node: ts.ClassDeclaration, name: string): void { this._pipeToName.set(node, name); }
/**
* Produce the compilation scope of a component, which is determined by the module that declares
* it.
*/
lookupCompilationScope(node: ts.ClassDeclaration): CompilationScope<Expression>|null {
node = ts.getOriginalNode(node) as ts.ClassDeclaration;
// If the component has no associated module, then it has no compilation scope.
if (!this._declararedTypeToModule.has(node)) {
return null;
}
const module = this._declararedTypeToModule.get(node) !;
// Compilation scope computation is somewhat expensive, so it's cached. Check the cache for
// the module.
if (this._compilationScopeCache.has(module)) {
// The compilation scope was cached.
const scope = this._compilationScopeCache.get(module) !;
// The scope as cached is in terms of References, not Expressions. Converting between them
// requires knowledge of the context file (in this case, the component node's source file).
return convertScopeToExpressions(scope, node.getSourceFile());
}
// This is the first time the scope for this module is being computed.
const directives = new Map<string, Reference>();
const pipes = new Map<string, Reference>();
// Process the declaration scope of the module, and lookup the selector of every declared type.
// The initial value of ngModuleImportedFrom is 'null' which signifies that the NgModule
// was not imported from a .d.ts source.
this.lookupScopes(module !, /* ngModuleImportedFrom */ null).compilation.forEach(ref => {
const selector =
this.lookupDirectiveSelector(ts.getOriginalNode(ref.node) as ts.ClassDeclaration);
// Only directives/components with selectors get added to the scope.
if (selector != null) {
directives.set(selector, ref);
}
});
const scope: CompilationScope<Reference> = {directives, pipes};
// Many components may be compiled in the same scope, so cache it.
this._compilationScopeCache.set(node, scope);
// Convert References to Expressions in the context of the component's source file.
return convertScopeToExpressions(scope, node.getSourceFile());
}
/**
* Lookup `SelectorScopes` for a given module.
*
* This function assumes that if the given module was imported from an absolute path
* (`ngModuleImportedFrom`) then all of its declarations are exported at that same path, as well
* as imports and exports from other modules that are relatively imported.
*/
private lookupScopes(node: ts.ClassDeclaration, ngModuleImportedFrom: string|null):
SelectorScopes {
let data: ModuleData|null = null;
// Either this module was analyzed directly, or has a precompiled ngModuleDef.
if (this._moduleToData.has(node)) {
// The module was analyzed before, and thus its data is available.
data = this._moduleToData.get(node) !;
} else {
// The module wasn't analyzed before, and probably has a precompiled ngModuleDef with a type
// annotation that specifies the needed metadata.
if (ngModuleImportedFrom === null) {
// TODO(alxhub): handle hand-compiled ngModuleDef in the current Program.
throw new Error(`Need to read .d.ts module but ngModuleImportedFrom is unspecified`);
}
data = this._readMetadataFromCompiledClass(node, ngModuleImportedFrom);
// Note that data here could still be null, if the class didn't have a precompiled
// ngModuleDef.
}
if (data === null) {
throw new Error(`Module not registered: ${node.name!.text}`);
}
return {
compilation: [
...data.declarations,
// Expand imports to the exported scope of those imports.
...flatten(data.imports.map(
ref => this.lookupScopes(ref.node as ts.ClassDeclaration, absoluteModuleName(ref))
.exported)),
// And include the compilation scope of exported modules.
...flatten(
data.exports.filter(ref => this._moduleToData.has(ref.node as ts.ClassDeclaration))
.map(
ref =>
this.lookupScopes(ref.node as ts.ClassDeclaration, absoluteModuleName(ref))
.exported))
],
exported: flatten(data.exports.map(ref => {
if (this._moduleToData.has(ref.node as ts.ClassDeclaration)) {
return this.lookupScopes(ref.node as ts.ClassDeclaration, absoluteModuleName(ref))
.exported;
} else {
return [ref];
}
})),
};
}
/**
* Lookup the selector of a component or directive class.
*
* Potentially this class is declared in a .d.ts file or otherwise has a manually created
* ngComponentDef/ngDirectiveDef. In this case, the type metadata of that definition is read
* to determine the selector.
*/
private lookupDirectiveSelector(node: ts.ClassDeclaration): string|null {
if (this._directiveToSelector.has(node)) {
return this._directiveToSelector.get(node) !;
} else {
return this._readSelectorFromCompiledClass(node);
}
}
private lookupPipeName(node: ts.ClassDeclaration): string|undefined {
return this._pipeToName.get(node);
}
/**
* Read the metadata from a class that has already been compiled somehow (either it's in a .d.ts
* file, or in a .ts file with a handwritten definition).
*
* @param clazz the class of interest
* @param ngModuleImportedFrom module specifier of the import path to assume for all declarations
* stemming from this module.
*/
private _readMetadataFromCompiledClass(clazz: ts.ClassDeclaration, ngModuleImportedFrom: string):
ModuleData|null {
// This operation is explicitly not memoized, as it depends on `ngModuleImportedFrom`.
// TODO(alxhub): investigate caching of .d.ts module metadata.
const ngModuleDef = reflectStaticField(clazz, 'ngModuleDef');
if (ngModuleDef === null) {
return null;
} else if (
// Validate that the shape of the ngModuleDef type is correct.
ngModuleDef.type === undefined || !ts.isTypeReferenceNode(ngModuleDef.type) ||
ngModuleDef.type.typeArguments === undefined ||
ngModuleDef.type.typeArguments.length !== 4) {
return null;
}
// Read the ModuleData out of the type arguments.
const [_, declarationMetadata, importMetadata, exportMetadata] = ngModuleDef.type.typeArguments;
return {
declarations: this._extractReferencesFromType(declarationMetadata, ngModuleImportedFrom),
exports: this._extractReferencesFromType(exportMetadata, ngModuleImportedFrom),
imports: this._extractReferencesFromType(importMetadata, ngModuleImportedFrom),
};
}
/**
* Get the selector from type metadata for a class with a precompiled ngComponentDef or
* ngDirectiveDef.
*/
private _readSelectorFromCompiledClass(clazz: ts.ClassDeclaration): string|null {
const def =
reflectStaticField(clazz, 'ngComponentDef') || reflectStaticField(clazz, 'ngDirectiveDef');
if (def === null) {
// No definition could be found.
return null;
} else if (
def.type === undefined || !ts.isTypeReferenceNode(def.type) ||
def.type.typeArguments === undefined || def.type.typeArguments.length !== 2) {
// The type metadata was the wrong shape.
return null;
}
const type = def.type.typeArguments[1];
if (!ts.isLiteralTypeNode(type) || !ts.isStringLiteral(type.literal)) {
// The type metadata was the wrong type.
return null;
}
return type.literal.text;
}
/**
* Process a `TypeNode` which is a tuple of references to other types, and return `Reference`s to
* them.
*
* This operation assumes that these types should be imported from `ngModuleImportedFrom` unless
* they themselves were imported from another absolute path.
*/
private _extractReferencesFromType(def: ts.TypeNode, ngModuleImportedFrom: string): Reference[] {
if (!ts.isTupleTypeNode(def)) {
return [];
}
return def.elementTypes.map(element => {
if (!ts.isTypeReferenceNode(element)) {
throw new Error(`Expected TypeReferenceNode`);
}
const type = element.typeName;
const {node, from} = reflectTypeEntityToDeclaration(type, this.checker);
const moduleName = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
const clazz = node as ts.ClassDeclaration;
return new AbsoluteReference(node, clazz.name !, moduleName, clazz.name !.text);
});
}
}
function flatten<T>(array: T[][]): T[] {
return array.reduce((accum, subArray) => {
accum.push(...subArray);
return accum;
}, [] as T[]);
}
function absoluteModuleName(ref: Reference): string|null {
const name = (ref.node as ts.ClassDeclaration).name !.text;
if (!(ref instanceof AbsoluteReference)) {
return null;
}
return ref.moduleName;
}
function convertReferenceMap(
map: Map<string, Reference>, context: ts.SourceFile): Map<string, Expression> {
return new Map<string, Expression>(Array.from(map.entries()).map(([selector, ref]): [
string, Expression
] => [selector, referenceToExpression(ref, context)]));
}
function convertScopeToExpressions(
scope: CompilationScope<Reference>, context: ts.SourceFile): CompilationScope<Expression> {
const directives = convertReferenceMap(scope.directives, context);
const pipes = convertReferenceMap(scope.pipes, context);
return {directives, pipes};
}

View File

@ -1,84 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, R3DependencyMetadata, R3ResolvedDependencyType, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {Reference, reflectConstructorParameters} from '../../metadata';
import {reflectImportedIdentifier} from '../../metadata/src/reflector';
export function getConstructorDependencies(
clazz: ts.ClassDeclaration, checker: ts.TypeChecker): R3DependencyMetadata[] {
const useType: R3DependencyMetadata[] = [];
const ctorParams = (reflectConstructorParameters(clazz, checker) || []);
ctorParams.forEach(param => {
let tokenExpr = param.typeValueExpr;
let optional = false, self = false, skipSelf = false, host = false;
let resolved = R3ResolvedDependencyType.Token;
param.decorators.filter(dec => dec.from === '@angular/core').forEach(dec => {
if (dec.name === 'Inject') {
if (dec.args.length !== 1) {
throw new Error(`Unexpected number of arguments to @Inject().`);
}
tokenExpr = dec.args[0];
} else if (dec.name === 'Optional') {
optional = true;
} else if (dec.name === 'SkipSelf') {
skipSelf = true;
} else if (dec.name === 'Self') {
self = true;
} else if (dec.name === 'Host') {
host = true;
} else if (dec.name === 'Attribute') {
if (dec.args.length !== 1) {
throw new Error(`Unexpected number of arguments to @Attribute().`);
}
tokenExpr = dec.args[0];
resolved = R3ResolvedDependencyType.Attribute;
} else {
throw new Error(`Unexpected decorator ${dec.name} on parameter.`);
}
});
if (tokenExpr === null) {
throw new Error(
`No suitable token for parameter ${(param.name as ts.Identifier).text} of class ${clazz.name!.text} with decorators ${param.decorators.map(dec => dec.from + '#' + dec.name).join(',')}`);
}
if (ts.isIdentifier(tokenExpr)) {
const importedSymbol = reflectImportedIdentifier(tokenExpr, checker);
if (importedSymbol !== null && importedSymbol.from === '@angular/core') {
switch (importedSymbol.name) {
case 'ElementRef':
resolved = R3ResolvedDependencyType.ElementRef;
break;
case 'Injector':
resolved = R3ResolvedDependencyType.Injector;
break;
case 'TemplateRef':
resolved = R3ResolvedDependencyType.TemplateRef;
break;
case 'ViewContainerRef':
resolved = R3ResolvedDependencyType.ViewContainerRef;
break;
default:
// Leave as a Token or Attribute.
}
}
}
const token = new WrappedNodeExpr(tokenExpr);
useType.push({token, optional, self, skipSelf, host, resolved});
});
return useType;
}
export function referenceToExpression(ref: Reference, context: ts.SourceFile): Expression {
const exp = ref.toExpression(context);
if (exp === null) {
throw new Error(`Could not refer to ${ts.SyntaxKind[ref.node.kind]}`);
}
return exp;
}

View File

@ -1,28 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
ts_library(
name = "test_lib",
testonly = 1,
srcs = glob([
"**/*.ts",
]),
deps = [
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/annotations",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/testing",
],
)
jasmine_node_test(
name = "test",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
deps = [
":test_lib",
"//tools/testing:node_no_angular",
],
)

View File

@ -1,79 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {AbsoluteReference, ResolvedReference} from '../../metadata/src/resolver';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {NgModuleDecoratorHandler} from '../src/ng_module';
import {SelectorScopeRegistry} from '../src/selector_scope';
describe('SelectorScopeRegistry', () => {
it('absolute imports work', () => {
const {program} = makeProgram([
{
name: 'node_modules/@angular/core/index.d.ts',
contents: `
export interface NgComponentDef<A, B> {}
export interface NgModuleDef<A, B, C, D> {}
`
},
{
name: 'node_modules/some_library/index.d.ts',
contents: `
import {NgComponentDef, NgModuleDef} from '@angular/core';
import * as i0 from './component';
export declare class SomeModule {
static ngModuleDef: NgModuleDef<SomeModule, [i0.SomeCmp], any, [i0.SomeCmp]>;
}
export declare class SomeCmp {
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
}
`
},
{
name: 'node_modules/some_library/component.d.ts',
contents: `
export declare class SomeCmp {}
`
},
{
name: 'entry.ts',
contents: `
export class ProgramCmp {}
export class ProgramModule {}
`
},
]);
const checker = program.getTypeChecker();
const ProgramModule =
getDeclaration(program, 'entry.ts', 'ProgramModule', ts.isClassDeclaration);
const ProgramCmp = getDeclaration(program, 'entry.ts', 'ProgramCmp', ts.isClassDeclaration);
const SomeModule = getDeclaration(
program, 'node_modules/some_library/index.d.ts', 'SomeModule', ts.isClassDeclaration);
expect(ProgramModule).toBeDefined();
expect(SomeModule).toBeDefined();
const registry = new SelectorScopeRegistry(checker);
registry.registerModule(ProgramModule, {
declarations: [new ResolvedReference(ProgramCmp, ProgramCmp.name !)],
exports: [],
imports: [new AbsoluteReference(SomeModule, SomeModule.name !, 'some_library', 'SomeModule')],
});
registry.registerSelector(ProgramCmp, 'program-cmp');
const scope = registry.lookupCompilationScope(ProgramCmp) !;
expect(scope).toBeDefined();
expect(scope.directives).toBeDefined();
expect(scope.directives.size).toBe(1);
});
});

View File

@ -1,77 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
/**
* The TypeScript compiler host used by `ngtsc`.
*
* It's mostly identical to the native `CompilerHost`, but also includes the ability to
* asynchronously resolve resources.
*/
export interface CompilerHost extends ts.CompilerHost {
/**
* Begin processing a resource file.
*
* When the returned Promise resolves, `loadResource` should be able to synchronously produce a
* `string` for the given file.
*/
preloadResource(file: string): Promise<void>;
/**
* Like `readFile`, but reads the contents of a resource file which may have been pre-processed
* by `preloadResource`.
*/
loadResource(file: string): string|undefined;
}
/**
* Implementation of `CompilerHost` which delegates to a native TypeScript host in most cases.
*/
export class NgtscCompilerHost implements CompilerHost {
constructor(private delegate: ts.CompilerHost) {}
getSourceFile(
fileName: string, languageVersion: ts.ScriptTarget,
onError?: ((message: string) => void)|undefined,
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
return this.delegate.getSourceFile(
fileName, languageVersion, onError, shouldCreateNewSourceFile);
}
getDefaultLibFileName(options: ts.CompilerOptions): string {
return this.delegate.getDefaultLibFileName(options);
}
writeFile(
fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void)|undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>): void {
return this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
}
getCurrentDirectory(): string { return this.delegate.getCurrentDirectory(); }
getDirectories(path: string): string[] { return this.delegate.getDirectories(path); }
getCanonicalFileName(fileName: string): string {
return this.delegate.getCanonicalFileName(fileName);
}
useCaseSensitiveFileNames(): boolean { return this.delegate.useCaseSensitiveFileNames(); }
getNewLine(): string { return this.delegate.getNewLine(); }
fileExists(fileName: string): boolean { return this.delegate.fileExists(fileName); }
readFile(fileName: string): string|undefined { return this.delegate.readFile(fileName); }
loadResource(file: string): string|undefined { throw new Error('Method not implemented.'); }
preloadResource(file: string): Promise<void> { throw new Error('Method not implemented.'); }
}

View File

@ -1,16 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "metadata",
srcs = glob([
"index.ts",
"src/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/metadata",
deps = [
"//packages:types",
"//packages/compiler",
],
)

View File

@ -1,11 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export {Decorator, Parameter, reflectConstructorParameters, reflectDecorator, reflectNonStaticField, reflectObjectLiteral, reflectStaticField, reflectTypeEntityToDeclaration,} from './src/reflector';
export {AbsoluteReference, Reference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver';

View File

@ -1,338 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
/**
* reflector.ts implements static reflection of declarations using the TypeScript `ts.TypeChecker`.
*/
/**
* A reflected parameter of a function, method, or constructor, indicating the name, any
* decorators, and an expression representing a reference to the value side of the parameter's
* declared type, if applicable.
*/
export interface Parameter {
/**
* Name of the parameter as a `ts.BindingName`, which allows the parameter name to be identified
* via sourcemaps.
*/
name: ts.BindingName;
/**
* A `ts.Expression` which represents a reference to the value side of the parameter's type.
*/
typeValueExpr: ts.Expression|null;
/**
* Array of decorators present on the parameter.
*/
decorators: Decorator[];
}
/**
* A reflected decorator, indicating the name, where it was imported from, and any arguments if the
* decorator is a call expression.
*/
export interface Decorator {
/**
* Name of the decorator, extracted from the decoration expression.
*/
name: string;
/**
* Import path (relative to the decorator's file) of the decorator itself.
*/
from: string;
/**
* The decorator node itself (useful for printing sourcemap based references to the decorator).
*/
node: ts.Decorator;
/**
* Any arguments of a call expression, if one is present. If the decorator was not a call
* expression, then this will be an empty array.
*/
args: ts.Expression[];
}
/**
* Reflect a `ts.ClassDeclaration` and determine the list of parameters.
*
* Note that this only reflects the referenced class and not any potential parent class - that must
* be handled by the caller.
*
* @param node the `ts.ClassDeclaration` to reflect
* @param checker a `ts.TypeChecker` used for reflection
* @returns a `Parameter` instance for each argument of the constructor, or `null` if no constructor
*/
export function reflectConstructorParameters(
node: ts.ClassDeclaration, checker: ts.TypeChecker): Parameter[]|null {
// Firstly, look for a constructor.
// clang-format off
const maybeCtor: ts.ConstructorDeclaration[] = node
.members
.filter(element => ts.isConstructorDeclaration(element)) as ts.ConstructorDeclaration[];
// clang-format on
if (maybeCtor.length !== 1) {
// No constructor.
return null;
}
// Reflect each parameter.
return maybeCtor[0].parameters.map(param => reflectParameter(param, checker));
}
/**
* Reflect a `ts.ParameterDeclaration` and determine its name, a token which refers to the value
* declaration of its type (if possible to statically determine), and its decorators, if any.
*/
function reflectParameter(node: ts.ParameterDeclaration, checker: ts.TypeChecker): Parameter {
// The name of the parameter is easy.
const name = node.name;
const decorators = node.decorators &&
node.decorators.map(decorator => reflectDecorator(decorator, checker))
.filter(decorator => decorator !== null) as Decorator[] ||
[];
// It may or may not be possible to write an expression that refers to the value side of the
// type named for the parameter.
let typeValueExpr: ts.Expression|null = null;
// It's not possible to get a value expression if the parameter doesn't even have a type.
if (node.type !== undefined) {
// It's only valid to convert a type reference to a value reference if the type actually has a
// value declaration associated with it.
const type = checker.getTypeFromTypeNode(node.type);
if (type.symbol !== undefined && type.symbol.valueDeclaration !== undefined) {
// The type points to a valid value declaration. Rewrite the TypeReference into an Expression
// which references the value pointed to by the TypeReference, if possible.
typeValueExpr = typeNodeToValueExpr(node.type);
}
}
return {
name, typeValueExpr, decorators,
};
}
/**
* Reflect a decorator and return a structure describing where it comes from and any arguments.
*
* Only imported decorators are considered, not locally defined decorators.
*/
export function reflectDecorator(decorator: ts.Decorator, checker: ts.TypeChecker): Decorator|null {
// Attempt to resolve the decorator expression into a reference to a concrete Identifier. The
// expression may contain a call to a function which returns the decorator function, in which
// case we want to return the arguments.
let decoratorOfInterest: ts.Expression = decorator.expression;
let args: ts.Expression[] = [];
// Check for call expressions.
if (ts.isCallExpression(decoratorOfInterest)) {
args = Array.from(decoratorOfInterest.arguments);
decoratorOfInterest = decoratorOfInterest.expression;
}
// The final resolved decorator should be a `ts.Identifier` - if it's not, then something is
// wrong and the decorator can't be resolved statically.
if (!ts.isIdentifier(decoratorOfInterest)) {
return null;
}
const importDecl = reflectImportedIdentifier(decoratorOfInterest, checker);
if (importDecl === null) {
return null;
}
return {
...importDecl,
node: decorator, args,
};
}
function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null {
if (ts.isTypeReferenceNode(node)) {
return entityNameToValue(node.typeName);
} else {
return null;
}
}
function entityNameToValue(node: ts.EntityName): ts.Expression|null {
if (ts.isQualifiedName(node)) {
const left = entityNameToValue(node.left);
return left !== null ? ts.createPropertyAccess(left, node.right) : null;
} else if (ts.isIdentifier(node)) {
return ts.getMutableClone(node);
} else {
return null;
}
}
function propertyNameToValue(node: ts.PropertyName): string|null {
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
return node.text;
} else {
return null;
}
}
export function reflectObjectLiteral(node: ts.ObjectLiteralExpression): Map<string, ts.Expression> {
const map = new Map<string, ts.Expression>();
node.properties.forEach(prop => {
if (ts.isPropertyAssignment(prop)) {
const name = propertyNameToValue(prop.name);
if (name === null) {
return;
}
map.set(name, prop.initializer);
} else if (ts.isShorthandPropertyAssignment(prop)) {
map.set(prop.name.text, prop.name);
} else {
return;
}
});
return map;
}
export function reflectImportedIdentifier(
id: ts.Identifier, checker: ts.TypeChecker): {name: string, from: string}|null {
const symbol = checker.getSymbolAtLocation(id);
if (symbol === undefined || symbol.declarations === undefined ||
symbol.declarations.length !== 1) {
return null;
}
// Ignore decorators that are defined locally (not imported).
const decl: ts.Declaration = symbol.declarations[0];
if (!ts.isImportSpecifier(decl)) {
return null;
}
// Walk back from the specifier to find the declaration, which carries the module specifier.
const importDecl = decl.parent !.parent !.parent !;
// The module specifier is guaranteed to be a string literal, so this should always pass.
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
// Not allowed to happen in TypeScript ASTs.
return null;
}
// Read the module specifier.
const from = importDecl.moduleSpecifier.text;
// Compute the name by which the decorator was exported, not imported.
const name = (decl.propertyName !== undefined ? decl.propertyName : decl.name).text;
return {from, name};
}
export interface DecoratedNode<T extends ts.Node> {
element: T;
decorators: Decorator[];
}
export function getDecoratedClassElements(
clazz: ts.ClassDeclaration, checker: ts.TypeChecker): DecoratedNode<ts.ClassElement>[] {
const decoratedElements: DecoratedNode<ts.ClassElement>[] = [];
clazz.members.forEach(element => {
if (element.decorators !== undefined) {
const decorators = element.decorators.map(decorator => reflectDecorator(decorator, checker))
.filter(decorator => decorator != null) as Decorator[];
if (decorators.length > 0) {
decoratedElements.push({element, decorators});
}
}
});
return decoratedElements;
}
export function reflectStaticField(
clazz: ts.ClassDeclaration, field: string): ts.PropertyDeclaration|null {
return clazz.members.find((member: ts.ClassElement): member is ts.PropertyDeclaration => {
// Check if the name matches.
if (member.name === undefined || !ts.isIdentifier(member.name) || member.name.text !== field) {
return false;
}
// Check if the property is static.
if (member.modifiers === undefined ||
!member.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)) {
return false;
}
// Found the field.
return true;
}) ||
null;
}
export function reflectNonStaticField(
clazz: ts.ClassDeclaration, field: string): ts.PropertyDeclaration|null {
return clazz.members.find((member: ts.ClassElement): member is ts.PropertyDeclaration => {
// Check if the name matches.
if (member.name === undefined || !ts.isIdentifier(member.name) || member.name.text !== field) {
return false;
}
// Check if the property is static.
if (member.modifiers !== undefined &&
member.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)) {
return false;
}
// Found the field.
return true;
}) ||
null;
}
export function reflectTypeEntityToDeclaration(
type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string | null} {
let realSymbol = checker.getSymbolAtLocation(type);
if (realSymbol === undefined) {
throw new Error(`Cannot resolve type entity to symbol`);
}
while (realSymbol.flags & ts.SymbolFlags.Alias) {
realSymbol = checker.getAliasedSymbol(realSymbol);
}
let node: ts.Declaration|null = null;
if (realSymbol.valueDeclaration !== undefined) {
node = realSymbol.valueDeclaration;
} else if (realSymbol.declarations !== undefined && realSymbol.declarations.length === 1) {
node = realSymbol.declarations[0];
} else {
throw new Error(`Cannot resolve type entity symbol to declaration`);
}
if (ts.isQualifiedName(type)) {
if (!ts.isIdentifier(type.left)) {
throw new Error(`Cannot handle qualified name with non-identifier lhs`);
}
const symbol = checker.getSymbolAtLocation(type.left);
if (symbol === undefined || symbol.declarations === undefined ||
symbol.declarations.length !== 1) {
throw new Error(`Cannot resolve qualified type entity lhs to symbol`);
}
const decl = symbol.declarations[0];
if (ts.isNamespaceImport(decl)) {
const clause = decl.parent !;
const importDecl = clause.parent !;
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
throw new Error(`Module specifier is not a string`);
}
return {node, from: importDecl.moduleSpecifier.text};
} else {
throw new Error(`Unknown import type?`);
}
} else {
return {node, from: null};
}
}

View File

@ -1,634 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* resolver.ts implements partial computation of expressions, resolving expressions to static
* values where possible and returning a `DynamicValue` signal when not.
*/
import {Expression, ExternalExpr, ExternalReference, WrappedNodeExpr} from '@angular/compiler';
import * as path from 'path';
import * as ts from 'typescript';
const TS_DTS_EXTENSION = /(\.d)?\.ts$/;
/**
* Represents a value which cannot be determined statically.
*
* Use `isDynamicValue` to determine whether a `ResolvedValue` is a `DynamicValue`.
*/
export class DynamicValue {
/**
* This is needed so the "is DynamicValue" assertion of `isDynamicValue` actually has meaning.
*
* Otherwise, "is DynamicValue" is akin to "is {}" which doesn't trigger narrowing.
*/
private _isDynamic = true;
}
/**
* An internal flyweight for `DynamicValue`. Eventually the dynamic value will carry information
* on the location of the node that could not be statically computed.
*/
const DYNAMIC_VALUE: DynamicValue = new DynamicValue();
/**
* Used to test whether a `ResolvedValue` is a `DynamicValue`.
*/
export function isDynamicValue(value: any): value is DynamicValue {
return value === DYNAMIC_VALUE;
}
/**
* A value resulting from static resolution.
*
* This could be a primitive, collection type, reference to a `ts.Node` that declares a
* non-primitive value, or a special `DynamicValue` type which indicates the value was not
* available statically.
*/
export type ResolvedValue = number | boolean | string | null | undefined | Reference |
ResolvedValueArray | ResolvedValueMap | DynamicValue;
/**
* An array of `ResolvedValue`s.
*
* This is a reified type to allow the circular reference of `ResolvedValue` -> `ResolvedValueArray`
* ->
* `ResolvedValue`.
*/
export interface ResolvedValueArray extends Array<ResolvedValue> {}
/**
* A map of strings to `ResolvedValue`s.
*
* This is a reified type to allow the circular reference of `ResolvedValue` -> `ResolvedValueMap` ->
* `ResolvedValue`.
*/ export interface ResolvedValueMap extends Map<string, ResolvedValue> {}
/**
* Tracks the scope of a function body, which includes `ResolvedValue`s for the parameters of that
* body.
*/
type Scope = Map<ts.ParameterDeclaration, ResolvedValue>;
/**
* A reference to a `ts.Node`.
*
* For example, if an expression evaluates to a function or class definition, it will be returned
* as a `Reference` (assuming references are allowed in evaluation).
*/
export abstract class Reference {
constructor(readonly node: ts.Node) {}
/**
* Whether an `Expression` can be generated which references the node.
*/
readonly expressable: boolean;
/**
* Generate an `Expression` representing this type, in the context of the given SourceFile.
*
* This could be a local variable reference, if the symbol is imported, or it could be a new
* import if needed.
*/
abstract toExpression(context: ts.SourceFile): Expression|null;
}
/**
* A reference to a node only, without any ability to get an `Expression` representing that node.
*
* This is used for returning references to things like method declarations, which are not directly
* referenceable.
*/
export class NodeReference extends Reference {
toExpression(context: ts.SourceFile): null { return null; }
}
/**
* A reference to a node which has a `ts.Identifier` and can be resolved to an `Expression`.
*
* Imports generated by `ResolvedReference`s are always relative.
*/
export class ResolvedReference extends Reference {
constructor(node: ts.Node, protected identifier: ts.Identifier) { super(node); }
readonly expressable = true;
toExpression(context: ts.SourceFile): Expression {
if (ts.getOriginalNode(context) === ts.getOriginalNode(this.node).getSourceFile()) {
return new WrappedNodeExpr(this.identifier);
} else {
// Relative import from context -> this.node.getSourceFile().
// TODO(alxhub): investigate the impact of multiple source roots here.
// TODO(alxhub): investigate the need to map such paths via the Host for proper g3 support.
let relative =
path.posix.relative(path.dirname(context.fileName), this.node.getSourceFile().fileName)
.replace(TS_DTS_EXTENSION, '');
// path.relative() does not include the leading './'.
if (!relative.startsWith('.')) {
relative = `./${relative}`;
}
// path.relative() returns the empty string (converted to './' above) if the two paths are the
// same.
if (relative === './') {
// Same file after all.
return new WrappedNodeExpr(this.identifier);
} else {
return new ExternalExpr(new ExternalReference(relative, this.identifier.text));
}
}
}
}
/**
* A reference to a node which has a `ts.Identifer` and an expected absolute module name.
*
* An `AbsoluteReference` can be resolved to an `Expression`, and if that expression is an import
* the module specifier will be an absolute module name, not a relative path.
*/
export class AbsoluteReference extends Reference {
constructor(
node: ts.Node, private identifier: ts.Identifier, readonly moduleName: string,
private symbolName: string) {
super(node);
}
readonly expressable = true;
toExpression(context: ts.SourceFile): Expression {
if (ts.getOriginalNode(context) === ts.getOriginalNode(this.node.getSourceFile())) {
return new WrappedNodeExpr(this.identifier);
} else {
return new ExternalExpr(new ExternalReference(this.moduleName, this.symbolName));
}
}
}
/**
* Statically resolve the given `ts.Expression` into a `ResolvedValue`.
*
* @param node the expression to statically resolve if possible
* @param checker a `ts.TypeChecker` used to understand the expression
* @returns a `ResolvedValue` representing the resolved value
*/
export function staticallyResolve(node: ts.Expression, checker: ts.TypeChecker): ResolvedValue {
return new StaticInterpreter(checker).visit(
node, {absoluteModuleName: null, scope: new Map<ts.ParameterDeclaration, ResolvedValue>()});
}
interface BinaryOperatorDef {
literal: boolean;
op: (a: any, b: any) => ResolvedValue;
}
function literalBinaryOp(op: (a: any, b: any) => any): BinaryOperatorDef {
return {op, literal: true};
}
function referenceBinaryOp(op: (a: any, b: any) => any): BinaryOperatorDef {
return {op, literal: false};
}
const BINARY_OPERATORS = new Map<ts.SyntaxKind, BinaryOperatorDef>([
[ts.SyntaxKind.PlusToken, literalBinaryOp((a, b) => a + b)],
[ts.SyntaxKind.MinusToken, literalBinaryOp((a, b) => a - b)],
[ts.SyntaxKind.AsteriskToken, literalBinaryOp((a, b) => a * b)],
[ts.SyntaxKind.SlashToken, literalBinaryOp((a, b) => a / b)],
[ts.SyntaxKind.PercentToken, literalBinaryOp((a, b) => a % b)],
[ts.SyntaxKind.AmpersandToken, literalBinaryOp((a, b) => a & b)],
[ts.SyntaxKind.BarToken, literalBinaryOp((a, b) => a | b)],
[ts.SyntaxKind.CaretToken, literalBinaryOp((a, b) => a ^ b)],
[ts.SyntaxKind.LessThanToken, literalBinaryOp((a, b) => a < b)],
[ts.SyntaxKind.LessThanEqualsToken, literalBinaryOp((a, b) => a <= b)],
[ts.SyntaxKind.GreaterThanToken, literalBinaryOp((a, b) => a > b)],
[ts.SyntaxKind.GreaterThanEqualsToken, literalBinaryOp((a, b) => a >= b)],
[ts.SyntaxKind.LessThanLessThanToken, literalBinaryOp((a, b) => a << b)],
[ts.SyntaxKind.GreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >> b)],
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >>> b)],
[ts.SyntaxKind.AsteriskAsteriskToken, literalBinaryOp((a, b) => Math.pow(a, b))],
[ts.SyntaxKind.AmpersandAmpersandToken, referenceBinaryOp((a, b) => a && b)],
[ts.SyntaxKind.BarBarToken, referenceBinaryOp((a, b) => a || b)]
]);
const UNARY_OPERATORS = new Map<ts.SyntaxKind, (a: any) => any>([
[ts.SyntaxKind.TildeToken, a => ~a], [ts.SyntaxKind.MinusToken, a => -a],
[ts.SyntaxKind.PlusToken, a => +a], [ts.SyntaxKind.ExclamationToken, a => !a]
]);
interface Context {
absoluteModuleName: string|null;
scope: Scope;
}
class StaticInterpreter {
constructor(private checker: ts.TypeChecker) {}
visit(node: ts.Expression, context: Context): ResolvedValue {
return this.visitExpression(node, context);
}
private visitExpression(node: ts.Expression, context: Context): ResolvedValue {
if (node.kind === ts.SyntaxKind.TrueKeyword) {
return true;
} else if (node.kind === ts.SyntaxKind.FalseKeyword) {
return false;
} else if (ts.isStringLiteral(node)) {
return node.text;
} else if (ts.isNoSubstitutionTemplateLiteral(node)) {
return node.text;
} else if (ts.isNumericLiteral(node)) {
return parseFloat(node.text);
} else if (ts.isObjectLiteralExpression(node)) {
return this.visitObjectLiteralExpression(node, context);
} else if (ts.isIdentifier(node)) {
return this.visitIdentifier(node, context);
} else if (ts.isPropertyAccessExpression(node)) {
return this.visitPropertyAccessExpression(node, context);
} else if (ts.isCallExpression(node)) {
return this.visitCallExpression(node, context);
} else if (ts.isConditionalExpression(node)) {
return this.visitConditionalExpression(node, context);
} else if (ts.isPrefixUnaryExpression(node)) {
return this.visitPrefixUnaryExpression(node, context);
} else if (ts.isBinaryExpression(node)) {
return this.visitBinaryExpression(node, context);
} else if (ts.isArrayLiteralExpression(node)) {
return this.visitArrayLiteralExpression(node, context);
} else if (ts.isParenthesizedExpression(node)) {
return this.visitParenthesizedExpression(node, context);
} else if (ts.isElementAccessExpression(node)) {
return this.visitElementAccessExpression(node, context);
} else if (ts.isAsExpression(node)) {
return this.visitExpression(node.expression, context);
} else if (ts.isNonNullExpression(node)) {
return this.visitExpression(node.expression, context);
} else if (ts.isClassDeclaration(node)) {
return this.visitDeclaration(node, context);
} else {
return DYNAMIC_VALUE;
}
}
private visitArrayLiteralExpression(node: ts.ArrayLiteralExpression, context: Context):
ResolvedValue {
const array: ResolvedValueArray = [];
for (let i = 0; i < node.elements.length; i++) {
const element = node.elements[i];
if (ts.isSpreadElement(element)) {
const spread = this.visitExpression(element.expression, context);
if (isDynamicValue(spread)) {
return DYNAMIC_VALUE;
}
if (!Array.isArray(spread)) {
throw new Error(`Unexpected value in spread expression: ${spread}`);
}
array.push(...spread);
} else {
const result = this.visitExpression(element, context);
if (isDynamicValue(result)) {
return DYNAMIC_VALUE;
}
array.push(result);
}
}
return array;
}
private visitObjectLiteralExpression(node: ts.ObjectLiteralExpression, context: Context):
ResolvedValue {
const map: ResolvedValueMap = new Map<string, ResolvedValue>();
for (let i = 0; i < node.properties.length; i++) {
const property = node.properties[i];
if (ts.isPropertyAssignment(property)) {
const name = this.stringNameFromPropertyName(property.name, context);
// Check whether the name can be determined statically.
if (name === undefined) {
return DYNAMIC_VALUE;
}
map.set(name, this.visitExpression(property.initializer, context));
} else if (ts.isShorthandPropertyAssignment(property)) {
const symbol = this.checker.getShorthandAssignmentValueSymbol(property);
if (symbol === undefined || symbol.valueDeclaration === undefined) {
return DYNAMIC_VALUE;
}
map.set(property.name.text, this.visitDeclaration(symbol.valueDeclaration, context));
} else if (ts.isSpreadAssignment(property)) {
const spread = this.visitExpression(property.expression, context);
if (isDynamicValue(spread)) {
return DYNAMIC_VALUE;
}
if (!(spread instanceof Map)) {
throw new Error(`Unexpected value in spread assignment: ${spread}`);
}
spread.forEach((value, key) => map.set(key, value));
} else {
return DYNAMIC_VALUE;
}
}
return map;
}
private visitIdentifier(node: ts.Identifier, context: Context): ResolvedValue {
let symbol: ts.Symbol|undefined = this.checker.getSymbolAtLocation(node);
if (symbol === undefined) {
return DYNAMIC_VALUE;
}
return this.visitSymbol(symbol, context);
}
private visitSymbol(symbol: ts.Symbol, context: Context): ResolvedValue {
let absoluteModuleName = context.absoluteModuleName;
if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
for (let i = 0; i < symbol.declarations.length; i++) {
const decl = symbol.declarations[i];
if (ts.isImportSpecifier(decl) && decl.parent !== undefined &&
decl.parent.parent !== undefined && decl.parent.parent.parent !== undefined) {
const importDecl = decl.parent.parent.parent;
if (ts.isStringLiteral(importDecl.moduleSpecifier)) {
const moduleSpecifier = importDecl.moduleSpecifier.text;
if (!moduleSpecifier.startsWith('.')) {
absoluteModuleName = moduleSpecifier;
}
}
}
}
}
const newContext = {...context, absoluteModuleName};
while (symbol.flags & ts.SymbolFlags.Alias) {
symbol = this.checker.getAliasedSymbol(symbol);
}
if (symbol.declarations === undefined) {
return DYNAMIC_VALUE;
}
if (symbol.valueDeclaration !== undefined) {
return this.visitDeclaration(symbol.valueDeclaration, newContext);
}
return symbol.declarations.reduce<ResolvedValue>((prev, decl) => {
if (!(isDynamicValue(prev) || prev instanceof Reference)) {
return prev;
}
return this.visitDeclaration(decl, newContext);
}, DYNAMIC_VALUE);
}
private visitDeclaration(node: ts.Declaration, context: Context): ResolvedValue {
if (ts.isVariableDeclaration(node)) {
if (!node.initializer) {
return undefined;
}
return this.visitExpression(node.initializer, context);
} else if (ts.isParameter(node) && context.scope.has(node)) {
return context.scope.get(node) !;
} else if (ts.isExportAssignment(node)) {
return this.visitExpression(node.expression, context);
} else if (ts.isSourceFile(node)) {
return this.visitSourceFile(node, context);
} else {
return this.getReference(node, context);
}
}
private visitElementAccessExpression(node: ts.ElementAccessExpression, context: Context):
ResolvedValue {
const lhs = this.visitExpression(node.expression, context);
if (node.argumentExpression === undefined) {
throw new Error(`Expected argument in ElementAccessExpression`);
}
if (isDynamicValue(lhs)) {
return DYNAMIC_VALUE;
}
const rhs = this.visitExpression(node.argumentExpression, context);
if (isDynamicValue(rhs)) {
return DYNAMIC_VALUE;
}
if (typeof rhs !== 'string' && typeof rhs !== 'number') {
throw new Error(
`ElementAccessExpression index should be string or number, got ${typeof rhs}: ${rhs}`);
}
return this.accessHelper(lhs, rhs, context);
}
private visitPropertyAccessExpression(node: ts.PropertyAccessExpression, context: Context):
ResolvedValue {
const lhs = this.visitExpression(node.expression, context);
const rhs = node.name.text;
// TODO: handle reference to class declaration.
if (isDynamicValue(lhs)) {
return DYNAMIC_VALUE;
}
return this.accessHelper(lhs, rhs, context);
}
private visitSourceFile(node: ts.SourceFile, context: Context): ResolvedValue {
const map = new Map<string, ResolvedValue>();
const symbol = this.checker.getSymbolAtLocation(node);
if (symbol === undefined) {
return DYNAMIC_VALUE;
}
const exports = this.checker.getExportsOfModule(symbol);
exports.forEach(symbol => map.set(symbol.name, this.visitSymbol(symbol, context)));
return map;
}
private accessHelper(lhs: ResolvedValue, rhs: string|number, context: Context): ResolvedValue {
const strIndex = `${rhs}`;
if (lhs instanceof Map) {
if (lhs.has(strIndex)) {
return lhs.get(strIndex) !;
} else {
throw new Error(`Invalid map access: [${Array.from(lhs.keys())}] dot ${rhs}`);
}
} else if (Array.isArray(lhs)) {
if (rhs === 'length') {
return rhs.length;
}
if (typeof rhs !== 'number' || !Number.isInteger(rhs)) {
return DYNAMIC_VALUE;
}
if (rhs < 0 || rhs >= lhs.length) {
throw new Error(`Index out of bounds: ${rhs} vs ${lhs.length}`);
}
return lhs[rhs];
} else if (lhs instanceof Reference) {
const ref = lhs.node;
if (ts.isClassDeclaration(ref)) {
let value: ResolvedValue = undefined;
const member =
ref.members.filter(member => isStatic(member))
.find(
member => member.name !== undefined &&
this.stringNameFromPropertyName(member.name, context) === strIndex);
if (member !== undefined) {
if (ts.isPropertyDeclaration(member) && member.initializer !== undefined) {
value = this.visitExpression(member.initializer, context);
} else if (ts.isMethodDeclaration(member)) {
value = new NodeReference(member);
}
}
return value;
}
}
throw new Error(`Invalid dot property access: ${lhs} dot ${rhs}`);
}
private visitCallExpression(node: ts.CallExpression, context: Context): ResolvedValue {
const lhs = this.visitExpression(node.expression, context);
if (!(lhs instanceof Reference)) {
throw new Error(`attempting to call something that is not a function: ${lhs}`);
} else if (!isFunctionOrMethodDeclaration(lhs.node) || !lhs.node.body) {
throw new Error(
`calling something that is not a function declaration? ${ts.SyntaxKind[lhs.node.kind]}`);
}
const fn = lhs.node;
const body = fn.body as ts.Block;
if (body.statements.length !== 1 || !ts.isReturnStatement(body.statements[0])) {
throw new Error('Function body must have a single return statement only.');
}
const ret = body.statements[0] as ts.ReturnStatement;
const newScope: Scope = new Map<ts.ParameterDeclaration, ResolvedValue>();
fn.parameters.forEach((param, index) => {
let value: ResolvedValue = undefined;
if (index < node.arguments.length) {
const arg = node.arguments[index];
value = this.visitExpression(arg, context);
}
if (value === undefined && param.initializer !== undefined) {
value = this.visitExpression(param.initializer, context);
}
newScope.set(param, value);
});
return ret.expression !== undefined ?
this.visitExpression(ret.expression, {...context, scope: newScope}) :
undefined;
}
private visitConditionalExpression(node: ts.ConditionalExpression, context: Context):
ResolvedValue {
const condition = this.visitExpression(node.condition, context);
if (isDynamicValue(condition)) {
return condition;
}
if (condition) {
return this.visitExpression(node.whenTrue, context);
} else {
return this.visitExpression(node.whenFalse, context);
}
}
private visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression, context: Context):
ResolvedValue {
const operatorKind = node.operator;
if (!UNARY_OPERATORS.has(operatorKind)) {
throw new Error(`Unsupported prefix unary operator: ${ts.SyntaxKind[operatorKind]}`);
}
const op = UNARY_OPERATORS.get(operatorKind) !;
const value = this.visitExpression(node.operand, context);
return isDynamicValue(value) ? DYNAMIC_VALUE : op(value);
}
private visitBinaryExpression(node: ts.BinaryExpression, context: Context): ResolvedValue {
const tokenKind = node.operatorToken.kind;
if (!BINARY_OPERATORS.has(tokenKind)) {
throw new Error(`Unsupported binary operator: ${ts.SyntaxKind[tokenKind]}`);
}
const opRecord = BINARY_OPERATORS.get(tokenKind) !;
let lhs: ResolvedValue, rhs: ResolvedValue;
if (opRecord.literal) {
lhs = literal(this.visitExpression(node.left, context));
rhs = literal(this.visitExpression(node.right, context));
} else {
lhs = this.visitExpression(node.left, context);
rhs = this.visitExpression(node.right, context);
}
return isDynamicValue(lhs) || isDynamicValue(rhs) ? DYNAMIC_VALUE : opRecord.op(lhs, rhs);
}
private visitParenthesizedExpression(node: ts.ParenthesizedExpression, context: Context):
ResolvedValue {
return this.visitExpression(node.expression, context);
}
private stringNameFromPropertyName(node: ts.PropertyName, context: Context): string|undefined {
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
return node.text;
} else { // ts.ComputedPropertyName
const literal = this.visitExpression(node.expression, context);
return typeof literal === 'string' ? literal : undefined;
}
}
private getReference(node: ts.Declaration, context: Context): Reference {
const id = identifierOfDeclaration(node);
if (id === undefined) {
throw new Error(`Don't know how to refer to ${ts.SyntaxKind[node.kind]}`);
}
if (context.absoluteModuleName !== null) {
// TODO(alxhub): investigate whether this can get symbol names wrong in the event of
// re-exports under different names.
return new AbsoluteReference(node, id, context.absoluteModuleName, id.text);
} else {
return new ResolvedReference(node, id);
}
}
}
function isStatic(element: ts.ClassElement): boolean {
return element.modifiers !== undefined &&
element.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
}
function isFunctionOrMethodDeclaration(node: ts.Node): node is ts.FunctionDeclaration|
ts.MethodDeclaration {
return ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node);
}
function literal(value: ResolvedValue): any {
if (value === null || value === undefined || typeof value === 'string' ||
typeof value === 'number' || typeof value === 'boolean') {
return value;
}
if (isDynamicValue(value)) {
return DYNAMIC_VALUE;
}
throw new Error(`Value ${value} is not literal and cannot be used in this context.`);
}
function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined {
if (ts.isClassDeclaration(decl)) {
return decl.name;
} else if (ts.isFunctionDeclaration(decl)) {
return decl.name;
} else if (ts.isVariableDeclaration(decl) && ts.isIdentifier(decl.name)) {
return decl.name;
} else if (ts.isShorthandPropertyAssignment(decl)) {
return decl.name;
} else {
return undefined;
}
}

View File

@ -1,27 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
ts_library(
name = "test_lib",
testonly = 1,
srcs = glob([
"**/*.ts",
]),
deps = [
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/testing",
],
)
jasmine_node_test(
name = "test",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
deps = [
":test_lib",
"//tools/testing:node_no_angular",
],
)

View File

@ -1,144 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {Parameter, reflectConstructorParameters} from '../src/reflector';
describe('reflector', () => {
describe('ctor params', () => {
it('should reflect a single argument', () => {
const {program} = makeProgram([{
name: 'entry.ts',
contents: `
class Bar {}
class Foo {
constructor(bar: Bar) {}
}
`
}]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const checker = program.getTypeChecker();
const args = reflectConstructorParameters(clazz, checker) !;
expect(args.length).toBe(1);
expectArgument(args[0], 'bar', 'Bar');
});
it('should reflect a decorated argument', () => {
const {program} = makeProgram([
{
name: 'dec.ts',
contents: `
export function dec(target: any, key: string, index: number) {
}
`
},
{
name: 'entry.ts',
contents: `
import {dec} from './dec';
class Bar {}
class Foo {
constructor(@dec bar: Bar) {}
}
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const checker = program.getTypeChecker();
const args = reflectConstructorParameters(clazz, checker) !;
expect(args.length).toBe(1);
expectArgument(args[0], 'bar', 'Bar', 'dec', './dec');
});
it('should reflect a decorated argument with a call', () => {
const {program} = makeProgram([
{
name: 'dec.ts',
contents: `
export function dec(target: any, key: string, index: number) {
}
`
},
{
name: 'entry.ts',
contents: `
import {dec} from './dec';
class Bar {}
class Foo {
constructor(@dec bar: Bar) {}
}
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const checker = program.getTypeChecker();
const args = reflectConstructorParameters(clazz, checker) !;
expect(args.length).toBe(1);
expectArgument(args[0], 'bar', 'Bar', 'dec', './dec');
});
it('should reflect a decorated argument with an indirection', () => {
const {program} = makeProgram([
{
name: 'bar.ts',
contents: `
export class Bar {}
`
},
{
name: 'entry.ts',
contents: `
import {Bar} from './bar';
import * as star from './bar';
class Foo {
constructor(bar: Bar, otherBar: star.Bar) {}
}
`
}
]);
const clazz = getDeclaration(program, 'entry.ts', 'Foo', ts.isClassDeclaration);
const checker = program.getTypeChecker();
const args = reflectConstructorParameters(clazz, checker) !;
expect(args.length).toBe(2);
expectArgument(args[0], 'bar', 'Bar');
expectArgument(args[1], 'otherBar', 'star.Bar');
});
});
});
function expectArgument(
arg: Parameter, name: string, type?: string, decorator?: string, decoratorFrom?: string): void {
expect(argExpressionToString(arg.name)).toEqual(name);
if (type === undefined) {
expect(arg.typeValueExpr).toBeNull();
} else {
expect(arg.typeValueExpr).not.toBeNull();
expect(argExpressionToString(arg.typeValueExpr !)).toEqual(type);
}
if (decorator !== undefined) {
expect(arg.decorators.length).toBeGreaterThan(0);
expect(arg.decorators.some(dec => dec.name === decorator && dec.from === decoratorFrom))
.toBe(true);
}
}
function argExpressionToString(name: ts.Node): string {
if (ts.isIdentifier(name)) {
return name.text;
} else if (ts.isPropertyAccessExpression(name)) {
return `${argExpressionToString(name.expression)}.${name.name.text}`;
} else {
throw new Error(`Unexpected node in arg expression: ${ts.SyntaxKind[name.kind]}.`);
}
}

View File

@ -1,249 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ExternalExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {Reference, ResolvedValue, staticallyResolve} from '../src/resolver';
function makeSimpleProgram(contents: string): ts.Program {
return makeProgram([{name: 'entry.ts', contents}]).program;
}
function makeExpression(
code: string, expr: string): {expression: ts.Expression, checker: ts.TypeChecker} {
const {program} =
makeProgram([{name: 'entry.ts', contents: `${code}; const target$ = ${expr};`}]);
const checker = program.getTypeChecker();
const decl = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
return {
expression: decl.initializer !,
checker,
};
}
function evaluate<T extends ResolvedValue>(code: string, expr: string): T {
const {expression, checker} = makeExpression(code, expr);
return staticallyResolve(expression, checker) as T;
}
describe('ngtsc metadata', () => {
it('reads a file correctly', () => {
const {program} = makeProgram([
{
name: 'entry.ts',
contents: `
import {Y} from './other';
const A = Y;
export const X = A;
`
},
{
name: 'other.ts',
contents: `
export const Y = 'test';
`
}
]);
const decl = getDeclaration(program, 'entry.ts', 'X', ts.isVariableDeclaration);
const value = staticallyResolve(decl.initializer !, program.getTypeChecker());
expect(value).toEqual('test');
});
it('map access works',
() => { expect(evaluate('const obj = {a: "test"};', 'obj.a')).toEqual('test'); });
it('function calls work', () => {
expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test');
});
it('conditionals work', () => {
expect(evaluate(`const x = false; const y = x ? 'true' : 'false';`, 'y')).toEqual('false');
});
it('addition works', () => { expect(evaluate(`const x = 1 + 2;`, 'x')).toEqual(3); });
it('static property on class works',
() => { expect(evaluate(`class Foo { static bar = 'test'; }`, 'Foo.bar')).toEqual('test'); });
it('static property call works', () => {
expect(evaluate(`class Foo { static bar(test) { return test; } }`, 'Foo.bar("test")'))
.toEqual('test');
});
it('indirected static property call works', () => {
expect(
evaluate(
`class Foo { static bar(test) { return test; } }; const fn = Foo.bar;`, 'fn("test")'))
.toEqual('test');
});
it('array works', () => {
expect(evaluate(`const x = 'test'; const y = [1, x, 2];`, 'y')).toEqual([1, 'test', 2]);
});
it('array spread works', () => {
expect(evaluate(`const a = [1, 2]; const b = [4, 5]; const c = [...a, 3, ...b];`, 'c'))
.toEqual([1, 2, 3, 4, 5]);
});
it('&& operations work', () => {
expect(evaluate(`const a = 'hello', b = 'world';`, 'a && b')).toEqual('world');
expect(evaluate(`const a = false, b = 'world';`, 'a && b')).toEqual(false);
expect(evaluate(`const a = 'hello', b = 0;`, 'a && b')).toEqual(0);
});
it('|| operations work', () => {
expect(evaluate(`const a = 'hello', b = 'world';`, 'a || b')).toEqual('hello');
expect(evaluate(`const a = false, b = 'world';`, 'a || b')).toEqual('world');
expect(evaluate(`const a = 'hello', b = 0;`, 'a || b')).toEqual('hello');
});
it('parentheticals work',
() => { expect(evaluate(`const a = 3, b = 4;`, 'a * (a + b)')).toEqual(21); });
it('array access works',
() => { expect(evaluate(`const a = [1, 2, 3];`, 'a[1] + a[0]')).toEqual(3); });
it('negation works', () => {
expect(evaluate(`const x = 3;`, '!x')).toEqual(false);
expect(evaluate(`const x = 3;`, '!!x')).toEqual(true);
});
it('imports work', () => {
const {program} = makeProgram([
{name: 'second.ts', contents: 'export function foo(bar) { return bar; }'},
{
name: 'entry.ts',
contents: `
import {foo} from './second';
const target$ = foo;
`
},
]);
const checker = program.getTypeChecker();
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const expr = result.initializer !;
const resolved = staticallyResolve(expr, checker);
if (!(resolved instanceof Reference)) {
return fail('Expected expression to resolve to a reference');
}
expect(ts.isFunctionDeclaration(resolved.node)).toBe(true);
expect(resolved.expressable).toBe(true);
const reference = resolved.toExpression(program.getSourceFile('entry.ts') !);
if (!(reference instanceof ExternalExpr)) {
return fail('Expected expression reference to be an external (import) expression');
}
expect(reference.value.moduleName).toBe('./second');
expect(reference.value.name).toBe('foo');
});
it('absolute imports work', () => {
const {program} = makeProgram([
{name: 'node_modules/some_library/index.d.ts', contents: 'export declare function foo(bar);'},
{
name: 'entry.ts',
contents: `
import {foo} from 'some_library';
const target$ = foo;
`
},
]);
const checker = program.getTypeChecker();
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const expr = result.initializer !;
const resolved = staticallyResolve(expr, checker);
if (!(resolved instanceof Reference)) {
return fail('Expected expression to resolve to a reference');
}
expect(ts.isFunctionDeclaration(resolved.node)).toBe(true);
expect(resolved.expressable).toBe(true);
const reference = resolved.toExpression(program.getSourceFile('entry.ts') !);
if (!(reference instanceof ExternalExpr)) {
return fail('Expected expression reference to be an external (import) expression');
}
expect(reference.value.moduleName).toBe('some_library');
expect(reference.value.name).toBe('foo');
});
it('reads values from default exports', () => {
const {program} = makeProgram([
{name: 'second.ts', contents: 'export default {property: "test"}'},
{
name: 'entry.ts',
contents: `
import mod from './second';
const target$ = mod.property;
`
},
]);
const checker = program.getTypeChecker();
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const expr = result.initializer !;
expect(staticallyResolve(expr, checker)).toEqual('test');
});
it('reads values from named exports', () => {
const {program} = makeProgram([
{name: 'second.ts', contents: 'export const a = {property: "test"};'},
{
name: 'entry.ts',
contents: `
import * as mod from './second';
const target$ = mod.a.property;
`
},
]);
const checker = program.getTypeChecker();
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const expr = result.initializer !;
expect(staticallyResolve(expr, checker)).toEqual('test');
});
it('chain of re-exports works', () => {
const {program} = makeProgram([
{name: 'const.ts', contents: 'export const value = {property: "test"};'},
{name: 'def.ts', contents: `import {value} from './const'; export default value;`},
{name: 'indirect-reexport.ts', contents: `import value from './def'; export {value};`},
{name: 'direct-reexport.ts', contents: `export {value} from './indirect-reexport';`},
{
name: 'entry.ts',
contents: `import * as mod from './direct-reexport'; const target$ = mod.value.property;`
},
]);
const checker = program.getTypeChecker();
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const expr = result.initializer !;
expect(staticallyResolve(expr, checker)).toEqual('test');
});
it('map spread works', () => {
const map: Map<string, number> = evaluate<Map<string, number>>(
`const a = {a: 1}; const b = {b: 2, c: 1}; const c = {...a, ...b, c: 3};`, 'c');
const obj: {[key: string]: number} = {};
map.forEach((value, key) => obj[key] = value);
expect(obj).toEqual({
a: 1,
b: 2,
c: 3,
});
});
it('indirected-via-object function call works', () => {
expect(evaluate(
`
function fn(res) { return res; }
const obj = {fn};
`,
'obj.fn("test")'))
.toEqual('test');
});
});

View File

@ -1,152 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {GeneratedFile} from '@angular/compiler';
import * as path from 'path';
import * as ts from 'typescript';
import * as api from '../transformers/api';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, SelectorScopeRegistry} from './annotations';
import {CompilerHost} from './compiler_host';
import {IvyCompilation, ivyTransformFactory} from './transform';
export class NgtscProgram implements api.Program {
private tsProgram: ts.Program;
constructor(
rootNames: ReadonlyArray<string>, private options: api.CompilerOptions,
private host: api.CompilerHost, oldProgram?: api.Program) {
this.tsProgram =
ts.createProgram(rootNames, options, host, oldProgram && oldProgram.getTsProgram());
}
getTsProgram(): ts.Program { return this.tsProgram; }
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getOptionsDiagnostics(cancellationToken);
}
getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
getTsSyntacticDiagnostics(
sourceFile?: ts.SourceFile|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken);
}
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
getTsSemanticDiagnostics(
sourceFile?: ts.SourceFile|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
}
getNgSemanticDiagnostics(
fileName?: string|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
loadNgStructureAsync(): Promise<void> { return Promise.resolve(); }
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
throw new Error('Method not implemented.');
}
getLibrarySummaries(): Map<string, api.LibrarySummary> {
throw new Error('Method not implemented.');
}
getEmittedGeneratedFiles(): Map<string, GeneratedFile> {
throw new Error('Method not implemented.');
}
getEmittedSourceFiles(): Map<string, ts.SourceFile> {
throw new Error('Method not implemented.');
}
emit(opts?: {
emitFlags?: api.EmitFlags,
cancellationToken?: ts.CancellationToken,
customTransformers?: api.CustomTransformers,
emitCallback?: api.TsEmitCallback,
mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback
}): ts.EmitResult {
const emitCallback = opts && opts.emitCallback || defaultEmitCallback;
const mergeEmitResultsCallback = opts && opts.mergeEmitResultsCallback || mergeEmitResults;
const checker = this.tsProgram.getTypeChecker();
const scopeRegistry = new SelectorScopeRegistry(checker);
// Set up the IvyCompilation, which manages state for the Ivy transformer.
const handlers = [
new ComponentDecoratorHandler(checker, scopeRegistry),
new DirectiveDecoratorHandler(checker, scopeRegistry),
new InjectableDecoratorHandler(checker),
new NgModuleDecoratorHandler(checker, scopeRegistry),
];
const compilation = new IvyCompilation(handlers, checker);
// Analyze every source file in the program.
this.tsProgram.getSourceFiles()
.filter(file => !file.fileName.endsWith('.d.ts'))
.forEach(file => compilation.analyze(file));
// Since there is no .d.ts transformation API, .d.ts files are transformed during write.
const writeFile: ts.WriteFileCallback =
(fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void) | undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>) => {
if (fileName.endsWith('.d.ts')) {
data = sourceFiles.reduce(
(data, sf) => compilation.transformedDtsFor(sf.fileName, data), data);
}
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
};
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
const emitResult = emitCallback({
program: this.tsProgram,
host: this.host,
options: this.options,
emitOnlyDtsFiles: false, writeFile,
customTransformers: {
before: [ivyTransformFactory(compilation)],
},
});
return emitResult;
}
}
const defaultEmitCallback: api.TsEmitCallback =
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
customTransformers}) =>
program.emit(
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
const diagnostics: ts.Diagnostic[] = [];
let emitSkipped = false;
const emittedFiles: string[] = [];
for (const er of emitResults) {
diagnostics.push(...er.diagnostics);
emitSkipped = emitSkipped || er.emitSkipped;
emittedFiles.push(...(er.emittedFiles || []));
}
return {diagnostics, emitSkipped, emittedFiles};
}

View File

@ -1,15 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
ts_library(
name = "testing",
testonly = 1,
srcs = glob([
"**/*.ts",
]),
deps = [
"//packages:types",
],
)

View File

@ -1,133 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as path from 'path';
import * as ts from 'typescript';
export function makeProgram(files: {name: string, contents: string}[]):
{program: ts.Program, host: ts.CompilerHost} {
const host = new InMemoryHost();
files.forEach(file => host.writeFile(file.name, file.contents));
const rootNames = files.map(file => host.getCanonicalFileName(file.name));
const program = ts.createProgram(
rootNames,
{noLib: true, experimentalDecorators: true, moduleResolution: ts.ModuleResolutionKind.NodeJs},
host);
const diags = [...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics()];
if (diags.length > 0) {
throw new Error(
`Typescript diagnostics failed! ${diags.map(diag => diag.messageText).join(', ')}`);
}
return {program, host};
}
export class InMemoryHost implements ts.CompilerHost {
private fileSystem = new Map<string, string>();
getSourceFile(
fileName: string, languageVersion: ts.ScriptTarget,
onError?: ((message: string) => void)|undefined,
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
const contents = this.fileSystem.get(this.getCanonicalFileName(fileName));
if (contents === undefined) {
onError && onError(`File does not exist: ${this.getCanonicalFileName(fileName)})`);
return undefined;
}
return ts.createSourceFile(fileName, contents, languageVersion, undefined, ts.ScriptKind.TS);
}
getDefaultLibFileName(options: ts.CompilerOptions): string { return '/lib.d.ts'; }
writeFile(
fileName: string, data: string, writeByteOrderMark?: boolean,
onError?: ((message: string) => void)|undefined,
sourceFiles?: ReadonlyArray<ts.SourceFile>): void {
this.fileSystem.set(this.getCanonicalFileName(fileName), data);
}
getCurrentDirectory(): string { return '/'; }
getDirectories(dir: string): string[] {
const fullDir = this.getCanonicalFileName(dir) + '/';
const dirSet = new Set(Array
// Look at all paths known to the host.
.from(this.fileSystem.keys())
// Filter out those that aren't under the requested directory.
.filter(candidate => candidate.startsWith(fullDir))
// Relativize the rest by the requested directory.
.map(candidate => candidate.substr(fullDir.length))
// What's left are dir/.../file.txt entries, and file.txt entries.
// Get the dirname, which
// yields '.' for the latter and dir/... for the former.
.map(candidate => path.dirname(candidate))
// Filter out the '.' entries, which were files.
.filter(candidate => candidate !== '.')
// Finally, split on / and grab the first entry.
.map(candidate => candidate.split('/', 1)[0]));
// Get the resulting values out of the Set.
return Array.from(dirSet);
}
getCanonicalFileName(fileName: string): string {
return path.posix.normalize(`${this.getCurrentDirectory()}/${fileName}`);
}
useCaseSensitiveFileNames(): boolean { return true; }
getNewLine(): string { return '\n'; }
fileExists(fileName: string): boolean { return this.fileSystem.has(fileName); }
readFile(fileName: string): string|undefined { return this.fileSystem.get(fileName); }
}
function bindingNameEquals(node: ts.BindingName, name: string): boolean {
if (ts.isIdentifier(node)) {
return node.text === name;
}
return false;
}
export function getDeclaration<T extends ts.Declaration>(
program: ts.Program, fileName: string, name: string, assert: (value: any) => value is T): T {
const sf = program.getSourceFile(fileName);
if (!sf) {
throw new Error(`No such file: ${fileName}`);
}
let chosenDecl: ts.Declaration|null = null;
sf.statements.forEach(stmt => {
if (chosenDecl !== null) {
return;
} else if (ts.isVariableStatement(stmt)) {
stmt.declarationList.declarations.forEach(decl => {
if (bindingNameEquals(decl.name, name)) {
chosenDecl = decl;
}
});
} else if (ts.isClassDeclaration(stmt) || ts.isFunctionDeclaration(stmt)) {
if (stmt.name !== undefined && stmt.name.text === name) {
chosenDecl = stmt;
}
}
});
chosenDecl = chosenDecl as ts.Declaration | null;
if (chosenDecl === null) {
throw new Error(`No such symbol: ${name} in ${fileName}`);
}
if (!assert(chosenDecl)) {
throw new Error(`Symbol ${name} from ${fileName} is a ${ts.SyntaxKind[chosenDecl.kind]}`);
}
return chosenDecl;
}

View File

@ -1,17 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "transform",
srcs = glob([
"index.ts",
"src/**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/transform",
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/util",
],
)

View File

@ -1,11 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './src/api';
export {IvyCompilation} from './src/compilation';
export {ivyTransformFactory} from './src/transform';

View File

@ -1,62 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, Statement, Type} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator} from '../../metadata';
/**
* Provides the interface between a decorator compiler from @angular/compiler and the Typescript
* compiler/transform.
*
* The decorator compilers in @angular/compiler do not depend on Typescript. The handler is
* responsible for extracting the information required to perform compilation from the decorators
* and Typescript source, invoking the decorator compiler, and returning the result.
*/
export interface DecoratorHandler<A> {
/**
* Scan a set of reflected decorators and determine if this handler is responsible for compilation
* of one of them.
*/
detect(decorator: Decorator[]): Decorator|undefined;
/**
* Perform analysis on the decorator/class combination, producing instructions for compilation
* if successful, or an array of diagnostic messages if the analysis fails or the decorator
* isn't valid.
*/
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<A>;
/**
* Generate a description of the field which should be added to the class, including any
* initialization code to be generated.
*/
compile(node: ts.ClassDeclaration, analysis: A): CompileResult;
}
/**
* The output of an analysis operation, consisting of possibly an arbitrary analysis object (used as
* the input to code generation) and potentially diagnostics if there were errors uncovered during
* analysis.
*/
export interface AnalysisOutput<A> {
analysis?: A;
diagnostics?: ts.Diagnostic[];
}
/**
* A description of the static field to add to a class, including an initialization expression
* and a type for the .d.ts file.
*/
export interface CompileResult {
field: string;
initializer: Expression;
statements: Statement[];
type: Type;
}

View File

@ -1,158 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, Type} from '@angular/compiler';
import * as ts from 'typescript';
import {Decorator, reflectDecorator} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from './api';
import {DtsFileTransformer} from './declaration';
import {ImportManager, translateType} from './translator';
/**
* Record of an adapter which decided to emit a static field, and the analysis it performed to
* prepare for that operation.
*/
interface EmitFieldOperation<T> {
adapter: DecoratorHandler<T>;
analysis: AnalysisOutput<T>;
decorator: ts.Decorator;
}
/**
* Manages a compilation of Ivy decorators into static fields across an entire ts.Program.
*
* The compilation is stateful - source files are analyzed and records of the operations that need
* to be performed during the transform/emit process are maintained internally.
*/
export class IvyCompilation {
/**
* Tracks classes which have been analyzed and found to have an Ivy decorator, and the
* information recorded about them for later compilation.
*/
private analysis = new Map<ts.ClassDeclaration, EmitFieldOperation<any>>();
/**
* Tracks the `DtsFileTransformer`s for each TS file that needs .d.ts transformations.
*/
private dtsMap = new Map<string, DtsFileTransformer>();
constructor(private handlers: DecoratorHandler<any>[], private checker: ts.TypeChecker) {}
/**
* Analyze a source file and produce diagnostics for it (if any).
*/
analyze(sf: ts.SourceFile): ts.Diagnostic[] {
const diagnostics: ts.Diagnostic[] = [];
const visit = (node: ts.Node) => {
// Process nodes recursively, and look for class declarations with decorators.
if (ts.isClassDeclaration(node) && node.decorators !== undefined) {
// The first step is to reflect the decorators, which will identify decorators
// that are imported from another module.
const decorators =
node.decorators.map(decorator => reflectDecorator(decorator, this.checker))
.filter(decorator => decorator !== null) as Decorator[];
// Look through the DecoratorHandlers to see if any are relevant.
this.handlers.forEach(adapter => {
// An adapter is relevant if it matches one of the decorators on the class.
const decorator = adapter.detect(decorators);
if (decorator === undefined) {
return;
}
// Check for multiple decorators on the same node. Technically speaking this
// could be supported, but right now it's an error.
if (this.analysis.has(node)) {
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
}
// Run analysis on the decorator. This will produce either diagnostics, an
// analysis result, or both.
const analysis = adapter.analyze(node, decorator);
if (analysis.diagnostics !== undefined) {
diagnostics.push(...analysis.diagnostics);
}
if (analysis.analysis !== undefined) {
this.analysis.set(node, {
adapter,
analysis: analysis.analysis,
decorator: decorator.node,
});
}
});
}
ts.forEachChild(node, visit);
};
visit(sf);
return diagnostics;
}
/**
* Perform a compilation operation on the given class declaration and return instructions to an
* AST transformer if any are available.
*/
compileIvyFieldFor(node: ts.ClassDeclaration): CompileResult|undefined {
// Look to see whether the original node was analyzed. If not, there's nothing to do.
const original = ts.getOriginalNode(node) as ts.ClassDeclaration;
if (!this.analysis.has(original)) {
return undefined;
}
const op = this.analysis.get(original) !;
// Run the actual compilation, which generates an Expression for the Ivy field.
const res = op.adapter.compile(node, op.analysis);
// Look up the .d.ts transformer for the input file and record that a field was generated,
// which will allow the .d.ts to be transformed later.
const fileName = node.getSourceFile().fileName;
const dtsTransformer = this.getDtsTransformer(fileName);
dtsTransformer.recordStaticField(node.name !.text, res);
// Return the instruction to the transformer so the field will be added.
return res;
}
/**
* Lookup the `ts.Decorator` which triggered transformation of a particular class declaration.
*/
ivyDecoratorFor(node: ts.ClassDeclaration): ts.Decorator|undefined {
const original = ts.getOriginalNode(node) as ts.ClassDeclaration;
if (!this.analysis.has(original)) {
return undefined;
}
return this.analysis.get(original) !.decorator;
}
/**
* Process a .d.ts source string and return a transformed version that incorporates the changes
* made to the source file.
*/
transformedDtsFor(tsFileName: string, dtsOriginalSource: string): string {
// No need to transform if no changes have been requested to the input file.
if (!this.dtsMap.has(tsFileName)) {
return dtsOriginalSource;
}
// Return the transformed .d.ts source.
return this.dtsMap.get(tsFileName) !.transform(dtsOriginalSource);
}
private getDtsTransformer(tsFileName: string): DtsFileTransformer {
if (!this.dtsMap.has(tsFileName)) {
this.dtsMap.set(tsFileName, new DtsFileTransformer());
}
return this.dtsMap.get(tsFileName) !;
}
}

View File

@ -1,54 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {CompileResult} from './api';
import {ImportManager, translateType} from './translator';
/**
* Processes .d.ts file text and adds static field declarations, with types.
*/
export class DtsFileTransformer {
private ivyFields = new Map<string, CompileResult>();
private imports = new ImportManager();
/**
* Track that a static field was added to the code for a class.
*/
recordStaticField(name: string, decl: CompileResult): void { this.ivyFields.set(name, decl); }
/**
* Process the .d.ts text for a file and add any declarations which were recorded.
*/
transform(dts: string): string {
const dtsFile =
ts.createSourceFile('out.d.ts', dts, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
for (let i = dtsFile.statements.length - 1; i >= 0; i--) {
const stmt = dtsFile.statements[i];
if (ts.isClassDeclaration(stmt) && stmt.name !== undefined &&
this.ivyFields.has(stmt.name.text)) {
const desc = this.ivyFields.get(stmt.name.text) !;
const before = dts.substring(0, stmt.end - 1);
const after = dts.substring(stmt.end - 1);
const type = translateType(desc.type, this.imports);
dts = before + ` static ${desc.field}: ${type};\n` + after;
}
}
const imports = this.imports.getAllImports();
if (imports.length !== 0) {
dts = imports.map(i => `import * as ${i.as} from '${i.name}';\n`).join() + dts;
}
return dts;
}
}

View File

@ -1,96 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {VisitListEntryResult, Visitor, visit} from '../../util/src/visitor';
import {IvyCompilation} from './compilation';
import {ImportManager, translateExpression, translateStatement} from './translator';
export function ivyTransformFactory(compilation: IvyCompilation):
ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
return (file: ts.SourceFile): ts.SourceFile => {
return transformIvySourceFile(compilation, context, file);
};
};
}
class IvyVisitor extends Visitor {
constructor(private compilation: IvyCompilation, private importManager: ImportManager) {
super();
}
visitClassDeclaration(node: ts.ClassDeclaration):
VisitListEntryResult<ts.Statement, ts.ClassDeclaration> {
// Determine if this class has an Ivy field that needs to be added, and compile the field
// to an expression if so.
const res = this.compilation.compileIvyFieldFor(node);
if (res !== undefined) {
// There is a field to add. Translate the initializer for the field into TS nodes.
const exprNode = translateExpression(res.initializer, this.importManager);
// Create a static property declaration for the new field.
const property = ts.createProperty(
undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], res.field, undefined, undefined,
exprNode);
// Replace the class declaration with an updated version.
node = ts.updateClassDeclaration(
node,
// Remove the decorator which triggered this compilation, leaving the others alone.
maybeFilterDecorator(node.decorators, this.compilation.ivyDecoratorFor(node) !),
node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
[...node.members, property]);
const statements = res.statements.map(stmt => translateStatement(stmt, this.importManager));
return {node, before: statements};
}
return {node};
}
}
/**
* A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
*/
function transformIvySourceFile(
compilation: IvyCompilation, context: ts.TransformationContext,
file: ts.SourceFile): ts.SourceFile {
const importManager = new ImportManager();
// Recursively scan through the AST and perform any updates requested by the IvyCompilation.
const sf = visit(file, new IvyVisitor(compilation, importManager), context);
// Generate the import statements to prepend.
const imports = importManager.getAllImports().map(
i => ts.createImportDeclaration(
undefined, undefined,
ts.createImportClause(undefined, ts.createNamespaceImport(ts.createIdentifier(i.as))),
ts.createLiteral(i.name)));
// Prepend imports if needed.
if (imports.length > 0) {
sf.statements = ts.createNodeArray([...imports, ...sf.statements]);
}
return sf;
}
function maybeFilterDecorator(
decorators: ts.NodeArray<ts.Decorator>| undefined,
toRemove: ts.Decorator): ts.NodeArray<ts.Decorator>|undefined {
if (decorators === undefined) {
return undefined;
}
const filtered = decorators.filter(dec => ts.getOriginalNode(dec) !== toRemove);
if (filtered.length === 0) {
return undefined;
}
return ts.createNodeArray(filtered);
}

View File

@ -1,368 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
import * as ts from 'typescript';
const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([
[BinaryOperator.And, ts.SyntaxKind.AmpersandAmpersandToken],
[BinaryOperator.Bigger, ts.SyntaxKind.GreaterThanToken],
[BinaryOperator.BiggerEquals, ts.SyntaxKind.GreaterThanEqualsToken],
[BinaryOperator.BitwiseAnd, ts.SyntaxKind.AmpersandToken],
[BinaryOperator.Divide, ts.SyntaxKind.SlashToken],
[BinaryOperator.Equals, ts.SyntaxKind.EqualsEqualsToken],
[BinaryOperator.Identical, ts.SyntaxKind.EqualsEqualsEqualsToken],
[BinaryOperator.Lower, ts.SyntaxKind.LessThanToken],
[BinaryOperator.LowerEquals, ts.SyntaxKind.LessThanEqualsToken],
[BinaryOperator.Minus, ts.SyntaxKind.MinusToken],
[BinaryOperator.Modulo, ts.SyntaxKind.PercentToken],
[BinaryOperator.Multiply, ts.SyntaxKind.AsteriskToken],
[BinaryOperator.NotEquals, ts.SyntaxKind.ExclamationEqualsToken],
[BinaryOperator.NotIdentical, ts.SyntaxKind.ExclamationEqualsEqualsToken],
[BinaryOperator.Or, ts.SyntaxKind.BarBarToken],
[BinaryOperator.Plus, ts.SyntaxKind.PlusToken],
]);
export class ImportManager {
private moduleToIndex = new Map<string, string>();
private nextIndex = 0;
generateNamedImport(moduleName: string): string {
if (!this.moduleToIndex.has(moduleName)) {
this.moduleToIndex.set(moduleName, `i${this.nextIndex++}`);
}
return this.moduleToIndex.get(moduleName) !;
}
getAllImports(): {name: string, as: string}[] {
return Array.from(this.moduleToIndex.keys()).map(name => {
const as = this.moduleToIndex.get(name) !;
return {name, as};
});
}
}
export function translateExpression(expression: Expression, imports: ImportManager): ts.Expression {
return expression.visitExpression(new ExpressionTranslatorVisitor(imports), null);
}
export function translateStatement(statement: Statement, imports: ImportManager): ts.Statement {
return statement.visitStatement(new ExpressionTranslatorVisitor(imports), null);
}
export function translateType(type: Type, imports: ImportManager): string {
return type.visitType(new TypeTranslatorVisitor(imports), null);
}
class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor {
constructor(private imports: ImportManager) {}
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): ts.VariableStatement {
return ts.createVariableStatement(
undefined,
ts.createVariableDeclarationList([ts.createVariableDeclaration(
stmt.name, undefined, stmt.value && stmt.value.visitExpression(this, context))]));
}
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): ts.FunctionDeclaration {
return ts.createFunctionDeclaration(
undefined, undefined, undefined, stmt.name, undefined,
stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)),
undefined,
ts.createBlock(stmt.statements.map(child => child.visitStatement(this, context))));
}
visitExpressionStmt(stmt: ExpressionStatement, context: any): ts.ExpressionStatement {
return ts.createStatement(stmt.expr.visitExpression(this, context));
}
visitReturnStmt(stmt: ReturnStatement, context: any): ts.ReturnStatement {
return ts.createReturn(stmt.value.visitExpression(this, context));
}
visitDeclareClassStmt(stmt: ClassStmt, context: any) {
throw new Error('Method not implemented.');
}
visitIfStmt(stmt: IfStmt, context: any): ts.IfStatement {
return ts.createIf(
stmt.condition.visitExpression(this, context),
ts.createBlock(stmt.trueCase.map(child => child.visitStatement(this, context))),
stmt.falseCase.length > 0 ?
ts.createBlock(stmt.falseCase.map(child => child.visitStatement(this, context))) :
undefined);
}
visitTryCatchStmt(stmt: TryCatchStmt, context: any) {
throw new Error('Method not implemented.');
}
visitThrowStmt(stmt: ThrowStmt, context: any) { throw new Error('Method not implemented.'); }
visitCommentStmt(stmt: CommentStmt, context: any): never {
throw new Error('Method not implemented.');
}
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): never {
throw new Error('Method not implemented.');
}
visitReadVarExpr(ast: ReadVarExpr, context: any): ts.Identifier {
return ts.createIdentifier(ast.name !);
}
visitWriteVarExpr(expr: WriteVarExpr, context: any): ts.BinaryExpression {
return ts.createBinary(
ts.createIdentifier(expr.name), ts.SyntaxKind.EqualsToken,
expr.value.visitExpression(this, context));
}
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitWritePropExpr(expr: WritePropExpr, context: any): ts.BinaryExpression {
return ts.createBinary(
ts.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name),
ts.SyntaxKind.EqualsToken, expr.value.visitExpression(this, context));
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): ts.CallExpression {
const target = ast.receiver.visitExpression(this, context);
return ts.createCall(
ast.name !== null ? ts.createPropertyAccess(target, ast.name) : target, undefined,
ast.args.map(arg => arg.visitExpression(this, context)));
}
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): ts.CallExpression {
return ts.createCall(
ast.fn.visitExpression(this, context), undefined,
ast.args.map(arg => arg.visitExpression(this, context)));
}
visitInstantiateExpr(ast: InstantiateExpr, context: any): ts.NewExpression {
return ts.createNew(
ast.classExpr.visitExpression(this, context), undefined,
ast.args.map(arg => arg.visitExpression(this, context)));
}
visitLiteralExpr(ast: LiteralExpr, context: any): ts.Expression {
if (ast.value === undefined) {
return ts.createIdentifier('undefined');
} else if (ast.value === null) {
return ts.createNull();
} else {
return ts.createLiteral(ast.value);
}
}
visitExternalExpr(ast: ExternalExpr, context: any): ts.PropertyAccessExpression {
if (ast.value.moduleName === null || ast.value.name === null) {
throw new Error(`Import unknown module or symbol ${ast.value}`);
}
return ts.createPropertyAccess(
ts.createIdentifier(this.imports.generateNamedImport(ast.value.moduleName)),
ts.createIdentifier(ast.value.name));
}
visitConditionalExpr(ast: ConditionalExpr, context: any): ts.ParenthesizedExpression {
return ts.createParen(ts.createConditional(
ast.condition.visitExpression(this, context), ast.trueCase.visitExpression(this, context),
ast.falseCase !.visitExpression(this, context)));
}
visitNotExpr(ast: NotExpr, context: any): ts.PrefixUnaryExpression {
return ts.createPrefix(
ts.SyntaxKind.ExclamationToken, ast.condition.visitExpression(this, context));
}
visitAssertNotNullExpr(ast: AssertNotNull, context: any): ts.NonNullExpression {
return ts.createNonNullExpression(ast.condition.visitExpression(this, context));
}
visitCastExpr(ast: CastExpr, context: any): ts.Expression {
return ast.value.visitExpression(this, context);
}
visitFunctionExpr(ast: FunctionExpr, context: any): ts.FunctionExpression {
return ts.createFunctionExpression(
undefined, undefined, ast.name || undefined, undefined,
ast.params.map(
param => ts.createParameter(
undefined, undefined, undefined, param.name, undefined, undefined, undefined)),
undefined, ts.createBlock(ast.statements.map(stmt => stmt.visitStatement(this, context))));
}
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): ts.Expression {
if (!BINARY_OPERATORS.has(ast.operator)) {
throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`);
}
const binEx = ts.createBinary(
ast.lhs.visitExpression(this, context), BINARY_OPERATORS.get(ast.operator) !,
ast.rhs.visitExpression(this, context));
return ast.parens ? ts.createParen(binEx) : binEx;
}
visitReadPropExpr(ast: ReadPropExpr, context: any): ts.PropertyAccessExpression {
return ts.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name);
}
visitReadKeyExpr(ast: ReadKeyExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): ts.ArrayLiteralExpression {
return ts.createArrayLiteral(ast.entries.map(expr => expr.visitExpression(this, context)));
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): ts.ObjectLiteralExpression {
const entries = ast.entries.map(
entry => ts.createPropertyAssignment(
entry.quoted ? ts.createLiteral(entry.key) : ts.createIdentifier(entry.key),
entry.value.visitExpression(this, context)));
return ts.createObjectLiteral(entries);
}
visitCommaExpr(ast: CommaExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { return ast.node; }
}
export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
constructor(private imports: ImportManager) {}
visitBuiltinType(type: BuiltinType, context: any): string {
switch (type.name) {
case BuiltinTypeName.Bool:
return 'boolean';
case BuiltinTypeName.Dynamic:
return 'any';
case BuiltinTypeName.Int:
case BuiltinTypeName.Number:
return 'number';
case BuiltinTypeName.String:
return 'string';
default:
throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`);
}
}
visitExpressionType(type: ExpressionType, context: any): any {
return type.value.visitExpression(this, context);
}
visitArrayType(type: ArrayType, context: any): string {
return `Array<${type.visitType(this, context)}>`;
}
visitMapType(type: MapType, context: any): string {
if (type.valueType !== null) {
return `{[key: string]: ${type.valueType.visitType(this, context)}}`;
} else {
return '{[key: string]: any}';
}
}
visitReadVarExpr(ast: ReadVarExpr, context: any): string {
if (ast.name === null) {
throw new Error(`ReadVarExpr with no variable name in type`);
}
return ast.name;
}
visitWriteVarExpr(expr: WriteVarExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitWritePropExpr(expr: WritePropExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitInstantiateExpr(ast: InstantiateExpr, context: any): never {
throw new Error('Method not implemented.');
}
visitLiteralExpr(ast: LiteralExpr, context: any): string {
if (typeof ast.value === 'string') {
const escaped = ast.value.replace(/\'/g, '\\\'');
return `'${escaped}'`;
} else {
return `${ast.value}`;
}
}
visitExternalExpr(ast: ExternalExpr, context: any): string {
if (ast.value.moduleName === null || ast.value.name === null) {
throw new Error(`Import unknown module or symbol`);
}
const base = `${this.imports.generateNamedImport(ast.value.moduleName)}.${ast.value.name}`;
if (ast.typeParams !== null) {
const generics = ast.typeParams.map(type => type.visitType(this, context)).join(', ');
return `${base}<${generics}>`;
} else {
return base;
}
}
visitConditionalExpr(ast: ConditionalExpr, context: any) {
throw new Error('Method not implemented.');
}
visitNotExpr(ast: NotExpr, context: any) { throw new Error('Method not implemented.'); }
visitAssertNotNullExpr(ast: AssertNotNull, context: any) {
throw new Error('Method not implemented.');
}
visitCastExpr(ast: CastExpr, context: any) { throw new Error('Method not implemented.'); }
visitFunctionExpr(ast: FunctionExpr, context: any) { throw new Error('Method not implemented.'); }
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any) {
throw new Error('Method not implemented.');
}
visitReadPropExpr(ast: ReadPropExpr, context: any) { throw new Error('Method not implemented.'); }
visitReadKeyExpr(ast: ReadKeyExpr, context: any) { throw new Error('Method not implemented.'); }
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): string {
const values = ast.entries.map(expr => expr.visitExpression(this, context));
return `[${values.join(',')}]`;
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any) {
throw new Error('Method not implemented.');
}
visitCommaExpr(ast: CommaExpr, context: any) { throw new Error('Method not implemented.'); }
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any) {
const node: ts.Node = ast.node;
if (ts.isIdentifier(node)) {
return node.text;
} else {
throw new Error(
`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`);
}
}
}

View File

@ -1,12 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "util",
srcs = glob([
"index.ts",
"src/**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/util",
)

View File

@ -1,116 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
/**
* Result type of visiting a node that's typically an entry in a list, which allows specifying that
* nodes should be added before the visited node in the output.
*/
export type VisitListEntryResult<B extends ts.Node, T extends B> = {
node: T,
before?: B[]
};
/**
* Visit a node with the given visitor and return a transformed copy.
*/
export function visit<T extends ts.Node>(
node: T, visitor: Visitor, context: ts.TransformationContext): T {
return visitor._visit(node, context);
}
/**
* Abstract base class for visitors, which processes certain nodes specially to allow insertion
* of other nodes before them.
*/
export abstract class Visitor {
/**
* Maps statements to an array of statements that should be inserted before them.
*/
private _before = new Map<ts.Node, ts.Statement[]>();
/**
* Visit a class declaration, returning at least the transformed declaration and optionally other
* nodes to insert before the declaration.
*/
visitClassDeclaration(node: ts.ClassDeclaration):
VisitListEntryResult<ts.Statement, ts.ClassDeclaration> {
return {node};
}
private _visitListEntryNode<T extends ts.Statement>(
node: T, visitor: (node: T) => VisitListEntryResult<ts.Statement, T>): T {
const result = visitor(node);
if (result.before !== undefined) {
// Record that some nodes should be inserted before the given declaration. The declaration's
// parent's _visit call is responsible for performing this insertion.
this._before.set(result.node, result.before);
}
return result.node;
}
/**
* Visit types of nodes which don't have their own explicit visitor.
*/
visitOtherNode<T extends ts.Node>(node: T): T { return node; }
/**
* @internal
*/
_visit<T extends ts.Node>(node: T, context: ts.TransformationContext): T {
// First, visit the node. visitedNode starts off as `null` but should be set after visiting
// is completed.
let visitedNode: T|null = null;
node = ts.visitEachChild(node, child => this._visit(child, context), context) as T;
if (ts.isClassDeclaration(node)) {
visitedNode = this._visitListEntryNode(
node, (node: ts.ClassDeclaration) => this.visitClassDeclaration(node)) as typeof node;
} else {
visitedNode = this.visitOtherNode(node);
}
// If the visited node has a `statements` array then process them, maybe replacing the visited
// node and adding additional statements.
if (hasStatements(visitedNode)) {
visitedNode = this._maybeProcessStatements(visitedNode);
}
return visitedNode;
}
private _maybeProcessStatements<T extends ts.Node&{statements: ts.NodeArray<ts.Statement>}>(
node: T): T {
// Shortcut - if every statement doesn't require nodes to be prepended, this is a no-op.
if (node.statements.every(stmt => !this._before.has(stmt))) {
return node;
}
// There are statements to prepend, so clone the original node.
const clone = ts.getMutableClone(node);
// Build a new list of statements and patch it onto the clone.
const newStatements: ts.Statement[] = [];
clone.statements.forEach(stmt => {
if (this._before.has(stmt)) {
newStatements.push(...(this._before.get(stmt) !as ts.Statement[]));
this._before.delete(stmt);
}
newStatements.push(stmt);
});
clone.statements = ts.createNodeArray(newStatements, node.statements.hasTrailingComma);
return clone;
}
}
function hasStatements(node: ts.Node): node is ts.Node&{statements: ts.NodeArray<ts.Statement>} {
const block = node as{statements?: any};
return block.statements !== undefined && Array.isArray(block.statements);
}

View File

@ -1,26 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
ts_library(
name = "test_lib",
testonly = 1,
srcs = glob([
"**/*.ts",
]),
deps = [
"//packages:types",
"//packages/compiler-cli/src/ngtsc/testing",
"//packages/compiler-cli/src/ngtsc/util",
],
)
jasmine_node_test(
name = "test",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
deps = [
":test_lib",
"//tools/testing:node_no_angular",
],
)

View File

@ -1,94 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {makeProgram} from '../../testing/in_memory_typescript';
import {VisitListEntryResult, Visitor, visit} from '../src/visitor';
class TestAstVisitor extends Visitor {
visitClassDeclaration(node: ts.ClassDeclaration):
VisitListEntryResult<ts.Statement, ts.ClassDeclaration> {
const name = node.name !.text;
const statics =
node.members.filter(member => (member.modifiers as ReadonlyArray<ts.Modifier>|| [
]).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword));
const idStatic = statics
.find(
el => ts.isPropertyDeclaration(el) && ts.isIdentifier(el.name) &&
el.name.text === 'id') as ts.PropertyDeclaration |
undefined;
if (idStatic !== undefined) {
return {
node,
before: [
ts.createVariableStatement(
undefined,
[
ts.createVariableDeclaration(`${name}_id`, undefined, idStatic.initializer),
]),
],
};
}
return {node};
}
}
function testTransformerFactory(context: ts.TransformationContext): ts.Transformer<ts.SourceFile> {
return (file: ts.SourceFile) => visit(file, new TestAstVisitor(), context);
}
describe('AST Visitor', () => {
it('should add a statement before class in plain file', () => {
const {program, host} =
makeProgram([{name: 'main.ts', contents: `class A { static id = 3; }`}]);
const sf = program.getSourceFile('main.ts') !;
program.emit(sf, undefined, undefined, undefined, {before: [testTransformerFactory]});
const main = host.readFile('/main.js');
expect(main).toMatch(/^var A_id = 3;/);
});
it('should add a statement before class inside function definition', () => {
const {program, host} = makeProgram([{
name: 'main.ts',
contents: `
export function foo() {
var x = 3;
class A { static id = 2; }
return A;
}
`
}]);
const sf = program.getSourceFile('main.ts') !;
program.emit(sf, undefined, undefined, undefined, {before: [testTransformerFactory]});
const main = host.readFile('/main.js');
expect(main).toMatch(/var x = 3;\s+var A_id = 2;\s+var A =/);
});
it('handles nested statements', () => {
const {program, host} = makeProgram([{
name: 'main.ts',
contents: `
export class A {
static id = 3;
foo() {
class B {
static id = 4;
}
return B;
}
}`
}]);
const sf = program.getSourceFile('main.ts') !;
program.emit(sf, undefined, undefined, undefined, {before: [testTransformerFactory]});
const main = host.readFile('/main.js');
expect(main).toMatch(/var A_id = 3;\s+var A = /);
expect(main).toMatch(/var B_id = 4;\s+var B = /);
});
});

View File

@ -182,17 +182,9 @@ export interface CompilerOptions extends ts.CompilerOptions {
* Not all features are supported with this option enabled. It is only supported
* for experimentation and testing of Render3 style code generation.
*
* Acceptable values are as follows:
*
* `false` - run ngc normally
* `true` - run ngc with its usual global analysis, but compile decorators to Ivy fields instead
* of running the View Engine compilers
* `ngtsc` - run the ngtsc compiler instead of the normal ngc compiler
* `tsc` - behave like plain tsc as much as possible (used for testing JIT code)
*
* @experimental
*/
enableIvy?: boolean|'ngtsc'|'tsc';
enableIvy?: boolean;
/** @internal */
collectAllErrors?: boolean;

View File

@ -12,7 +12,6 @@ import * as ts from 'typescript';
import {TypeCheckHost} from '../diagnostics/translate_diagnostics';
import {METADATA_VERSION, ModuleMetadata} from '../metadata/index';
import {NgtscCompilerHost} from '../ngtsc/compiler_host';
import {CompilerHost, CompilerOptions, LibrarySummary} from './api';
import {MetadataReaderHost, createMetadataReaderCache, readMetadata} from './metadata_reader';
@ -24,9 +23,6 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export function createCompilerHost(
{options, tsHost = ts.createCompilerHost(options, true)}:
{options: CompilerOptions, tsHost?: ts.CompilerHost}): CompilerHost {
if (options.enableIvy === 'ngtsc' || options.enableIvy === 'tsc') {
return new NgtscCompilerHost(tsHost);
}
return tsHost;
}

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {error} from './util';
export interface Node { sourceSpan: ParseSourceSpan|null; }
@ -21,7 +20,7 @@ const _VALID_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
export class TypeScriptNodeEmitter {
updateSourceFile(sourceFile: ts.SourceFile, stmts: Statement[], preamble?: string):
[ts.SourceFile, Map<ts.Node, Node>] {
const converter = new NodeEmitterVisitor();
const converter = new _NodeEmitterVisitor();
// [].concat flattens the result so that each `visit...` method can also return an array of
// stmts.
const statements: any[] = [].concat(
@ -63,7 +62,7 @@ export class TypeScriptNodeEmitter {
export function updateSourceFile(
sourceFile: ts.SourceFile, module: PartialModule,
context: ts.TransformationContext): [ts.SourceFile, Map<ts.Node, Node>] {
const converter = new NodeEmitterVisitor();
const converter = new _NodeEmitterVisitor();
converter.loadExportedVariableIdentifiers(sourceFile);
const prefixStatements = module.statements.filter(statement => !(statement instanceof ClassStmt));
@ -149,7 +148,7 @@ function firstAfter<T>(a: T[], predicate: (value: T) => boolean) {
// A recorded node is a subtype of the node that is marked as being recorded. This is used
// to ensure that NodeEmitterVisitor.record has been called on all nodes returned by the
// NodeEmitterVisitor
export type RecordedNode<T extends ts.Node = ts.Node> = (T & { __recorded: any;}) | null;
type RecordedNode<T extends ts.Node = ts.Node> = (T & { __recorded: any; }) | null;
function escapeLiteral(value: string): string {
return value.replace(/(\"|\\)/g, '\\$1').replace(/(\n)|(\r)/g, function(v, n, r) {
@ -184,7 +183,7 @@ function isExportTypeStatement(statement: ts.Statement): boolean {
/**
* Visits an output ast and produces the corresponding TypeScript synthetic nodes.
*/
export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
private _nodeMap = new Map<ts.Node, Node>();
private _importsWithPrefixes = new Map<string, string>();
private _reexports = new Map<string, {name: string, as: string}[]>();
@ -462,9 +461,6 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
return commentStmt;
}
// ExpressionVisitor
visitWrappedNodeExpr(expr: WrappedNodeExpr<any>) { return this.record(expr, expr.node); }
// ExpressionVisitor
visitReadVarExpr(expr: ReadVarExpr) {
switch (expr.builtin) {

View File

@ -15,7 +15,6 @@ import * as ts from 'typescript';
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
import {compareVersions} from '../diagnostics/typescript_version';
import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata/index';
import {NgtscProgram} from '../ngtsc/program';
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback, TsMergeEmitResultsCallback} from './api';
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
@ -26,7 +25,6 @@ import {getAngularEmitterTransformFactory} from './node_emitter_transform';
import {PartialModuleMetadataTransformer} from './r3_metadata_transform';
import {StripDecoratorsMetadataTransformer, getDecoratorStripTransformerFactory} from './r3_strip_decorators';
import {getAngularClassTransformerFactory} from './r3_transform';
import {TscPassThroughProgram} from './tsc_pass_through';
import {DTS, GENERATED_FILES, StructureIsReused, TS, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused, userError} from './util';
@ -109,7 +107,7 @@ const MIN_TS_VERSION = '2.7.2';
* ∀ supported typescript version v, v < MAX_TS_VERSION
* MAX_TS_VERSION is not considered as a supported TypeScript version
*/
const MAX_TS_VERSION = '2.9.0';
const MAX_TS_VERSION = '2.8.0';
class AngularCompilerProgram implements Program {
private rootNames: string[];
@ -288,9 +286,6 @@ class AngularCompilerProgram implements Program {
emitCallback?: TsEmitCallback,
mergeEmitResultsCallback?: TsMergeEmitResultsCallback,
} = {}): ts.EmitResult {
if (this.options.enableIvy === 'ngtsc' || this.options.enableIvy === 'tsc') {
throw new Error('Cannot run legacy compiler in ngtsc mode');
}
return this.options.enableIvy === true ? this._emitRender3(parameters) :
this._emitRender2(parameters);
}
@ -338,34 +333,14 @@ class AngularCompilerProgram implements Program {
/* genFiles */ undefined, /* partialModules */ modules,
/* stripDecorators */ this.reifiedDecorators, customTransformers);
// Restore the original references before we emit so TypeScript doesn't emit
// a reference to the .d.ts file.
const augmentedReferences = new Map<ts.SourceFile, ReadonlyArray<ts.FileReference>>();
for (const sourceFile of this.tsProgram.getSourceFiles()) {
const originalReferences = getOriginalReferences(sourceFile);
if (originalReferences) {
augmentedReferences.set(sourceFile, sourceFile.referencedFiles);
sourceFile.referencedFiles = originalReferences;
}
}
try {
return emitCallback({
const emitResult = emitCallback({
program: this.tsProgram,
host: this.host,
options: this.options,
writeFile: writeTsFile, emitOnlyDtsFiles,
customTransformers: tsCustomTransformers
});
} finally {
// Restore the references back to the augmented value to ensure that the
// checks that TypeScript makes for project structure reuse will succeed.
for (const [sourceFile, references] of Array.from(augmentedReferences)) {
// TODO(chuckj): Remove any cast after updating build to 2.6
(sourceFile as any).referencedFiles = references;
}
}
return emitResult;
}
private _emitRender2(
@ -928,11 +903,6 @@ export function createProgram({rootNames, options, host, oldProgram}: {
options: CompilerOptions,
host: CompilerHost, oldProgram?: Program
}): Program {
if (options.enableIvy === 'ngtsc') {
return new NgtscProgram(rootNames, options, host, oldProgram);
} else if (options.enableIvy === 'tsc') {
return new TscPassThroughProgram(rootNames, options, host, oldProgram);
}
return new AngularCompilerProgram(rootNames, options, host, oldProgram);
}

View File

@ -1,107 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {GeneratedFile} from '@angular/compiler';
import * as path from 'path';
import * as ts from 'typescript';
import * as api from '../transformers/api';
/**
* An implementation of the `Program` API which behaves like plain `tsc` and does not include any
* Angular-specific behavior whatsoever.
*
* This allows `ngc` to behave like `tsc` in cases where JIT code needs to be tested.
*/
export class TscPassThroughProgram implements api.Program {
private tsProgram: ts.Program;
constructor(
rootNames: ReadonlyArray<string>, private options: api.CompilerOptions,
private host: api.CompilerHost, oldProgram?: api.Program) {
this.tsProgram =
ts.createProgram(rootNames, options, host, oldProgram && oldProgram.getTsProgram());
}
getTsProgram(): ts.Program { return this.tsProgram; }
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getOptionsDiagnostics(cancellationToken);
}
getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
getTsSyntacticDiagnostics(
sourceFile?: ts.SourceFile|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken);
}
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
getTsSemanticDiagnostics(
sourceFile?: ts.SourceFile|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<ts.Diagnostic> {
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
}
getNgSemanticDiagnostics(
fileName?: string|undefined,
cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray<api.Diagnostic> {
return [];
}
loadNgStructureAsync(): Promise<void> { return Promise.resolve(); }
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
throw new Error('Method not implemented.');
}
getLibrarySummaries(): Map<string, api.LibrarySummary> {
throw new Error('Method not implemented.');
}
getEmittedGeneratedFiles(): Map<string, GeneratedFile> {
throw new Error('Method not implemented.');
}
getEmittedSourceFiles(): Map<string, ts.SourceFile> {
throw new Error('Method not implemented.');
}
emit(opts?: {
emitFlags?: api.EmitFlags,
cancellationToken?: ts.CancellationToken,
customTransformers?: api.CustomTransformers,
emitCallback?: api.TsEmitCallback,
mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback
}): ts.EmitResult {
const emitCallback = opts && opts.emitCallback || defaultEmitCallback;
const emitResult = emitCallback({
program: this.tsProgram,
host: this.host,
options: this.options,
emitOnlyDtsFiles: false,
});
return emitResult;
}
}
const defaultEmitCallback: api.TsEmitCallback =
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
customTransformers}) =>
program.emit(
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);

View File

@ -2021,7 +2021,6 @@ describe('ngc transformer command-line', () => {
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')]);
expect(exitCode).toBe(0, 'Compile failed');
expect(emittedFile('hello-world.js')).toContain('ngComponentDef');
expect(emittedFile('hello-world.js')).toContain('HelloWorldComponent_Factory');
});
it('should emit an injection of a string token', () => {

View File

@ -1,27 +0,0 @@
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
ts_library(
name = "ngtsc_lib",
testonly = 1,
srcs = [
"ngtsc_spec.ts",
],
deps = [
"//packages/compiler",
"//packages/compiler-cli",
"//packages/compiler-cli/test:test_utils",
],
)
jasmine_node_test(
name = "ngtsc",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
data = [
"//packages/compiler-cli/test/ngtsc/fake_core:npm_package",
],
deps = [
":ngtsc_lib",
"//tools/testing:node_no_angular",
],
)

View File

@ -1,22 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ng_package", "ts_library")
ts_library(
name = "fake_core",
srcs = [
"index.ts",
],
module_name = "@angular/core",
)
ng_package(
name = "npm_package",
srcs = [
"package.json",
],
entry_point = "packages/fake_core/index.js",
deps = [
":fake_core",
],
)

View File

@ -1,13 +0,0 @@
`fake_core` is a library designed to expose some of the same symbols as `@angular/core`, without
requiring compilation of the whole of `@angular/core`. This enables unit tests for the compiler to
be written without incurring long rebuilds for every change.
* `@angular/core` is compiled with `@angular/compiler-cli`, and therefore has an implicit dependency
on it. Therefore core must be rebuilt if the compiler changes.
* Tests for the compiler which intend to build code that depends on `@angular/core` must have
a data dependency on `@angular/core`. Therefore core must be built to run the compiler tests, and
thus rebuilt if the compiler changes.
This rebuild cycle is expensive and slow. `fake_core` avoids this by exposing a subset of the
`@angular/core` API, which enables applications to be built by the ngtsc compiler without
needing a full version of core present at compile time.

View File

@ -1,27 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
type FnWithArg<T> = (arg?: any) => T;
function callableClassDecorator(): FnWithArg<(clazz: any) => any> {
return null !;
}
function callableParamDecorator(): FnWithArg<(a: any, b: any, c: any) => void> {
return null !;
}
export const Component = callableClassDecorator();
export const Directive = callableClassDecorator();
export const Injectable = callableClassDecorator();
export const NgModule = callableClassDecorator();
export const Inject = callableParamDecorator();
export const Self = callableParamDecorator();
export const SkipSelf = callableParamDecorator();
export const Optional = callableParamDecorator();

View File

@ -1,13 +0,0 @@
{
"name": "@angular/core",
"version": "0.0.0-FAKE-FOR-TESTS",
"description": "Fake version of core for ngtsc tests",
"main": "./bundles/fake_core.umd.js",
"module": "./fesm5/fake_core.js",
"es2015": "./fesm2015/fake_core.js",
"esm5": "./esm5/index.js",
"esm2015": "./esm2015/index.js",
"fesm5": "./fesm5/fake_core.js",
"fesm2015": "./fesm2015/fake_core.js",
"typings": "./index.d.ts"
}

View File

@ -1,183 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {main, readCommandLineAndConfiguration, watchMode} from '../../src/main';
import {TestSupport, isInBazel, makeTempDir, setup} from '../test_support';
function setupFakeCore(support: TestSupport): void {
const fakeCore = path.join(
process.env.TEST_SRCDIR, 'angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package');
const nodeModulesPath = path.join(support.basePath, 'node_modules');
const angularCoreDirectory = path.join(nodeModulesPath, '@angular/core');
fs.symlinkSync(fakeCore, angularCoreDirectory);
}
function getNgRootDir() {
const moduleFilename = module.filename.replace(/\\/g, '/');
const distIndex = moduleFilename.indexOf('/dist/all');
return moduleFilename.substr(0, distIndex);
}
describe('ngtsc behavioral tests', () => {
if (!isInBazel()) {
// These tests should be excluded from the non-Bazel build.
return;
}
let basePath: string;
let outDir: string;
let write: (fileName: string, content: string) => void;
let errorSpy: jasmine.Spy&((s: string) => void);
function shouldExist(fileName: string) {
if (!fs.existsSync(path.resolve(outDir, fileName))) {
throw new Error(`Expected ${fileName} to be emitted (outDir: ${outDir})`);
}
}
function shouldNotExist(fileName: string) {
if (fs.existsSync(path.resolve(outDir, fileName))) {
throw new Error(`Did not expect ${fileName} to be emitted (outDir: ${outDir})`);
}
}
function getContents(fileName: string): string {
shouldExist(fileName);
const modulePath = path.resolve(outDir, fileName);
return fs.readFileSync(modulePath, 'utf8');
}
function writeConfig(
tsconfig: string =
'{"extends": "./tsconfig-base.json", "angularCompilerOptions": {"enableIvy": "ngtsc"}}') {
write('tsconfig.json', tsconfig);
}
beforeEach(() => {
errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error);
const support = setup();
basePath = support.basePath;
outDir = path.join(basePath, 'built');
process.chdir(basePath);
write = (fileName: string, content: string) => { support.write(fileName, content); };
setupFakeCore(support);
write('tsconfig-base.json', `{
"compilerOptions": {
"experimentalDecorators": true,
"skipLibCheck": true,
"noImplicitAny": true,
"types": [],
"outDir": "built",
"rootDir": ".",
"baseUrl": ".",
"declaration": true,
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"lib": ["es6", "dom"],
"typeRoots": ["node_modules/@types"]
},
"angularCompilerOptions": {
"enableIvy": "ngtsc"
}
}`);
});
it('should compile Injectables without errors', () => {
writeConfig();
write('test.ts', `
import {Injectable} from '@angular/core';
@Injectable()
export class Dep {}
@Injectable()
export class Service {
constructor(dep: Dep) {}
}
`);
const exitCode = main(['-p', basePath], errorSpy);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
const jsContents = getContents('test.js');
expect(jsContents).toContain('Dep.ngInjectableDef =');
expect(jsContents).toContain('Service.ngInjectableDef =');
expect(jsContents).not.toContain('__decorate');
const dtsContents = getContents('test.d.ts');
expect(dtsContents).toContain('static ngInjectableDef: i0.InjectableDef<Dep>;');
expect(dtsContents).toContain('static ngInjectableDef: i0.InjectableDef<Service>;');
});
it('should compile Components without errors', () => {
writeConfig();
write('test.ts', `
import {Component} from '@angular/core';
@Component({
selector: 'test-cmp',
template: 'this is a test',
})
export class TestCmp {}
`);
const exitCode = main(['-p', basePath], errorSpy);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
const jsContents = getContents('test.js');
expect(jsContents).toContain('TestCmp.ngComponentDef = i0.ɵdefineComponent');
expect(jsContents).not.toContain('__decorate');
const dtsContents = getContents('test.d.ts');
expect(dtsContents).toContain('static ngComponentDef: i0.ComponentDef<TestCmp, \'test-cmp\'>');
});
it('should compile NgModules without errors', () => {
writeConfig();
write('test.ts', `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'test-cmp',
template: 'this is a test',
})
export class TestCmp {}
@NgModule({
declarations: [TestCmp],
})
export class TestModule {}
`);
const exitCode = main(['-p', basePath], errorSpy);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
const jsContents = getContents('test.js');
expect(jsContents)
.toContain(
'i0.ɵdefineNgModule({ type: TestModule, bootstrap: [], ' +
'declarations: [TestCmp], imports: [], exports: [] })');
const dtsContents = getContents('test.d.ts');
expect(dtsContents).toContain('static ngComponentDef: i0.ComponentDef<TestCmp, \'test-cmp\'>');
expect(dtsContents)
.toContain('static ngModuleDef: i0.NgModuleDef<TestModule, [TestCmp], [], []>');
expect(dtsContents).not.toContain('__decorate');
});
});

View File

@ -21,10 +21,7 @@ ng_package(
],
entry_point = "packages/compiler/compiler.js",
include_devmode_srcs = True,
tags = [
"ivy-jit",
"release-with-framework",
],
tags = ["release-with-framework"],
deps = [
":compiler",
"//packages/compiler/testing",

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileInjectableMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileShallowModuleMetadata, CompileStylesheetMetadata, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileInjectableMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileShallowModuleMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl, tokenReference} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {ConstantPool} from '../constant_pool';
import {ViewEncapsulation} from '../core';
@ -14,19 +14,16 @@ import {MessageBundle} from '../i18n/message_bundle';
import {Identifiers, createTokenForExternalReference} from '../identifiers';
import {InjectableCompiler} from '../injectable_compiler';
import {CompileMetadataResolver} from '../metadata_resolver';
import * as html from '../ml_parser/ast';
import {HtmlParser} from '../ml_parser/html_parser';
import {removeWhitespaces} from '../ml_parser/html_whitespaces';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast';
import {ParseError} from '../parse_util';
import {compileNgModuleFromRender2 as compileR3Module} from '../render3/r3_module_compiler';
import {compilePipe as compileR3Pipe} from '../render3/r3_pipe_compiler';
import {htmlAstToRender3Ast} from '../render3/r3_template_transform';
import {compileComponentFromRender2 as compileR3Component, compileDirectiveFromRender2 as compileR3Directive} from '../render3/view/compiler';
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
import {compileNgModule as compileIvyModule} from '../render3/r3_module_compiler';
import {compilePipe as compileIvyPipe} from '../render3/r3_pipe_compiler';
import {OutputMode} from '../render3/r3_types';
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {BindingParser} from '../template_parser/binding_parser';
@ -43,11 +40,15 @@ import {LazyRoute, listLazyRoutes, parseLazyRoute} from './lazy_routes';
import {PartialModule} from './partial_module';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {createForJitStub, serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName} from './util';
import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util';
const enum StubEmitFlags { Basic = 1 << 0, TypeCheck = 1 << 1, All = TypeCheck | Basic }
enum StubEmitFlags {
Basic = 1 << 0,
TypeCheck = 1 << 1,
All = TypeCheck | Basic
}
export class AotCompiler {
private _templateAstCache =
@ -362,19 +363,18 @@ export class AotCompiler {
private _compileShallowModules(
fileName: string, shallowModules: CompileShallowModuleMetadata[],
context: OutputContext): void {
shallowModules.forEach(module => compileR3Module(context, module, this._injectableCompiler));
shallowModules.forEach(module => compileIvyModule(context, module, this._injectableCompiler));
}
private _compilePartialModule(
fileName: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: CompileNgModuleMetadata[],
injectables: CompileInjectableMetadata[], context: OutputContext): void {
const classes: o.ClassStmt[] = [];
const errors: ParseError[] = [];
const schemaRegistry = new DomElementSchemaRegistry();
const hostBindingParser = new BindingParser(
this._templateParser.expressionParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [],
errors);
this._templateParser.expressionParser, DEFAULT_INTERPOLATION_CONFIG, null !, [], errors);
// Process all components and directives
directives.forEach(directiveType => {
@ -385,46 +385,21 @@ export class AotCompiler {
error(
`Cannot determine the module for component '${identifierName(directiveMetadata.type)}'`);
let htmlAst = directiveMetadata.template !.htmlAst !;
const preserveWhitespaces = directiveMetadata !.template !.preserveWhitespaces;
if (!preserveWhitespaces) {
htmlAst = removeWhitespaces(htmlAst);
}
const render3Ast = htmlAstToRender3Ast(htmlAst.rootNodes, hostBindingParser);
// Map of StaticType by directive selectors
const directiveTypeBySel = new Map<string, any>();
const directives = module.transitiveModule.directives.map(
dir => this._metadataResolver.getDirectiveSummary(dir.reference));
directives.forEach(directive => {
if (directive.selector) {
directiveTypeBySel.set(directive.selector, directive.type.reference);
}
});
// Map of StaticType by pipe names
const pipeTypeByName = new Map<string, any>();
const pipes = module.transitiveModule.pipes.map(
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
pipes.forEach(pipe => { pipeTypeByName.set(pipe.name, pipe.type.reference); });
compileR3Component(
context, directiveMetadata, render3Ast, this.reflector, hostBindingParser,
directiveTypeBySel, pipeTypeByName);
const {template: parsedTemplate, pipes: parsedPipes} =
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
compileIvyComponent(
context, directiveMetadata, parsedPipes, parsedTemplate, this.reflector,
hostBindingParser, OutputMode.PartialClass);
} else {
compileR3Directive(context, directiveMetadata, this.reflector, hostBindingParser);
compileIvyDirective(
context, directiveMetadata, this.reflector, hostBindingParser, OutputMode.PartialClass);
}
});
pipes.forEach(pipeType => {
const pipeMetadata = this._metadataResolver.getPipeMetadata(pipeType);
if (pipeMetadata) {
compileR3Pipe(context, pipeMetadata, this.reflector);
compileIvyPipe(context, pipeMetadata, this.reflector, OutputMode.PartialClass);
}
});
@ -918,13 +893,16 @@ export function analyzeFileForInjectables(
if (!symbolMeta || symbolMeta.__symbolic === 'error') {
return;
}
let isNgSymbol = false;
if (symbolMeta.__symbolic === 'class') {
if (metadataResolver.isInjectable(symbol)) {
isNgSymbol = true;
const injectable = metadataResolver.getInjectableMetadata(symbol, null, false);
if (injectable) {
injectables.push(injectable);
}
} else if (metadataResolver.isNgModule(symbol)) {
isNgSymbol = true;
const module = metadataResolver.getShallowModuleMetadata(symbol);
if (module) {
shallowModules.push(module);

View File

@ -18,5 +18,5 @@ export interface AotCompilerOptions {
fullTemplateTypeCheck?: boolean;
allowEmptyCodegenFiles?: boolean;
strictInjectionParameters?: boolean;
enableIvy?: boolean|'ngtsc'|'tsc';
enableIvy?: boolean;
}

View File

@ -50,7 +50,6 @@ export {JitCompiler} from './jit/compiler';
export * from './compile_reflector';
export * from './url_resolver';
export * from './resource_loader';
export {ConstantPool} from './constant_pool';
export {DirectiveResolver} from './directive_resolver';
export {PipeResolver} from './pipe_resolver';
export {NgModuleResolver} from './ng_module_resolver';
@ -68,7 +67,7 @@ export * from './ml_parser/html_tags';
export * from './ml_parser/interpolation_config';
export * from './ml_parser/tags';
export {NgModuleCompiler} from './ng_module_compiler';
export {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
export {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
export {EmitterVisitorContext} from './output/abstract_emitter';
export * from './output/ts_emitter';
export * from './parse_util';
@ -79,11 +78,4 @@ export * from './template_parser/template_parser';
export {ViewCompiler} from './view_compiler/view_compiler';
export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
export {SourceMap} from './output/source_map';
export * from './injectable_compiler_2';
export * from './render3/view/api';
export {jitExpression} from './render3/r3_jit';
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
export {compileNgModule, R3NgModuleMetadata} from './render3/r3_module_compiler';
export {makeBindingParser, parseTemplate} from './render3/view/template';
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings} from './render3/view/compiler';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -295,9 +295,7 @@ class KeyVisitor implements o.ExpressionVisitor {
`EX:${ast.value.runtime.name}`;
}
visitReadVarExpr(node: o.ReadVarExpr) { return `VAR:${node.name}`; }
visitWrappedNodeExpr = invalid;
visitReadVarExpr = invalid;
visitWriteVarExpr = invalid;
visitWriteKeyExpr = invalid;
visitWritePropExpr = invalid;

View File

@ -12,8 +12,6 @@
// This is important to prevent a build cycle, as @angular/core needs to
// be compiled with the compiler.
import {CssSelector} from './selector';
export interface Inject { token: any; }
export const createInject = makeMetadataFactory<Inject>('Inject', (token: any) => ({token}));
export const createInjectionToken = makeMetadataFactory<object>(
@ -297,84 +295,3 @@ export interface Route {
children?: Route[];
loadChildren?: string|Type|any;
}
/**
* Flags used to generate R3-style CSS Selectors. They are pasted from
* core/src/render3/projection.ts because they cannot be referenced directly.
*/
export const enum SelectorFlags {
/** Indicates this is the beginning of a new negative selector */
NOT = 0b0001,
/** Mode for matching attributes */
ATTRIBUTE = 0b0010,
/** Mode for matching tag names */
ELEMENT = 0b0100,
/** Mode for matching class names */
CLASS = 0b1000,
}
// These are a copy the CSS types from core/src/render3/interfaces/projection.ts
// They are duplicated here as they cannot be directly referenced from core.
export type R3CssSelector = (string | SelectorFlags)[];
export type R3CssSelectorList = R3CssSelector[];
function parserSelectorToSimpleSelector(selector: CssSelector): R3CssSelector {
const classes = selector.classNames && selector.classNames.length ?
[SelectorFlags.CLASS, ...selector.classNames] :
[];
const elementName = selector.element && selector.element !== '*' ? selector.element : '';
return [elementName, ...selector.attrs, ...classes];
}
function parserSelectorToNegativeSelector(selector: CssSelector): R3CssSelector {
const classes = selector.classNames && selector.classNames.length ?
[SelectorFlags.CLASS, ...selector.classNames] :
[];
if (selector.element) {
return [
SelectorFlags.NOT | SelectorFlags.ELEMENT, selector.element, ...selector.attrs, ...classes
];
} else if (selector.attrs.length) {
return [SelectorFlags.NOT | SelectorFlags.ATTRIBUTE, ...selector.attrs, ...classes];
} else {
return selector.classNames && selector.classNames.length ?
[SelectorFlags.NOT | SelectorFlags.CLASS, ...selector.classNames] :
[];
}
}
function parserSelectorToR3Selector(selector: CssSelector): R3CssSelector {
const positive = parserSelectorToSimpleSelector(selector);
const negative: R3CssSelectorList = selector.notSelectors && selector.notSelectors.length ?
selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
[];
return positive.concat(...negative);
}
export function parseSelectorToR3Selector(selector: string): R3CssSelectorList {
const selectors = CssSelector.parse(selector);
return selectors.map(parserSelectorToR3Selector);
}
// Pasted from render3/interfaces/definition since it cannot be referenced directly
/**
* Flags passed into template functions to determine which blocks (i.e. creation, update)
* should be executed.
*
* Typically, a template runs both the creation block and the update block on initialization and
* subsequent runs only execute the update block. However, dynamically created views require that
* the creation block be executed separately from the update block (for backwards compat).
*/
export const enum RenderFlags {
/* Whether to run the creation block (e.g. create elements and directives) */
Create = 0b01,
/* Whether to run the update block (e.g. refresh bindings) */
Update = 0b10
}

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SecurityContext} from '../core';
import {ParseSourceSpan} from '../parse_util';
export class ParserError {
public message: string;
@ -216,7 +215,7 @@ export class ASTWithSource extends AST {
export class TemplateBinding {
constructor(
public span: ParseSpan, public key: string, public keyIsVar: boolean, public name: string,
public expression: ASTWithSource|null) {}
public expression: ASTWithSource) {}
}
export interface AstVisitor {
@ -664,62 +663,3 @@ export function visitAstChildren(ast: AST, visitor: AstVisitor, context?: any) {
visitSafePropertyRead(ast) { visit(ast.receiver); },
});
}
// Bindings
export class ParsedProperty {
public readonly isLiteral: boolean;
public readonly isAnimation: boolean;
constructor(
public name: string, public expression: ASTWithSource, public type: ParsedPropertyType,
public sourceSpan: ParseSourceSpan) {
this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
}
}
export enum ParsedPropertyType {
DEFAULT,
LITERAL_ATTR,
ANIMATION
}
export const enum ParsedEventType {
// DOM or Directive event
Regular,
// Animation specific event
Animation,
}
export class ParsedEvent {
// Regular events have a target
// Animation events have a phase
constructor(
public name: string, public targetOrPhase: string, public type: ParsedEventType,
public handler: AST, public sourceSpan: ParseSourceSpan) {}
}
export class ParsedVariable {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
}
export const enum BindingType {
// A regular binding to a property (e.g. `[property]="expression"`).
Property,
// A binding to an element attribute (e.g. `[attr.name]="expression"`).
Attribute,
// A binding to a CSS class (e.g. `[class.name]="condition"`).
Class,
// A binding to a style rule (e.g. `[style.rule]="expression"`).
Style,
// A binding to an animation reference (e.g. `[animate.key]="expression"`).
Animation,
}
export class BoundElementProperty {
constructor(
public name: string, public type: BindingType, public securityContext: SecurityContext,
public value: AST, public unit: string|null, public sourceSpan: ParseSourceSpan) {}
}

View File

@ -98,11 +98,19 @@ export class Parser {
return new Quote(new ParseSpan(0, input.length), prefix, uninterpretedExpression, location);
}
parseTemplateBindings(tplKey: string, tplValue: string, location: any):
parseTemplateBindings(prefixToken: string|null, input: string, location: any):
TemplateBindingParseResult {
const tokens = this._lexer.tokenize(tplValue);
return new _ParseAST(tplValue, location, tokens, tplValue.length, false, this.errors, 0)
.parseTemplateBindings(tplKey);
const tokens = this._lexer.tokenize(input);
if (prefixToken) {
// Prefix the tokens with the tokens from prefixToken but have them take no space (0 index).
const prefixTokens = this._lexer.tokenize(prefixToken).map(t => {
t.index = 0;
return t;
});
tokens.unshift(...prefixTokens);
}
return new _ParseAST(input, location, tokens, input.length, false, this.errors, 0)
.parseTemplateBindings();
}
parseInterpolation(
@ -678,49 +686,48 @@ export class _ParseAST {
return result.toString();
}
// Parses the AST for `<some-tag *tplKey=AST>`
parseTemplateBindings(tplKey: string): TemplateBindingParseResult {
let firstBinding = true;
parseTemplateBindings(): TemplateBindingParseResult {
const bindings: TemplateBinding[] = [];
let prefix: string = null !;
const warnings: string[] = [];
do {
while (this.index < this.tokens.length) {
const start = this.inputIndex;
let rawKey: string;
let key: string;
let isVar: boolean = false;
if (firstBinding) {
rawKey = key = tplKey;
firstBinding = false;
} else {
isVar = this.peekKeywordLet();
if (isVar) this.advance();
rawKey = this.expectTemplateBindingKey();
key = isVar ? rawKey : tplKey + rawKey[0].toUpperCase() + rawKey.substring(1);
this.optionalCharacter(chars.$COLON);
let keyIsVar: boolean = this.peekKeywordLet();
if (keyIsVar) {
this.advance();
}
let rawKey = this.expectTemplateBindingKey();
let key = rawKey;
if (!keyIsVar) {
if (prefix == null) {
prefix = key;
} else {
key = prefix + key[0].toUpperCase() + key.substring(1);
}
}
this.optionalCharacter(chars.$COLON);
let name: string = null !;
let expression: ASTWithSource|null = null;
if (isVar) {
let expression: ASTWithSource = null !;
if (keyIsVar) {
if (this.optionalOperator('=')) {
name = this.expectTemplateBindingKey();
} else {
name = '\$implicit';
}
} else if (this.peekKeywordAs()) {
const letStart = this.inputIndex;
this.advance(); // consume `as`
name = rawKey;
key = this.expectTemplateBindingKey(); // read local var name
isVar = true;
keyIsVar = true;
} else if (this.next !== EOF && !this.peekKeywordLet()) {
const start = this.inputIndex;
const ast = this.parsePipe();
const source = this.input.substring(start - this.offset, this.inputIndex - this.offset);
expression = new ASTWithSource(ast, source, this.location, this.errors);
}
bindings.push(new TemplateBinding(this.span(start), key, isVar, name, expression));
if (this.peekKeywordAs() && !isVar) {
bindings.push(new TemplateBinding(this.span(start), key, keyIsVar, name, expression));
if (this.peekKeywordAs() && !keyIsVar) {
const letStart = this.inputIndex;
this.advance(); // consume `as`
const letName = this.expectTemplateBindingKey(); // read local var name
@ -729,8 +736,7 @@ export class _ParseAST {
if (!this.optionalCharacter(chars.$SEMICOLON)) {
this.optionalCharacter(chars.$COMMA);
}
} while (this.index < this.tokens.length);
}
return new TemplateBindingParseResult(bindings, warnings, this.errors);
}

View File

@ -65,7 +65,6 @@ export class Identifiers {
static INJECTOR: o.ExternalReference = {name: 'INJECTOR', moduleName: CORE};
static Injector: o.ExternalReference = {name: 'Injector', moduleName: CORE};
static defineInjectable: o.ExternalReference = {name: 'defineInjectable', moduleName: CORE};
static InjectableDef: o.ExternalReference = {name: 'InjectableDef', moduleName: CORE};
static ViewEncapsulation: o.ExternalReference = {
name: 'ViewEncapsulation',
moduleName: CORE,

View File

@ -1,113 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectFlags} from './core';
import {Identifiers} from './identifiers';
import * as o from './output/output_ast';
import {R3DependencyMetadata, compileFactoryFunction} from './render3/r3_factory';
import {mapToMapExpression} from './render3/util';
export interface InjectableDef {
expression: o.Expression;
type: o.Type;
}
export interface R3InjectableMetadata {
name: string;
type: o.Expression;
providedIn: o.Expression;
useClass?: o.Expression;
useFactory?: o.Expression;
useExisting?: o.Expression;
useValue?: o.Expression;
deps?: R3DependencyMetadata[];
}
export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
let factory: o.Expression = o.NULL_EXPR;
function makeFn(ret: o.Expression): o.Expression {
return o.fn([], [new o.ReturnStatement(ret)], undefined, undefined, `${meta.name}_Factory`);
}
if (meta.useClass !== undefined || meta.useFactory !== undefined) {
// First, handle useClass and useFactory together, since both involve a similar call to
// `compileFactoryFunction`. Either dependencies are explicitly specified, in which case
// a factory function call is generated, or they're not specified and the calls are special-
// cased.
if (meta.deps !== undefined) {
// Either call `new meta.useClass(...)` or `meta.useFactory(...)`.
const fnOrClass: o.Expression = meta.useClass || meta.useFactory !;
// useNew: true if meta.useClass, false for meta.useFactory.
const useNew = meta.useClass !== undefined;
factory = compileFactoryFunction({
name: meta.name,
fnOrClass,
useNew,
injectFn: Identifiers.inject,
useOptionalParam: true,
deps: meta.deps,
});
} else if (meta.useClass !== undefined) {
// Special case for useClass where the factory from the class's ngInjectableDef is used.
if (meta.useClass.isEquivalent(meta.type)) {
// For the injectable compiler, useClass represents a foreign type that should be
// instantiated to satisfy construction of the given type. It's not valid to specify
// useClass === type, since the useClass type is expected to already be compiled.
throw new Error(
`useClass is the same as the type, but no deps specified, which is invalid.`);
}
factory =
makeFn(new o.ReadPropExpr(new o.ReadPropExpr(meta.useClass, 'ngInjectableDef'), 'factory')
.callFn([]));
} else if (meta.useFactory !== undefined) {
// Special case for useFactory where no arguments are passed.
factory = meta.useFactory.callFn([]);
} else {
// Can't happen - outer conditional guards against both useClass and useFactory being
// undefined.
throw new Error('Reached unreachable block in injectable compiler.');
}
} else if (meta.useValue !== undefined) {
// Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for
// client code because meta.useValue is an Expression which will be defined even if the actual
// value is undefined.
factory = makeFn(meta.useValue);
} else if (meta.useExisting !== undefined) {
// useExisting is an `inject` call on the existing token.
factory = makeFn(o.importExpr(Identifiers.inject).callFn([meta.useExisting]));
} else {
// A strict type is compiled according to useClass semantics, except the dependencies are
// required.
if (meta.deps === undefined) {
throw new Error(`Type compilation of an injectable requires dependencies.`);
}
factory = compileFactoryFunction({
name: meta.name,
fnOrClass: meta.type,
useNew: true,
injectFn: Identifiers.inject,
useOptionalParam: true,
deps: meta.deps,
});
}
const token = meta.type;
const providedIn = meta.providedIn;
const expression = o.importExpr(Identifiers.defineInjectable).callFn([mapToMapExpression(
{token, factory, providedIn})]);
const type = new o.ExpressionType(
o.importExpr(Identifiers.InjectableDef, [new o.ExpressionType(meta.type)]));
return {
expression, type,
};
}

View File

@ -49,7 +49,7 @@ export function replaceNgsp(value: string): string {
* whitespace removal. The default option for whitespace removal will be revisited in Angular 6
* and might be changed to "on" by default.
*/
export class WhitespaceVisitor implements html.Visitor {
class WhitespaceVisitor implements html.Visitor {
visitElement(element: html.Element, context: any): any {
if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
// don't descent into elements where we need to preserve whitespaces

View File

@ -312,9 +312,6 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
ctx.print(expr, `)`);
return null;
}
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): any {
throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
let varName = ast.name !;
if (ast.builtin != null) {

View File

@ -70,10 +70,6 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
ctx.println(stmt, `};`);
}
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): any {
throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string|null {
if (ast.builtin === o.BuiltinVar.This) {
ctx.print(ast, 'self');

View File

@ -279,21 +279,6 @@ export class ReadVarExpr extends Expression {
}
}
export class WrappedNodeExpr<T> extends Expression {
constructor(public node: T, type?: Type|null, sourceSpan?: ParseSourceSpan|null) {
super(type, sourceSpan);
}
isEquivalent(e: Expression): boolean {
return e instanceof WrappedNodeExpr && this.node === e.node;
}
isConstant() { return false; }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitWrappedNodeExpr(this, context);
}
}
export class WriteVarExpr extends Expression {
public value: Expression;
@ -737,7 +722,6 @@ export interface ExpressionVisitor {
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any;
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any;
visitCommaExpr(ast: CommaExpr, context: any): any;
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any;
}
export const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
@ -989,10 +973,6 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return this.transformExpr(ast, context); }
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any {
return this.transformExpr(ast, context);
}
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
return this.transformExpr(
new WriteVarExpr(
@ -1219,7 +1199,6 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
}
visitArrayType(type: ArrayType, context: any): any { return this.visitType(type, context); }
visitMapType(type: MapType, context: any): any { return this.visitType(type, context); }
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { return ast; }
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
return this.visitExpression(ast, context);
}

View File

@ -114,9 +114,6 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
}
throw new Error(`Not declared variable ${expr.name}`);
}
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: _ExecutionContext): never {
throw new Error('Cannot interpret a WrappedNodeExpr.');
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: _ExecutionContext): any {
let varName = ast.name !;
if (ast.builtin != null) {

View File

@ -68,12 +68,15 @@ export class JitEmitterVisitor extends AbstractJsEmitterVisitor {
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
return null;
const value = this.reflector.resolveExternalReference(ast.value);
let id = this._evalArgValues.indexOf(value);
if (id === -1) {
id = this._evalArgValues.length;
this._evalArgValues.push(value);
const name = identifierName({reference: value}) || 'val';
this._evalArgNames.push(`jit_${name}_${id}`);
}
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): any {
this._emitReferenceToExternal(ast, ast.node, ctx);
ctx.print(ast, this._evalArgNames[id]);
return null;
}
@ -97,16 +100,4 @@ export class JitEmitterVisitor extends AbstractJsEmitterVisitor {
}
return super.visitDeclareClassStmt(stmt, ctx);
}
private _emitReferenceToExternal(ast: o.Expression, value: any, ctx: EmitterVisitorContext):
void {
let id = this._evalArgValues.indexOf(value);
if (id === -1) {
id = this._evalArgValues.length;
this._evalArgValues.push(value);
const name = identifierName({reference: value}) || 'val';
this._evalArgNames.push(`jit_${name}_${id}`);
}
ctx.print(ast, this._evalArgNames[id]);
}
}

View File

@ -169,10 +169,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
return null;
}
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): never {
throw new Error('Cannot visit a WrappedNodeExpr when outputting Typescript.');
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ctx.print(ast, `(<`);
ast.type !.visitType(this, ctx);

View File

@ -1,223 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SecurityContext} from '../core';
import {AST, BindingType, BoundElementProperty, ParsedEvent, ParsedEventType} from '../expression_parser/ast';
import {ParseSourceSpan} from '../parse_util';
export interface Node {
sourceSpan: ParseSourceSpan;
visit<Result>(visitor: Visitor<Result>): Result;
}
export class Text implements Node {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitText(this); }
}
export class BoundText implements Node {
constructor(public value: AST, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundText(this); }
}
export class TextAttribute implements Node {
constructor(
public name: string, public value: string, public sourceSpan: ParseSourceSpan,
public valueSpan?: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitTextAttribute(this); }
}
export class BoundAttribute implements Node {
constructor(
public name: string, public type: BindingType, public securityContext: SecurityContext,
public value: AST, public unit: string|null, public sourceSpan: ParseSourceSpan) {}
static fromBoundElementProperty(prop: BoundElementProperty) {
return new BoundAttribute(
prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan);
}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundAttribute(this); }
}
export class BoundEvent implements Node {
constructor(
public name: string, public handler: AST, public target: string|null,
public phase: string|null, public sourceSpan: ParseSourceSpan) {}
static fromParsedEvent(event: ParsedEvent) {
const target: string|null = event.type === ParsedEventType.Regular ? event.targetOrPhase : null;
const phase: string|null =
event.type === ParsedEventType.Animation ? event.targetOrPhase : null;
return new BoundEvent(event.name, event.handler, target, phase, event.sourceSpan);
}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundEvent(this); }
}
export class Element implements Node {
constructor(
public name: string, public attributes: TextAttribute[], public inputs: BoundAttribute[],
public outputs: BoundEvent[], public children: Node[], public references: Reference[],
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null,
public endSourceSpan: ParseSourceSpan|null) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitElement(this); }
}
export class Template implements Node {
constructor(
public attributes: TextAttribute[], public inputs: BoundAttribute[], public children: Node[],
public references: Reference[], public variables: Variable[],
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null,
public endSourceSpan: ParseSourceSpan|null) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitTemplate(this); }
}
export class Content implements Node {
constructor(
public selectorIndex: number, public attributes: TextAttribute[],
public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitContent(this); }
}
export class Variable implements Node {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitVariable(this); }
}
export class Reference implements Node {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitReference(this); }
}
export interface Visitor<Result = any> {
// Returning a truthy value from `visit()` will prevent `visitAll()` from the call to the typed
// method and result returned will become the result included in `visitAll()`s result array.
visit?(node: Node): Result;
visitElement(element: Element): Result;
visitTemplate(template: Template): Result;
visitContent(content: Content): Result;
visitVariable(variable: Variable): Result;
visitReference(reference: Reference): Result;
visitTextAttribute(attribute: TextAttribute): Result;
visitBoundAttribute(attribute: BoundAttribute): Result;
visitBoundEvent(attribute: BoundEvent): Result;
visitText(text: Text): Result;
visitBoundText(text: BoundText): Result;
}
export class NullVisitor implements Visitor<void> {
visitElement(element: Element): void {}
visitTemplate(template: Template): void {}
visitContent(content: Content): void {}
visitVariable(variable: Variable): void {}
visitReference(reference: Reference): void {}
visitTextAttribute(attribute: TextAttribute): void {}
visitBoundAttribute(attribute: BoundAttribute): void {}
visitBoundEvent(attribute: BoundEvent): void {}
visitText(text: Text): void {}
visitBoundText(text: BoundText): void {}
}
export class RecursiveVisitor implements Visitor<void> {
visitElement(element: Element): void {
visitAll(this, element.attributes);
visitAll(this, element.children);
visitAll(this, element.references);
}
visitTemplate(template: Template): void {
visitAll(this, template.attributes);
visitAll(this, template.children);
visitAll(this, template.references);
visitAll(this, template.variables);
}
visitContent(content: Content): void {}
visitVariable(variable: Variable): void {}
visitReference(reference: Reference): void {}
visitTextAttribute(attribute: TextAttribute): void {}
visitBoundAttribute(attribute: BoundAttribute): void {}
visitBoundEvent(attribute: BoundEvent): void {}
visitText(text: Text): void {}
visitBoundText(text: BoundText): void {}
}
export class TransformVisitor implements Visitor<Node> {
visitElement(element: Element): Node {
const newAttributes = transformAll(this, element.attributes);
const newInputs = transformAll(this, element.inputs);
const newOutputs = transformAll(this, element.outputs);
const newChildren = transformAll(this, element.children);
const newReferences = transformAll(this, element.references);
if (newAttributes != element.attributes || newInputs != element.inputs ||
newOutputs != element.outputs || newChildren != element.children ||
newReferences != element.references) {
return new Element(
element.name, newAttributes, newInputs, newOutputs, newChildren, newReferences,
element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
}
return element;
}
visitTemplate(template: Template): Node {
const newAttributes = transformAll(this, template.attributes);
const newInputs = transformAll(this, template.inputs);
const newChildren = transformAll(this, template.children);
const newReferences = transformAll(this, template.references);
const newVariables = transformAll(this, template.variables);
if (newAttributes != template.attributes || newInputs != template.inputs ||
newChildren != template.children || newVariables != template.variables ||
newReferences != template.references) {
return new Template(
newAttributes, newInputs, newChildren, newReferences, newVariables, template.sourceSpan,
template.startSourceSpan, template.endSourceSpan);
}
return template;
}
visitContent(content: Content): Node { return content; }
visitVariable(variable: Variable): Node { return variable; }
visitReference(reference: Reference): Node { return reference; }
visitTextAttribute(attribute: TextAttribute): Node { return attribute; }
visitBoundAttribute(attribute: BoundAttribute): Node { return attribute; }
visitBoundEvent(attribute: BoundEvent): Node { return attribute; }
visitText(text: Text): Node { return text; }
visitBoundText(text: BoundText): Node { return text; }
}
export function visitAll<Result>(visitor: Visitor<Result>, nodes: Node[]): Result[] {
const result: Result[] = [];
if (visitor.visit) {
for (const node of nodes) {
const newNode = visitor.visit(node) || node.visit(visitor);
}
} else {
for (const node of nodes) {
const newNode = node.visit(visitor);
if (newNode) {
result.push(newNode);
}
}
}
return result;
}
export function transformAll<Result extends Node>(
visitor: Visitor<Node>, nodes: Result[]): Result[] {
const result: Result[] = [];
let changed = false;
for (const node of nodes) {
const newNode = node.visit(visitor);
if (newNode) {
result.push(newNode as Result);
}
changed = changed || newNode != node;
}
return changed ? result : nodes;
}

View File

@ -0,0 +1,106 @@
/**
* @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 {StaticReflector} from '../aot/static_reflector';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileTypeMetadata} from '../compile_metadata';
import {DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Lexer, ParseError, Parser} from '../compiler';
import {CompileMetadataResolver} from '../metadata_resolver';
import * as o from '../output/output_ast';
import {BindingParser} from '../template_parser/binding_parser';
import {TemplateAst} from '../template_parser/template_ast';
import {OutputContext} from '../util';
import {compilePipe} from './r3_pipe_compiler';
import {BUILD_OPTIMIZER_REMOVE, OutputMode} from './r3_types';
import {compileComponent, compileDirective} from './r3_view_compiler';
export const enum ModuleKind {
Renderer2,
Renderer3,
}
/**
* Produce the back-patching function for the given module to the output context.
*/
export function compileModuleBackPatch(
outputCtx: OutputContext, name: string, module: CompileNgModuleMetadata, kind: ModuleKind,
backPatchReferenceOf: (module: CompileTypeMetadata) => o.Expression,
parseTemplate: (
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
directiveIdentifiers: CompileIdentifierMetadata[]) => {
template: TemplateAst[],
pipes: CompilePipeSummary[]
},
reflector: StaticReflector, resolver: CompileMetadataResolver) {
const imports: o.Statement[] = [];
let statements: o.Statement[] = [];
// Call dependent back patching
for (const importedModule of module.importedModules) {
const importBackPatchFunction = backPatchReferenceOf(importedModule.type);
// e.g. // @BUILD_OPTIMIZER_REMOVE
imports.push(new o.CommentStmt(BUILD_OPTIMIZER_REMOVE));
// e.g. ngBackPatch_some_other_module_Module();
imports.push(importBackPatchFunction.callFn([]).toStmt());
}
// The local output context allows collecting the back-patch statements that
// are generated by the various compilers which allows putting placing them
// into the body of a function instead of at global scope.
const localCtx: OutputContext = {
statements,
constantPool: outputCtx.constantPool,
genFilePath: outputCtx.genFilePath,
importExpr: outputCtx.importExpr
};
// e.g. export function ngBackPatch_some_module_Lib1Module()
if (kind === ModuleKind.Renderer2) {
// For all Renderer2 modules generate back-patching code for all the components, directives,
// pipes, and injectables as well as the injector def for the module itself.
const expressionParser = new Parser(new Lexer());
const elementSchemaRegistry = new DomElementSchemaRegistry();
const errors: ParseError[] = [];
const hostBindingParser = new BindingParser(
expressionParser, DEFAULT_INTERPOLATION_CONFIG, elementSchemaRegistry, [], errors);
// Back-patch all declared directive and components
for (const declaredDirective of module.declaredDirectives) {
const declaredDirectiveMetadata = resolver.getDirectiveMetadata(declaredDirective.reference);
if (declaredDirectiveMetadata.isComponent) {
const {template: parsedTemplate, pipes: parsedPipes} =
parseTemplate(declaredDirectiveMetadata, module, module.transitiveModule.directives);
compileComponent(
localCtx, declaredDirectiveMetadata, parsedPipes, parsedTemplate, reflector,
hostBindingParser, OutputMode.BackPatch);
} else {
compileDirective(
localCtx, declaredDirectiveMetadata, reflector, hostBindingParser,
OutputMode.BackPatch);
}
}
// Back-patch all pipes declared in the module.
for (const pipeType of module.declaredPipes) {
const pipeMetadata = resolver.getPipeMetadata(pipeType.reference);
if (pipeMetadata) {
compilePipe(localCtx, pipeMetadata, reflector, OutputMode.BackPatch);
}
}
if (errors.length) {
throw new Error(errors.map(e => e.toString()).join('\n'));
}
}
outputCtx.statements.push(new o.DeclareFunctionStmt(
name, [], [...imports, ...statements], o.INFERRED_TYPE, [o.StmtModifier.Exported]));
}

View File

@ -1,288 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {StaticSymbol} from '../aot/static_symbol';
import {CompileTypeMetadata, tokenReference} from '../compile_metadata';
import {CompileReflector} from '../compile_reflector';
import {InjectFlags} from '../core';
import {Identifiers} from '../identifiers';
import * as o from '../output/output_ast';
import {Identifiers as R3} from '../render3/r3_identifiers';
import {OutputContext} from '../util';
import {unsupported} from './view/util';
/**
* Metadata required by the factory generator to generate a `factory` function for a type.
*/
export interface R3FactoryMetadata {
/**
* String name of the type being generated (used to name the factory function).
*/
name: string;
/**
* An expression representing the function (or constructor) which will instantiate the requested
* type.
*
* This could be a reference to a constructor type, or to a user-defined factory function. The
* `useNew` property determines whether it will be called as a constructor or not.
*/
fnOrClass: o.Expression;
/**
* Regardless of whether `fnOrClass` is a constructor function or a user-defined factory, it
* may have 0 or more parameters, which will be injected according to the `R3DependencyMetadata`
* for those parameters.
*/
deps: R3DependencyMetadata[];
/**
* Whether to interpret `fnOrClass` as a constructor function (`useNew: true`) or as a factory
* (`useNew: false`).
*/
useNew: boolean;
/**
* An expression for the function which will be used to inject dependencies. The API of this
* function could be different, and other options control how it will be invoked.
*/
injectFn: o.ExternalReference;
/**
* Whether the `injectFn` given above accepts a 2nd parameter indicating the default value to
* be used to resolve missing @Optional dependencies.
*
* If the optional parameter is used, injectFn for an optional dependency will be invoked as:
* `injectFn(token, null, flags)`.
*
* If it's not used, injectFn for an optional dependency will be invoked as:
* `injectFn(token, flags)`. The Optional flag will indicate that injectFn should select a default
* value if it cannot satisfy the injection request for the token.
*/
useOptionalParam: boolean;
/**
* If present, the return of the factory function will be an array with the injected value in the
* 0th position and the extra results included in subsequent positions.
*
* Occasionally APIs want to construct additional values when the factory function is called. The
* paradigm there is to have the factory function return an array, with the DI-created value as
* well as other values. Specifying `extraResults` enables this functionality.
*/
extraResults?: o.Expression[];
}
/**
* Resolved type of a dependency.
*
* Occasionally, dependencies will have special significance which is known statically. In that
* case the `R3ResolvedDependencyType` informs the factory generator that a particular dependency
* should be generated specially (usually by calling a special injection function instead of the
* standard one).
*/
export enum R3ResolvedDependencyType {
/**
* A normal token dependency.
*/
Token = 0,
/**
* The dependency is for an attribute.
*
* The token expression is a string representing the attribute name.
*/
Attribute = 1,
/**
* The dependency is for the `Injector` type itself.
*/
Injector = 2,
/**
* The dependency is for `ElementRef`.
*/
ElementRef = 3,
/**
* The dependency is for `TemplateRef`.
*/
TemplateRef = 4,
/**
* The dependency is for `ViewContainerRef`.
*/
ViewContainerRef = 5,
}
/**
* Metadata representing a single dependency to be injected into a constructor or function call.
*/
export interface R3DependencyMetadata {
/**
* An expression representing the token or value to be injected.
*/
token: o.Expression;
/**
* An enum indicating whether this dependency has special meaning to Angular and needs to be
* injected specially.
*/
resolved: R3ResolvedDependencyType;
/**
* Whether the dependency has an @Host qualifier.
*/
host: boolean;
/**
* Whether the dependency has an @Optional qualifier.
*/
optional: boolean;
/**
* Whether the dependency has an @Self qualifier.
*/
self: boolean;
/**
* Whether the dependency has an @SkipSelf qualifier.
*/
skipSelf: boolean;
}
/**
* Construct a factory function expression for the given `R3FactoryMetadata`.
*/
export function compileFactoryFunction(meta: R3FactoryMetadata): o.Expression {
// Each dependency becomes an invocation of an inject*() function.
const args =
meta.deps.map(dep => compileInjectDependency(dep, meta.injectFn, meta.useOptionalParam));
// The overall result depends on whether this is construction or function invocation.
const expr = meta.useNew ? new o.InstantiateExpr(meta.fnOrClass, args) :
new o.InvokeFunctionExpr(meta.fnOrClass, args);
// If `extraResults` is specified, then the result is an array consisting of the instantiated
// value plus any extra results.
const retExpr =
meta.extraResults === undefined ? expr : o.literalArr([expr, ...meta.extraResults]);
return o.fn(
[], [new o.ReturnStatement(retExpr)], o.INFERRED_TYPE, undefined, `${meta.name}_Factory`);
}
function compileInjectDependency(
dep: R3DependencyMetadata, injectFn: o.ExternalReference,
useOptionalParam: boolean): o.Expression {
// Interpret the dependency according to its resolved type.
switch (dep.resolved) {
case R3ResolvedDependencyType.Token:
case R3ResolvedDependencyType.Injector: {
// Build up the injection flags according to the metadata.
const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) |
(dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) |
(dep.optional ? InjectFlags.Optional : 0);
// Determine the token used for injection. In almost all cases this is the given token, but
// if the dependency is resolved to the `Injector` then the special `INJECTOR` token is used
// instead.
let token: o.Expression = dep.token;
if (dep.resolved === R3ResolvedDependencyType.Injector) {
token = o.importExpr(Identifiers.INJECTOR);
}
// Build up the arguments to the injectFn call.
const injectArgs = [dep.token];
// If this dependency is optional or otherwise has non-default flags, then additional
// parameters describing how to inject the dependency must be passed to the inject function
// that's being used.
if (flags !== InjectFlags.Default || dep.optional) {
// Either the dependency is optional, or non-default flags are in use. Either of these cases
// necessitates adding an argument for the default value if such an argument is required
// by the inject function (useOptionalParam === true).
if (useOptionalParam) {
// The inject function requires a default value parameter.
injectArgs.push(dep.optional ? o.NULL_EXPR : o.literal(undefined));
}
// The last parameter is always the InjectFlags, which only need to be specified if they're
// non-default.
if (flags !== InjectFlags.Default) {
injectArgs.push(o.literal(flags));
}
}
return o.importExpr(injectFn).callFn(injectArgs);
}
case R3ResolvedDependencyType.Attribute:
// In the case of attributes, the attribute name in question is given as the token.
return o.importExpr(R3.injectAttribute).callFn([dep.token]);
case R3ResolvedDependencyType.ElementRef:
return o.importExpr(R3.injectElementRef).callFn([]);
case R3ResolvedDependencyType.TemplateRef:
return o.importExpr(R3.injectTemplateRef).callFn([]);
case R3ResolvedDependencyType.ViewContainerRef:
return o.importExpr(R3.injectViewContainerRef).callFn([]);
default:
return unsupported(
`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);
}
}
/**
* A helper function useful for extracting `R3DependencyMetadata` from a Render2
* `CompileTypeMetadata` instance.
*/
export function dependenciesFromGlobalMetadata(
type: CompileTypeMetadata, outputCtx: OutputContext,
reflector: CompileReflector): R3DependencyMetadata[] {
// Use the `CompileReflector` to look up references to some well-known Angular types. These will
// be compared with the token to statically determine whether the token has significance to
// Angular, and set the correct `R3ResolvedDependencyType` as a result.
const elementRef = reflector.resolveExternalReference(Identifiers.ElementRef);
const templateRef = reflector.resolveExternalReference(Identifiers.TemplateRef);
const viewContainerRef = reflector.resolveExternalReference(Identifiers.ViewContainerRef);
const injectorRef = reflector.resolveExternalReference(Identifiers.Injector);
// Iterate through the type's DI dependencies and produce `R3DependencyMetadata` for each of them.
const deps: R3DependencyMetadata[] = [];
for (let dependency of type.diDeps) {
if (dependency.token) {
const tokenRef = tokenReference(dependency.token);
let resolved: R3ResolvedDependencyType = R3ResolvedDependencyType.Token;
if (tokenRef === elementRef) {
resolved = R3ResolvedDependencyType.ElementRef;
} else if (tokenRef === templateRef) {
resolved = R3ResolvedDependencyType.TemplateRef;
} else if (tokenRef === viewContainerRef) {
resolved = R3ResolvedDependencyType.ViewContainerRef;
} else if (tokenRef === injectorRef) {
resolved = R3ResolvedDependencyType.Injector;
} else if (dependency.isAttribute) {
resolved = R3ResolvedDependencyType.Attribute;
}
// In the case of most dependencies, the token will be a reference to a type. Sometimes,
// however, it can be a string, in the case of older Angular code or @Attribute injection.
const token =
tokenRef instanceof StaticSymbol ? outputCtx.importExpr(tokenRef) : o.literal(tokenRef);
// Construct the dependency.
deps.push({
token,
resolved,
host: !!dependency.isHost,
optional: !!dependency.isOptional,
self: !!dependency.isSelf,
skipSelf: !!dependency.isSkipSelf,
});
} else {
unsupported('dependency without a token');
}
}
return deps;
}

View File

@ -17,15 +17,7 @@ export class Identifiers {
static PATCH_DEPS = 'patchedDeps';
/* Instructions */
static namespaceHTML: o.ExternalReference = {name: NH', moduleName: CORE};
static namespaceMathML: o.ExternalReference = {name: 'ɵNM', moduleName: CORE};
static namespaceSVG: o.ExternalReference = {name: 'ɵNS', moduleName: CORE};
static element: o.ExternalReference = {name: 'ɵEe', moduleName: CORE};
static elementStart: o.ExternalReference = {name: 'ɵE', moduleName: CORE};
static createElement: o.ExternalReference = {name: E', moduleName: CORE};
static elementEnd: o.ExternalReference = {name: 'ɵe', moduleName: CORE};
@ -33,19 +25,21 @@ export class Identifiers {
static elementAttribute: o.ExternalReference = {name: 'ɵa', moduleName: CORE};
static elementClass: o.ExternalReference = {name: 'ɵk', moduleName: CORE};
static elementClassNamed: o.ExternalReference = {name: 'ɵkn', moduleName: CORE};
static elementStyle: o.ExternalReference = {name: 'ɵs', moduleName: CORE};
static elementStyleNamed: o.ExternalReference = {name: 'ɵsn', moduleName: CORE};
static containerCreate: o.ExternalReference = {name: 'ɵC', moduleName: CORE};
static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};
static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
static textBinding: o.ExternalReference = {name: t', moduleName: CORE};
static directiveInput: o.ExternalReference = {name: i', moduleName: CORE};
static textCreateBound: o.ExternalReference = {name: 'ɵt', moduleName: CORE};
static bind: o.ExternalReference = {name: 'ɵb', moduleName: CORE};
@ -77,14 +71,15 @@ export class Identifiers {
static pipeBindV: o.ExternalReference = {name: 'ɵpbV', moduleName: CORE};
static load: o.ExternalReference = {name: 'ɵld', moduleName: CORE};
static loadDirective: o.ExternalReference = {name: 'ɵd', moduleName: CORE};
static pipe: o.ExternalReference = {name: 'ɵPp', moduleName: CORE};
static projection: o.ExternalReference = {name: 'ɵP', moduleName: CORE};
static projectionDef: o.ExternalReference = {name: 'ɵpD', moduleName: CORE};
static inject: o.ExternalReference = {name: 'inject', moduleName: CORE};
static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};
static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};
static injectAttribute: o.ExternalReference = {name: 'ɵinjectAttribute', moduleName: CORE};
@ -99,33 +94,16 @@ export class Identifiers {
static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};
static ComponentDef: o.ExternalReference = {
name: 'ComponentDef',
moduleName: CORE,
};
static defineDirective: o.ExternalReference = {
name: 'ɵdefineDirective',
moduleName: CORE,
};
static DirectiveDef: o.ExternalReference = {
name: 'DirectiveDef',
moduleName: CORE,
};
static defineInjector: o.ExternalReference = {
name: 'defineInjector',
moduleName: CORE,
};
static NgModuleDef: o.ExternalReference = {
name: 'NgModuleDef',
moduleName: CORE,
};
static defineNgModule: o.ExternalReference = {name: 'ɵdefineNgModule', moduleName: CORE};
static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE};
static query: o.ExternalReference = {name: 'ɵQ', moduleName: CORE};
@ -134,7 +112,4 @@ export class Identifiers {
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};
static listener: o.ExternalReference = {name: 'ɵL', moduleName: CORE};
// Reserve slots for pure functions
static reserveSlots: o.ExternalReference = {name: 'ɵrS', moduleName: CORE};
}

View File

@ -1,74 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileReflector} from '../compile_reflector';
import {ConstantPool} from '../constant_pool';
import * as o from '../output/output_ast';
import {jitStatements} from '../output/output_jit';
/**
* Implementation of `CompileReflector` which resolves references to @angular/core
* symbols at runtime, according to a consumer-provided mapping.
*
* Only supports `resolveExternalReference`, all other methods throw.
*/
class R3JitReflector implements CompileReflector {
constructor(private context: {[key: string]: any}) {}
resolveExternalReference(ref: o.ExternalReference): any {
// This reflector only handles @angular/core imports.
if (ref.moduleName !== '@angular/core') {
throw new Error(
`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
}
if (!this.context.hasOwnProperty(ref.name !)) {
throw new Error(`No value provided for @angular/core symbol '${ref.name!}'.`);
}
return this.context[ref.name !];
}
parameters(typeOrFunc: any): any[][] { throw new Error('Not implemented.'); }
annotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); }
shallowAnnotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); }
tryAnnotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); }
propMetadata(typeOrFunc: any): {[key: string]: any[];} { throw new Error('Not implemented.'); }
hasLifecycleHook(type: any, lcProperty: string): boolean { throw new Error('Not implemented.'); }
guards(typeOrFunc: any): {[key: string]: any;} { throw new Error('Not implemented.'); }
componentModuleUrl(type: any, cmpMetadata: any): string { throw new Error('Not implemented.'); }
}
/**
* JIT compiles an expression and returns the result of executing that expression.
*
* @param def the definition which will be compiled and executed to get the value to patch
* @param context an object map of @angular/core symbol names to symbols which will be available in
* the context of the compiled expression
* @param sourceUrl a URL to use for the source map of the compiled expression
* @param constantPool an optional `ConstantPool` which contains constants used in the expression
*/
export function jitExpression(
def: o.Expression, context: {[key: string]: any}, sourceUrl: string,
constantPool?: ConstantPool): any {
// The ConstantPool may contain Statements which declare variables used in the final expression.
// Therefore, its statements need to precede the actual JIT operation. The final statement is a
// declaration of $def which is set to the expression being compiled.
const statements: o.Statement[] = [
...(constantPool !== undefined ? constantPool.statements : []),
new o.DeclareVarStmt('$def', def, undefined, [o.StmtModifier.Exported]),
];
const res = jitStatements(sourceUrl, statements, new R3JitReflector(context), false);
return res['$def'];
}

View File

@ -14,75 +14,22 @@ import * as o from '../output/output_ast';
import {OutputContext} from '../util';
import {Identifiers as R3} from './r3_identifiers';
import {convertMetaToOutput, mapToMapExpression} from './util';
export interface R3NgModuleDef {
expression: o.Expression;
type: o.Type;
additionalStatements: o.Statement[];
const EMPTY_ARRAY = o.literalArr([]);
function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression {
if (Array.isArray(meta)) {
return o.literalArr(meta.map(entry => convertMetaToOutput(entry, ctx)));
} else if (meta instanceof StaticSymbol) {
return ctx.importExpr(meta);
} else if (meta == null) {
return o.literal(meta);
} else {
throw new Error(`Internal error: Unsupported or unknown metadata: ${meta}`);
}
}
/**
* Metadata required by the module compiler to generate a `ngModuleDef` for a type.
*/
export interface R3NgModuleMetadata {
/**
* An expression representing the module type being compiled.
*/
type: o.Expression;
/**
* An array of expressions representing the bootstrap components specified by the module.
*/
bootstrap: o.Expression[];
/**
* An array of expressions representing the directives and pipes declared by the module.
*/
declarations: o.Expression[];
/**
* An array of expressions representing the imports of the module.
*/
imports: o.Expression[];
/**
* An array of expressions representing the exports of the module.
*/
exports: o.Expression[];
/**
* Whether to emit the selector scope values (declarations, imports, exports) inline into the
* module definition, or to generate additional statements which patch them on. Inline emission
* does not allow components to be tree-shaken, but is useful for JIT mode.
*/
emitInline: boolean;
}
/**
* Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
*/
export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
const {type: moduleType, bootstrap, declarations, imports, exports} = meta;
const expression = o.importExpr(R3.defineNgModule).callFn([mapToMapExpression({
type: moduleType,
bootstrap: o.literalArr(bootstrap),
declarations: o.literalArr(declarations),
imports: o.literalArr(imports),
exports: o.literalArr(exports),
})]);
const type = new o.ExpressionType(o.importExpr(R3.NgModuleDef, [
new o.ExpressionType(moduleType), new o.ExpressionType(o.literalArr(declarations)),
new o.ExpressionType(o.literalArr(imports)), new o.ExpressionType(o.literalArr(exports))
]));
const additionalStatements: o.Statement[] = [];
return {expression, type, additionalStatements};
}
// TODO(alxhub): integrate this with `compileNgModule`. Currently the two are separate operations.
export function compileNgModuleFromRender2(
export function compileNgModule(
ctx: OutputContext, ngModule: CompileShallowModuleMetadata,
injectableCompiler: InjectableCompiler): void {
const className = identifierName(ngModule.type) !;
@ -111,8 +58,3 @@ export function compileNgModuleFromRender2(
/* constructorMethod */ new o.ClassMethod(null, [], []),
/* methods */[]));
}
function accessExportScope(module: o.Expression): o.Expression {
const selectorScope = new o.ReadPropExpr(module, 'ngModuleDef');
return new o.ReadPropExpr(selectorScope, 'exported');
}

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileNgModuleMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
import {StaticReflector} from '../aot/static_reflector';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileTypeMetadata, identifierName} from '../compile_metadata';
import {CompileMetadataResolver} from '../metadata_resolver';
import * as o from '../output/output_ast';
import {OutputContext} from '../util';

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