Compare commits

..

207 Commits

Author SHA1 Message Date
4ce70b9edf release: cut the vv7.0.0-beta.2 release 2018-08-15 15:35:41 -07:00
1f1103913a refactor(ivy): cleanup the public API for core/testing (#25492)
PR Close #25492
2018-08-15 09:53:17 -07:00
01ec5fd6b0 fix(service-worker): Cache-Control: no-cache on assets breaks service worker (#25408)
At the moment `cacheAge` can we undefined when having `Cache-Control` set to `no-cache` due the mapping method in `needToRevalidate`

Closes #25442

PR Close #25408
2018-08-14 16:40:15 -07:00
be2cf4dfd6 docs(core): Correct spelling error in directives docs (#25377)
Link to life-cycle hooks was spelt as "life-cycle hoooks".
PR Close #25377
2018-08-14 16:39:33 -07:00
eeb81b9370 docs: enable debug tools with current versions of Angular (#25361)
Updating code snippet in docs that shows how to enable debug tools.
PR Close #25361
2018-08-14 16:38:26 -07:00
b5f354f2fb build(bazel): update to rules_typescript 0.16.0 & update to tagged rules_webtesting 0.2.1 (#25433)
PR Close #25433
2018-08-14 16:37:15 -07:00
a0a29fdd27 feat(ivy): Add AOT handling for bare classes with Input and Output decorators (#25367)
PR Close #25367
2018-08-14 16:36:18 -07:00
26066f282e fix(ivy): consider exported modules from other compilation scopes (#25425)
PR Close #25425
2018-08-14 14:23:24 -07:00
b40c437379 fix(ivy): ensure factory statements are emitted correctly (#25425)
A small bug caused base factory variable statements for @Component to
not be emitted properly. At the same time as this is fixed, those
statements are now emitted as const.

PR Close #25425
2018-08-14 14:23:24 -07:00
82e2725154 fix(ivy): handle the case where no base factory is found (#25425)
When an Angular decorated class is inherited, it might be the case that
the entire inheritance chain actually has no constructor defined. In
that event, a factory which simply instantiates the type without any
arguments should be used.

PR Close #25425
2018-08-14 14:23:24 -07:00
c13901f4c8 build(docs-infra): remove stability labels from API docs (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
6a2130117f build(docs-infra): clean up API package template (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
4e45f2c481 build(docs-infra): include packages in API template breadcrumbs (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
78f477652e build(docs-infra): change breadcrumb delimiter to > (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
98f336c0fb docs: add api doc for programmatic animation classes (#24668)
PR Close #24668
2018-08-14 13:15:27 -07:00
9117fa199c test(ivy): activate local references canonical spec (#25462)
PR Close #25462
2018-08-14 12:01:55 -07:00
0c4209f4b9 refactor(ivy): harmonize container and element / elementContainer signatures (#25458)
PR Close #25458
2018-08-14 12:01:23 -07:00
14ac7ad6b4 feat(ivy): implement TestBed (#25369)
PR Close #25369
2018-08-14 11:58:47 -07:00
85106375ac refactor(ivy): misc cleanup (#25369)
PR Close #25369
2018-08-14 11:58:47 -07:00
ecb5dc03f9 docs: add Accelebrate to resources (#23204)
PR Close #23204
2018-08-14 11:58:04 -07:00
bbb3f8fa60 docs(ivy): add better documentation around debugging ivy tests (#25432)
PR Close #25432
2018-08-13 21:44:55 -07:00
09711507f9 ci: github robot should enforce that all requested reviews are submitted (#25336)
See docs in the diff for justification.
PR Close #25336
2018-08-13 21:39:04 -07:00
3ac7070009 docs: fix typo in Architecture overview page (#25438)
PR Close #25438
2018-08-13 21:38:22 -07:00
c869b143c6 build: drop unused re-export of json_marshal. (#25449)
This appears to be unused, and will be removed from rules_typescript now
that `struct.to_json` can take `dicts`.

PR Close #25449
2018-08-13 21:37:41 -07:00
e0314b5d90 docs: add link to Yarn in README (#24856)
Remove the code markdown. It is not code, it is a name.

PR Close #24856
2018-08-13 21:36:13 -07:00
1bb30147d3 fix(ivy): add typeof guard around ngDevMode for instances where we cannot set it in Node (#25475)
PR Close #25475
2018-08-13 20:50:53 -07:00
fb2c5241fc fix(bazel): correct type concatenated to devmode_js (#25467)
PR Close #25467
2018-08-13 17:27:45 -07:00
97d8b5ed88 fix(ivy): reordering how root is acquired (#25470)
PR Close #25470
2018-08-13 17:25:47 -07:00
4a4d6fb0e6 test(ivy): compiler compliance tests should support ? (#25435)
PR Close #25435
2018-08-10 17:12:18 -07:00
2016afdbff fix(ivy): remove ivy dependency on ViewEngine's resolveRendererType2 (#25396)
PR Close #25396
2018-08-10 13:32:04 -07:00
c8c1aa7fc0 docs(aio): add async validation chapter (#25189)
Closes #22881

PR Close #25189
2018-08-10 09:14:25 -07:00
409860a4da fix(ivy): queries should be restored when view changes (#25415)
PR Close #25415
2018-08-10 09:13:16 -07:00
2b128a47b9 refactor(ivy): queries should not rely on LNode (#25415)
PR Close #25415
2018-08-10 09:13:16 -07:00
209cc7e1b0 docs: fix typo in testing guide (closes #25400) (#25418)
PR Close #25418
2018-08-10 09:11:35 -07:00
2d759927d4 feat(ivy): add support for ng-container in the compiler (#25383)
PR Close #25383
2018-08-09 13:13:04 -07:00
7058072ff6 feat(ivy): enable .ngfactory.js generation in g3 only (#25392)
This turns on generation of ngfactory.js files when compiling in Ivy
mode in g3. They're not turned on for Bazel users as there appears to
be a strange interaction with the way our tests run in Bazel mode.

PR Close #25392
2018-08-09 09:58:13 -07:00
33fd7e0784 fix(ivy): export NgModuleFactory via r3_symbols for core factories (#25392)
When @angular/core is compiled by ngtsc, a factory file is generated
for ApplicationModule, that is currently invalid because r3_symbols
does not export NgModuleFactory. This change fixes that issue and
ensures the generated ngfactory file for @angular/core is valid.

PR Close #25392
2018-08-09 09:58:13 -07:00
2befc65777 fix(ivy): ngtsc should pay attention to declaration order (#25392)
When generating the 'directives:' property of ngComponentDef, ngtsc
needs to be conscious of declaration order. If a directive being
written into the array is declarated after the component currently
being compiled, then the entire directives array needs to be wrapped
in a closure.

This commit fixes ngtsc to pay attention to such ordering issues
within directives arrays.

PR Close #25392
2018-08-09 09:58:13 -07:00
6f085f8610 fix(ivy): add missing exportAs field to ngDirectiveDef (#25392)
This commit includes the missing exportAs field from @Directive and
propagates it into the ngDirectiveDef.

PR Close #25392
2018-08-09 09:58:13 -07:00
5be186035f feat(ivy): enable inheritance of factory functions in definitions (#25392)
This commit creates an API for factory functions which allows them
to be inherited from one another. To do so, it differentiates between
the factory function as a wrapper for a constructor and the factory
function in ngInjectableDefs which is determined by a default
provider.

The new form is:

factory: (t?) => new (t || SomeType)(inject(Dep1), inject(Dep2))

The 't' parameter allows for constructor inheritance. A subclass with
no declared constructor inherits its constructor from the superclass.
With the 't' parameter, a subclass can call the superclass' factory
function and use it to create an instance of the subclass.

For @Injectables with configured providers, the factory function is
of the form:

factory: (t?) => t ? constructorInject(t) : provider();

where constructorInject(t) creates an instance of 't' using the
naturally declared constructor of the type, and where provider()
creates an instance of the base type using the special declared
provider on @Injectable.

PR Close #25392
2018-08-09 09:58:13 -07:00
fba276d3d1 fix(ivy): use a single constant pool per source file (#25392)
Previously, ngtsc used a new ConstantPool for each decorator
compilation. This could result in collisions between constants in the
top-level scope.

Now, ngtsc uses a single ConstantPool for each source file being
compiled, and merges the constant statements into the file after the
import section.

PR Close #25392
2018-08-09 09:58:13 -07:00
9c92a6fc7a test(ivy): add test for <ng-container> and shallow queries (#25379)
PR Close #25379
2018-08-09 07:35:01 -07:00
6c4da9dcd3 build: stop printing source-map-support warning (#25339)
PR Close #25339
2018-08-08 19:02:57 -07:00
b64fed1ba3 fix(ivy): prevent ngDevMode from getting removed too early (#25371)
PR Close #25371
2018-08-08 13:13:16 -07:00
8bbce3feff docs: copy edit architecture guide (#25328)
PR Close #25328
2018-08-08 13:12:53 -07:00
6c359afce6 docs: make css multiline in styleguide for consistency (#25300)
PR Close #25300
2018-08-08 13:12:34 -07:00
a3f1e2cb42 style: format .bzl files with buildifier (#23544)
PR Close #23544
2018-08-08 13:12:07 -07:00
090824526b ci: enforce formatting of .bzl files (#23544)
These are now enforced in google3 so we want to match, so that PRs don't get held up when we sync

PR Close #23544
2018-08-08 13:12:07 -07:00
bfdbdc2ee6 docs(bazel): add skydoc generation (#23544)
PR Close #23544
2018-08-08 13:12:07 -07:00
638ff760a5 docs: add ngrx book to the docs (#23389)
PR Close #23389
2018-08-08 13:11:45 -07:00
74518c4b2e style: fix whitespace and indentation in the testing guide (#21669)
PR Close #21669
2018-08-08 13:11:15 -07:00
ebf508fcd0 docs: add docs for fakeAsync test with custom macroTask in aio (#21669)
PR Close #21669
2018-08-08 13:11:15 -07:00
1039bea53b docs: Clarify breaking change in minor release (#25393)
The breaking change was in an experimental feature. Update to clarify the wording.

PR Close #25393
2018-08-08 13:06:58 -07:00
a2593cbfb1 release: cut the v7.0.0-beta.1 release 2018-08-08 12:28:40 -07:00
70a3deb8a5 docs: release notes for the v6.1.2 release 2018-08-08 12:13:04 -07:00
843479449d Revert "build: update Bazel to 0.16 (#25316)" (#25391)
This reverts commit 4eb8ac6de9 because 0.16 is not
widely available yet (e.g. on Mac) and it is blocking the Angular release.

PR Close #25391
2018-08-08 10:52:23 -07:00
732026c3f5 feat(core): add DoBootstrap interface. (#24558)
Closes #24557.

PR Close #24558
2018-08-07 13:17:06 -07:00
ec6d6175d2 docs(aio): Angular course in Portuguese #21836 2018-08-07 12:07:54 -07:00
af9ced9026 fix(ivy): project ng-container nodes (#25354)
PR Close #25354
2018-08-07 12:02:48 -07:00
c6e5b971d6 fix(compiler-cli): use the oldProgram option in watch mode (#21364)
The performCompilation() is always called with an undefined oldProgram option (even in watch mode).
This was regression introduced in: 957be960d2

Partial fix, discovered in: #21361

PR Close #21364
2018-08-07 11:58:38 -07:00
dbdbbdbe86 fix(ivy): support ng-container inside another ng-container (#25346)
PR Close #25346
2018-08-07 11:48:42 -07:00
fefc860f35 fix(ivy): fix bug with banana-in-a-box expressions in nested templates (#25321)
Inside of a nested template, an attempt to generate code for a banana-
in-a-box expression would cause a crash in the _AstToIrVisitor, as it
was not handling the case where a write would be generated to a local
variable.

This change supports such a mode of operation.

PR Close #25321
2018-08-07 11:45:32 -07:00
02e201ab1a fix: add mappings for ngfactory & ngsummary files to their module names in aot summary resolver (#25335)
PR Close #25335
2018-08-07 11:13:28 -07:00
7bf5a43385 docs: refining code of tutorial 7 routing (#22151)
Removed the dead code from hero-detail.component.ts

Fixes #21908

PR Close #22151
2018-08-07 11:08:53 -07:00
2fe05abbc4 docs: update resources to include UI-jar (#21200)
PR Close #21200
2018-08-07 11:07:38 -07:00
2505c077d7 test(upgrade): reduce flaky-ness by increasing timeout (#24937)
PR Close #24937
2018-08-06 14:52:50 -07:00
066fc6a0ca refactor(upgrade): improve internal AngularJS typings (#24937)
PR Close #24937
2018-08-06 14:52:50 -07:00
07ab98bbb0 build(upgrade): use correct sources in BUILD.bazel (#24937)
PR Close #24937
2018-08-06 14:52:50 -07:00
16c03c0f38 fix(core): In Testability.whenStable update callback, pass more complete (#25010)
data about tasks.

When building a list of pending tasks for callers of whenStable(),
Testability will copy data about the task into a new object, in order to
avoid leaking references to tasks.

This change copies more properties from Tasks into the list of pending
tasks, as well as a reference to Task.data to give callers more
information about the tasks that are pending.

Specifically, this also copies runCount and task ID, which are needed in
order for callers to know when a given task is repeating.

PR Close #25010
2018-08-06 13:49:19 -07:00
3355502f2f fix(ivy): support ng-container at the root of a view with delayed insertion (#25329)
PR Close #25329
2018-08-06 13:47:44 -07:00
02c15a2448 docs: update to 2nd edition of Learning Angular (#20934)
PR Close #20934
2018-08-06 13:44:42 -07:00
6861bc5b06 docs: clarify heroes example (#21216)
PR Close #21216
2018-08-06 13:44:17 -07:00
b8887ddf16 docs: fix table in comparing observables guide (#22485)
PR Close #22485
2018-08-06 13:41:15 -07:00
4933e103d3 docs(core): clarify supported ViewChild selectors (#22784)
PR Close #22784
2018-08-06 13:40:47 -07:00
7d006c5005 docs(core): fix tree-shakable spelling (#24057)
PR Close #24057
2018-08-06 13:40:15 -07:00
67ad59c245 docs: standardize spelling of tree-shakable (#24057)
PR Close #24057
2018-08-06 13:40:15 -07:00
397530ab24 docs: remove code in universal hero detail component (#25215)
This reverts commit e9cc3dad8f39bc8dfabfb708a825f90fcd2ab697.

PR Close #25215
2018-08-06 13:39:23 -07:00
a9881bb18e docs: replace npm with yarn in lockfile readme (#25309)
PR Close #25309
2018-08-06 13:38:13 -07:00
c1587029db docs: add missing word in outputs description. (#25330)
PR Close #25330
2018-08-06 13:36:49 -07:00
2b906f652f docs: fix typo (#25331)
PR Close #25331
2018-08-06 13:36:09 -07:00
c67f1bb38e docs: several fixes for NPM package guide (#20186)
PR Close #20186
2018-08-06 11:32:30 -07:00
637ae135c5 docs(http): fixed example unit test for error catching (#25306)
The example unit test should test the service when the backend
application is not available, by providing a mock error response.
Although, the test will
fail as the mock response from the server is valid (it does not simulate
a
error response, but valid response with an error status 404).
This merge request fix this issue by replacing MockResponse with
MockError

This PR resolves 19499 issue

PR Close #25306
2018-08-06 11:31:57 -07:00
4eb8ac6de9 build: update Bazel to 0.16 (#25316)
PR Close #25316
2018-08-06 11:30:24 -07:00
ba1e25f53f fix(router): take base uri into account in setUpLocationSync() (#20244)
Normalize the full URL (including the base uri) before passing it to
`router.navigateByUrl()`.

Fixes #20061

PR Close #20244
2018-08-06 11:11:07 -07:00
97b5cb2e3b docs(aio): add Made with Angular (#21297)
PR Close #21297
2018-08-06 09:50:14 -07:00
795e1e8a38 test(ivy): improve error message for the compiler compliance test (#25291)
before:

```
Expected to find features 'import * as i0 from "@angular/core";
import { Directive, Input } from '@angular/core';

```

after:

```
Failed to find "template" after "...Component_Factory() { return new
MyComponent(); }," in:
'import * as i0 from "@angular/core";
import { Directive, Input } from '@angular/core';```

```

PR Close #25291
2018-08-05 15:31:19 -07:00
aea8832243 refactor(ivy): misc cleanup (#25291)
PR Close #25291
2018-08-05 15:31:19 -07:00
1e7ca22078 refactor(ivy): make all directives public by default (#25291)
To match the View Engine behavior.

We should make this configurable so that the node injector is tree shaken when
directives do not need to be published.

PR Close #25291
2018-08-05 15:31:19 -07:00
26a15cc534 build: skip ivy builds when not publishing (#25299)
PR Close #25299
2018-08-04 14:17:00 -07:00
b0d86c1c2f refactor(bazel): dont rely on language target to downlevel for loop (#24534)
PR Close #24534
2018-08-03 15:55:18 -07:00
dc0715142f test(docs-infra): log docs examples e2e spec paths to aid debugging (#25293)
It seems that occasionally the sharding of docs examples e2e tests gets
messed up resulting in some tests not being run. This can cause CI to be
green on a PR, when they shouldn't (because the failing tests didn't run
at all).

It is unclear under what circumstances this happens, so printing the
paths of found e2e specs will help debug the issue when it comes up
again.

PR Close #25293
2018-08-03 15:30:31 -07:00
4e264781ee refactor(ivy): do not mention LViewData in public documentation (#25292)
PR Close #25292
2018-08-03 14:48:11 -07:00
79a9c71422 test(ivy): remove unnecessary common import (#25297)
PR Close #25297
2018-08-03 14:34:02 -07:00
15cc85c54a style(common): fix short param names (#23667)
PR Close #23667
2018-08-03 14:09:27 -07:00
725bae1921 docs(common): fix content errors (#23667)
PR Close #23667
2018-08-03 14:09:27 -07:00
eb999300d9 test(ivy): run compiler compliance tests without rebuilding core,common (#25248)
Previously the compiler compliance tests ran and built test code with
real dependencies on @angular/core and @angular/common. This meant that
any changes to the compiler would result in long rebuild processes
for tests to rerun.

This change removes those dependencies and causes test code to be built
against the fake_core stub of @angular/core that the ngtsc tests use.
This change also removes the dependency on @angular/common entirely, as
locality means it's possible to reference *ngIf without needing to link
to an implementation.

PR Close #25248
2018-08-03 13:08:51 -07:00
afa6b9e794 fix(ivy): execute the optional begin and end methods of the rendererFactory (#25273)
This is required to i.e. flush animations when using a Renderer2.
`rf.begin()` and `rf.end()` around the change detection.

PR Close #25273
2018-08-03 10:17:13 -07:00
0822dc70f2 feat(ivy): generate .ngfactory stubs if requested (#25176)
Existing bootstrap code in the wild depends on the existence of
.ngfactory files, which Ivy does not need. This commit adds the
capability in ngtsc to generate .ngfactory files which bridge
existing bootstrap code with Ivy.

This is an initial step. Remaining work includes complying with
the compiler option to specify a generated file directory, as well
as presumably testing in g3.

PR Close #25176
2018-08-03 09:42:06 -07:00
728d98d3a9 fix(ivy): add bound proerties name to template (#25272)
Before this change bound properties would not be used when matching directives
at runtime.

That is `<ng-template [ngIf]=cond>...</ng-template>` would not trigger the
`ngIf` directive.

PR Close #25272
2018-08-02 22:59:04 -07:00
2f4abbf5a1 fix(ivy): fix inline template bindings parsing (#25272)
PR Close #25272
2018-08-02 22:59:04 -07:00
1000fb8406 test(ivy): add tests for attributes and bound attributes to the tpl transform (#25272)
PR Close #25272
2018-08-02 22:59:04 -07:00
b38931b484 fix(ivy): use devModeEqual in no change mode (#25252)
To avoid the unfamous error `Expression has changed after it was checked.`

PR Close #25252
2018-08-02 22:57:28 -07:00
1fb7111da1 fix(ivy): content query results should be available in content hooks (#25271)
PR Close #25271
2018-08-02 19:32:09 -07:00
c2c12e52fe feat(ivy): support ng-container as a child of an already inserted view (#25227)
PR Close #25227
2018-08-02 18:50:03 -07:00
28c7a4efbc feat(ivy): add basic support for ng-container (#25227)
This commit adds basic support for <ng-container> - most of the
functionality should work as long as <ng-container> is a child of
a regular element.

PR Close #25227
2018-08-02 18:50:03 -07:00
4f741e74e1 docs: release notes for the v6.1.1 release 2018-08-02 14:23:36 -07:00
6bacd32fbd release: cut the v7.0.0-beta.0 release 2018-08-02 11:42:10 -07:00
183757daa2 fix(core): fix tests for uninitialized @Output error (#19116)
PR Close #19116
2018-08-02 09:37:21 -07:00
adf510f986 fix(core): throw error message when @Output not initialized (#19116)
Closes #3664

PR Close #19116
2018-08-02 09:37:21 -07:00
74bce18190 Revert "docs: refactor http module import for style guide app.module (#25001)" (#25263)
This reverts commit 88da8f3d52.

PR Close #25263
2018-08-02 09:20:12 -07:00
3ba5220839 refactor(forms): ngForm element selector has been deprecated in favor of ng-form (#23721)
This has been deprecated to keep selector consistent with other core Angular selectors.  As element selectors are in kebab-case.

 Now deprecated:
 ```
 <ngForm #myForm="ngForm">
 ```

 After:
 ```
 <ng-form #myForm="ngForm">
 ```

You can also choose to supress this warnings by providing a config for `FormsModule` during import:

```ts
imports: [
 FormsModule.withConfig({warnOnDeprecatedNgFormSelector: 'never'});
]

Closes: #23678

PR Close #23721
2018-08-02 08:34:43 -07:00
5982425436 test(common): TokenExtractor should extend HttpXsrfTokenExtractor in xsrf spec (#24649)
PR Close #24649
2018-08-02 08:34:15 -07:00
140248ade0 test(common): remove unused import in xsrf spec (#24649)
PR Close #24649
2018-08-02 08:34:14 -07:00
e60737f63c docs(docs-infra): adds note according to Symlink problem (#24714)
docs: adds note according to Symlink problem

Closes #24709
docs(docs-infra): adds section "Developing on Windows"


Merge remote-tracking branch 'origin/aioREADME' into aioREADME


docs(docs-infra): adds information about admin rights


docs(docs-infra): adds hint


docs(docs-infra): Change to link


PR Close #24714
2018-08-02 08:33:24 -07:00
7b89711402 docs(elements): add section about custom element typings in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
f1223628a6 docs(elements): add link to full example in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
1b4269ad85 docs(elements): remove unnecessary whitespace in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
a224df43af feat(docs-infra): support sending Google Analytics events (#25042)
PR Close #25042
2018-08-01 17:04:19 -07:00
d46a961509 docs(ivy): API doc tweaks (#25258)
PR Close #25258
2018-08-01 16:19:54 -07:00
20b453008f docs: update reactiveconf 2018 in events (#24739)
PR Close #24739
2018-08-01 16:15:18 -07:00
06a1974a48 docs: Update the link to the Jasmine docs (#25175)
Solves #24462.

Also update the http part of the link to to https.

PR Close #25175
2018-08-01 16:12:43 -07:00
3d7f555044 ci: update pullapprove groups and add docs (#25257)
With this update to permissions the docs team can easily identify the technical reviewer for a particular doc, which should streamline the reviews.

I also added Jennifer into all groups that contain docs, so that she can approve changes that contain only editorial changes.

Closes #21692

PR Close #25257
2018-08-01 15:53:41 -07:00
06af7943a4 test(upgrade): run tests against AngularJS v1.7.x as well (#25231)
PR Close #25231
2018-08-01 14:10:21 -07:00
fa70a2a650 build(bazel): entry point file couldn't be resolved [ts-api-guardian] (#25052)
* When using `ts-api-guardian` on Windows, the input file can't be found due to wrong normalized path delimiters.

PR Close #25052
2018-08-01 13:29:27 -07:00
bde4402675 build: update hello_world__closure to google-closure-compiler 20180716.0.0 (#25236)
PR Close #25236
2018-08-01 13:23:35 -07:00
7d6b258778 build: revert yarn.lock rxjs version to 6.0.0 (#25236)
PR Close #25236
2018-08-01 13:23:35 -07:00
5342aeaafd docs(aio): update Kendo UI description in resource.json (#24845)
PR Close #24845
2018-08-01 10:59:16 -07:00
1dd2eaa7d2 docs: fix typos and missing word in tutorial (#20764)
PR Close #20764
2018-08-01 10:56:31 -07:00
af07ffc2ad docs(core): remove experimental tag (#24032)
PR Close #24032
2018-08-01 10:56:07 -07:00
2b6e1f0f4b docs(core): remove experimental tag (#24032)
Remove experimental note on APP_INITIALIZER.

PR Close #24032
2018-08-01 10:56:06 -07:00
7a4fb44f8d docs(aio): add Kevin Yang to GDE resources (#24791)
Add files via upload
PR Close #24791
2018-08-01 10:55:41 -07:00
88da8f3d52 docs: refactor http module import for style guide app.module (#25001)
PR Close #25001
2018-08-01 10:55:17 -07:00
01e6dab544 fix(compiler-cli): correct realPath to realpath. (#25023)
The optional property on `ts.CompilerHost` is called `realpath` (lower
case), not `realPath` (lower camel case).

It is not clear to me what the impact of this is, but the author's
intent was clearly to override `realpath`.

PR Close #25023
2018-08-01 10:54:51 -07:00
a9ecf4b929 docs: refactor lazy loading modules example (#25071)
PR Close #25071
2018-08-01 10:54:00 -07:00
166ddaadca docs(router): clarify scroll position wording (#25077)
PR Close #25077
2018-08-01 10:53:35 -07:00
1e5327872d docs(core): replace ReflectiveInjector example with Static Injector example (#25162)
PR Close #25162
2018-08-01 10:52:32 -07:00
367841d237 docs: replace ReflectiveInjector samples with Injector samples (#25162)
PR Close #25162
2018-08-01 10:52:32 -07:00
d5b73832bf refactor(animations): do not use short parameter names (#25198)
PR Close #25198
2018-08-01 10:51:58 -07:00
7075c418f9 docs(changelog): remove reverted feature entry (#25206)
PR Close #25206
2018-08-01 10:51:28 -07:00
466e026f6f docs(changelog): remove duplicate entries (#25206)
PR Close #25206
2018-08-01 10:51:28 -07:00
4976a58780 docs: update to account for CLI changes (#25223)
This should help clarify the use of providedIn and correct the documentation where it was showing the use of a now depreciated CLI command flag.

I am openly looking for feedback on this change to figure out the best wording.

PR Close #25223
2018-08-01 10:51:05 -07:00
bafe1a0d2a build(bazel): fix typo in protractor test target definition (#25235)
PR Close #25235
2018-08-01 10:50:43 -07:00
c8a4fb1faf fix(ivy): walk declaration views in listener (#25228)
PR Close #25228
2018-07-31 16:35:20 -07:00
64516da6b0 feat(ivy): support inheriting input/output from bare base class (#25094)
PR Close #25094
2018-07-31 16:25:11 -07:00
6e2a1877ab refactor(core): remove withBody from public testing API (#25171)
PR Close #25171
2018-07-31 15:09:32 -07:00
aafd502bcb fix(ivy): default to ngDevMode = true (#25208)
Before the `ngDevMode` had to be set explicitly or it would throw
an exception at runtime. This changes it so that if `ngDevModu` is
`undefined` than we default to `ngDevMode = true`. In other words
unless the developer has explicitly asked to make a prodution build
by setting `ngDevMode = false` as compilation constant, the default
is `ngDevMode = true`.

This also fixes a minor bug where the setup code would read
`global['ngDevMode']` but all other code would read `global.ngDevMode`.
This would cause issues with closure compiler since the
reading of the `ngDevMode` must be consistent.

PR Close #25208
2018-07-31 14:19:19 -07:00
4cb1074850 docs(aio): add short description for entryComponents (#21360)
PR Close #21360
2018-07-31 13:18:36 -07:00
76d8eb021c build: make postinstall script compatible with Windows (#25232)
PR Close #25232
2018-07-31 13:17:54 -07:00
3f6fc00d73 docs(forms): fix incorrect variables naming in the comments (#25150)
PR Close #25150
2018-07-31 11:42:15 -07:00
5254d3447d build(bazel): update to rules_nodejs 0.11.2 and latest rules_typescript (#25169)
PR Close #25169
2018-07-31 11:41:50 -07:00
4ee9db959a docs(docs-infra): fix topnav layout for smaller screens (#25181)
PR Close #25181
2018-07-31 11:41:22 -07:00
3f20a2fb5a docs: fix link to "Override component providers" (#24967)
Closes #24966

PR Close #24967
2018-07-30 21:53:21 -07:00
e3834b7001 feat(ivy): support change detection on the root view (#25085)
PR Close #25085
2018-07-30 21:50:54 -07:00
36648293a8 refactor(ivy): misc (#25174)
PR Close #25174
2018-07-30 16:59:48 -07:00
cd89eb8404 feat(ivy): implement the getters of ViewContainerRef (#25174)
BREAKING CHANGE: ViewContainerRef.parentInjector is deprecated without replacement

PR Close #25174
2018-07-30 16:59:48 -07:00
e99d860393 feat(compiler): add "original" placeholder value on extracted XMB (#25079)
Update XMB placeholders(<ph>) to include the original value on top of an
example. Placeholders can by definition have one example(<ex>) tag and a
text node. The text node is used by TC as the "original" value from the
placeholder, while the example should represent a dummy value.
For example: <ph name="PET"><ex>Gopher</ex>{{ petName }}</ph>.
This change makes sure that we have the original text, but it *DOES NOT*
make sure that the example is correct. The example has the same wrong
behavior of showing the interpolation text rather than a useful
example.

No breaking changes, but tools that depend on the previous behavior and
don't consider the full XMB definition may fail to parse the XMB.
Fixes b/72565847

PR Close #25079
2018-07-30 16:49:00 -07:00
24789e9ad9 docs(aio): add StrongBrew to the trainer list (#24891)
PR Close #24891
2018-07-30 16:48:17 -07:00
f94f9640d0 ci: correctly encode quoted params passed as params to curl
Previously the auth token could have been split into three separate args in bash which resulted
in two bogus requests being sent out for each curl call. These requests had to time out before
the real request was made, but without the token.

I couldn't find a better way to quickly fix this without adding some duplication.
2018-07-30 16:46:11 -07:00
4d5167ec83 docs: update bootstrapping and entry component guide to use httpclient (#25178)
PR Close #25178
2018-07-30 16:00:19 -07:00
efc6684cd3 docs: fix typo in dependency injection guide (#24972)
PR Close #24972
2018-07-30 15:56:35 -07:00
2ef777b0b2 fix(ivy): convert context code into a tree-shakable instruction (#24943)
PR Close #24943
2018-07-30 15:54:11 -07:00
fe14f180a6 fix(compiler): update compiler to flatten nested template fns (#24943)
PR Close #24943
2018-07-30 15:54:11 -07:00
87419097da fix(ivy): flatten template fns for nested views (#24943)
PR Close #24943
2018-07-30 15:54:11 -07:00
9a6d26e05b docs: refactor pipe example to use the HttpClient (#22741)
PR Close #22741
2018-07-30 14:34:32 -07:00
6a797d5401 refactor(ivy): element and ElementStart retuns void (#25173)
use `loadElement` to load an element when needed in specs

PR Close #25173
2018-07-27 17:22:18 -07:00
89e8b6fc0e refactor(ivy): update specs to make use of the element() instruction (#25173)
PR Close #25173
2018-07-27 17:22:18 -07:00
f82b6b2ed7 build(bazel): fix typos in comments (#25172)
PR Close #25172
2018-07-27 17:20:58 -07:00
a87d44c187 refactor(ivy): do not deep import from ngtsc into ngcc (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
43d0e3dd72 feat(ivy): implement initial ngcc package transformer (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
5b32aa4486 feat(ivy): implement esm2015 and esm5 ngcc file renderers (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
844d510d3f feat(ivy): implement ngcc Analyzer (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
2f70e90493 feat(ivy): implement esm2015 and esm5 file parsers (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
45cf5b5dad feat(ivy): implement esm2015 and esm5 reflection hosts (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
4ad2f11919 test(ivy): implement ngcc specific version of makeProgram (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
d7aa20d912 feat(ivy): ngcc project skeleton (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
a673494412 build: add dependencies to be used by ngcc (#24897)
PR Close #24897
2018-07-27 17:15:31 -07:00
07e6de5788 test(ivy): allow makeProgram to be more configurable (#24897)
This supports use cases needed by ngcc, where the compilation
needs to be configured for JavaScript differently to normal TypeScript.

PR Close #24897
2018-07-27 17:15:31 -07:00
6f1685ab98 fix(ivy): allow FunctionExpression to indicate a method declaration (#24897)
In some code formats (e.g. ES5) methods can actually be function
expressions. For example:

```js
function MyClass() {}
// this static method is declared as a function expression
MyClass.staticMethod = function() { ... };
```

PR Close #24897
2018-07-27 17:15:31 -07:00
67588ec606 refactor(ivy): allow ImportManager to have configurable prefix (#24897)
The ngcc compiler will want to specify its own prefix when rendering
definitions.

PR Close #24897
2018-07-27 17:15:31 -07:00
ee2c050521 fix(ivy): make ngtsc ClassMember node and declaration optional (#24897)
Not all code formats have associated nodes and declarations for class members.

PR Close #24897
2018-07-27 17:15:30 -07:00
185b932138 refactor(ivy): TypeScriptReflectionHost.isClass cannot be a type discriminator (#24897)
The `ReflectionHost` interface that is being implemented only expects a
return value of `boolean`.

Moreover, if you want to extend this class to support non-TS code formats,
e.g. ES5, the result of this call returning true does not mean that the `node`
is a `ClassDeclaration`. It could be a `VariableDeclaration`.

PR Close #24897
2018-07-27 17:15:30 -07:00
5e98421d33 style(ivy): remove underscore from TypeScriptReflectionHost._getDeclarationOfSymbol (#24897)
The linter complains that non-private members must be marked
with `@internal` if they start with an underscore.

PR Close #24897
2018-07-27 17:15:30 -07:00
8e65891985 build(ivy): fix ci failures (#25166)
PR Close #25166
2018-07-27 18:47:13 -04:00
7f59170f77 refactor(ivy): use element() where applicable in di_spec (#25166)
For future ref
Search `elementStart\(([^)]+)\);\s*\n\s*elementEnd\(\);`
Replace `element($1)`

PR Close #25166
2018-07-27 18:47:13 -04:00
9ea112473b refactor(ivy): use bit operations in node injector (#25166)
PR Close #25166
2018-07-27 18:47:13 -04:00
16f0ac38b8 refactor(ivy): simplify node injector imports (#25166)
PR Close #25166
2018-07-27 18:47:13 -04:00
15df853622 fix(ivy): walk the node injector tree and then the module injector tree (#25166)
- `directiveInjector()` is used to inject anything in the directive / component
/ pipe factories so adding `InjectionToken<T>` as a supported token type.
- `getOrCreateInjectable()` should search first in the node injector tree and
then in the module injector tree (was either or before the PR).

PR Close #25166
2018-07-27 18:47:13 -04:00
d3c0915598 docs(ivy): clarify injector API docs (#25166)
PR Close #25166
2018-07-27 18:47:13 -04:00
ce98634dfd build(compiler-cli): update tsickle dependency to support TypeScript 2.9 (#25152)
The original range (`^0.30.0`) does not match `0.32.1`, which enables support for TypeScript 2.9.

Close #25141

PR Close #25152
2018-07-27 11:25:28 -07:00
342678486d test: fix typings for DoneFn (#25163)
This also fixes CI tests, which were accidentally broken in #24663.

PR Close #25163
2018-07-27 11:13:32 -07:00
e8d4211d5c feat(docs-infra): allow notification bar to show arbitrary content (#25020)
This change generalises the notification bar rendering to allow
more complex content to be displayed.

Now you must provide the full HTML of the notification message
when using `<aio-notification>`.

Also you can control whether clicking the content triggers the
notification to close or not.

This will support the new notification specified in "Other Items : 3" of
[#24140](https://github.com/angular/angular/issues/24140#issuecomment-397480410)

PR Close #25020
2018-07-27 09:29:40 -07:00
6a4d66d432 style(docs-infra): remove unnecessary call to console.log() (#25020)
PR Close #25020
2018-07-27 09:29:39 -07:00
a3cf61b7cf docs: refactor feature modules example (#25069)
PR Close #25069
2018-07-27 09:28:12 -07:00
a1b185b723 docs: Change unnecessary step in ToH-Tutorial (#25059)
PR Close #25059
2018-07-27 09:25:59 -07:00
601064e41d build(bazel): add comment about angular bazel rules API re-export from /index.bzl (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
e265ccd82c build(bazel): add comment for patch-types work-around (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
dd44f63c73 build(bazel): show bazel progress in CircleCI to prevent 10m timeout with no output (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
1d051c5841 build(bazel): use bazel managed node_modules for downstream angular from source build support (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
323faf954b docs(router): Removed unneeded trailing text. (#24894)
PR Close #24894
2018-07-26 17:01:02 -07:00
3169edd77a fix(ivy): don't crash in listLazyRoutes() (#25080)
This commit replaces the "not implemented" error when calling
listLazyRoutes() with an empty result, which will allow testing
in the CLI before listLazyRoutes() is implemented.

PR Close #25080
2018-07-26 16:38:10 -07:00
8de304c15a fix(ivy): wait for preanalyze promises in loadNgStructureAsync() (#25080)
loadNgStructureAsync() for ngtsc has a bug where it returns a
Promise<Promise[]> instead of awaiting the entire array of Promises.

This commit uses Promise.all() to await the whole set.

PR Close #25080
2018-07-26 16:38:09 -07:00
0d1d5898e3 fix(bazel): allow compile_strategy to be (privately) imported (#25080)
compile_strategy() is used to decide whether to build Angular code
using ngc (legacy) or ngtsc (local). In order for g3 BUILD rules
to switch properly and allow testing of Ivy in g3, they need to
import this function.

This commit removes the _ prefix which allows the function to be
imported.

PR Close #25080
2018-07-26 16:38:09 -07:00
6fe865b080 fix(ivy): don't use a custom ts.CompilerHost for ngtsc (#25080)
ngtsc used to have a custom ts.CompilerHost which delegated to the plain
ts.CompilerHost. There's no need for this wrapper class and it causes
issues with CLI integration, so delete it.

PR Close #25080
2018-07-26 16:38:09 -07:00
e0c0c44d99 fix(ivy): allow relative imports of .d.ts files (#25080)
ngtsc used to assume that all .d.ts dependencies (that is, third party
packages) were imported via an absolute module path. It turns out this
assumption isn't valid; some build tools allow relative imports of
other compilation units.

In the absolute case, ngtsc assumes (and still does) that all referenced
types are available through the entrypoint from which an @NgModule was
imported. This commit adds support for relative imports, in which case
ngtsc will use relative path resolution to determine the imports.

PR Close #25080
2018-07-26 16:38:09 -07:00
13a0d527f6 fix(ivy): correctly write cross-file references (#25080)
There is a bug in the existing handling for cross-file references.
Suppose there are two files, module.ts and component.ts.

component.ts declares two components, one of which uses the other.
In the Ivy model, this means the component will get a directives:
reference to the other in its defineComponent call.

That reference is generated by looking at the declared components
of the module (in module.ts). However, the way ngtsc tracks this
reference, it ends up comparing the identifier of the component
in module.ts with the component.ts file, detecting they're not in
the same file, and generating a relative import.

This commit changes ngtsc to track all identifiers of a reference,
including the one by which it is declared. This allows toExpression()
to correctly decide that a local reference is okay in component.ts.

PR Close #25080
2018-07-26 16:38:09 -07:00
ed7aa1c3e5 fix(ivy): force new imports for .d.ts files (#25080)
When ngtsc encounters a reference to a type (for example, a Component
type listed in an NgModule declarations array), it traces the import
of that type and attempts to determine the best way to refer to it.

In the event the type is defined in the same file where a reference
is being generated, the identifier of the type is used. If the type
was imported, ngtsc has a choice. It can use the identifier from the
original import, or it can write a new import to the module where the
type came from.

ngtsc has a bug currently when it elects to rely on the user's import.
When writing a .d.ts file, the user's import may have been elided as
the type was not referred to from the type side of the program. Thus,
in .d.ts files ngtsc must always assume the import may not exist, and
generate a new one.

In .js output the import is guaranteed to still exist, so it's
preferable for ngtsc to continue using the existing import if one is
available.

This commit changes how @angular/compiler writes type definitions, and
allows it to use a different expression to write a type definition than
is used to write the value. This allows ngtsc to specify that types in
type definitions should always be imported. A corresponding change to
the staticallyResolve() Reference system allows the choice of which
type of import to use when generating an Expression from a Reference.

PR Close #25080
2018-07-26 16:38:09 -07:00
f902b5ec59 feat(ivy): resolve forwardRef() for queries (#25080)
@ContentChild[ren] and @ViewChild[ren] can contain a forwardRef() to a
type. This commit allows ngtsc to unwrap the forward reference and
deal with the node inside.

It includes two modes of support for forward reference resolution -
a foreign function resolver which understands deeply nested forward
references in expressions that are being statically evaluated, and
an unwrapForwardRef() function which deals only with top-level nodes.

Both will be useful in the future, but for now only unwrapForwardRef()
is used.

PR Close #25080
2018-07-26 16:38:09 -07:00
335 changed files with 17315 additions and 4969 deletions

View File

@ -48,7 +48,7 @@ jobs:
# Check BUILD.bazel formatting before we have a node_modules directory
# Then we don't need any exclude pattern to avoid checking those files
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
- run: 'buildifier -mode=check $(find . -type f \( -name "*.bzl" -or -name BUILD.bazel -or -name BUILD \)) ||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
# Run the skylark linter to check our Bazel rules
# deprecated-api is disabled because we use actions.new_file(genfiles_dir)

View File

@ -62,6 +62,13 @@ merge:
# list of checks that will determine if the merge label can be added
checks:
# require that the PR has reviews from all requested reviewers
#
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
# Rather than merging PRs with pending reviews, if all PullApprove requirements are satisfied and additional reviews are not needed pending reviewers should be removed via GitHub UI (this also leaves an audit trail behind these decisions).
requireReviews: true,
# whether the PR shouldn't have a conflict with the base branch
noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")

View File

@ -1,9 +1,62 @@
<a name="6.1.1"></a>
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
<a name="7.0.0-beta.2"></a>
# [7.0.0-beta.2](https://github.com/angular/angular/compare/7.0.0-beta.1...7.0.0-beta.2) (2018-08-15)
### Bug Fixes
* **bazel:** correct type concatenated to devmode_js ([#25467](https://github.com/angular/angular/issues/25467)) ([fb2c524](https://github.com/angular/angular/commit/fb2c524))
* **service-worker:** `Cache-Control: no-cache` on assets breaks service worker ([#25408](https://github.com/angular/angular/issues/25408)) ([01ec5fd](https://github.com/angular/angular/commit/01ec5fd)), closes [#25442](https://github.com/angular/angular/issues/25442)
<a name="7.0.0-beta.1"></a>
# [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08)
### Bug Fixes
* **compiler-cli:** use the oldProgram option in watch mode ([#21364](https://github.com/angular/angular/issues/21364)) ([c6e5b97](https://github.com/angular/angular/commit/c6e5b97)), closes [#21361](https://github.com/angular/angular/issues/21361)
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([02e201a](https://github.com/angular/angular/commit/02e201a))
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ba1e25f](https://github.com/angular/angular/commit/ba1e25f)), closes [#20061](https://github.com/angular/angular/issues/20061)
### Features
* **core:** add DoBootstrap interface. ([#24558](https://github.com/angular/angular/issues/24558)) ([732026c](https://github.com/angular/angular/commit/732026c)), closes [#24557](https://github.com/angular/angular/issues/24557)
<a name="6.1.2"></a>
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
### Bug Fixes
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ae9b4e6](https://github.com/angular/angular/commit/ae9b4e6)), closes [#20061](https://github.com/angular/angular/issues/20061)
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([054fbbe](https://github.com/angular/angular/commit/054fbbe))
<a name="7.0.0-beta.0"></a>
# [7.0.0-beta.0](https://github.com/angular/angular/compare/6.1.0...7.0.0-beta.0) (2018-08-02)
### Bug Fixes
* **bazel:** allow compile_strategy to be (privately) imported ([#25080](https://github.com/angular/angular/issues/25080)) ([0d1d589](https://github.com/angular/angular/commit/0d1d589))
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
* **compiler-cli:** correct realPath to realpath. ([#25023](https://github.com/angular/angular/issues/25023)) ([01e6dab](https://github.com/angular/angular/commit/01e6dab))
* **core:** throw error message when @Output not initialized ([#19116](https://github.com/angular/angular/issues/19116)) ([adf510f](https://github.com/angular/angular/commit/adf510f)), closes [#3664](https://github.com/angular/angular/issues/3664)
### Features
* **compiler:** add "original" placeholder value on extracted XMB ([#25079](https://github.com/angular/angular/issues/25079)) ([e99d860](https://github.com/angular/angular/commit/e99d860))
<a name="6.1.1"></a>
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
* **compiler-cli:** correct tsickle dependency version to fix typescript 2.9 compatibility ([fec29fa](https://github.com/angular/angular/commit/317c7087c56b72aa74cd6d6a8f719e6e7fec29fa))
@ -93,7 +146,7 @@
* **bazel:** turn on preserve-symlinks ([#24881](https://github.com/angular/angular/issues/24881)) ([c438b5e](https://github.com/angular/angular/commit/c438b5e))
### BREAKING CHANGES
### Angular Labs (experimental feature) breaking change
* **bazel:** Use of @angular/bazel rules now requires calling ng_setup_workspace() in your WORKSPACE file.

View File

@ -6,23 +6,30 @@ workspace(name = "angular")
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/20ff5892612f8359aec8aaf26dd3902a24976ada.zip",
strip_prefix = "rules_nodejs-20ff5892612f8359aec8aaf26dd3902a24976ada",
sha256 = "07da9d4c3e688a02745d0f50709a87744706d4f5d1959b799b0ac38e97acd622",
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.4.zip"],
strip_prefix = "rules_nodejs-0.11.4",
sha256 = "c31c4ead696944a50fad2b3ee9dfbbeffe31a8dcca0b21b9bf5b3e6c6b069801",
)
http_archive(
name = "bazel_skylib",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
strip_prefix = "bazel-skylib-0.3.1",
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
)
http_archive(
name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
strip_prefix = "rules_webtesting-0.2.1",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
)
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.3.zip",
strip_prefix = "rules_typescript-0.15.3",
sha256 = "a2b26ac3fc13036011196063db1bf7f1eae81334449201dc28087ebfa3708c99",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
strip_prefix = "rules_typescript-0.16.0",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
)
http_archive(
@ -31,6 +38,13 @@ http_archive(
sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a",
)
http_archive(
name = "io_bazel_rules_sass",
url = "https://github.com/bazelbuild/rules_sass/archive/0.1.0.zip",
strip_prefix = "rules_sass-0.1.0",
sha256 = "b243c4d64f054c174051785862ab079050d90b37a1cef7da93821c6981cb9ad4",
)
# This commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
@ -51,6 +65,14 @@ http_archive(
sha256 = "e373d2ae24955c1254c495c9c421c009d88966565c35e4e8444c082cb1f0f48f",
)
http_archive(
name = "io_bazel_skydoc",
# TODO: switch to upstream when https://github.com/bazelbuild/skydoc/pull/103 is merged
url = "https://github.com/alexeagle/skydoc/archive/fe2e9f888d28e567fef62ec9d4a93c425526d701.zip",
strip_prefix = "skydoc-fe2e9f888d28e567fef62ec9d4a93c425526d701",
sha256 = "7bfb5545f59792a2745f2523b9eef363f9c3e7274791c030885e7069f8116016",
)
# We have a source dependency on the Devkit repository, because it's built with
# Bazel.
# This allows us to edit sources and have the effect appear immediately without
@ -137,3 +159,12 @@ yarn_install(
package_json = "//tools/http-server:package.json",
yarn_lock = "//tools/http-server:yarn.lock",
)
##################################
# Skylark documentation generation
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
skydoc_repositories()

View File

@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
## Developer tasks
We use `yarn` to manage the dependencies and to run build tasks.
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:

View File

@ -16,6 +16,7 @@ describe('Form Validation Tests', function () {
tests('Template-Driven Form');
bobTests();
asyncValidationTests();
crossValidationTests();
});
@ -26,6 +27,7 @@ describe('Form Validation Tests', function () {
tests('Reactive Form');
bobTests();
asyncValidationTests();
crossValidationTests();
});
});
@ -45,6 +47,7 @@ let page: {
errorMessages: ElementArrayFinder,
heroFormButtons: ElementArrayFinder,
heroSubmitted: ElementFinder,
alterEgoErrors: ElementFinder,
crossValidationErrorMessage: ElementFinder,
};
@ -63,6 +66,7 @@ function getPage(sectionTag: string) {
errorMessages: section.all(by.css('div.alert')),
heroFormButtons: buttons,
heroSubmitted: section.element(by.css('.submitted-message')),
alterEgoErrors: section.element(by.css('.alter-ego-errors')),
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
};
}
@ -156,6 +160,16 @@ function expectFormIsInvalid() {
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
}
function triggerAlterEgoValidation() {
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
element(by.css('app-root')).click()
}
function waitForAlterEgoValidation() {
// alterEgo async validation will be performed in 400ms
browser.sleep(400);
}
function bobTests() {
const emsg = 'Name cannot be Bob.';
@ -177,6 +191,32 @@ function bobTests() {
});
}
function asyncValidationTests() {
const emsg = 'Alter ego is already taken.';
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Eric');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid();
expect(page.alterEgoErrors.getText()).toBe(emsg);
});
it('should be ok again with different values', function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('John');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid();
expect(page.alterEgoErrors.isPresent()).toBe(false);
});
}
function crossValidationTests() {
const emsg = 'Name cannot match alter ego.';
@ -187,6 +227,9 @@ function crossValidationTests() {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Batman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid();
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
});
@ -198,6 +241,9 @@ function crossValidationTests() {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Superman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid();
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
});

View File

@ -8,6 +8,7 @@ import { HeroFormTemplateComponent } from './template/hero-form-template.compone
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
@NgModule({
imports: [
@ -20,7 +21,8 @@ import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.d
HeroFormTemplateComponent,
HeroFormReactiveComponent,
ForbiddenValidatorDirective,
IdentityRevealedValidatorDirective
IdentityRevealedValidatorDirective,
UniqueAlterEgoValidatorDirective
],
bootstrap: [ AppComponent ]
})

View File

@ -0,0 +1,48 @@
/* tslint:disable: member-ordering forin */
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({
selector: 'app-hero-form-reactive',
templateUrl: './hero-form-reactive.component.html',
styleUrls: ['./hero-form-reactive.component.css'],
})
export class HeroFormReactiveComponent implements OnInit {
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
heroForm: FormGroup;
ngOnInit(): void {
// #docregion async-validation
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required)
});
// #enddocregion async-validation
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
// #docregion async-validation
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
// #enddocregion async-validation
}

View File

@ -35,6 +35,13 @@
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
formControlName="alterEgo" >
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->

View File

@ -4,6 +4,7 @@ import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({
selector: 'app-hero-form-reactive',
@ -25,7 +26,10 @@ export class HeroFormReactiveComponent implements OnInit {
Validators.minLength(4),
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'alterEgo': new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required)
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
}
@ -33,4 +37,8 @@ export class HeroFormReactiveComponent implements OnInit {
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
}

View File

@ -0,0 +1,46 @@
import { Directive, forwardRef, Injectable } from '@angular/core';
import {
AsyncValidator,
AbstractControl,
NG_ASYNC_VALIDATORS,
ValidationErrors
} from '@angular/forms';
import { catchError, map } from 'rxjs/operators';
import { HeroesService } from './heroes.service';
import { Observable } from 'rxjs';
// #docregion async-validator
@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
constructor(private heroesService: HeroesService) {}
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
catchError(() => null)
);
}
}
// #enddocregion async-validator
// #docregion async-validator-directive
@Directive({
selector: '[appUniqueAlterEgo]',
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => UniqueAlterEgoValidator),
multi: true
}
]
})
export class UniqueAlterEgoValidatorDirective {
constructor(private validator: UniqueAlterEgoValidator) {}
validate(control: AbstractControl) {
this.validator.validate(control);
}
}
// #enddocregion async-validator-directive

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
const ALTER_EGOS = ['Eric'];
@Injectable({ providedIn: 'root' })
export class HeroesService {
isAlterEgoTaken(alterEgo: string): Observable<boolean> {
const isTaken = ALTER_EGOS.includes(alterEgo);
return of(isTaken).pipe(delay(400));
}
}

View File

@ -35,8 +35,20 @@
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
name="alterEgo" [(ngModel)]="hero.alterEgo" >
<!-- #docregion async-validation -->
<input id="alterEgo" class="form-control" name="alterEgo"
#alterEgo="ngModel"
[(ngModel)]="hero.alterEgo"
[ngModelOptions]="{ updateOn: 'blur' }"
appUniqueAlterEgo>
<!-- #enddocregion async-validation -->
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->

View File

@ -23,7 +23,10 @@ import { Hero, HeroService } from './shared';
`,
styles: [`
.heroes {
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;

View File

@ -1,6 +1,9 @@
/* #docregion */
.heroes {
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;

View File

@ -0,0 +1,27 @@
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
import { CanvasComponent } from './canvas.component';
describe('CanvasComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
CanvasComponent
],
}).compileComponents();
}));
beforeEach(() => {
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
{
source: 'HTMLCanvasElement.toBlob',
callbackArgs: [{ size: 200 }]
}
];
});
it('should be able to generate blob data from canvas', fakeAsync(() => {
const fixture = TestBed.createComponent(CanvasComponent);
fixture.detectChanges();
tick();
const app = fixture.debugElement.componentInstance;
expect(app.blobSize).toBeGreaterThan(0);
}));
});

View File

@ -0,0 +1,25 @@
import { Component, AfterViewInit, ViewChild } from '@angular/core';
@Component({
selector: 'sample-canvas',
template: '<canvas #sampleCanvas width="200" height="200"></canvas>'
})
export class CanvasComponent implements AfterViewInit {
blobSize: number;
@ViewChild('sampleCanvas') sampleCanvas;
constructor() { }
ngAfterViewInit() {
const canvas = this.sampleCanvas.nativeElement;
const context = canvas.getContext('2d');
if (context) {
context.clearRect(0, 0, 200, 200);
context.fillStyle = '#FF1122';
context.fillRect(0, 0, 200, 200);
canvas.toBlob((blob: any) => {
this.blobSize = blob.size;
});
}
}
}

View File

@ -1,6 +1,6 @@
// #docplaster
// #docregion
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
// #docregion added-imports
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
@ -17,7 +17,7 @@ import { HeroService } from '../hero.service';
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
@Input() hero: Hero;
hero: Hero;
// #docregion ctor
constructor(

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
@Input() hero: Hero;
hero: Hero;
constructor(
private route: ActivatedRoute,

View File

@ -1,8 +1,7 @@
# Introduction to components
<img src="generated/images/guide/architecture/hero-component.png" alt="Component" class="left">
A _component_ controls a patch of screen called a *view*. For example, individual components define and control each of the following views from the [Tutorial](tutorial/index):
A *component* controls a patch of screen called a *view*.
For example, individual components define and control each of the following views from the [Tutorial](tutorial):
* The app root with the navigation links.
* The list of heroes.
@ -11,38 +10,38 @@ A _component_ controls a patch of screen called a *view*. For example, individua
You define a component's application logic&mdash;what it does to support the view&mdash;inside a class.
The class interacts with the view through an API of properties and methods.
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
The service is provided to the component through the dependency injection system.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"></code-example>
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
<hr/>
## Component metadata
<img src="generated/images/guide/architecture/metadata.png" alt="Metadata" class="left">
The `@Component` decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that `HeroListComponent` is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the `@Component` decorator.
The metadata for a component tells Angular where to get the major building blocks it needs to create and present the component and its view. In particular, it associates a _template_ with the component, either directly with inline code, or by reference. Together, the component and its template describe a _view_.
The metadata for a component tells Angular where to get the major building blocks that it needs to create and present the component and its view. In particular, it associates a *template* with the component, either directly with inline code, or by reference. Together, the component and its template describe a *view*.
In addition to containing or pointing to the template, the `@Component` metadata configures, for example, how the component can be referenced in HTML and what services it requires.
Here's an example of basic metadata for `HeroListComponent`:
Here's an example of basic metadata for `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
This example shows some of the most useful `@Component` configuration options:
This example shows some of the most useful `@Component` configuration options:
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's _host view_.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
<hr/>
## Templates and views
@ -50,7 +49,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
You define a component's view with its companion template. A template is a form of HTML that tells Angular how to render the component.
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's _host view_. The component can also define a _view hierarchy_, which contains _embedded views_, hosted by other components.
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's *host view*. The component can also define a *view hierarchy*, which contains *embedded views*, hosted by other components.
<figure>
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
@ -60,43 +59,47 @@ A view hierarchy can include views from components in the same NgModule, but it
## Template syntax
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use _data binding_ to coordinate the app and DOM data, _pipes_ to transform data before it is displayed, and _directives_ to apply app logic to what gets displayed.
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use *data binding* to coordinate the app and DOM data, *pipes* to transform data before it is displayed, and *directives* to apply app logic to what gets displayed.
For example, here is a template for the Tutorial's `HeroListComponent`:
For example, here is a template for the Tutorial's `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.html" title="src/app/hero-list.component.html"></code-example>
This template uses typical HTML elements like `<h2>` and `<p>`, and also includes Angular template-syntax elements, `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<app-hero-detail>`. The template-syntax elements tell Angular how to render the HTML to the screen, using program logic and data.
* The `*ngFor` directive tells Angular to iterate over a list.
* The `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`. The `HeroDetailComponent` (code not shown) is a child component of the `HeroListComponent` that defines the Hero-detail view. Notice how custom components like this mix seamlessly with native HTML in the same layouts.
* The `*ngFor` directive tells Angular to iterate over a list.
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
### Data binding
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push and pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
Angular supports *two-way data binding*, a mechanism for coordinating parts of a template with parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
Angular supports *two-way data binding*, a mechanism for coordinating the parts of a template with the parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
The following diagram shows the four forms of data binding markup. Each form has a direction&mdash;to the DOM, from the DOM, or in both directions.
The following diagram shows the four forms of data binding markup. Each form has a direction: to the DOM, from the DOM, or both.
<figure>
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
</figure>
This example from the `HeroListComponent` template uses three of these forms:
This example from the `HeroListComponent` template uses three of these forms.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding"></code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
displays the component's `hero.name` property value within the `<li>` element.
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of `selectedHero` from
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of
`selectedHero` from the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
**Two-way data binding** is an important fourth form that combines property and event binding in a single notation. Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive:
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
@ -104,7 +107,7 @@ In two-way binding, a data property value flows to the input box from the compon
The user's changes also flow back to the component, resetting the property to the latest value,
as with event binding.
Angular processes *all* data bindings once per JavaScript event cycle,
Angular processes *all* data bindings once for each JavaScript event cycle,
from the root of the application component tree through all child components.
<figure>
@ -119,17 +122,17 @@ Data binding plays an important role in communication between a template and its
### Pipes
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe):
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe).
`{{interpolated_value | pipe_name}}`
`{{interpolated_value | pipe_name}}`
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe:
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe.
```
```
<!-- Default format: output 'Jun 15, 2015'-->
<p>Today is {{today | date}}</p>
@ -140,33 +143,38 @@ Data binding plays an important role in communication between a template and its
<p>The time is {{today | date:'shortTime'}}</p>
```
<hr/>
### Directives
<img src="generated/images/guide/architecture/directive.png" alt="Directives" class="left">
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive` decorator.
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive()` decorator.
A component is technically a directive - but components are so distinctive and central to Angular applications that Angular defines the `@Component` decorator, which extends the `@Directive` decorator with template-oriented features.
A component is technically a directive.
However, components are so distinctive and central to Angular applications that Angular
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
template-oriented features.
There are two kinds of directives besides components: _structural_ and _attribute_ directives. Just as for components, the metadata for a directive associates the class with a `selector` that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
#### Structural directives
Structural directives alter layout by adding, removing, and replacing elements in DOM. The example template uses two built-in structural directives to add application logic to how the view is rendered:
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
The example template uses two built-in structural directives to add application logic to how the view is rendered.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural"></code-example>
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
#### Attribute directives
Attribute directives alter the appearance or behavior of an existing element.
*Attribute directives* alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically an `<input>`) by setting its display value property and responding to change events.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
@ -175,6 +183,8 @@ Angular has more pre-defined directives that either alter the layout structure
or modify aspects of DOM elements and components
(for example, [ngStyle](guide/template-syntax#ngStyle) and [ngClass](guide/template-syntax#ngClass)).
You can also write your own directives. Components such as `HeroListComponent` are one kind of custom directive. You can also create custom structural and attribute directives.
<div class="alert is-helpful">
<!-- PENDING: link to where to learn more about other kinds! -->
Learn more in the [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives) guides.
</div>

View File

@ -1,40 +1,39 @@
# Introduction to modules
<img src="generated/images/guide/architecture/module.png" alt="Module" class="left">
Angular apps are modular and Angular has its own modularity system called *NgModules*.
NgModules are containers for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. They can contain components, service providers, and other code files whose scope is defined by the containing NgModule. They can import functionality that is exported from other NgModules, and export selected functionality for use by other NgModules.
Angular apps are modular and Angular has its own modularity system called _NgModules_. An NgModule is a container for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. It can contain components, service providers, and other code files whose scope is defined by the containing NgModule. It can import functionality that is exported from other NgModules, and export selected functionality for use by other NgModules.
Every Angular app has at least one NgModule class, [the *root module*](guide/bootstrapping), which is conventionally named `AppModule` and resides in a file named `app.module.ts`. You launch your app by *bootstrapping* the root NgModule.
Every Angular app has at least one NgModule class, [the _root module_](guide/bootstrapping), which is conventionally named `AppModule` and resides in a file named `app.module.ts`. You launch your app by *bootstrapping* the root NgModule.
While a small application might have only one NgModule, most apps have many more _feature modules_. The _root_ NgModule for an app is so named because it can include child NgModules in a hierarchy of any depth.
While a small application might have only one NgModule, most apps have many more *feature modules*. The *root* NgModule for an app is so named because it can include child NgModules in a hierarchy of any depth.
## NgModule metadata
An NgModule is defined as a class decorated with `@NgModule`. The `@NgModule` decorator is a function that takes a single metadata object, whose properties describe the module. The most important properties are as follows.
An NgModule is defined by a class decorated with `@NgModule()`. The `@NgModule()` decorator is a function that takes a single metadata object, whose properties describe the module. The most important properties are as follows.
* `declarations`&mdash;The [components](guide/architecture-components), _directives_, and _pipes_ that belong to this NgModule.
* `declarations`: The [components](guide/architecture-components), *directives*, and *pipes* that belong to this NgModule.
* `exports`&mdash;The subset of declarations that should be visible and usable in the _component templates_ of other NgModules.
* `exports`: The subset of declarations that should be visible and usable in the *component templates* of other NgModules.
* `imports`&mdash;Other modules whose exported classes are needed by component templates declared in _this_ NgModule.
* `imports`: Other modules whose exported classes are needed by component templates declared in *this* NgModule.
* `providers`&mdash;Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level, which is often preferred.)
* `providers`: Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level, which is often preferred.)
* `bootstrap`&mdash;The main application view, called the _root component_, which hosts all other app views. Only the _root NgModule_ should set this `bootstrap` property.
* `bootstrap`: The main application view, called the *root component*, which hosts all other app views. Only the *root NgModule* should set the `bootstrap` property.
Here's a simple root NgModule definition:
Here's a simple root NgModule definition.
<code-example path="architecture/src/app/mini-app.ts" region="module" title="src/app/app.module.ts" linenums="false"></code-example>
<div class="alert is-helpful">
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root NgModule has no reason to _export_ anything because other modules don't need to _import_ the root NgModule.
The `export` property of `AppComponent` is included here for illustration; it isn't actually necessary in this example. A root NgModule has no reason to *export* anything because other modules don't need to *import* the root NgModule.
</div>
## NgModules and components
NgModules provide a _compilation context_ for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
NgModules provide a *compilation context* for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
<figure>
@ -44,7 +43,7 @@ NgModules provide a _compilation context_ for their components. A root NgModule
<br class="clear">
A component and its template together define a _view_. A component can contain a _view hierarchy_, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
A component and its template together define a *view*. A component can contain a *view hierarchy*, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
<figure>
@ -54,17 +53,17 @@ A component and its template together define a _view_. A component can contain a
<br class="clear">
When you create a component, it is associated directly with a single view, called the _host view_. The host view can be the root of a view hierarchy, which can contain _embedded views_, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
When you create a component, it's associated directly with a single view, called the *host view*. The host view can be the root of a view hierarchy, which can contain *embedded views*, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
<div class="alert is-helpful">
The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
**Note:** The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
</div>
## NgModules and JavaScript modules
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are two different and _complementary_ module systems. You can use them both to write your apps.
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are *complementary* module systems that you can use together to write your apps.
In JavaScript each _file_ is a module and all objects defined in the file belong to that module.
In JavaScript each *file* is a module and all objects defined in the file belong to that module.
The module declares some objects to be public by marking them with the `export` key word.
Other JavaScript modules use *import statements* to access public objects from other modules.
@ -80,29 +79,28 @@ Other JavaScript modules use *import statements* to access public objects from o
<img src="generated/images/guide/architecture/library-module.png" alt="Component" class="left">
Angular ships as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the `npm` package manager and import parts of them with JavaScript `import` statements.
Angular loads as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the `npm` package manager and import parts of them with JavaScript `import` statements.
<br class="clear">
For example, import Angular's `Component` decorator from the `@angular/core` library like this:
For example, import Angular's `Component` decorator from the `@angular/core` library like this.
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
You also import NgModules from Angular _libraries_ using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library:
You also import NgModules from Angular *libraries* using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library.
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
In the example of the simple root module above, the application module needs material from within the `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
In the example of the simple root module above, the application module needs material from within
`BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"></code-example>
In this way you're using both the Angular and JavaScript module systems _together_. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
In this way you're using the Angular and JavaScript module systems *together*. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
<div class="alert is-helpful">
Learn more from the [NgModules](guide/ngmodules) page.
Learn more from the [NgModules](guide/ngmodules) guide.
</div>
<hr/>

View File

@ -1,48 +1,50 @@
# Next steps: tools and techniques
Once you have understood the basic building blocks, you can begin to learn more about the features and tools that are available to help you develop and deliver Angular applications. Angular provides a lot more features and services that are covered in this documentation.
After you understand the basic Angular building blocks, you can begin to learn more
about the features and tools that are available to help you develop and deliver Angular applications.
Here are some key features.
#### Responsive programming tools
## Responsive programming tools
* [Lifecycle hooks](guide/lifecycle-hooks): Tap into key moments in the lifetime of a component, from its creation to its destruction, by implementing the lifecycle hook interfaces.
* [Lifecycle hooks](guide/lifecycle-hooks): Tap into key moments in the lifetime of a component, from its creation to its destruction, by implementing the lifecycle hook interfaces.
* [Observables and event processing](guide/observables): How to use observables with components and services to publish and subscribe to messages of any type, such as user-interaction events and asynchronous operation results.
* [Observables and event processing](guide/observables): How to use observables with components and services to publish and subscribe to messages of any type, such as user-interaction events and asynchronous operation results.
#### Client-server interaction tools
## Client-server interaction tools
* [HTTP](guide/http): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
* [HTTP](guide/http): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
* [Service Workers](guide/service-worker-intro): A service worker is a script that runs in the web browser and manages caching for an application. Service workers function as a network proxy. They intercept outgoing HTTP requests and can, for example, deliver a cached response if one is available. You can significantly improve the user experience by using a service worker to reduce dependency on the network.
* [Service Workers](guide/service-worker-intro): Use a service worker to reduce dependency on the network
significantly improving the use experience.
#### Domain-specific libraries
## Domain-specific libraries
* [Animations](guide/animations): Animate component behavior
without deep knowledge of animation techniques or CSS with Angular's animation library.
* [Animations](guide/animations): Use Angular's animation library to animate component behavior
without deep knowledge of animation techniques or CSS.
* [Forms](guide/forms): Support complex data entry scenarios with HTML-based validation and dirty checking.
* [Forms](guide/forms): Support complex data entry scenarios with HTML-based validation and dirty checking.
#### Support for the development cycle
## Support for the development cycle
* [Testing Platform](guide/testing): Run unit tests on your application parts as they interact with the Angular framework.
* [Testing platform](guide/testing): Run unit tests on your application parts as they interact with the Angular framework.
* [Internationalization](guide/i18n): Angular's internationalization (i18n) tools can help you make your app available in multiple languages.
* [Internationalization](guide/i18n): Make your app available in multiple languages with Angular's internationalization (i18n) tools.
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
* [Security guidelines](guide/security): Learn about Angular's built-in protections against common web-app vulnerabilities and attacks such as cross-site scripting attacks.
* [Security guidelines](guide/security): Learn about Angular's built-in protections against common web-app vulnerabilities and attacks such as cross-site scripting attacks.
#### Setup and deployment tools
## Setup and deployment tools
* [Setup for local development](guide/setup): Learn how to set up a new project for development with QuickStart.
* [Setup for local development](guide/setup): Set up a new project for development with QuickStart.
* [Installation](guide/npm-packages): The [Angular CLI](https://cli.angular.io/), Angular applications, and Angular itself depend on features and functionality provided by libraries that are available as [npm](https://docs.npmjs.com/) packages.
* [Installation](guide/npm-packages): The [Angular CLI](https://cli.angular.io/), Angular applications, and Angular itself depend on features and functionality provided by libraries that are available as [npm](https://docs.npmjs.com/) packages.
* [Typescript Configuration](guide/typescript-configuration): TypeScript is the primary language for Angular application development.
* [TypeScript configuration](guide/typescript-configuration): TypeScript is the primary language for Angular application development.
* [Browser support](guide/browser-support): Learn how to make your apps compatible across a wide range of browsers.
* [Browser support](guide/browser-support): Make your apps compatible across a wide range of browsers.
* [Deployment](guide/deployment): Learn techniques for deploying your Angular application to a remote server.
* [Deployment](guide/deployment): Learn techniques for deploying your Angular application to a remote server.
<hr/>

View File

@ -1,22 +1,32 @@
# Introduction to services and dependency injection
<img src="generated/images/guide/architecture/service.png" alt="Service" class="left">
*Service* is a broad category encompassing any value, function, or feature that an app needs.
A service is typically a class with a narrow, well-defined purpose.
It should do something specific and do it well.
_Service_ is a broad category encompassing any value, function, or feature that an app needs. A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
<br class="clear">
Angular distinguishes components from services to increase modularity and reusability.
By separating a component's view-related functionality from other kinds of processing,
you can make your component classes lean and efficient.
Angular distinguishes components from services in order to increase modularity and reusability.
Ideally, a component's job is to enable the user experience and nothing more.
A component should present properties and methods for data binding,
in order to mediate between the view (rendered by the template)
and the application logic (which often includes some notion of a *model*).
* By separating a component's view-related functionality from other kinds of processing, you can make your component classes lean and efficient. Ideally, a component's job is to enable the user experience and nothing more. It should present properties and methods for data binding, in order to mediate between the view (rendered by the template) and the application logic (which often includes some notion of a _model_).
A component can delegate certain tasks to services, such as fetching data from the server,
validating user input, or logging directly to the console.
By defining such processing tasks in an *injectable service class*, you make those tasks
available to any component.
You can also make your app more adaptable by injecting different providers of the same kind of service,
as appropriate in different circumstances.
* A component should not need to define things like how to fetch data from the server, validate user input, or log directly to the console. Instead, it can delegate such tasks to services. By defining that kind of processing task in an injectable service class, you make it available to any component. You can also make your app more adaptable by injecting different providers of the same kind of service, as appropriate in different circumstances.
Angular doesn't *enforce* these principles. Angular does help you *follow* these principles by making it easy to factor your
application logic into services and make those services available to components through *dependency injection*.
Angular doesn't *enforce* these principles. Angular does help you *follow* these principles
by making it easy to factor your application logic into services and make those services
available to components through *dependency injection*.
## Service examples
Here's an example of a service class that logs to the browser console:
Here's an example of a service class that logs to the browser console.
<code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class"></code-example>
@ -24,35 +34,41 @@ Services can depend on other services. For example, here's a `HeroService` that
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class"></code-example>
<hr/>
## Dependency injection
## Dependency injection (DI)
<img src="generated/images/guide/architecture/dependency-injection.png" alt="Service" class="left">
DI is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
To define a class as a service in Angular, use the `@Injectable` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
Similarly, use the `@Injectable` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) _has_ a dependency. A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
* The *injector* is the main mechanism. Angular creates an application-wide injector for you during the bootstrap process, and additional injectors as needed. You don't have to create injectors.
*Dependency injection* (often called DI) is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
* An injector creates dependencies, and maintains a *container* of dependency instances that it reuses if possible.
* The *injector* is the main mechanism. You don't have to create an Angular injector. Angular creates an application-wide injector for you during the bootstrap process.
* A *provider* is an object that tell an injector how to obtain or create a dependency.
* The injector maintains a *container* of dependency instances that it has already created, and reuses them if possible.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For a service, the provider is typically the service class itself.
* A *provider* is a recipe for creating a dependency. For a service, this is typically the service class itself. For any dependency you need in your app, you must register a provider with the app's injector, so that the injector can use it to create new instances.
<div class="alert is-helpful">
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the types of its constructor parameters. For example, the constructor of `HeroListComponent` needs a `HeroService`:
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
</div>
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types. For example, the constructor of `HeroListComponent` needs `HeroService`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
When Angular discovers that a component depends on a service, it first checks if the injector already has any existing instances of that service. If a requested service instance does not yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments.
The process of `HeroService` injection looks something like this:
The process of `HeroService` injection looks something like this.
<figure>
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
@ -60,35 +76,41 @@ The process of `HeroService` injection looks something like this:
### Providing services
You must register at least one *provider* of any service you are going to use. A service can register providers itself, making it available everywhere, or you can register providers with specific modules or components. You register providers in the metadata of the service (in the `@Injectable` decorator), or in the `@NgModule` or `@Component` metadata
You must register at least one *provider* of any service you are going to use.
The provider can be part of the service's own metadata, making that service available everywhere,
or you can register providers with specific modules or components.
You register providers in the metadata of the service (in the `@Injectable()` decorator),
or in the `@NgModule()` or `@Component()` metadata
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable` decorator. The tutorial uses this method to register the provider of HeroService class definition:
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
```
@Injectable({
providedIn: 'root',
})
```
```
@Injectable({
providedIn: 'root',
})
```
When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects into any class that asks for it. Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService`
and injects it into any class that asks for it.
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
by removing the service from the compiled app if it isn't used.
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule` decorator:
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
```
@NgModule({
providers: [
BackendService,
Logger
],
...
})
```
```
@NgModule({
providers: [
BackendService,
Logger
],
...
})
```
* When you register a provider at the component level, you get a new instance of the
service with each new instance of that component. At the component level, register a service provider in the `providers` property of the `@Component` metadata:
service with each new instance of that component.
At the component level, register a service provider in the `providers` property of the `@Component()` metadata.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
For more detailed information, see the [Dependency Injection](guide/dependency-injection) section.
<hr/>

View File

@ -1,11 +1,11 @@
# Architecture overview
Angular is a platform and framework for building client applications in HTML and TypeScript.
Angular is itself written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
Angular is written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
The basic building blocks of an Angular application are _NgModules_, which provide a compilation context for _components_. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a _root module_ that enables bootstrapping, and typically has many more _feature modules_.
The basic building blocks of an Angular application are *NgModules*, which provide a compilation context for *components*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*.
* Components define *views*, which are sets of screen elements that Angular can choose among and modify according to your program logic and data. Every app has at least a root component.
* Components define *views*, which are sets of screen elements that Angular can choose among and modify according to your program logic and data.
* Components use *services*, which provide specific functionality not directly related to views. Service providers can be *injected* into components as *dependencies*, making your code modular, reusable, and efficient.
@ -13,19 +13,19 @@ Both components and services are simply classes, with *decorators* that mark the
* The metadata for a component class associates it with a *template* that defines a view. A template combines ordinary HTML with Angular *directives* and *binding markup* that allow Angular to modify the HTML before rendering it for display.
* The metadata for a service class provides the information Angular needs to make it available to components through *Dependency Injection (DI)*.
* The metadata for a service class provides the information Angular needs to make it available to components through *dependency injection (DI)*.
An app's components typically define many views, arranged hierarchically. Angular provides the `Router` service to help you define navigation paths among views. The router provides sophisticated in-browser navigational capabilities.
## Modules
Angular defines the `NgModule`, which differs from and complements the JavaScript (ES2015) module. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
Angular *NgModules* differ from and complement JavaScript (ES2015) modules. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
Every Angular app has a _root module_, conventionally named `AppModule`, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.
Every Angular app has a *root module*, conventionally named `AppModule`, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.
Like JavaScript modules, NgModules can import functionality from other NgModules, and allow their own functionality to be exported and used by other NgModules. For example, to use the router service in your app, you import the `Router` NgModule.
Organizing your code into distinct functional modules helps in managing development of complex applications, and in designing for reusability. In addition, this technique lets you take advantage of _lazy-loading_&mdash;that is, loading modules on demand&mdash;in order to minimize the amount of code that needs to be loaded at startup.
Organizing your code into distinct functional modules helps in managing development of complex applications, and in designing for reusability. In addition, this technique lets you take advantage of *lazy-loading*&mdash;that is, loading modules on demand&mdash;to minimize the amount of code that needs to be loaded at startup.
<div class="alert is-helpful">
@ -35,13 +35,13 @@ Organizing your code into distinct functional modules helps in managing developm
## Components
Every Angular application has at least one component, the *root component* that connects a component hierarchy with the page DOM. Each component defines a class that contains application data and logic, and is associated with an HTML *template* that defines a view to be displayed in a target environment.
Every Angular application has at least one component, the *root component* that connects a component hierarchy with the page document object model (DOM). Each component defines a class that contains application data and logic, and is associated with an HTML *template* that defines a view to be displayed in a target environment.
The `@Component` decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
The `@Component()` decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
<div class="alert is-helpful">
Decorators are functions that modify JavaScript classes. Angular defines a number of such decorators that attach specific kinds of metadata to classes, so that it knows what those classes mean and how they should work.
Decorators are functions that modify JavaScript classes. Angular defines a number of decorators that attach specific kinds of metadata to classes, so that the system knows what those classes mean and how they should work.
<a href="https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.x5c2ndtx0">Learn more about decorators on the web.</a>
@ -49,15 +49,18 @@ The `@Component` decorator identifies the class immediately below it as a compon
### Templates, directives, and data binding
A template combines HTML with Angular markup that can modify the HTML elements before they are displayed.
Template *directives* provide program logic, and *binding markup* connects your application data and the document object model (DOM).
A template combines HTML with Angular markup that can modify HTML elements before they are displayed.
Template *directives* provide program logic, and *binding markup* connects your application data and the DOM.
There are two types of data binding:
* *Event binding* lets your app respond to user input in the target environment by updating your application data.
* *Property binding* lets you interpolate values that are computed from your application data into the HTML.
Before a view is displayed, Angular evaluates the directives and resolves the binding syntax in the template to modify the HTML elements and the DOM, according to your program data and logic. Angular supports *two-way data binding*, meaning that changes in the DOM, such as user choices, can also be reflected back into your program data.
Before a view is displayed, Angular evaluates the directives and resolves the binding syntax in the template to modify the HTML elements and the DOM, according to your program data and logic. Angular supports *two-way data binding*, meaning that changes in the DOM, such as user choices, are also reflected in your program data.
Your templates can also use *pipes* to improve the user experience by transforming values for display. Use pipes to display, for example, dates and currency values in a way appropriate to the user's locale. Angular provides predefined pipes for common transformations, and you can also define your own.
Your templates can use *pipes* to improve the user experience by transforming values for display.
For example, use pipes to display dates and currency values that are appropriate for a user's locale.
Angular provides predefined pipes for common transformations, and you can also define your own pipes.
<div class="alert is-helpful">
@ -70,13 +73,13 @@ Your templates can also use *pipes* to improve the user experience by transformi
## Services and dependency injection
For data or logic that is not associated with a specific view, and that you want to share across components, you create a *service* class. A service class definition is immediately preceded by the `@Injectable` decorator. The decorator provides the metadata that allows your service to be *injected* into client components as a dependency.
For data or logic that isn't associated with a specific view, and that you want to share across components, you create a *service* class. A service class definition is immediately preceded by the `@Injectable()` decorator. The decorator provides the metadata that allows your service to be *injected* into client components as a dependency.
*Dependency injection* (or DI) lets you keep your component classes lean and efficient. They don't fetch data from the server, validate user input, or log directly to the console; they delegate such tasks to services.
*Dependency injection* (DI) lets you keep your component classes lean and efficient. They don't fetch data from the server, validate user input, or log directly to the console; they delegate such tasks to services.
<div class="alert is-helpful">
For a more detailed discusssion, see [Introduction to services and DI](guide/architecture-services).
For a more detailed discussion, see [Introduction to services and DI](guide/architecture-services).
</div>
@ -85,14 +88,16 @@ For data or logic that is not associated with a specific view, and that you want
The Angular `Router` NgModule provides a service that lets you define a navigation path among the different application states and view hierarchies in your app. It is modeled on the familiar browser navigation conventions:
* Enter a URL in the address bar and the browser navigates to a corresponding page.
* Click links on the page and the browser navigates to a new page.
* Click the browser's back and forward buttons and the browser navigates backward and forward through the history of pages you've seen.
The router maps URL-like paths to views instead of pages. When a user performs an action, such as clicking a link, that would load a new page in the browser, the router intercepts the browser's behavior, and shows or hides view hierarchies.
If the router determines that the current application state requires particular functionality, and the module that defines it has not been loaded, the router can _lazy-load_ the module on demand.
If the router determines that the current application state requires particular functionality, and the module that defines it hasn't been loaded, the router can *lazy-load* the module on demand.
The router interprets a link URL according to your app's view navigation rules and data state. You can navigate to new views when the user clicks a button, selects from a drop box, or in response to some other stimulus from any source. The Router logs activity in the browser's history journal, so the back and forward buttons work as well.
The router interprets a link URL according to your app's view navigation rules and data state. You can navigate to new views when the user clicks a button or selects from a drop box, or in response to some other stimulus from any source. The router logs activity in the browser's history, so the back and forward buttons work as well.
To define navigation rules, you associate *navigation paths* with your components. A path uses a URL-like syntax that integrates your program data, in much the same way that template syntax integrates your views with your program data. You can then apply program logic to choose which views to show or to hide, in response to user input and your own access rules.
@ -119,19 +124,26 @@ You've learned the basics about the main building blocks of an Angular applicati
Each of these subjects is introduced in more detail in the following pages.
* [Modules](guide/architecture-modules)
* [Components](guide/architecture-components)
* [Templates](guide/architecture-components#templates-and-views)
* [Metadata](guide/architecture-components#component-metadata)
* [Introduction to Modules](guide/architecture-modules)
* [Introduction to Components](guide/architecture-components)
* [Templates and views](guide/architecture-components#templates-and-views)
* [Component metadata](guide/architecture-components#component-metadata)
* [Data binding](guide/architecture-components#data-binding)
* [Directives](guide/architecture-components#directives)
* [Pipes](guide/architecture-components#pipes)
* [Services and dependency injection](guide/architecture-services)
* [Introduction to services and dependency injection](guide/architecture-services)
<div class="alert is-helpful">
Note that the code referenced on these pages is available as a <live-example></live-example>.
</div>
When you are familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps](guide/architecture-next-steps).
When you're familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps: tools and techniques](guide/architecture-next-steps).
</div>

View File

@ -89,47 +89,51 @@ promise.then(() => {
The following code snippets illustrate how the same kind of operation is defined using observables and promises.
<table>
<tr>
<th>Operation</th>
<th>Observable</th>
<th>Promise</th>
</tr>
<tr>
<td>Creation</td>
<td>
<pre>new Observable((observer) => {
observer.next(123);
});</pre>
</td>
<td>
<pre>new Promise((resolve, reject) => {
resolve(123);
});</pre>
</td>
</tr>
<tr>
<td>Transform</td>
<td><pre>obs.map((value) => value * 2 );</pre></td>
<td><pre>promise.then((value) => value * 2);</pre></td>
</tr>
<tr>
<td>Subscribe</td>
<td>
<pre>sub = obs.subscribe((value) => {
console.log(value)
});</pre>
</td>
<td>
<pre>promise.then((value) => {
console.log(value);
});</pre>
</td>
</tr>
<tr>
<td>Unsubscribe</td>
<td><pre>sub.unsubscribe();</pre></td>
<td>Implied by promise resolution.</td>
</tr>
<thead>
<tr>
<th>Operation</th>
<th>Observable</th>
<th>Promise</th>
</tr>
</thead>
<tbody>
<tr>
<td>Creation</td>
<td>
<pre>new Observable((observer) => {
observer.next(123);
});</pre>
</td>
<td>
<pre>new Promise((resolve, reject) => {
resolve(123);
});</pre>
</td>
</tr>
<tr>
<td>Transform</td>
<td><pre>obs.map((value) => value * 2 );</pre></td>
<td><pre>promise.then((value) => value * 2);</pre></td>
</tr>
<tr>
<td>Subscribe</td>
<td>
<pre>sub = obs.subscribe((value) => {
console.log(value)
});</pre>
</td>
<td>
<pre>promise.then((value) => {
console.log(value);
});</pre>
</td>
</tr>
<tr>
<td>Unsubscribe</td>
<td><pre>sub.unsubscribe();</pre></td>
<td>Implied by promise resolution.</td>
</tr>
</tbody>
</table>
## Observables compared to events API

View File

@ -410,7 +410,7 @@ Here you see the new and the old implementation side-by-side:
Tree shaking is the ability to remove code that is not referenced in an application from the final bundle. Tree-shakable providers give Angular the ability to remove services that are not used in your application from the final output. This significantly reduces the size of your bundles.
Ideally, if an application is not injecting a service, it should not be included in the final output. However, it turns out that the Angular compiler cannot identify at build time if the service will be required or not. Because it's always possible to inject a service directly using `injector.get(Service)`, Angular cannot identify all of the places in your code where this injection could happen, so it has no choice but to include the service in the injector regardless. Thus, services provided in modules are not tree-shakeable.
Ideally, if an application is not injecting a service, it should not be included in the final output. However, it turns out that the Angular compiler cannot identify at build time if the service will be required or not. Because it's always possible to inject a service directly using `injector.get(Service)`, Angular cannot identify all of the places in your code where this injection could happen, so it has no choice but to include the service in the injector regardless. Thus, services provided in modules are not tree-shakable.
Let us consider an example of non-tree-shakable providers in Angular.
@ -424,13 +424,13 @@ This module can then be imported into your application module, to make the servi
When `ngc` runs, it compiles AppModule into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services.
Tree-shaking doesn't work in the method above because Angular cannot decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakeable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself.
Tree-shaking doesn't work in the method above because Angular cannot decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself.
#### Creating tree-shakable providers
To create providers that are tree-shakable, the information that used to be specified in the module should be specified in the `@Injectable` decorator on the service itself.
The following example shows the tree-shakeable equivalent to the `ServiceModule` example above:
The following example shows the tree-shakable equivalent to the `ServiceModule` example above:
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" title="src/app/tree-shaking/service.ts" linenums="false"> </code-example>
@ -690,7 +690,7 @@ If the factory function needs access to other DI tokens, it can use the inject f
<code-example>
const TOKEN =
new InjectionToken('tree-shakeable token',
new InjectionToken('tree-shakable token',
{ providedIn: 'root', factory: () =>
new AppConfig(inject(Parameter1), inject(Parameter2)), });
</code-example>

View File

@ -284,4 +284,71 @@ This completes the cross validation example. We managed to:
- validate the form based on the values of two sibling controls,
- show a descriptive error message after the user interacted with the form and the validation failed.
## Async Validation
This section shows how to create asynchronous validators. It assumes some basic knowledge of creating [custom validators](guide/form-validation#custom-validators).
### The Basics
Just like synchronous validators have the `ValidatorFn` and `Validator` interfaces, asynchronous validators have their own counterparts: `AsyncValidatorFn` and `AsyncValidator`.
They are very similar with the only difference being:
* They must return a Promise or an Observable,
* The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`.
It is important to note that the asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful. This check allows forms to avoid potentially expensive async validation processes such as an HTTP request if more basic validation methods fail.
After asynchronous validation begins, the form control enters a `pending` state. You can inspect the control's `pending` property and use it to give visual feedback about the ongoing validation.
A common UI pattern is to show a spinner while the async validation is being performed. The following example presents how to achieve this with template-driven forms:
```html
<input [(ngModel)}="name" #model="ngModel" appSomeAsyncValidator>
<app-spinner *ngIf="model.pending"></app-spinner>
```
### Implementing Custom Async Validator
In the following section, validation is performed asynchronously to ensure that our heroes pick an alter ego that is not already taken. New heroes are constantly enlisting and old heroes are leaving the service. That means that we do not have the list of available alter egos ahead of time.
To validate the potential alter ego, we need to consult a central database of all currently enlisted heroes. The process is asynchronous, so we need a special validator for that.
Let's start by creating the validator class.
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:
```typescript
interface HeroesService {
isAlterEgoTaken: (alterEgo: string) => Observable<boolean>;
}
```
In a real world application, the `HeroesService` is responsible for making an HTTP request to the hero database to check if the alter ego is available. From the validator's point of view, the actual implementation of the service is not important, so we can just code against the `HeroesService` interface.
As the validation begins, the `UniqueAlterEgoValidator` delegates to the `HeroesService` `isAlterEgoTaken()` method with the current control value. At this point the control is marked as `pending` and remains in this state until the observable chain returned from the `validate()` method completes.
The `isAlterEgoTaken()` method dispatches an HTTP request that checks if the alter ego is available, and returns `Observable<boolean>` as the result. We pipe the response through the `map` operator and transform it into a validation result. As always, we return `null` if the form is valid, and `ValidationErrors` if it is not. We make sure to handle any potential errors with the `catchError` operator.
Here we decided that `isAlterEgoTaken()` error is treated as a successful validation, because failure to make a validation request does not necessarily mean that the alter ego is invalid. You could handle the error differently and return the `ValidationError` object instead.
After some time passes, the observable chain completes and the async validation is done. The `pending` flag is set to `false`, and the form validity is updated.
### Note on performance
By default, all validators are run after every form value change. With synchronous validators, this will not likely have a noticeable impact on application performance. However, it's common for async validators to perform some kind of HTTP request to validate the control. Dispatching an HTTP request after every keystroke could put a strain on the backend API, and should be avoided if possible.
We can delay updating the form validity by changing the `updateOn` property from `change` (default) to `submit` or `blur`.
With template-driven forms:
```html
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
```
With reactive forms:
```typescript
new FormControl('', {updateOn: 'blur'});
```
**You can run the <live-example></live-example> to see the complete reactive and template-driven example code.**

View File

@ -73,11 +73,11 @@ Typically you dont interact with the compiler directly; rather, you use it in
**@angular/forms**: support for both [template-driven](guide/forms) and [reactive forms](guide/reactive-forms).
**@angular/http**: Angular's old, soon-to-be-deprecated, HTTP client.
**@angular/http**: Angular's old, deprecated, HTTP client.
**@angular/platform-browser**: Everything DOM and browser related, especially
the pieces that help render into the DOM.
This package also includes the `bootstrapStatic()` method
This package also includes the `bootstrapModuleFactory()` method
for bootstrapping applications for production builds that pre-compile with [AOT](guide/aot-compiler).
**@angular/platform-browser-dynamic**: Includes [Providers](api/core/Provider)

View File

@ -6,8 +6,8 @@ This guide offers tips and techniques for unit and integration testing Angular a
The guide presents tests of a sample CLI application that is much like the [_Tour of Heroes_ tutorial](tutorial).
The sample application and all tests in this guide are available for inspection and experimentation:
* <live-example embedded-style>Sample app</live-example>
* <live-example stackblitz="specs">Tests</live-example>
- <live-example embedded-style>Sample app</live-example>
- <live-example stackblitz="specs">Tests</live-example>
<hr>
@ -84,7 +84,7 @@ The test file extension **must be `.spec.ts`** so that tooling can identify it a
</div>
The `app.component.ts` and `app.component.spec.ts` files are siblings in the same folder.
The `app.component.ts` and `app.component.spec.ts` files are siblings in the same folder.
The root file names (`app.component`) are the same for both files.
Adopt these two conventions in your own projects for _every kind_ of test file.
@ -153,7 +153,7 @@ when you use the `TestBed` testing utility to provide and create services.
#### Angular _TestBed_
The `TestBed` is the most important of the Angular testing utilities.
The `TestBed` is the most important of the Angular testing utilities.
The `TestBed` creates a dynamically-constructed Angular _test_ module that emulates
an Angular [@NgModule](guide/ngmodules).
@ -403,11 +403,11 @@ respond to user input and gestures, or integrate with its parent and child compo
None of the _class-only_ tests above can answer key questions about how the
components actually behave on screen.
* Is `Lightswitch.clicked()` bound to anything such that the user can invoke it?
* Is the `Lightswitch.message` displayed?
* Can the user actually select the hero displayed by `DashboardHeroComponent`?
* Is the hero name displayed as expected (i.e, in uppercase)?
* Is the welcome message displayed by the template of `WelcomeComponent`?
- Is `Lightswitch.clicked()` bound to anything such that the user can invoke it?
- Is the `Lightswitch.message` displayed?
- Can the user actually select the hero displayed by `DashboardHeroComponent`?
- Is the hero name displayed as expected (i.e, in uppercase)?
- Is the welcome message displayed by the template of `WelcomeComponent`?
These may not be troubling questions for the simple components illustrated above.
But many components have complex interactions with the DOM elements
@ -625,7 +625,7 @@ For example, the component might render first on the server as part of a strateg
If it doesn't support `querySelector`, the previous test could fail.
The `DebugElement` offers query methods that work for all supported platforms.
These query methods take a _predicate_ function that returns `true` when a node in the `DebugElement` tree matches the selection criteria.
These query methods take a _predicate_ function that returns `true` when a node in the `DebugElement` tree matches the selection criteria.
You create a _predicate_ with the help of a `By` class imported from a
library for the runtime platform. Here's the `By` import for the browser platform:
@ -645,10 +645,10 @@ The following example re-implements the previous test with
Some noteworthy observations:
* The `By.css()` static method selects `DebugElement` nodes
with a [standard CSS selector](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_started/Selectors "CSS selectors").
* The query returns a `DebugElement` for the paragraph.
* You must unwrap that result to get the paragraph element.
- The `By.css()` static method selects `DebugElement` nodes
with a [standard CSS selector](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_started/Selectors 'CSS selectors').
- The query returns a `DebugElement` for the paragraph.
- You must unwrap that result to get the paragraph element.
When you're filtering by CSS selector and only testing properties of a browser's _native element_, the `By.css` approach may be overkill.
@ -706,6 +706,7 @@ Your instinct is to write a test that immediately inspects the `<h1>` like this:
</code-example>
_That test fails_ with the message:
```javascript
expected '' to contain 'Test Tour of Heroes'.
```
@ -739,7 +740,6 @@ the component _before Angular initiates data binding and calls [lifecycle hooks]
Here's another test that changes the component's `title` property _before_ calling `fixture.detectChanges()`.
<code-example
path="testing/src/app/banner/banner.component.spec.ts"
region="after-change">
@ -1051,6 +1051,7 @@ The test must wait at least one full turn of the JavaScript engine before the
value becomes available. The test must become _asynchronous_.
{@a fake-async}
#### Async test with _fakeAsync()_
The following test confirms the expected behavior when the service returns an `ErrorObservable`.
@ -1061,6 +1062,7 @@ The following test confirms the expected behavior when the service returns an `E
</code-example>
Note that the `it()` function receives an argument of the following form.
```javascript
fakeAsync(() => { /* test body */ })`
```
@ -1081,6 +1083,54 @@ In this case, it waits for the error handler's `setTimeout()`;
The `tick` function is one of the Angular testing utilities that you import with `TestBed`.
It's a companion to `fakeAsync` and you can only call it within a `fakeAsync` body.
#### Support more macroTasks
By default `fakeAsync` supports the following `macroTasks`.
- setTimeout
- setInterval
- requestAnimationFrame
- webkitRequestAnimationFrame
- mozRequestAnimationFrame
If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macroTask scheduled in fake async test` error will be thrown.
<code-tabs>
<code-pane
path="testing/src/app/shared/canvas.component.spec.ts"
title="src/app/shared/canvas.component.spec.ts" linenums="false">
</code-pane>
<code-pane
path="testing/src/app/shared/canvas.component.ts"
title="src/app/shared/canvas.component.ts" linenums="false">
</code-pane>
</code-tabs>
If you want to support such case, you need to define the `macroTask` you want to support in `beforeEach`.
For example:
```javascript
beforeEach(() => {
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
{
source: 'HTMLCanvasElement.toBlob',
callbackArgs: [{ size: 200 }]
}
];
});
it('toBlob should be able to run in fakeAsync', fakeAsync(() => {
const canvas: HTMLCanvasElement = document.getElementById('canvas') as HTMLCanvasElement;
let blob = null;
canvas.toBlob(function(b) {
blob = b;
});
tick();
expect(blob.size).toBe(200);
})
);
```
#### Async observables
You might be satisfied with the test coverage of these tests.
@ -1213,8 +1263,7 @@ But it is occasionally necessary.
For example, you can't call `async` or `fakeAsync` when testing
code that involves the `intervalTimer()` or the RxJS `delay()` operator.
Here are two mover versions of the previous test, written with `done()`.
Here are two more versions of the previous test, written with `done()`.
The first one subscribes to the `Observable` exposed to the template by the component's `quote` property.
<code-example
@ -1345,7 +1394,6 @@ A _hot_ observable is already producing values _before_ you subscribe to it.
The [_Router.events_](api/router/Router#events) observable,
which reports router activity, is a _hot_ observable.
RxJS marble testing is a rich subject, beyond the scope of this guide.
Learn about it on the web, starting with the
[official documentation](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md).
@ -1391,9 +1439,9 @@ Here's the component's full definition:
While testing a component this simple has little intrinsic value, it's worth knowing how.
You can use one of these approaches:
* Test it as used by `DashboardComponent`.
* Test it as a stand-alone component.
* Test it as used by a substitute for `DashboardComponent`.
- Test it as used by `DashboardComponent`.
- Test it as a stand-alone component.
- Test it as used by a substitute for `DashboardComponent`.
A quick look at the `DashboardComponent` constructor discourages the first approach:
@ -1513,6 +1561,7 @@ which is perfectly fine for _this component_.
</code-example>
{@a click-helper}
#### _click()_ helper
Clicking a button, an anchor, or an arbitrary HTML element is a common test task.
@ -1708,10 +1757,11 @@ by manipulating the `ActivatedRoute` injected into the component's constructor.
You know how to spy on the `Router` and a data service.
You'll take a different approach with `ActivatedRoute` because
* `paramMap` returns an `Observable` that can emit more than one value
during a test.
* You need the router helper function, `convertToParamMap()`, to create a `ParamMap`.
* Other _routed components_ tests need a test double for `ActivatedRoute`.
- `paramMap` returns an `Observable` that can emit more than one value
during a test.
- You need the router helper function, `convertToParamMap()`, to create a `ParamMap`.
- Other _routed components_ tests need a test double for `ActivatedRoute`.
These differences argue for a re-usable stub class.
@ -1730,8 +1780,8 @@ This sample puts `ActivatedRouteStub` in `testing/activated-route-stub.ts`.
<div class="alert is-helpful">
Consider writing a more capable version of this stub class with
the [_marble testing library_](#marble-testing).
Consider writing a more capable version of this stub class with
the [_marble testing library_](#marble-testing).
</div>
@ -1847,7 +1897,7 @@ The rest are stubs.
{@a no-errors-schema}
#### *NO\_ERRORS\_SCHEMA*
#### _NO_ERRORS_SCHEMA_
In the second approach, add `NO_ERRORS_SCHEMA` to the `TestBed.schemas` metadata.
@ -1876,7 +1926,7 @@ in the component's template that matter for tests.
The `NO_ERRORS_SCHEMA` approach is the easier of the two but don't overuse it.
The `NO_ERRORS_SCHEMA` also prevents the compiler from telling you about the missing
The `NO_ERRORS_SCHEMA` also prevents the compiler from telling you about the missing
components and attributes that you omitted inadvertently or misspelled.
You could waste hours chasing phantom bugs that the compiler would have caught in an instant.
@ -1949,12 +1999,12 @@ A little more setup triggers the initial data binding and gets references to the
Three points of special interest:
1. You can locate the anchor elements with an attached directive using `By.directive`.
1. You can locate the anchor elements with an attached directive using `By.directive`.
1. The query returns `DebugElement` wrappers around the matching elements.
1. The query returns `DebugElement` wrappers around the matching elements.
1. Each `DebugElement` exposes a dependency injector with the
specific instance of the directive attached to that element.
1. Each `DebugElement` exposes a dependency injector with the
specific instance of the directive attached to that element.
The `AppComponent` links to validate are as follows:
@ -2029,11 +2079,11 @@ But there's plenty of template complexity even in this simple form.
Tests that exercise the component need ...
* to wait until a hero arrives before elements appear in the DOM.
* a reference to the title text.
* a reference to the name input box to inspect and set it.
* references to the two buttons so they can click them.
* spies for some of the component and router methods.
- to wait until a hero arrives before elements appear in the DOM.
- a reference to the title text.
- a reference to the name input box to inspect and set it.
- references to the two buttons so they can click them.
- spies for some of the component and router methods.
Even a small form such as this one can produce a mess of tortured conditional setup and CSS element selection.
@ -2139,8 +2189,8 @@ Error: ViewDestroyedError: Attempt to use a destroyed view
A typical approach is to divide the setup logic into two separate `beforeEach()` functions:
1. An async `beforeEach()` that compiles the components
1. A synchronous `beforeEach()` that performs the remaining setup.
1. An async `beforeEach()` that compiles the components
1. A synchronous `beforeEach()` that performs the remaining setup.
To follow this pattern, import the `async()` helper with the other testing symbols.
@ -2245,10 +2295,10 @@ which means you can also specify `providers` and `imports`.
The `HeroDetailComponent` requires a lot of help despite its small size and simple construction.
In addition to the support it receives from the default testing module `CommonModule`, it needs:
* `NgModel` and friends in the `FormsModule` to enable two-way data binding.
* The `TitleCasePipe` from the `shared` folder.
* Router services (which these tests are stubbing).
* Hero data access services (also stubbed).
- `NgModel` and friends in the `FormsModule` to enable two-way data binding.
- The `TitleCasePipe` from the `shared` folder.
- Router services (which these tests are stubbing).
- Hero data access services (also stubbed).
One approach is to configure the testing module from the individual pieces as in this example:
@ -2375,7 +2425,7 @@ The [override metadata object](#metadata-override-object) is a generic defined a
A metadata override object can either add-and-remove elements in metadata properties or completely reset those properties.
This example resets the component's `providers` metadata.
The type parameter, `T`, is the kind of metadata you'd pass to the `@Component` decorator:
The type parameter, `T`, is the kind of metadata you'd pass to the `@Component` decorator:
<code-example format="." language="javascript">
selector?: string;
@ -2475,20 +2525,20 @@ Here are some tests of this component:
A few techniques are noteworthy:
* The `By.directive` predicate is a great way to get the elements that have this directive _when their element types are unknown_.
- The `By.directive` predicate is a great way to get the elements that have this directive _when their element types are unknown_.
* The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not">`:not` pseudo-class</a>
in `By.css('h2:not([highlight])')` helps find `<h2>` elements that _do not_ have the directive.
`By.css('*:not([highlight])')` finds _any_ element that does not have the directive.
- The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not">`:not` pseudo-class</a>
in `By.css('h2:not([highlight])')` helps find `<h2>` elements that _do not_ have the directive.
`By.css('*:not([highlight])')` finds _any_ element that does not have the directive.
* `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction.
But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction.
- `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction.
But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction.
* Angular adds a directive to the injector of the element to which it is applied.
The test for the default color uses the injector of the second `<h2>` to get its `HighlightDirective` instance
and its `defaultColor`.
- Angular adds a directive to the injector of the element to which it is applied.
The test for the default color uses the injector of the second `<h2>` to get its `HighlightDirective` instance
and its `defaultColor`.
* `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
- `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
<hr>
@ -2531,13 +2581,13 @@ Consider adding component tests such as this one:
Debug specs in the browser in the same way that you debug an application.
1. Reveal the karma browser window (hidden earlier).
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on windows; `Command-Option-I` in OSX).
1. Pick the "sources" section.
1. Open the `1st.spec.ts` test file (Control/Command-P, then start typing the name of the file).
1. Set a breakpoint in the test.
1. Refresh the browser, and it stops at the breakpoint.
1. Reveal the karma browser window (hidden earlier).
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on windows; `Command-Option-I` in OSX).
1. Pick the "sources" section.
1. Open the `1st.spec.ts` test file (Control/Command-P, then start typing the name of the file).
1. Set a breakpoint in the test.
1. Refresh the browser, and it stops at the breakpoint.
<figure>
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
@ -2910,7 +2960,6 @@ Here are the most important static methods, in order of likely utility.
</tr>
</table
A few of the `TestBed` instance methods are not covered by static `TestBed` _class_ methods.
These are rarely needed.
@ -3334,9 +3383,9 @@ The following example finds all `DebugElements` with a reference to a template l
The Angular `By` class has three static methods for common predicates:
* `By.all` - return all elements.
* `By.css(selector)` - return elements with matching CSS selectors.
* `By.directive(directive)` - return elements that Angular matched to an instance of the directive class.
- `By.all` - return all elements.
- `By.css(selector)` - return elements with matching CSS selectors.
- `By.directive(directive)` - return elements that Angular matched to an instance of the directive class.
<code-example path="testing/src/app/hero/hero-list.component.spec.ts" region="by" title="app/hero/hero-list.component.spec.ts" linenums="false"></code-example>
@ -3353,11 +3402,11 @@ The Angular `By` class has three static methods for common predicates:
It's a good idea to put unit test spec files in the same folder
as the application source code files that they test:
* Such tests are easy to find.
* You see at a glance if a part of your application lacks tests.
* Nearby tests can reveal how a part works in context.
* When you move the source (inevitable), you remember to move the test.
* When you rename the source file (inevitable), you remember to rename the test file.
- Such tests are easy to find.
- You see at a glance if a part of your application lacks tests.
- Nearby tests can reveal how a part works in context.
- When you move the source (inevitable), you remember to move the test.
- When you rename the source file (inevitable), you remember to rename the test file.
<hr>
@ -3376,6 +3425,7 @@ Of course specs that test the test helpers belong in the `test` folder,
next to their corresponding helper files.
{@a q-e2e}
#### Why not rely on E2E tests of DOM integration?
The component DOM tests described in this guide often require extensive setup and

View File

@ -16,6 +16,12 @@
"rev": true,
"title": "Angular Conferences and Angular Camps in Moscow, Russia.",
"url": "https://angular-ru.github.io/"
},
"made-with-angular": {
"desc": "A showcase of web apps built with Angular.",
"rev": true,
"title": "Made with Angular",
"url": "https://www.madewithangular.com/"
}
}
},
@ -260,6 +266,13 @@
"title": "Nx",
"logo": "https://nrwl.io/assets/nx-logo.png",
"url": "https://nrwl.io/nx"
},
"uijar": {
"desc": "A drop in module to automatically create a living style guide based on the test you write for your components.",
"logo": "",
"rev": true,
"title": "UI-jar - Test Driven Style Guide Development",
"url": "https://github.com/ui-jar/ui-jar"
}
}
},
@ -421,8 +434,8 @@
"-KLIzGEp8Mh5W-FkiQnL": {
"desc": "Your quick, no-nonsense guide to building real-world apps with Angular",
"rev": true,
"title": "Learning Angular",
"url": "https://www.packtpub.com/web-development/learning-angular-2"
"title": "Learning Angular - Second Edition",
"url": "https://www.packtpub.com/web-development/learning-angular-second-edition"
},
"3ab": {
"desc": "More than 15 books from O'Reilly about Angular",
@ -503,6 +516,13 @@
"rev": true,
"title": "The Angular Guide by Wishtack (Français)",
"url": "https://guide-angular.wishtack.io/"
},
"ab5": {
"desc": "How to build Angular applications using NGRX",
"logo": "",
"rev": true,
"title": "Architecting Angular Applications with NGRX",
"url": "https://www.packtpub.com/web-development/architecting-angular-applications-redux"
}
}
},
@ -629,12 +649,24 @@
"rev": true,
"title": "AngularFirebase.com",
"url": "https://angularfirebase.com/"
},
"loiane-angulartraining": {
"desc": "Free Angular course in Portuguese.",
"rev": true,
"title": "Loiane Training (Português)",
"url": "https://loiane.training/course/angular/"
}
}
},
"Workshops & Onsite Training": {
"order": 2,
"resources": {
"-acceleb": {
"desc": "Customized, Instructor-Led Angular Training",
"rev": true,
"title": "Accelebrate",
"url": "https://www.accelebrate.com/angular-training"
},
"-KLIBoFWStce29UCwkvY": {
"desc": "Private Angular Training and Mentoring",
"rev": true,

View File

@ -291,7 +291,8 @@ That header is in the `httpOptions` constant defined in the `HeroService`.
<code-example
path="toh-pt6/src/app/hero.service.ts"
region="http-options">
region="http-options"
title="src/app/hero.service.ts">
</code-example>
Refresh the browser, change a hero name and save your change. Navigating to the previous view is implemented in the `save()` method defined in `HeroDetailComponent`.

View File

@ -68,7 +68,7 @@ function runE2e() {
// that they should run under. Then run each app/spec collection sequentially.
function findAndRunE2eTests(filter, outputFile, shard) {
const shardParts = shard ? shard.split('/') : [0,1];
const shardParts = shard ? shard.split('/') : [0, 1];
const shardModulo = parseInt(shardParts[0], 10);
const shardDivider = parseInt(shardParts[1], 10);
@ -82,11 +82,17 @@ function findAndRunE2eTests(filter, outputFile, shard) {
const status = { passed: [], failed: [] };
return getE2eSpecs(EXAMPLES_PATH, filter)
.then(e2eSpecPaths => {
console.log('All e2e specs:');
logSpecs(e2eSpecPaths);
Object.keys(e2eSpecPaths).forEach(key => {
const value = e2eSpecPaths[key];
e2eSpecPaths[key] = value.filter((p, index) => index % shardDivider === shardModulo);
});
console.log(`E2e specs for shard ${shardParts.join('/')}:`);
logSpecs(e2eSpecPaths);
return e2eSpecPaths.systemjs.reduce((promise, specPath) => {
return promise.then(() => {
const examplePath = path.dirname(specPath);
@ -313,4 +319,16 @@ function loadExampleConfig(exampleFolder) {
return config;
}
// Log the specs (for debugging purposes).
// `e2eSpecPaths` is of type: `{[type: string]: string[]}`
// (where `type` is `systemjs`, `cli, etc.)
function logSpecs(e2eSpecPaths) {
Object.keys(e2eSpecPaths).forEach(type => {
const paths = e2eSpecPaths[type];
console.log(` ${type.toUpperCase()}:`);
console.log(paths.map(p => ` ${p}`).join('\n'));
});
}
runE2e();

View File

@ -8,11 +8,27 @@ module.exports = function computeApiBreadCrumbs(API_DOC_TYPES_TO_RENDER) {
if (API_DOC_TYPES_TO_RENDER.indexOf(doc.docType) !== -1) {
doc.breadCrumbs = [];
doc.breadCrumbs.push({ text: 'API', path: '/api' });
if (doc.moduleDoc) doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
if (isSecondaryEntryPoint(doc)) {
doc.breadCrumbs.push(createPackageBreadcrumb(doc));
}
if (doc.moduleDoc) {
if (isSecondaryEntryPoint(doc.moduleDoc)) {
doc.breadCrumbs.push(createPackageBreadcrumb(doc.moduleDoc));
}
doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
}
doc.breadCrumbs.push({ text: doc.name, path: doc.path });
}
});
}
};
};
function isSecondaryEntryPoint(doc) {
return doc.docType === 'package' && !doc.isPrimaryPackage;
}
function createPackageBreadcrumb(doc) {
return { text: doc.packageInfo.primary.name, path: doc.packageInfo.primary.path };
}

View File

@ -17,11 +17,17 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package'];
const processor = processorFactory(API_DOC_TYPES_TO_RENDER);
const httpPackage = { docType: 'package', name: '@angular/http', id: 'http', path: 'http', isPrimaryPackage: true };
const httpTestingPackage = { docType: 'package', name: '@angular/http/testing', id: 'http/testing', path: 'http/testing', packageInfo: { primary: httpPackage } };
const testRequestClass = { docType: 'class', name: 'TestRequest', path: 'http/testing/test-request', moduleDoc: httpTestingPackage };
const docs = [
{ docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } },
{ docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } },
{ docType: 'guide', name: 'Guide One', path: 'guide/guide-1' },
{ docType: 'package', name: 'testing', id: 'http/testing', path: 'http/testing' },
httpPackage,
httpTestingPackage,
testRequestClass
];
processor.$process(docs);
@ -38,7 +44,18 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
expect(docs[2].breadCrumbs).toBeUndefined();
expect(docs[3].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: 'testing', path: 'http/testing' },
{ text: '@angular/http', path: 'http' },
]);
expect(docs[4].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
]);
expect(docs[5].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
{ text: 'TestRequest', path: 'http/testing/test-request' },
]);
});
});

View File

@ -1,6 +1,6 @@
{% import "lib/githubLinks.html" as github -%}
{% set comma = joiner(',') %}
{% set slash = joiner('/') %}
{% set breadcrumbDelimiter = joiner('&gt;') %}
<article>
{$ github.githubLinks(doc, versionInfo) $}
<div class="breadcrumb">
@ -14,14 +14,11 @@
]
}
</script>
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ slash() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
</div>
<header class="api-header">
<h1>{$ doc.name $}</h1>
<label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label>
{% if doc.deprecated !== undefined %}<label class="api-status-label deprecated">deprecated</label>{% endif %}
{% if doc.experimental !== undefined %}<label class="api-status-label experimental">experimental</label>{% endif %}
{% if doc.stable !== undefined %}<label class="api-status-label stable">stable</label>{% endif %}
{% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %}
</header>
<aio-toc class="embedded"></aio-toc>

View File

@ -1,6 +1,6 @@
{% extends 'base.template.html' -%}
{% macro listItems(items, title) %}
{% macro listItems(items, title, overridePath) %}
{% if items.length %}
<section class="export-list">
<h3>{$ title $}</h3>
@ -8,7 +8,7 @@
{% for item in items %}
<tr>
<td><code class="code-anchor">
<a href="{$ item.path $}">{$ item.name $}</a></code></td>
<a href="{$ overridePath or item.path $}">{$ item.name $}</a></code></td>
<td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td>
</tr>
{% endfor %}
@ -24,12 +24,13 @@
{% include "includes/see-also.html" %}
{% if doc.isPrimaryPackage %}
<h2>Entry points</h2>
{$ listItems([doc.packageInfo.primary], 'Primary') $}
{$ listItems([doc.packageInfo.primary], 'Primary', '#primary-entry-point-exports') $}
{$ listItems(doc.packageInfo.secondary, 'Secondary') $}
{% endif %}
<h2>Exports</h2>
<h2>{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports</h2>
{$ listItems(doc.classes, 'Classes') $}
{$ listItems(doc.decorators, 'Decorators') $}
{$ listItems(doc.functions, 'Functions') $}

View File

@ -8,8 +8,8 @@ source ${currentDir}/scripts/ci/_travis-fold.sh
# TODO(i): wrap into subshell, so that we don't pollute CWD, but not yet to minimize diff collision with Jason
cd ${currentDir}
PACKAGES=(core
compiler
PACKAGES=(compiler
core
common
animations
platform-browser
@ -27,7 +27,8 @@ PACKAGES=(core
service-worker
elements)
TSC_PACKAGES=(compiler-cli
TSC_PACKAGES=(compiler
compiler-cli
language-service
benchpress)
@ -239,7 +240,13 @@ compilePackage() {
# For TSC_PACKAGES items
if containsElement "${3}" "${TSC_PACKAGES[@]}"; then
echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json"
local package_name=$(basename "${2}")
$TSC -p ${1}/tsconfig-build.json
if [[ "${3}" = "compiler" ]]; then
if [[ "${package_name}" = "testing" ]]; then
echo "$(cat ${LICENSE_BANNER}) ${N} export * from './${package_name}/${package_name}'" > ${2}/../${package_name}.d.ts
fi
fi
else
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json"
local package_name=$(basename "${2}")

View File

@ -68,7 +68,26 @@ new as of May 2017 and not very stable yet.
You can use [ibazel] to get a "watch mode" that continuously
keeps the outputs up-to-date as you save sources.
### Various Flags Used For Tests
If you're experiencing problems with seemingly unrelated tests failing, it may be because you're not using the proper flags with your Bazel test runs in Angular.
See also: [`//tools/bazel.rc`](https://github.com/angular/angular/blob/master/tools/bazel.rc) where `--define=ivy=false` is defined as default.
- `--config=debug`: build and launch in debug mode (see [debugging](#debugging) instructions below)
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This is done by generating the [`src/ivy_switch.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch.ts) file from [`ivy_switch_legacy.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_legacy.ts) (default), [`ivy_switch_jit.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_jit.ts), or [`ivy_switch_local.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_local.ts).
- `legacy`: (default behavior) compile against View Engine, e.g. `--define=compile=legacy`
- `jit`: Compile in ivy JIT mode, e.g. `--define=compile=jit`
- `local`: Compile in ivy AOT move, e.g. `--define=compile=local`
- `--test_tag_filters=<tag>`: filter tests down to tags defined in the `tag` config
of your rules in any given `BUILD.bazel`.
- `ivy-jit`: This flag should be set for tests that should be excuted with ivy JIT, e.g. `--test_tag_filters=ivy-jit`. For this, you may have to include `--define=compile=jit`.
- `ivy-local`: Only run tests that have to do with ivy AOT. For this, you may have to include `--define=compile=local`, e.g. `--test_tag_filters=ivy-local`..
- `ivy-only`: Only run ivy related tests, e.g. `--test_tag_filters=ivy-only`.
### Debugging a Node Test
<a id="debugging"></a>
- Open chrome at: [chrome://inspect](chrome://inspect)
- Click on `Open dedicated DevTools for Node` to launch a debugger.
@ -82,7 +101,7 @@ First time setup:
- Go to Debug > Add configuration (in the menu bar) to open `launch.json`
- Add the following to the `configurations` array:
```
```json
{
"name": "Attach (inspect)",
"type": "node",
@ -107,6 +126,7 @@ First time setup:
},
```
**Setting breakpoints directly in your code files may not work in VSCode**. This is because the files you're actually debugging are built files that exist in a `./private/...` folder.
The easiest way to debug a test for now is to add a `debugger` statement in the code
and launch the bazel corresponding test (`bazel test <target> --config=debug`).
@ -153,7 +173,7 @@ Note that Bazel has a `--stamp` argument to `bazel build`, but this has no effec
Bazel supports fetching action results from a cache, allowing a clean build to pick up artifacts from prior builds.
This makes builds incremental, even on CI.
It works because Bazel assigns a content-based hash to all action inputs, which is used as the cache key for the action outputs.
Thanks the the hermeticity property, we can skip executing an action if the inputs hash is already present in the cache.
Thanks to the hermeticity property, we can skip executing an action if the inputs hash is already present in the cache.
Of course, non-hermeticity in an action can cause problems.
At worst, you can fetch a broken artifact from the cache, making your build non-reproducible.

View File

@ -14,11 +14,17 @@ Ctrl + Shift + j.
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import {ApplicationRef} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {enableDebugTools} from '@angular/platform-browser';
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
});
platformBrowserDynamic().bootstrapModule(AppModule)
.then(moduleRef => {
const applicationRef = moduleRef.injector.get(ApplicationRef);
const appComponent = applicationRef.components[0];
enableDebugTools(appComponent);
})
```
### Using debug tools

View File

@ -4,7 +4,7 @@
# found in the LICENSE file at https://angular.io/license
""" Public API surface is re-exported here.
This API is exported for user's building angular from source in downstream
This API is exported for users building angular from source in downstream
projects. The rules from packages/bazel are re-exported here as well
as the ng_setup_workspace repository rule needed when building angular
from source downstream. Alternately, this API is available from the
@ -12,11 +12,13 @@ from source downstream. Alternately, this API is available from the
used in a downstream project.
"""
load("//packages/bazel:index.bzl",
load(
"//packages/bazel:index.bzl",
_ng_module = "ng_module",
_ng_package = "ng_package",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite")
_protractor_web_test_suite = "protractor_web_test_suite",
)
load("//tools:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
ng_module = _ng_module

View File

@ -24,9 +24,8 @@ filegroup(
ANGULAR_TESTING = [
"node_modules/@angular/*/bundles/*-testing.umd.js",
# We use AOT, so the compiler and the dynamic platform-browser should be
# visible only in tests
"node_modules/@angular/compiler/bundles/*.umd.js",
# We use AOT, so the dynamic platform-browser should be visible only in tests
# NOTE that we still need to include the compiler because the core depends on it
"node_modules/@angular/platform-browser-dynamic/bundles/*.umd.js",
]

View File

@ -6,23 +6,30 @@ workspace(name = "bazel_integration_test")
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
strip_prefix = "rules_nodejs-0.10.1",
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.4.zip"],
strip_prefix = "rules_nodejs-0.11.4",
sha256 = "c31c4ead696944a50fad2b3ee9dfbbeffe31a8dcca0b21b9bf5b3e6c6b069801",
)
http_archive(
name = "bazel_skylib",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
strip_prefix = "bazel-skylib-0.3.1",
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
)
http_archive(
name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/8fd9ce0fd9254bde251da0bc373d6cd08e811434.zip",
strip_prefix = "rules_webtesting-8fd9ce0fd9254bde251da0bc373d6cd08e811434",
sha256 = "4baee95fcfadfbaf868707af8accfd1cb98c5d13f808908e0152468bfb47f0f7",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
strip_prefix = "rules_webtesting-0.2.1",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
)
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
strip_prefix = "rules_typescript-0.15.0",
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
strip_prefix = "rules_typescript-0.16.0",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
)
http_archive(

View File

@ -32,6 +32,7 @@ ts_library(
name = "test_lib",
testonly = 1,
srcs = glob(["*.spec.ts"]),
tsconfig = "//src:tsconfig.json",
deps = [":hello-world"],
)

View File

@ -5,6 +5,7 @@ ts_library(
name = "e2e",
testonly = 1,
srcs = ["app.spec.ts"],
tsconfig = ":tsconfig.json",
)
ts_library(

View File

@ -3,24 +3,24 @@
"@angular/animations@file:../../dist/packages-dist/animations":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/bazel@file:../../dist/packages-dist/bazel":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
"@bazel/typescript" "^0.15.0"
"@types/node" "6.0.84"
protobufjs "5.0.0"
"@angular/common@file:../../dist/packages-dist/common":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
chokidar "^1.4.2"
convert-source-map "^1.5.1"
@ -31,22 +31,22 @@
tsickle "^0.32.1"
"@angular/compiler@file:../../dist/packages-dist/compiler":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/core@file:../../dist/packages-dist/core":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
@ -1410,9 +1410,9 @@ tough-cookie@~2.3.3:
dependencies:
punycode "^1.4.1"
tsickle@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.30.0.tgz#7941146ae92933854a8742fa1047606c4536649b"
tsickle@^0.32.1:
version "0.32.1"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.32.1.tgz#f16e94ba80b32fc9ebe320dc94fbc2ca7f3521a5"
dependencies:
jasmine-diff "^0.1.3"
minimist "^1.2.0"

View File

@ -19,6 +19,9 @@ node_modules/zone.js/dist/zone_externs.js
--js node_modules/rxjs/operators/package.json
--js node_modules/rxjs/_esm2015/operators/index.js
--js node_modules/@angular/compiler/package.json
--js node_modules/@angular/compiler/fesm2015/compiler.js
--js node_modules/@angular/core/package.json
--js node_modules/@angular/core/fesm2015/core.js
--js node_modules/@angular/core/src/testability/testability.externs.js

View File

@ -17,6 +17,9 @@ node_modules/zone.js/dist/zone_externs.js
--module_resolution=node
--package_json_entry_names es2015
--js node_modules/@angular/compiler/package.json
--js node_modules/@angular/compiler/fesm2015/compiler.js
--js node_modules/@angular/core/package.json
--js node_modules/@angular/core/fesm2015/core.js
--js node_modules/@angular/core/src/testability/testability.externs.js

View File

@ -9,6 +9,7 @@
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/core": "file:../../dist/packages-dist/core",
"@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",
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
"rxjs": "file:../../node_modules/rxjs",

View File

@ -3,40 +3,48 @@
"@angular/animations@file:../../dist/packages-dist/animations":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/common@file:../../dist/packages-dist/common":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
chokidar "^1.4.2"
convert-source-map "^1.5.1"
magic-string "^0.25.0"
minimist "^1.2.0"
reflect-metadata "^0.1.2"
tsickle "^0.27.2"
source-map "^0.6.1"
tsickle "^0.32.1"
"@angular/compiler@file:../../dist/packages-dist/compiler":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/core@file:../../dist/packages-dist/core":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
tslib "^1.9.0"
"@angular/platform-server@file:../../dist/packages-dist/platform-server":
version "6.0.0-beta.7-8203e0365a"
version "7.0.0-beta.1-d2d510089c"
dependencies:
domino "^2.0.1"
tslib "^1.9.0"
@ -510,6 +518,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
@ -612,6 +624,10 @@ dev-ip@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
diff@^3.2.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
domino@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/domino/-/domino-2.0.1.tgz#9e1d63215d0fe8dcb8202bff07effa1a216db504"
@ -1203,6 +1219,12 @@ jasmine-core@~2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
jasmine-diff@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/jasmine-diff/-/jasmine-diff-0.1.3.tgz#93ccc2dcc41028c5ddd4606558074839f2deeaa8"
dependencies:
diff "^3.2.0"
jasmine@^2.5.3:
version "2.8.0"
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e"
@ -1319,6 +1341,12 @@ lodash@^4.11.1, lodash@^4.5.1:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
magic-string@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.0.tgz#1f3696f9931ff0a1ed4c132250529e19cad6759b"
dependencies:
sourcemap-codec "^1.4.1"
micromatch@2.3.11, micromatch@^2.1.5:
version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
@ -1835,7 +1863,7 @@ rx@4.1.0:
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
"rxjs@file:../../node_modules/rxjs":
version "6.0.0-alpha.4"
version "6.0.0"
dependencies:
tslib "^1.9.0"
@ -2016,10 +2044,14 @@ source-map@^0.5.1, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.0:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
sourcemap-codec@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2"
spawn-command@^0.0.2-1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
@ -2180,10 +2212,11 @@ tree-kill@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
tsickle@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736"
tsickle@^0.32.1:
version "0.32.1"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.32.1.tgz#f16e94ba80b32fc9ebe320dc94fbc2ca7f3521a5"
dependencies:
jasmine-diff "^0.1.3"
minimist "^1.2.0"
mkdirp "^0.5.1"
source-map "^0.6.0"
@ -2204,7 +2237,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
"typescript@file:../../node_modules/typescript":
version "2.7.2"
version "2.9.2"
ua-parser-js@0.7.12:
version "0.7.12"
@ -2424,4 +2457,4 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
"zone.js@file:../../node_modules/zone.js":
version "0.8.20"
version "0.8.26"

View File

@ -84,7 +84,7 @@ module.exports = function(config) {
'dist/all/@angular/elements/schematics/**',
'dist/all/@angular/examples/**/e2e_test/*',
'dist/all/@angular/language-service/**',
'dist/all/@angular/router/test/**',
'dist/all/@angular/router/**/test/**',
'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/all/angular1_router.js',
'dist/examples/**/e2e_test/**',

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "6.1.1",
"version": "7.0.0-beta.2",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -19,7 +19,7 @@
"preskylint": "bazel build --noshow_progress @io_bazel//src/tools/skylark/java/com/google/devtools/skylark/skylint:Skylint",
"skylint": "find . -type f -name \"*.bzl\" ! -path \"*/node_modules/*\" ! -path \"./dist/*\" | xargs $(bazel info bazel-bin)/external/io_bazel/src/tools/skylark/java/com/google/devtools/skylark/skylint/Skylint --disable-checks=deprecated-api",
"prebuildifier": "bazel build --noshow_progress @com_github_bazelbuild_buildtools//buildifier",
"buildifier": "find . -type f \\( -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs $(bazel info bazel-bin)/external/com_github_bazelbuild_buildtools/buildifier/*/buildifier",
"buildifier": "find . -type f \\( -name \"*.bzl\" -or -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs $(bazel info bazel-bin)/external/com_github_bazelbuild_buildtools/buildifier/*/buildifier",
"preinstall": "node tools/yarn/check-yarn.js",
"postinstall": "yarn update-webdriver && node ./tools/postinstall-patches.js && yarn patch-types",
"//patch-types": "work-around for issue https://github.com/angular/angular/issues/25051",
@ -46,12 +46,14 @@
"@types/base64-js": "1.2.5",
"@types/chai": "^4.1.2",
"@types/chokidar": "1.7.3",
"@types/convert-source-map": "^1.5.1",
"@types/diff": "^3.2.2",
"@types/fs-extra": "4.0.2",
"@types/hammerjs": "2.0.35",
"@types/jasmine": "^2.8.8",
"@types/jasminewd2": "^2.0.3",
"@types/minimist": "^1.2.0",
"@types/mock-fs": "^3.6.30",
"@types/node": "6.0.88",
"@types/selenium-webdriver": "3.0.7",
"@types/shelljs": "^0.7.8",
@ -74,6 +76,7 @@
"cldr-data-downloader": "0.3.2",
"cldrjs": "0.5.0",
"conventional-changelog": "1.1.0",
"convert-source-map": "^1.5.1",
"cors": "2.8.4",
"diff": "^3.5.0",
"domino": "2.0.1",
@ -99,7 +102,9 @@
"karma-sauce-launcher": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"madge": "0.5.0",
"magic-string": "^0.25.0",
"minimist": "1.2.0",
"mock-fs": "^4.5.0",
"mutation-observer": "^1.0.3",
"node-uuid": "1.4.8",
"protobufjs": "5.0.0",
@ -112,7 +117,7 @@
"selenium-webdriver": "3.5.0",
"semver": "5.4.1",
"shelljs": "^0.8.1",
"source-map": "0.5.7",
"source-map": "^0.6.1",
"source-map-support": "0.4.18",
"systemjs": "0.18.10",
"tsickle": "0.32",

View File

@ -9,31 +9,42 @@ import {AnimationMetadata, AnimationOptions} from './animation_metadata';
import {AnimationPlayer} from './players/animation_player';
/**
* AnimationBuilder is an injectable service that is available when the {@link
* BrowserAnimationsModule BrowserAnimationsModule} or {@link NoopAnimationsModule
* NoopAnimationsModule} modules are used within an application.
* An injectable service that produces an animation sequence programmatically within an
* Angular component or directive.
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`.
*
* The purpose of this service is to produce an animation sequence programmatically within an
* angular component or directive.
* @usageNotes
*
* Programmatic animations are first built and then a player is created when the build animation is
* attached to an element.
* To use this service, add it to your component or directive as a dependency.
* The service is instantiated along with your component.
*
* Apps do not typically need to create their own animation players, but if you
* do need to, follow these steps:
*
* 1. Use the `build()` method to create a programmatic animation using the
* `animate()` function. The method returns an `AnimationFactory` instance.
*
* 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element.
*
* 3. Use the player object to control the animation programmatically.
*
* For example:
*
* ```ts
* // remember to include the BrowserAnimationsModule module for this to work...
* // import the service from BrowserAnimationsModule
* import {AnimationBuilder} from '@angular/animations';
*
* // require the service as a dependency
* class MyCmp {
* constructor(private _builder: AnimationBuilder) {}
*
* makeAnimation(element: any) {
* // first build the animation
* // first define a reusable animation
* const myAnimation = this._builder.build([
* style({ width: 0 }),
* animate(1000, style({ width: '100px' }))
* ]);
*
* // then create a player from it
* // use the returned factory object to create a player
* const player = myAnimation.create(element);
*
* player.play();
@ -41,22 +52,29 @@ import {AnimationPlayer} from './players/animation_player';
* }
* ```
*
* When an animation is built an instance of {@link AnimationFactory AnimationFactory} will be
* returned. Using that an {@link AnimationPlayer AnimationPlayer} can be created which can then be
* used to start the animation.
*
* @experimental Animation support is experimental.
*/
export abstract class AnimationBuilder {
/**
* Builds a factory for producing a defined animation.
* @param animation A reusable animation definition.
* @returns A factory object that can create a player for the defined animation.
* @see `animate()`
*/
abstract build(animation: AnimationMetadata|AnimationMetadata[]): AnimationFactory;
}
/**
* An instance of `AnimationFactory` is returned from {@link AnimationBuilder#build
* AnimationBuilder.build}.
* A factory object returned from the `AnimationBuilder`.`build()` method.
*
* @experimental Animation support is experimental.
*/
export abstract class AnimationFactory {
/**
* Creates an `AnimationPlayer` instance for the reusable animation defined by
* the `AnimationBuilder`.`build()` method that created this factory.
* Attaches the new player a DOM element.
* @param element The DOM element to which to attach the animation.
* @param options A set of options that can include a time delay and
* additional developer-defined parameters.
*/
abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
}

View File

@ -38,11 +38,33 @@
* @experimental Animation support is experimental.
*/
export interface AnimationEvent {
/**
* The name of the state from which the animation is triggered.
*/
fromState: string;
/**
* The name of the state in which the animation completes.
*/
toState: string;
/**
* The time it takes the animation to complete, in milliseconds.
*/
totalTime: number;
/**
* The animation phase in which the callback was invoked, one of
* "start" or "done".
*/
phaseName: string;
/**
* The element to which the animation is attached.
*/
element: any;
/**
* Internal.
*/
triggerName: string;
/**
* Internal.
*/
disabled: boolean;
}

View File

@ -9,6 +9,14 @@
import {scheduleMicroTask} from '../util';
import {AnimationPlayer} from './animation_player';
/**
* A programmatic controller for a group of reusable animations.
* Used internally to control animations.
*
* @see `AnimationPlayer`
* @see `{@link animations/group group()}`
*
*/
export class AnimationGroupPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = [];
private _onStartFns: Function[] = [];

View File

@ -8,37 +8,110 @@
import {scheduleMicroTask} from '../util';
/**
* AnimationPlayer controls an animation sequence that was produced from a programmatic animation.
* (see {@link AnimationBuilder AnimationBuilder} for more information on how to create programmatic
* animations.)
* Provides programmatic control of a reusable animation sequence,
* built using the `build()` method of `AnimationBuilder`. The `build()` method
* returns a factory, whose `create()` method instantiates and initializes this interface.
*
* @see `AnimationBuilder`
* @see `AnimationFactory`
* @see `animate()`
*
* @experimental Animation support is experimental.
*/
export interface AnimationPlayer {
/**
* Provides a callback to invoke when the animation finishes.
* @param fn The callback function.
* @see `finish()`
*/
onDone(fn: () => void): void;
/**
* Provides a callback to invoke when the animation starts.
* @param fn The callback function.
* @see `run()`
*/
onStart(fn: () => void): void;
/**
* Provides a callback to invoke after the animation is destroyed.
* @param fn The callback function.
* @see `destroy()`
* @see `beforeDestroy()`
*/
onDestroy(fn: () => void): void;
/**
* Initializes the animation.
*/
init(): void;
/**
* Reports whether the animation has started.
* @returns True if the animation has started, false otherwise.
*/
hasStarted(): boolean;
/**
* Runs the animation, invoking the `onStart()` callback.
*/
play(): void;
/**
* Pauses the animation.
*/
pause(): void;
/**
* Restarts the paused animation.
*/
restart(): void;
/**
* Ends the animation, invoking the `onDone()` callback.
*/
finish(): void;
/**
* Destroys the animation, after invoking the `beforeDestroy()` callback.
* Calls the `onDestroy()` callback when destruction is completed.
*/
destroy(): void;
/**
* Resets the animation to its initial state.
*/
reset(): void;
/**
* Sets the position of the animation.
* @param position A 0-based offset into the duration, in milliseconds.
*/
setPosition(position: any /** TODO #9100 */): void;
/**
* Reports the current position of the animation.
* @returns A 0-based offset into the duration, in milliseconds.
*/
getPosition(): number;
/**
* The parent of this player, if any.
*/
parentPlayer: AnimationPlayer|null;
/**
* The total run time of the animation, in milliseconds.
*/
readonly totalTime: number;
/**
* Provides a callback to invoke before the animation is destroyed.
*/
beforeDestroy?: () => any;
/** @internal */
/** @internal
* Internal
*/
triggerCallback?: (phaseName: string) => void;
/** @internal */
/** @internal
* Internal
*/
disabled?: boolean;
}
/**
* @experimental Animation support is experimental.
* An empty programmatic controller for reusable animations.
* Used internally when animations are disabled, to avoid
* checking for the null case when an animation player is expected.
*
* @see `animate()`
* @see `AnimationPlayer`
* @see `GroupPlayer`
*
*/
export class NoopAnimationPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = [];

View File

@ -14,6 +14,7 @@ npm_package(
"package.json",
"//packages/bazel/src:package_assets",
],
packages = ["//packages/bazel/docs"],
# Re-host //packages/bazel/ which is just // in the public distro
replacements = {
"//packages/bazel/": "//",

View File

@ -0,0 +1,16 @@
load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc")
skylark_doc(
name = "docs",
srcs = [
"//packages/bazel/src:ng_module.bzl",
"//packages/bazel/src:ng_rollup_bundle.bzl",
"//packages/bazel/src:ng_setup_workspace.bzl",
"//packages/bazel/src/ng_package:ng_package.bzl",
"//packages/bazel/src/protractor:protractor_web_test.bzl",
],
format = "html",
site_root = "/bazel-builds",
strip_prefix = "packages/bazel/",
visibility = ["//packages/bazel:__subpackages__"],
)

View File

@ -10,12 +10,16 @@ Users should not load files under "/src"
load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module")
load("//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package")
load("//packages/bazel/src/protractor:protractor_web_test.bzl",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite")
load(
"//packages/bazel/src/protractor:protractor_web_test.bzl",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite",
)
ng_module = _ng_module
ng_package = _ng_package
protractor_web_test = _protractor_web_test
protractor_web_test_suite = _protractor_web_test_suite
ng_setup_workspace = _ng_setup_workspace
# DO NOT ADD PUBLIC API without including in the documentation generation
# Run `bazel build //packages/bazel/docs` to verify

View File

@ -19,6 +19,7 @@ nodejs_binary(
# dependency @build_bazel_rules_nodejs_rollup_deps. We don't need any
# additional npm dependencies when we run rollup or uglify.
entry_point = "build_bazel_rules_nodejs_rollup_deps/node_modules/rollup/bin/rollup",
install_source_map_support = False,
node_modules = "@build_bazel_rules_nodejs_rollup_deps//:node_modules",
)
@ -26,4 +27,5 @@ nodejs_binary(
name = "modify_tsconfig",
data = ["modify_tsconfig.js"],
entry_point = "angular/packages/bazel/src/modify_tsconfig.js",
install_source_map_support = False,
)

View File

@ -28,75 +28,78 @@ ESM5Info = provider(
)
def _map_closure_path(file):
result = file.short_path[:-len(".closure.js")]
# short_path is meant to be used when accessing runfiles in a binary, where
# the CWD is inside the current repo. Therefore files in external repo have a
# short_path of ../external/wkspc/path/to/package
# We want to strip the first two segments from such paths.
if (result.startswith("../")):
result = "/".join(result.split("/")[2:])
return result + ".js"
result = file.short_path[:-len(".closure.js")]
# short_path is meant to be used when accessing runfiles in a binary, where
# the CWD is inside the current repo. Therefore files in external repo have a
# short_path of ../external/wkspc/path/to/package
# We want to strip the first two segments from such paths.
if (result.startswith("../")):
result = "/".join(result.split("/")[2:])
return result + ".js"
def _join(array):
return "/".join([p for p in array if p])
return "/".join([p for p in array if p])
def _esm5_outputs_aspect(target, ctx):
if not hasattr(target, "typescript"):
return []
if not hasattr(target, "typescript"):
return []
# We create a new tsconfig.json file that will have our compilation settings
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)
# We create a new tsconfig.json file that will have our compilation settings
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)
workspace = target.label.workspace_root if target.label.workspace_root else ""
workspace = target.label.workspace_root if target.label.workspace_root else ""
# re-root the outputs under a ".esm5" directory so the path don't collide
out_dir = ctx.label.name + ".esm5"
if workspace:
out_dir = out_dir + "/" + workspace
# re-root the outputs under a ".esm5" directory so the path don't collide
out_dir = ctx.label.name + ".esm5"
if workspace:
out_dir = out_dir + "/" + workspace
outputs = [ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)]))
for f in target.typescript.replay_params.outputs
if not f.short_path.endswith(".externs.js")]
outputs = [
ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)]))
for f in target.typescript.replay_params.outputs
if not f.short_path.endswith(".externs.js")
]
ctx.actions.run(
executable = ctx.executable._modify_tsconfig,
inputs = [target.typescript.replay_params.tsconfig],
outputs = [tsconfig],
arguments = [
target.typescript.replay_params.tsconfig.path,
tsconfig.path,
_join([workspace, target.label.package, ctx.label.name + ".esm5"]),
ctx.bin_dir.path
],
)
ctx.actions.run(
executable = ctx.executable._modify_tsconfig,
inputs = [target.typescript.replay_params.tsconfig],
outputs = [tsconfig],
arguments = [
target.typescript.replay_params.tsconfig.path,
tsconfig.path,
_join([workspace, target.label.package, ctx.label.name + ".esm5"]),
ctx.bin_dir.path,
],
)
ctx.actions.run(
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label,
inputs = target.typescript.replay_params.inputs + [tsconfig],
outputs = outputs,
arguments = [tsconfig.path],
executable = target.typescript.replay_params.compiler,
execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0",
},
)
ctx.actions.run(
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label,
inputs = target.typescript.replay_params.inputs + [tsconfig],
outputs = outputs,
arguments = [tsconfig.path],
executable = target.typescript.replay_params.compiler,
execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0",
},
)
root_dir = _join([
ctx.bin_dir.path,
workspace,
target.label.package,
ctx.label.name + ".esm5",
])
root_dir = _join([
ctx.bin_dir.path,
workspace,
target.label.package,
ctx.label.name + ".esm5",
])
transitive_output={root_dir: depset(outputs)}
for dep in ctx.rule.attr.deps:
if ESM5Info in dep:
transitive_output.update(dep[ESM5Info].transitive_output)
transitive_output = {root_dir: depset(outputs)}
for dep in ctx.rule.attr.deps:
if ESM5Info in dep:
transitive_output.update(dep[ESM5Info].transitive_output)
return [ESM5Info(
transitive_output = transitive_output,
)]
return [ESM5Info(
transitive_output = transitive_output,
)]
# Downstream rules can use this aspect to access the ESM5 output flavor.
# Only terminal rules (those which expect never to be used in deps[]) should do
@ -104,12 +107,13 @@ def _esm5_outputs_aspect(target, ctx):
esm5_outputs_aspect = aspect(
implementation = _esm5_outputs_aspect,
# Recurse to the deps of any target we visit
attr_aspects = ['deps'],
attr_aspects = ["deps"],
attrs = {
"_modify_tsconfig": attr.label(
default = Label("//packages/bazel/src:modify_tsconfig"),
executable = True,
cfg = "host"),
cfg = "host",
),
# We must list tsc_wrapped here to ensure it's built before the action runs
# For some reason, having the compiler output as an input to the action above
# is not sufficient.
@ -128,43 +132,44 @@ esm5_outputs_aspect = aspect(
)
def esm5_root_dir(ctx):
return ctx.label.name + ".esm5"
return ctx.label.name + ".esm5"
def flatten_esm5(ctx):
"""Merge together the .esm5 folders from the dependencies.
"""Merge together the .esm5 folders from the dependencies.
Two different dependencies A and B may have outputs like
`bazel-bin/path/to/A.esm5/path/to/lib.js`
`bazel-bin/path/to/B.esm5/path/to/main.js`
Two different dependencies A and B may have outputs like
`bazel-bin/path/to/A.esm5/path/to/lib.js`
`bazel-bin/path/to/B.esm5/path/to/main.js`
In order to run rollup on this app, in case main.js contains `import from './lib'`
they need to be together in the same root directory, so if we depend on both A and B
we need the outputs to be
`bazel-bin/path/to/my_rule.esm5/path/to/lib.js`
`bazel-bin/path/to/my_rule.esm5/path/to/main.js`
In order to run rollup on this app, in case main.js contains `import from './lib'`
they need to be together in the same root directory, so if we depend on both A and B
we need the outputs to be
`bazel-bin/path/to/my_rule.esm5/path/to/lib.js`
`bazel-bin/path/to/my_rule.esm5/path/to/main.js`
Args:
ctx: the skylark rule execution context
Args:
ctx: the skylark rule execution context
Returns:
list of flattened files
"""
esm5_sources = []
result = []
for dep in ctx.attr.deps:
if ESM5Info in dep:
transitive_output = dep[ESM5Info].transitive_output
esm5_sources.extend(transitive_output.values())
for f in depset(transitive = esm5_sources).to_list():
path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):]
if (path.startswith("../")):
path = "external/" + path[3:]
rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path]))
result.append(rerooted_file)
# print("copy", f.short_path, "to", rerooted_file.short_path)
ctx.actions.expand_template(
output = rerooted_file,
template = f,
substitutions = {},
)
return result
Returns:
list of flattened files
"""
esm5_sources = []
result = []
for dep in ctx.attr.deps:
if ESM5Info in dep:
transitive_output = dep[ESM5Info].transitive_output
esm5_sources.extend(transitive_output.values())
for f in depset(transitive = esm5_sources).to_list():
path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):]
if (path.startswith("../")):
path = "external/" + path[3:]
rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path]))
result.append(rerooted_file)
# print("copy", f.short_path, "to", rerooted_file.short_path)
ctx.actions.expand_template(
output = rerooted_file,
template = f,
substitutions = {},
)
return result

View File

@ -2,273 +2,277 @@
#
# 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
"""Implementation of the ng_module rule.
"""Run Angular's AOT template compiler
"""
load(":rules_typescript.bzl",
"tsc_wrapped_tsconfig",
load(
":rules_typescript.bzl",
"COMMON_ATTRIBUTES",
"COMMON_OUTPUTS",
"compile_ts",
"DEPS_ASPECTS",
"compile_ts",
"ts_providers_dict_to_struct",
"tsc_wrapped_tsconfig",
)
def _compile_strategy(ctx):
"""Detect which strategy should be used to implement ng_module.
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.
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
Args:
ctx: skylark rule execution context
Returns:
one of 'legacy', 'local', 'jit', or 'global' depending on the configuration in ctx
"""
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']
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 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'
if strategy == "legacy" and hasattr(ctx.attr, "_global_mode") and ctx.attr._global_mode:
strategy = "global"
return strategy
return strategy
def _compiler_name(ctx):
"""Selects a user-visible name depending on the current compilation strategy.
"""Selects a user-visible name depending on the current compilation strategy.
Args:
ctx: skylark rule execution context
Args:
ctx: skylark rule execution context
Returns:
the name of the current compiler to be displayed in build output
"""
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')
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.
"""Determines the value of the enableIvy option in the generated tsconfig.
Args:
ctx: skylark rule execution context
Args:
ctx: skylark rule execution context
Returns:
the value of enableIvy that needs to be set in angularCompilerOptions in the generated tsconfig
"""
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')
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.
"""Determines whether Angular outputs will be produced by the current compilation strategy.
Args:
ctx: skylark rule execution context
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
"""
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'
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"):
ext_len = len(".ng.html")
elif file.short_path.endswith(".html"):
ext_len = len(".html")
return file.short_path[len(ctx.label.package) + 1:-ext_len]
ext_len = len(".ts")
if file.short_path.endswith(".ng.html"):
ext_len = len(".ng.html")
elif file.short_path.endswith(".html"):
ext_len = len(".html")
return file.short_path[len(ctx.label.package) + 1:-ext_len]
# Return true if run with bazel (the open-sourced version of blaze), false if
# run with blaze.
def _is_bazel():
return not hasattr(native, "genmpm")
return not hasattr(native, "genmpm")
def _flat_module_out_file(ctx):
"""Provide a default for the flat_module_out_file attribute.
"""Provide a default for the flat_module_out_file attribute.
We cannot use the default="" parameter of ctx.attr because the value is calculated
from other attributes (name)
We cannot use the default="" parameter of ctx.attr because the value is calculated
from other attributes (name)
Args:
ctx: skylark rule execution context
Args:
ctx: skylark rule execution context
Returns:
a basename used for the flat module out (no extension)
"""
if hasattr(ctx.attr, "flat_module_out_file") and ctx.attr.flat_module_out_file:
return ctx.attr.flat_module_out_file
return "%s_public_index" % ctx.label.name
Returns:
a basename used for the flat module out (no extension)
"""
if hasattr(ctx.attr, "flat_module_out_file") and ctx.attr.flat_module_out_file:
return ctx.attr.flat_module_out_file
return "%s_public_index" % ctx.label.name
def _should_produce_flat_module_outs(ctx):
"""Should we produce flat module outputs.
"""Should we produce flat module outputs.
We only produce flat module outs when we expect the ng_module is meant to be published,
based on the presence of the module_name attribute.
We only produce flat module outs when we expect the ng_module is meant to be published,
based on the presence of the module_name attribute.
Args:
ctx: skylark rule execution context
Args:
ctx: skylark rule execution context
Returns:
true iff we should run the bundle_index_host to produce flat module metadata and bundle index
"""
return _is_bazel() and ctx.attr.module_name
Returns:
true iff we should run the bundle_index_host to produce flat module metadata and bundle index
"""
return _is_bazel() and ctx.attr.module_name
# Calculate the expected output of the template compiler for every source in
# 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)
include_ng_files = _include_ng_files(ctx)
devmode_js_files = []
closure_js_files = []
declaration_files = []
summary_files = []
metadata_files = []
devmode_js_files = []
closure_js_files = []
declaration_files = []
summary_files = []
metadata_files = []
factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories])
factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories])
for src in ctx.files.srcs + ctx.files.assets:
package_prefix = ctx.label.package + "/" if ctx.label.package else ""
for src in ctx.files.srcs + ctx.files.assets:
package_prefix = ctx.label.package + "/" if ctx.label.package else ""
# Strip external repository name from path if src is from external repository
# If src is from external repository, it's short_path will be ../<external_repo_name>/...
short_path = src.short_path if src.short_path[0:2] != ".." else "/".join(src.short_path.split("/")[2:])
# Strip external repository name from path if src is from external repository
# If src is from external repository, it's short_path will be ../<external_repo_name>/...
short_path = src.short_path if src.short_path[0:2] != ".." else "/".join(src.short_path.split("/")[2:])
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):
devmode_js = [
".ngfactory.js",
".ngsummary.js",
".js",
]
summaries = [".ngsummary.json"]
metadata = [".metadata.json"]
else:
devmode_js = [".js"]
summaries = []
metadata = []
elif include_ng_files and short_path.endswith(".css"):
basename = short_path[len(package_prefix):-len(".css")]
devmode_js = [
".css.shim.ngstyle.js",
".css.ngstyle.js",
]
summaries = []
metadata = []
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):
devmode_js = [
".ngfactory.js",
".ngsummary.js",
".js",
]
summaries = [".ngsummary.json"]
metadata = [".metadata.json"]
else:
devmode_js = [".js"]
if not _is_bazel():
devmode_js += [".ngfactory.js"]
summaries = []
metadata = []
elif include_ng_files and short_path.endswith(".css"):
basename = short_path[len(package_prefix):-len(".css")]
devmode_js = [
".css.shim.ngstyle.js",
".css.ngstyle.js",
]
summaries = []
metadata = []
else:
continue
filter_summaries = ctx.attr.filter_summaries
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")]
declarations = [f.replace(".js", ".d.ts") for f in devmode_js]
devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js]
closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js]
declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations]
summary_files += [ctx.actions.declare_file(basename + ext) for ext in summaries]
if not _is_bazel():
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):
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))
bundle_index_typings = ctx.actions.declare_file("%s.d.ts" % flat_module_out)
declaration_files.append(bundle_index_typings)
metadata_files.append(ctx.actions.declare_file("%s.metadata.json" % flat_module_out))
else:
continue
bundle_index_typings = None
filter_summaries = ctx.attr.filter_summaries
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")]
declarations = [f.replace(".js", ".d.ts") for f in devmode_js]
# 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 = []
devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js]
closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js]
declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations]
summary_files += [ctx.actions.declare_file(basename + ext) for ext in summaries]
if not _is_bazel():
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):
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))
bundle_index_typings = ctx.actions.declare_file("%s.d.ts" % flat_module_out)
declaration_files.append(bundle_index_typings)
metadata_files.append(ctx.actions.declare_file("%s.metadata.json" % flat_module_out))
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,
devmode_js = devmode_js_files,
declarations = declaration_files,
summaries = summary_files,
metadata = metadata_files,
bundle_index_typings = bundle_index_typings,
i18n_messages = i18n_messages_files,
)
return struct(
closure_js = closure_js_files,
devmode_js = devmode_js_files,
declarations = declaration_files,
summaries = summary_files,
metadata = metadata_files,
bundle_index_typings = bundle_index_typings,
i18n_messages = i18n_messages_files,
)
def _ngc_tsconfig(ctx, files, srcs, **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:
expected_outs = outs.closure_js
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:
expected_outs = outs.closure_js
angular_compiler_options = {
"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),
"fullTemplateTypeCheck": ctx.attr.type_check,
# FIXME: wrong place to de-dupe
"expectedOut": depset([o.path for o in expected_outs]).to_list()
}
angular_compiler_options = {
"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),
"fullTemplateTypeCheck": ctx.attr.type_check,
# FIXME: wrong place to de-dupe
"expectedOut": depset([o.path for o in expected_outs]).to_list(),
}
if _should_produce_flat_module_outs(ctx):
angular_compiler_options["flatModuleId"] = ctx.attr.module_name
angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx)
angular_compiler_options["flatModulePrivateSymbolPrefix"] = "_".join(
[ctx.workspace_name] + ctx.label.package.split("/") + [ctx.label.name, ""])
if _should_produce_flat_module_outs(ctx):
angular_compiler_options["flatModuleId"] = ctx.attr.module_name
angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx)
angular_compiler_options["flatModulePrivateSymbolPrefix"] = "_".join(
[ctx.workspace_name] + ctx.label.package.split("/") + [ctx.label.name, ""],
)
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
"angularCompilerOptions": angular_compiler_options
})
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
"angularCompilerOptions": angular_compiler_options,
})
def _collect_summaries_aspect_impl(target, ctx):
results = depset(target.angular.summaries if hasattr(target, "angular") else [])
results = depset(target.angular.summaries if hasattr(target, "angular") else [])
# If we are visiting empty-srcs ts_library, this is a re-export
srcs = ctx.rule.attr.srcs if hasattr(ctx.rule.attr, "srcs") else []
# If we are visiting empty-srcs ts_library, this is a re-export
srcs = ctx.rule.attr.srcs if hasattr(ctx.rule.attr, "srcs") else []
# "re-export" rules should expose all the files of their deps
if not srcs and hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps:
if (hasattr(dep, "angular")):
results = depset(dep.angular.summaries, transitive = [results])
# "re-export" rules should expose all the files of their deps
if not srcs and hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps:
if (hasattr(dep, "angular")):
results = depset(dep.angular.summaries, transitive = [results])
return struct(collect_summaries_aspect_result = results)
return struct(collect_summaries_aspect_result = results)
_collect_summaries_aspect = aspect(
implementation = _collect_summaries_aspect_impl,
@ -278,213 +282,233 @@ _collect_summaries_aspect = aspect(
# Extra options passed to Node when running ngc.
_EXTRA_NODE_OPTIONS_FLAGS = [
# Expose the v8 garbage collection API to JS.
"--node_options=--expose-gc"
"--node_options=--expose-gc",
]
def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file,
node_opts, locale=None, i18n_args=[]):
"""Helper function to create the ngc action.
def ngc_compile_action(
ctx,
label,
inputs,
outputs,
messages_out,
tsconfig_file,
node_opts,
locale = None,
i18n_args = []):
"""Helper function to create the ngc action.
This is exposed for google3 to wire up i18n replay rules, and is not intended
as part of the public API.
This is exposed for google3 to wire up i18n replay rules, and is not intended
as part of the public API.
Args:
ctx: skylark context
label: the label of the ng_module being compiled
inputs: passed to the ngc action's inputs
outputs: passed to the ngc action's outputs
messages_out: produced xmb files
tsconfig_file: tsconfig file with settings used for the compilation
node_opts: list of strings, extra nodejs options.
locale: i18n locale, or None
i18n_args: additional command-line arguments to ngc
Args:
ctx: skylark context
label: the label of the ng_module being compiled
inputs: passed to the ngc action's inputs
outputs: passed to the ngc action's outputs
messages_out: produced xmb files
tsconfig_file: tsconfig file with settings used for the compilation
node_opts: list of strings, extra nodejs options.
locale: i18n locale, or None
i18n_args: additional command-line arguments to ngc
Returns:
the parameters of the compilation which will be used to replay the ngc action for i18N.
"""
Returns:
the parameters of the compilation which will be used to replay the ngc action for i18N.
"""
include_ng_files = _include_ng_files(ctx)
include_ng_files = _include_ng_files(ctx)
mnemonic = "AngularTemplateCompile"
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
mnemonic = "AngularTemplateCompile"
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
if locale:
mnemonic = "AngularI18NMerging"
supports_workers = "0"
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
(label, locale))
else:
supports_workers = str(int(ctx.attr._supports_workers))
if locale:
mnemonic = "AngularI18NMerging"
supports_workers = "0"
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
(label, locale))
else:
supports_workers = str(int(ctx.attr._supports_workers))
arguments = (list(_EXTRA_NODE_OPTIONS_FLAGS) +
["--node_options=%s" % opt for opt in node_opts])
# One at-sign makes this a params-file, enabling the worker strategy.
# Two at-signs escapes the argument so it's passed through to ngc
# rather than the contents getting expanded.
if supports_workers == "1":
arguments += ["@@" + tsconfig_file.path]
else:
arguments += ["-p", tsconfig_file.path]
arguments = (list(_EXTRA_NODE_OPTIONS_FLAGS) +
["--node_options=%s" % opt for opt in node_opts])
arguments += i18n_args
# One at-sign makes this a params-file, enabling the worker strategy.
# Two at-signs escapes the argument so it's passed through to ngc
# rather than the contents getting expanded.
if supports_workers == "1":
arguments += ["@@" + tsconfig_file.path]
else:
arguments += ["-p", tsconfig_file.path]
ctx.actions.run(
progress_message = progress_message,
mnemonic = mnemonic,
inputs = inputs,
outputs = outputs,
arguments = arguments,
executable = ctx.executable.compiler,
execution_requirements = {
"supports-workers": supports_workers,
},
)
arguments += i18n_args
if include_ng_files and messages_out != None:
ctx.actions.run(
inputs = list(inputs),
outputs = messages_out,
executable = ctx.executable._ng_xi18n,
arguments = (_EXTRA_NODE_OPTIONS_FLAGS +
[tsconfig_file.path] +
# The base path is bin_dir because of the way the ngc
# compiler host is configured. So we need to explicitly
# point to genfiles/ to redirect the output.
["../genfiles/" + messages_out[0].short_path]),
progress_message = "Extracting Angular 2 messages (ng_xi18n)",
mnemonic = "Angular2MessageExtractor")
if not locale and not ctx.attr.no_i18n:
return struct(
label = label,
tsconfig = tsconfig_file,
progress_message = progress_message,
mnemonic = mnemonic,
inputs = inputs,
outputs = outputs,
compiler = ctx.executable.compiler,
arguments = arguments,
executable = ctx.executable.compiler,
execution_requirements = {
"supports-workers": supports_workers,
},
)
return None
if include_ng_files and messages_out != None:
ctx.actions.run(
inputs = list(inputs),
outputs = messages_out,
executable = ctx.executable._ng_xi18n,
arguments = (_EXTRA_NODE_OPTIONS_FLAGS +
[tsconfig_file.path] +
# The base path is bin_dir because of the way the ngc
# compiler host is configured. So we need to explicitly
# point to genfiles/ to redirect the output.
["../genfiles/" + messages_out[0].short_path]),
progress_message = "Extracting Angular 2 messages (ng_xi18n)",
mnemonic = "Angular2MessageExtractor",
)
if not locale and not ctx.attr.no_i18n:
return struct(
label = label,
tsconfig = tsconfig_file,
inputs = inputs,
outputs = outputs,
compiler = ctx.executable.compiler,
)
return None
def _compile_action(ctx, inputs, outputs, messages_out, tsconfig_file, node_opts):
# Give the Angular compiler all the user-listed assets
file_inputs = list(ctx.files.assets)
# Give the Angular compiler all the user-listed assets
file_inputs = list(ctx.files.assets)
# The compiler only needs to see TypeScript sources from the npm dependencies,
# but may need to look at package.json and ngsummary.json files as well.
if hasattr(ctx.attr, "node_modules"):
file_inputs += [f for f in ctx.files.node_modules
if f.path.endswith(".ts") or f.path.endswith(".json")]
# The compiler only needs to see TypeScript sources from the npm dependencies,
# but may need to look at package.json and ngsummary.json files as well.
if hasattr(ctx.attr, "node_modules"):
file_inputs += [
f
for f in ctx.files.node_modules
if f.path.endswith(".ts") or f.path.endswith(".json")
]
# If the user supplies a tsconfig.json file, the Angular compiler needs to read it
if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig:
file_inputs.append(ctx.file.tsconfig)
# If the user supplies a tsconfig.json file, the Angular compiler needs to read it
if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig:
file_inputs.append(ctx.file.tsconfig)
# Collect the inputs and summary files from our deps
action_inputs = depset(file_inputs,
transitive = [inputs] + [dep.collect_summaries_aspect_result for dep in ctx.attr.deps
if hasattr(dep, "collect_summaries_aspect_result")])
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts)
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, outs.i18n_messages, tsconfig_file, node_opts)
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, None, tsconfig_file, node_opts)
def _ts_expected_outs(ctx, label):
# rules_typescript expects a function with two arguments, but our
# implementation doesn't use the label
_ignored = [label]
return _expected_outs(ctx)
def ng_module_impl(ctx, ts_compile_actions):
"""Implementation function for the ng_module rule.
This is exposed so that google3 can have its own entry point that re-uses this
and is not meant as a public API.
Args:
ctx: the skylark rule context
ts_compile_actions: generates all the actions to run an ngc compilation
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)
providers = ts_compile_actions(
ctx, is_library=True, compile_action=_prodmode_compile_action,
devmode_compile_action=_devmode_compile_action,
tsc_wrapped_tsconfig=_ngc_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 len(outs.metadata) > 1:
fail("expecting exactly one metadata output for " + str(ctx.label))
providers["angular"]["flat_module_metadata"] = struct(
module_name = ctx.attr.module_name,
metadata_file = outs.metadata[0],
typings_file = outs.bundle_index_typings,
flat_module_out_file = _flat_module_out_file(ctx),
# Collect the inputs and summary files from our deps
action_inputs = depset(
file_inputs,
transitive = [inputs] + [
dep.collect_summaries_aspect_result
for dep in ctx.attr.deps
if hasattr(dep, "collect_summaries_aspect_result")
],
)
return providers
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts)
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, outs.i18n_messages, tsconfig_file, node_opts)
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, None, tsconfig_file, node_opts)
def _ts_expected_outs(ctx, label):
# rules_typescript expects a function with two arguments, but our
# implementation doesn't use the label
_ignored = [label]
return _expected_outs(ctx)
def ng_module_impl(ctx, ts_compile_actions):
"""Implementation function for the ng_module rule.
This is exposed so that google3 can have its own entry point that re-uses this
and is not meant as a public API.
Args:
ctx: the skylark rule context
ts_compile_actions: generates all the actions to run an ngc compilation
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)
providers = ts_compile_actions(
ctx,
is_library = True,
compile_action = _prodmode_compile_action,
devmode_compile_action = _devmode_compile_action,
tsc_wrapped_tsconfig = _ngc_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 len(outs.metadata) > 1:
fail("expecting exactly one metadata output for " + str(ctx.label))
providers["angular"]["flat_module_metadata"] = struct(
module_name = ctx.attr.module_name,
metadata_file = outs.metadata[0],
typings_file = outs.bundle_index_typings,
flat_module_out_file = _flat_module_out_file(ctx),
)
return providers
def _ng_module_impl(ctx):
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
NG_MODULE_ATTRIBUTES = {
"srcs": attr.label_list(allow_files = [".ts"]),
"deps": attr.label_list(aspects = DEPS_ASPECTS + [_collect_summaries_aspect]),
"assets": attr.label_list(allow_files = [
".css",
# TODO(alexeagle): change this to ".ng.html" when usages updated
".html",
]),
# Note: DEPS_ASPECTS is already a list, we add the cast to workaround
# https://github.com/bazelbuild/skydoc/issues/21
"deps": attr.label_list(
doc = "Targets that are imported by this target",
aspects = list(DEPS_ASPECTS) + [_collect_summaries_aspect],
),
"assets": attr.label_list(
doc = ".html and .css files needed by the Angular compiler",
allow_files = [
".css",
# TODO(alexeagle): change this to ".ng.html" when usages updated
".html",
],
),
"factories": attr.label_list(
allow_files = [".ts", ".html"],
mandatory = False),
mandatory = False,
),
"filter_summaries": attr.bool(default = False),
"type_check": attr.bool(default = True),
"inline_resources": attr.bool(default = True),
"no_i18n": attr.bool(default = False),
"compiler": attr.label(
default = Label("//packages/bazel/src/ngc-wrapped"),
executable = True,
cfg = "host",
),
"_ng_xi18n": attr.label(
default = Label("//packages/bazel/src/ngc-wrapped:xi18n"),
executable = True,
cfg = "host",
),
"_supports_workers": attr.bool(default = True),
}
@ -495,9 +519,8 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{
# The default assumes the user specified a target "node_modules" in their
# root BUILD file.
"node_modules": attr.label(
default = Label("@//:node_modules")
default = Label("@//:node_modules"),
),
"entry_point": attr.string(),
# Default is %{name}_public_index
@ -515,7 +538,13 @@ ng_module = rule(
attrs = NG_MODULE_RULE_ATTRS,
outputs = COMMON_OUTPUTS,
)
"""
Run the Angular AOT template compiler.
This rule extends the [ts_library] rule.
[ts_library]: http://tsetse.info/api/build_defs.html#ts_library
"""
# 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

View File

@ -22,5 +22,6 @@ nodejs_binary(
name = "packager",
data = ["lib"],
entry_point = "angular/packages/bazel/src/ng_package/packager.js",
install_source_map_support = False,
node_modules = "@angular_packager_deps//:node_modules",
)

View File

@ -2,45 +2,58 @@
#
# 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
"""Implementation of the ng_package rule.
"""Package Angular libraries for npm distribution
If all users of an Angular library use Bazel (e.g. internal usage in your company)
then you should simply add your library to the `deps` of the consuming application.
These rules exist for compatibility with non-Bazel consumers of your library.
It packages your library following the Angular Package Format, see the
specification of this format at https://goo.gl/jB3GVv
"""
load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es6_sources")
load("@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl",
"write_rollup_config",
"rollup_module_mappings_aspect",
"run_uglify",
"ROLLUP_ATTRS")
load("@build_bazel_rules_nodejs//:internal/npm_package/npm_package.bzl",
"NPM_PACKAGE_ATTRS",
"NPM_PACKAGE_OUTPUTS",
"create_package")
load(
"@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl",
"ROLLUP_ATTRS",
"rollup_module_mappings_aspect",
"run_uglify",
"write_rollup_config",
)
load(
"@build_bazel_rules_nodejs//:internal/npm_package/npm_package.bzl",
"NPM_PACKAGE_ATTRS",
"NPM_PACKAGE_OUTPUTS",
"create_package",
)
load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "flatten_esm5", "esm5_root_dir")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
# Convert from some-dash-case to someCamelCase
def _convert_dash_case_to_camel_case(s):
parts = s.split("-")
# First letter in the result is always unchanged
return s[0] + "".join([p.title() for p in parts])[1:]
parts = s.split("-")
# First letter in the result is always unchanged
return s[0] + "".join([p.title() for p in parts])[1:]
# Convert from a package name on npm to an identifier that's a legal global symbol
# @angular/core -> ng.core
# @angular/platform-browser-dynamic/testing -> ng.platformBrowserDynamic.testing
def _global_name(package_name):
# strip npm scoped package qualifier
start = 1 if package_name.startswith("@") else 0
parts = package_name[start:].split("/")
result_parts = []
for p in parts:
# Special case for angular's short name
if p == "angular":
result_parts.append("ng")
else:
result_parts.append(_convert_dash_case_to_camel_case(p))
return ".".join(result_parts)
# strip npm scoped package qualifier
start = 1 if package_name.startswith("@") else 0
parts = package_name[start:].split("/")
result_parts = []
for p in parts:
# Special case for angular's short name
if p == "angular":
result_parts.append("ng")
else:
result_parts.append(_convert_dash_case_to_camel_case(p))
return ".".join(result_parts)
WELL_KNOWN_GLOBALS = { p: _global_name(p) for p in [
WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [
"@angular/upgrade",
"@angular/upgrade/static",
"@angular/forms",
@ -77,242 +90,259 @@ WELL_KNOWN_GLOBALS = { p: _global_name(p) for p in [
]}
def _rollup(ctx, bundle_name, rollup_config, entry_point, inputs, js_output, format = "es", package_name = "", include_tslib = False):
map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output)
map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output)
args = ctx.actions.args()
args.add("--config", rollup_config)
args = ctx.actions.args()
args.add("--config", rollup_config)
args.add("--input", entry_point)
args.add("--output.file", js_output)
args.add("--output.format", format)
if package_name:
args.add("--output.name", _global_name(package_name))
args.add("--amd.id", package_name)
args.add("--input", entry_point)
args.add("--output.file", js_output)
args.add("--output.format", format)
if package_name:
args.add("--output.name", _global_name(package_name))
args.add("--amd.id", package_name)
# Note: if the input has external source maps then we need to also install and use
# `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead
# of command line args
args.add("--sourcemap")
# Note: if the input has external source maps then we need to also install and use
# `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead
# of command line args
args.add("--sourcemap")
globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals)
external = globals.keys()
if not include_tslib:
external.append("tslib")
args.add_joined("--external", external, join_with=",")
globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals)
external = globals.keys()
if not include_tslib:
external.append("tslib")
args.add_joined("--external", external, join_with = ",")
args.add_joined(
"--globals",
["%s:%s" % g for g in globals.items()],
join_with=",")
args.add_joined(
"--globals",
["%s:%s" % g for g in globals.items()],
join_with = ",",
)
args.add("--silent")
args.add("--silent")
other_inputs = [ctx.executable._rollup, rollup_config]
if ctx.file.license_banner:
other_inputs.append(ctx.file.license_banner)
if ctx.version_file:
other_inputs.append(ctx.version_file)
ctx.actions.run(
progress_message = "ng_package: Rollup %s %s" % (bundle_name, ctx.label),
mnemonic = "AngularPackageRollup",
inputs = inputs.to_list() + other_inputs,
outputs = [js_output, map_output],
executable = ctx.executable._rollup,
arguments = [args],
)
return struct(
js = js_output,
map = map_output,
)
other_inputs = [ctx.executable._rollup, rollup_config]
if ctx.file.license_banner:
other_inputs.append(ctx.file.license_banner)
if ctx.version_file:
other_inputs.append(ctx.version_file)
ctx.actions.run(
progress_message = "ng_package: Rollup %s %s" % (bundle_name, ctx.label),
mnemonic = "AngularPackageRollup",
inputs = inputs.to_list() + other_inputs,
outputs = [js_output, map_output],
executable = ctx.executable._rollup,
arguments = [args],
)
return struct(
js = js_output,
map = map_output,
)
# convert from [{js: js_file1, map: map_file1}, ...] to
# [js_filepath1, map_filepath1, ...]
def _flatten_paths(directory):
result = []
for f in directory:
result.append(f.js.path)
if f.map:
result.append(f.map.path)
return result
result = []
for f in directory:
result.append(f.js.path)
if f.map:
result.append(f.map.path)
return result
# takes an depset of files and returns an array that doesn't contain any generated files by ngc
def _filter_out_generated_files(files):
result = []
for file in files:
if (not(file.path.endswith(".ngfactory.js") or file.path.endswith(".ngsummary.js") or file.path.endswith(".ngstyle.js"))):
result.append(file)
return depset(result)
result = []
for file in files:
if (not (file.path.endswith(".ngfactory.js") or file.path.endswith(".ngsummary.js") or file.path.endswith(".ngstyle.js"))):
result.append(file)
return depset(result)
def _esm2015_root_dir(ctx):
return ctx.label.name + ".es6"
return ctx.label.name + ".es6"
# ng_package produces package that is npm-ready.
def _ng_package_impl(ctx):
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx))
esm5_sources = _filter_out_generated_files(flatten_esm5(ctx))
esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx))
esm5_sources = _filter_out_generated_files(flatten_esm5(ctx))
# These accumulators match the directory names where the files live in the
# Angular package format.
fesm2015 = []
fesm5 = []
esm2015 = []
esm5 = []
bundles = []
# These accumulators match the directory names where the files live in the
# Angular package format.
fesm2015 = []
fesm5 = []
esm2015 = []
esm5 = []
bundles = []
# For Angular Package Format v6, we put all the individual .js files in the
# esm5/ and esm2015/ folders.
for f in esm5_sources.to_list():
if f.path.endswith(".js"):
esm5.append(struct(js = f, map = None))
for f in esm_2015_files.to_list():
if f.path.endswith(".js"):
esm2015.append(struct(js = f, map = None))
# For Angular Package Format v6, we put all the individual .js files in the
# esm5/ and esm2015/ folders.
for f in esm5_sources.to_list():
if f.path.endswith(".js"):
esm5.append(struct(js = f, map = None))
for f in esm_2015_files.to_list():
if f.path.endswith(".js"):
esm2015.append(struct(js = f, map = None))
# We infer the entry points to be:
# - ng_module rules in the deps (they have an "angular" provider)
# - in this package or a subpackage
# - those that have a module_name attribute (they produce flat module metadata)
flat_module_metadata = []
# Name given in the package.json name field, eg. @angular/core/testing
package_name = ""
deps_in_package = [d for d in ctx.attr.deps if d.label.package.startswith(ctx.label.package)]
for dep in deps_in_package:
# Intentionally evaluates to empty string for the main entry point
entry_point = dep.label.package[len(ctx.label.package) + 1:]
if hasattr(dep, "module_name"):
package_name = dep.module_name
if hasattr(dep, "angular") and hasattr(dep.angular, "flat_module_metadata"):
flat_module_metadata.append(dep.angular.flat_module_metadata)
flat_module_out_file = dep.angular.flat_module_metadata.flat_module_out_file + ".js"
# We infer the entry points to be:
# - ng_module rules in the deps (they have an "angular" provider)
# - in this package or a subpackage
# - those that have a module_name attribute (they produce flat module metadata)
flat_module_metadata = []
# Name given in the package.json name field, eg. @angular/core/testing
package_name = ""
deps_in_package = [d for d in ctx.attr.deps if d.label.package.startswith(ctx.label.package)]
for dep in deps_in_package:
# Intentionally evaluates to empty string for the main entry point
entry_point = dep.label.package[len(ctx.label.package) + 1:]
if hasattr(dep, "module_name"):
package_name = dep.module_name
if hasattr(dep, "angular") and hasattr(dep.angular, "flat_module_metadata"):
flat_module_metadata.append(dep.angular.flat_module_metadata)
flat_module_out_file = dep.angular.flat_module_metadata.flat_module_out_file + ".js"
else:
# fallback to a reasonable default
flat_module_out_file = "index.js"
es2015_entry_point = "/".join([p for p in [
ctx.bin_dir.path,
ctx.label.package,
_esm2015_root_dir(ctx),
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
es5_entry_point = "/".join([p for p in [
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
if entry_point:
# TODO jasonaden says there is no particular reason these filenames differ
prefix = primary_entry_point_name(ctx.attr.name, ctx.attr.entry_point, ctx.attr.entry_point_name)
umd_output_filename = "-".join([prefix] + entry_point.split("/"))
fesm_output_filename = entry_point.replace("/", "__")
fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename)
fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename)
umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename)
min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename)
else:
fesm2015_output = ctx.outputs.fesm2015
fesm5_output = ctx.outputs.fesm5
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
esm2015_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename = "_%s.rollup_esm2015.conf.js")
esm5_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename = "_%s.rollup_esm5.conf.js")
fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, esm_2015_files + ctx.files.node_modules, fesm2015_output))
fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, fesm5_output))
bundles.append(
_rollup(
ctx,
"umd",
esm5_config,
es5_entry_point,
esm5_sources + ctx.files.node_modules,
umd_output,
format = "umd",
package_name = package_name,
include_tslib = True,
),
)
uglify_sourcemap = run_uglify(
ctx,
umd_output,
min_output,
config_name = entry_point.replace("/", "_"),
)
bundles.append(struct(js = min_output, map = uglify_sourcemap))
packager_inputs = (
ctx.files.srcs +
ctx.files.data +
esm5_sources.to_list() +
depset(transitive = [
d.typescript.transitive_declarations
for d in ctx.attr.deps
if hasattr(d, "typescript")
]).to_list() +
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map]
)
packager_args = ctx.actions.args()
packager_args.use_param_file("%s", use_always = True)
# The order of arguments matters here, as they are read in order in packager.ts.
packager_args.add(npm_package_directory.path)
packager_args.add(ctx.label.package)
packager_args.add_joined([ctx.bin_dir.path, ctx.label.package], join_with = "/")
packager_args.add_joined([ctx.genfiles_dir.path, ctx.label.package], join_with = "/")
# Marshal the metadata into a JSON string so we can parse the data structure
# in the TypeScript program easily.
metadata_arg = {}
for m in flat_module_metadata:
packager_inputs.extend([m.metadata_file])
metadata_arg[m.module_name] = {
"index": m.typings_file.path.replace(".d.ts", ".js"),
"typings": m.typings_file.path,
"metadata": m.metadata_file.path,
}
packager_args.add(str(metadata_arg))
if ctx.file.readme_md:
packager_inputs.append(ctx.file.readme_md)
packager_args.add(ctx.file.readme_md.path)
else:
# fallback to a reasonable default
flat_module_out_file = "index.js"
# placeholder
packager_args.add("")
es2015_entry_point = "/".join([p for p in [
ctx.bin_dir.path,
ctx.label.package,
_esm2015_root_dir(ctx),
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
packager_args.add_joined(_flatten_paths(fesm2015), join_with = ",")
packager_args.add_joined(_flatten_paths(fesm5), join_with = ",")
packager_args.add_joined(_flatten_paths(esm2015), join_with = ",")
packager_args.add_joined(_flatten_paths(esm5), join_with = ",")
packager_args.add_joined(_flatten_paths(bundles), join_with = ",")
packager_args.add_joined([s.path for s in ctx.files.srcs], join_with = ",")
es5_entry_point = "/".join([p for p in [
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
# TODO: figure out a better way to gather runfiles providers from the transitive closure.
packager_args.add_joined([d.path for d in ctx.files.data], join_with = ",")
if entry_point:
# TODO jasonaden says there is no particular reason these filenames differ
prefix = primary_entry_point_name(ctx.attr.name, ctx.attr.entry_point, ctx.attr.entry_point_name)
umd_output_filename = "-".join([prefix] + entry_point.split("/"))
fesm_output_filename = entry_point.replace("/", "__")
fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename)
fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename)
umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename)
min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename)
if ctx.file.license_banner:
packager_inputs.append(ctx.file.license_banner)
packager_args.add(ctx.file.license_banner)
else:
fesm2015_output = ctx.outputs.fesm2015
fesm5_output = ctx.outputs.fesm5
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
# placeholder
packager_args.add("")
esm2015_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename="_%s.rollup_esm2015.conf.js")
esm5_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename="_%s.rollup_esm5.conf.js")
ctx.actions.run(
progress_message = "Angular Packaging: building npm package %s" % str(ctx.label),
mnemonic = "AngularPackage",
inputs = packager_inputs,
outputs = [npm_package_directory],
executable = ctx.executable._ng_packager,
arguments = [packager_args],
)
fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, esm_2015_files + ctx.files.node_modules, fesm2015_output))
fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, fesm5_output))
devfiles = depset()
if ctx.attr.include_devmode_srcs:
for d in ctx.attr.deps:
devfiles = depset(transitive = [devfiles, d.files, d.node_sources])
bundles.append(
_rollup(ctx, "umd", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, umd_output,
format = "umd", package_name = package_name, include_tslib = True))
uglify_sourcemap = run_uglify(ctx, umd_output, min_output,
config_name = entry_point.replace("/", "_"))
bundles.append(struct(js = min_output, map = uglify_sourcemap))
packager_inputs = (
ctx.files.srcs +
ctx.files.data +
esm5_sources.to_list() +
depset(transitive = [d.typescript.transitive_declarations
for d in ctx.attr.deps
if hasattr(d, "typescript")]).to_list() +
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map])
packager_args = ctx.actions.args()
packager_args.use_param_file("%s", use_always = True)
# The order of arguments matters here, as they are read in order in packager.ts.
packager_args.add(npm_package_directory.path)
packager_args.add(ctx.label.package)
packager_args.add_joined([ctx.bin_dir.path, ctx.label.package], join_with="/")
packager_args.add_joined([ctx.genfiles_dir.path, ctx.label.package], join_with="/")
# Marshal the metadata into a JSON string so we can parse the data structure
# in the TypeScript program easily.
metadata_arg = {}
for m in flat_module_metadata:
packager_inputs.extend([m.metadata_file])
metadata_arg[m.module_name] = {
"index": m.typings_file.path.replace(".d.ts", ".js"),
"typings": m.typings_file.path,
"metadata": m.metadata_file.path,
}
packager_args.add(str(metadata_arg))
if ctx.file.readme_md:
packager_inputs.append(ctx.file.readme_md)
packager_args.add(ctx.file.readme_md.path)
else:
# placeholder
packager_args.add("")
packager_args.add_joined(_flatten_paths(fesm2015), join_with=",")
packager_args.add_joined(_flatten_paths(fesm5), join_with=",")
packager_args.add_joined(_flatten_paths(esm2015), join_with=",")
packager_args.add_joined(_flatten_paths(esm5), join_with=",")
packager_args.add_joined(_flatten_paths(bundles), join_with=",")
packager_args.add_joined([s.path for s in ctx.files.srcs], join_with=",")
# TODO: figure out a better way to gather runfiles providers from the transitive closure.
packager_args.add_joined([d.path for d in ctx.files.data], join_with=",")
if ctx.file.license_banner:
packager_inputs.append(ctx.file.license_banner)
packager_args.add(ctx.file.license_banner)
else:
# placeholder
packager_args.add("")
ctx.actions.run(
progress_message = "Angular Packaging: building npm package %s" % str(ctx.label),
mnemonic = "AngularPackage",
inputs = packager_inputs,
outputs = [npm_package_directory],
executable = ctx.executable._ng_packager,
arguments = [packager_args],
)
devfiles = depset()
if ctx.attr.include_devmode_srcs:
for d in ctx.attr.deps:
devfiles = depset(transitive = [devfiles, d.files, d.node_sources])
# Re-use the create_package function from the nodejs npm_package rule.
package_dir = create_package(
ctx,
devfiles.to_list(),
[npm_package_directory] + ctx.files.packages)
return [DefaultInfo(
files = depset([package_dir])
)]
# Re-use the create_package function from the nodejs npm_package rule.
package_dir = create_package(
ctx,
devfiles.to_list(),
[npm_package_directory] + ctx.files.packages,
)
return [DefaultInfo(
files = depset([package_dir]),
)]
NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
"srcs": attr.label_list(allow_files = True),
@ -327,22 +357,29 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
),
"include_devmode_srcs": attr.bool(default = False),
"readme_md": attr.label(allow_single_file = FileType([".md"])),
"globals": attr.string_dict(default={}),
"globals": attr.string_dict(default = {}),
"entry_point_name": attr.string(
doc = "Name to use when generating bundle files for the primary entry-point.",
doc = "Name to use when generating bundle files for the primary entry-point.",
),
"_ng_packager": attr.label(
default=Label("//packages/bazel/src/ng_package:packager"),
executable=True, cfg="host"),
default = Label("//packages/bazel/src/ng_package:packager"),
executable = True,
cfg = "host",
),
"_rollup": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup"),
executable=True, cfg="host"),
default = Label("@build_bazel_rules_nodejs//internal/rollup"),
executable = True,
cfg = "host",
),
"_rollup_config_tmpl": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"),
allow_single_file=True),
default = Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"),
allow_single_file = True,
),
"_uglify": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:uglify"),
executable=True, cfg="host"),
default = Label("@build_bazel_rules_nodejs//internal/rollup:uglify"),
executable = True,
cfg = "host",
),
}))
# Angular wants these named after the entry_point,
@ -352,35 +389,63 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
# some/path/to/my/package/index.js
# we assume the files should be named "package.*.js"
def primary_entry_point_name(name, entry_point, entry_point_name):
if entry_point_name:
# If an explicit entry_point_name is given, use that.
return entry_point_name
elif entry_point.find("/") >= 0:
# If the entry_point has multiple path segments, use the second one.
# E.g., for "@angular/cdk/a11y", use "cdk".
return entry_point.split("/")[-2]
else:
# Fall back to the name of the ng_package rule.
return name
"""This is not a public API.
Compute the name of the primary entry point in the library.
Args:
name: the name of the `ng_package` rule, as a fallback.
entry_point: The starting point of the application, see rollup_bundle.
entry_point_name: if set, this is the returned value.
Returns:
name of the entry point, which will appear in the name of generated bundles
"""
if entry_point_name:
# If an explicit entry_point_name is given, use that.
return entry_point_name
elif entry_point.find("/") >= 0:
# If the entry_point has multiple path segments, use the second one.
# E.g., for "@angular/cdk/a11y", use "cdk".
return entry_point.split("/")[-2]
else:
# Fall back to the name of the ng_package rule.
return name
def ng_package_outputs(name, entry_point, entry_point_name):
basename = primary_entry_point_name(name, entry_point, entry_point_name)
outputs = {
"fesm5": "fesm5/%s.js" % basename,
"fesm2015": "fesm2015/%s.js" % basename,
"umd": "%s.umd.js" % basename,
"umd_min": "%s.umd.min.js" % basename,
}
for key in NPM_PACKAGE_OUTPUTS:
# NPM_PACKAGE_OUTPUTS is a "normal" dict-valued outputs so it looks like
# "pack": "%{name}.pack",
# But this is a function-valued outputs.
# Bazel won't replace the %{name} token so we have to do it.
outputs[key] = NPM_PACKAGE_OUTPUTS[key].replace("%{name}", name)
return outputs
"""This is not a public API.
This function computes the named outputs for an ng_package rule.
Args:
name: value of the name attribute
entry_point: value of the entry_point attribute
entry_point_name: value of the entry_point_name attribute
Returns:
dict of named outputs of the rule
"""
basename = primary_entry_point_name(name, entry_point, entry_point_name)
outputs = {
"fesm5": "fesm5/%s.js" % basename,
"fesm2015": "fesm2015/%s.js" % basename,
"umd": "%s.umd.js" % basename,
"umd_min": "%s.umd.min.js" % basename,
}
for key in NPM_PACKAGE_OUTPUTS:
# NPM_PACKAGE_OUTPUTS is a "normal" dict-valued outputs so it looks like
# "pack": "%{name}.pack",
# But this is a function-valued outputs.
# Bazel won't replace the %{name} token so we have to do it.
outputs[key] = NPM_PACKAGE_OUTPUTS[key].replace("%{name}", name)
return outputs
ng_package = rule(
implementation = _ng_package_impl,
attrs = NG_PACKAGE_ATTRS,
outputs = ng_package_outputs,
)
"""
ng_package produces an npm-ready package from an Angular library.
"""

View File

@ -3,143 +3,177 @@
# 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
"""This provides a variant of rollup_bundle that works better for Angular apps.
"""Rollup with Build Optimizer
It registers @angular-devkit/build-optimizer as a rollup plugin, to get
This provides a variant of the [rollup_bundle] rule that works better for Angular apps.
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
better optimization. It also uses ESM5 format inputs, as this is what
build-optimizer is hard-coded to look for and transform.
[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html
"""
load("@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl",
"rollup_module_mappings_aspect",
load(
"@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl",
"ROLLUP_ATTRS",
"ROLLUP_OUTPUTS",
"write_rollup_config",
"rollup_module_mappings_aspect",
"run_rollup",
"run_sourcemapexplorer",
"run_uglify",
"run_sourcemapexplorer")
"write_rollup_config",
)
load("@build_bazel_rules_nodejs//internal:collect_es6_sources.bzl", collect_es2015_sources = "collect_es6_sources")
load(":esm5.bzl", "esm5_outputs_aspect", "flatten_esm5", "esm5_root_dir")
load(":esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
PACKAGES=["packages/core/src", "packages/common/src", "packages/compiler/src", "external/rxjs"]
PLUGIN_CONFIG="{sideEffectFreeModules: [\n%s]}" % ",\n".join(
[" '.esm5/{0}'".format(p) for p in PACKAGES])
BO_ROLLUP="angular_cli/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js"
BO_PLUGIN="require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG)
PACKAGES = ["packages/core/src", "packages/common/src", "packages/compiler/src", "external/rxjs"]
PLUGIN_CONFIG = "{sideEffectFreeModules: [\n%s]}" % ",\n".join(
[" '.esm5/{0}'".format(p) for p in PACKAGES],
)
BO_ROLLUP = "angular_cli/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js"
BO_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.
"""Determine whether to use the Angular or upstream versions of the rollup_bundle rule.
In most modes, the Angular version of rollup is used. This runs build optimizer as part of its
processing, which affects decorators and annotations.
In most modes, the Angular version of rollup is used. This runs build optimizer as part of its
processing, which affects decorators and annotations.
In JIT 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.
In JIT 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
Args:
ctx: skylark rule execution context
Returns:
true iff the Angular version of rollup with build optimizer should be used, false otherwise
"""
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 == 'jit'
if "compile" not in ctx.var:
return False
strategy = ctx.var["compile"]
return strategy == "jit"
def run_brotli(ctx, input, output):
ctx.actions.run(
executable = ctx.executable._brotli,
inputs = [input],
outputs = [output],
arguments = ["--output=%s" % output.path, input.path],
)
"""Execute the Brotli compression utility.
Args:
ctx: Bazel's rule execution context
input: any file
output: the compressed file
"""
ctx.actions.run(
executable = ctx.executable._brotli,
inputs = [input],
outputs = [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)
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]
)
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)
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))
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)
# 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.
esm2015_rollup_config = write_rollup_config(ctx, filename = "_%s.rollup_es6.conf.js")
run_rollup(ctx, collect_es2015_sources(ctx), esm2015_rollup_config, ctx.outputs.build_es6)
# 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.
esm2015_rollup_config = write_rollup_config(ctx, filename = "_%s.rollup_es6.conf.js")
run_rollup(ctx, collect_es2015_sources(ctx), esm2015_rollup_config, ctx.outputs.build_es6)
esm5_sources = flatten_esm5(ctx)
esm5_sources = flatten_esm5(ctx)
rollup_config = write_rollup_config(ctx, [BO_PLUGIN], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
rollup_sourcemap = run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_es5)
rollup_config = write_rollup_config(ctx, [BO_PLUGIN], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
rollup_sourcemap = run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_es5)
sourcemap = run_uglify(ctx,
ctx.outputs.build_es5,
ctx.outputs.build_es5_min,
comments = False,
in_source_map = rollup_sourcemap)
run_uglify(ctx,
ctx.outputs.build_es5,
ctx.outputs.build_es5_min_debug,
debug = True, comments = False)
sourcemap = run_uglify(
ctx,
ctx.outputs.build_es5,
ctx.outputs.build_es5_min,
comments = False,
in_source_map = rollup_sourcemap,
)
run_uglify(
ctx,
ctx.outputs.build_es5,
ctx.outputs.build_es5_min_debug,
debug = True,
comments = False,
)
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)
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_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, sourcemap, ctx.outputs.explore_html)
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, sourcemap, ctx.outputs.explore_html)
return DefaultInfo(files=depset([ctx.outputs.build_es5_min, sourcemap]))
return DefaultInfo(files = depset([ctx.outputs.build_es5_min, sourcemap]))
ng_rollup_bundle = rule(
implementation = _ng_rollup_bundle,
attrs = dict(ROLLUP_ATTRS, **{
"deps": attr.label_list(aspects = [
rollup_module_mappings_aspect,
esm5_outputs_aspect,
]),
"deps": attr.label_list(
doc = """Other targets that provide JavaScript files.
Typically this will be `ts_library` or `ng_module` targets.""",
aspects = [
rollup_module_mappings_aspect,
esm5_outputs_aspect,
],
),
"_rollup": attr.label(
executable = True,
cfg = "host",
default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer")),
default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer"),
),
"_brotli": attr.label(
executable = True,
cfg = "host",
default = Label("@org_brotli//:brotli")),
default = Label("@org_brotli//:brotli"),
),
}),
outputs = dict(ROLLUP_OUTPUTS, **{
"build_es5_min_compressed": "%{name}.min.js.br",
}),
)
"""
Run [Rollup] with the [Build Optimizer] plugin.
This rule extends from the [rollup_bundle] rule, so attributes and outputs of
that rule are used here too.
[Rollup]: https://rollupjs.org/
[Build Optimizer]: https://www.npmjs.com/package/@angular-devkit/build-optimizer
[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html
"""

View File

@ -6,15 +6,19 @@
"Install toolchain dependencies"
load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install")
load("@build_bazel_rules_typescript//:defs.bzl", "check_rules_typescript_version")
def ng_setup_workspace():
"""This repository rule should be called from your WORKSPACE file.
"""This repository rule should be called from your WORKSPACE file.
It creates some additional Bazel external repositories that are used internally
by the Angular rules.
"""
yarn_install(
name = "angular_packager_deps",
package_json = "@angular//packages/bazel/src/ng_package:package.json",
yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock",
)
It creates some additional Bazel external repositories that are used internally
by the Angular rules.
"""
yarn_install(
name = "angular_packager_deps",
package_json = "@angular//packages/bazel/src/ng_package:package.json",
yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock",
)
# 0.16.0: minimal version required to work with ng_module
check_rules_typescript_version("0.16.0")

View File

@ -30,7 +30,7 @@ nodejs_binary(
name = "ngc-wrapped",
data = [
":ngc_lib",
"@build_bazel_rules_typescript//internal:worker_protocol.proto",
"@build_bazel_rules_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto",
],
entry_point = "angular/packages/bazel/src/ngc-wrapped/index.js",
visibility = ["//visibility:public"],

View File

@ -76,7 +76,8 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
export function relativeToRootDirs(filePath: string, rootDirs: string[]): string {
if (!filePath) return filePath;
// NB: the rootDirs should have been sorted longest-first
for (const dir of rootDirs || []) {
for (let i = 0; i < rootDirs.length; i++) {
const dir = rootDirs[i];
const rel = path.posix.relative(dir, filePath);
if (rel.indexOf('.') != 0) return rel;
}
@ -106,7 +107,9 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
fileLoader = new CachedFileLoader(fileCache, allowNonHermeticReads);
// Resolve the inputs to absolute paths to match TypeScript internals
const resolvedInputs: {[path: string]: string} = {};
for (const key of Object.keys(inputs)) {
const inputKeys = Object.keys(inputs);
for (let i = 0; i < inputKeys.length; i++) {
const key = inputKeys[i];
resolvedInputs[resolveNormalizedPath(key)] = inputs[key];
}
fileCache.updateCache(resolvedInputs);
@ -124,7 +127,11 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
// Disable downleveling and Closure annotation if in Ivy mode.
if (isInIvyMode) {
compilerOpts.annotateForClosureCompiler = false;
// In pass-through mode for TypeScript, we want to turn off decorator transpilation entirely.
// This causes ngc to be have exactly like tsc.
if (compilerOpts.enableIvy === 'tsc') {
compilerOpts.annotateForClosureCompiler = false;
}
compilerOpts.annotationsAs = 'decorators';
}
@ -207,7 +214,7 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
if (fileName ===
path.join(compilerOpts.baseUrl, bazelOpts.package, compilerOpts.flatModuleOutFile + '.ts'))
return true;
// Also handle the case when angular is build from source as an external repository
// Also handle the case when angular is built from source as an external repository
if (fileName ===
path.join(
compilerOpts.baseUrl, 'external/angular', bazelOpts.package,
@ -306,8 +313,8 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
fs.writeFileSync(bazelOpts.tsickleExternsPath, externs);
}
for (const missing of writtenExpectedOuts) {
originalWriteFile(missing, '', false);
for (let i = 0; i < writtenExpectedOuts.length; i++) {
originalWriteFile(writtenExpectedOuts[i], '', false);
}
return {program, diagnostics};
@ -323,7 +330,8 @@ function generateMetadataJson(
program: ts.Program, files: string[], rootDirs: string[], bazelBin: string,
tsHost: ts.CompilerHost) {
const collector = new ng.MetadataCollector();
for (const file of files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
const metadata = collector.getMetadata(sourceFile);
@ -353,7 +361,9 @@ function gatherDiagnosticsForInputsOnly(
// program.getDeclarationDiagnostics() it somehow corrupts the emit.
diagnostics.push(...tsProgram.getOptionsDiagnostics());
diagnostics.push(...tsProgram.getGlobalDiagnostics());
for (const sf of tsProgram.getSourceFiles().filter(f => isCompilationTarget(bazelOpts, f))) {
const programFiles = tsProgram.getSourceFiles().filter(f => isCompilationTarget(bazelOpts, f));
for (let i = 0; i < programFiles.length; i++) {
const sf = programFiles[i];
// Note: We only get the diagnostics for individual files
// to e.g. not check libraries.
diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf));

View File

@ -2,12 +2,12 @@
#
# 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
"""Implementation of the protractor_web_test and protractor_web_test_suite rules.
"""
"Run end-to-end tests with Protractor"
load("@build_bazel_rules_nodejs//internal:node.bzl",
"sources_aspect",
load(
"@build_bazel_rules_nodejs//internal:node.bzl",
"expand_path_into_runfiles",
"sources_aspect",
)
load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite")
load("@io_bazel_rules_webtesting//web/internal:constants.bzl", "DEFAULT_WRAPPED_TEST_TAGS")
@ -16,69 +16,71 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
_CONF_TMPL = "//packages/bazel/src/protractor:protractor.conf.js"
def _protractor_web_test_impl(ctx):
configuration = ctx.actions.declare_file(
"%s.conf.js" % ctx.label.name,
sibling=ctx.outputs.executable)
configuration = ctx.actions.declare_file(
"%s.conf.js" % ctx.label.name,
sibling = ctx.outputs.executable,
)
files = depset(ctx.files.srcs)
for d in ctx.attr.deps:
if hasattr(d, "node_sources"):
files = depset(transitive = [files, d.node_sources])
elif hasattr(d, "files"):
files = depset(transitive = [files, d.files])
files = depset(ctx.files.srcs)
for d in ctx.attr.deps:
if hasattr(d, "node_sources"):
files = depset(transitive = [files, d.node_sources])
elif hasattr(d, "files"):
files = depset(transitive = [files, d.files])
specs = [
expand_path_into_runfiles(ctx, f.short_path)
for f in files
]
specs = [
expand_path_into_runfiles(ctx, f.short_path)
for f in files
]
configuration_sources = []
if ctx.file.configuration:
configuration_sources = [ctx.file.configuration]
if hasattr(ctx.attr.configuration, "node_sources"):
configuration_sources = ctx.attr.configuration.node_sources.to_list()
configuration_sources = []
if ctx.file.configuration:
configuration_sources = [ctx.file.configuration]
if hasattr(ctx.attr.configuration, "node_sources"):
configuration_sources = ctx.attr.configuration.node_sources.to_list()
configuration_file = ctx.file.configuration
if hasattr(ctx.attr.configuration, "typescript"):
configuration_file = ctx.attr.configuration.typescript.es5_sources.to_list()[0]
configuration_file = ctx.file.configuration
if hasattr(ctx.attr.configuration, "typescript"):
configuration_file = ctx.attr.configuration.typescript.es5_sources.to_list()[0]
on_prepare_sources = []
if ctx.file.on_prepare:
on_prepare_sources = [ctx.file.on_prepare]
if hasattr(ctx.attr.on_prepare, "node_sources"):
on_prepare_sources = ctx.attr.on_prepare.node_sources.to_list()
on_prepare_sources = []
if ctx.file.on_prepare:
on_prepare_sources = [ctx.file.on_prepare]
if hasattr(ctx.attr.on_prepare, "node_sources"):
on_prepare_sources = ctx.attr.on_prepare.node_sources.to_list()
on_prepare_file = ctx.file.on_prepare
if hasattr(ctx.attr.on_prepare, "typescript"):
on_prepare_file = ctx.attr.on_prepare.typescript.es5_sources.to_list()[0]
on_prepare_file = ctx.file.on_prepare
if hasattr(ctx.attr.on_prepare, "typescript"):
on_prepare_file = ctx.attr.on_prepare.typescript.es5_sources.to_list()[0]
protractor_executable_path = ctx.executable.protractor.short_path
if protractor_executable_path.startswith('..'):
protractor_executable_path = "external" + protractor_executable_path[2:]
protractor_executable_path = ctx.executable.protractor.short_path
if protractor_executable_path.startswith(".."):
protractor_executable_path = "external" + protractor_executable_path[2:]
server_executable_path = ''
if ctx.executable.server:
server_executable_path = ctx.executable.server.short_path
if server_executable_path.startswith('..'):
server_executable_path = "external" + protractor_executable_path[2:]
server_executable_path = ""
if ctx.executable.server:
server_executable_path = ctx.executable.server.short_path
if server_executable_path.startswith(".."):
server_executable_path = "external" + protractor_executable_path[2:]
ctx.actions.expand_template(
output = configuration,
template = ctx.file._conf_tmpl,
substitutions = {
"TMPL_config": expand_path_into_runfiles(ctx, configuration_file.short_path) if configuration_file else "",
"TMPL_on_prepare": expand_path_into_runfiles(ctx, on_prepare_file.short_path) if on_prepare_file else "",
"TMPL_workspace": ctx.workspace_name,
"TMPL_server": server_executable_path,
"TMPL_specs": "\n".join([" '%s'," % e for e in specs]),
})
ctx.actions.expand_template(
output = configuration,
template = ctx.file._conf_tmpl,
substitutions = {
"TMPL_config": expand_path_into_runfiles(ctx, configuration_file.short_path) if configuration_file else "",
"TMPL_on_prepare": expand_path_into_runfiles(ctx, on_prepare_file.short_path) if on_prepare_file else "",
"TMPL_workspace": ctx.workspace_name,
"TMPL_server": server_executable_path,
"TMPL_specs": "\n".join([" '%s'," % e for e in specs]),
},
)
runfiles = [configuration] + configuration_sources + on_prepare_sources
runfiles = [configuration] + configuration_sources + on_prepare_sources
ctx.actions.write(
output = ctx.outputs.executable,
is_executable = True,
content = """#!/usr/bin/env bash
ctx.actions.write(
output = ctx.outputs.executable,
is_executable = True,
content = """#!/usr/bin/env bash
if [ -e "$RUNFILE_MANIFEST_FILE" ]; then
while read line; do
declare -a PARTS=($line)
@ -101,19 +103,22 @@ echo "Protractor $PROTRACTOR_VERSION"
# Run the protractor binary
$PROTRACTOR $CONF
""".format(TMPL_protractor = protractor_executable_path,
TMPL_conf = configuration.short_path))
return [DefaultInfo(
files = depset([ctx.outputs.executable]),
runfiles = ctx.runfiles(
files = runfiles,
transitive_files = files,
# Propagate protractor_bin and its runfiles
collect_data = True,
collect_default = True,
),
executable = ctx.outputs.executable,
)]
""".format(
TMPL_protractor = protractor_executable_path,
TMPL_conf = configuration.short_path,
),
)
return [DefaultInfo(
files = depset([ctx.outputs.executable]),
runfiles = ctx.runfiles(
files = runfiles,
transitive_files = files,
# Propagate protractor_bin and its runfiles
collect_data = True,
collect_default = True,
),
executable = ctx.outputs.executable,
)]
_protractor_web_test = rule(
implementation = _protractor_web_test_impl,
@ -124,36 +129,43 @@ _protractor_web_test = rule(
doc = "Protractor configuration file",
allow_single_file = True,
cfg = "data",
aspects = [sources_aspect]),
aspects = [sources_aspect],
),
"srcs": attr.label_list(
doc = "A list of JavaScript test files",
allow_files = [".js"]),
allow_files = [".js"],
),
"on_prepare": attr.label(
doc = """A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.""",
allow_single_file = True,
cfg = "data",
aspects = [sources_aspect]),
aspects = [sources_aspect],
),
"deps": attr.label_list(
doc = "Other targets which produce JavaScript such as `ts_library`",
allow_files = True,
aspects = [sources_aspect]),
aspects = [sources_aspect],
),
"data": attr.label_list(
doc = "Runtime dependencies",
cfg = "data"),
cfg = "data",
),
"server": attr.label(
doc = "Optional server executable target",
executable = True,
cfg = "data",
single_file = False,
allow_files = True),
allow_files = True,
),
"protractor": attr.label(
doc = "Protractor executable target (set by protractor_web_test macro)",
executable = True,
cfg = "data",
single_file = False,
allow_files = True),
allow_files = True,
),
"_conf_tmpl": attr.label(
default = Label(_CONF_TMPL),
allow_single_file = True,
@ -162,180 +174,184 @@ _protractor_web_test = rule(
)
def protractor_web_test(
name,
configuration = None,
on_prepare = None,
srcs = [],
deps = [],
data = [],
server = None,
tags = [],
**kwargs):
"""Runs a protractor test in a browser.
name,
configuration = None,
on_prepare = None,
srcs = [],
deps = [],
data = [],
server = None,
tags = [],
**kwargs):
"""Runs a protractor test in a browser.
Args:
name: The name of the test
configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies
server: Optional server executable target
tags: Standard Bazel tags, this macro adds one for ibazel
**kwargs: passed through to `_protractor_web_test`
"""
Args:
name: The name of the test
configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies
server: Optional server executable target
tags: Standard Bazel tags, this macro adds one for ibazel
**kwargs: passed through to `_protractor_web_test`
"""
protractor_bin_name = name + "_protractor_bin"
protractor_bin_name = name + "_protractor_bin"
nodejs_binary(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
nodejs_binary(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
# Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask :protractor_bin_name for its runfiles attr
web_test_data = data + [":" + protractor_bin_name]
if server:
web_test_data += [server]
# Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask :protractor_bin_name for its runfiles attr
web_test_data = data + [":" + protractor_bin_name]
if server:
web_test_data += [server]
_protractor_web_test(
name = name,
configuration = configuration,
on_prepare=on_prepare,
srcs = srcs,
deps = deps,
data = web_test_data,
server = server,
protractor = protractor_bin_name,
tags = tags + [
# Users don't need to know that this tag is required to run under ibazel
"ibazel_notify_changes",
],
**kwargs)
_protractor_web_test(
name = name,
configuration = configuration,
on_prepare = on_prepare,
srcs = srcs,
deps = deps,
data = web_test_data,
server = server,
protractor = protractor_bin_name,
tags = tags + [
# Users don't need to know that this tag is required to run under ibazel
"ibazel_notify_changes",
],
**kwargs
)
def protractor_web_test_suite(
name,
configuration = None,
on_prepare = None,
srcs = [],
deps = [],
data = [],
server = None,
browsers=["@io_bazel_rules_webtesting//browsers:chromium-local"],
args=None,
browser_overrides=None,
config=None,
flaky=None,
local=None,
shard_count=None,
size=None,
tags = [],
test_suite_tags=None,
timeout=None,
visibility=None,
web_test_data=[],
wrapped_test_tags=None,
**remaining_keyword_args):
"""Defines a test_suite of web_test targets that wrap a protractor_web_test target.
name,
configuration = None,
on_prepare = None,
srcs = [],
deps = [],
data = [],
server = None,
browsers = ["@io_bazel_rules_webtesting//browsers:chromium-local"],
args = None,
browser_overrides = None,
config = None,
flaky = None,
local = None,
shard_count = None,
size = None,
tags = [],
test_suite_tags = None,
timeout = None,
visibility = None,
web_test_data = [],
wrapped_test_tags = None,
**remaining_keyword_args):
"""Defines a test_suite of web_test targets that wrap a protractor_web_test target.
Args:
name: The base name of the test.
configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies
server: Optional server executable target
browsers: A sequence of labels specifying the browsers to use.
args: Args for web_test targets generated by this extension.
browser_overrides: Dictionary; optional; default is an empty dictionary. A
dictionary mapping from browser names to browser-specific web_test
attributes, such as shard_count, flakiness, timeout, etc. For example:
{'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1}
'//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}.
config: Label; optional; Configuration of web test features.
flaky: A boolean specifying that the test is flaky. If set, the test will
be retried up to 3 times (default: 0)
local: boolean; optional.
shard_count: The number of test shards to use per browser. (default: 1)
size: A string specifying the test size. (default: 'large')
tags: A list of test tag strings to apply to each generated web_test target.
This macro adds a couple for ibazel.
test_suite_tags: A list of tag strings for the generated test_suite.
timeout: A string specifying the test timeout (default: computed from size)
visibility: List of labels; optional.
web_test_data: Data dependencies for the web_test.
wrapped_test_tags: A list of test tag strings to use for the wrapped test
**remaining_keyword_args: Arguments for the wrapped test target.
"""
# Check explicitly for None so that users can set this to the empty list
if wrapped_test_tags == None:
wrapped_test_tags = DEFAULT_WRAPPED_TEST_TAGS
Args:
name: The base name of the test.
configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies
server: Optional server executable target
browsers: A sequence of labels specifying the browsers to use.
args: Args for web_test targets generated by this extension.
browser_overrides: Dictionary; optional; default is an empty dictionary. A
dictionary mapping from browser names to browser-specific web_test
attributes, such as shard_count, flakiness, timeout, etc. For example:
{'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1}
'//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}.
config: Label; optional; Configuration of web test features.
flaky: A boolean specifying that the test is flaky. If set, the test will
be retried up to 3 times (default: 0)
local: boolean; optional.
shard_count: The number of test shards to use per browser. (default: 1)
size: A string specifying the test size. (default: 'large')
tags: A list of test tag strings to apply to each generated web_test target.
This macro adds a couple for ibazel.
test_suite_tags: A list of tag strings for the generated test_suite.
timeout: A string specifying the test timeout (default: computed from size)
visibility: List of labels; optional.
web_test_data: Data dependencies for the web_test.
wrapped_test_tags: A list of test tag strings to use for the wrapped test
**remaining_keyword_args: Arguments for the wrapped test target.
"""
size = size or "large"
# Check explicitly for None so that users can set this to the empty list
if wrapped_test_tags == None:
wrapped_test_tags = DEFAULT_WRAPPED_TEST_TAGS
wrapped_test_name = name + "_wrapped_test"
protractor_bin_name = name + "_protractor_bin"
size = size or "large"
# Users don't need to know that this tag is required to run under ibazel
tags = tags + ["ibazel_notify_changes"]
wrapped_test_name = name + "_wrapped_test"
protractor_bin_name = name + "_protractor_bin"
nodejs_binary(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
# Users don't need to know that this tag is required to run under ibazel
tags = tags + ["ibazel_notify_changes"]
# Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask the :protractor_bin_name for its runfiles attr
web_test_data = web_test_data + [":" + protractor_bin_name]
if server:
web_test_data += [server]
nodejs_binary(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
_protractor_web_test(
name=wrapped_test_name,
configuration=configuration,
on_prepare=on_prepare,
srcs=srcs,
deps=deps,
data=web_test_data,
server=server,
protractor=protractor_bin_name,
args=args,
flaky=flaky,
local=local,
shard_count=shard_count,
size=size,
tags=wrapped_test_tags,
timeout=timeout,
visibility=["//visibility:private"],
**remaining_keyword_args)
# Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask the :protractor_bin_name for its runfiles attr
web_test_data = web_test_data + [":" + protractor_bin_name]
if server:
web_test_data += [server]
web_test_suite(
name=name,
launcher=":"+wrapped_test_name,
args=args,
browsers=browsers,
browser_overrides=browser_overrides,
config=config,
data=web_test_data,
flaky=flaky,
local=local,
shard_count=shard_count,
size=size,
tags=tags,
test=wrapped_test_name,
test_suite_tags=test_suite_tags,
timeout=timeout,
visibility=visibility)
_protractor_web_test(
name = wrapped_test_name,
configuration = configuration,
on_prepare = on_prepare,
srcs = srcs,
deps = deps,
data = web_test_data,
server = server,
protractor = protractor_bin_name,
args = args,
flaky = flaky,
local = local,
shard_count = shard_count,
size = size,
tags = wrapped_test_tags,
timeout = timeout,
visibility = ["//visibility:private"],
**remaining_keyword_args
)
web_test_suite(
name = name,
launcher = ":" + wrapped_test_name,
args = args,
browsers = browsers,
browser_overrides = browser_overrides,
config = config,
data = web_test_data,
flaky = flaky,
local = local,
shard_count = shard_count,
size = size,
tags = tags,
test = wrapped_test_name,
test_suite_tags = test_suite_tags,
timeout = timeout,
visibility = visibility,
)

View File

@ -1,26 +1,22 @@
"""Allows different paths for these imports in google3.
"""
load("@build_bazel_rules_typescript//internal:build_defs.bzl",
load(
"@build_bazel_rules_typescript//internal:build_defs.bzl",
_tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig",
)
load("@build_bazel_rules_typescript//internal:common/compilation.bzl",
load(
"@build_bazel_rules_typescript//internal:common/compilation.bzl",
_COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES",
_COMMON_OUTPUTS = "COMMON_OUTPUTS",
_compile_ts = "compile_ts",
_DEPS_ASPECTS = "DEPS_ASPECTS",
_compile_ts = "compile_ts",
_ts_providers_dict_to_struct = "ts_providers_dict_to_struct",
)
load("@build_bazel_rules_typescript//internal:common/json_marshal.bzl",
_json_marshal = "json_marshal",
)
tsc_wrapped_tsconfig = _tsc_wrapped_tsconfig
COMMON_ATTRIBUTES = _COMMON_ATTRIBUTES
COMMON_OUTPUTS = _COMMON_OUTPUTS
compile_ts = _compile_ts
DEPS_ASPECTS = _DEPS_ASPECTS
ts_providers_dict_to_struct = _ts_providers_dict_to_struct
json_marshal = _json_marshal

View File

@ -229,14 +229,8 @@ describe('@angular/core ng_package', () => {
expect(shx.cat('testing.metadata.json'))
.toContain(`"exports":[{"from":"./testing/testing"}],"flatModuleIndexRedirect":true`);
});
it('should have an \'actual\' metadata.json file', () => {
expect(shx.cat('testing/testing.metadata.json'))
.toContain(`"metadata":{"async":{"__symbolic":"function"},`);
});
});
describe('fesm2015', () => {
it('should have a fesm15 file in the /fesm2015 directory',
() => { expect(shx.cat('fesm2015/testing.js')).toContain(`export {`); });

View File

@ -30,6 +30,6 @@ jasmine_node_test(
":angular_core",
"//packages/bazel/test/ngc-wrapped/empty:empty_tsconfig.json",
"//packages/bazel/test/ngc-wrapped/empty:tsconfig.json",
"@build_bazel_rules_typescript//internal:worker_protocol.proto",
"@build_bazel_rules_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto",
],
)

View File

@ -6,6 +6,7 @@ load("//tools/http-server:http_server.bzl", "http_server")
ts_library(
name = "app",
srcs = ["app.ts"],
tsconfig = ":tsconfig.json",
)
ts_devserver(
@ -33,6 +34,7 @@ ts_library(
name = "ts_spec",
testonly = True,
srcs = ["test.spec.ts"],
tsconfig = ":tsconfig.json",
)
protractor_web_test_suite(

View File

@ -1,5 +1,5 @@
{
"compilerOptions": {
"lib": ["es2015"]
"lib": ["dom", "es2015"]
}
}

View File

@ -5,6 +5,7 @@ ts_library(
name = "ts_spec",
testonly = True,
srcs = ["test.spec.ts"],
tsconfig = ":tsconfig.json",
)
ts_library(

View File

@ -28,13 +28,13 @@ export interface HttpParameterCodec {
*
*/
export class HttpUrlEncodingCodec implements HttpParameterCodec {
encodeKey(k: string): string { return standardEncoding(k); }
encodeKey(key: string): string { return standardEncoding(key); }
encodeValue(v: string): string { return standardEncoding(v); }
encodeValue(value: string): string { return standardEncoding(value); }
decodeKey(k: string): string { return decodeURIComponent(k); }
decodeKey(key: string): string { return decodeURIComponent(key); }
decodeValue(v: string) { return decodeURIComponent(v); }
decodeValue(value: string) { return decodeURIComponent(value); }
}

View File

@ -51,22 +51,22 @@ export class NgClass implements DoCheck {
private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Input('class')
set klass(v: string) {
set klass(value: string) {
this._removeClasses(this._initialClasses);
this._initialClasses = typeof v === 'string' ? v.split(/\s+/) : [];
this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : [];
this._applyClasses(this._initialClasses);
this._applyClasses(this._rawClass);
}
@Input()
set ngClass(v: string|string[]|Set<string>|{[klass: string]: any}) {
set ngClass(value: string|string[]|Set<string>|{[klass: string]: any}) {
this._removeClasses(this._rawClass);
this._applyClasses(this._initialClasses);
this._iterableDiffer = null;
this._keyValueDiffer = null;
this._rawClass = typeof v === 'string' ? v.split(/\s+/) : v;
this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value;
if (this._rawClass) {
if (isListLikeIterable(this._rawClass)) {

View File

@ -16,6 +16,8 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
* `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
* any existing component will get destroyed.
*
* @usageNotes
*
* ### Fine tune control
*
* You can control the component creation process by using the following optional attributes:
@ -50,7 +52,8 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
* ngModuleFactory: moduleFactory;">
* </ng-container>
* ```
* ## Example
*
* ### A simple example
*
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
*

View File

@ -27,6 +27,8 @@ export class NgForOfContext<T> {
* for each instantiated template inherits from the outer context with the given loop variable
* set to the current item from the iterable.
*
* @usageNotes
*
* ### Local Variables
*
* `NgForOf` provides several exported values that can be aliased to local variables:

View File

@ -17,13 +17,16 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
* - `then` template is the inline template of `ngIf` unless bound to a different value.
* - `else` template is blank unless it is bound.
*
* ## Most common usage
*
* @usageNotes
*
* ### Most common usage
*
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
* seen in this example:
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
*
* ## Showing an alternative template using `else`
* ### Showing an alternative template using `else`
*
* If it is necessary to display a template when the `expression` is falsy use the `else` template
* binding as shown. Note that the `else` binding points to a `<ng-template>` labeled `#elseBlock`.
@ -32,7 +35,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
*
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
*
* ## Using non-inlined `then` template
* ### Using non-inlined `then` template
*
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
@ -40,7 +43,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
*
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
*
* ## Storing conditional result in a variable
* ### Storing conditional result in a variable
*
* A common pattern is that we need to show a set of properties from the same object. If the
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against

View File

@ -41,10 +41,10 @@ export class NgStyle implements DoCheck {
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Input()
set ngStyle(v: {[key: string]: string}) {
this._ngStyle = v;
if (!this._differ && v) {
this._differ = this._differs.find(v).create();
set ngStyle(values: {[key: string]: string}) {
this._ngStyle = values;
if (!this._differ && values) {
this._differ = this._differs.find(values).create();
}
}

View File

@ -11,11 +11,6 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
/**
* @ngModule CommonModule
*
* @usageNotes
* ```
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* ```
*
* @description
*
* Inserts an embedded view from a prepared `TemplateRef`.
@ -24,13 +19,17 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
* `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
* by the local template `let` declarations.
*
* Note: using the key `$implicit` in the context object will set its value as default.
* @usageNotes
* ```
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* ```
*
* ## Example
* Using the key `$implicit` in the context object will set its value as default.
*
* ### Example
*
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
*
*
*/
@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges {

View File

@ -25,6 +25,8 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
* For instance, if you call `location.go('/foo')`, the browser's URL will become
* `example.com#/foo`.
*
* @usageNotes
*
* ### Example
*
* {@example common/location/ts/hash_location_component.ts region='LocationComponent'}

View File

@ -27,7 +27,9 @@ export interface PopStateEvent {
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
* to the URL's path or the URL's hash segment.
*
* Note: it's better to use {@link Router#navigate} service to trigger route changes. Use
* @usageNotes
*
* It's better to use {@link Router#navigate} service to trigger route changes. Use
* `Location` only if you need to interact with or create normalized URLs outside of
* routing.
*
@ -39,6 +41,7 @@ export interface PopStateEvent {
* - `/my/app/user/123/` **is not** normalized
*
* ### Example
*
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
*
*/

View File

@ -47,6 +47,8 @@ export abstract class LocationStrategy {
* representing the URL prefix that should be preserved when generating and recognizing
* URLs.
*
* @usageNotes
*
* ### Example
*
* ```typescript

View File

@ -34,6 +34,8 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* @usageNotes
*
* ### Example
*
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}

View File

@ -67,4 +67,4 @@ export interface LocationChangeEvent {
/**
* @experimental
*/
export interface LocationChangeListener { (e: LocationChangeEvent): any; }
export interface LocationChangeListener { (event: LocationChangeEvent): any; }

View File

@ -51,8 +51,9 @@ const _observableStrategy = new ObservableStrategy();
* changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
* potential memory leaks.
*
* @usageNotes
*
* ## Examples
* ### Examples
*
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
* promise.
@ -64,7 +65,6 @@ const _observableStrategy = new ObservableStrategy();
*
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
*
*
*/
@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy, PipeTransform {

View File

@ -64,6 +64,8 @@ import {DateFormatter} from './intl';
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
* browsers.
*
* @usageNotes
*
* ### Examples
*
* Assuming `dateObj` is (year: 2010, month: 9, day: 3, hour: 12 PM, minute: 05, second: 08)

View File

@ -78,11 +78,12 @@ function formatNumber(
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
*
* @usageNotes
*
* ### Example
*
* {@example common/pipes/ts/number_pipe.ts region='DeprecatedNumberPipe'}
*
*
*/
@Pipe({name: 'number'})
export class DeprecatedDecimalPipe implements PipeTransform {
@ -106,6 +107,8 @@ export class DeprecatedDecimalPipe implements PipeTransform {
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
*
* @usageNotes
*
* ### Example
*
* {@example common/pipes/ts/percent_pipe.ts region='DeprecatedPercentPipe'}
@ -140,6 +143,8 @@ export class DeprecatedPercentPipe implements PipeTransform {
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
*
* @usageNotes
*
* ### Example
*
* {@example common/pipes/ts/currency_pipe.ts region='DeprecatedCurrencyPipe'}

View File

@ -18,7 +18,9 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g;
*
* Maps a value to a string that pluralizes the value according to locale rules.
*
* ## Example
* @usageNotes
*
* ### Example
*
* {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'}
*

View File

@ -18,7 +18,9 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* If none of the keys of the `mapping` match the `value`, then the content
* of the `other` key is returned when present, otherwise an empty string is returned.
*
* ## Example
* @usageNotes
*
* ### Example
*
* {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'}
*

View File

@ -18,9 +18,8 @@ import {Pipe, PipeTransform} from '@angular/core';
*
* The following component uses a JSON pipe to convert an object
* to JSON format, and displays the string in both formats for comparison.
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
*
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
*
*/
@Pipe({name: 'json', pure: false})

View File

@ -44,7 +44,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
*
* <code-example path="common/pipes/ts/number_pipe.ts" region='NumberPipe'></code-example>
*
*
*/
@Pipe({name: 'number'})
export class DecimalPipe implements PipeTransform {

View File

@ -15,6 +15,8 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
*
* Creates a new `Array` or `String` containing a subset (slice) of the elements.
*
* @usageNotes
*
* All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()`
* and `String.prototype.slice()`.
*
@ -31,14 +33,15 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
*
* produces the following:
*
* <li>b</li>
* <li>c</li>
* ```html
* <li>b</li>
* <li>c</li>
* ```
*
* ## String Examples
* ### String Examples
*
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'}
*
*
*/
@Pipe({name: 'slice', pure: false})

View File

@ -26,6 +26,7 @@ ts_library(
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/annotations",
"//packages/compiler-cli/src/ngtsc/factories",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
],
@ -41,5 +42,8 @@ npm_package(
"ivy-local",
"release-with-framework",
],
deps = [":compiler-cli"],
deps = [
":compiler-cli",
"//packages/compiler-cli/src/ngcc",
],
)

View File

@ -6,16 +6,16 @@
"""
def _extract_flat_module_index(ctx):
files = []
for dep in ctx.attr.deps:
if hasattr(dep, "angular"):
metadata = dep.angular.flat_module_metadata
files.extend([metadata.metadata_file, metadata.typings_file])
return [DefaultInfo(files = depset(files))]
files = []
for dep in ctx.attr.deps:
if hasattr(dep, "angular"):
metadata = dep.angular.flat_module_metadata
files.extend([metadata.metadata_file, metadata.typings_file])
return [DefaultInfo(files = depset(files))]
extract_flat_module_index = rule(
implementation = _extract_flat_module_index,
attrs = {
"deps": attr.label_list(),
"deps": attr.label_list(),
},
)

View File

@ -5,6 +5,7 @@
"main": "index.js",
"typings": "index.d.ts",
"bin": {
"ivy-ngcc": "./src/ngcc/main-ngcc.js",
"ngc": "./src/main.js",
"ng-xi18n": "./src/extract_i18n.js"
},
@ -12,7 +13,10 @@
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0",
"tsickle": "^0.32.1",
"chokidar": "^1.4.2"
"chokidar": "^1.4.2",
"convert-source-map": "^1.5.1",
"magic-string": "^0.25.0",
"source-map": "^0.6.1"
},
"peerDependencies": {
"typescript": ">=2.7.2 <2.10",

View File

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

View File

@ -0,0 +1,30 @@
# Angular Compatibility Compiler (ngcc)
This compiler will convert `node_modules` compiled with `ngc`, into `node_modules` which
appear to have been compiled with `ngtsc`.
This conversion will allow such "legacy" packages to be used by the Ivy rendering engine.
## Building
The project is built using Bazel:
```bash
bazel build //packages/compiler-cli/src/ngcc
```
## Unit Testing
The unit tests are built and run using Bazel:
```bash
bazel test //packages/compiler-cli/src/ngcc/test
```
## Integration Testing
There are tests that check the behaviour of the overall executable:
```bash
bazel test //packages/compiler-cli/test/ngcc
```

View File

@ -0,0 +1,9 @@
/**
* @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 {mainNgcc} from './src/main';

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