Compare commits

..

183 Commits

Author SHA1 Message Date
cea02414b0 docs: add changelog for 5.0.0-beta.3 2017-08-09 16:07:37 -07:00
f0ec31e47f release: cut the 5.0.0-beta.3 release 2017-08-09 16:04:57 -07:00
ff5c58be6b feat(forms): add default updateOn values for groups and arrays (#18536)
This commit adds support for setting default `updateOn` values
in `FormGroups` and `FormArrays`. If you set `updateOn` to
’blur’` at the group level, all child controls will default to `’blur’`,
unless the child has explicitly specified a different `updateOn` value.

```
const c = new FormGroup({
   one: new FormControl()
}, {updateOn: blur});
```

 It's worth noting that parent groups will always update their value and
validity immediately upon value/validity updates from children. In other
words, if a group is set to update on blur and its children are individually
set to update on change, the group will still update on change with its
children; its default value will simply not be used.
2017-08-09 15:41:53 -07:00
dca50deae4 docs(core): deprecate ReflectiveInjector
closes #18598
2017-08-09 14:44:49 -07:00
7f2037f0b6 test(aio): fix running docs examples against local builds (#18520)
This commit also updates the version of `@angular/cli` used for docs examples.
The previous (transient) dependency `@ngtools/webpack` was not compatible with
`@angular/compiler-cli@>=5` and was breaking when running against the local
builds (currently at 5.0.0-beta.2). The version of `@ngtools/webpack` used by
the latest `@angular/cli` version is compatible with `@angular/compiler-cli@5`.
2017-08-09 14:21:10 -07:00
fd6ae571b8 fix(aio): add missing code snippet (#18547)
The snippet got lost some time during the migration from the old version (it is
[present in v2][1]).

[1]: https://v2.angular.io/docs/ts/latest/cookbook/aot-compiler.html#!#running-the-application

Fixes #18544
2017-08-09 14:20:25 -07:00
b14250bef9 test(aio): fix the deploy-to-firebase tests
This commit also ensures that if the tests fail, the script exits with an error.
2017-08-09 14:18:33 -07:00
6fb5250185 ci(aio): fix deploying the stable branch to Firebase
The `deploy-to-firebase.sh` always expects there to be a
`src/extra-files/<mode>` directory and breaks if it doesn't exist.
2017-08-09 14:18:33 -07:00
2f9d8ff46d test(animations): disable buggy test in Chrome 39 (#18483)
Fixes #15793
2017-08-09 14:15:40 -07:00
e54bd59f22 fix(core): forbid destroyed views to be inserted or moved in VC (#18568)
Fixes #17780
2017-08-09 14:11:51 -07:00
1e1833198d ci(aio): fix deploying to firebase (#18590) 2017-08-08 13:59:25 -07:00
6f2038cc85 fix(compiler-cli): fix and re-enble expression lowering (#18570)
Fixes issue uncovered by #18388 and re-enables expression
lowering disabled by #18513.
2017-08-08 12:40:08 -07:00
f0a55016af fix(core): fix platform-browser-dynamic (#18576)
follow-up for #18496
2017-08-08 11:41:12 -07:00
fcadbf4bf6 perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.

Code savings for HelloWorld using Closure:

Reflective: bundle.js:  105,864(34,190 gzip)
    Static: bundle.js:  154,889(33,555 gzip)
                            645( 2%)

BREAKING CHANGE:

`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.

Example:
Before:
```
[
  MyClass,
  {provide: ClassA, useClass: SubClassA}
]

```

After:
```
[
  {provide: MyClass, deps: [Dep1,...]},
  {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```

NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.

Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.

DEPRECATION:

- `ReflectiveInjector` is now deprecated as it will be remove. Use
  `Injector.create` as a replacement.

closes #18496
2017-08-07 15:42:34 -07:00
d9d00bd9b5 feat(core): Create StaticInjector which does not depend on Reflect polyfill. 2017-08-07 15:40:15 -07:00
f69561b2de feat(forms): add updateOn submit option to FormControls (#18514) 2017-08-07 15:39:25 -07:00
685cc26ab2 fix(common): don't recreate view when context shape doesn't change (#18277)
Problem description: when using ngTemplateOutlet with context as
an object literal in a template and binding to the context's property
the embedded view would get re-created even if context object remains
essentially the same (the same shape, just update to one properties).
This happens since currently change detection will re-create object
references when an object literal is used and one of its properties
gets updated through a binding.

Solution: this commit changes ngTemplateOutlet logic so we take
context object shape into account before deciding if we should
re-create view or just update existing context.

Fixes #13407
2017-08-07 14:31:26 -07:00
5b7432b6ea fix(compiler-cli): remove minimist dependency of compiler-cli/index (#18532)
Indirectly removes the minimist dependency in the language service
package was added with the addition of `ngc.ts`.
2017-08-07 14:30:35 -07:00
04b18a9f46 docs(common): fix the DatePipe API docs (#18548) 2017-08-07 11:47:08 -07:00
05472cb21b fix(animations): support persisting dynamic styles within animation states (#18468)
Closes #18423
Closes #17505
2017-08-07 11:40:04 -07:00
c0c03dc4ba fix(animations): revert container/queried animations accordingly during cancel (#18516) 2017-08-07 11:38:30 -07:00
f9f8924c49 docs(aio): typo & update the bio (#18559) 2017-08-07 10:20:11 -07:00
10897d6473 ci(aio): compute AIO deployment mode
There are now 3 modes for deployment: next, stable, archive.
We compute which mode (and other deployment properties)
from the `TRAVIS_BRANCH` and the `STABLE_BRANCH`.

If the TRAVIS_BRANCH is master we deploy as "next".
If the `TRAVIS_BRANCH` matches the `STABLE_BRANCH` we deploy as "stable".

Otherwise if the branch has a major version lower than the stable version
and its minor version is highest of similar branches we deploy as "archive".

For "archive" deployments we compute the firebase project and deployment
url based on the major version of the `TRAVIS_BRANCH`.

As well as choosing where to deploy the build, we also use this
to select the environment file for the AIO Angular app.
This will enable the app to change its rendering and behaviour
based on its mode.

See #18287
Closes #18297
2017-08-04 09:14:18 -07:00
340837aa46 feat(aio): add "archive" and "next" color themes 2017-08-04 09:13:34 -07:00
42ef1be75c feat(aio): redirect marketing pages to docs if deploy mode is archive
See #18287
2017-08-04 09:13:34 -07:00
a5801b6020 feat(aio): add deploy mode to version picker
See #18287
2017-08-04 09:13:34 -07:00
70b62949de feat(aio): enable deployment mode to be set via URL query
The deployment mode set from the environment provided at build time;
or overridden by the `mode` query parameter: e.g. `...?mode=archive`

See #18287
2017-08-04 09:13:34 -07:00
36161d99f6 feat(aio): update UI based on deployment mode
* Add a banner if the mode is "archive"
* Add a `mode-...` class to the `aio-shell` element to enable
mode based theming.

See #18287
2017-08-04 09:13:34 -07:00
0714139e37 ci(aio): include extra files in AIO deployment based on mode
Any files that are inside the `extra-files/{mode}` folder
will be copied over to the `dist` folder before deployment
to Firebase.

See #18287
2017-08-04 09:13:34 -07:00
bcb36d9b6d ci(aio): compute AIO deployment mode
There are now 3 modes for deployment: next, stable, archive.
We compute which mode (and other deployment properties)
from the `TRAVIS_BRANCH` and the `STABLE_BRANCH`.

If the TRAVIS_BRANCH is master we deploy as "next".
Otherwise if the branch is the highest of its minor versions
we deploy as "stable" if the `TRAVIS_BRANCH` matches the `STABLE_BRANCH` or
else "archive".

For "archive" deployments we compute the firebase project and deployment
url based on the major version of the `TRAVIS_BRANCH`.

As well as choosing where to deploy the build, we also use this
to select the environment file for the AIO Angular app.
This will enable the app to change its rendering and behaviour
based on its mode.

See #18287
2017-08-04 09:13:34 -07:00
ca695e0632 fix(compiler-cli): disable buggy expression lowering (#18513) 2017-08-03 14:31:23 -07:00
9b015a95eb docs(aio): tech edits to form validation
closes #18495
2017-08-03 13:57:31 -07:00
939dc44391 docs(forms): update and re-organize validation guide 2017-08-03 13:56:53 -07:00
5651e4ac72 fix(compiler-cli): modified ngc to throw all errors, not just syntax (#18388) 2017-08-03 11:10:47 -07:00
1dca575701 fix(compiler): ignore @import in multi-line css (#18452)
Fixes #18038
2017-08-03 11:00:38 -07:00
333a708bb6 feat(forms): add updateOn blur option to FormControls (#18408)
By default, the value and validation status of a `FormControl` updates
whenever its value changes. If an application has heavy validation
requirements, updating on every text change can sometimes be too expensive.

This commit introduces a new option that improves performance by delaying
form control updates until the "blur" event.  To use it, set the `updateOn`
option to `blur` when instantiating the `FormControl`.

```ts
// example without validators
const c = new FormControl(, { updateOn: blur });

// example with validators
const c= new FormControl(, {
   validators: Validators.required,
   updateOn: blur
});
```

Like in AngularJS, setting `updateOn` to `blur` will delay the update of
the value as well as the validation status. Updating value and validity
together keeps the system easy to reason about, as the two will always be
in sync. It's  also worth noting that the value/validation pipeline does
still run when the form is initialized (in order to support initial values).

Closes #7113
2017-08-02 18:10:10 -07:00
3a227a1f6f refactor(router): compile router cleanly with TypeScript 2.4 (#18465) 2017-08-02 17:32:02 -07:00
81cb5bc3a7 docs(router): fix typo (#18479) 2017-08-02 17:31:09 -07:00
1640d2aa0b refactor(platform-browser): compiler platform-browser packages cleanly (#18464) 2017-08-02 16:30:50 -07:00
5f501c722b refactor(forms): compile forms cleanly with TypeScript 2.4 (#18462) 2017-08-02 16:29:31 -07:00
ea07856cc5 refactor(upgrade): compile upgrade cleanly with TypeScript 2.4 (#18461) 2017-08-02 16:28:04 -07:00
7c47b62a96 fix(compiler): cleanly compile with TypeScript 2.4 (#18456) 2017-08-02 16:26:42 -07:00
e25b3dd163 fix(benchpress): compile cleanly with TS 2.4 (#18455) 2017-08-02 16:24:00 -07:00
6a88659c9a test(upgrade): fix an IE9 timer issue in downgrade module tests (#18482) 2017-08-02 16:09:38 -07:00
44ae6e94e3 fix(aio): fix layout of the webpack guide (#18493)
This is possibly a temporary fix for the layout, until we decide whether we want
to remove the guide or properly add it to the SideNav menu.

Fixes #17912
2017-08-02 16:00:36 -07:00
1635a06bda ci(aio): Add commit message to payload data (#18137) 2017-08-02 15:59:20 -07:00
3923c30df0 docs(aio): fix missing anchor-open in i18n documentation (#18476)
The Internationalisation documentation, "Translate text nodes" section, has an incomplete
markdown anchor, and leaks markdown into the page. Fix the anchor by adding the opening bracket.
2017-08-02 15:56:01 -07:00
99017bf3ff fix(aio): correctly redirect cookbook/a1-a2-quick-reference.html (#18418)
Fixes #18415
2017-08-02 15:54:22 -07:00
4d117faf1a test(common): skip some DatePipe tests in old Chrome where Intl is buggy (#15784) 2017-08-02 15:51:59 -07:00
5cc9913ded docs(aio): replace old blog link in footer (#18448)
Fixes #18233
2017-08-02 15:45:54 -07:00
1d09838622 ci: remove chromium fold reference (#18445) 2017-08-02 15:43:26 -07:00
9adf40aa77 build(aio): use cli 1.3.0-rc (#18290) 2017-08-02 15:37:03 -07:00
89c616199f docs: improve github labels by introducing "PR target" labels (#18436)
I also renamed all "pr_*" lables to "PR *" lables, removed obsolete
"chore" label, and added docs label.
2017-08-02 15:30:36 -07:00
1e1af7ffcb docs: changelog for 5.0.0-beta.2 release 2017-08-02 13:23:27 -07:00
a84b2bc945 release: cut the 5.0.0-beta.2 release 2017-08-02 13:21:07 -07:00
7abcb99d57 docs: add changelog for 4.3.3 2017-08-02 13:19:00 -07:00
49cd8513e4 feat(router): add events tracking activation of individual routes
* Adds `ChildActivationStart` and `ChildActivationEnd`
* Adds test to verify the PreActivation phase of routing
2017-08-01 10:44:00 -07:00
82b067fc40 build: ignore node_modules for tslint 2017-08-01 10:13:44 -07:00
9479a106bb build: enable TSLint on the packages folder 2017-07-31 15:47:57 -07:00
e64b54b67b fix(compiler): do not consider arguments when determining recursion
The static reflectory check for macro function recursion was too
agressive and disallowed calling a function with argument that also
calls the same function. For example, it disallowed nested animation
groups.

Fixes: #17467
2017-07-31 13:42:31 -07:00
cc2a4c41f9 build(aio): fix warning about missing <h1>
Fixes #17549
2017-07-31 13:40:07 -07:00
a11542a375 docs(aio): fixed list format in FormArray section 2017-07-31 11:31:05 -07:00
b6c4af6495 feat(compiler-cli): automatically lower lambda expressions in metadata 2017-07-31 11:30:44 -07:00
67dff7bd5d feat(tsc-wrapped): allow values to be substituted by collector clients
Also reenabled tests that were unintentionally disabled when they were
moved from tools/@angular.
2017-07-31 11:30:44 -07:00
381471d338 fix(compiler): fix for element needing implicit parent placed in top-level ng-container
fixes #18314
2017-07-31 11:30:19 -07:00
ebef5e697a feat(forms): add options arg to abstract controls
FormControls, FormGroups, and FormArrays now optionally accept an options
object as their second argument. Validators and async validators can be
passed in as part of this options object (though they can still be passed
in as the second and third arg as before).

```ts
const c = new FormControl(, {
   validators: [Validators.required],
   asyncValidators: [myAsyncValidator]
});
```

This commit also adds support for passing arrays of validators and async
validators to FormGroups and FormArrays, which formerly only accepted
individual functions.

```ts
const g = new FormGroup({
   one: new FormControl()
}, [myPasswordValidator, myOtherValidator]);
```

This change paves the way for adding more options to AbstractControls,
such as more fine-grained control of validation timing.
2017-07-31 11:29:32 -07:00
d71ae278ef fix(aio): fix links to source for paths with symlinks
Fixes #18353
2017-07-28 15:28:59 -07:00
46207538ef ci: short-circuit npm install for aio builds that use yarn only 2017-07-28 15:28:28 -07:00
71eb7437b6 docs(aio): delay ngUpgrade e2e test to avoid flakes 2017-07-28 15:28:28 -07:00
b5ffbe342b build: short-circuit build for AIO tasks 2017-07-28 15:28:28 -07:00
0f79223008 docs(aio): fix deprecated protractor API usage
`browser.getLocationAbsUrl()` is deprecated.
We should use `browser.getCurrentUrl()` instead.
2017-07-28 15:28:28 -07:00
a085223331 ci(aio): test the example e2e files using local build of Angular 2017-07-28 15:28:28 -07:00
c383048259 build(aio): ignore generated aot files
Assets such as images and data which are generated
by the aot build were not being ignored.
2017-07-28 15:28:28 -07:00
b18eb04b46 docs(aio): remove generated styles.css file
This file should have been ignored as it is created
during the build of the example
2017-07-28 15:28:28 -07:00
c8c2ab012a build(aio): support overriding the Angular packages in examples with locally built ones 2017-07-28 15:28:28 -07:00
ecff8e6c93 build(aio): refactor and test the example-boilerplate tool 2017-07-28 15:28:28 -07:00
51f1da1b85 ci: shard the aio example e2e tests 2017-07-28 15:28:28 -07:00
a5e18c4cdf ci(aio): support sharding of example e2e tests 2017-07-28 15:28:28 -07:00
cf6284656f build(aio): upgrade @angular/material to 2.0.0-meta.8 2017-07-28 15:26:45 -07:00
3182ddaf3e build(aio): upgrade @angular/* to 4.3.1 2017-07-28 15:26:45 -07:00
416ed691e5 docs(aio): fix URLSearchParams interface link to MDN
Fixes #18367
2017-07-28 15:26:04 -07:00
0fb7484d51 refactor(aio): move content-specific images to content/images/
Fixes #17053
2017-07-28 15:06:49 -07:00
6a3454e81e refactor(aio): rename unused directories to _unused 2017-07-28 15:06:49 -07:00
c3fbe87012 fix(aio): fix link to logo in example 2017-07-28 15:06:49 -07:00
24117d7a49 refactor(aio): move unused images to unused directories
This prevents the ServiceWorker from prefetching unnecessary files.
2017-07-28 15:06:49 -07:00
5808153359 docs: add changelog for 5.0.0.-beta.1 2017-07-27 14:59:24 -07:00
9030c8a03e release: cut the 5.0.0-beta.1 release 2017-07-27 14:57:38 -07:00
b14fc06fa2 docs: add changelog for 4.3.2 2017-07-27 14:52:35 -07:00
a7f2468184 Revert "fix(router): should throw when lazy loaded module doesn't define any routes (#15001)"
This reverts commit 82923a381d.
2017-07-27 10:53:01 -07:00
fae47d86b3 refactor(forms): move value accessor tests into own spec (#18356)
PR Close #18356
2017-07-26 17:55:37 -05:00
d20ac14fe2 refactor(compiler-cli): allow custom error checking function in ngc (#18355)
PR Close #18355
2017-07-26 17:55:31 -05:00
cae3e6dca0 ci: give ownership of ngc-wrapped to compiler-cli maintainers (#18354)
PR Close #18354
2017-07-26 17:55:24 -05:00
086f4aa72c fix(router): child CanActivate guard should wait for parent to complete (#18110)
Closes #15670

PR Close #18110
2017-07-26 17:11:22 -05:00
82923a381d fix(router): should throw when lazy loaded module doesn't define any routes (#15001)
Closes #14596

PR Close #15001
2017-07-26 17:11:07 -05:00
5152abb037 docs(aio): add my details as a contributor (#18315)
PR Close #18315
2017-07-26 17:11:07 -05:00
67f7032321 fix(aio): correctly process markdown link in "Browser Support" (#18349)
The markdown processor expects an empty line between an opening tag and the
markdown content. (If there is no empty line, the content is interpreted as
plain HTML.)
Previously, the line between the opening `<td>` and the content contained
whitespace, which caused the content to be interpreted as HTML and not markdown.

Fixes #18312

PR Close #18349
2017-07-26 16:07:26 -05:00
205abe8140 build: fix broken bazel build (#18335) 2017-07-26 09:40:33 -07:00
b582e2b311 docs(aio): update examples to 4.3 2017-07-25 15:32:38 -07:00
91ab39cc55 docs(aio) - Fixed link to the glossary dash-case term (#18311)
PR Close #18311
2017-07-25 15:59:28 -05:00
38ec05f533 fix(compiler): add equiv & disp attributes to Xliff2 ICU placeholders (#18283)
Fixes #17344

PR Close #18283
2017-07-25 15:58:53 -05:00
b3085e96c2 feat(compiler): add representation of placeholders to xliff & xmb
Closes #17345
2017-07-25 15:58:53 -05:00
4cea2bd612 docs(platform-server): inline PlatformOptions and add doc strings (#18264)
Fix documentation for the options passed into renderModule and
renderModuleFactory.

PR Close #18264
2017-07-25 15:58:13 -05:00
ce47546188 refactor(compiler-cli): add support for browser compiler bundle (#17979)
PR Close #17979
2017-07-25 15:51:46 -05:00
6279e50d78 perf(core): use native addEventListener for faster rendering. (#18107)
Angular can make many assumptions about its event handlers. As a result
the bookkeeping for native addEventListener is significantly cheaper
than Zone's addEventLister which can't make such assumptions.

This change bypasses the Zone's addEventListener if present and always
uses the native addEventHandler. As a result registering event listeners
is about 3 times faster.

PR Close #18107
2017-07-25 15:35:44 -05:00
8bcb268140 ci: use chrome stable (#18307) 2017-07-25 11:18:24 -07:00
6fc5940959 build: Bazel builds ngfactories for packages/core (#18289)
PR Close #18289
2017-07-21 18:09:47 -05:00
0317c4c478 build: update bazel rules to latest (#18289) 2017-07-21 18:09:44 -05:00
b7a6f52d59 perf: latest tsickle to tree shake: abstract class methods & interfaces (#18236)
In previous version of tsickle abstract class methods were materialized.
The change resulted in 6Kb savings in angular.io bundle.

This change also required the removal of `@private` and `@return` type
annotation as it is explicitly dissalowed by tsickle.

NOTE: removed casts in front of `makeDecorator` due to:
https://github.com/angular/devkit/issues/45

```
 14938 Jul 19 13:16 0.b19e913fbdd6507d346b.chunk.js
  1535 Jul 19 13:16 inline.d8e019ea3cfdd86c2bd0.bundle.js
589178 Jul 19 13:16 main.54c97bcb6f254776b678.bundle.js
 34333 Jul 19 13:16 polyfills.4a3c9ca9481d53803157.bundle.js

 14938 Jul 18 16:55 0.b19e913fbdd6507d346b.chunk.js
  1535 Jul 18 16:55 inline.0c83abb44fad9a2768a7.bundle.js
582786 Jul 18 16:55 main.ea290db71b051813e156.bundle.js
 34333 Jul 18 16:55 polyfills.4a3c9ca9481d53803157.bundle.js

main savings: 589178 - 582786 = 6,392
```

PR Close #18236
2017-07-21 16:35:37 -05:00
7ae7573bc8 fix(core): invoke error handler outside of the Angular Zone (#18269)
In Node.JS console.log/error/warn functions actually resuls in a socket
write which in turn is considered by Zone.js as an async task.

This means that if there is any exception during change detection in a platform-server
application the error handler will make the Angular Zone unstable which
in turn will cause change detection to run on next tick and cause an
infinite loop.

It is also better to run the error handler outside of the Angular Zone
in general on all platforms so that an error in the error handler itself doesn't cause an
infinite loop.

Fixes #17073, #7774.

PR Close #18269
2017-07-21 16:35:23 -05:00
abee785821 refactor(tsc-wrapped): update tsc-wrapped to pass strictNullCheck (#18160)
PR Close #18160
2017-07-21 12:26:20 -05:00
619e625ee2 refactor(tsc-wrapped): move tsc-wrapped to the packages directory (#18160) 2017-07-21 12:26:16 -05:00
a6c635e69e ci: force precise on Travis (#18282)
PR Close #18282
2017-07-21 12:20:27 -05:00
e0a9625e46 test: fix bad merge (#18267)
PR Close #18267
2017-07-21 11:53:34 -05:00
fd0cc01eed fix(animations): export BrowserModule as apart of BrowserAnimationsModule (#18263)
PR Close #18263
2017-07-20 17:47:49 -05:00
1bfc77bf8c docs(aio): pngcrush all pngs (#18243)
PR Close #18243
2017-07-20 17:47:06 -05:00
a094769bca fix(platform-server): don't clobber parse5 properties when setting (#18237)
element properties.

Fixes #17050.

We now store all element properties in a separate 'properties' bag.

PR Close #18237
2017-07-20 17:46:37 -05:00
b4c98305da refactor(common): CleanUp HttpClient's imports (#18120)
PR Close #18120
2017-07-20 17:43:23 -05:00
a3a54299af fix(compiler): allow numbers for ICU message cases in lexer (#18095)
Closes #18095
Fixes #17799
2017-07-20 17:41:54 -05:00
15a3e2d307 ci(aio): fix aio payload script 2017-07-20 15:32:32 -07:00
54e0244954 Revert "docs: Remove unneeded file (#18106)"
This reverts commit 72fe45db2b.
2017-07-20 16:46:47 -05:00
43c33d5663 fix(upgrade): ensure downgraded components are created in the Angular zone (#18209)
PR Close #18209
2017-07-20 16:25:11 -05:00
6d7799fce9 test(upgrade): fail tests when there are AngularJS errors (#18209) 2017-07-20 16:25:07 -05:00
d31dc7b2b3 fix(upgrade): throw error if trying to get injector before setting (#18209)
Previously, `undefined` would be returned.
This change makes it easier to identify incorrect uses/bugs.
(Discussed in https://github.com/angular/angular/pull/18213#issuecomment-316191308.)
2017-07-20 16:24:45 -05:00
4cd4f7a208 aio: debounce search and delay index building (#18134)
* feat(aio): debounce search requests

* feat(aio): delay loading search worker and index
2017-07-20 09:51:40 -07:00
72fe45db2b docs: Remove unneeded file (#18106) 2017-07-20 09:50:17 -07:00
8d2819121b fix(aio): invalid formatting in architecure.md (#18159)
Introduced in e110a80caf (diff-9ac9c6a9277eea9856d75249a7c0a40aL127)
2017-07-20 09:48:02 -07:00
073e8ba2f2 docs(aio): replace old blog link (#18252)
Fixes #18233
* Docs(aio): Replaced old blog link Now with the Link to the new Angular.io Blog
* Removed double braces
2017-07-20 09:45:14 -07:00
5d1864fe68 docs: fixing invisible tag in README (#18245) 2017-07-20 09:43:42 -07:00
eaa843b55f ci: test bazel builds on travis (#18240) 2017-07-20 09:40:40 -07:00
c6cf678a07 docs(aio): Fixed typo with closing div 2017-07-20 09:38:28 -07:00
31cb418370 docs(aio): Fix http guide 2017-07-20 09:35:27 -07:00
8de44cf5e3 docs: fix wrong link in CONTRIBUTING.md (#18228)
The link to the CONTRIBUTING.md file was not correct
2017-07-20 09:33:15 -07:00
c67bad4f43 docs(router): minor typo (#18226)
Fix a minor typo in the description of a router spec.
2017-07-20 09:32:34 -07:00
410f21c75c docs(aio): fix typo in NgModule FAQs (#18211)
Fix #18133. Fix typo 'added' to 'add' in the 'Why does lazy loading create a child injector' section.
2017-07-20 09:07:30 -07:00
54ea5b6ffd docs: add changelog for 5.0.0-beta.0 2017-07-19 13:16:43 -07:00
0af03beaed release: cut the 5.0.0-beta.0 release 2017-07-19 13:12:50 -07:00
d71fa734f5 docs: add changelog for 4.3.1 2017-07-19 12:55:08 -07:00
6f45519d6f feat(animations): support :increment and :decrement transition aliases 2017-07-19 11:24:00 -07:00
65c9e13105 fix(compiler-cli): don't generate empty <target/> when extracting xliff
Fixes #15754
2017-07-19 09:45:52 -07:00
9208f0beea docs(aio): fix typo in Router documentation
Fix title and link to RouteConfigLoadEnd documentation
2017-07-19 15:01:50 +01:00
5344be5182 fix(animations): make sure @.disabled works in non-animation components
Note 4.3 only!

Prior to this fix when [@.disabled] was used in a component that
contained zero animation code it wouldn't register properly because the
renderer associated with that component was not an animation renderer.
This patch ensures that it gets registered even when there are no
animations set.
2017-07-18 16:37:04 -07:00
5db6f38b73 fix(animations): do not crash animations if a nested component fires CD during CD
Closes #18193
2017-07-18 15:22:43 -07:00
d22f8f54db fix(animations): always camelcase style property names that contain auto styles
Closes #17938
2017-07-18 15:22:30 -07:00
23146c9201 fix(animations): capture cancelled animation styles within grouped animations
Closes #17170
2017-07-18 15:22:10 -07:00
a5205c686e fix(upgrade): allow accessing AngularJS injector from downgraded module 2017-07-18 14:00:19 -07:00
807648251f fix(platform-server): provide XhrFactory for HttpClient 2017-07-18 13:59:26 -07:00
5c62e300e1 fix(common): send flushed body as error instead of null
fix #18181
2017-07-18 10:57:51 -07:00
256bc8acdd docs(http): Make name of injected HttpTestingController consistent 2017-07-18 10:35:56 -07:00
59c23c7bd7 feat(upgrade): propagate touched state of NgModelController 2017-07-18 10:35:35 -07:00
e03adb9edd docs(platform-server): add doc string for PlatformOptions 2017-07-18 10:34:57 -07:00
b399cb26d9 fix(router): terminal route in custom matcher 2017-07-18 10:25:18 -07:00
3b588fe2b0 docs: fix typo 2017-07-18 10:11:55 -07:00
95635c18c7 fix(compiler): ensure jit external id arguments names are unique
Fixes: #17558, #17378, #8676
2017-07-18 10:11:32 -07:00
e20cfe1bbc fix(router): canDeactivate guards should run from bottom to top
Closes #15657.
2017-07-18 10:04:39 -07:00
eb6fb5f87e fix(router): should navigate to the same url when config changes
Closes #15535
2017-07-18 10:04:11 -07:00
ad3029e786 fix(router): should run resolvers for the same route concurrently
Fixes #14279
2017-07-18 10:03:33 -07:00
2a2fe11e8d docs(aio): fix HttpClient setting new header sample 2017-07-18 10:02:27 -07:00
7d0f2cd51e fix(aio): remove title attribute from CodeExampleComponent
This was causing browser to add an unwanted tooltip that appeared
when the user hovers over the code.

See #17524
2017-07-18 17:55:28 +01:00
36faba1aab fix(aio): add quote to module 2017-07-18 17:48:04 +01:00
92179bcc64 fix(aio): do not wrap <code-tabs> tab labels
Fixes #17751
2017-07-18 17:43:59 +01:00
cdb069ab0e docs(aio): fix cheatsheet layout for narrow screens
* Tell the app that this will have no Table of Contents, since we have no
h2 headings anyway.
* Remove all the `nbsp;` from the code since that doesn't help with layout
* Remove side padding from sidenav-content when screen is narrow
* Restyle the cheatsheet table when the screen is narrow
2017-07-18 17:32:43 +01:00
c453b7bcfa build(aio): fail doc-gen if referenced images are missing 2017-07-18 11:45:05 +01:00
9d97163c64 docs(aio): fix broken image sources 2017-07-18 11:45:05 +01:00
f054c8360b docs(aio): fix up broken links 2017-07-18 11:45:05 +01:00
758848961e build(aio): abort doc-gen on dangling links 2017-07-18 11:45:05 +01:00
99b666614d build(aio): abort doc-gen if an example is missing
Closes #16936
2017-07-18 11:45:05 +01:00
3f331b53b2 docs(aio): update jelbourn photo 2017-07-17 14:51:11 -07:00
375d598a9f docs(aio): add George's fixes 2017-07-17 14:49:19 -07:00
cd67fced1c docs(aio): add npm install to Sublime instructions 2017-07-17 14:49:19 -07:00
a77cf7ee37 docs(aio): add Chuck's comments 2017-07-17 14:49:19 -07:00
2150b45954 docs(aio): add language service doc 2017-07-17 14:49:19 -07:00
9f99f4fae2 docs: fix HttpClient sample 2017-07-17 14:03:32 -07:00
c6ad212a98 docs(aio): fix HttpClient's interceptor sample 2017-07-17 14:03:16 -07:00
47b3ecd9a3 docs(http): fix "Expecting and answering requests" example mistake
Possibly overlooked testing documentation mistake fixed:
- `http.get('/data')` probably ought to be paired with `httpMock.expectOne('/data')` instead of `httpMock.expectOne('/data')`.
2017-07-17 14:01:44 -07:00
8c81c62d46 docs: fix HttpClient logging's sample 2017-07-17 14:01:24 -07:00
7e72317059 docs(http): fixed syntax error in AuthInterceptor example 2017-07-17 14:00:59 -07:00
0bb8423df9 ci: add GK and PBD to aio content and marketing groups 2017-07-17 10:52:34 -07:00
95698d93ad fix(aio): remove title from callout 2017-07-15 15:35:15 +01:00
c649da9f0a fix(aio): remove unused news.html file
Although outdated and not used, the file would be picked up and showed in search
results.
2017-07-15 15:29:20 +01:00
0bf0c35bca build(aio): render type parameters of API function exports
Fixes #18123
2017-07-15 08:52:35 +01:00
97e6901ded Revert "revert: revert: ci(aio): exclude changes in aio/content folder"
This reverts commit 3d85f72652.

Still causing repeated flakes on master.
2017-07-14 14:54:31 -07:00
30e76fcd80 feat(upgrade): support lazy-loading Angular module into AngularJS app 2017-07-14 14:10:30 -07:00
44b50427d9 refactor(upgrade): clean up some types 2017-07-14 14:10:30 -07:00
256 changed files with 8177 additions and 3005 deletions

View File

@ -42,7 +42,7 @@ jobs:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
- run: bazel build ...
- run: bazel build packages/...
- save_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
paths:

View File

@ -69,8 +69,8 @@ groups:
- "*.lock"
- "tools/*"
exclude:
- "tools/@angular/tsc-wrapped/*"
- "tools/public_api_guard/*"
- "tools/ngc-wrapped/*"
- "aio/*"
users:
- IgorMinar #primary
@ -136,8 +136,9 @@ groups:
compiler-cli:
conditions:
files:
- "tools/@angular/tsc-wrapped/*"
- "packages/tsc-wrapped/*"
- "packages/compiler-cli/*"
- "tools/ngc-wrapped/*"
users:
- alexeagle
- chuckjaz

View File

@ -1,21 +1,126 @@
<a name="4.3.4"></a>
## [4.3.4](https://github.com/angular/angular/compare/4.3.3...4.3.4) (2017-08-10)
<a name="5.0.0-beta.3"></a>
# [5.0.0-beta.3](https://github.com/angular/angular/compare/5.0.0-beta.2...5.0.0-beta.3) (2017-08-09)
### Bug Fixes
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([5a165eb](https://github.com/angular/angular/commit/5a165eb))
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([e0660b1](https://github.com/angular/angular/commit/e0660b1)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([5afc7ab](https://github.com/angular/angular/commit/5afc7ab))
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([5e4054b](https://github.com/angular/angular/commit/5e4054b))
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([c0c03dc](https://github.com/angular/angular/commit/c0c03dc))
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([05472cb](https://github.com/angular/angular/commit/05472cb)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([e25b3dd](https://github.com/angular/angular/commit/e25b3dd))
* **common:** don't recreate view when context shape doesn't change ([#18277](https://github.com/angular/angular/issues/18277)) ([685cc26](https://github.com/angular/angular/commit/685cc26)), closes [#13407](https://github.com/angular/angular/issues/13407)
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([7c47b62](https://github.com/angular/angular/commit/7c47b62))
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([1dca575](https://github.com/angular/angular/commit/1dca575)), closes [#18038](https://github.com/angular/angular/issues/18038)
* **compiler-cli:** disable buggy expression lowering ([#18513](https://github.com/angular/angular/issues/18513)) ([ca695e0](https://github.com/angular/angular/commit/ca695e0))
* **compiler-cli:** fix and re-enable expression lowering ([#18570](https://github.com/angular/angular/issues/18570)) ([6f2038c](https://github.com/angular/angular/commit/6f2038c)), closes [#18388](https://github.com/angular/angular/issues/18388)
* **compiler-cli:** modified ngc to throw all errors, not just syntax ([#18388](https://github.com/angular/angular/issues/18388)) ([5651e4a](https://github.com/angular/angular/commit/5651e4a))
* **compiler-cli:** remove minimist dependency of compiler-cli/index ([#18532](https://github.com/angular/angular/issues/18532)) ([5b7432b](https://github.com/angular/angular/commit/5b7432b))
* **core:** fix platform-browser-dynamic ([#18576](https://github.com/angular/angular/issues/18576)) ([f0a5501](https://github.com/angular/angular/commit/f0a5501))
* **core:** forbid destroyed views to be inserted or moved in VC ([#18568](https://github.com/angular/angular/issues/18568)) ([e54bd59](https://github.com/angular/angular/commit/e54bd59)), closes [#17780](https://github.com/angular/angular/issues/17780)
### Features
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
### Performance Improvements
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
### BREAKING CHANGES
* `platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Component` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
<a name="5.0.0-beta.2"></a>
# [5.0.0-beta.2](https://github.com/angular/angular/compare/5.0.0-beta.1...5.0.0-beta.2) (2017-08-02)
### Bug Fixes
* **compiler:** do not consider arguments when determining recursion ([e64b54b](https://github.com/angular/angular/commit/e64b54b))
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([381471d](https://github.com/angular/angular/commit/381471d)), closes [#18314](https://github.com/angular/angular/issues/18314)
### Features
* **forms:** add options arg to abstract controls ([ebef5e6](https://github.com/angular/angular/commit/ebef5e6))
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
<a name="4.3.3"></a>
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
### Bug Fixes
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
<a name="5.0.0-beta.1"></a>
# [5.0.0-beta.1](https://github.com/angular/angular/compare/5.0.0-beta.0...5.0.0-beta.1) (2017-07-27)
### Bug Fixes
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([fd0cc01](https://github.com/angular/angular/commit/fd0cc01))
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([38ec05f](https://github.com/angular/angular/commit/38ec05f)), closes [#17344](https://github.com/angular/angular/issues/17344)
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a3a5429](https://github.com/angular/angular/commit/a3a5429)), closes [#17799](https://github.com/angular/angular/issues/17799)
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([7ae7573](https://github.com/angular/angular/commit/7ae7573)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([a094769](https://github.com/angular/angular/commit/a094769)), closes [#17050](https://github.com/angular/angular/issues/17050)
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([086f4aa](https://github.com/angular/angular/commit/086f4aa)), closes [#15670](https://github.com/angular/angular/issues/15670)
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([82923a3](https://github.com/angular/angular/commit/82923a3)), closes [#14596](https://github.com/angular/angular/issues/14596)
* **upgrade:** ensure downgraded components are created in the Angular zone ([#18209](https://github.com/angular/angular/issues/18209)) ([43c33d5](https://github.com/angular/angular/commit/43c33d5))
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([d31dc7b](https://github.com/angular/angular/commit/d31dc7b))
### Features
* **compiler:** add representation of placeholders to xliff & xmb ([b3085e9](https://github.com/angular/angular/commit/b3085e9)), closes [#17345](https://github.com/angular/angular/issues/17345)
### Performance Improvements
* latest tsickle to tree shake: abstract class methods & interfaces ([#18236](https://github.com/angular/angular/issues/18236)) ([b7a6f52](https://github.com/angular/angular/commit/b7a6f52))
* **core:** use native addEventListener for faster rendering. ([#18107](https://github.com/angular/angular/issues/18107)) ([6279e50](https://github.com/angular/angular/commit/6279e50))
<a name="4.3.2"></a>
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
@ -33,6 +138,35 @@
<a name="5.0.0-beta.0"></a>
# [5.0.0-beta.0](https://github.com/angular/angular/compare/4.3.0...5.0.0-beta.0) (2017-07-19)
### Bug Fixes
* **animations:** always camelcase style property names that contain auto styles ([d22f8f5](https://github.com/angular/angular/commit/d22f8f5)), closes [#17938](https://github.com/angular/angular/issues/17938)
* **animations:** capture cancelled animation styles within grouped animations ([23146c9](https://github.com/angular/angular/commit/23146c9)), closes [#17170](https://github.com/angular/angular/issues/17170)
* **animations:** do not crash animations if a nested component fires CD during CD ([5db6f38](https://github.com/angular/angular/commit/5db6f38)), closes [#18193](https://github.com/angular/angular/issues/18193)
* **animations:** make sure @.disabled works in non-animation components ([5344be5](https://github.com/angular/angular/commit/5344be5))
* **common:** send flushed body as error instead of null ([5c62e30](https://github.com/angular/angular/commit/5c62e30)), closes [#18181](https://github.com/angular/angular/issues/18181)
* **compiler:** ensure jit external id arguments names are unique ([95635c1](https://github.com/angular/angular/commit/95635c1))
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([65c9e13](https://github.com/angular/angular/commit/65c9e13)), closes [#15754](https://github.com/angular/angular/issues/15754)
* **platform-server:** provide XhrFactory for HttpClient ([8076482](https://github.com/angular/angular/commit/8076482))
* **router:** canDeactivate guards should run from bottom to top ([e20cfe1](https://github.com/angular/angular/commit/e20cfe1)), closes [#15657](https://github.com/angular/angular/issues/15657)
* **router:** should navigate to the same url when config changes ([eb6fb5f](https://github.com/angular/angular/commit/eb6fb5f)), closes [#15535](https://github.com/angular/angular/issues/15535)
* **router:** should run resolvers for the same route concurrently ([ad3029e](https://github.com/angular/angular/commit/ad3029e)), closes [#14279](https://github.com/angular/angular/issues/14279)
* **router:** terminal route in custom matcher ([b399cb2](https://github.com/angular/angular/commit/b399cb2))
* **upgrade:** allow accessing AngularJS injector from downgraded module ([a5205c6](https://github.com/angular/angular/commit/a5205c6))
### Features
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
<a name="4.3.1"></a>
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)

View File

@ -0,0 +1,176 @@
# Angular Language Service
The Angular Language Service is a way to get completions, errors,
hints, and navigation inside your Angular templates whether they
are external in an HTML file or embedded in annotations/decorators
in a string. The Angular Language Service autodetects that you are
opening an Angular file, reads your `tsconfig.json` file, finds all the
templates you have in your application, and then provides language
services for any templates that you open.
## Autocompletion
Autocompletion can speed up your development time by providing you with
contextual possibilities and hints as you type. This example shows
autocomplete in an interpolation. As you type it out,
you can hit tab to complete.
<figure>
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
</figure>
There are also completions within
elements. Any elements you have as a component selector will
show up in the completion list.
## Error checking
The Angular Language Service can also forewarn you of mistakes in your code.
In this example, Angular doesn't know what `orders` is or where it comes from.
<figure>
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
</figure>
## Navigation
Navigation allows you to hover to
see where a component, directive, module, etc. is from and then
click and press F12 to go directly to its definition.
<figure>
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
</figure>
## Angular Language Service in your editor
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
[WebStorm](https://www.jetbrains.com/webstorm).
### Visual Studio Code
In Visual Studio Code, install Angular Language Service from the store,
which is accessible from the bottom icon on the left menu pane.
You can also use the VS Quick Open (⌘+P) to search for the extension. When you've opened it,
enter the following command:
```sh
ext install ng-template
```
Then click the install button to install the Angular Language Service.
### WebStorm
In webstorm, you have to install the language service as a dev dependency.
When Angular sees this dev dependency, it provides the
language service inside of WebStorm. Webstorm then gives you
colorization inside the template and autocomplete in addition to the Angular Language Service.
Here's the dev dependency
you need to have in `package.json`:
```json
devDependencies {
"@angular/language-service": "^4.0.0"
}
```
Then in the terminal window at the root of your project,
install the `devDependencies` with `npm` or `yarn`:
```sh
npm install
```
*OR*
```sh
yarn
```
*OR*
```sh
yarn install
```
### Sublime Text
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
Install the latest version of typescript in a local `node_modules` directory:
```sh
npm install --save-dev typescript
```
Then install the Angular Language Service in the same location:
```sh
npm install --save-dev @angular/language-service
```
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
```json
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
```
## Installing in your project
You can also install Angular Language Service in your project with the
following `npm` command:
```sh
npm install --save-dev @angular/language-service
```
Additionally, add the following to the `"compilerOptions"` section of
your project's `tsconfig.json`.
```json
"plugins": [
{"name": "@angular/language-service"}
]
```
Note that this only provides diagnostics and completions in `.ts`
files. You need a custom sublime plugin (or modifications to the current plugin)
for completions in HTML files.
## How the Language Service works
When you use an editor with a language service, there's an
editor process which starts a separate language process/service
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
Any time you type inside of the editor, it sends information to the other process to
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
context, it can then determine what the children can be.
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
For more in-depth information, see the
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
<hr>
## More on Information
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
Service from [ng-conf](https://www.ng-conf.org/) 2017.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

View File

@ -1,10 +1,10 @@
<!--FULL HEADER BLOCK-->
<!-- FULL HEADER BLOCK -->
<header>
<!--BACKGROUND IMAGE-->
<!-- BACKGROUND IMAGE -->
<div class="background-sky hero"></div>
<!--INTRO SECTION -->
<!-- INTRO SECTION -->
<section id="intro">
<!-- LOGO -->
@ -12,31 +12,33 @@
<img src="assets/images/logos/angular/angular.svg"/>
</div>
<!-- CONTAINER -->
<!-- CONTAINER -->
<div class="homepage-container">
<!-- container content starts -->
<div class="hero-headline no-toc">One framework.<br>Mobile &amp; desktop.</div>
<a class="button hero-cta" href="guide/quickstart">Get Started</a>
</div><!-- CONTAINER END -->
</section>
</div>
</section>
</header>
<!-- MAIN CONTENT -->
<article>
<h1 class="no-toc" style="display: none"></h1>
<div class="home-rows">
<!--Announcement Bar-->
<!-- Announcement Bar -->
<div class="homepage-container">
<div class="announcement-bar">
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
<p>Join us at our newest event, October 2017</p>
<a class="button" href="https://angularmix.com/">Learn More</a>
</div>
</div>
</div>
<!-- Group 1-->
<!-- Group 1 -->
<div layout="row" layout-xs="column" class="home-row homepage-container">
<div class="promo-img-container promo-1">
<div>
@ -53,7 +55,8 @@
</div>
</div>
<hr>
<!-- Group 2-->
<!-- Group 2 -->
<div layout="row" layout-xs="column" class="home-row">
<div class="text-container">
<div class="text-block">
@ -71,7 +74,7 @@
</div>
<hr>
<!-- Group 3-->
<!-- Group 3 -->
<div layout="row" layout-xs="column" class="home-row">
<div class="promo-img-container promo-3">
<div><img src="generated/images/marketing/home/joyful-development.svg" alt="IDE example"></div>
@ -88,9 +91,8 @@
</div>
<hr>
<!-- Group 4-->
<!-- Group 4 -->
<div layout="row" layout-xs="column" class="home-row">
<div class="text-container">
<div class="text-block l-pad-top-2">
<div class="text-headline">Loved by Millions</div>
@ -105,20 +107,19 @@
</div>
</div>
<!-- CTA CARDS -->
<div layout="row" layout-xs="column" class="home-row">
<a href="guide/quickstart">
<div class="card">
<!-- CTA CARDS -->
<div layout="row" layout-xs="column" class="home-row">
<a href="guide/quickstart">
<div class="card">
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
<div class="card-text-container">
<div class="text-headline">Get Started</div>
<p>Start building your Angular application.</p>
</div>
</div>
</a>
</div>
</div>
</a>
</div>
</div> <!-- end of home rows -->
</div><!-- end of home rows -->
</article>

View File

@ -288,6 +288,11 @@
"title": "Internationalization (i18n)",
"tooltip": "Translate the app's template text into multiple languages."
},
{
"url": "guide/language-service",
"title": "Language Service",
"tooltip": "Use Angular Language Service to speed up dev time."
},
{
"url": "guide/security",
"title": "Security",

View File

@ -41,12 +41,9 @@ const ANGULAR_PACKAGES = [
'platform-browser-dynamic',
'platform-server',
'router',
'tsc-wrapped',
'upgrade',
];
const ANGULAR_TOOLS_PACKAGES_PATH = path.resolve(ANGULAR_DIST_PATH, 'tools', '@angular');
const ANGULAR_TOOLS_PACKAGES = [
'tsc-wrapped'
];
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
@ -63,7 +60,6 @@ class ExampleBoilerPlate {
// Replace the Angular packages with those from the dist folder, if necessary
if (useLocal) {
ANGULAR_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_PACKAGES_PATH, packageName));
ANGULAR_TOOLS_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_TOOLS_PACKAGES_PATH, packageName));
}
// Get all the examples folders, indicated by those that contain a `example-config.json` file
@ -137,4 +133,4 @@ module.exports = new ExampleBoilerPlate();
// If this file was run directly then run the main function,
if (require.main === module) {
module.exports.main();
}
}

View File

@ -32,7 +32,7 @@ describe('example-boilerplate tool', () => {
// for example
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/tools/@angular'), 'tsc-wrapped');
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'tsc-wrapped');
});
it('should process all the example folders', () => {

View File

@ -34,5 +34,5 @@ function getText(h1) {
(node.properties.ariaHidden === 'true' || node.properties['aria-hidden'] === 'true')
));
return toString(cleaned);
}
return cleaned ? toString(cleaned) : '';
}

View File

@ -69,4 +69,14 @@ describe('h1Checker postprocessor', () => {
processor.$process([doc]);
expect(doc.vFile.title).toEqual('What is Angular?');
});
});
it('should not break if the h1 is empty (except for an aria-hidden anchor)', () => {
const doc = {
docType: 'a',
renderedContent: `
<h1><a aria-hidden="true"></a></h1>
`
};
expect(() => processor.$process([doc])).not.toThrow();
});
});

View File

@ -333,7 +333,7 @@ echo "====== BUILDING: Version ${VERSION}"
N="
"
TSC=`pwd`/node_modules/.bin/tsc
NGC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
NGC="node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main"
MAP_SOURCES="node `pwd`/scripts/build/map_sources.js "
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
TSCONFIG=./tools/tsconfig.json
@ -345,8 +345,6 @@ if [[ ${BUILD_TOOLS} == true ]]; then
rm -rf ./dist/tools/
mkdir -p ./dist/tools/
$(npm bin)/tsc -p ${TSCONFIG}
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
travisFoldEnd "build tools"
fi
@ -398,11 +396,11 @@ if [[ ${BUILD_ALL} == true && ${TYPECHECK_ALL} == true ]]; then
TSCONFIG="packages/tsconfig.json"
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
$NGC -p ${TSCONFIG}
$TSC -p ${TSCONFIG}
travisFoldEnd "tsc -p ${TSCONFIG}"
TSCONFIG="modules/tsconfig.json"
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
$NGC -p ${TSCONFIG}
$TSC -p ${TSCONFIG}
travisFoldEnd "tsc -p ${TSCONFIG}"
fi
@ -414,6 +412,20 @@ if [[ ${BUILD_ALL} == true ]]; then
fi
fi
if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json ====="
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json ====="
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json
cp ./packages/tsc-wrapped/package.json ./dist/packages-dist/tsc-wrapped
cp ./packages/tsc-wrapped/README.md ./dist/packages-dist/tsc-wrapped
(
cd dist/packages-dist/tsc-wrapped
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
)
fi
for PACKAGE in ${PACKAGES[@]}
do
travisFoldStart "build package: ${PACKAGE}" "no-xtrace"

View File

@ -10,7 +10,7 @@
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"google-closure-compiler": "20170409.0.0",
"rxjs": "5.3.1",
"typescript": "2.1.6",

View File

@ -12,7 +12,7 @@
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"@types/minimist": "^1.2.0",
"@types/node": "^7.0.5",
"minimist": "^1.2.0",

View File

@ -15,7 +15,7 @@
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "2.5.41",
"rxjs": "file:../../node_modules/rxjs",

View File

@ -15,7 +15,7 @@
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "2.5.41",
"rxjs": "file:../../node_modules/rxjs",

View File

@ -15,7 +15,7 @@
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "2.5.41",
"rxjs": "file:../../node_modules/rxjs",

View File

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

View File

@ -11,7 +11,7 @@ const yargs = require('yargs');
const nodeUuid = require('node-uuid');
import * as fs from 'fs-extra';
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, Provider} from '@angular/benchpress';
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, StaticProvider} from '@angular/benchpress';
import {readCommandLine as readE2eCommandLine, openBrowser} from './e2e_util';
let cmdArgs: {'sample-size': number, 'force-gc': boolean, 'dryrun': boolean, 'bundles': boolean};
@ -59,7 +59,7 @@ function createBenchpressRunner(): Runner {
}
const resultsFolder = './dist/benchmark_results';
fs.ensureDirSync(resultsFolder);
const providers: Provider[] = [
const providers: StaticProvider[] = [
SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
{provide: Options.FORCE_GC, useValue: cmdArgs['force-gc']},
{provide: Options.DEFAULT_DESCRIPTION, useValue: {'runId': runId}}, JsonFileReporter.PROVIDERS,

View File

@ -1,6 +1,8 @@
# How to run the examples locally
```
$ cp -r ./modules/playground ./dist/all/
$ ./node_modules/.bin/tsc -p modules --emitDecoratorMetadata -w
$ gulp serve
$ open http://localhost:8000/all/playground/src/hello_world/index.html?bundles=false
```

View File

@ -13,8 +13,8 @@
"selenium-webdriver": ["../node_modules/@types/selenium-webdriver/index.d.ts"],
"rxjs/*": ["../node_modules/rxjs/*"],
"@angular/*": ["../dist/all/@angular/*"],
"@angular/tsc-wrapped": ["../dist/tools/@angular/tsc-wrapped"],
"@angular/tsc-wrapped/*": ["../dist/tools/@angular/tsc-wrapped/*"]
"@angular/tsc-wrapped": ["../dist/packages-dist/tsc-wrapped"],
"@angular/tsc-wrapped/*": ["../dist/packages-dist/tsc-wrapped/*"]
},
"rootDir": ".",
"inlineSourceMap": true,

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "4.3.0-beta.1",
"version": "5.0.0-beta.0",
"dependencies": {
"@types/angularjs": {
"version": "1.5.13-alpha"
@ -4841,7 +4841,7 @@
}
},
"tsickle": {
"version": "0.21.6"
"version": "0.23.4"
},
"tslib": {
"version": "1.7.1"
@ -4901,7 +4901,7 @@
"version": "0.0.6"
},
"typescript": {
"version": "2.3.2"
"version": "2.3.4"
},
"ua-parser-js": {
"version": "0.7.10"

8
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "4.3.0-beta.1",
"version": "5.0.0-beta.0",
"dependencies": {
"@types/angularjs": {
"version": "1.5.13-alpha",
@ -7733,9 +7733,9 @@
}
},
"tsickle": {
"version": "0.21.6",
"from": "tsickle@0.21.6",
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.21.6.tgz"
"version": "0.23.4",
"from": "tsickle@0.23.4",
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.23.4.tgz"
},
"tslib": {
"version": "1.7.1",

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "4.3.4",
"version": "5.0.0-beta.3",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -93,10 +93,10 @@
"source-map-support": "^0.4.2",
"systemjs": "0.18.10",
"ts-api-guardian": "^0.2.2",
"tsickle": "^0.21.1",
"tsickle": "^0.23.4",
"tslint": "^4.1.1",
"tslint-eslint-rules": "^3.1.0",
"typescript": "^2.3.2",
"typescript": "^2.3.4",
"universal-analytics": "^0.3.9",
"vrsource-tslint-rules": "^4.0.0",
"webpack": "^1.12.6",

View File

@ -24,8 +24,14 @@ export function parseTransitionExpr(
function parseInnerTransitionStr(
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
if (eventStr[0] == ':') {
eventStr = parseAnimationAlias(eventStr, errors);
const result = parseAnimationAlias(eventStr, errors);
if (typeof result == 'function') {
expressions.push(result);
return;
}
eventStr = result as string;
}
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
if (match == null || match.length < 4) {
errors.push(`The provided transition expression "${eventStr}" is not supported`);
@ -43,12 +49,16 @@ function parseInnerTransitionStr(
}
}
function parseAnimationAlias(alias: string, errors: string[]): string {
function parseAnimationAlias(alias: string, errors: string[]): string|TransitionMatcherFn {
switch (alias) {
case ':enter':
return 'void => *';
case ':leave':
return '* => void';
case ':increment':
return (fromState: any, toState: any): boolean => parseFloat(toState) > parseFloat(fromState);
case ':decrement':
return (fromState: any, toState: any): boolean => parseFloat(toState) < parseFloat(fromState);
default:
errors.push(`The transition alias value "${alias}" is not supported`);
return '* => *';

View File

@ -509,7 +509,7 @@ export class TransitionAnimationEngine {
// this method is designed to be overridden by the code that uses this engine
public onRemovalComplete = (element: any, context: any) => {};
// tslint:disable-next-line
/** @internal */
_onRemovalComplete(element: any, context: any) { this.onRemovalComplete(element, context); }
constructor(public driver: AnimationDriver, private _normalizer: AnimationStyleNormalizer) {}

View File

@ -710,7 +710,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
* ])
* ```
*
* ### Transition Aliases (`:enter` and `:leave`)
* ### Using :enter and :leave
*
* Given that enter (insertion) and leave (removal) animations are so common, the `transition`
* function accepts both `:enter` and `:leave` values which are aliases for the `void => *` and `*
@ -720,12 +720,88 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
* transition(":enter", [
* style({ opacity: 0 }),
* animate(500, style({ opacity: 1 }))
* ])
* ]),
* transition(":leave", [
* animate(500, style({ opacity: 0 }))
* ])
* ```
*
* ### Using :increment and :decrement
* In addition to the :enter and :leave transition aliases, the :increment and :decrement aliases
* can be used to kick off a transition when a numeric value has increased or decreased in value.
*
* ```
* import {group, animate, query, transition, style, trigger} from '@angular/animations';
* import {Component} from '@angular/core';
*
* @Component({
* selector: 'banner-carousel-component',
* styles: [`
* .banner-container {
* position:relative;
* height:500px;
* overflow:hidden;
* }
* .banner-container > .banner {
* position:absolute;
* left:0;
* top:0;
* font-size:200px;
* line-height:500px;
* font-weight:bold;
* text-align:center;
* width:100%;
* }
* `],
* template: `
* <button (click)="previous()">Previous</button>
* <button (click)="next()">Next</button>
* <hr>
* <div [@bannerAnimation]="selectedIndex" class="banner-container">
* <div class="banner"> {{ banner }} </div>
* </div>
* `
* animations: [
* trigger('bannerAnimation', [
* transition(":increment", group([
* query(':enter', [
* style({ left: '100%' }),
* animate('0.5s ease-out', style('*'))
* ]),
* query(':leave', [
* animate('0.5s ease-out', style({ left: '-100%' }))
* ])
* ])),
* transition(":decrement", group([
* query(':enter', [
* style({ left: '-100%' }),
* animate('0.5s ease-out', style('*'))
* ]),
* query(':leave', [
* animate('0.5s ease-out', style({ left: '100%' }))
* ])
* ])),
* ])
* ]
* })
* class BannerCarouselComponent {
* allBanners: string[] = ['1', '2', '3', '4'];
* selectedIndex: number = 0;
*
* get banners() {
* return [this.allBanners[this.selectedIndex]];
* }
*
* previous() {
* this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
* }
*
* next() {
* this.selectedIndex = Math.min(this.selectedIndex + 1, this.allBanners.length - 1);
* }
* }
* ```
*
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
*
* @experimental Animation support is experimental.

View File

@ -9,7 +9,7 @@
// Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata';
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';
export {InjectionToken, Injector, Provider, ReflectiveInjector, StaticProvider} from '@angular/core';
export {Options} from './src/common_options';
export {MeasureValues} from './src/measure_values';
export {Metric} from './src/metric';

View File

@ -20,7 +20,14 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
export class PerflogMetric extends Metric {
static SET_TIMEOUT = new InjectionToken('PerflogMetric.setTimeout');
static PROVIDERS = [
PerflogMetric, {
{
provide: PerflogMetric,
deps: [
WebDriverExtension, PerflogMetric.SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC,
Options.CAPTURE_FRAMES, Options.RECEIVED_DATA, Options.REQUEST_COUNT
]
},
{
provide: PerflogMetric.SET_TIMEOUT,
useValue: (fn: Function, millis: number) => <any>setTimeout(fn, millis)
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable} from '@angular/core';
import {Inject, Injectable, StaticProvider} from '@angular/core';
import {Options} from '../common_options';
import {Metric} from '../metric';
@ -14,7 +14,8 @@ import {WebDriverAdapter} from '../web_driver_adapter';
@Injectable()
export class UserMetric extends Metric {
static PROVIDERS = [UserMetric];
static PROVIDERS =
<StaticProvider[]>[{provide: UserMetric, deps: [Options.USER_METRICS, WebDriverAdapter]}];
constructor(
@Inject(Options.USER_METRICS) private _userMetrics: {[key: string]: string},

View File

@ -22,7 +22,11 @@ export class ConsoleReporter extends Reporter {
static PRINT = new InjectionToken('ConsoleReporter.print');
static COLUMN_WIDTH = new InjectionToken('ConsoleReporter.columnWidth');
static PROVIDERS = [
ConsoleReporter, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18}, {
{
provide: ConsoleReporter,
deps: [ConsoleReporter.COLUMN_WIDTH, SampleDescription, ConsoleReporter.PRINT]
},
{provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18}, {
provide: ConsoleReporter.PRINT,
useValue: function(v: any) {
// tslint:disable-next-line:no-console

View File

@ -22,7 +22,13 @@ import {formatStats, sortedProps} from './util';
@Injectable()
export class JsonFileReporter extends Reporter {
static PATH = new InjectionToken('JsonFileReporter.path');
static PROVIDERS = [JsonFileReporter, {provide: JsonFileReporter.PATH, useValue: '.'}];
static PROVIDERS = [
{
provide: JsonFileReporter,
deps: [SampleDescription, JsonFileReporter.PATH, Options.WRITE_FILE, Options.NOW]
},
{provide: JsonFileReporter.PATH, useValue: '.'}
];
constructor(
private _description: SampleDescription, @Inject(JsonFileReporter.PATH) private _path: string,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider, ReflectiveInjector} from '@angular/core';
import {Injector, StaticProvider} from '@angular/core';
import {Options} from './common_options';
import {Metric} from './metric';
@ -34,17 +34,17 @@ import {IOsDriverExtension} from './webdriver/ios_driver_extension';
* It provides defaults, creates the injector and calls the sampler.
*/
export class Runner {
constructor(private _defaultProviders: Provider[] = []) {}
constructor(private _defaultProviders: StaticProvider[] = []) {}
sample({id, execute, prepare, microMetrics, providers, userMetrics}: {
id: string,
execute?: Function,
prepare?: Function,
microMetrics?: {[key: string]: string},
providers?: Provider[],
providers?: StaticProvider[],
userMetrics?: {[key: string]: string}
}): Promise<SampleState> {
const sampleProviders: Provider[] = [
const sampleProviders: StaticProvider[] = [
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
{provide: Options.EXECUTE, useValue: execute}
];
@ -61,7 +61,7 @@ export class Runner {
sampleProviders.push(providers);
}
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
const inj = Injector.create(sampleProviders);
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
return Promise
@ -75,7 +75,7 @@ export class Runner {
// Only WebDriverAdapter is reused.
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
// injectors are handled better.
const injector = ReflectiveInjector.resolveAndCreate([
const injector = Injector.create([
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
{provide: Options.USER_AGENT, useValue: userAgent},
{provide: WebDriverAdapter, useValue: adapter}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable} from '@angular/core';
import {Inject, Injectable, StaticProvider} from '@angular/core';
import {Options} from './common_options';
import {MeasureValues} from './measure_values';
@ -26,8 +26,12 @@ import {WebDriverAdapter} from './web_driver_adapter';
*/
@Injectable()
export class Sampler {
static PROVIDERS = [Sampler];
static PROVIDERS = <StaticProvider[]>[{
provide: Sampler,
deps: [
WebDriverAdapter, Metric, Reporter, Validator, Options.PREPARE, Options.EXECUTE, Options.NOW
]
}];
constructor(
private _driver: WebDriverAdapter, private _metric: Metric, private _reporter: Reporter,
private _validator: Validator, @Inject(Options.PREPARE) private _prepare: Function,

View File

@ -21,7 +21,11 @@ export class RegressionSlopeValidator extends Validator {
static SAMPLE_SIZE = new InjectionToken('RegressionSlopeValidator.sampleSize');
static METRIC = new InjectionToken('RegressionSlopeValidator.metric');
static PROVIDERS = [
RegressionSlopeValidator, {provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
{
provide: RegressionSlopeValidator,
deps: [RegressionSlopeValidator.SAMPLE_SIZE, RegressionSlopeValidator.METRIC]
},
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
{provide: RegressionSlopeValidator.METRIC, useValue: 'scriptTime'}
];

View File

@ -17,7 +17,10 @@ import {Validator} from '../validator';
@Injectable()
export class SizeValidator extends Validator {
static SAMPLE_SIZE = new InjectionToken('SizeValidator.sampleSize');
static PROVIDERS = [SizeValidator, {provide: SizeValidator.SAMPLE_SIZE, useValue: 10}];
static PROVIDERS = [
{provide: SizeValidator, deps: [SizeValidator.SAMPLE_SIZE]},
{provide: SizeValidator.SAMPLE_SIZE, useValue: 10}
];
constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable} from '@angular/core';
import {Inject, Injectable, StaticProvider} from '@angular/core';
import {Options} from '../common_options';
import {WebDriverAdapter} from '../web_driver_adapter';
@ -21,7 +21,10 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
*/
@Injectable()
export class ChromeDriverExtension extends WebDriverExtension {
static PROVIDERS = [ChromeDriverExtension];
static PROVIDERS = <StaticProvider>[{
provide: ChromeDriverExtension,
deps: [WebDriverAdapter, Options.USER_AGENT]
}];
private _majorChromeVersion: number;
private _firstRun = true;

View File

@ -13,7 +13,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
@Injectable()
export class FirefoxDriverExtension extends WebDriverExtension {
static PROVIDERS = [FirefoxDriverExtension];
static PROVIDERS = [{provide: FirefoxDriverExtension, deps: [WebDriverAdapter]}];
private _profilerStarted: boolean;

View File

@ -13,7 +13,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
@Injectable()
export class IOsDriverExtension extends WebDriverExtension {
static PROVIDERS = [IOsDriverExtension];
static PROVIDERS = [{provide: IOsDriverExtension, deps: [WebDriverAdapter]}];
constructor(private _driver: WebDriverAdapter) { super(); }

View File

@ -6,15 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticProvider} from '@angular/core';
import {WebDriverAdapter} from '../web_driver_adapter';
/**
* Adapter for the selenium-webdriver.
*/
export class SeleniumWebDriverAdapter extends WebDriverAdapter {
static PROTRACTOR_PROVIDERS = [{
static PROTRACTOR_PROVIDERS = <StaticProvider[]>[{
provide: WebDriverAdapter,
useFactory: () => new SeleniumWebDriverAdapter((<any>global).browser)
useFactory: () => new SeleniumWebDriverAdapter((<any>global).browser),
deps: []
}];
constructor(private _driver: any) { super(); }

View File

@ -7,12 +7,13 @@
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
import {Injector, Metric, MultiMetric} from '../../index';
export function main() {
function createMetric(ids: any[]) {
const m = ReflectiveInjector
.resolveAndCreate([
const m = Injector
.create([
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
MultiMetric.provideWith(ids)
])

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider} from '@angular/core';
import {StaticProvider} from '@angular/core';
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
import {Injector, Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, WebDriverExtension} from '../../index';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -33,7 +33,7 @@ export function main() {
if (!microMetrics) {
microMetrics = {};
}
const providers: Provider[] = [
const providers: StaticProvider[] = [
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
provide: PerflogMetric.SET_TIMEOUT,
@ -59,7 +59,7 @@ export function main() {
if (requestCount != null) {
providers.push({provide: Options.REQUEST_COUNT, useValue: requestCount});
}
return ReflectiveInjector.resolveAndCreate(providers).get(PerflogMetric);
return Injector.create(providers).get(PerflogMetric);
}
describe('perflog metric', () => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider, ReflectiveInjector} from '@angular/core';
import {Injector, StaticProvider} from '@angular/core';
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
@ -25,12 +25,12 @@ export function main() {
userMetrics = {};
}
wdAdapter = new MockDriverAdapter();
const providers: Provider[] = [
const providers: StaticProvider[] = [
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
{provide: Options.USER_METRICS, useValue: userMetrics},
{provide: WebDriverAdapter, useValue: wdAdapter}
];
return ReflectiveInjector.resolveAndCreate(providers).get(UserMetric);
return Injector.create(providers).get(UserMetric);
}
describe('user metric', () => {

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider} from '@angular/core';
import {StaticProvider} from '@angular/core';
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../../index';
export function main() {
describe('console reporter', () => {
@ -30,7 +30,7 @@ export function main() {
if (sampleId == null) {
sampleId = 'null';
}
const providers: Provider[] = [
const providers: StaticProvider[] = [
ConsoleReporter.PROVIDERS, {
provide: SampleDescription,
useValue: new SampleDescription(sampleId, descriptions, metrics !)
@ -40,7 +40,7 @@ export function main() {
if (columnWidth != null) {
providers.push({provide: ConsoleReporter.COLUMN_WIDTH, useValue: columnWidth});
}
reporter = ReflectiveInjector.resolveAndCreate(providers).get(ConsoleReporter);
reporter = Injector.create(providers).get(ConsoleReporter);
}
it('should print the sample id, description and table header', () => {

View File

@ -8,7 +8,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} from '../../index';
export function main() {
describe('file reporter', () => {
@ -34,7 +34,7 @@ export function main() {
}
}
];
return ReflectiveInjector.resolveAndCreate(providers).get(JsonFileReporter);
return Injector.create(providers).get(JsonFileReporter);
}
it('should write all data into a file',

View File

@ -8,12 +8,12 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
import {Injector, MeasureValues, MultiReporter, Reporter} from '../../index';
export function main() {
function createReporters(ids: any[]) {
const r = ReflectiveInjector
.resolveAndCreate([
const r = Injector
.create([
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
MultiReporter.provideWith(ids)
])

View File

@ -8,11 +8,11 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
import {Injector, Metric, Options, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
export function main() {
describe('runner', () => {
let injector: ReflectiveInjector;
let injector: Injector;
let runner: Runner;
function createRunner(defaultProviders?: any[]): Runner {
@ -22,7 +22,7 @@ export function main() {
runner = new Runner([
defaultProviders, {
provide: Sampler,
useFactory: (_injector: ReflectiveInjector) => {
useFactory: (_injector: Injector) => {
injector = _injector;
return new MockSampler();
},

View File

@ -8,7 +8,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
export function main() {
const EMPTY_EXECUTE = () => {};
@ -44,7 +44,7 @@ export function main() {
providers.push({provide: Options.PREPARE, useValue: prepare});
}
sampler = ReflectiveInjector.resolveAndCreate(providers).get(Sampler);
sampler = Injector.create(providers).get(Sampler);
}
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',

View File

@ -8,15 +8,15 @@
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
import {Injector, MeasureValues, RegressionSlopeValidator} from '../../index';
export function main() {
describe('regression slope validator', () => {
let validator: RegressionSlopeValidator;
function createValidator({size, metric}: {size: number, metric: string}) {
validator = ReflectiveInjector
.resolveAndCreate([
validator = Injector
.create([
RegressionSlopeValidator.PROVIDERS,
{provide: RegressionSlopeValidator.METRIC, useValue: metric},
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: size}

View File

@ -8,7 +8,7 @@
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
import {Injector, MeasureValues, SizeValidator} from '../../index';
export function main() {
describe('size validator', () => {
@ -16,8 +16,8 @@ export function main() {
function createValidator(size: number) {
validator =
ReflectiveInjector
.resolveAndCreate(
Injector
.create(
[SizeValidator.PROVIDERS, {provide: SizeValidator.SAMPLE_SIZE, useValue: size}])
.get(SizeValidator);
}

View File

@ -8,14 +8,14 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
import {Injector, Options, WebDriverExtension} from '../index';
export function main() {
function createExtension(ids: any[], caps: any) {
return new Promise<any>((res, rej) => {
try {
res(ReflectiveInjector
.resolveAndCreate([
res(Injector
.create([
ids.map((id) => ({provide: id, useValue: new MockExtension(id)})),
{provide: Options.CAPABILITIES, useValue: caps},
WebDriverExtension.provideFirstSupported(ids)

View File

@ -8,7 +8,7 @@
import {AsyncTestCompleter, describe, expect, iit, inject, it} from '@angular/core/testing/src/testing_internal';
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {ChromeDriverExtension, Injector, Options, WebDriverAdapter, WebDriverExtension} from '../../index';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -41,8 +41,8 @@ export function main() {
userAgent = CHROME45_USER_AGENT;
}
log = [];
extension = ReflectiveInjector
.resolveAndCreate([
extension = Injector
.create([
ChromeDriverExtension.PROVIDERS, {
provide: WebDriverAdapter,
useValue: new MockDriverAdapter(log, perfRecords, messageMethod)

View File

@ -8,7 +8,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {IOsDriverExtension, Injector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -24,8 +24,8 @@ export function main() {
}
log = [];
extension =
ReflectiveInjector
.resolveAndCreate([
Injector
.create([
IOsDriverExtension.PROVIDERS,
{provide: WebDriverAdapter, useValue: new MockDriverAdapter(log, perfRecords)}
])

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, Provider, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, SimpleChanges, StaticProvider, Type, ViewContainerRef} from '@angular/core';
/**
* Instantiates a single {@link Component} type and inserts its Host View into current View.

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
/**
* @ngModule CommonModule
@ -49,13 +49,58 @@ export class NgTemplateOutlet implements OnChanges {
set ngOutletContext(context: Object) { this.ngTemplateOutletContext = context; }
ngOnChanges(changes: SimpleChanges) {
if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
}
const recreateView = this._shouldRecreateView(changes);
if (this.ngTemplateOutlet) {
this._viewRef = this._viewContainerRef.createEmbeddedView(
this.ngTemplateOutlet, this.ngTemplateOutletContext);
if (recreateView) {
if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
}
if (this.ngTemplateOutlet) {
this._viewRef = this._viewContainerRef.createEmbeddedView(
this.ngTemplateOutlet, this.ngTemplateOutletContext);
}
} else {
if (this._viewRef && this.ngTemplateOutletContext) {
this._updateExistingContext(this.ngTemplateOutletContext);
}
}
}
/**
* We need to re-create existing embedded view if:
* - templateRef has changed
* - context has changes
*
* To mark context object as changed when the corresponding object
* shape changes (new properties are added or existing properties are removed).
* In other words we consider context with the same properties as "the same" even
* if object reference changes (see https://github.com/angular/angular/issues/13407).
*/
private _shouldRecreateView(changes: SimpleChanges): boolean {
const ctxChange = changes['ngTemplateOutletContext'];
return !!changes['ngTemplateOutlet'] || (ctxChange && this._hasContextShapeChanged(ctxChange));
}
private _hasContextShapeChanged(ctxChange: SimpleChange): boolean {
const prevCtxKeys = Object.keys(ctxChange.previousValue || {});
const currCtxKeys = Object.keys(ctxChange.currentValue || {});
if (prevCtxKeys.length === currCtxKeys.length) {
for (let propName of currCtxKeys) {
if (prevCtxKeys.indexOf(propName) === -1) {
return true;
}
}
return false;
} else {
return true;
}
}
private _updateExistingContext(ctx: Object): void {
for (let propName of Object.keys(ctx)) {
(<any>this._viewRef.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
}
}
}

View File

@ -8,7 +8,7 @@
import {CommonModule} from '@angular/common';
import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet';
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, Provider, QueryList, ReflectiveInjector, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, StaticProvider, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {TestBed, async, fakeAsync} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -96,7 +96,7 @@ export function main() {
const uniqueValue = {};
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.componentInstance.injector = ReflectiveInjector.resolveAndCreate(
fixture.componentInstance.injector = Injector.create(
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
fixture.detectChanges();

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Component, ContentChildren, Directive, NO_ERRORS_SCHEMA, QueryList, TemplateRef} from '@angular/core';
import {Component, ContentChildren, Directive, Injectable, NO_ERRORS_SCHEMA, OnDestroy, QueryList, TemplateRef} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -26,11 +26,9 @@ export function main() {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestComponent,
CaptureTplRefs,
],
declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt],
imports: [CommonModule],
providers: [DestroyedSpyService]
});
});
@ -125,9 +123,105 @@ export function main() {
fixture.componentInstance.context = {shawshank: 'was here'};
detectChangesAndExpectText('was here');
}));
it('should update but not destroy embedded view when context values change', () => {
const template =
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
fixture = createTestComponent(template);
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
detectChangesAndExpectText('Content to destroy:bar');
expect(spyService.destroyed).toBeFalsy();
fixture.componentInstance.value = 'baz';
detectChangesAndExpectText('Content to destroy:baz');
expect(spyService.destroyed).toBeFalsy();
});
it('should recreate embedded view when context shape changes', () => {
const template =
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="context"></ng-template>`;
fixture = createTestComponent(template);
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
detectChangesAndExpectText('Content to destroy:bar');
expect(spyService.destroyed).toBeFalsy();
fixture.componentInstance.context = {foo: 'baz', other: true};
detectChangesAndExpectText('Content to destroy:baz');
expect(spyService.destroyed).toBeTruthy();
});
it('should destroy embedded view when context value changes and templateRef becomes undefined',
() => {
const template =
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
fixture = createTestComponent(template);
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
detectChangesAndExpectText('Content to destroy:bar');
expect(spyService.destroyed).toBeFalsy();
fixture.componentInstance.value = 'baz';
detectChangesAndExpectText('');
expect(spyService.destroyed).toBeTruthy();
});
it('should not try to update null / undefined context when context changes but template stays the same',
() => {
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
fixture.componentInstance.value = 'baz';
detectChangesAndExpectText('');
});
it('should not try to update null / undefined context when template changes', () => {
const template = `<ng-template let-foo="foo" #tpl1>{{foo}}</ng-template>` +
`<ng-template let-foo="foo" #tpl2>{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl1 : tpl2" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
fixture.componentInstance.value = 'baz';
detectChangesAndExpectText('');
});
it('should not try to update context on undefined view', () => {
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
`<ng-template [ngTemplateOutlet]="value === 'bar' ? null : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
fixture.componentInstance.value = 'baz';
detectChangesAndExpectText('');
});
});
}
@Injectable()
class DestroyedSpyService {
destroyed = false;
}
@Component({selector: 'destroyable-cmpt', template: 'Content to destroy'})
class DestroyableCmpt implements OnDestroy {
constructor(private _spyService: DestroyedSpyService) {}
ngOnDestroy(): void { this._spyService.destroyed = true; }
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
@ -137,6 +231,7 @@ class CaptureTplRefs {
class TestComponent {
currentTplRef: TemplateRef<any>;
context: any = {foo: 'bar'};
value = 'bar';
}
function createTestComponent(template: string): ComponentFixture<TestComponent> {

View File

@ -0,0 +1,17 @@
package(default_visibility=["//visibility:public"])
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
ts_library(
name = "compiler-cli",
srcs = glob(["**/*.ts"], exclude=[
"integrationtest/**",
"test/**",
]),
module_name = "@angular/compiler-cli",
deps = [
"//packages/compiler",
"//packages/core",
"//packages/tsc-wrapped",
],
tsconfig = ":tsconfig-build.json",
)

View File

@ -121,9 +121,6 @@ At a high level, this program
# Build Angular and the compiler
./build.sh
# Copy over the package so we can test the compiler tests
$ cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped
# Run the test once
# (First edit the LINKABLE_PKGS to use npm link instead of npm install)
$ ./scripts/ci/offline_compiler_test.sh

View File

@ -20,7 +20,7 @@ export {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Sp
export * from './src/transformers/api';
export * from './src/transformers/entry_points';
export {main as ngc} from './src/ngc';
export {performCompilation} from './src/perform-compile';
// TODO(hansl): moving to Angular 4 need to update this API.
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api';

View File

@ -9,7 +9,7 @@
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"@angular/tsc-wrapped": "4.3.4",
"@angular/tsc-wrapped": "5.0.0-beta.3",
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0"
},

View File

@ -100,8 +100,8 @@ export class CodeGenerator {
}
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
translations: transContent,
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale, missingTranslation,
i18nFormat: cliOptions.i18nFormat || undefined,
locale: cliOptions.locale || undefined, missingTranslation,
enableLegacyTemplate: options.enableLegacyTemplate !== false,
enableSummariesForJit: options.enableSummariesForJit !== false,
});

View File

@ -25,8 +25,9 @@ export interface CompilerHostContext extends ts.ModuleResolutionHost {
assumeFileExists(fileName: string): void;
}
export interface MetadataProvider { getMetadata(source: ts.SourceFile): ModuleMetadata|undefined; }
export class CompilerHost implements AotCompilerHost {
protected metadataCollector = new MetadataCollector();
private isGenDirChildOfRootDir: boolean;
protected basePath: string;
private genDir: string;
@ -39,7 +40,8 @@ export class CompilerHost implements AotCompilerHost {
constructor(
protected program: ts.Program, protected options: AngularCompilerOptions,
protected context: CompilerHostContext, collectorOptions?: CollectorOptions) {
protected context: CompilerHostContext, collectorOptions?: CollectorOptions,
protected metadataProvider: MetadataProvider = new MetadataCollector()) {
// normalize the path so that it never ends with '/'.
this.basePath = path.normalize(path.join(this.options.basePath !, '.')).replace(/\\/g, '/');
this.genDir = path.normalize(path.join(this.options.genDir !, '.')).replace(/\\/g, '/');
@ -206,7 +208,7 @@ export class CompilerHost implements AotCompilerHost {
}
const sf = this.getSourceFile(filePath);
const metadata = this.metadataCollector.getMetadata(sf);
const metadata = this.metadataProvider.getMetadata(sf);
return metadata ? [metadata] : [];
}
@ -245,7 +247,7 @@ export class CompilerHost implements AotCompilerHost {
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
}
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
const exports = this.metadataProvider.getMetadata(this.getSourceFile(dtsFilePath));
if (exports) {
for (let prop in exports.metadata) {
if (!v3Metadata.metadata[prop]) {

View File

@ -10,179 +10,14 @@
import 'reflect-metadata';
import {isSyntaxError, syntaxError} from '@angular/compiler';
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import * as api from './transformers/api';
import * as ng from './transformers/entry_points';
const TS_EXT = /\.ts$/;
import {performCompilation, readConfiguration, throwOnDiagnostics} from './perform-compile';
type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
}
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
if (diags && diags.length) {
if (isTsDiagnostics(diags)) {
return ts.formatDiagnostics(diags, {
getCurrentDirectory: () => cwd,
getCanonicalFileName: fileName => fileName,
getNewLine: () => ts.sys.newLine
});
} else {
return diags
.map(d => {
let res = api.DiagnosticCategory[d.category];
if (d.span) {
res +=
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
}
if (d.span && d.span.details) {
res += `: ${d.span.details}, ${d.message}\n`;
} else {
res += `: ${d.message}\n`;
}
return res;
})
.join();
}
} else
return '';
}
function check(cwd: string, ...args: Diagnostics[]) {
if (args.some(diags => !!(diags && diags[0]))) {
throw syntaxError(args.map(diags => {
if (diags && diags[0]) {
return formatDiagnostics(cwd, diags);
}
})
.filter(message => !!message)
.join(''));
}
}
function syntheticError(message: string): ts.Diagnostic {
return {
file: null as any as ts.SourceFile,
start: 0,
length: 0,
messageText: message,
category: ts.DiagnosticCategory.Error,
code: 0
};
}
export function readConfiguration(
project: string, basePath: string, existingOptions?: ts.CompilerOptions) {
// Allow a directory containing tsconfig.json as the project value
// Note, TS@next returns an empty array, while earlier versions throw
const projectFile =
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
if (error) check(basePath, [error]);
const parseConfigHost = {
useCaseSensitiveFileNames: true,
fileExists: fs.existsSync,
readDirectory: ts.sys.readDirectory,
readFile: ts.sys.readFile
};
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
check(basePath, parsed.errors);
// Default codegen goes to the current directory
// Parsed options are already converted to absolute paths
const ngOptions = config.angularCompilerOptions || {};
// Ignore the genDir option
ngOptions.genDir = basePath;
return {parsed, ngOptions};
}
function getProjectDirectory(project: string): string {
let isFile: boolean;
try {
isFile = fs.lstatSync(project).isFile();
} catch (e) {
// Project doesn't exist. Assume it is a file has an extension. This case happens
// when the project file is passed to set basePath but no tsconfig.json file exists.
// It is used in tests to ensure that the options can be passed in without there being
// an actual config file.
isFile = path.extname(project) !== '';
}
// If project refers to a file, the project directory is the file's parent directory
// otherwise project is the project directory.
return isFile ? path.dirname(project) : project;
}
export function performCompilation(
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
consoleError: (s: string) => void = console.error, tsCompilerHost?: ts.CompilerHost) {
try {
ngOptions.basePath = basePath;
ngOptions.genDir = basePath;
let host = tsCompilerHost || ts.createCompilerHost(options, true);
host.realpath = p => p;
const rootFileNames = files.map(f => path.normalize(f));
const addGeneratedFileName = (fileName: string) => {
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
rootFileNames.push(fileName);
}
};
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
const {host: bundleHost, indexName, errors} =
createBundleIndexHost(ngOptions, rootFileNames, host);
if (errors) check(basePath, errors);
if (indexName) addGeneratedFileName(indexName);
host = bundleHost;
}
const ngHostOptions = {...options, ...ngOptions};
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
const ngProgram =
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
// Check parameter diagnostics
check(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
// Check syntactic diagnostics
check(basePath, ngProgram.getTsSyntacticDiagnostics());
// Check TypeScript semantic and Angular structure diagnostics
check(basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
// Check Angular semantic diagnostics
check(basePath, ngProgram.getNgSemanticDiagnostics());
ngProgram.emit({
emitFlags: api.EmitFlags.Default |
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
});
} catch (e) {
if (isSyntaxError(e)) {
console.error(e.message);
consoleError(e.message);
return 1;
}
}
return 0;
}
export function main(args: string[], consoleError: (s: string) => void = console.error): number {
export function main(
args: string[], consoleError: (s: string) => void = console.error,
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics): number {
try {
const parsedArgs = require('minimist')(args);
const project = parsedArgs.p || parsedArgs.project || '.';
@ -191,8 +26,9 @@ export function main(args: string[], consoleError: (s: string) => void = console
// file names in tsconfig are resolved relative to this absolute path
const basePath = path.resolve(process.cwd(), projectDir);
const {parsed, ngOptions} = readConfiguration(project, basePath);
return performCompilation(basePath, parsed.fileNames, parsed.options, ngOptions, consoleError);
const {parsed, ngOptions} = readConfiguration(project, basePath, checkFunc);
return performCompilation(
basePath, parsed.fileNames, parsed.options, ngOptions, consoleError, checkFunc);
} catch (e) {
consoleError(e.stack);
consoleError('Compilation failed');

View File

@ -81,12 +81,10 @@ class CustomLoaderModuleResolutionHostAdapter extends ModuleResolutionHostAdapte
/**
* @internal
* @private
*/
export class NgTools_InternalApi_NG_2 {
/**
* @internal
* @private
*/
static codeGen(options: NgTools_InternalApi_NG2_CodeGen_Options): Promise<any> {
const hostContext: CompilerHostContext =
@ -114,7 +112,6 @@ export class NgTools_InternalApi_NG_2 {
/**
* @internal
* @private
*/
static listLazyRoutes(options: NgTools_InternalApi_NG2_ListLazyRoutes_Options):
NgTools_InternalApi_NG_2_LazyRouteMap {
@ -144,7 +141,6 @@ export class NgTools_InternalApi_NG_2 {
/**
* @internal
* @private
*/
static extractI18n(options: NgTools_InternalApi_NG2_ExtractI18n_Options): Promise<any> {
const hostContext: CompilerHostContext =

View File

@ -49,11 +49,6 @@ export class RouteDef {
}
/**
*
* @returns {LazyRouteMap}
* @private
*/
export function listLazyRoutesOfModule(
entryModule: string, host: AotCompilerHost, reflector: StaticReflector): LazyRouteMap {
const entryRouteDef = RouteDef.fromString(entryModule);
@ -111,7 +106,6 @@ function _resolveModule(modulePath: string, containingFile: string, host: AotCom
/**
* Throw an exception if a route is in a route map, but does not point to the same module.
* @private
*/
function _assertRoute(map: LazyRouteMap, route: LazyRoute) {
const r = route.routeDef.toString();
@ -173,7 +167,6 @@ function _extractLazyRoutesFromStaticModule(
/**
* Get the NgModule Metadata of a symbol.
* @private
*/
function _getNgModuleMetadata(staticSymbol: StaticSymbol, reflector: StaticReflector): NgModule {
const ngModules = reflector.annotations(staticSymbol).filter((s: any) => s instanceof NgModule);
@ -204,7 +197,6 @@ function _collectRoutes(
/**
* Return the loadChildren values of a list of Route.
* @private
*/
function _collectLoadChildren(routes: Route[]): string[] {
return routes.reduce((m, r) => {

View File

@ -132,7 +132,7 @@ export class PathMappedCompilerHost extends CompilerHost {
} else {
const sf = this.getSourceFile(rootedPath);
sf.fileName = sf.fileName;
const metadata = this.metadataCollector.getMetadata(sf);
const metadata = this.metadataProvider.getMetadata(sf);
return metadata ? [metadata] : [];
}
}

View File

@ -0,0 +1,192 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {isSyntaxError, syntaxError} from '@angular/compiler';
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import * as api from './transformers/api';
import * as ng from './transformers/entry_points';
const TS_EXT = /\.ts$/;
export type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
}
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
if (diags && diags.length) {
if (isTsDiagnostics(diags)) {
return ts.formatDiagnostics(diags, {
getCurrentDirectory: () => cwd,
getCanonicalFileName: fileName => fileName,
getNewLine: () => ts.sys.newLine
});
} else {
return diags
.map(d => {
let res = api.DiagnosticCategory[d.category];
if (d.span) {
res +=
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
}
if (d.span && d.span.details) {
res += `: ${d.span.details}, ${d.message}\n`;
} else {
res += `: ${d.message}\n`;
}
return res;
})
.join();
}
} else
return '';
}
/**
* Throw a syntax error exception with a message formatted for output
* if the args parameter contains diagnostics errors.
*
* @param cwd The directory to report error as relative to.
* @param args A list of potentially empty diagnostic errors.
*/
export function throwOnDiagnostics(cwd: string, ...args: Diagnostics[]) {
if (args.some(diags => !!(diags && diags[0]))) {
throw syntaxError(args.map(diags => {
if (diags && diags[0]) {
return formatDiagnostics(cwd, diags);
}
})
.filter(message => !!message)
.join(''));
}
}
function syntheticError(message: string): ts.Diagnostic {
return {
file: null as any as ts.SourceFile,
start: 0,
length: 0,
messageText: message,
category: ts.DiagnosticCategory.Error,
code: 0
};
}
export function readConfiguration(
project: string, basePath: string,
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
existingOptions?: ts.CompilerOptions) {
// Allow a directory containing tsconfig.json as the project value
// Note, TS@next returns an empty array, while earlier versions throw
const projectFile =
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
if (error) checkFunc(basePath, [error]);
const parseConfigHost = {
useCaseSensitiveFileNames: true,
fileExists: fs.existsSync,
readDirectory: ts.sys.readDirectory,
readFile: ts.sys.readFile
};
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
checkFunc(basePath, parsed.errors);
// Default codegen goes to the current directory
// Parsed options are already converted to absolute paths
const ngOptions = config.angularCompilerOptions || {};
// Ignore the genDir option
ngOptions.genDir = basePath;
return {parsed, ngOptions};
}
function getProjectDirectory(project: string): string {
let isFile: boolean;
try {
isFile = fs.lstatSync(project).isFile();
} catch (e) {
// Project doesn't exist. Assume it is a file has an extension. This case happens
// when the project file is passed to set basePath but no tsconfig.json file exists.
// It is used in tests to ensure that the options can be passed in without there being
// an actual config file.
isFile = path.extname(project) !== '';
}
// If project refers to a file, the project directory is the file's parent directory
// otherwise project is the project directory.
return isFile ? path.dirname(project) : project;
}
export function performCompilation(
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
consoleError: (s: string) => void = console.error,
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
tsCompilerHost?: ts.CompilerHost) {
try {
ngOptions.basePath = basePath;
ngOptions.genDir = basePath;
let host = tsCompilerHost || ts.createCompilerHost(options, true);
host.realpath = p => p;
const rootFileNames = files.map(f => path.normalize(f));
const addGeneratedFileName = (fileName: string) => {
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
rootFileNames.push(fileName);
}
};
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
const {host: bundleHost, indexName, errors} =
createBundleIndexHost(ngOptions, rootFileNames, host);
if (errors) checkFunc(basePath, errors);
if (indexName) addGeneratedFileName(indexName);
host = bundleHost;
}
const ngHostOptions = {...options, ...ngOptions};
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
const ngProgram =
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
// Check parameter diagnostics
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
// Check syntactic diagnostics
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());
// Check TypeScript semantic and Angular structure diagnostics
checkFunc(
basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
// Check Angular semantic diagnostics
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
ngProgram.emit({
emitFlags: api.EmitFlags.Default |
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
});
} catch (e) {
if (isSyntaxError(e)) {
console.error(e.message);
consoleError(e.message);
return 1;
}
throw e;
}
return 0;
}

View File

@ -91,6 +91,10 @@ export interface CompilerOptions extends ts.CompilerOptions {
// Whether to enable support for <template> and the template attribute (true by default)
enableLegacyTemplate?: boolean;
// Whether to enable lowering expressions lambdas and expressions in a reference value
// position.
disableExpressionLowering?: boolean;
}
export interface ModuleFilenameResolver {

View File

@ -0,0 +1,222 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
export interface LoweringRequest {
kind: ts.SyntaxKind;
location: number;
end: number;
name: string;
}
export type RequestLocationMap = Map<number, LoweringRequest>;
interface Declaration {
name: string;
node: ts.Node;
}
interface DeclarationInsert {
declarations: Declaration[];
priorTo: ts.Node;
}
function toMap<T, K>(items: T[], select: (item: T) => K): Map<K, T> {
return new Map(items.map<[K, T]>(i => [select(i), i]));
}
// We will never lower expressions in a nested lexical scope so avoid entering them.
// This also avoids a bug in TypeScript 2.3 where the lexical scopes get out of sync
// when using visitEachChild.
function isLexicalScope(node: ts.Node): boolean {
switch (node.kind) {
case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.ClassExpression:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.FunctionType:
case ts.SyntaxKind.TypeLiteral:
case ts.SyntaxKind.ArrayType:
return true;
}
return false;
}
function transformSourceFile(
sourceFile: ts.SourceFile, requests: RequestLocationMap,
context: ts.TransformationContext): ts.SourceFile {
const inserts: DeclarationInsert[] = [];
// Calculate the range of intersting locations. The transform will only visit nodes in this
// range to improve the performance on large files.
const locations = Array.from(requests.keys());
const min = Math.min(...locations);
const max = Math.max(...locations);
function visitSourceFile(sourceFile: ts.SourceFile): ts.SourceFile {
function topLevelStatement(node: ts.Node): ts.Node {
const declarations: Declaration[] = [];
function visitNode(node: ts.Node): ts.Node {
const nodeRequest = requests.get(node.pos);
if (nodeRequest && nodeRequest.kind == node.kind && nodeRequest.end == node.end) {
// This node is requested to be rewritten as a reference to the exported name.
// Record that the node needs to be moved to an exported variable with the given name
const name = nodeRequest.name;
declarations.push({name, node});
return ts.createIdentifier(name);
}
let result = node;
if (node.pos <= max && node.end >= min && !isLexicalScope(node)) {
result = ts.visitEachChild(node, visitNode, context);
}
return result;
}
const result =
(node.pos <= max && node.end >= min) ? ts.visitEachChild(node, visitNode, context) : node;
if (declarations.length) {
inserts.push({priorTo: result, declarations});
}
return result;
}
const traversedSource = ts.visitEachChild(sourceFile, topLevelStatement, context);
if (inserts.length) {
// Insert the declarations before the rewritten statement that references them.
const insertMap = toMap(inserts, i => i.priorTo);
const newStatements: ts.Statement[] = [...traversedSource.statements];
for (let i = newStatements.length; i >= 0; i--) {
const statement = newStatements[i];
const insert = insertMap.get(statement);
if (insert) {
const declarations = insert.declarations.map(
i => ts.createVariableDeclaration(
i.name, /* type */ undefined, i.node as ts.Expression));
const statement = ts.createVariableStatement(
/* modifiers */ undefined,
ts.createVariableDeclarationList(declarations, ts.NodeFlags.Const));
newStatements.splice(i, 0, statement);
}
}
// Insert an exports clause to export the declarations
newStatements.push(ts.createExportDeclaration(
/* decorators */ undefined,
/* modifiers */ undefined,
ts.createNamedExports(
inserts
.reduce(
(accumulator, insert) => [...accumulator, ...insert.declarations],
[] as Declaration[])
.map(
declaration => ts.createExportSpecifier(
/* propertyName */ undefined, declaration.name)))));
return ts.updateSourceFileNode(traversedSource, newStatements);
}
return traversedSource;
}
return visitSourceFile(sourceFile);
}
export function getExpressionLoweringTransformFactory(requestsMap: RequestsMap):
(context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
// Return the factory
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile): ts.SourceFile => {
const requests = requestsMap.getRequests(sourceFile);
if (requests && requests.size) {
return transformSourceFile(sourceFile, requests, context);
}
return sourceFile;
};
}
export interface RequestsMap { getRequests(sourceFile: ts.SourceFile): RequestLocationMap; }
interface MetadataAndLoweringRequests {
metadata: ModuleMetadata|undefined;
requests: RequestLocationMap;
}
function shouldLower(node: ts.Node | undefined): boolean {
if (node) {
switch (node.kind) {
case ts.SyntaxKind.SourceFile:
case ts.SyntaxKind.Decorator:
// Lower expressions that are local to the module scope or
// in a decorator.
return true;
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
// Don't lower expressions in a declaration.
return false;
case ts.SyntaxKind.VariableDeclaration:
// Avoid lowering expressions already in an exported variable declaration
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) == 0;
}
return shouldLower(node.parent);
}
return true;
}
export class LowerMetadataCache implements RequestsMap {
private collector: MetadataCollector;
private metadataCache = new Map<string, MetadataAndLoweringRequests>();
constructor(options: CollectorOptions, private strict?: boolean) {
this.collector = new MetadataCollector(options);
}
getMetadata(sourceFile: ts.SourceFile): ModuleMetadata|undefined {
return this.ensureMetadataAndRequests(sourceFile).metadata;
}
getRequests(sourceFile: ts.SourceFile): RequestLocationMap {
return this.ensureMetadataAndRequests(sourceFile).requests;
}
private ensureMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
let result = this.metadataCache.get(sourceFile.fileName);
if (!result) {
result = this.getMetadataAndRequests(sourceFile);
this.metadataCache.set(sourceFile.fileName, result);
}
return result;
}
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
let identNumber = 0;
const freshIdent = () => '\u0275' + identNumber++;
const requests = new Map<number, LoweringRequest>();
const replaceNode = (node: ts.Node) => {
const name = freshIdent();
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
return {__symbolic: 'reference', name};
};
const substituteExpression = (value: MetadataValue, node: ts.Node): MetadataValue => {
if ((node.kind === ts.SyntaxKind.ArrowFunction ||
node.kind === ts.SyntaxKind.FunctionExpression) &&
shouldLower(node)) {
return replaceNode(node);
}
return value;
};
const metadata = this.collector.getMetadata(sourceFile, this.strict, substituteExpression);
return {metadata, requests};
}
}

View File

@ -289,4 +289,4 @@ function createModuleFilenameResolverHost(host: ts.ModuleResolutionHost):
resolveModuleNameHost.realpath = (fileName: string) => fileName;
return resolveModuleNameHost;
}
}

View File

@ -16,6 +16,7 @@ import {CompilerHost as AotCompilerHost, CompilerHostContext} from '../compiler_
import {TypeChecker} from '../diagnostics/check_types';
import {CompilerHost, CompilerOptions, Diagnostic, DiagnosticCategory, EmitFlags, Program} from './api';
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
const GENERATED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$|\.ngsummary\.js$/;
@ -34,7 +35,7 @@ class AngularCompilerProgram implements Program {
private aotCompilerHost: AotCompilerHost;
private compiler: AotCompiler;
private srcNames: string[];
private collector: MetadataCollector;
private metadataCache: LowerMetadataCache;
// Lazily initialized fields
private _analyzedModules: NgAnalyzedModules|undefined;
private _structuralDiagnostics: Diagnostic[] = [];
@ -54,13 +55,14 @@ class AngularCompilerProgram implements Program {
this.tsProgram = ts.createProgram(rootNames, options, host, this.oldTsProgram);
this.srcNames = this.tsProgram.getSourceFiles().map(sf => sf.fileName);
this.aotCompilerHost = new AotCompilerHost(this.tsProgram, options, host);
this.metadataCache = new LowerMetadataCache({quotedNames: true}, !!options.strictMetadataEmit);
this.aotCompilerHost = new AotCompilerHost(
this.tsProgram, options, host, /* collectorOptions */ undefined, this.metadataCache);
if (host.readResource) {
this.aotCompilerHost.loadResource = host.readResource.bind(host);
}
const {compiler} = createAotCompiler(this.aotCompilerHost, options);
this.compiler = compiler;
this.collector = new MetadataCollector({quotedNames: true});
}
// Program implementation
@ -117,11 +119,9 @@ class AngularCompilerProgram implements Program {
const emitMap = new Map<string, string>();
const result = this.programWithStubs.emit(
/* targetSourceFile */ undefined,
createWriteFileCallback(emitFlags, this.host, this.collector, this.options, emitMap),
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS, {
after: this.options.skipTemplateCodegen ? [] : [getAngularEmitterTransformFactory(
this.generatedFiles)]
});
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap),
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
this.calculateTransforms());
this.generatedFiles.forEach(file => {
if (file.source && file.source.length && SUMMARY_JSON_FILES.test(file.genFileUrl)) {
@ -183,6 +183,21 @@ class AngularCompilerProgram implements Program {
return this.generatedFiles && this._generatedFileDiagnostics !;
}
private calculateTransforms(): ts.CustomTransformers {
const before: ts.TransformerFactory<ts.SourceFile>[] = [];
const after: ts.TransformerFactory<ts.SourceFile>[] = [];
if (!this.options.disableExpressionLowering) {
before.push(getExpressionLoweringTransformFactory(this.metadataCache));
}
if (!this.options.skipTemplateCodegen) {
after.push(getAngularEmitterTransformFactory(this.generatedFiles));
}
const result: ts.CustomTransformers = {};
if (before.length) result.before = before;
if (after.length) result.after = after;
return result;
}
private catchAnalysisError(e: any): NgAnalyzedModules {
if (isSyntaxError(e)) {
const parserErrors = getParseErrors(e);
@ -256,8 +271,7 @@ export function createProgram(
}
function writeMetadata(
emitFilePath: string, sourceFile: ts.SourceFile, collector: MetadataCollector,
ngOptions: CompilerOptions) {
emitFilePath: string, sourceFile: ts.SourceFile, metadataCache: LowerMetadataCache) {
if (/\.js$/.test(emitFilePath)) {
const path = emitFilePath.replace(/\.js$/, '.metadata.json');
@ -270,7 +284,7 @@ function writeMetadata(
collectableFile = (collectableFile as any).original;
}
const metadata = collector.getMetadata(collectableFile, !!ngOptions.strictMetadataEmit);
const metadata = metadataCache.getMetadata(collectableFile);
if (metadata) {
const metadataText = JSON.stringify([metadata]);
writeFileSync(path, metadataText, {encoding: 'utf-8'});
@ -279,8 +293,8 @@ function writeMetadata(
}
function createWriteFileCallback(
emitFlags: EmitFlags, host: ts.CompilerHost, collector: MetadataCollector,
ngOptions: CompilerOptions, emitMap: Map<string, string>) {
emitFlags: EmitFlags, host: ts.CompilerHost, metadataCache: LowerMetadataCache,
emitMap: Map<string, string>) {
const withMetadata =
(fileName: string, data: string, writeByteOrderMark: boolean,
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
@ -290,7 +304,7 @@ function createWriteFileCallback(
}
if (!generatedFile && sourceFiles && sourceFiles.length == 1) {
emitMap.set(sourceFiles[0].fileName, fileName);
writeMetadata(fileName, sourceFiles[0], collector, ngOptions);
writeMetadata(fileName, sourceFiles[0], metadataCache);
}
};
const withoutMetadata =

View File

@ -11,7 +11,8 @@ import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {main, performCompilation} from '../src/ngc';
import {main} from '../src/ngc';
import {performCompilation, readConfiguration} from '../src/perform-compile';
function getNgRootDir() {
const moduleFilename = module.filename.replace(/\\/g, '/');
@ -308,7 +309,6 @@ describe('ngc command-line', () => {
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')]);
expect(exitCode).toEqual(0);
expect(fs.existsSync(path.resolve(outDir, 'mymodule.ngfactory.js'))).toBe(true);
expect(fs.existsSync(path.resolve(
outDir, 'node_modules', '@angular', 'core', 'src',
@ -316,6 +316,145 @@ describe('ngc command-line', () => {
.toBe(true);
});
describe('expression lowering', () => {
beforeEach(() => {
writeConfig(`{
"extends": "./tsconfig-base.json",
"files": ["mymodule.ts"]
}`);
});
function compile(): number {
const errors: string[] = [];
const result = main(['-p', path.join(basePath, 'tsconfig.json')], s => errors.push(s));
expect(errors).toEqual([]);
return result;
}
it('should be able to lower a lambda expression in a provider', () => {
write('mymodule.ts', `
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
class Foo {}
@NgModule({
imports: [CommonModule],
providers: [{provide: 'someToken', useFactory: () => new Foo()}]
})
export class MyModule {}
`);
expect(compile()).toEqual(0);
const mymodulejs = path.resolve(outDir, 'mymodule.js');
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('export { ɵ0');
const mymodulefactory = path.resolve(outDir, 'mymodule.ngfactory.js');
const mymodulefactorySource = fs.readFileSync(mymodulefactory, 'utf8');
expect(mymodulefactorySource).toContain('"someToken", i1.ɵ0');
});
it('should be able to lower a function expression in a provider', () => {
write('mymodule.ts', `
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
class Foo {}
@NgModule({
imports: [CommonModule],
providers: [{provide: 'someToken', useFactory: function() {return new Foo();}}]
})
export class MyModule {}
`);
expect(compile()).toEqual(0);
const mymodulejs = path.resolve(outDir, 'mymodule.js');
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('export { ɵ0');
const mymodulefactory = path.resolve(outDir, 'mymodule.ngfactory.js');
const mymodulefactorySource = fs.readFileSync(mymodulefactory, 'utf8');
expect(mymodulefactorySource).toContain('"someToken", i1.ɵ0');
});
it('should able to lower multiple expressions', () => {
write('mymodule.ts', `
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
class Foo {}
@NgModule({
imports: [CommonModule],
providers: [
{provide: 'someToken', useFactory: () => new Foo()},
{provide: 'someToken', useFactory: () => new Foo()},
{provide: 'someToken', useFactory: () => new Foo()},
{provide: 'someToken', useFactory: () => new Foo()}
]
})
export class MyModule {}
`);
expect(compile()).toEqual(0);
const mymodulejs = path.resolve(outDir, 'mymodule.js');
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
expect(mymoduleSource).toContain('ɵ0 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('ɵ1 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('ɵ2 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('ɵ3 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('export { ɵ0, ɵ1, ɵ2, ɵ3');
});
it('should be able to lower an indirect expression', () => {
write('mymodule.ts', `
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
class Foo {}
const factory = () => new Foo();
@NgModule({
imports: [CommonModule],
providers: [{provide: 'someToken', useFactory: factory}]
})
export class MyModule {}
`);
expect(compile()).toEqual(0, 'Compile failed');
const mymodulejs = path.resolve(outDir, 'mymodule.js');
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
expect(mymoduleSource).toContain('export { ɵ0');
});
it('should not lower a lambda that is already exported', () => {
write('mymodule.ts', `
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
class Foo {}
export const factory = () => new Foo();
@NgModule({
imports: [CommonModule],
providers: [{provide: 'someToken', useFactory: factory}]
})
export class MyModule {}
`);
expect(compile()).toEqual(0);
const mymodulejs = path.resolve(outDir, 'mymodule.js');
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
expect(mymoduleSource).not.toContain('ɵ0');
});
});
const shouldExist = (fileName: string) => {
if (!fs.existsSync(path.resolve(outDir, fileName))) {
throw new Error(`Expected ${fileName} to be emitted (outDir: ${outDir})`);

View File

@ -0,0 +1,105 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
describe('Expression lowering', () => {
it('should be able to lower a simple expression', () => {
expect(convert('const a = 1 +◊b: 2◊;')).toBe('const b = 2; const a = 1 + b; export { b };');
});
it('should be able to lower an expression in a decorator', () => {
expect(convert(`
import {Component} from '@angular/core';
@Component({
provider: [{provide: 'someToken', useFactory:◊l: () => null◊}]
})
class MyClass {}
`)).toContain('const l = () => null; exports.l = l;');
});
});
function convert(annotatedSource: string) {
const annotations: {start: number, length: number, name: string}[] = [];
let adjustment = 0;
const unannotatedSource = annotatedSource.replace(
/◊([a-zA-Z]+):(.*)◊/g,
(text: string, name: string, source: string, index: number): string => {
annotations.push({start: index + adjustment, length: source.length, name});
adjustment -= text.length - source.length;
return source;
});
const baseFileName = 'someFile';
const moduleName = '/' + baseFileName;
const fileName = moduleName + '.ts';
const context = new MockAotContext('/', {[baseFileName + '.ts']: unannotatedSource});
const host = new MockCompilerHost(context);
const sourceFile = ts.createSourceFile(
fileName, unannotatedSource, ts.ScriptTarget.Latest, /* setParentNodes */ true);
const requests = new Map<number, LoweringRequest>();
for (const annotation of annotations) {
const node = findNode(sourceFile, annotation.start, annotation.length);
if (!node) throw new Error('Invalid test specification. Could not find the node to substitute');
const location = node.pos;
requests.set(location, {name: annotation.name, kind: node.kind, location, end: node.end});
}
const program = ts.createProgram(
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
const moduleSourceFile = program.getSourceFile(fileName);
const transformers: ts.CustomTransformers = {
before: [getExpressionLoweringTransformFactory({
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
if (sourceFile.fileName == moduleSourceFile.fileName) {
return requests;
} else {return new Map();}
}
})]
};
let result: string = '';
const emitResult = program.emit(
moduleSourceFile, (emittedFileName, data, writeByteOrderMark, onError, sourceFiles) => {
if (fileName.startsWith(moduleName)) {
result = data;
}
}, undefined, undefined, transformers);
return normalizeResult(result);
};
function findNode(node: ts.Node, start: number, length: number): ts.Node|undefined {
function find(node: ts.Node): ts.Node|undefined {
if (node.getFullStart() == start && node.getEnd() == start + length) {
return node;
}
if (node.getFullStart() <= start && node.getEnd() >= start + length) {
return ts.forEachChild(node, find);
}
}
return ts.forEachChild(node, find);
}
function normalizeResult(result: string): string {
// Remove TypeScript prefixes
// Remove new lines
// Squish adjacent spaces
// Remove prefix and postfix spaces
return result.replace('"use strict";', ' ')
.replace('exports.__esModule = true;', ' ')
.replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ')
.replace(/\n/g, ' ')
.replace(/ +/g, ' ')
.replace(/^ /g, '')
.replace(/ $/g, '');
}

View File

@ -14,7 +14,7 @@
"@angular/http": ["../../dist/packages/http"],
"@angular/platform-server": ["../../dist/packages/platform-server"],
"@angular/platform-browser": ["../../dist/packages/platform-browser"],
"@angular/tsc-wrapped": ["../../dist/tools/@angular/tsc-wrapped"]
"@angular/tsc-wrapped": ["../../dist/packages-dist/tsc-wrapped"]
},
"rootDir": ".",
"sourceMap": true,

View File

@ -0,0 +1,13 @@
package(default_visibility=["//visibility:public"])
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
ts_library(
name = "compiler",
srcs = glob(["**/*.ts"], exclude=[
"test/**",
"testing/**",
]),
module_name = "@angular/compiler",
deps = ["//packages/core"],
tsconfig = ":tsconfig-build.json",
)

View File

@ -376,7 +376,6 @@ export class StaticReflector implements CompileReflector {
if (calling.get(functionSymbol)) {
throw new Error('Recursion not supported');
}
calling.set(functionSymbol, true);
try {
const value = targetFunction['value'];
if (value && (depth != 0 || value.__symbolic != 'error')) {
@ -387,6 +386,7 @@ export class StaticReflector implements CompileReflector {
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
calling.set(functionSymbol, true);
const functionScope = BindingScope.build();
for (let i = 0; i < parameters.length; i++) {
functionScope.define(parameters[i], args[i]);

View File

@ -345,4 +345,4 @@ class FromJsonDeserializer extends ValueTransformer {
return super.visitStringMap(map, context);
}
}
}
}

View File

@ -231,7 +231,6 @@ class _Scanner {
* @param two second symbol (part of the operator when the second code point matches)
* @param threeCode code point for the third symbol
* @param three third symbol (part of the operator when provided and matches source expression)
* @returns {Token}
*/
scanComplexOperator(
start: number, one: string, twoCode: number, two: string, threeCode?: number,

View File

@ -139,23 +139,28 @@ class _WriteVisitor implements i18n.Visitor {
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
const ctype = getCtypeForTag(ph.tag);
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype});
if (ph.isVoid) {
// void tags have no children nor closing tags
return [startTagPh];
return [new xml.Tag(
_PLACEHOLDER_TAG, {id: ph.startName, ctype, 'equiv-text': `<${ph.tag}/>`})];
}
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype});
const startTagPh =
new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype, 'equiv-text': `<${ph.tag}>`});
const closeTagPh =
new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype, 'equiv-text': `</${ph.tag}>`});
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
}
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name})];
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name, 'equiv-text': `{{${ph.value}}}`})];
}
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name})];
const equivText =
`{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`;
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name, 'equiv-text': equivText})];
}
serialize(nodes: i18n.Node[]): xml.Node[] {

View File

@ -129,11 +129,16 @@ class _Visitor implements i18n.Visitor {
}
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
const exTag = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`{{${ph.value}}}`)]);
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name}, [exTag])];
}
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
const exTag = new xml.Tag(_EXEMPLE_TAG, {}, [
new xml.Text(
`{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`)
]);
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name}, [exTag])];
}
serialize(nodes: i18n.Node[]): xml.Node[] {

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console} from '@angular/core';
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, Injector, MissingTranslationStrategy, Optional, PACKAGE_ROOT_URL, PlatformRef, StaticProvider, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console} from '@angular/core';
import {StaticSymbolCache} from '../aot/static_symbol';
import {CompileReflector} from '../compile_reflector';
import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
@ -16,7 +17,7 @@ import {Lexer} from '../expression_parser/lexer';
import {Parser} from '../expression_parser/parser';
import * as i18n from '../i18n/index';
import {CompilerInjectable} from '../injectable';
import {CompileMetadataResolver} from '../metadata_resolver';
import {CompileMetadataResolver, ERROR_COLLECTOR_TOKEN} from '../metadata_resolver';
import {HtmlParser} from '../ml_parser/html_parser';
import {NgModuleCompiler} from '../ng_module_compiler';
import {NgModuleResolver} from '../ng_module_resolver';
@ -26,7 +27,7 @@ import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {StyleCompiler} from '../style_compiler';
import {JitSummaryResolver, SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {TEMPLATE_TRANSFORMS, TemplateParser} from '../template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler';
@ -45,17 +46,18 @@ const baseHtmlParser = new InjectionToken('HtmlParser');
* A set of providers that provide `JitCompiler` and its dependencies to use for
* template compilation.
*/
export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> = [
export const COMPILER_PROVIDERS = <StaticProvider[]>[
{provide: CompileReflector, useValue: new JitReflector()},
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
JitSummaryResolver,
{provide: JitSummaryResolver, deps: []},
{provide: SummaryResolver, useExisting: JitSummaryResolver},
Console,
Lexer,
Parser,
{provide: Console, deps: []},
{provide: Lexer, deps: []},
{provide: Parser, deps: [Lexer]},
{
provide: baseHtmlParser,
useClass: HtmlParser,
deps: [],
},
{
provide: i18n.I18NHtmlParser,
@ -78,22 +80,37 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
provide: HtmlParser,
useExisting: i18n.I18NHtmlParser,
},
TemplateParser,
DirectiveNormalizer,
CompileMetadataResolver,
{
provide: TemplateParser, deps: [CompilerConfig, CompileReflector,
Parser, ElementSchemaRegistry,
i18n.I18NHtmlParser, Console, [Optional, TEMPLATE_TRANSFORMS]]
},
{ provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]},
{ provide: CompileMetadataResolver, deps: [CompilerConfig, NgModuleResolver,
DirectiveResolver, PipeResolver,
SummaryResolver,
ElementSchemaRegistry,
DirectiveNormalizer, Console,
[Optional, StaticSymbolCache],
CompileReflector,
[Optional, ERROR_COLLECTOR_TOKEN]]},
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
NgModuleCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()},
JitCompiler,
{provide: Compiler, useExisting: JitCompiler},
DomElementSchemaRegistry,
{provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
UrlResolver,
DirectiveResolver,
PipeResolver,
NgModuleResolver,
{ provide: StyleCompiler, deps: [UrlResolver]},
{ provide: ViewCompiler, deps: [CompilerConfig, CompileReflector, ElementSchemaRegistry]},
{ provide: NgModuleCompiler, deps: [CompileReflector] },
{ provide: CompilerConfig, useValue: new CompilerConfig()},
{ provide: JitCompiler, deps: [Injector, CompileMetadataResolver,
TemplateParser, StyleCompiler,
ViewCompiler, NgModuleCompiler,
SummaryResolver, CompilerConfig,
Console]},
{ provide: Compiler, useExisting: JitCompiler},
{ provide: DomElementSchemaRegistry, deps: []},
{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
{ provide: UrlResolver, deps: [PACKAGE_ROOT_URL]},
{ provide: DirectiveResolver, deps: [CompileReflector]},
{ provide: PipeResolver, deps: [CompileReflector]},
{ provide: NgModuleResolver, deps: [CompileReflector]},
];
@CompilerInjectable()
@ -112,7 +129,7 @@ export class JitCompilerFactory implements CompilerFactory {
}
createCompiler(options: CompilerOptions[] = []): Compiler {
const opts = _mergeOptions(this._defaultOptions.concat(options));
const injector = ReflectiveInjector.resolveAndCreate([
const injector = Injector.create([
COMPILER_PROVIDERS, {
provide: CompilerConfig,
useFactory: () => {
@ -142,7 +159,7 @@ export class JitCompilerFactory implements CompilerFactory {
*/
export const platformCoreDynamic = createPlatformFactory(platformCore, 'coreDynamic', [
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory},
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
]);
function _mergeOptions(optionsArr: CompilerOptions[]): CompilerOptions {

View File

@ -150,7 +150,7 @@ class _Tokenizer {
}
/**
* @returns {boolean} whether an ICU token has been created
* @returns whether an ICU token has been created
* @internal
*/
private _tokenizeExpansionForm(): boolean {

View File

@ -401,7 +401,6 @@ export class BindingParser {
* @param propName the name of the property / attribute
* @param sourceSpan
* @param isAttr true when binding to an attribute
* @private
*/
private _validatePropertyOrAttributeName(
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {

View File

@ -202,7 +202,6 @@ function _buildFromEncodedParts(
* $6 = <undefined> query without ?
* $7 = Related fragment without #
* </pre>
* @type {!RegExp}
* @internal
*/
const _splitRe = new RegExp(

View File

@ -1071,4 +1071,4 @@ function calcStaticDynamicQueryFlags(
flags |= NodeFlags.DynamicQuery;
}
return flags;
}
}

View File

@ -462,6 +462,20 @@ describe('StaticReflector', () => {
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
});
it('should be able to get metadata for a class calling a macro function', () => {
const annotations = reflector.annotations(
reflector.getStaticSymbol('/tmp/src/call-macro-function.ts', 'MyComponent'));
expect(annotations.length).toBe(1);
expect(annotations[0].providers.useValue).toBe(100);
});
it('should be able to get metadata for a class calling a nested macro function', () => {
const annotations = reflector.annotations(
reflector.getStaticSymbol('/tmp/src/call-macro-function.ts', 'MyComponentNested'));
expect(annotations.length).toBe(1);
expect(annotations[0].providers.useValue.useValue).toBe(100);
});
// #13605
it('should not throw on unknown decorators', () => {
const data = Object.create(DEFAULT_TEST_DATA);
@ -1392,6 +1406,25 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
static VALUE = 'Some string';
}
`,
'/tmp/src/macro-function.ts': `
export function v(value: any) {
return { provide: 'a', useValue: value };
}
`,
'/tmp/src/call-macro-function.ts': `
import {Component} from '@angular/core';
import {v} from './macro-function';
@Component({
providers: v(100)
})
export class MyComponent { }
@Component({
providers: v(v(100))
})
export class MyComponentNested { }
`,
'/tmp/src/static-field-reference.ts': `
import {Component} from '@angular/core';
import {MyModule} from './static-field';

View File

@ -405,7 +405,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost {
constructor(private host: ts.CompilerHost) {}
getMetadataFor(moduleName: string): ModuleMetadata {
getMetadataFor(moduleName: string): ModuleMetadata|undefined {
const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest);
return this.collector.getMetadata(source);
}

View File

@ -328,10 +328,7 @@ export function main() {
describe('normalizeExternalStylesheets', () => {
beforeEach(() => {
TestBed.configureCompiler(
{providers: [{provide: ResourceLoader, useClass: SpyResourceLoader}]});
});
beforeEach(() => { TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); });
it('should load an external stylesheet',
inject(

View File

@ -26,6 +26,7 @@ export class I18nComponent {
}
export class FrLocalization extends NgLocalization {
public static PROVIDE = {provide: NgLocalization, useClass: FrLocalization, deps: []};
getPluralCategory(value: number): string {
switch (value) {
case 0:
@ -113,6 +114,7 @@ export const HTML = `
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
<div id="i18n-3c"><div i18n><div>with <div>nested</div> placeholders</div></div></div>
<div>
<p id="i18n-4" i18n-title title="on not translatable node"></p>

View File

@ -26,8 +26,8 @@ export function main() {
beforeEach(async(() => {
TestBed.configureCompiler({
providers: [
{provide: ResourceLoader, useClass: SpyResourceLoader},
{provide: NgLocalization, useClass: FrLocalization},
SpyResourceLoader.PROVIDE,
FrLocalization.PROVIDE,
{provide: TRANSLATIONS, useValue: XLIFF2_TOMERGE},
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf2'},
]
@ -80,6 +80,15 @@ const XLIFF2_TOMERGE = `
<target><pc id="0" equivStart="START_ITALIC_TEXT" equivEnd="CLOSE_ITALIC_TEXT" type="fmt" dispStart="&lt;i&gt;" dispEnd="&lt;/i&gt;">avec des espaces réservés</pc></target>
</segment>
</unit>
<unit id="5415448997399451992">
<notes>
<note category="location">file.ts:11</note>
</notes>
<segment>
<source><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">with <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">nested</pc> placeholders</pc></source>
<target><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">avec <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">espaces réservés</pc> imbriqués</pc></target>
</segment>
</unit>
<unit id="5525133077318024839">
<segment>
<source>on not translatable node</source>
@ -242,9 +251,17 @@ const XLIFF2_EXTRACTED = `
<source><pc id="0" equivStart="START_ITALIC_TEXT" equivEnd="CLOSE_ITALIC_TEXT" type="fmt" dispStart="&lt;i&gt;" dispEnd="&lt;/i&gt;">with placeholders</pc></source>
</segment>
</unit>
<unit id="5415448997399451992">
<notes>
<note category="location">file.ts:11</note>
</notes>
<segment>
<source><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">with <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="&lt;div&gt;" dispEnd="&lt;/div&gt;">nested</pc> placeholders</pc></source>
</segment>
</unit>
<unit id="5525133077318024839">
<notes>
<note category="location">file.ts:13</note>
<note category="location">file.ts:14</note>
</notes>
<segment>
<source>on not translatable node</source>
@ -252,7 +269,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="8670732454866344690">
<notes>
<note category="location">file.ts:14</note>
<note category="location">file.ts:15</note>
</notes>
<segment>
<source>on translatable node</source>
@ -260,8 +277,8 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="4593805537723189714">
<notes>
<note category="location">file.ts:19</note>
<note category="location">file.ts:36</note>
<note category="location">file.ts:20</note>
<note category="location">file.ts:37</note>
</notes>
<segment>
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">many</pc>} }</source>
@ -269,7 +286,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="4360321700965841752">
<notes>
<note category="location">file.ts:21,23</note>
<note category="location">file.ts:22,24</note>
</notes>
<segment>
<source>
@ -279,7 +296,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="5460933846928880074">
<notes>
<note category="location">file.ts:22</note>
<note category="location">file.ts:23</note>
</notes>
<segment>
<source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</source>
@ -287,7 +304,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="1746565782635215">
<notes>
<note category="location">file.ts:24,26</note>
<note category="location">file.ts:25,27</note>
</notes>
<segment>
<source>
@ -297,7 +314,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="5868084092545682515">
<notes>
<note category="location">file.ts:25</note>
<note category="location">file.ts:26</note>
</notes>
<segment>
<source>{VAR_SELECT, select, m {male} f {female} }</source>
@ -305,7 +322,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="4851788426695310455">
<notes>
<note category="location">file.ts:28</note>
<note category="location">file.ts:29</note>
</notes>
<segment>
<source><ph id="0" equiv="INTERPOLATION" disp="{{ &quot;count = &quot; + count }}"/></source>
@ -313,7 +330,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="9013357158046221374">
<notes>
<note category="location">file.ts:29</note>
<note category="location">file.ts:30</note>
</notes>
<segment>
<source>sex = <ph id="0" equiv="INTERPOLATION" disp="{{ sex }}"/></source>
@ -321,7 +338,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="8324617391167353662">
<notes>
<note category="location">file.ts:30</note>
<note category="location">file.ts:31</note>
</notes>
<segment>
<source><ph id="0" equiv="CUSTOM_NAME" disp="{{ &quot;custom name&quot; //i18n(ph=&quot;CUSTOM_NAME&quot;) }}"/></source>
@ -329,8 +346,8 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="7685649297917455806">
<notes>
<note category="location">file.ts:35</note>
<note category="location">file.ts:53</note>
<note category="location">file.ts:36</note>
<note category="location">file.ts:54</note>
</notes>
<segment>
<source>in a translatable section</source>
@ -338,7 +355,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="2387287228265107305">
<notes>
<note category="location">file.ts:33,37</note>
<note category="location">file.ts:34,38</note>
</notes>
<segment>
<source>
@ -350,7 +367,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="1491627405349178954">
<notes>
<note category="location">file.ts:39</note>
<note category="location">file.ts:40</note>
</notes>
<segment>
<source>it <pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">should</pc> work</source>
@ -358,7 +375,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="i18n16">
<notes>
<note category="location">file.ts:41</note>
<note category="location">file.ts:42</note>
</notes>
<segment>
<source>with an explicit ID</source>
@ -366,7 +383,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="i18n17">
<notes>
<note category="location">file.ts:42</note>
<note category="location">file.ts:43</note>
</notes>
<segment>
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">many</pc>} }</source>
@ -375,7 +392,7 @@ const XLIFF2_EXTRACTED = `
<unit id="4085484936881858615">
<notes>
<note category="description">desc</note>
<note category="location">file.ts:45,51</note>
<note category="location">file.ts:46,52</note>
</notes>
<segment>
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph id="0" equiv="INTERPOLATION" disp="{{response.getItemsList().length}}"/> results} }</source>
@ -383,7 +400,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="4035252431381981115">
<notes>
<note category="location">file.ts:53</note>
<note category="location">file.ts:54</note>
</notes>
<segment>
<source>foo<pc id="0" equivStart="START_LINK" equivEnd="CLOSE_LINK" type="link" dispStart="&lt;a&gt;" dispEnd="&lt;/a&gt;">bar</pc></source>
@ -391,7 +408,7 @@ const XLIFF2_EXTRACTED = `
</unit>
<unit id="5339604010413301604">
<notes>
<note category="location">file.ts:55</note>
<note category="location">file.ts:56</note>
</notes>
<segment>
<source><ph id="0" equiv="MAP NAME" disp="{{ &apos;test&apos; //i18n(ph=&quot;map name&quot;) }}"/></source>

View File

@ -26,8 +26,8 @@ export function main() {
beforeEach(async(() => {
TestBed.configureCompiler({
providers: [
{provide: ResourceLoader, useClass: SpyResourceLoader},
{provide: NgLocalization, useClass: FrLocalization},
SpyResourceLoader.PROVIDE,
FrLocalization.PROVIDE,
{provide: TRANSLATIONS, useValue: XLIFF_TOMERGE},
{provide: TRANSLATIONS_FORMAT, useValue: 'xliff'},
]
@ -70,9 +70,17 @@ const XLIFF_TOMERGE = `
<note priority="1" from="meaning">different meaning</note>
</trans-unit>
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
<source><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="&lt;i&gt;"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="&lt;/i&gt;"/></source>
<target><x id="START_ITALIC_TEXT" ctype="x-i"/>avec des espaces réservés<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></target>
</trans-unit>
<trans-unit id="651d7249d3a225037eb66f3433d98ad4a86f0a22" datatype="html">
<source><x id="START_TAG_DIV" ctype="x-div"/>with <x id="START_TAG_DIV" ctype="x-div"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
<target><x id="START_TAG_DIV" ctype="x-div"/>with <x id="START_TAG_DIV" ctype="x-div"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div"/></target>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
<source>on not translatable node</source>
<target>sur des balises non traductibles</target>
@ -82,12 +90,12 @@ const XLIFF_TOMERGE = `
<target>sur des balises traductibles</target>
</trans-unit>
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/>} }</source>
<target>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<x id="START_BOLD_TEXT" ctype="x-b"/>beaucoup<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</target>
</trans-unit>
<trans-unit id="ba9106fca6e4e33a9b703a8c1927e2d1794dd9ff" datatype="html">
<source>
<x id="ICU"/>
<x id="ICU" equiv-text="{sex, select, m {...} f {...}}"/>
</source>
<target><x id="ICU"/></target>
</trans-unit>
@ -97,7 +105,7 @@ const XLIFF_TOMERGE = `
</trans-unit>
<trans-unit id="078b7089573c5f66a2f78dce0adaa55e6715beb1" datatype="html">
<source>
<x id="ICU"/>
<x id="ICU" equiv-text="{sexB, select, m {...} f {...}}"/>
</source>
<target><x id="ICU"/></target>
</trans-unit>
@ -106,15 +114,15 @@ const XLIFF_TOMERGE = `
<target>{VAR_SELECT, select, m {homme} f {femme} }</target>
</trans-unit>
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
<source><x id="INTERPOLATION"/></source>
<source><x id="INTERPOLATION" equiv-text="{{ &quot;count = &quot; + count }}"/></source>
<target><x id="INTERPOLATION"/></target>
</trans-unit>
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
<source>sex = <x id="INTERPOLATION"/></source>
<source>sex = <x id="INTERPOLATION" equiv-text="{{ sex }}"/></source>
<target>sexe = <x id="INTERPOLATION"/></target>
</trans-unit>
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
<source><x id="CUSTOM_NAME"/></source>
<source><x id="CUSTOM_NAME" equiv-text="{{ &quot;custom name&quot; //i18n(ph=&quot;CUSTOM_NAME&quot;) }}"/></source>
<target><x id="CUSTOM_NAME"/></target>
</trans-unit>
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
@ -123,9 +131,9 @@ const XLIFF_TOMERGE = `
</trans-unit>
<trans-unit id="eee74a5be8a75881a4785905bd8302a71f7d9f75" datatype="html">
<source>
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
<x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
<x id="START_HEADING_LEVEL1" ctype="x-h1" equiv-text="&lt;h1&gt;"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1" equiv-text="&lt;/h1&gt;"/>
<x id="START_TAG_DIV" ctype="x-div" equiv-text="&lt;div&gt;"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/>
<x id="START_TAG_DIV_1" ctype="x-div" equiv-text="&lt;div&gt;"/><x id="ICU" equiv-text="{count, plural, =0 {...} =1 {...} =2 {...} other {...}}"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/>
</source>
<target>
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Balises dans les commentaires html<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
@ -134,7 +142,7 @@ const XLIFF_TOMERGE = `
</target>
</trans-unit>
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
<source>it <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/> work</source>
<target>ca <x id="START_BOLD_TEXT" ctype="x-b"/>devrait<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> marcher</target>
</trans-unit>
<trans-unit id="i18n16" datatype="html">
@ -142,20 +150,20 @@ const XLIFF_TOMERGE = `
<target>avec un ID explicite</target>
</trans-unit>
<trans-unit id="i18n17" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/>} }</source>
<target>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<x id="START_BOLD_TEXT" ctype="x-b"/>beaucoup<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</target>
</trans-unit>
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION" equiv-text="{{response.getItemsList().length}}"/> results} }</source>
<target>{VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {Found <x id="INTERPOLATION"/> réponse} }</target>
<note priority="1" from="description">desc</note>
</trans-unit>
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
<source>foo<x id="START_LINK" ctype="x-a" equiv-text="&lt;a&gt;"/>bar<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a&gt;"/></source>
<target>FOO<x id="START_LINK" ctype="x-a"/>BAR<x id="CLOSE_LINK" ctype="x-a"/></target>
</trans-unit>
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
<source><x id="MAP NAME"/></source>
<source><x id="MAP NAME" equiv-text="{{ &apos;test&apos; //i18n(ph=&quot;map name&quot;) }}"/></source>
<target><x id="MAP NAME"/></target>
</trans-unit>`;
@ -183,7 +191,7 @@ const XLIFF_EXTRACTED = `
<note priority="1" from="meaning">different meaning</note>
</trans-unit>
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
<source><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="&lt;i&gt;"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="&lt;/i&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">9</context>
@ -193,146 +201,153 @@ const XLIFF_EXTRACTED = `
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="651d7249d3a225037eb66f3433d98ad4a86f0a22" datatype="html">
<source><x id="START_TAG_DIV" ctype="x-div" equiv-text="&lt;div&gt;"/>with <x id="START_TAG_DIV" ctype="x-div" equiv-text="&lt;div&gt;"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
<source>on not translatable node</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">13</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html">
<source>on translatable node</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">14</context>
<context context-type="linenumber">15</context>
</context-group>
</trans-unit>
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/>} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">19</context>
<context context-type="linenumber">20</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">36</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit id="ba9106fca6e4e33a9b703a8c1927e2d1794dd9ff" datatype="html">
<source>
<x id="ICU"/>
<x id="ICU" equiv-text="{sex, select, 0 {...} m {...} f {...}}"/>
</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">21</context>
<context context-type="linenumber">22</context>
</context-group>
</trans-unit>
<trans-unit id="7b7916d063ebaafcd2e9dcdf697b5e184fcb8099" datatype="html">
<source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">22</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="078b7089573c5f66a2f78dce0adaa55e6715beb1" datatype="html">
<source>
<x id="ICU"/>
<x id="ICU" equiv-text="{sexB, select, m {...} f {...}}"/>
</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">24</context>
<context context-type="linenumber">25</context>
</context-group>
</trans-unit>
<trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html">
<source>{VAR_SELECT, select, m {male} f {female} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">25</context>
<context context-type="linenumber">26</context>
</context-group>
</trans-unit>
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
<source><x id="INTERPOLATION"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">28</context>
</context-group>
</trans-unit>
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
<source>sex = <x id="INTERPOLATION"/></source>
<source><x id="INTERPOLATION" equiv-text="{{ &quot;count = &quot; + count }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">29</context>
</context-group>
</trans-unit>
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
<source><x id="CUSTOM_NAME"/></source>
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
<source>sex = <x id="INTERPOLATION" equiv-text="{{ sex }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
<source><x id="CUSTOM_NAME" equiv-text="{{ &quot;custom name&quot; //i18n(ph=&quot;CUSTOM_NAME&quot;) }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
<source>in a translatable section</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">35</context>
<context context-type="linenumber">36</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">53</context>
<context context-type="linenumber">54</context>
</context-group>
</trans-unit>
<trans-unit id="eee74a5be8a75881a4785905bd8302a71f7d9f75" datatype="html">
<source>
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
<x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
<x id="START_HEADING_LEVEL1" ctype="x-h1" equiv-text="&lt;h1&gt;"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1" equiv-text="&lt;/h1&gt;"/>
<x id="START_TAG_DIV" ctype="x-div" equiv-text="&lt;div&gt;"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/>
<x id="START_TAG_DIV_1" ctype="x-div" equiv-text="&lt;div&gt;"/><x id="ICU" equiv-text="{count, plural, =0 {...} =1 {...} =2 {...} other {...}}"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/>
</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">33</context>
<context context-type="linenumber">34</context>
</context-group>
</trans-unit>
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
<source>it <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/> work</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">39</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="i18n16" datatype="html">
<source>with an explicit ID</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">41</context>
</context-group>
</trans-unit>
<trans-unit id="i18n17" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">42</context>
</context-group>
</trans-unit>
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
<trans-unit id="i18n17" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/>} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">45</context>
<context context-type="linenumber">43</context>
</context-group>
</trans-unit>
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION" equiv-text="{{response.getItemsList().length}}"/> results} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">46</context>
</context-group>
<note priority="1" from="description">desc</note>
</trans-unit>
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
<source>foo<x id="START_LINK" ctype="x-a" equiv-text="&lt;a&gt;"/>bar<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">53</context>
<context context-type="linenumber">54</context>
</context-group>
</trans-unit>
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
<source><x id="MAP NAME"/></source>
<source><x id="MAP NAME" equiv-text="{{ &apos;test&apos; //i18n(ph=&quot;map name&quot;) }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">55</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>`;

View File

@ -26,8 +26,8 @@ export function main() {
beforeEach(async(() => {
TestBed.configureCompiler({
providers: [
{provide: ResourceLoader, useClass: SpyResourceLoader},
{provide: NgLocalization, useClass: FrLocalization},
SpyResourceLoader.PROVIDE,
FrLocalization.PROVIDE,
{provide: TRANSLATIONS, useValue: XTB},
{provide: TRANSLATIONS_FORMAT, useValue: 'xtb'},
]
@ -61,6 +61,7 @@ const XTB = `
<translation id="3707494640264351337">imbriqué</translation>
<translation id="5539162898278769904">imbriqué</translation>
<translation id="3780349238193953556"><ph name="START_ITALIC_TEXT"/>avec des espaces réservés<ph name="CLOSE_ITALIC_TEXT"/></translation>
<translation id="5415448997399451992"><ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph>avec <ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph>des espaces réservés<ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph> imbriqués<ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph></translation>
<translation id="5525133077318024839">sur des balises non traductibles</translation>
<translation id="8670732454866344690">sur des balises traductibles</translation>
<translation id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph name="START_BOLD_TEXT"/>beaucoup<ph name="CLOSE_BOLD_TEXT"/>}}</translation>
@ -90,29 +91,30 @@ const XMB = `<msg id="615790887472569365"><source>file.ts:3</source>i18n attribu
<msg id="3707494640264351337"><source>file.ts:5</source>nested</msg>
<msg id="5539162898278769904" meaning="different meaning"><source>file.ts:7</source>nested</msg>
<msg id="3780349238193953556"><source>file.ts:9</source><source>file.ts:10</source><ph name="START_ITALIC_TEXT"><ex>&lt;i&gt;</ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex>&lt;/i&gt;</ex></ph></msg>
<msg id="5525133077318024839"><source>file.ts:13</source>on not translatable node</msg>
<msg id="8670732454866344690"><source>file.ts:14</source>on translatable node</msg>
<msg id="4593805537723189714"><source>file.ts:19</source><source>file.ts:36</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="4360321700965841752"><source>file.ts:21,23</source>
<ph name="ICU"><ex>ICU</ex></ph>
<msg id="5415448997399451992"><source>file.ts:11</source><ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph>with <ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph>nested<ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph> placeholders<ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph></msg>
<msg id="5525133077318024839"><source>file.ts:14</source>on not translatable node</msg>
<msg id="8670732454866344690"><source>file.ts:15</source>on translatable node</msg>
<msg id="4593805537723189714"><source>file.ts:20</source><source>file.ts:37</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="4360321700965841752"><source>file.ts:22,24</source>
<ph name="ICU"><ex>{sex, select, 0 {...} m {...} f {...}}</ex></ph>
</msg>
<msg id="5460933846928880074"><source>file.ts:22</source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</msg>
<msg id="1746565782635215"><source>file.ts:24,26</source>
<ph name="ICU"><ex>ICU</ex></ph>
<msg id="5460933846928880074"><source>file.ts:23</source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</msg>
<msg id="1746565782635215"><source>file.ts:25,27</source>
<ph name="ICU"><ex>{sexB, select, m {...} f {...}}</ex></ph>
</msg>
<msg id="5868084092545682515"><source>file.ts:25</source>{VAR_SELECT, select, m {male} f {female} }</msg>
<msg id="4851788426695310455"><source>file.ts:28</source><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
<msg id="9013357158046221374"><source>file.ts:29</source>sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
<msg id="8324617391167353662"><source>file.ts:30</source><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
<msg id="7685649297917455806"><source>file.ts:35</source><source>file.ts:53</source>in a translatable section</msg>
<msg id="2387287228265107305"><source>file.ts:33,37</source>
<msg id="5868084092545682515"><source>file.ts:26</source>{VAR_SELECT, select, m {male} f {female} }</msg>
<msg id="4851788426695310455"><source>file.ts:29</source><ph name="INTERPOLATION"><ex>{{ &quot;count = &quot; + count }}</ex></ph></msg>
<msg id="9013357158046221374"><source>file.ts:30</source>sex = <ph name="INTERPOLATION"><ex>{{ sex }}</ex></ph></msg>
<msg id="8324617391167353662"><source>file.ts:31</source><ph name="CUSTOM_NAME"><ex>{{ &quot;custom name&quot; //i18n(ph=&quot;CUSTOM_NAME&quot;) }}</ex></ph></msg>
<msg id="7685649297917455806"><source>file.ts:36</source><source>file.ts:54</source>in a translatable section</msg>
<msg id="2387287228265107305"><source>file.ts:34,38</source>
<ph name="START_HEADING_LEVEL1"><ex>&lt;h1&gt;</ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex>&lt;/h1&gt;</ex></ph>
<ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
<ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"><ex>ICU</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
<ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"><ex>{count, plural, =0 {...} =1 {...} =2 {...} other {...}}</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
</msg>
<msg id="1491627405349178954"><source>file.ts:39</source>it <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> work</msg>
<msg id="i18n16"><source>file.ts:41</source>with an explicit ID</msg>
<msg id="i18n17"><source>file.ts:42</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="4085484936881858615" desc="desc"><source>file.ts:45,51</source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
<msg id="4035252431381981115"><source>file.ts:53</source>foo<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>bar<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></msg>
<msg id="5339604010413301604"><source>file.ts:55</source><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`;
<msg id="1491627405349178954"><source>file.ts:40</source>it <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> work</msg>
<msg id="i18n16"><source>file.ts:42</source>with an explicit ID</msg>
<msg id="i18n17"><source>file.ts:43</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="4085484936881858615" desc="desc"><source>file.ts:46,52</source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>{{response.getItemsList().length}}</ex></ph> results} }</msg>
<msg id="4035252431381981115"><source>file.ts:54</source>foo<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>bar<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></msg>
<msg id="5339604010413301604"><source>file.ts:56</source><ph name="MAP_NAME"><ex>{{ &apos;test&apos; //i18n(ph=&quot;map name&quot;) }}</ex></ph></msg>`;

View File

@ -24,7 +24,7 @@ const HTML = `
<p i18n="@@bar">foo</p>
<p i18n="ph names"><br><img><div></div></p>
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
<p i18n>Test: { count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} } =other {a lot}}</p>
<p i18n>multi
lines</p>
`;
@ -41,14 +41,14 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</context-group>
</trans-unit>
<trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html">
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> <x id="INTERPOLATION"/></source>
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&lt;b&gt;"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&lt;/b&gt;"/> <x id="INTERPOLATION" equiv-text="{{ interpolation}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html">
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source>
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="&lt;p&gt;"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="&lt;/p&gt;"/>} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">4</context>
@ -84,7 +84,7 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</context-group>
</trans-unit>
<trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html">
<source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
<source><x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br/&gt;"/><x id="TAG_IMG" ctype="image" equiv-text="&lt;img/&gt;"/><x id="START_TAG_DIV" ctype="x-div" equiv-text="&lt;div&gt;"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="&lt;/div&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">9</context>
@ -92,14 +92,21 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<note priority="1" from="description">ph names</note>
</trans-unit>
<trans-unit id="baz" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="&lt;p&gt;"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="&lt;/p&gt;"/>} } } }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<trans-unit id="52ffa620dcd76247a56d5331f34e73f340a43cdb" datatype="html">
<source>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="1503afd0ccc20ff01d5e2266a9157b7b342ba494" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="&lt;p&gt;"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="&lt;/p&gt;"/>} } } =other {a lot} }</source>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context>
@ -192,9 +199,17 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</target>
</trans-unit>
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</target>
<trans-unit id="52ffa620dcd76247a56d5331f34e73f340a43cdb" datatype="html">
<source>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></source>
<target>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="1503afd0ccc20ff01d5e2266a9157b7b342ba494" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } =other {a lot} }</source>
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } =other {beaucoup} }</target>
</trans-unit>
<trans-unit id="fcfa109b0e152d4c217dbc02530be0bcb8123ad1" datatype="html">
<source>multi
@ -253,9 +268,10 @@ export function main(): void {
'empty target': '',
'baz':
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}',
'0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4':
'52ffa620dcd76247a56d5331f34e73f340a43cdb': 'Test: <ph name="ICU"/>',
'1503afd0ccc20ff01d5e2266a9157b7b342ba494':
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' +
' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}',
' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}',
'fcfa109b0e152d4c217dbc02530be0bcb8123ad1': `multi
lignes`
});

View File

@ -21,7 +21,7 @@ export function main(): void {
<p i18n="m|d@@i">foo</p>
<p i18n="@@bar">foo</p>
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
<p i18n>Test: { count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} } =other {a lot}}</p>
<p i18n>multi
lines</p>`;
@ -48,13 +48,14 @@ lines</p>`;
<!ELEMENT ex (#PCDATA)>
]>
<messagebundle>
<msg id="7056919470098446707"><source>file.ts:3</source>translatable element <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
<msg id="7056919470098446707"><source>file.ts:3</source>translatable element <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> <ph name="INTERPOLATION"><ex>{{ interpolation}}</ex></ph></msg>
<msg id="2981514368455622387"><source>file.ts:4</source>{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} }</msg>
<msg id="7999024498831672133" desc="d" meaning="m"><source>file.ts:5</source>foo</msg>
<msg id="i" desc="d" meaning="m"><source>file.ts:6</source>foo</msg>
<msg id="bar"><source>file.ts:7</source>foo</msg>
<msg id="baz"><source>file.ts:8</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} } } }</msg>
<msg id="2015957479576096115"><source>file.ts:9</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} } } }</msg>
<msg id="6997386649824869937"><source>file.ts:9</source>Test: <ph name="ICU"><ex>{ count, plural, =0 {...} =other {...}}</ex></ph></msg>
<msg id="5229984852258993423"><source>file.ts:9</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} } } =other {a lot} }</msg>
<msg id="2340165783990709777"><source>file.ts:10,11</source>multi
lines</msg>
</messagebundle>

View File

@ -36,7 +36,7 @@ export function main() {
beforeEach(() => {
TestBed.configureCompiler(
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader}]});
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader, deps: []}]});
});
it('should throw when using a templateUrl that has not been compiled before', async(() => {
@ -68,7 +68,7 @@ export function main() {
beforeEach(() => {
TestBed.configureCompiler(
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader}]});
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader, deps: []}]});
});
it('should allow to use templateUrl components that have been loaded before', async(() => {
@ -88,10 +88,7 @@ export function main() {
let dirResolver: MockDirectiveResolver;
let injector: Injector;
beforeEach(() => {
TestBed.configureCompiler(
{providers: [{provide: ResourceLoader, useClass: SpyResourceLoader}]});
});
beforeEach(() => { TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); });
beforeEach(fakeAsync(inject(
[Compiler, ResourceLoader, DirectiveResolver, Injector],

View File

@ -11,5 +11,6 @@ import {ResourceLoader} from '@angular/compiler/src/resource_loader';
import {SpyObject} from '@angular/core/testing/src/testing_internal';
export class SpyResourceLoader extends SpyObject {
public static PROVIDE = {provide: ResourceLoader, useClass: SpyResourceLoader, deps: []};
constructor() { super(ResourceLoader); }
}

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