Compare commits
46 Commits
zone.js-0.
...
4.1.3
Author | SHA1 | Date | |
---|---|---|---|
7edc5e96f3 | |||
7b802faa96 | |||
6f039d7a3c | |||
0a82f7d69f | |||
b7f85813d5 | |||
a5bdbed306 | |||
883ca285de | |||
afb7540067 | |||
47df3d681f | |||
cd7fe31fbc | |||
4203b1bb1f | |||
055db5ad6a | |||
62a8618536 | |||
419fe0ca0d | |||
e46a65f8fd | |||
f0e1043a1d | |||
c1938f5af1 | |||
915eae50b4 | |||
68675e625a | |||
d0e1688514 | |||
ee6705af49 | |||
9218812011 | |||
ec77bf9fb0 | |||
fee5b2ad56 | |||
9c70a3cfb1 | |||
ec3b6e9603 | |||
63066f7697 | |||
5f6d0f2340 | |||
4a0e93b03b | |||
1f5dce2128 | |||
14e7e43ad8 | |||
54d4b893fd | |||
4670cf51cc | |||
dd4e501999 | |||
9124994849 | |||
4fbc61469f | |||
8a883f24f6 | |||
07cef367ac | |||
c060110695 | |||
93ff3166ab | |||
85a1b54c6e | |||
acf83b90bc | |||
f66e59ebe4 | |||
d932e724ab | |||
dcaa11a88b | |||
427d63a422 |
@ -32,7 +32,7 @@ env:
|
||||
global:
|
||||
# GITHUB_TOKEN_ANGULAR=<github token, a personal access token of the angular-builds account, account access in valentine>
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "rNqXoy2gqjbF5tBXlRBy+oiYntO3BtzcxZuEtlLMzNaTNzC4dyMOFub0GkzIPWwOzkARoEU9Kv+bC97fDVbCBUKeyzzEqxqddUKhzRxeaYjsefJ6XeTvBvDxwo7wDwyxZSuWdBeGAe4eARVHm7ypsd+AlvqxtzjyS27TK2BzdL4="
|
||||
- secure: "aCdHveZuY8AT4Jr1JoJB4LxZsnGWRe/KseZh1YXYe5UtufFCtTVHvUcLn0j2aLBF0KpdyS+hWf0i4np9jthKu2xPKriefoPgCMpisYeC0MFkwbmv+XlgkUbgkgVZMGiVyX7DCYXVahxIoOUjVMEDCbNiHTIrfEuyq24U3ok2tHc="
|
||||
# FIREBASE_TOKEN
|
||||
# This is needed for publishing builds to the "aio-staging" firebase site.
|
||||
# TODO(i): the token was generated using the iminar@google account, we should switch to a shared/role-base account.
|
||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -1,3 +1,50 @@
|
||||
<a name="4.1.3"></a>
|
||||
## [4.1.3](https://github.com/angular/angular/compare/4.1.2...4.1.3) (2017-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add typescript 2.3.2 typings test ([#16738](https://github.com/angular/angular/issues/16738)) ([a5bdbed](https://github.com/angular/angular/commit/a5bdbed)), closes [#16663](https://github.com/angular/angular/issues/16663)
|
||||
* **compiler-cli:** import routing module with forRoot ([#16438](https://github.com/angular/angular/issues/16438)) ([b7f8581](https://github.com/angular/angular/commit/b7f8581))
|
||||
* **platform-server:** wait for async app initializers to complete before removing server side styles ([#16712](https://github.com/angular/angular/issues/16712)) ([0a82f7d](https://github.com/angular/angular/commit/0a82f7d)), closes [#15716](https://github.com/angular/angular/issues/15716)
|
||||
* **router:** Wrap Promise-like instances in native Promises ([#16759](https://github.com/angular/angular/issues/16759)) ([883ca28](https://github.com/angular/angular/commit/883ca28))
|
||||
* **upgrade:** Prevent renaming of $inject property ([#16706](https://github.com/angular/angular/issues/16706)) ([afb7540](https://github.com/angular/angular/commit/afb7540))
|
||||
* **upgrade:** use quote to prevent ClossureCompiler obfuscating $event. ([#16724](https://github.com/angular/angular/issues/16724)) ([47df3d6](https://github.com/angular/angular/commit/47df3d6))
|
||||
|
||||
|
||||
|
||||
<a name="4.1.2"></a>
|
||||
## [4.1.2](https://github.com/angular/angular/compare/4.1.1...4.1.2) (2017-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** avoid a `...null` spread in extraction ([#16547](https://github.com/angular/angular/issues/16547)) ([d0e1688](https://github.com/angular/angular/commit/d0e1688))
|
||||
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([ee6705a](https://github.com/angular/angular/commit/ee6705a)), closes [#15578](https://github.com/angular/angular/issues/15578)
|
||||
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([9218812](https://github.com/angular/angular/commit/9218812)), closes [#14321](https://github.com/angular/angular/issues/14321)
|
||||
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** honor RequestArgs.search and RequestArgs.params map type ([63066f7](https://github.com/angular/angular/commit/63066f7)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
|
||||
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([ec3b6e9](https://github.com/angular/angular/commit/ec3b6e9)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
|
||||
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([915eae5](https://github.com/angular/angular/commit/915eae5)), closes [#16310](https://github.com/angular/angular/issues/16310)
|
||||
|
||||
|
||||
|
||||
<a name="4.1.1"></a>
|
||||
## [4.1.1](https://github.com/angular/angular/compare/4.1.0...4.1.1) (2017-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core**: strictNullCheck support. ([#16389](https://github.com/angular/angular/issues/16389)) ([#16389](https://github.com/angular/angular/issues/16389)) ([427d63a](https://github.com/angular/angular/commit/427d63a)), closes [#16357](https://github.com/angular/angular/issues/16357)
|
||||
* **core:** allow directives to inject the component’s `ChangeDetectorRef`. ([#16394](https://github.com/angular/angular/issues/16394)) ([f66e59e](https://github.com/angular/angular/commit/f66e59e)), closes [#12816](https://github.com/angular/angular/issues/12816)
|
||||
* **core:** allow to detach `OnPush` components ([#16394](https://github.com/angular/angular/issues/16394)) ([acf83b9](https://github.com/angular/angular/commit/acf83b9)), closes [#9720](https://github.com/angular/angular/issues/9720)
|
||||
* **core:** don’t set `ng-version` for dynamically created components ([#16394](https://github.com/angular/angular/issues/16394)) ([85a1b54](https://github.com/angular/angular/commit/85a1b54)), closes [#15880](https://github.com/angular/angular/issues/15880)
|
||||
* **core:** don’t stop change detection because of errors ([07cef36](https://github.com/angular/angular/commit/07cef36)), closes [#9531](https://github.com/angular/angular/issues/9531) [#2413](https://github.com/angular/angular/issues/2413) [#15925](https://github.com/angular/angular/issues/15925)
|
||||
* **language-service:** remove asserts for non-null expressions ([#16422](https://github.com/angular/angular/issues/16422)) ([c060110](https://github.com/angular/angular/commit/c060110))
|
||||
* **upgrade:** initialize all inputs in time for `ngOnChanges()` ([dd4e501](https://github.com/angular/angular/commit/dd4e501)), closes [#16212](https://github.com/angular/angular/issues/16212)
|
||||
|
||||
|
||||
|
||||
<a name="4.1.0"></a>
|
||||
# [4.1.0](https://github.com/angular/angular/compare/4.1.0-rc.0...4.1.0) (2017-04-26)
|
||||
|
||||
|
@ -501,7 +501,7 @@ those callbacks like this:
|
||||
|
||||
|
||||
|
||||
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
|
||||
The callbacks receive an `AnimationEvent` that contains useful properties such as
|
||||
`fromState`, `toState` and `totalTime`.
|
||||
|
||||
Those callbacks will fire whether or not an animation is picked up.
|
@ -19,6 +19,12 @@ I'm sorry but we don't understand the problem you are reporting.
|
||||
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
## Angular: Plunker Needed (v1)
|
||||
```
|
||||
I'm sorry but reported issues require a plunker reproducing the problem.
|
||||
|
||||
If this issue persists, please create a plunker using this template and describe the difference between the expected and current behavior and create a new issue: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
## Angular: Duplicate (v1)
|
||||
```
|
||||
@ -38,6 +44,10 @@ If the problem still persists, please file a new issue and ensure you provide al
|
||||
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
|
||||
```
|
||||
|
||||
## Angular: Behaving as Expected (v1)
|
||||
```
|
||||
It appears this behaves as expected. If you still feel there is an issue, please provide further details in a new issue.
|
||||
```
|
||||
|
||||
## Angular: Non-reproducible (v1)
|
||||
```
|
||||
|
@ -29,9 +29,10 @@ function loadTask(fileName, taskName) {
|
||||
|
||||
gulp.task('format:enforce', loadTask('format', 'enforce'));
|
||||
gulp.task('format', loadTask('format', 'format'));
|
||||
gulp.task('build.sh', loadTask('build'));
|
||||
gulp.task('build.sh', loadTask('build', 'all'));
|
||||
gulp.task('build.sh:no-bundle', loadTask('build', 'no-bundle'));
|
||||
gulp.task('public-api:enforce', loadTask('public-api', 'enforce'));
|
||||
gulp.task('public-api:update', ['build.sh'], loadTask('public-api', 'update'));
|
||||
gulp.task('public-api:update', ['build.sh:no-bundle'], loadTask('public-api', 'update'));
|
||||
gulp.task('lint', ['format:enforce', 'validate-commit-messages', 'tslint']);
|
||||
gulp.task('tslint', ['tools:build'], loadTask('lint'));
|
||||
gulp.task('validate-commit-messages', loadTask('validate-commit-message'));
|
||||
|
1
integration/.gitignore
vendored
1
integration/.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
/rxjs/
|
||||
built/
|
||||
dist/
|
||||
vendor/
|
||||
|
@ -32,13 +32,7 @@ Angular's `node_modules` is installed.
|
||||
|
||||
## Running integration tests
|
||||
|
||||
The first time you run the tests, you'll need some setup:
|
||||
|
||||
```shell
|
||||
$ ./integration/build_rxjs_es6.sh
|
||||
```
|
||||
|
||||
Now you can iterate on the tests by keeping the dist folder up-to-date.
|
||||
You can iterate on the tests by keeping the dist folder up-to-date.
|
||||
See the `package.json` of the test(s) you're debugging, to see which dist/ folders they install from.
|
||||
Then run the right `tsc --watch` command to keep those dist folders up-to-date, for example:
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script builds rxjs from source, with an ES6 target, and with tsickle turned on.
|
||||
# We need to do this until we work with the RxJS team to get a distribution that works
|
||||
# with Closure Compiler.
|
||||
# Note that in nodejs, we still run the standard RxJS distribution. This one is only
|
||||
# used for the bundle that targets a browser runtime.
|
||||
# TODO(alexeagle): discuss with Jay Phelps once we have a recommendation
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
rm -rf rxjs
|
||||
git clone https://github.com/ReactiveX/rxjs.git --depth=200
|
||||
git -C rxjs/ checkout 5.0.3
|
||||
cp rxjs.tsconfig.json rxjs/
|
||||
TSC="node --max-old-space-size=3000 ../dist/tools/@angular/tsc-wrapped/src/main"
|
||||
$TSC -p rxjs/rxjs.tsconfig.json
|
@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Build a folder using angular ES6 and the closure compiler
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
# The ES6 distro we built for rxjs works only in the browser, not in nodejs.
|
||||
# Since we installed rxjs in node_modules for ngc to use, we have to point
|
||||
# to the alternate distro when compiling with closure.
|
||||
rm -rf vendor
|
||||
mkdir vendor
|
||||
cp -pr ../rxjs/dist/es6 vendor/rxjs
|
||||
|
||||
CLOSURE_ARGS=(
|
||||
"--language_in=ES6_STRICT"
|
||||
"--language_out=ES5"
|
||||
"--compilation_level=ADVANCED_OPTIMIZATIONS"
|
||||
"--warning_level=QUIET"
|
||||
"--js_output_file=dist/bundle.js"
|
||||
"--create_source_map=%outname%.map"
|
||||
"--variable_renaming_report=dist/variable_renaming_report"
|
||||
"--property_renaming_report=dist/property_renaming_report"
|
||||
|
||||
# Don't include ES6 polyfills
|
||||
"--rewrite_polyfills=false"
|
||||
|
||||
# List of path prefixes to be removed from ES6 & CommonJS modules.
|
||||
"--js_module_root=node_modules/@angular/core"
|
||||
"--js_module_root=node_modules/@angular/common"
|
||||
"--js_module_root=node_modules/@angular/compiler"
|
||||
"--js_module_root=node_modules/@angular/platform-browser"
|
||||
"--js_module_root=vendor"
|
||||
|
||||
# Uncomment for easier debugging
|
||||
# "--formatting=PRETTY_PRINT"
|
||||
|
||||
node_modules/@angular/core/src/testability/testability.externs.js
|
||||
node_modules/zone.js/dist/zone.js
|
||||
$(find -L vendor/rxjs -name *.js)
|
||||
node_modules/@angular/core/@angular/core.js
|
||||
node_modules/@angular/common/@angular/common.js
|
||||
node_modules/@angular/compiler/@angular/compiler.js
|
||||
node_modules/@angular/platform-browser/@angular/platform-browser.js
|
||||
"built/src/*.js"
|
||||
"--entry_point=./built/src/main"
|
||||
)
|
||||
|
||||
java -jar node_modules/google-closure-compiler/compiler.jar $(echo ${CLOSURE_ARGS[*]})
|
||||
# gzip on Travis doesn't have --keep option so copy the original file first
|
||||
cp dist/bundle.js dist/bundle.tmp
|
||||
gzip -f dist/bundle.js
|
||||
mv dist/bundle.tmp dist/bundle.js
|
||||
ls -alH dist/bundle*
|
30
integration/hello_world__closure/closure.conf
Normal file
30
integration/hello_world__closure/closure.conf
Normal file
@ -0,0 +1,30 @@
|
||||
--compilation_level=ADVANCED_OPTIMIZATIONS
|
||||
--language_out=ES5
|
||||
--js_output_file=dist/bundle.js
|
||||
--output_manifest=dist/manifest.MF
|
||||
--variable_renaming_report=dist/variable_renaming_report
|
||||
--property_renaming_report=dist/property_renaming_report
|
||||
--create_source_map=%outname%.map
|
||||
|
||||
--warning_level=QUIET
|
||||
--dependency_mode=STRICT
|
||||
--rewrite_polyfills=false
|
||||
|
||||
node_modules/zone.js/dist/zone_externs.js
|
||||
|
||||
--js node_modules/rxjs/**.js
|
||||
--process_common_js_modules
|
||||
--module_resolution=node
|
||||
|
||||
node_modules/@angular/core/@angular/core.js
|
||||
--js_module_root=node_modules/@angular/core
|
||||
node_modules/@angular/core/src/testability/testability.externs.js
|
||||
|
||||
node_modules/@angular/common/@angular/common.js
|
||||
--js_module_root=node_modules/@angular/common
|
||||
|
||||
node_modules/@angular/platform-browser/@angular/platform-browser.js
|
||||
--js_module_root=node_modules/@angular/platform-browser
|
||||
|
||||
--js built/**.js
|
||||
--entry_point=built/src/main
|
@ -5,7 +5,8 @@
|
||||
"server": {
|
||||
"baseDir": "src",
|
||||
"routes": {
|
||||
"/dist": "dist"
|
||||
"/dist": "dist",
|
||||
"/node_modules": "node_modules"
|
||||
},
|
||||
"middleware": {
|
||||
"0": null
|
||||
|
@ -11,19 +11,20 @@
|
||||
"@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",
|
||||
"google-closure-compiler": "20161201.0.0",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"google-closure-compiler": "20170409.0.0",
|
||||
"rxjs": "5.3.1",
|
||||
"typescript": "2.1.6",
|
||||
"zone.js": "0.7.6"
|
||||
"zone.js": "0.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "2.5.41",
|
||||
"concurrently": "3.1.0",
|
||||
"concurrently": "3.4.0",
|
||||
"lite-server": "2.2.2",
|
||||
"protractor": "file:../../node_modules/protractor"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "ngc && ./bundle.sh && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first",
|
||||
"closure": "java -jar node_modules/google-closure-compiler/compiler.jar --flagfile closure.conf",
|
||||
"test": "ngc && yarn run closure && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first",
|
||||
"serve": "lite-server -c e2e/browser.config.json",
|
||||
"preprotractor": "tsc -p e2e",
|
||||
"protractor": "protractor e2e/protractor.config.js"
|
||||
|
@ -1,14 +1,18 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello World</title>
|
||||
<base href="/">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<hello-world-app>Loading...</hello-world-app>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.min.js"></script>
|
||||
<script src="dist/bundle.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -24,7 +24,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "2.5.41",
|
||||
"concurrently": "3.1.0",
|
||||
"concurrently": "3.4.0",
|
||||
"lite-server": "2.2.2",
|
||||
"protractor": "file:../../node_modules/protractor"
|
||||
}
|
||||
|
@ -10,6 +10,6 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"typescript": "2.3.0-dev.20170223"
|
||||
"typescript": "2.3.0"
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
typescript@2.3.0-dev.20170223:
|
||||
version "2.3.0-dev.20170223"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.0-dev.20170223.tgz#286494c36625ea2eb26f963ed205cd9ca5c41447"
|
||||
typescript@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.0.tgz#2e63e09284392bc8158a2444c33e2093795c0418"
|
||||
|
@ -4,12 +4,6 @@ set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
if [ ! -d "rxjs/dist/es6" ]; then
|
||||
echo "You must run build the ES2015 version of RxJS for some tests:"
|
||||
echo "./integration/build_rxjs_es6.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Workaround https://github.com/yarnpkg/yarn/issues/2165
|
||||
# Yarn will cache file://dist URIs and not update Angular code
|
||||
readonly cache=.yarn_local_cache
|
||||
@ -20,7 +14,7 @@ rm_cache
|
||||
mkdir $cache
|
||||
trap rm_cache EXIT
|
||||
|
||||
for testDir in $(ls | grep -v rxjs | grep -v node_modules) ; do
|
||||
for testDir in $(ls | grep -v node_modules) ; do
|
||||
[[ -d "$testDir" ]] || continue
|
||||
echo "#################################"
|
||||
echo "Running integration test $testDir"
|
||||
|
@ -1,42 +0,0 @@
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"skipMetadataEmit": true,
|
||||
"annotationsAs": "static fields",
|
||||
"annotateForClosureCompiler": true
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
"types": [],
|
||||
/**
|
||||
* Remaining options are copied from
|
||||
* https://github.com/ReactiveX/rxjs/blob/cba74135810a8e6bbe0b3c7732e8544b0869589e/tsconfig.json
|
||||
* TODO(alexeagle): use "extends" instead when Angular is on TS 2.1
|
||||
*/
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "es2015",
|
||||
"target": "es6",
|
||||
"outDir": "dist/es6",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es2015.iterable",
|
||||
"es2015.collection",
|
||||
"es2015.promise",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"formatCodeOptions": {
|
||||
"indentSize": 2,
|
||||
"tabSize": 2
|
||||
},
|
||||
"files": [
|
||||
"src/Rx.ts"
|
||||
]
|
||||
}
|
@ -8,17 +8,18 @@
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import * as compilerTesting from '@angular/compiler/testing';
|
||||
import * as coreTesting from '@angular/core';
|
||||
import * as core from '@angular/core/testing';
|
||||
import * as httpTesting from '@angular/http';
|
||||
import * as http from '@angular/http/testing';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser';
|
||||
import * as core from '@angular/core';
|
||||
import * as coreTesting from '@angular/core/testing';
|
||||
import * as forms from '@angular/forms';
|
||||
import * as http from '@angular/http';
|
||||
import * as httpTesting from '@angular/http/testing';
|
||||
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
|
||||
import * as platformBrowser from '@angular/platform-browser/testing';
|
||||
import * as platformServerTesting from '@angular/platform-server';
|
||||
import * as platformServer from '@angular/platform-server/testing';
|
||||
import * as routerTesting from '@angular/router';
|
||||
import * as router from '@angular/router/testing';
|
||||
import * as platformBrowser from '@angular/platform-browser';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser/testing';
|
||||
import * as platformServer from '@angular/platform-server';
|
||||
import * as platformServerTesting from '@angular/platform-server/testing';
|
||||
import * as router from '@angular/router';
|
||||
import * as routerTesting from '@angular/router/testing';
|
||||
import * as upgrade from '@angular/upgrade';
|
||||
|
||||
export default {
|
||||
@ -26,6 +27,7 @@ export default {
|
||||
compilerTesting,
|
||||
core,
|
||||
coreTesting,
|
||||
forms,
|
||||
http,
|
||||
httpTesting,
|
||||
platformBrowser,
|
||||
|
@ -9,6 +9,7 @@
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/http": "file:../../dist/packages-dist/http",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
|
@ -9,8 +9,7 @@
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom", "es2015.collection", "es2015.iterable", "es2015.promise"],
|
||||
"types": [],
|
||||
// TODO(i): strictNullChecks should turned on but are temporarily disabled due to #15432
|
||||
"strictNullChecks": false
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"include-all.ts",
|
||||
|
@ -8,17 +8,18 @@
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import * as compilerTesting from '@angular/compiler/testing';
|
||||
import * as coreTesting from '@angular/core';
|
||||
import * as core from '@angular/core/testing';
|
||||
import * as httpTesting from '@angular/http';
|
||||
import * as http from '@angular/http/testing';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser';
|
||||
import * as core from '@angular/core';
|
||||
import * as coreTesting from '@angular/core/testing';
|
||||
import * as forms from '@angular/forms';
|
||||
import * as http from '@angular/http';
|
||||
import * as httpTesting from '@angular/http/testing';
|
||||
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
|
||||
import * as platformBrowser from '@angular/platform-browser/testing';
|
||||
import * as platformServerTesting from '@angular/platform-server';
|
||||
import * as platformServer from '@angular/platform-server/testing';
|
||||
import * as routerTesting from '@angular/router';
|
||||
import * as router from '@angular/router/testing';
|
||||
import * as platformBrowser from '@angular/platform-browser';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser/testing';
|
||||
import * as platformServer from '@angular/platform-server';
|
||||
import * as platformServerTesting from '@angular/platform-server/testing';
|
||||
import * as router from '@angular/router';
|
||||
import * as routerTesting from '@angular/router/testing';
|
||||
import * as upgrade from '@angular/upgrade';
|
||||
|
||||
export default {
|
||||
@ -26,6 +27,7 @@ export default {
|
||||
compilerTesting,
|
||||
core,
|
||||
coreTesting,
|
||||
forms,
|
||||
http,
|
||||
httpTesting,
|
||||
platformBrowser,
|
||||
|
@ -9,6 +9,7 @@
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/http": "file:../../dist/packages-dist/http",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
|
@ -15,8 +15,7 @@
|
||||
"es2015.promise"
|
||||
],
|
||||
"types": [],
|
||||
// TODO(i): strictNullChecks should turned on but are temporarily disabled due to #15432
|
||||
"strictNullChecks": false
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"include-all.ts",
|
||||
|
41
integration/typings_test_ts23/include-all.ts
Normal file
41
integration/typings_test_ts23/include-all.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import * as compilerTesting from '@angular/compiler/testing';
|
||||
import * as core from '@angular/core';
|
||||
import * as coreTesting from '@angular/core/testing';
|
||||
import * as forms from '@angular/forms';
|
||||
import * as http from '@angular/http';
|
||||
import * as httpTesting from '@angular/http/testing';
|
||||
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
|
||||
import * as platformBrowser from '@angular/platform-browser';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser/testing';
|
||||
import * as platformServer from '@angular/platform-server';
|
||||
import * as platformServerTesting from '@angular/platform-server/testing';
|
||||
import * as router from '@angular/router';
|
||||
import * as routerTesting from '@angular/router/testing';
|
||||
import * as upgrade from '@angular/upgrade';
|
||||
|
||||
export default {
|
||||
compiler,
|
||||
compilerTesting,
|
||||
core,
|
||||
coreTesting,
|
||||
forms,
|
||||
http,
|
||||
httpTesting,
|
||||
platformBrowser,
|
||||
platformBrowserTesting,
|
||||
platformBrowserDynamic,
|
||||
platformServer,
|
||||
platformServerTesting,
|
||||
router,
|
||||
routerTesting,
|
||||
upgrade
|
||||
};
|
28
integration/typings_test_ts23/package.json
Normal file
28
integration/typings_test_ts23/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "angular-integration",
|
||||
"description": "Assert that users with TypeScript 2.2 can type-check an Angular application",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/http": "file:../../dist/packages-dist/http",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"typescript": "2.3.2",
|
||||
"zone.js": "0.7.6"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tsc"
|
||||
}
|
||||
}
|
24
integration/typings_test_ts23/tsconfig.json
Normal file
24
integration/typings_test_ts23/tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../dist/typing-test/",
|
||||
"rootDir": ".",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection",
|
||||
"es2015.iterable",
|
||||
"es2015.promise"
|
||||
],
|
||||
"types": [],
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"include-all.ts",
|
||||
"node_modules/@types/jasmine/index.d.ts"
|
||||
]
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {NgIf} from '@angular/common';
|
||||
import {ComponentFactory, ComponentFactoryResolver, ComponentRef, Injector, NgModuleRef, RendererFactory2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {ComponentFactory, ComponentFactoryResolver, ComponentRef, ErrorHandler, Injector, NgModuleRef, RendererFactory2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {ArgumentType, BindingFlags, NodeFlags, ViewDefinition, ViewFlags, anchorDef, createComponentFactory, directiveDef, elementDef, initServicesIfNeeded, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||
import {DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||
@ -108,6 +108,7 @@ export class AppModule implements Injector, NgModuleRef<any> {
|
||||
case Sanitizer:
|
||||
return this.sanitizer;
|
||||
case RootRenderer:
|
||||
case ErrorHandler:
|
||||
return null;
|
||||
case NgModuleRef:
|
||||
return this;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.1.0",
|
||||
"version": "4.1.3",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -120,7 +120,7 @@ export interface AnimationGroupMetadata extends AnimationMetadata { steps: Anima
|
||||
/**
|
||||
* `trigger` is an animation-specific function that is designed to be used inside of Angular's
|
||||
animation DSL language. If this information is new, please navigate to the {@link
|
||||
Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
Component#animations component animations metadata page} to gain a better understanding of
|
||||
how animations in Angular are used.
|
||||
*
|
||||
* `trigger` Creates an animation trigger which will a list of {@link state state} and {@link
|
||||
@ -128,7 +128,7 @@ export interface AnimationGroupMetadata extends AnimationMetadata { steps: Anima
|
||||
changes.
|
||||
*
|
||||
* Triggers are registered within the component annotation data under the {@link
|
||||
Component#animations-anchor animations section}. An animation trigger can be placed on an element
|
||||
Component#animations animations section}. An animation trigger can be placed on an element
|
||||
within a template by referencing the name of the trigger followed by the expression value that the
|
||||
trigger is bound to (in the form of `[@triggerName]="expression"`.
|
||||
*
|
||||
@ -175,7 +175,7 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
|
||||
/**
|
||||
* `animate` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `animate` specifies an animation step that will apply the provided `styles` data for a given
|
||||
@ -226,7 +226,7 @@ export function animate(
|
||||
/**
|
||||
* `group` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `group` specifies a list of animation steps that are all run in parallel. Grouped animations are
|
||||
@ -261,7 +261,7 @@ export function group(steps: AnimationMetadata[]): AnimationGroupMetadata {
|
||||
/**
|
||||
* `sequence` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `sequence` Specifies a list of animation steps that are run one by one. (`sequence` is used by
|
||||
@ -299,7 +299,7 @@ export function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata
|
||||
/**
|
||||
* `style` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `style` declares a key/value object containing CSS properties/styles that can then be used for
|
||||
@ -347,7 +347,7 @@ export function style(
|
||||
/**
|
||||
* `state` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `state` declares an animation state within the given trigger. When a state is active within a
|
||||
@ -399,7 +399,7 @@ export function state(name: string, styles: AnimationStyleMetadata): AnimationSt
|
||||
/**
|
||||
* `keyframes` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `keyframes` specifies a collection of {@link style style} entries each optionally characterized
|
||||
@ -448,7 +448,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
/**
|
||||
* `transition` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* Component#animations component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `transition` declares the {@link sequence sequence of animation steps} that will be run when the
|
||||
|
@ -22,9 +22,6 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
|
||||
* * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for
|
||||
* the Component. Defaults to the injector of the current view container.
|
||||
*
|
||||
* * `ngComponentOutletProviders`: Optional injectable objects ({@link Provider}) that are visible
|
||||
* to the component.
|
||||
*
|
||||
* * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content
|
||||
* section of the component, if exists.
|
||||
*
|
||||
@ -52,7 +49,7 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
|
||||
* ngModuleFactory: moduleFactory;">
|
||||
* </ng-container>
|
||||
* ```
|
||||
* # Example
|
||||
* ## Example
|
||||
*
|
||||
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
|
||||
*
|
||||
|
@ -17,13 +17,13 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||
* - `then` template is the inline template of `ngIf` unless bound to a different value.
|
||||
* - `else` template is blank unless it is bound.
|
||||
*
|
||||
* # Most common usage
|
||||
* ## Most common usage
|
||||
*
|
||||
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
|
||||
* seen in this example:
|
||||
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
|
||||
*
|
||||
* # Showing an alternative template using `else`
|
||||
* ## Showing an alternative template using `else`
|
||||
*
|
||||
* If it is necessary to display a template when the `expression` is falsy use the `else` template
|
||||
* binding as shown. Note that the `else` binding points to a `<ng-template>` labeled `#elseBlock`.
|
||||
@ -32,7 +32,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||
*
|
||||
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
|
||||
*
|
||||
* # Using non-inlined `then` template
|
||||
* ## Using non-inlined `then` template
|
||||
*
|
||||
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
|
||||
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
|
||||
@ -40,7 +40,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||
*
|
||||
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
|
||||
*
|
||||
* # Storing conditional result in a variable
|
||||
* ## Storing conditional result in a variable
|
||||
*
|
||||
* A common pattern is that we need to show a set of properties from the same object. If the
|
||||
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against
|
||||
|
@ -26,7 +26,7 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* # Example
|
||||
* ## Example
|
||||
*
|
||||
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
|
||||
*
|
||||
|
@ -14,22 +14,18 @@ import {InjectionToken} from '@angular/core';
|
||||
* `PlatformLocation` encapsulates all calls to DOM apis, which allows the Router to be platform
|
||||
* agnostic.
|
||||
* This means that we can have different implementation of `PlatformLocation` for the different
|
||||
* platforms
|
||||
* that angular supports. For example, the default `PlatformLocation` is {@link
|
||||
* BrowserPlatformLocation},
|
||||
* however when you run your app in a WebWorker you use {@link WebWorkerPlatformLocation}.
|
||||
* platforms that angular supports. For example, `@angular/platform-browser` provides an
|
||||
* implementation specific to the browser environment, while `@angular/platform-webworker` provides
|
||||
* one suitable for use with web workers.
|
||||
*
|
||||
* The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy}
|
||||
* when
|
||||
* they need to interact with the DOM apis like pushState, popState, etc...
|
||||
* when they need to interact with the DOM apis like pushState, popState, etc...
|
||||
*
|
||||
* {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly
|
||||
* by
|
||||
* the {@link Router} in order to navigate between routes. Since all interactions between {@link
|
||||
* by the {@link Router} in order to navigate between routes. Since all interactions between {@link
|
||||
* Router} /
|
||||
* {@link Location} / {@link LocationStrategy} and DOM apis flow through the `PlatformLocation`
|
||||
* class
|
||||
* they are all platform independent.
|
||||
* class they are all platform independent.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "4.1.0",
|
||||
"@angular/tsc-wrapped": "4.1.3",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -149,8 +149,13 @@ function _extractLazyRoutesFromStaticModule(
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const importedSymbols = ((moduleMetadata.imports || []) as any[])
|
||||
.filter(i => i instanceof StaticSymbol) as StaticSymbol[];
|
||||
const importedSymbols =
|
||||
((moduleMetadata.imports || []) as any[])
|
||||
.filter(i => i instanceof StaticSymbol || i.ngModule instanceof StaticSymbol)
|
||||
.map(i => {
|
||||
if (i instanceof StaticSymbol) return i;
|
||||
return i.ngModule;
|
||||
}) as StaticSymbol[];
|
||||
|
||||
return importedSymbols
|
||||
.reduce(
|
||||
|
@ -26,7 +26,7 @@ export class MessageBundle {
|
||||
private _implicitAttrs: {[k: string]: string[]}, private _locale: string|null = null) {}
|
||||
|
||||
updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig):
|
||||
ParseError[]|null {
|
||||
ParseError[] {
|
||||
const htmlParserResult = this._htmlParser.parse(html, url, true, interpolationConfig);
|
||||
|
||||
if (htmlParserResult.errors.length) {
|
||||
@ -41,7 +41,7 @@ export class MessageBundle {
|
||||
}
|
||||
|
||||
this._messages.push(...i18nParserResult.messages);
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
// Return the message in the internal format
|
||||
|
@ -6,8 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
|
||||
import {RenderComponentType, ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
|
||||
import {GeneratedFile} from '@angular/compiler';
|
||||
import {NodeFlags} from '@angular/core/src/view/index';
|
||||
import {async} from '@angular/core/testing';
|
||||
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
|
||||
@ -15,60 +14,16 @@ import * as ts from 'typescript';
|
||||
|
||||
import {extractSourceMap, originalPositionFor} from '../output/source_map_util';
|
||||
|
||||
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, MockMetadataBundlerHost, settings} from './test_util';
|
||||
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
const minCoreIndex = `
|
||||
export * from './src/application_module';
|
||||
export * from './src/change_detection';
|
||||
export * from './src/metadata';
|
||||
export * from './src/di/metadata';
|
||||
export * from './src/di/injector';
|
||||
export * from './src/di/injection_token';
|
||||
export * from './src/linker';
|
||||
export * from './src/render';
|
||||
export * from './src/codegen_private_exports';
|
||||
`;
|
||||
import {EmittingCompilerHost, MockData, MockDirectory, MockMetadataBundlerHost, arrayToMockDir, arrayToMockMap, compile, settings, setup, toMockFileArray} from './test_util';
|
||||
|
||||
describe('compiler (unbundled Angular)', () => {
|
||||
let angularFiles: Map<string, string>;
|
||||
|
||||
beforeAll(() => {
|
||||
const emittingHost = new EmittingCompilerHost([], {emitMetadata: true});
|
||||
emittingHost.addScript('@angular/core/index.ts', minCoreIndex);
|
||||
const emittingProgram = ts.createProgram(emittingHost.scripts, settings, emittingHost);
|
||||
emittingProgram.emit();
|
||||
|
||||
angularFiles = emittingHost.written;
|
||||
});
|
||||
|
||||
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||
let angularFiles = setup();
|
||||
|
||||
describe('Quickstart', () => {
|
||||
let host: MockCompilerHost;
|
||||
let aotHost: MockAotCompilerHost;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockCompilerHost(QUICKSTART, FILES, angularFiles);
|
||||
aotHost = new MockAotCompilerHost(host);
|
||||
});
|
||||
|
||||
it('should compile',
|
||||
async(() => compile(host, aotHost, expectNoDiagnostics).then(generatedFiles => {
|
||||
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
})));
|
||||
|
||||
it('should compile using summaries',
|
||||
async(() => summaryCompile(host, aotHost).then(generatedFiles => {
|
||||
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
|
||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||
})));
|
||||
});
|
||||
|
||||
@ -97,17 +52,11 @@ describe('compiler (unbundled Angular)', () => {
|
||||
});
|
||||
|
||||
function compileApp(): Promise<GeneratedFile> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const host = new MockCompilerHost(['/app/app.module.ts'], rootDir, angularFiles);
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
let result: GeneratedFile[];
|
||||
let error: Error;
|
||||
resolve(compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics)
|
||||
return compile([rootDir, angularFiles])
|
||||
.then(
|
||||
(files) => files.find(
|
||||
genFile => genFile.srcFileUrl === componentPath &&
|
||||
genFile.genFileUrl.endsWith('.ts'))));
|
||||
});
|
||||
({genFiles}) => {return genFiles.find(
|
||||
genFile =>
|
||||
genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'))});
|
||||
}
|
||||
|
||||
function findLineAndColumn(
|
||||
@ -247,7 +196,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
describe('errors', () => {
|
||||
it('should only warn if not all arguments of an @Injectable class can be resolved',
|
||||
async(() => {
|
||||
const FILES: MockData = {
|
||||
const FILES: MockDirectory = {
|
||||
app: {
|
||||
'app.ts': `
|
||||
import {Injectable} from '@angular/core';
|
||||
@ -259,10 +208,8 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
const warnSpy = spyOn(console, 'warn');
|
||||
compile(host, aotHost, expectNoDiagnostics).then(() => {
|
||||
compile([FILES, angularFiles]).then(() => {
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
|
||||
});
|
||||
@ -271,7 +218,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
});
|
||||
|
||||
it('should add the preamble to generated files', async(() => {
|
||||
const FILES: MockData = {
|
||||
const FILES: MockDirectory = {
|
||||
app: {
|
||||
'app.ts': `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
@ -284,13 +231,10 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
const genFilePreamble = '/* Hello world! */';
|
||||
compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics, {genFilePreamble})
|
||||
.then((generatedFiles) => {
|
||||
const genFile = generatedFiles.find(
|
||||
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
||||
compile([FILES, angularFiles], {genFilePreamble}).then(({genFiles}) => {
|
||||
const genFile =
|
||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
||||
expect(genFile.source.startsWith(genFilePreamble)).toBe(true);
|
||||
});
|
||||
|
||||
@ -299,7 +243,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
describe('ComponentFactories', () => {
|
||||
it('should include inputs, outputs and ng-content selectors in the component factory',
|
||||
async(() => {
|
||||
const FILES: MockData = {
|
||||
const FILES: MockDirectory = {
|
||||
app: {
|
||||
'app.ts': `
|
||||
import {Component, NgModule, Input, Output} from '@angular/core';
|
||||
@ -323,11 +267,8 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
let generatedFiles: GeneratedFile[];
|
||||
compile(host, aotHost, expectNoDiagnostics).then((generatedFiles) => {
|
||||
const genFile = generatedFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
||||
compile([FILES, angularFiles]).then(({genFiles}) => {
|
||||
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
||||
const createComponentFactoryCall =
|
||||
/ɵccf\([^)]*\)/m.exec(genFile.source) ![0].replace(/\s*/g, '');
|
||||
// selector
|
||||
@ -345,7 +286,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
describe('generated templates', () => {
|
||||
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit',
|
||||
async(() => {
|
||||
const FILES: MockData = {
|
||||
const FILES: MockDirectory = {
|
||||
app: {
|
||||
'app.ts': `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
@ -358,12 +299,8 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
const genFilePreamble = '/* Hello world! */';
|
||||
compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics, {genFilePreamble})
|
||||
.then((generatedFiles) => {
|
||||
const genFile = generatedFiles.find(
|
||||
compile([FILES, angularFiles]).then(({genFiles}) => {
|
||||
const genFile = genFiles.find(
|
||||
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
||||
expect(genFile.source).not.toContain('check(');
|
||||
});
|
||||
@ -372,23 +309,6 @@ describe('compiler (unbundled Angular)', () => {
|
||||
});
|
||||
|
||||
describe('inheritance with summaries', () => {
|
||||
function compileWithSummaries(
|
||||
libInput: MockData, appInput: MockData): Promise<GeneratedFile[]> {
|
||||
const libHost = new MockCompilerHost(['/lib/base.ts'], libInput, angularFiles);
|
||||
const libAotHost = new MockAotCompilerHost(libHost);
|
||||
libAotHost.tsFilesOnly();
|
||||
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
|
||||
const appAotHost = new MockAotCompilerHost(appHost);
|
||||
appAotHost.tsFilesOnly();
|
||||
return compile(libHost, libAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
|
||||
.then(() => {
|
||||
libHost.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
|
||||
libHost.overrides.forEach((value, key) => appHost.override(key, value));
|
||||
|
||||
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
});
|
||||
}
|
||||
|
||||
function compileParentAndChild(
|
||||
{parentClassDecorator, parentModuleDecorator, childClassDecorator, childModuleDecorator}: {
|
||||
parentClassDecorator: string,
|
||||
@ -396,7 +316,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
childClassDecorator: string,
|
||||
childModuleDecorator: string
|
||||
}) {
|
||||
const libInput: MockData = {
|
||||
const libInput: MockDirectory = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
|
||||
@ -409,7 +329,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockData = {
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
|
||||
@ -424,13 +344,14 @@ describe('compiler (unbundled Angular)', () => {
|
||||
}
|
||||
};
|
||||
|
||||
return compileWithSummaries(libInput, appInput)
|
||||
.then((generatedFiles) => generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
|
||||
return compile([libInput, angularFiles], {useSummaries: true})
|
||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
||||
.then(({genFiles}) => genFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
|
||||
}
|
||||
|
||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units',
|
||||
async(() => {
|
||||
const libInput: MockData = {
|
||||
const libInput: MockDirectory = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
export class AParam {}
|
||||
@ -442,7 +363,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockData = {
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
@ -459,8 +380,10 @@ describe('compiler (unbundled Angular)', () => {
|
||||
}
|
||||
};
|
||||
|
||||
compileWithSummaries(libInput, appInput).then((generatedFiles) => {
|
||||
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
compile([libInput, angularFiles], {useSummaries: true})
|
||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
||||
.then(({genFiles}) => {
|
||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||
expect(mainNgFactory.source)
|
||||
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam]`);
|
||||
@ -469,7 +392,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
|
||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
|
||||
async(() => {
|
||||
const lib1Input: MockData = {
|
||||
const lib1Input: MockDirectory = {
|
||||
'lib1': {
|
||||
'base.ts': `
|
||||
export class AParam {}
|
||||
@ -482,7 +405,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const lib2Input: MockData = {
|
||||
const lib2Input: MockDirectory = {
|
||||
'lib2': {
|
||||
'middle.ts': `
|
||||
import {Base} from '../lib1/base';
|
||||
@ -492,7 +415,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
};
|
||||
|
||||
|
||||
const appInput: MockData = {
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
@ -508,29 +431,11 @@ describe('compiler (unbundled Angular)', () => {
|
||||
`
|
||||
}
|
||||
};
|
||||
const lib1Host = new MockCompilerHost(['/lib1/base.ts'], lib1Input, angularFiles);
|
||||
const lib1AotHost = new MockAotCompilerHost(lib1Host);
|
||||
lib1AotHost.tsFilesOnly();
|
||||
const lib2Host = new MockCompilerHost(['/lib2/middle.ts'], lib2Input, angularFiles);
|
||||
const lib2AotHost = new MockAotCompilerHost(lib2Host);
|
||||
lib2AotHost.tsFilesOnly();
|
||||
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
|
||||
const appAotHost = new MockAotCompilerHost(appHost);
|
||||
appAotHost.tsFilesOnly();
|
||||
compile(lib1Host, lib1AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
|
||||
.then(() => {
|
||||
lib1Host.writtenFiles.forEach((value, key) => lib2Host.writeFile(key, value, false));
|
||||
lib1Host.overrides.forEach((value, key) => lib2Host.override(key, value));
|
||||
return compile(
|
||||
lib2Host, lib2AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
})
|
||||
.then(() => {
|
||||
lib2Host.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
|
||||
lib2Host.overrides.forEach((value, key) => appHost.override(key, value));
|
||||
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
})
|
||||
.then((generatedFiles) => {
|
||||
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
compile([lib1Input, angularFiles], {useSummaries: true})
|
||||
.then(({outDir}) => compile([outDir, lib2Input, angularFiles], {useSummaries: true}))
|
||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
||||
.then(({genFiles}) => {
|
||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||
expect(mainNgFactory.source)
|
||||
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam_2]`);
|
||||
@ -660,6 +565,8 @@ describe('compiler (unbundled Angular)', () => {
|
||||
});
|
||||
|
||||
describe('compiler (bundled Angular)', () => {
|
||||
setup({compileAngular: false});
|
||||
|
||||
let angularFiles: Map<string, string>;
|
||||
|
||||
beforeAll(() => {
|
||||
@ -681,34 +588,19 @@ describe('compiler (bundled Angular)', () => {
|
||||
const bundleIndexName = emittingHost.effectiveName('@angular/core/bundle_index.ts');
|
||||
const emittingProgram = ts.createProgram([bundleIndexName], settings, emittingHost);
|
||||
emittingProgram.emit();
|
||||
angularFiles = emittingHost.written;
|
||||
angularFiles = emittingHost.writtenAngularFiles();
|
||||
});
|
||||
|
||||
describe('Quickstart', () => {
|
||||
let host: MockCompilerHost;
|
||||
let aotHost: MockAotCompilerHost;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockCompilerHost(QUICKSTART, FILES, angularFiles);
|
||||
aotHost = new MockAotCompilerHost(host);
|
||||
});
|
||||
|
||||
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||
|
||||
it('should compile',
|
||||
async(() => compile(host, aotHost, expectNoDiagnostics).then(generatedFiles => {
|
||||
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
|
||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
||||
.toBeDefined();
|
||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||
})));
|
||||
});
|
||||
|
||||
describe('Bundled library', () => {
|
||||
let host: MockCompilerHost;
|
||||
let aotHost: MockAotCompilerHost;
|
||||
let libraryFiles: Map<string, string>;
|
||||
let libraryFiles: MockDirectory;
|
||||
|
||||
beforeAll(() => {
|
||||
// Emit the library bundle
|
||||
@ -728,135 +620,22 @@ describe('compiler (bundled Angular)', () => {
|
||||
// Emit the sources
|
||||
const emittingProgram = ts.createProgram(['/bolder/index.ts'], settings, emittingHost);
|
||||
emittingProgram.emit();
|
||||
libraryFiles = emittingHost.written;
|
||||
const libFiles = emittingHost.written;
|
||||
|
||||
// Copy the .html file
|
||||
const htmlFileName = '/bolder/src/bolder.component.html';
|
||||
libraryFiles.set(htmlFileName, emittingHost.readFile(htmlFileName));
|
||||
libFiles.set(htmlFileName, emittingHost.readFile(htmlFileName));
|
||||
|
||||
libraryFiles = arrayToMockDir(toMockFileArray(libFiles).map(
|
||||
({fileName, content}) => ({fileName: `/node_modules${fileName}`, content})));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockCompilerHost(
|
||||
LIBRARY_USING_APP_MODULE, LIBRARY_USING_APP, angularFiles, [libraryFiles]);
|
||||
aotHost = new MockAotCompilerHost(host);
|
||||
});
|
||||
|
||||
it('should compile',
|
||||
async(() => compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics)));
|
||||
|
||||
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||
it('should compile', async(() => compile([LIBRARY_USING_APP, libraryFiles, angularFiles])));
|
||||
});
|
||||
});
|
||||
|
||||
function expectNoDiagnostics(program: ts.Program) {
|
||||
function fileInfo(diagnostic: ts.Diagnostic): string {
|
||||
if (diagnostic.file) {
|
||||
return `${diagnostic.file.fileName}(${diagnostic.start}): `;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function chars(len: number, ch: string): string { return new Array(len).fill(ch).join(''); }
|
||||
|
||||
function lineNoOf(offset: number, text: string): number {
|
||||
let result = 1;
|
||||
for (let i = 0; i < offset; i++) {
|
||||
if (text[i] == '\n') result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function lineInfo(diagnostic: ts.Diagnostic): string {
|
||||
if (diagnostic.file) {
|
||||
const start = diagnostic.start;
|
||||
let end = diagnostic.start + diagnostic.length;
|
||||
const source = diagnostic.file.text;
|
||||
let lineStart = start;
|
||||
let lineEnd = end;
|
||||
while (lineStart > 0 && source[lineStart] != '\n') lineStart--;
|
||||
if (lineStart < start) lineStart++;
|
||||
while (lineEnd < source.length && source[lineEnd] != '\n') lineEnd++;
|
||||
let line = source.substring(lineStart, lineEnd);
|
||||
const lineIndex = line.indexOf('/n');
|
||||
if (lineIndex > 0) {
|
||||
line = line.substr(0, lineIndex);
|
||||
end = start + lineIndex;
|
||||
}
|
||||
const lineNo = lineNoOf(start, source) + ': ';
|
||||
return '\n' + lineNo + line + '\n' + chars(start - lineStart + lineNo.length, ' ') +
|
||||
chars(end - start, '^');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) {
|
||||
if (diagnostics && diagnostics.length) {
|
||||
throw new Error(
|
||||
'Errors from TypeScript:\n' +
|
||||
diagnostics.map(d => `${fileInfo(d)}${d.messageText}${lineInfo(d)}`).join(' \n'));
|
||||
}
|
||||
}
|
||||
expectNoDiagnostics(program.getOptionsDiagnostics());
|
||||
expectNoDiagnostics(program.getSyntacticDiagnostics());
|
||||
expectNoDiagnostics(program.getSemanticDiagnostics());
|
||||
}
|
||||
|
||||
function expectNoDiagnosticsAndEmit(program: ts.Program) {
|
||||
expectNoDiagnostics(program);
|
||||
program.emit();
|
||||
}
|
||||
|
||||
function isDTS(fileName: string): boolean {
|
||||
return /\.d\.ts$/.test(fileName);
|
||||
}
|
||||
|
||||
function isSource(fileName: string): boolean {
|
||||
return /\.ts$/.test(fileName);
|
||||
}
|
||||
|
||||
function isFactory(fileName: string): boolean {
|
||||
return /\.ngfactory\./.test(fileName);
|
||||
}
|
||||
|
||||
function summaryCompile(
|
||||
host: MockCompilerHost, aotHost: MockAotCompilerHost,
|
||||
preCompile?: (program: ts.Program) => void) {
|
||||
// First compile the program to generate the summary files.
|
||||
return compile(host, aotHost).then(generatedFiles => {
|
||||
// Remove generated files that were not generated from a DTS file
|
||||
host.remove(generatedFiles.filter(f => !isDTS(f.srcFileUrl)).map(f => f.genFileUrl));
|
||||
|
||||
// Next compile the program shrowding metadata and only treating .ts files as source.
|
||||
aotHost.hideMetadata();
|
||||
aotHost.tsFilesOnly();
|
||||
|
||||
return compile(host, aotHost);
|
||||
});
|
||||
}
|
||||
|
||||
function compile(
|
||||
host: MockCompilerHost, aotHost: AotCompilerHost, preCompile?: (program: ts.Program) => void,
|
||||
postCompile: (program: ts.Program) => void = expectNoDiagnostics,
|
||||
options: AotCompilerOptions = {}): Promise<GeneratedFile[]> {
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const program = ts.createProgram(scripts, settings, host);
|
||||
if (preCompile) preCompile(program);
|
||||
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
||||
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName))
|
||||
.then(generatedFiles => {
|
||||
generatedFiles.forEach(
|
||||
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
|
||||
host.override(file.genFileUrl, file.source));
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const newProgram = ts.createProgram(scripts, settings, host);
|
||||
if (postCompile) postCompile(newProgram);
|
||||
return generatedFiles;
|
||||
});
|
||||
}
|
||||
|
||||
const QUICKSTART = ['/quickstart/app/app.module.ts'];
|
||||
const FILES: MockData = {
|
||||
const QUICKSTART: MockDirectory = {
|
||||
quickstart: {
|
||||
app: {
|
||||
'app.component.ts': `
|
||||
@ -891,7 +670,7 @@ const FILES: MockData = {
|
||||
}
|
||||
};
|
||||
|
||||
const LIBRARY: MockData = {
|
||||
const LIBRARY: MockDirectory = {
|
||||
bolder: {
|
||||
'public-api.ts': `
|
||||
export * from './src/bolder.component';
|
||||
@ -927,7 +706,7 @@ const LIBRARY: MockData = {
|
||||
};
|
||||
|
||||
const LIBRARY_USING_APP_MODULE = ['/lib-user/app/app.module.ts'];
|
||||
const LIBRARY_USING_APP: MockData = {
|
||||
const LIBRARY_USING_APP: MockDirectory = {
|
||||
'lib-user': {
|
||||
app: {
|
||||
'app.component.ts': `
|
||||
|
@ -6,19 +6,26 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompilerHost} from '@angular/compiler';
|
||||
import {AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
|
||||
import {ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
|
||||
import {MetadataBundlerHost, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export type MockData = string | MockDirectory;
|
||||
let nodeModulesPath: string;
|
||||
let angularSourcePath: string;
|
||||
let rootPath: string;
|
||||
|
||||
calcPathsOnDisc();
|
||||
|
||||
export type MockFileOrDirectory = string | MockDirectory;
|
||||
|
||||
export type MockDirectory = {
|
||||
[name: string]: MockData | undefined;
|
||||
[name: string]: MockFileOrDirectory | undefined;
|
||||
};
|
||||
|
||||
export function isDirectory(data: MockData | undefined): data is MockDirectory {
|
||||
export function isDirectory(data: MockFileOrDirectory | undefined): data is MockDirectory {
|
||||
return typeof data !== 'string';
|
||||
}
|
||||
|
||||
@ -43,12 +50,21 @@ export const settings: ts.CompilerOptions = {
|
||||
|
||||
export interface EmitterOptions {
|
||||
emitMetadata: boolean;
|
||||
mockData?: MockData;
|
||||
mockData?: MockDirectory;
|
||||
}
|
||||
|
||||
function calcPathsOnDisc() {
|
||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||
const distIndex = moduleFilename.indexOf('/dist/all');
|
||||
if (distIndex >= 0) {
|
||||
rootPath = moduleFilename.substr(0, distIndex);
|
||||
nodeModulesPath = path.join(rootPath, 'node_modules');
|
||||
angularSourcePath = path.join(rootPath, 'packages');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
private angularSourcePath: string|undefined;
|
||||
private nodeModulesPath: string|undefined;
|
||||
private addedFiles = new Map<string, string>();
|
||||
private writtenFiles = new Map<string, string>();
|
||||
private scriptNames: string[];
|
||||
@ -56,19 +72,18 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
private collector = new MetadataCollector();
|
||||
|
||||
constructor(scriptNames: string[], private options: EmitterOptions) {
|
||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||
const distIndex = moduleFilename.indexOf('/dist/all');
|
||||
if (distIndex >= 0) {
|
||||
const root = moduleFilename.substr(0, distIndex);
|
||||
this.nodeModulesPath = path.join(root, 'node_modules');
|
||||
this.angularSourcePath = path.join(root, 'packages');
|
||||
|
||||
// Rewrite references to scripts with '@angular' to its corresponding location in
|
||||
// the source tree.
|
||||
this.scriptNames = scriptNames.map(f => this.effectiveName(f));
|
||||
|
||||
this.root = root;
|
||||
this.root = rootPath;
|
||||
}
|
||||
|
||||
public writtenAngularFiles(target = new Map<string, string>()): Map<string, string> {
|
||||
this.written.forEach((value, key) => {
|
||||
const path = `/node_modules/@angular${key.substring(angularSourcePath.length)}`;
|
||||
target.set(path, value);
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
public addScript(fileName: string, content: string) {
|
||||
@ -97,7 +112,7 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
public effectiveName(fileName: string): string {
|
||||
const prefix = '@angular/';
|
||||
return fileName.startsWith('@angular/') ?
|
||||
path.join(this.angularSourcePath, fileName.substr(prefix.length)) :
|
||||
path.join(angularSourcePath, fileName.substr(prefix.length)) :
|
||||
fileName;
|
||||
}
|
||||
|
||||
@ -171,31 +186,17 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
getNewLine(): string { return '\n'; }
|
||||
}
|
||||
|
||||
const MOCK_NODEMODULES_PREFIX = '/node_modules/';
|
||||
|
||||
export class MockCompilerHost implements ts.CompilerHost {
|
||||
scriptNames: string[];
|
||||
|
||||
private angularSourcePath: string|undefined;
|
||||
private nodeModulesPath: string|undefined;
|
||||
public overrides = new Map<string, string>();
|
||||
public writtenFiles = new Map<string, string>();
|
||||
private sourceFiles = new Map<string, ts.SourceFile>();
|
||||
private assumeExists = new Set<string>();
|
||||
private traces: string[] = [];
|
||||
|
||||
constructor(
|
||||
scriptNames: string[], private data: MockData, private angular: Map<string, string>,
|
||||
private libraries?: Map<string, string>[]) {
|
||||
constructor(scriptNames: string[], private data: MockDirectory) {
|
||||
this.scriptNames = scriptNames.slice(0);
|
||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||
let angularIndex = moduleFilename.indexOf('@angular');
|
||||
let distIndex = moduleFilename.indexOf('/dist/all');
|
||||
if (distIndex >= 0) {
|
||||
const root = moduleFilename.substr(0, distIndex);
|
||||
this.nodeModulesPath = path.join(root, 'node_modules');
|
||||
this.angularSourcePath = path.join(root, 'packages');
|
||||
}
|
||||
}
|
||||
|
||||
// Test API
|
||||
@ -234,22 +235,13 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||
const effectiveName = this.getEffectiveName(fileName);
|
||||
if (effectiveName == fileName) {
|
||||
let result = open(fileName, this.data) != null;
|
||||
if (!result && fileName.startsWith(MOCK_NODEMODULES_PREFIX)) {
|
||||
const libraryPath = fileName.substr(MOCK_NODEMODULES_PREFIX.length - 1);
|
||||
for (const library of this.libraries !) {
|
||||
if (library.has(libraryPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (fileName.match(rxjs)) {
|
||||
let result = fs.existsSync(effectiveName);
|
||||
return result;
|
||||
}
|
||||
const result = this.angular.has(effectiveName);
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,12 +307,6 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||
let effectiveName = this.getEffectiveName(fileName);
|
||||
if (effectiveName === fileName) {
|
||||
const result = open(fileName, this.data);
|
||||
if (!result && fileName.startsWith(MOCK_NODEMODULES_PREFIX)) {
|
||||
const libraryPath = fileName.substr(MOCK_NODEMODULES_PREFIX.length - 1);
|
||||
for (const library of this.libraries !) {
|
||||
if (library.has(libraryPath)) return library.get(libraryPath);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (fileName.match(rxjs)) {
|
||||
@ -328,22 +314,16 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||
return fs.readFileSync(fileName, 'utf8');
|
||||
}
|
||||
}
|
||||
return this.angular.get(effectiveName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getEffectiveName(name: string): string {
|
||||
const node_modules = 'node_modules';
|
||||
const at_angular = '/@angular';
|
||||
const rxjs = '/rxjs';
|
||||
if (name.startsWith('/' + node_modules)) {
|
||||
if (this.angularSourcePath && name.startsWith('/' + node_modules + at_angular)) {
|
||||
return path.join(
|
||||
this.angularSourcePath, name.substr(node_modules.length + at_angular.length + 1));
|
||||
}
|
||||
if (this.nodeModulesPath && name.startsWith('/' + node_modules + rxjs)) {
|
||||
return path.join(this.nodeModulesPath, name.substr(node_modules.length + 1));
|
||||
if (nodeModulesPath && name.startsWith('/' + node_modules + rxjs)) {
|
||||
return path.join(nodeModulesPath, name.substr(node_modules.length + 1));
|
||||
}
|
||||
}
|
||||
return name;
|
||||
@ -439,11 +419,12 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost {
|
||||
}
|
||||
}
|
||||
|
||||
function find(fileName: string, data: MockData | undefined): MockData|undefined {
|
||||
function find(fileName: string, data: MockFileOrDirectory | undefined): MockFileOrDirectory|
|
||||
undefined {
|
||||
if (!data) return undefined;
|
||||
let names = fileName.split('/');
|
||||
if (names.length && !names[0].length) names.shift();
|
||||
let current: MockData|undefined = data;
|
||||
let current: MockFileOrDirectory|undefined = data;
|
||||
for (let name of names) {
|
||||
if (typeof current === 'string')
|
||||
return undefined;
|
||||
@ -454,7 +435,7 @@ function find(fileName: string, data: MockData | undefined): MockData|undefined
|
||||
return current;
|
||||
}
|
||||
|
||||
function open(fileName: string, data: MockData | undefined): string|undefined {
|
||||
function open(fileName: string, data: MockFileOrDirectory | undefined): string|undefined {
|
||||
let result = find(fileName, data);
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
@ -462,7 +443,204 @@ function open(fileName: string, data: MockData | undefined): string|undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function directoryExists(dirname: string, data: MockData | undefined): boolean {
|
||||
function directoryExists(dirname: string, data: MockFileOrDirectory | undefined): boolean {
|
||||
let result = find(dirname, data);
|
||||
return !!result && typeof result !== 'string';
|
||||
}
|
||||
|
||||
export type MockFileArray = {
|
||||
fileName: string,
|
||||
content: string
|
||||
}[];
|
||||
|
||||
export type MockData = MockDirectory | Map<string, string>| (MockDirectory | Map<string, string>)[];
|
||||
|
||||
export function toMockFileArray(data: MockData, target: MockFileArray = []): MockFileArray {
|
||||
if (data instanceof Map) {
|
||||
mapToMockFileArray(data, target);
|
||||
} else if (Array.isArray(data)) {
|
||||
data.forEach(entry => toMockFileArray(entry, target));
|
||||
} else {
|
||||
mockDirToFileArray(data, '', target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function mockDirToFileArray(dir: MockDirectory, path: string, target: MockFileArray) {
|
||||
Object.keys(dir).forEach((localFileName) => {
|
||||
const value = dir[localFileName] !;
|
||||
const fileName = `${path}/${localFileName}`;
|
||||
if (typeof value === 'string') {
|
||||
target.push({fileName, content: value});
|
||||
} else {
|
||||
mockDirToFileArray(value, fileName, target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mapToMockFileArray(files: Map<string, string>, target: MockFileArray) {
|
||||
files.forEach((content, fileName) => { target.push({fileName, content}); });
|
||||
}
|
||||
|
||||
export function arrayToMockMap(arr: MockFileArray): Map<string, string> {
|
||||
const map = new Map<string, string>();
|
||||
arr.forEach(({fileName, content}) => { map.set(fileName, content); });
|
||||
return map;
|
||||
}
|
||||
|
||||
export function arrayToMockDir(arr: MockFileArray): MockDirectory {
|
||||
const rootDir: MockDirectory = {};
|
||||
arr.forEach(({fileName, content}) => {
|
||||
let pathParts = fileName.split('/');
|
||||
// trim trailing slash
|
||||
let startIndex = pathParts[0] ? 0 : 1;
|
||||
// get/create the directory
|
||||
let currentDir = rootDir;
|
||||
for (let i = startIndex; i < pathParts.length - 1; i++) {
|
||||
const pathPart = pathParts[i];
|
||||
let localDir = <MockDirectory>currentDir[pathPart];
|
||||
if (!localDir) {
|
||||
currentDir[pathPart] = localDir = {};
|
||||
}
|
||||
currentDir = localDir;
|
||||
}
|
||||
// write the file
|
||||
currentDir[pathParts[pathParts.length - 1]] = content;
|
||||
});
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
const minCoreIndex = `
|
||||
export * from './src/application_module';
|
||||
export * from './src/change_detection';
|
||||
export * from './src/metadata';
|
||||
export * from './src/di/metadata';
|
||||
export * from './src/di/injector';
|
||||
export * from './src/di/injection_token';
|
||||
export * from './src/linker';
|
||||
export * from './src/render';
|
||||
export * from './src/codegen_private_exports';
|
||||
`;
|
||||
|
||||
export function setup(options: {compileAngular: boolean} = {
|
||||
compileAngular: true
|
||||
}) {
|
||||
let angularFiles = new Map<string, string>();
|
||||
|
||||
beforeAll(() => {
|
||||
if (options.compileAngular) {
|
||||
const emittingHost = new EmittingCompilerHost([], {emitMetadata: true});
|
||||
emittingHost.addScript('@angular/core/index.ts', minCoreIndex);
|
||||
const emittingProgram = ts.createProgram(emittingHost.scripts, settings, emittingHost);
|
||||
emittingProgram.emit();
|
||||
emittingHost.writtenAngularFiles(angularFiles);
|
||||
}
|
||||
});
|
||||
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||
|
||||
return angularFiles;
|
||||
}
|
||||
|
||||
export function expectNoDiagnostics(program: ts.Program) {
|
||||
function fileInfo(diagnostic: ts.Diagnostic): string {
|
||||
if (diagnostic.file) {
|
||||
return `${diagnostic.file.fileName}(${diagnostic.start}): `;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function chars(len: number, ch: string): string { return new Array(len).fill(ch).join(''); }
|
||||
|
||||
function lineNoOf(offset: number, text: string): number {
|
||||
let result = 1;
|
||||
for (let i = 0; i < offset; i++) {
|
||||
if (text[i] == '\n') result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function lineInfo(diagnostic: ts.Diagnostic): string {
|
||||
if (diagnostic.file) {
|
||||
const start = diagnostic.start;
|
||||
let end = diagnostic.start + diagnostic.length;
|
||||
const source = diagnostic.file.text;
|
||||
let lineStart = start;
|
||||
let lineEnd = end;
|
||||
while (lineStart > 0 && source[lineStart] != '\n') lineStart--;
|
||||
if (lineStart < start) lineStart++;
|
||||
while (lineEnd < source.length && source[lineEnd] != '\n') lineEnd++;
|
||||
let line = source.substring(lineStart, lineEnd);
|
||||
const lineIndex = line.indexOf('/n');
|
||||
if (lineIndex > 0) {
|
||||
line = line.substr(0, lineIndex);
|
||||
end = start + lineIndex;
|
||||
}
|
||||
const lineNo = lineNoOf(start, source) + ': ';
|
||||
return '\n' + lineNo + line + '\n' + chars(start - lineStart + lineNo.length, ' ') +
|
||||
chars(end - start, '^');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) {
|
||||
if (diagnostics && diagnostics.length) {
|
||||
throw new Error(
|
||||
'Errors from TypeScript:\n' +
|
||||
diagnostics.map(d => `${fileInfo(d)}${d.messageText}${lineInfo(d)}`).join(' \n'));
|
||||
}
|
||||
}
|
||||
expectNoDiagnostics(program.getOptionsDiagnostics());
|
||||
expectNoDiagnostics(program.getSyntacticDiagnostics());
|
||||
expectNoDiagnostics(program.getSemanticDiagnostics());
|
||||
}
|
||||
|
||||
function isSource(fileName: string): boolean {
|
||||
return !/\.d\.ts$/.test(fileName) && /\.ts$/.test(fileName);
|
||||
}
|
||||
|
||||
export function compile(rootDirs: MockData, options: {
|
||||
emit?: boolean,
|
||||
useSummaries?: boolean,
|
||||
preCompile?: (program: ts.Program) => void,
|
||||
postCompile?: (program: ts.Program) => void,
|
||||
}& AotCompilerOptions = {}): Promise<{genFiles: GeneratedFile[], outDir: MockDirectory}> {
|
||||
// Make sure we always return errors via the promise...
|
||||
return Promise.resolve(null).then(() => {
|
||||
// when using summaries, always emit so the next step can use the results.
|
||||
const emit = options.emit || options.useSummaries;
|
||||
const preCompile = options.preCompile || expectNoDiagnostics;
|
||||
const postCompile = options.postCompile || expectNoDiagnostics;
|
||||
const rootDirArr = toMockFileArray(rootDirs);
|
||||
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
||||
|
||||
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
if (options.useSummaries) {
|
||||
aotHost.hideMetadata();
|
||||
aotHost.tsFilesOnly();
|
||||
}
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const program = ts.createProgram(scripts, settings, host);
|
||||
if (preCompile) preCompile(program);
|
||||
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
||||
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
|
||||
genFiles.forEach(
|
||||
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
|
||||
host.override(file.genFileUrl, file.source));
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const newProgram = ts.createProgram(scripts, settings, host);
|
||||
if (postCompile) postCompile(newProgram);
|
||||
if (emit) {
|
||||
newProgram.emit();
|
||||
}
|
||||
let outDir: MockDirectory = {};
|
||||
if (emit) {
|
||||
outDir = arrayToMockDir(toMockFileArray([
|
||||
host.writtenFiles, host.overrides
|
||||
]).filter((entry) => !isSource(entry.fileName)));
|
||||
}
|
||||
return {genFiles, outDir};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -24,23 +24,48 @@ export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Applicatio
|
||||
*/
|
||||
@Injectable()
|
||||
export class ApplicationInitStatus {
|
||||
private resolve: Function;
|
||||
private reject: Function;
|
||||
private initialized = false;
|
||||
private _donePromise: Promise<any>;
|
||||
private _done = false;
|
||||
|
||||
constructor(@Inject(APP_INITIALIZER) @Optional() appInits: (() => any)[]) {
|
||||
constructor(@Inject(APP_INITIALIZER) @Optional() private appInits: (() => any)[]) {
|
||||
this._donePromise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
runInitializers() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const asyncInitPromises: Promise<any>[] = [];
|
||||
if (appInits) {
|
||||
for (let i = 0; i < appInits.length; i++) {
|
||||
const initResult = appInits[i]();
|
||||
|
||||
const complete =
|
||||
() => {
|
||||
this._done = true;
|
||||
this.resolve();
|
||||
}
|
||||
|
||||
if (this.appInits) {
|
||||
for (let i = 0; i < this.appInits.length; i++) {
|
||||
const initResult = this.appInits[i]();
|
||||
if (isPromise(initResult)) {
|
||||
asyncInitPromises.push(initResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._donePromise = Promise.all(asyncInitPromises).then(() => { this._done = true; });
|
||||
|
||||
Promise.all(asyncInitPromises).then(() => { complete(); }).catch(e => { this.reject(e); });
|
||||
|
||||
if (asyncInitPromises.length === 0) {
|
||||
this._done = true;
|
||||
complete();
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
get done(): boolean { return this._done; }
|
||||
|
@ -162,8 +162,8 @@ export function getPlatform(): PlatformRef|null {
|
||||
* has exactly one platform, and services (such as reflection) which are common
|
||||
* to every Angular application running on the page are bound in its scope.
|
||||
*
|
||||
* A page's platform is initialized implicitly when {@link bootstrap}() is called, or
|
||||
* explicitly by calling {@link createPlatform}().
|
||||
* A page's platform is initialized implicitly when a platform is created via a platform factory
|
||||
* (e.g. {@link platformBrowser}), or explicitly by calling the {@link createPlatform} function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -302,6 +302,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||
ngZone !.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
|
||||
return _callAndReportToErrorHandler(exceptionHandler, () => {
|
||||
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
|
||||
initStatus.runInitializers();
|
||||
return initStatus.donePromise.then(() => {
|
||||
this._moduleDoBootstrap(moduleRef);
|
||||
return moduleRef;
|
||||
@ -344,8 +345,6 @@ export class PlatformRef_ extends PlatformRef {
|
||||
/**
|
||||
* A reference to an Angular application running on a page.
|
||||
*
|
||||
* For more about Angular applications, see the documentation for {@link bootstrap}.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export abstract class ApplicationRef {
|
||||
@ -553,6 +552,9 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
if (this._enforceNoNewChanges) {
|
||||
this._views.forEach((view) => view.checkNoChanges());
|
||||
}
|
||||
} catch (e) {
|
||||
// Attention: Don't rethrow as it could cancel subscriptions to Observables!
|
||||
this._exceptionHandler.handleError(e);
|
||||
} finally {
|
||||
this._runningTick = false;
|
||||
wtfLeave(scope);
|
||||
|
@ -50,7 +50,7 @@ export abstract class Injector {
|
||||
/**
|
||||
* Retrieves an instance from the injector based on the provided token.
|
||||
* If not found:
|
||||
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
|
||||
* - Throws an error if no `notFoundValue` that is not equal to
|
||||
* Injector.THROW_IF_NOT_FOUND is given
|
||||
* - Returns the `notFoundValue` otherwise
|
||||
*/
|
||||
|
@ -127,7 +127,7 @@ export interface InjectableDecorator {
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Injectable'}
|
||||
*
|
||||
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
|
||||
* {@link Injector} will throw an error when trying to instantiate a class that
|
||||
* does not have `@Injectable` marker, as shown in the example below.
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='InjectableThrows'}
|
||||
|
@ -113,7 +113,7 @@ export abstract class ReflectiveInjector implements Injector {
|
||||
*
|
||||
* This function is slower than the corresponding `fromResolvedProviders`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
|
||||
* See {@link ReflectiveInjector#resolve} and {@link ReflectiveInjector#fromResolvedProviders}.
|
||||
*/
|
||||
static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
|
||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
@ -190,7 +190,7 @@ export abstract class ReflectiveInjector implements Injector {
|
||||
*
|
||||
* This function is slower than the corresponding `createChildFromResolved`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
|
||||
* See {@link ReflectiveInjector#resolve} and {@link ReflectiveInjector#createChildFromResolved}.
|
||||
*/
|
||||
abstract resolveAndCreateChild(providers: Provider[]): ReflectiveInjector;
|
||||
|
||||
|
@ -64,8 +64,11 @@ export class EventEmitter<T> extends Subject<T> {
|
||||
__isAsync: boolean;
|
||||
|
||||
/**
|
||||
* Creates an instance of [EventEmitter], which depending on [isAsync],
|
||||
* Creates an instance of {@link EventEmitter}, which depending on `isAsync`,
|
||||
* delivers events synchronously or asynchronously.
|
||||
*
|
||||
* @param isAsync By default, events are delivered synchronously (default value: `false`).
|
||||
* Set to `true` for asynchronous event delivery.
|
||||
*/
|
||||
constructor(isAsync: boolean = false) {
|
||||
super();
|
||||
|
@ -54,14 +54,14 @@ export class Compiler {
|
||||
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> { throw _throwError(); }
|
||||
|
||||
/**
|
||||
* Same as {@link compileModuleSync} but also creates ComponentFactories for all components.
|
||||
* Same as {@link #compileModuleSync} but also creates ComponentFactories for all components.
|
||||
*/
|
||||
compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> {
|
||||
throw _throwError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link compileModuleAsync} but also creates ComponentFactories for all components.
|
||||
* Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components.
|
||||
*/
|
||||
compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>):
|
||||
Promise<ModuleWithComponentFactories<T>> {
|
||||
|
@ -16,7 +16,8 @@ import {getSymbolIterator} from '../util';
|
||||
* An unmodifiable list of items that Angular keeps up to date when the state
|
||||
* of the application changes.
|
||||
*
|
||||
* The type of object that {@link Query} and {@link ViewQueryMetadata} provide.
|
||||
* The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList}
|
||||
* provide.
|
||||
*
|
||||
* Implements an iterable interface, therefore it can be used in both ES6
|
||||
* javascript `for (var i of items)` loops as well as in Angular templates with
|
||||
|
@ -287,7 +287,7 @@ export const ContentChild: ContentChildDecorator = makePropDecorator(
|
||||
/**
|
||||
* Type of the ViewChildren decorator / constructor function.
|
||||
*
|
||||
* See {@ViewChildren}.
|
||||
* See {@link ViewChildren}.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -662,8 +662,8 @@ export interface Component extends Directive {
|
||||
* encapsulation.
|
||||
*
|
||||
* When no `encapsulation` is defined for the component, the default value from the
|
||||
* {@link CompilerConfig} is used. The default is `ViewEncapsulation.Emulated`}. Provide a new
|
||||
* `CompilerConfig` to override this value.
|
||||
* {@link CompilerOptions} is used. The default is `ViewEncapsulation.Emulated`}. Provide a new
|
||||
* `CompilerOptions` to override this value.
|
||||
*
|
||||
* If the encapsulation is set to `ViewEncapsulation.Emulated` and the component has no `styles`
|
||||
* nor `styleUrls` the encapsulation will automatically be switched to `ViewEncapsulation.None`.
|
||||
|
@ -9,14 +9,14 @@
|
||||
/**
|
||||
* Defines template and style encapsulation options available for Component's {@link Component}.
|
||||
*
|
||||
* See {@link ViewMetadata#encapsulation}.
|
||||
* See {@link Component#encapsulation}.
|
||||
* @stable
|
||||
*/
|
||||
export enum ViewEncapsulation {
|
||||
/**
|
||||
* Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
|
||||
* Element and pre-processing the style rules provided via
|
||||
* {@link ViewMetadata#styles} or {@link ViewMetadata#stylesUrls}, and adding the new Host Element
|
||||
* {@link Component#styles} or {@link Component#styleUrls}, and adding the new Host Element
|
||||
* attribute to all selectors.
|
||||
*
|
||||
* This is the default option.
|
||||
@ -61,19 +61,19 @@ export enum ViewEncapsulation {
|
||||
* {@link Component}
|
||||
*/
|
||||
export class ViewMetadata {
|
||||
/** {@link Component.templateUrl} */
|
||||
/** {@link Component#templateUrl} */
|
||||
templateUrl: string|undefined;
|
||||
/** {@link Component.template} */
|
||||
/** {@link Component#template} */
|
||||
template: string|undefined;
|
||||
/** {@link Component.stylesUrl} */
|
||||
/** {@link Component#stylesUrl} */
|
||||
styleUrls: string[]|undefined;
|
||||
/** {@link Component.styles} */
|
||||
/** {@link Component#styles} */
|
||||
styles: string[]|undefined;
|
||||
/** {@link Component.encapsulation} */
|
||||
/** {@link Component#encapsulation} */
|
||||
encapsulation: ViewEncapsulation|undefined;
|
||||
/** {@link Component.animation} */
|
||||
/** {@link Component#animation} */
|
||||
animations: any[]|undefined;
|
||||
/** {@link Component.interpolation} */
|
||||
/** {@link Component#interpolation} */
|
||||
interpolation: [string, string]|undefined;
|
||||
|
||||
constructor(
|
||||
|
@ -99,7 +99,8 @@ export const Renderer2Interceptor = new InjectionToken<Renderer2[]>('Renderer2In
|
||||
*
|
||||
* Use this service to bypass Angular's templating and make custom UI changes that can't be
|
||||
* expressed declaratively. For example if you need to set a property or an attribute whose name is
|
||||
* not statically known, use {@link #setElementProperty} or {@link #setElementAttribute}
|
||||
* not statically known, use {@link Renderer#setElementProperty} or {@link
|
||||
* Renderer#setElementAttribute}
|
||||
* respectively.
|
||||
*
|
||||
* If you are implementing a custom renderer, you must implement this interface.
|
||||
|
@ -189,7 +189,14 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def:
|
||||
}
|
||||
|
||||
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||
return (event: any) => dispatchEvent(view, index, eventName, event);
|
||||
return (event: any) => {
|
||||
try {
|
||||
return dispatchEvent(view, index, eventName, event);
|
||||
} catch (e) {
|
||||
// Attention: Don't rethrow, to keep in sync with directive events.
|
||||
view.root.errorHandler.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -148,7 +148,14 @@ export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||
}
|
||||
|
||||
function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||
return (event: any) => dispatchEvent(view, index, eventName, event);
|
||||
return (event: any) => {
|
||||
try {
|
||||
return dispatchEvent(view, index, eventName, event);
|
||||
} catch (e) {
|
||||
// Attention: Don't rethrow, as it would cancel Observable subscriptions!
|
||||
view.root.errorHandler.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function checkAndUpdateDirectiveInline(
|
||||
@ -355,6 +362,12 @@ export function resolveDep(
|
||||
}
|
||||
const tokenKey = depDef.tokenKey;
|
||||
|
||||
if (tokenKey === ChangeDetectorRefTokenKey) {
|
||||
// directives on the same element as a component should be able to control the change detector
|
||||
// of that component as well.
|
||||
allowPrivateServices = !!(elDef && elDef.element !.componentView);
|
||||
}
|
||||
|
||||
if (elDef && (depDef.flags & DepFlags.SkipSelf)) {
|
||||
allowPrivateServices = false;
|
||||
elDef = elDef.parent !;
|
||||
|
@ -90,7 +90,9 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||
const view = Services.createRootView(
|
||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
|
||||
const component = asProviderData(view, componentNodeIndex).instance;
|
||||
if (rootSelectorOrNode) {
|
||||
view.renderer.setAttribute(asElementData(view, 0).renderElement, 'ng-version', VERSION.full);
|
||||
}
|
||||
|
||||
return new ComponentRef_(view, new ViewRef_(view), component);
|
||||
}
|
||||
@ -236,11 +238,11 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
|
||||
get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; }
|
||||
|
||||
markForCheck(): void { markParentViewsForCheck(this._view); }
|
||||
detach(): void { this._view.state &= ~ViewState.ChecksEnabled; }
|
||||
detach(): void { this._view.state &= ~ViewState.Attached; }
|
||||
detectChanges(): void { Services.checkAndUpdateView(this._view); }
|
||||
checkNoChanges(): void { Services.checkNoChangesView(this._view); }
|
||||
|
||||
reattach(): void { this._view.state |= ViewState.ChecksEnabled; }
|
||||
reattach(): void { this._view.state |= ViewState.Attached; }
|
||||
onDestroy(callback: Function) {
|
||||
if (!this._view.disposables) {
|
||||
this._view.disposables = [];
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
||||
import {Injector} from '../di';
|
||||
import {ErrorHandler} from '../error_handler';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||
import {Sanitizer} from '../security';
|
||||
@ -104,11 +105,12 @@ function createRootData(
|
||||
elInjector: Injector, ngModule: NgModuleRef<any>, rendererFactory: RendererFactory2,
|
||||
projectableNodes: any[][], rootSelectorOrNode: any): RootData {
|
||||
const sanitizer = ngModule.injector.get(Sanitizer);
|
||||
const errorHandler = ngModule.injector.get(ErrorHandler);
|
||||
const renderer = rendererFactory.createRenderer(null, null);
|
||||
return {
|
||||
ngModule,
|
||||
injector: elInjector, projectableNodes,
|
||||
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer
|
||||
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler
|
||||
};
|
||||
}
|
||||
|
||||
@ -439,7 +441,6 @@ function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any
|
||||
if (isViewDebugError(e) || !_currentView) {
|
||||
throw e;
|
||||
}
|
||||
_currentView.state |= ViewState.Errored;
|
||||
throw viewWrappedDebugError(e, getCurrentDebugContext() !);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injector} from '../di';
|
||||
import {ErrorHandler} from '../error_handler';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
@ -140,37 +141,38 @@ export const enum NodeFlags {
|
||||
None = 0,
|
||||
TypeElement = 1 << 0,
|
||||
TypeText = 1 << 1,
|
||||
ProjectedTemplate = 1 << 2,
|
||||
CatRenderNode = TypeElement | TypeText,
|
||||
TypeNgContent = 1 << 2,
|
||||
TypePipe = 1 << 3,
|
||||
TypePureArray = 1 << 4,
|
||||
TypePureObject = 1 << 5,
|
||||
TypePurePipe = 1 << 6,
|
||||
TypeNgContent = 1 << 3,
|
||||
TypePipe = 1 << 4,
|
||||
TypePureArray = 1 << 5,
|
||||
TypePureObject = 1 << 6,
|
||||
TypePurePipe = 1 << 7,
|
||||
CatPureExpression = TypePureArray | TypePureObject | TypePurePipe,
|
||||
TypeValueProvider = 1 << 7,
|
||||
TypeClassProvider = 1 << 8,
|
||||
TypeFactoryProvider = 1 << 9,
|
||||
TypeUseExistingProvider = 1 << 10,
|
||||
LazyProvider = 1 << 11,
|
||||
PrivateProvider = 1 << 12,
|
||||
TypeDirective = 1 << 13,
|
||||
Component = 1 << 14,
|
||||
TypeValueProvider = 1 << 8,
|
||||
TypeClassProvider = 1 << 9,
|
||||
TypeFactoryProvider = 1 << 10,
|
||||
TypeUseExistingProvider = 1 << 11,
|
||||
LazyProvider = 1 << 12,
|
||||
PrivateProvider = 1 << 13,
|
||||
TypeDirective = 1 << 14,
|
||||
Component = 1 << 15,
|
||||
CatProvider = TypeValueProvider | TypeClassProvider | TypeFactoryProvider |
|
||||
TypeUseExistingProvider | TypeDirective,
|
||||
OnInit = 1 << 15,
|
||||
OnDestroy = 1 << 16,
|
||||
DoCheck = 1 << 17,
|
||||
OnChanges = 1 << 18,
|
||||
AfterContentInit = 1 << 19,
|
||||
AfterContentChecked = 1 << 20,
|
||||
AfterViewInit = 1 << 21,
|
||||
AfterViewChecked = 1 << 22,
|
||||
EmbeddedViews = 1 << 23,
|
||||
ComponentView = 1 << 24,
|
||||
TypeContentQuery = 1 << 25,
|
||||
TypeViewQuery = 1 << 26,
|
||||
StaticQuery = 1 << 27,
|
||||
DynamicQuery = 1 << 28,
|
||||
OnInit = 1 << 16,
|
||||
OnDestroy = 1 << 17,
|
||||
DoCheck = 1 << 18,
|
||||
OnChanges = 1 << 19,
|
||||
AfterContentInit = 1 << 20,
|
||||
AfterContentChecked = 1 << 21,
|
||||
AfterViewInit = 1 << 22,
|
||||
AfterViewChecked = 1 << 23,
|
||||
EmbeddedViews = 1 << 24,
|
||||
ComponentView = 1 << 25,
|
||||
TypeContentQuery = 1 << 26,
|
||||
TypeViewQuery = 1 << 27,
|
||||
StaticQuery = 1 << 28,
|
||||
DynamicQuery = 1 << 29,
|
||||
CatQuery = TypeContentQuery | TypeViewQuery,
|
||||
|
||||
// mutually exclusive values...
|
||||
@ -323,10 +325,17 @@ export interface ViewData {
|
||||
* Bitmask of states
|
||||
*/
|
||||
export const enum ViewState {
|
||||
FirstCheck = 1 << 0,
|
||||
ChecksEnabled = 1 << 1,
|
||||
Errored = 1 << 2,
|
||||
Destroyed = 1 << 3
|
||||
BeforeFirstCheck = 1 << 0,
|
||||
FirstCheck = 1 << 1,
|
||||
Attached = 1 << 2,
|
||||
ChecksEnabled = 1 << 3,
|
||||
IsProjectedView = 1 << 4,
|
||||
CheckProjectedView = 1 << 5,
|
||||
CheckProjectedViews = 1 << 6,
|
||||
Destroyed = 1 << 7,
|
||||
|
||||
CatDetectChanges = Attached | ChecksEnabled,
|
||||
CatInit = BeforeFirstCheck | CatDetectChanges
|
||||
}
|
||||
|
||||
export interface DisposableFn { (): void; }
|
||||
@ -429,6 +438,7 @@ export interface RootData {
|
||||
selectorOrNode: any;
|
||||
renderer: Renderer2;
|
||||
rendererFactory: RendererFactory2;
|
||||
errorHandler: ErrorHandler;
|
||||
sanitizer: Sanitizer;
|
||||
}
|
||||
|
||||
|
@ -100,10 +100,10 @@ export function checkAndUpdateBinding(
|
||||
export function checkBindingNoChanges(
|
||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
|
||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||
if ((view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
|
||||
if ((view.state & ViewState.BeforeFirstCheck) || !devModeEqual(oldValue, value)) {
|
||||
throw expressionChangedAfterItHasBeenCheckedError(
|
||||
Services.createDebugContext(view, def.index), oldValue, value,
|
||||
(view.state & ViewState.FirstCheck) !== 0);
|
||||
(view.state & ViewState.BeforeFirstCheck) !== 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,6 +117,14 @@ export function markParentViewsForCheck(view: ViewData) {
|
||||
}
|
||||
}
|
||||
|
||||
export function markParentViewsForCheckProjectedViews(view: ViewData, endView: ViewData) {
|
||||
let currView: ViewData|null = view;
|
||||
while (currView && currView !== endView) {
|
||||
currView.state |= ViewState.CheckProjectedViews;
|
||||
currView = currView.viewContainerParent || currView.parent;
|
||||
}
|
||||
}
|
||||
|
||||
export function dispatchEvent(
|
||||
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
|
@ -17,7 +17,8 @@ import {checkAndUpdateQuery, createQuery} from './query';
|
||||
import {createTemplateData, createViewContainerData} from './refs';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
|
||||
import {NOOP, checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util';
|
||||
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveViewDefinition} from './util';
|
||||
import {detachProjectedView} from './view_attach';
|
||||
|
||||
export function viewDef(
|
||||
flags: ViewFlags, nodes: NodeDef[], updateDirectives?: ViewUpdateFn,
|
||||
@ -211,7 +212,7 @@ function createView(
|
||||
viewContainerParent: null, parentNodeDef,
|
||||
context: null,
|
||||
component: null, nodes,
|
||||
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer,
|
||||
state: ViewState.CatInit, root, renderer,
|
||||
oldValues: new Array(def.bindingCount), disposables
|
||||
};
|
||||
return view;
|
||||
@ -314,15 +315,24 @@ function createViewNodes(view: ViewData) {
|
||||
}
|
||||
|
||||
export function checkNoChangesView(view: ViewData) {
|
||||
markProjectedViewsForCheck(view);
|
||||
Services.updateDirectives(view, CheckType.CheckNoChanges);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||
Services.updateRenderer(view, CheckType.CheckNoChanges);
|
||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||
// Note: We don't check queries for changes as we didn't do this in v2.x.
|
||||
// TODO(tbosch): investigate if we can enable the check again in v5.x with a nicer error message.
|
||||
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
|
||||
}
|
||||
|
||||
export function checkAndUpdateView(view: ViewData) {
|
||||
if (view.state & ViewState.BeforeFirstCheck) {
|
||||
view.state &= ~ViewState.BeforeFirstCheck;
|
||||
view.state |= ViewState.FirstCheck;
|
||||
} else {
|
||||
view.state &= ~ViewState.FirstCheck;
|
||||
}
|
||||
markProjectedViewsForCheck(view);
|
||||
Services.updateDirectives(view, CheckType.CheckAndUpdate);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
@ -337,7 +347,6 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
view, NodeFlags.TypeViewQuery, NodeFlags.DynamicQuery, CheckType.CheckAndUpdate);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterViewChecked |
|
||||
(view.state & ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
|
||||
@ -345,7 +354,7 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
if (view.def.flags & ViewFlags.OnPush) {
|
||||
view.state &= ~ViewState.ChecksEnabled;
|
||||
}
|
||||
view.state &= ~ViewState.FirstCheck;
|
||||
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
|
||||
}
|
||||
|
||||
export function checkAndUpdateNode(
|
||||
@ -358,6 +367,31 @@ export function checkAndUpdateNode(
|
||||
}
|
||||
}
|
||||
|
||||
function markProjectedViewsForCheck(view: ViewData) {
|
||||
const def = view.def;
|
||||
if (!(def.nodeFlags & NodeFlags.ProjectedTemplate)) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < def.nodes.length; i++) {
|
||||
const nodeDef = def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
|
||||
const projectedViews = asElementData(view, i).template._projectedViews;
|
||||
if (projectedViews) {
|
||||
for (let i = 0; i < projectedViews.length; i++) {
|
||||
const projectedView = projectedViews[i];
|
||||
projectedView.state |= ViewState.CheckProjectedView;
|
||||
markParentViewsForCheckProjectedViews(projectedView, view);
|
||||
}
|
||||
}
|
||||
} else if ((nodeDef.childFlags & NodeFlags.ProjectedTemplate) === 0) {
|
||||
// a parent with leafs
|
||||
// no child is a component,
|
||||
// then skip the children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkAndUpdateNodeInline(
|
||||
view: ViewData, nodeDef: NodeDef, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
|
||||
v6?: any, v7?: any, v8?: any, v9?: any): boolean {
|
||||
@ -453,7 +487,7 @@ function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
|
||||
if (queryList.dirty) {
|
||||
throw expressionChangedAfterItHasBeenCheckedError(
|
||||
Services.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query!.id} not dirty`,
|
||||
`Query ${nodeDef.query!.id} dirty`, (view.state & ViewState.FirstCheck) !== 0);
|
||||
`Query ${nodeDef.query!.id} dirty`, (view.state & ViewState.BeforeFirstCheck) !== 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,6 +503,7 @@ export function destroyView(view: ViewData) {
|
||||
view.disposables[i]();
|
||||
}
|
||||
}
|
||||
detachProjectedView(view);
|
||||
if (view.renderer.destroyNode) {
|
||||
destroyViewNodes(view);
|
||||
}
|
||||
@ -493,7 +528,9 @@ function destroyViewNodes(view: ViewData) {
|
||||
enum ViewAction {
|
||||
CreateViewNodes,
|
||||
CheckNoChanges,
|
||||
CheckNoChangesProjectedViews,
|
||||
CheckAndUpdate,
|
||||
CheckAndUpdateProjectedViews,
|
||||
Destroy
|
||||
}
|
||||
|
||||
@ -542,18 +579,44 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
||||
const viewState = view.state;
|
||||
switch (action) {
|
||||
case ViewAction.CheckNoChanges:
|
||||
if ((viewState & ViewState.ChecksEnabled) &&
|
||||
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
|
||||
checkNoChangesView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, ViewAction.CheckNoChangesProjectedViews);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckNoChangesProjectedViews:
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if (viewState & ViewState.CheckProjectedView) {
|
||||
checkNoChangesView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckAndUpdate:
|
||||
if ((viewState & ViewState.ChecksEnabled) &&
|
||||
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
|
||||
checkAndUpdateView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, ViewAction.CheckAndUpdateProjectedViews);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckAndUpdateProjectedViews:
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if (viewState & ViewState.CheckProjectedView) {
|
||||
checkAndUpdateView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.Destroy:
|
||||
// Note: destroyView recurses over all views,
|
||||
// so we don't need to special case projected views here.
|
||||
destroyView(view);
|
||||
break;
|
||||
case ViewAction.CreateViewNodes:
|
||||
@ -562,6 +625,11 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
||||
}
|
||||
}
|
||||
|
||||
function execProjectedViewsAction(view: ViewData, action: ViewAction) {
|
||||
execEmbeddedViewsAction(view, action);
|
||||
execComponentViewsAction(view, action);
|
||||
}
|
||||
|
||||
function execQueriesAction(
|
||||
view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags,
|
||||
checkType: CheckType) {
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ElementData, Services, ViewData} from './types';
|
||||
import {RenderNodeAction, declaredViewContainer, renderNode, visitRootRenderNodes} from './util';
|
||||
import {ElementData, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewState} from './types';
|
||||
import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, visitRootRenderNodes} from './util';
|
||||
|
||||
export function attachEmbeddedView(
|
||||
parentView: ViewData, elementData: ElementData, viewIndex: number | undefined | null,
|
||||
@ -18,14 +18,7 @@ export function attachEmbeddedView(
|
||||
}
|
||||
view.viewContainerParent = parentView;
|
||||
addToArray(embeddedViews, viewIndex !, view);
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (dvcElementData && dvcElementData !== elementData) {
|
||||
let projectedViews = dvcElementData.template._projectedViews;
|
||||
if (!projectedViews) {
|
||||
projectedViews = dvcElementData.template._projectedViews = [];
|
||||
}
|
||||
projectedViews.push(view);
|
||||
}
|
||||
attachProjectedView(elementData, view);
|
||||
|
||||
Services.dirtyParentQueries(view);
|
||||
|
||||
@ -33,6 +26,43 @@ export function attachEmbeddedView(
|
||||
renderAttachEmbeddedView(elementData, prevView, view);
|
||||
}
|
||||
|
||||
function attachProjectedView(vcElementData: ElementData, view: ViewData) {
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (!dvcElementData || dvcElementData === vcElementData ||
|
||||
view.state & ViewState.IsProjectedView) {
|
||||
return;
|
||||
}
|
||||
// Note: For performance reasons, we
|
||||
// - add a view to template._projectedViews only 1x throughout its lifetime,
|
||||
// and remove it not until the view is destroyed.
|
||||
// (hard, as when a parent view is attached/detached we would need to attach/detach all
|
||||
// nested projected views as well, even accross component boundaries).
|
||||
// - don't track the insertion order of views in the projected views array
|
||||
// (hard, as when the views of the same template are inserted different view containers)
|
||||
view.state |= ViewState.IsProjectedView;
|
||||
let projectedViews = dvcElementData.template._projectedViews;
|
||||
if (!projectedViews) {
|
||||
projectedViews = dvcElementData.template._projectedViews = [];
|
||||
}
|
||||
projectedViews.push(view);
|
||||
// Note: we are changing the NodeDef here as we cannot calculate
|
||||
// the fact whether a template is used for projection during compilation.
|
||||
markNodeAsProjectedTemplate(view.parent !.def, view.parentNodeDef !);
|
||||
}
|
||||
|
||||
function markNodeAsProjectedTemplate(viewDef: ViewDefinition, nodeDef: NodeDef) {
|
||||
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
|
||||
return;
|
||||
}
|
||||
viewDef.nodeFlags |= NodeFlags.ProjectedTemplate;
|
||||
nodeDef.flags |= NodeFlags.ProjectedTemplate;
|
||||
let parentNodeDef = nodeDef.parent;
|
||||
while (parentNodeDef) {
|
||||
parentNodeDef.childFlags |= NodeFlags.ProjectedTemplate;
|
||||
parentNodeDef = parentNodeDef.parent;
|
||||
}
|
||||
}
|
||||
|
||||
export function detachEmbeddedView(elementData: ElementData, viewIndex?: number): ViewData|null {
|
||||
const embeddedViews = elementData.viewContainer !._embeddedViews;
|
||||
if (viewIndex == null || viewIndex >= embeddedViews.length) {
|
||||
@ -45,12 +75,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex?: number)
|
||||
view.viewContainerParent = null;
|
||||
removeFromArray(embeddedViews, viewIndex);
|
||||
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (dvcElementData && dvcElementData !== elementData) {
|
||||
const projectedViews = dvcElementData.template._projectedViews;
|
||||
removeFromArray(projectedViews, projectedViews.indexOf(view));
|
||||
}
|
||||
|
||||
// See attachProjectedView for why we don't update projectedViews here.
|
||||
Services.dirtyParentQueries(view);
|
||||
|
||||
renderDetachView(view);
|
||||
@ -58,6 +83,20 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex?: number)
|
||||
return view;
|
||||
}
|
||||
|
||||
export function detachProjectedView(view: ViewData) {
|
||||
if (!(view.state & ViewState.IsProjectedView)) {
|
||||
return;
|
||||
}
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (dvcElementData) {
|
||||
const projectedViews = dvcElementData.template._projectedViews;
|
||||
if (projectedViews) {
|
||||
removeFromArray(projectedViews, projectedViews.indexOf(view));
|
||||
Services.dirtyParentQueries(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function moveEmbeddedView(
|
||||
elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData {
|
||||
const embeddedViews = elementData.viewContainer !._embeddedViews;
|
||||
|
@ -13,8 +13,8 @@ import {EventEmitter} from '../event_emitter';
|
||||
*
|
||||
* The most common use of this service is to optimize performance when starting a work consisting of
|
||||
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
||||
* Angular. Such tasks can be kicked off via {@link runOutsideAngular} and if needed, these tasks
|
||||
* can reenter the Angular zone via {@link run}.
|
||||
* Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
||||
* can reenter the Angular zone via {@link #run}.
|
||||
*
|
||||
* <!-- TODO: add/fix links to:
|
||||
* - docs explaining zones and the use of zones in Angular and change-detection
|
||||
@ -132,7 +132,7 @@ export class NgZone {
|
||||
* the function.
|
||||
*
|
||||
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
||||
* outside of the Angular zone (typically started via {@link runOutsideAngular}).
|
||||
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
||||
*
|
||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||
* within the Angular zone.
|
||||
@ -151,13 +151,14 @@ export class NgZone {
|
||||
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
||||
* the function.
|
||||
*
|
||||
* Running functions via `runOutsideAngular` allows you to escape Angular's zone and do work that
|
||||
* Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
|
||||
* work that
|
||||
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
||||
*
|
||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||
* outside of the Angular zone.
|
||||
*
|
||||
* Use {@link run} to reenter the Angular zone and do work that updates the application model.
|
||||
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
||||
*/
|
||||
runOutsideAngular(fn: () => any): any { return this.outer.run(fn); }
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* 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 {Injector} from '@angular/core';
|
||||
import {APP_INITIALIZER, ApplicationInitStatus} from '../src/application_init';
|
||||
import {TestBed, async, inject} from '../testing';
|
||||
|
||||
@ -14,11 +15,13 @@ export function main() {
|
||||
|
||||
it('should return true for `done`',
|
||||
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
|
||||
status.runInitializers();
|
||||
expect(status.done).toBe(true);
|
||||
})));
|
||||
|
||||
it('should return a promise that resolves immediately for `donePromise`',
|
||||
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
|
||||
status.runInitializers();
|
||||
status.donePromise.then(() => { expect(status.done).toBe(true); });
|
||||
})));
|
||||
});
|
||||
@ -26,15 +29,32 @@ export function main() {
|
||||
describe('with async initializers', () => {
|
||||
let resolve: (result: any) => void;
|
||||
let promise: Promise<any>;
|
||||
let completerResolver = false;
|
||||
beforeEach(() => {
|
||||
let initializerFactory = (injector: Injector) => {
|
||||
return () => {
|
||||
const initStatus = injector.get(ApplicationInitStatus);
|
||||
initStatus.donePromise.then(() => { expect(completerResolver).toBe(true); });
|
||||
}
|
||||
};
|
||||
promise = new Promise((res) => { resolve = res; });
|
||||
TestBed.configureTestingModule(
|
||||
{providers: [{provide: APP_INITIALIZER, multi: true, useValue: () => promise}]});
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: APP_INITIALIZER, multi: true, useValue: () => promise},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useFactory: initializerFactory,
|
||||
deps: [Injector]
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should update the status once all async initializers are done',
|
||||
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
|
||||
let completerResolver = false;
|
||||
status.runInitializers();
|
||||
|
||||
setTimeout(() => {
|
||||
completerResolver = true;
|
||||
resolve(null);
|
||||
|
@ -103,20 +103,33 @@ export function main() {
|
||||
describe('ApplicationRef', () => {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
||||
|
||||
it('should throw when reentering tick', inject([ApplicationRef], (ref: ApplicationRef_) => {
|
||||
const view = jasmine.createSpyObj('view', ['detach', 'attachToAppRef']);
|
||||
const viewRef = jasmine.createSpyObj(
|
||||
'viewRef', ['detectChanges', 'detachFromAppRef', 'attachToAppRef']);
|
||||
viewRef.internalView = view;
|
||||
view.ref = viewRef;
|
||||
it('should throw when reentering tick', () => {
|
||||
@Component({template: '{{reenter()}}'})
|
||||
class ReenteringComponent {
|
||||
reenterCount = 1;
|
||||
reenterErr: any;
|
||||
|
||||
constructor(private appRef: ApplicationRef) {}
|
||||
|
||||
reenter() {
|
||||
if (this.reenterCount--) {
|
||||
try {
|
||||
ref.attachView(viewRef);
|
||||
viewRef.detectChanges.and.callFake(() => ref.tick());
|
||||
expect(() => ref.tick()).toThrowError('ApplicationRef.tick is called recursively');
|
||||
} finally {
|
||||
ref.detachView(viewRef);
|
||||
this.appRef.tick();
|
||||
} catch (e) {
|
||||
this.reenterErr = e;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [ReenteringComponent]})
|
||||
.createComponent(ReenteringComponent);
|
||||
const appRef = TestBed.get(ApplicationRef) as ApplicationRef;
|
||||
appRef.attachView(fixture.componentRef.hostView);
|
||||
appRef.tick();
|
||||
expect(fixture.componentInstance.reenterErr.message)
|
||||
.toBe('ApplicationRef.tick is called recursively');
|
||||
});
|
||||
|
||||
describe('APP_BOOTSTRAP_LISTENER', () => {
|
||||
let capturedCompRefs: ComponentRef<any>[];
|
||||
@ -184,11 +197,9 @@ export function main() {
|
||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]))
|
||||
.then(() => expect(false).toBe(true), (e) => {
|
||||
expect(e).toBe('Test');
|
||||
// Note: if the modules throws an error during construction,
|
||||
// we don't have an injector and therefore no way of
|
||||
// getting the exception handler. So
|
||||
// the error is only rethrown but not logged via the exception handler.
|
||||
expect(mockConsole.res).toEqual([]);
|
||||
// Error rethrown will be seen by the exception handler since it's after
|
||||
// construction.
|
||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||
});
|
||||
}));
|
||||
|
||||
@ -281,11 +292,9 @@ export function main() {
|
||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]));
|
||||
expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test');
|
||||
// Note: if the modules throws an error during construction,
|
||||
// we don't have an injector and therefore no way of
|
||||
// getting the exception handler. So
|
||||
// the error is only rethrown but not logged via the exception handler.
|
||||
expect(mockConsole.res).toEqual([]);
|
||||
// Error rethrown will be seen by the exception handler since it's after
|
||||
// construction.
|
||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||
}));
|
||||
|
||||
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
|
||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/src/test_bindings';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactory2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactory2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
import {DomElementSchemaRegistry} from '../../../compiler/index';
|
||||
import {MockSchemaRegistry} from '../../../compiler/testing/index';
|
||||
|
||||
@ -765,6 +766,7 @@ export function main() {
|
||||
try {
|
||||
ctx.detectChanges(false);
|
||||
} catch (e) {
|
||||
expect(e.message).toBe('Boom!');
|
||||
errored = true;
|
||||
}
|
||||
expect(errored).toBe(true);
|
||||
@ -776,7 +778,8 @@ export function main() {
|
||||
try {
|
||||
ctx.detectChanges(false);
|
||||
} catch (e) {
|
||||
throw new Error('Second detectChanges() should not have run detection.');
|
||||
expect(e.message).toBe('Boom!');
|
||||
throw new Error('Second detectChanges() should not have called ngOnInit.');
|
||||
}
|
||||
expect(directiveLog.filter(['ngOnInit'])).toEqual([]);
|
||||
}));
|
||||
@ -1175,6 +1178,21 @@ export function main() {
|
||||
expect(renderLog.log).toEqual([]);
|
||||
}));
|
||||
|
||||
it('Detached should disable OnPush', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<push-cmp [value]="value"></push-cmp>');
|
||||
ctx.componentInstance.value = 0;
|
||||
ctx.detectChanges();
|
||||
renderLog.clear();
|
||||
|
||||
const cmp: CompWithRef = queryDirs(ctx.debugElement, PushComp)[0];
|
||||
cmp.changeDetectorRef.detach();
|
||||
|
||||
ctx.componentInstance.value = 1;
|
||||
ctx.detectChanges();
|
||||
|
||||
expect(renderLog.log).toEqual([]);
|
||||
}));
|
||||
|
||||
it('Detached view can be checked locally', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<wrap-comp-with-ref></wrap-comp-with-ref>');
|
||||
const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0];
|
||||
@ -1225,7 +1243,6 @@ export function main() {
|
||||
|
||||
ctx.detectChanges();
|
||||
expect(cmp.renderCount).toBe(count);
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
@ -1277,6 +1294,120 @@ export function main() {
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
|
||||
describe('projected views', () => {
|
||||
let log: string[];
|
||||
|
||||
@Directive({selector: '[i]'})
|
||||
class DummyDirective {
|
||||
@Input()
|
||||
i: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'main-cmp',
|
||||
template:
|
||||
`<span [i]="log('start')"></span><outer-cmp><ng-template><span [i]="log('tpl')"></span></ng-template></outer-cmp>`
|
||||
})
|
||||
class MainComp {
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`main-${id}`); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'outer-cmp',
|
||||
template:
|
||||
`<span [i]="log('start')"></span><inner-cmp [outerTpl]="tpl"><ng-template><span [i]="log('tpl')"></span></ng-template></inner-cmp>`
|
||||
})
|
||||
class OuterComp {
|
||||
@ContentChild(TemplateRef)
|
||||
tpl: TemplateRef<any>;
|
||||
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`outer-${id}`); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'inner-cmp',
|
||||
template:
|
||||
`<span [i]="log('start')"></span>><ng-container [ngTemplateOutlet]="outerTpl"></ng-container><ng-container [ngTemplateOutlet]="tpl"></ng-container>`
|
||||
})
|
||||
class InnerComp {
|
||||
@ContentChild(TemplateRef)
|
||||
tpl: TemplateRef<any>;
|
||||
|
||||
@Input()
|
||||
outerTpl: TemplateRef<any>
|
||||
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`inner-${id}`); }
|
||||
}
|
||||
|
||||
let ctx: ComponentFixture<MainComp>;
|
||||
let mainComp: MainComp;
|
||||
let outerComp: OuterComp;
|
||||
let innerComp: InnerComp;
|
||||
|
||||
beforeEach(() => {
|
||||
log = [];
|
||||
ctx = TestBed
|
||||
.configureTestingModule(
|
||||
{declarations: [MainComp, OuterComp, InnerComp, DummyDirective]})
|
||||
.createComponent(MainComp);
|
||||
mainComp = ctx.componentInstance;
|
||||
outerComp = ctx.debugElement.query(By.directive(OuterComp)).injector.get(OuterComp);
|
||||
innerComp = ctx.debugElement.query(By.directive(InnerComp)).injector.get(InnerComp);
|
||||
});
|
||||
|
||||
it('should dirty check projected views in regular order', () => {
|
||||
ctx.detectChanges(false);
|
||||
expect(log).toEqual(
|
||||
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
|
||||
log = [];
|
||||
ctx.detectChanges(false);
|
||||
expect(log).toEqual(
|
||||
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
});
|
||||
|
||||
it('should not dirty check projected views if neither the declaration nor the insertion place is dirty checked',
|
||||
() => {
|
||||
ctx.detectChanges(false);
|
||||
log = [];
|
||||
mainComp.cdRef.detach();
|
||||
ctx.detectChanges(false);
|
||||
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
|
||||
it('should dirty check projected views if the insertion place is dirty checked', () => {
|
||||
ctx.detectChanges(false);
|
||||
log = [];
|
||||
|
||||
innerComp.cdRef.detectChanges();
|
||||
expect(log).toEqual(['inner-start', 'main-tpl', 'outer-tpl']);
|
||||
});
|
||||
|
||||
it('should dirty check projected views if the declaration place is dirty checked', () => {
|
||||
ctx.detectChanges(false);
|
||||
log = [];
|
||||
innerComp.cdRef.detach();
|
||||
mainComp.cdRef.detectChanges();
|
||||
|
||||
expect(log).toEqual(['main-start', 'outer-start', 'main-tpl', 'outer-tpl']);
|
||||
|
||||
log = [];
|
||||
outerComp.cdRef.detectChanges();
|
||||
|
||||
expect(log).toEqual(['outer-start', 'outer-tpl']);
|
||||
|
||||
log = [];
|
||||
outerComp.cdRef.detach();
|
||||
mainComp.cdRef.detectChanges();
|
||||
|
||||
expect(log).toEqual(['main-start', 'main-tpl']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('class binding', () => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Compiler, ComponentFactory, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
|
||||
import {Compiler, ComponentFactory, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
|
||||
@ -1480,16 +1480,18 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
|
||||
const tc = fixture.debugElement.children[0];
|
||||
|
||||
try {
|
||||
const errorHandler = tc.injector.get(ErrorHandler);
|
||||
let err: any;
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => err = e);
|
||||
tc.injector.get(DirectiveEmittingEvent).fireEvent('boom');
|
||||
} catch (e) {
|
||||
const c = getDebugContext(e);
|
||||
|
||||
expect(err).toBeTruthy();
|
||||
const c = getDebugContext(err);
|
||||
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN');
|
||||
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
|
||||
expect((<Injector>c.injector).get).toBeTruthy();
|
||||
expect(c.context).toBe(fixture.componentInstance);
|
||||
expect(c.references['local']).toBeDefined();
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -536,6 +536,52 @@ export function main() {
|
||||
expect(q.query.length).toBe(0);
|
||||
});
|
||||
|
||||
// Note: This tests is just document our current behavior, which we do
|
||||
// for performance reasons.
|
||||
it('should not affected queries for projected templates if views are detached or moved', () => {
|
||||
const template =
|
||||
'<manual-projecting #q><ng-template let-x="x"><div [text]="x"></div></ng-template></manual-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'] as ManualProjecting;
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'});
|
||||
const view2 = q.vc.createEmbeddedView(q.template, {'x': '2'});
|
||||
view.detectChanges();
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
|
||||
|
||||
q.vc.detach(1);
|
||||
q.vc.detach(0);
|
||||
|
||||
view.detectChanges();
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
|
||||
|
||||
q.vc.insert(view2);
|
||||
q.vc.insert(view1);
|
||||
|
||||
view.detectChanges();
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
|
||||
});
|
||||
|
||||
it('should remove manually projected templates if their parent view is destroyed', () => {
|
||||
const template = `
|
||||
<manual-projecting #q><ng-template #tpl><div text="1"></div></ng-template></manual-projecting>
|
||||
<div *ngIf="shouldShow">
|
||||
<ng-container [ngTemplateOutlet]="tpl"></ng-container>
|
||||
</div>
|
||||
`;
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
view.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.length).toBe(1);
|
||||
|
||||
view.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
expect(q.query.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not throw if a content template is queried and created in the view during change detection',
|
||||
() => {
|
||||
@Component(
|
||||
|
@ -6,15 +6,19 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ContentChild, Directive, InjectionToken, Injector, Input, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ApplicationRef, Component, ComponentRef, ContentChild, Directive, ErrorHandler, EventEmitter, HostListener, InjectionToken, Injector, Input, NgModule, NgModuleRef, NgZone, Output, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChildren, ViewContainerRef, destroyPlatform} from '@angular/core';
|
||||
import {TestBed, async, fakeAsync, inject, tick} from '@angular/core/testing';
|
||||
import {BrowserModule, By, DOCUMENT} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
|
||||
declareTestsUsingBootstrap();
|
||||
}
|
||||
|
||||
function declareTests({useJit}: {useJit: boolean}) {
|
||||
@ -365,6 +369,132 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
expect(testDirs[1].tpl).toBeDefined();
|
||||
expect(testDirs[2].tpl).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not add ng-version for dynamically created components', () => {
|
||||
@Component({template: ''})
|
||||
class App {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [App], entryComponents: [App]})
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
const modRef = TestBed.configureTestingModule({imports: [MyModule]})
|
||||
.get(NgModuleRef) as NgModuleRef<MyModule>;
|
||||
const compRef =
|
||||
modRef.componentFactoryResolver.resolveComponentFactory(App).create(Injector.NULL);
|
||||
|
||||
expect(getDOM().hasAttribute(compRef.location.nativeElement, 'ng-version')).toBe(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function declareTestsUsingBootstrap() {
|
||||
// Place to put reproductions for regressions
|
||||
describe('regressions using bootstrap', () => {
|
||||
const COMP_SELECTOR = 'root-comp';
|
||||
|
||||
class MockConsole {
|
||||
errors: any[][] = [];
|
||||
error(...s: any[]): void { this.errors.push(s); }
|
||||
}
|
||||
|
||||
let logger: MockConsole;
|
||||
let errorHandler: ErrorHandler;
|
||||
|
||||
beforeEach(inject([DOCUMENT], (doc: any) => {
|
||||
destroyPlatform();
|
||||
const el = getDOM().createElement(COMP_SELECTOR, doc);
|
||||
getDOM().appendChild(doc.body, el);
|
||||
|
||||
logger = new MockConsole();
|
||||
errorHandler = new ErrorHandler();
|
||||
errorHandler._console = logger as any;
|
||||
}));
|
||||
|
||||
afterEach(() => { destroyPlatform(); });
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
// This test needs a real DOM....
|
||||
|
||||
it('should keep change detecting if there was an error', (done) => {
|
||||
@Component({
|
||||
selector: COMP_SELECTOR,
|
||||
template:
|
||||
'<button (click)="next()"></button><button (click)="nextAndThrow()"></button><button (dirClick)="nextAndThrow()"></button><span>Value:{{value}}</span><span>{{throwIfNeeded()}}</span>'
|
||||
})
|
||||
class ErrorComp {
|
||||
value = 0;
|
||||
thrownValue = 0;
|
||||
next() { this.value++; }
|
||||
nextAndThrow() {
|
||||
this.value++;
|
||||
this.throwIfNeeded();
|
||||
}
|
||||
throwIfNeeded() {
|
||||
NgZone.assertInAngularZone();
|
||||
if (this.thrownValue !== this.value) {
|
||||
this.thrownValue = this.value;
|
||||
throw new Error(`Error: ${this.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirClick]'})
|
||||
class EventDir {
|
||||
@Output()
|
||||
dirClick = new EventEmitter();
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(event: any) { this.dirClick.next(event); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
declarations: [ErrorComp, EventDir],
|
||||
bootstrap: [ErrorComp],
|
||||
providers: [{provide: ErrorHandler, useValue: errorHandler}],
|
||||
})
|
||||
class TestModule {
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(TestModule).then((ref) => {
|
||||
NgZone.assertNotInAngularZone();
|
||||
const appRef = ref.injector.get(ApplicationRef) as ApplicationRef;
|
||||
const compRef = appRef.components[0] as ComponentRef<ErrorComp>;
|
||||
const compEl = compRef.location.nativeElement;
|
||||
const nextBtn = compEl.children[0];
|
||||
const nextAndThrowBtn = compEl.children[1];
|
||||
const nextAndThrowDirBtn = compEl.children[2];
|
||||
|
||||
nextBtn.click();
|
||||
assertValueAndErrors(compEl, 1, 0);
|
||||
nextBtn.click();
|
||||
assertValueAndErrors(compEl, 2, 2);
|
||||
|
||||
nextAndThrowBtn.click();
|
||||
assertValueAndErrors(compEl, 3, 4);
|
||||
nextAndThrowBtn.click();
|
||||
assertValueAndErrors(compEl, 4, 6);
|
||||
|
||||
nextAndThrowDirBtn.click();
|
||||
assertValueAndErrors(compEl, 5, 8);
|
||||
nextAndThrowDirBtn.click();
|
||||
assertValueAndErrors(compEl, 6, 10);
|
||||
|
||||
// Assert that there were no more errors
|
||||
expect(logger.errors.length).toBe(12);
|
||||
done();
|
||||
});
|
||||
|
||||
function assertValueAndErrors(compEl: any, value: number, errorIndex: number) {
|
||||
expect(compEl).toHaveText(`Value:${value}`);
|
||||
expect(logger.errors[errorIndex][0]).toBe('ERROR');
|
||||
expect(logger.errors[errorIndex][1].message).toBe(`Error: ${value}`);
|
||||
expect(logger.errors[errorIndex + 1][0]).toBe('ERROR CONTEXT');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {ResourceLoader} from '@angular/compiler';
|
||||
import {SourceMap} from '@angular/compiler/src/output/source_map';
|
||||
import {extractSourceMap, originalPositionFor} from '@angular/compiler/test/output/source_map_util';
|
||||
import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock';
|
||||
import {Attribute, Component, Directive, ɵglobal} from '@angular/core';
|
||||
import {Attribute, Component, Directive, ErrorHandler, ɵglobal} from '@angular/core';
|
||||
import {getErrorLogger} from '@angular/core/src/errors';
|
||||
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||
|
||||
@ -231,11 +231,10 @@ export function main() {
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
|
||||
let error: any;
|
||||
try {
|
||||
const errorHandler = TestBed.get(ErrorHandler);
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e);
|
||||
comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
// the stack should point to the binding
|
||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||
line: 2,
|
||||
|
@ -652,6 +652,46 @@ export function main() {
|
||||
expect(compEl.nativeElement).toHaveText('1');
|
||||
});
|
||||
|
||||
it('should inject ChangeDetectorRef of a same element component into a directive', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [PushComponentNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef]});
|
||||
const cf = createComponentFixture(
|
||||
'<div componentNeedsChangeDetectorRef directiveNeedsChangeDetectorRef></div>');
|
||||
cf.detectChanges();
|
||||
const compEl = cf.debugElement.children[0];
|
||||
const comp = compEl.injector.get(PushComponentNeedsChangeDetectorRef);
|
||||
const dir = compEl.injector.get(DirectiveNeedsChangeDetectorRef);
|
||||
comp.counter = 1;
|
||||
cf.detectChanges();
|
||||
expect(compEl.nativeElement).toHaveText('0');
|
||||
dir.changeDetectorRef.markForCheck();
|
||||
cf.detectChanges();
|
||||
expect(compEl.nativeElement).toHaveText('1');
|
||||
});
|
||||
|
||||
it(`should not inject ChangeDetectorRef of a parent element's component into a directive`, () => {
|
||||
TestBed
|
||||
.configureTestingModule({
|
||||
declarations: [PushComponentNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef]
|
||||
})
|
||||
.overrideComponent(
|
||||
PushComponentNeedsChangeDetectorRef,
|
||||
{set: {template: '<ng-content></ng-content>{{counter}}'}});
|
||||
const cf = createComponentFixture(
|
||||
'<div componentNeedsChangeDetectorRef><div directiveNeedsChangeDetectorRef></div></div>');
|
||||
cf.detectChanges();
|
||||
const compEl = cf.debugElement.children[0];
|
||||
const comp = compEl.injector.get(PushComponentNeedsChangeDetectorRef);
|
||||
const dirEl = compEl.children[0];
|
||||
const dir = dirEl.injector.get(DirectiveNeedsChangeDetectorRef);
|
||||
comp.counter = 1;
|
||||
cf.detectChanges();
|
||||
expect(compEl.nativeElement).toHaveText('0');
|
||||
dir.changeDetectorRef.markForCheck();
|
||||
cf.detectChanges();
|
||||
expect(compEl.nativeElement).toHaveText('0');
|
||||
});
|
||||
|
||||
it('should inject ViewContainerRef', () => {
|
||||
TestBed.configureTestingModule({declarations: [NeedsViewContainerRef]});
|
||||
const el = createComponent('<div needsViewContainerRef></div>');
|
||||
|
@ -230,7 +230,7 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
it('should stop dirty checking views that threw errors in change detection', () => {
|
||||
it('should not stop dirty checking views that threw errors in change detection', () => {
|
||||
class AComp {
|
||||
a: any;
|
||||
}
|
||||
@ -255,8 +255,8 @@ export function main() {
|
||||
expect(update).toHaveBeenCalled();
|
||||
|
||||
update.calls.reset();
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
|
||||
expect(update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -6,9 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {ErrorHandler, Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ArgumentType, BindingFlags, DebugContext, NodeDef, NodeFlags, OutputType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper';
|
||||
@ -282,17 +283,14 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should report debug info on event errors', () => {
|
||||
const handleErrorSpy = spyOn(TestBed.get(ErrorHandler), 'handleError');
|
||||
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
||||
NodeFlags.None, null !, null !, 0, 'button', null !, null !, [[null !, 'click']],
|
||||
() => { throw new Error('Test'); })]));
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
const err = handleErrorSpy.calls.mostRecent().args[0];
|
||||
expect(err).toBeTruthy();
|
||||
expect(err.message).toBe('Test');
|
||||
const debugCtx = getDebugContext(err);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, Renderer2, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, ErrorHandler, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, Renderer2, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ArgumentType, BindingFlags, DebugContext, DepFlags, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {TestBed, inject, withModule} from '@angular/core/testing';
|
||||
@ -381,6 +381,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should report debug info on event errors', () => {
|
||||
const handleErrorSpy = spyOn(TestBed.get(ErrorHandler), 'handleError');
|
||||
let emitter = new EventEmitter<any>();
|
||||
|
||||
class SomeService {
|
||||
@ -395,12 +396,8 @@ export function main() {
|
||||
NodeFlags.None, null !, 0, SomeService, [], null !, {emitter: 'someEventName'})
|
||||
]));
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
emitter.emit('someEventInstance');
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
const err = handleErrorSpy.calls.mostRecent().args[0];
|
||||
expect(err).toBeTruthy();
|
||||
const debugCtx = getDebugContext(err);
|
||||
expect(debugCtx.view).toBe(view);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
|
||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
|
||||
import {AsyncTestCompleter} from './async_test_completer';
|
||||
import {ComponentFixture} from './component_fixture';
|
||||
import {MetadataOverride} from './metadata_override';
|
||||
@ -283,6 +283,9 @@ export class TestBed implements Injector {
|
||||
const ngZoneInjector = ReflectiveInjector.resolveAndCreate(
|
||||
[{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
||||
this._moduleRef = this._moduleWithComponentFactories.ngModuleFactory.create(ngZoneInjector);
|
||||
// ApplicationInitStatus.runInitializers() is marked @internal to core. So casting to any
|
||||
// before accessing it.
|
||||
(this._moduleRef.injector.get(ApplicationInitStatus) as any).runInitializers();
|
||||
this._instantiated = true;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ const resolvedPromise = Promise.resolve(null);
|
||||
* This directive can be used by itself or as part of a larger form. All you need is the
|
||||
* `ngModel` selector to activate it.
|
||||
*
|
||||
* It accepts a domain model as an optional {@link @Input}. If you have a one-way binding
|
||||
* It accepts a domain model as an optional {@link Input}. If you have a one-way binding
|
||||
* to `ngModel` with `[]` syntax, changing the value of the domain model in the component
|
||||
* class will set the value in the view. If you have a two-way binding with `[()]` syntax
|
||||
* (also known as 'banana-box syntax'), the value in the UI will always be synced back to
|
||||
|
@ -45,12 +45,12 @@ export const formControlBinding: any = {
|
||||
* {@link AbstractControl}.
|
||||
*
|
||||
* **Set the value**: You can pass in an initial value when instantiating the {@link FormControl},
|
||||
* or you can set it programmatically later using {@link AbstractControl.setValue} or
|
||||
* {@link AbstractControl.patchValue}.
|
||||
* or you can set it programmatically later using {@link AbstractControl#setValue} or
|
||||
* {@link AbstractControl#patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* subscribe to the {@link AbstractControl#valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
|
@ -45,7 +45,7 @@ export const controlNameBinding: any = {
|
||||
* closest {@link FormGroup} or {@link FormArray} above it.
|
||||
*
|
||||
* **Access the control**: You can access the {@link FormControl} associated with
|
||||
* this directive by using the {@link AbstractControl.get} method.
|
||||
* this directive by using the {@link AbstractControl#get} method.
|
||||
* Ex: `this.form.get('first');`
|
||||
*
|
||||
* **Get value**: the `value` property is always synced and available on the {@link FormControl}.
|
||||
@ -53,11 +53,11 @@ export const controlNameBinding: any = {
|
||||
*
|
||||
* **Set value**: You can set an initial value for the control when instantiating the
|
||||
* {@link FormControl}, or you can set it programmatically later using
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}.
|
||||
* {@link AbstractControl#setValue} or {@link AbstractControl#patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* subscribe to the {@link AbstractControl#valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
|
@ -34,11 +34,11 @@ export const formDirectiveProvider: any = {
|
||||
*
|
||||
* **Set value**: You can set the form's initial value when instantiating the
|
||||
* {@link FormGroup}, or you can set it programmatically later using the {@link FormGroup}'s
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue} methods.
|
||||
* {@link AbstractControl#setValue} or {@link AbstractControl#patchValue} methods.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the form, you can subscribe
|
||||
* to the {@link FormGroup}'s {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* its {@link AbstractControl.statusChanges} event to be notified when the validation status is
|
||||
* to the {@link FormGroup}'s {@link AbstractControl#valueChanges} event. You can also listen to
|
||||
* its {@link AbstractControl#statusChanges} event to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* Furthermore, you can listen to the directive's `ngSubmit` event to be notified when the user has
|
||||
|
@ -40,7 +40,7 @@ export const formGroupNameProvider: any = {
|
||||
* controls into their own nested object.
|
||||
*
|
||||
* **Access the group**: You can access the associated {@link FormGroup} using the
|
||||
* {@link AbstractControl.get} method. Ex: `this.form.get('name')`.
|
||||
* {@link AbstractControl#get} method. Ex: `this.form.get('name')`.
|
||||
*
|
||||
* You can also access individual controls within the group using dot syntax.
|
||||
* Ex: `this.form.get('name.first')`
|
||||
@ -50,11 +50,11 @@ export const formGroupNameProvider: any = {
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the {@link FormGroup}, or you can set it programmatically later using
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}.
|
||||
* {@link AbstractControl#setValue} or {@link AbstractControl#patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the group, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* subscribe to the {@link AbstractControl#valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
@ -111,7 +111,7 @@ export const formArrayNameProvider: any = {
|
||||
* form controls dynamically.
|
||||
*
|
||||
* **Access the array**: You can access the associated {@link FormArray} using the
|
||||
* {@link AbstractControl.get} method on the parent {@link FormGroup}.
|
||||
* {@link AbstractControl#get} method on the parent {@link FormGroup}.
|
||||
* Ex: `this.form.get('cities')`.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
@ -119,16 +119,16 @@ export const formArrayNameProvider: any = {
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the {@link FormArray}, or you can set the value programmatically later using the
|
||||
* {@link FormArray}'s {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}
|
||||
* {@link FormArray}'s {@link AbstractControl#setValue} or {@link AbstractControl#patchValue}
|
||||
* methods.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the array, you can
|
||||
* subscribe to the {@link FormArray}'s {@link AbstractControl.valueChanges} event. You can also
|
||||
* listen to its {@link AbstractControl.statusChanges} event to be notified when the validation
|
||||
* subscribe to the {@link FormArray}'s {@link AbstractControl#valueChanges} event. You can also
|
||||
* listen to its {@link AbstractControl#statusChanges} event to be notified when the validation
|
||||
* status is re-calculated.
|
||||
*
|
||||
* **Add new controls**: You can add new controls to the {@link FormArray} dynamically by
|
||||
* calling its {@link FormArray.push} method.
|
||||
* calling its {@link FormArray#push} method.
|
||||
* Ex: `this.form.get('cities').push(new FormControl());`
|
||||
*
|
||||
* ### Example
|
||||
|
@ -659,24 +659,25 @@ export class FormControl extends AbstractControl {
|
||||
* If `emitViewToModelChange` is `true`, an ngModelChange event will be fired to update the
|
||||
* model. This is the default behavior if `emitViewToModelChange` is not specified.
|
||||
*/
|
||||
setValue(value: any, {onlySelf, emitEvent, emitModelToViewChange, emitViewToModelChange}: {
|
||||
setValue(value: any, options: {
|
||||
onlySelf?: boolean,
|
||||
emitEvent?: boolean,
|
||||
emitModelToViewChange?: boolean,
|
||||
emitViewToModelChange?: boolean
|
||||
} = {}): void {
|
||||
this._value = value;
|
||||
if (this._onChange.length && emitModelToViewChange !== false) {
|
||||
this._onChange.forEach((changeFn) => changeFn(this._value, emitViewToModelChange !== false));
|
||||
if (this._onChange.length && options.emitModelToViewChange !== false) {
|
||||
this._onChange.forEach(
|
||||
(changeFn) => changeFn(this._value, options.emitViewToModelChange !== false));
|
||||
}
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this.updateValueAndValidity(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the value of a control.
|
||||
*
|
||||
* This function is functionally the same as {@link FormControl.setValue} at this level.
|
||||
* It exists for symmetry with {@link FormGroup.patchValue} on `FormGroups` and `FormArrays`,
|
||||
* This function is functionally the same as {@link FormControl#setValue} at this level.
|
||||
* It exists for symmetry with {@link FormGroup#patchValue} on `FormGroups` and `FormArrays`,
|
||||
* where it does behave differently.
|
||||
*/
|
||||
patchValue(value: any, options: {
|
||||
@ -716,12 +717,11 @@ export class FormControl extends AbstractControl {
|
||||
* console.log(this.control.status); // 'DISABLED'
|
||||
* ```
|
||||
*/
|
||||
reset(formState: any = null, {onlySelf, emitEvent}: {onlySelf?: boolean,
|
||||
emitEvent?: boolean} = {}): void {
|
||||
reset(formState: any = null, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
this._applyFormState(formState);
|
||||
this.markAsPristine({onlySelf});
|
||||
this.markAsUntouched({onlySelf});
|
||||
this.setValue(this._value, {onlySelf, emitEvent});
|
||||
this.markAsPristine(options);
|
||||
this.markAsUntouched(options);
|
||||
this.setValue(this._value, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -842,7 +842,7 @@ export class FormGroup extends AbstractControl {
|
||||
* Registers a control with the group's list of controls.
|
||||
*
|
||||
* This method does not update value or validity of the control, so for
|
||||
* most cases you'll want to use {@link FormGroup.addControl} instead.
|
||||
* most cases you'll want to use {@link FormGroup#addControl} instead.
|
||||
*/
|
||||
registerControl(name: string, control: AbstractControl): AbstractControl {
|
||||
if (this.controls[name]) return this.controls[name];
|
||||
@ -886,7 +886,7 @@ export class FormGroup extends AbstractControl {
|
||||
* Check whether there is an enabled control with the given name in the group.
|
||||
*
|
||||
* It will return false for disabled controls. If you'd like to check for
|
||||
* existence in the group only, use {@link AbstractControl.get} instead.
|
||||
* existence in the group only, use {@link AbstractControl#get} instead.
|
||||
*/
|
||||
contains(controlName: string): boolean {
|
||||
return this.controls.hasOwnProperty(controlName) && this.controls[controlName].enabled;
|
||||
@ -914,15 +914,14 @@ export class FormGroup extends AbstractControl {
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
setValue(
|
||||
value: {[key: string]: any},
|
||||
{onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
setValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
this._checkAllValuesPresent(value);
|
||||
Object.keys(value).forEach(name => {
|
||||
this._throwIfControlMissing(name);
|
||||
this.controls[name].setValue(value[name], {onlySelf: true, emitEvent});
|
||||
this.controls[name].setValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this.updateValueAndValidity(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -946,15 +945,14 @@ export class FormGroup extends AbstractControl {
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
patchValue(
|
||||
value: {[key: string]: any},
|
||||
{onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
patchValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
Object.keys(value).forEach(name => {
|
||||
if (this.controls[name]) {
|
||||
this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent});
|
||||
this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
||||
}
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this.updateValueAndValidity(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -989,14 +987,13 @@ export class FormGroup extends AbstractControl {
|
||||
* console.log(this.form.get('first').status); // 'DISABLED'
|
||||
* ```
|
||||
*/
|
||||
reset(value: any = {}, {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
reset(value: any = {}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
this._forEachChild((control: AbstractControl, name: string) => {
|
||||
control.reset(value[name], {onlySelf: true, emitEvent});
|
||||
control.reset(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this._updatePristine({onlySelf});
|
||||
this._updateTouched({onlySelf});
|
||||
this.updateValueAndValidity(options);
|
||||
this._updatePristine(options);
|
||||
this._updateTouched(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1222,14 +1219,13 @@ export class FormArray extends AbstractControl {
|
||||
* console.log(arr.value); // ['Nancy', 'Drew']
|
||||
* ```
|
||||
*/
|
||||
setValue(value: any[], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
setValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
this._checkAllValuesPresent(value);
|
||||
value.forEach((newValue: any, index: number) => {
|
||||
this._throwIfControlMissing(index);
|
||||
this.at(index).setValue(newValue, {onlySelf: true, emitEvent});
|
||||
this.at(index).setValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this.updateValueAndValidity(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1252,14 +1248,13 @@ export class FormArray extends AbstractControl {
|
||||
* console.log(arr.value); // ['Nancy', null]
|
||||
* ```
|
||||
*/
|
||||
patchValue(value: any[], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
patchValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
value.forEach((newValue: any, index: number) => {
|
||||
if (this.at(index)) {
|
||||
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent});
|
||||
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
|
||||
}
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this.updateValueAndValidity(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1293,14 +1288,13 @@ export class FormArray extends AbstractControl {
|
||||
* console.log(this.arr.get(0).status); // 'DISABLED'
|
||||
* ```
|
||||
*/
|
||||
reset(value: any = [], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
||||
void {
|
||||
reset(value: any = [], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
this._forEachChild((control: AbstractControl, index: number) => {
|
||||
control.reset(value[index], {onlySelf: true, emitEvent});
|
||||
control.reset(value[index], {onlySelf: true, emitEvent: options.emitEvent});
|
||||
});
|
||||
this.updateValueAndValidity({onlySelf, emitEvent});
|
||||
this._updatePristine({onlySelf});
|
||||
this._updateTouched({onlySelf});
|
||||
this.updateValueAndValidity(options);
|
||||
this._updatePristine(options);
|
||||
this._updateTouched(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,9 +24,6 @@ function isEmptyInputValue(value: any): boolean {
|
||||
*
|
||||
* Provide this using `multi: true` to add validators.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/forms/ts/ng_validators/ng_validators.ts region='ng_validators'}
|
||||
* @stable
|
||||
*/
|
||||
export const NG_VALIDATORS = new InjectionToken<Array<Validator|Function>>('NgValidators');
|
||||
|
@ -37,14 +37,32 @@ export abstract class Body {
|
||||
|
||||
/**
|
||||
* Returns the body as a string, presuming `toString()` can be called on the response body.
|
||||
*
|
||||
* When decoding an `ArrayBuffer`, the optional `encodingHint` parameter determines how the
|
||||
* bytes in the buffer will be interpreted. Valid values are:
|
||||
*
|
||||
* - `legacy` - incorrectly interpret the bytes as UTF-16 (technically, UCS-2). Only characters
|
||||
* in the Basic Multilingual Plane are supported, surrogate pairs are not handled correctly.
|
||||
* In addition, the endianness of the 16-bit octet pairs in the `ArrayBuffer` is not taken
|
||||
* into consideration. This is the default behavior to avoid breaking apps, but should be
|
||||
* considered deprecated.
|
||||
*
|
||||
* - `iso-8859` - interpret the bytes as ISO-8859 (which can be used for ASCII encoded text).
|
||||
*/
|
||||
text(): string {
|
||||
text(encodingHint: 'legacy'|'iso-8859' = 'legacy'): string {
|
||||
if (this._body instanceof URLSearchParams) {
|
||||
return this._body.toString();
|
||||
}
|
||||
|
||||
if (this._body instanceof ArrayBuffer) {
|
||||
return String.fromCharCode.apply(null, new Uint16Array(<ArrayBuffer>this._body));
|
||||
switch (encodingHint) {
|
||||
case 'legacy':
|
||||
return String.fromCharCode.apply(null, new Uint16Array(this._body as ArrayBuffer));
|
||||
case 'iso-8859':
|
||||
return String.fromCharCode.apply(null, new Uint8Array(this._body as ArrayBuffer));
|
||||
default:
|
||||
throw new Error(`Invalid value for encodingHint: ${encodingHint}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._body == null) {
|
||||
|
@ -42,6 +42,15 @@ export function getResponseURL(xhr: any): string|null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function stringToArrayBuffer8(input: String): ArrayBuffer {
|
||||
const view = new Uint8Array(input.length);
|
||||
for (let i = 0, strLen = input.length; i < strLen; i++) {
|
||||
view[i] = input.charCodeAt(i);
|
||||
}
|
||||
return view.buffer;
|
||||
}
|
||||
|
||||
|
||||
export function stringToArrayBuffer(input: String): ArrayBuffer {
|
||||
const view = new Uint16Array(input.length);
|
||||
for (let i = 0, strLen = input.length; i < strLen; i++) {
|
||||
|
@ -76,8 +76,14 @@ export class Request extends Body {
|
||||
// TODO: assert that url is present
|
||||
const url = requestOptions.url;
|
||||
this.url = requestOptions.url !;
|
||||
if (requestOptions.params) {
|
||||
const params = requestOptions.params.toString();
|
||||
const paramsArg = requestOptions.params || requestOptions.search;
|
||||
if (paramsArg) {
|
||||
let params: string;
|
||||
if (typeof paramsArg === 'object' && !(paramsArg instanceof URLSearchParams)) {
|
||||
params = urlEncodeParams(paramsArg).toString();
|
||||
} else {
|
||||
params = paramsArg.toString();
|
||||
}
|
||||
if (params.length > 0) {
|
||||
let prefix = '?';
|
||||
if (this.url.indexOf('?') != -1) {
|
||||
@ -163,8 +169,22 @@ export class Request extends Body {
|
||||
}
|
||||
}
|
||||
|
||||
function urlEncodeParams(params: {[key: string]: any}): URLSearchParams {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (value && Array.isArray(value)) {
|
||||
value.forEach(element => searchParams.append(key, element.toString()));
|
||||
} else {
|
||||
searchParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
return searchParams;
|
||||
}
|
||||
|
||||
const noop = function() {};
|
||||
const w = typeof window == 'object' ? window : noop;
|
||||
const FormData = (w as any /** TODO #9100 */)['FormData'] || noop;
|
||||
const Blob = (w as any /** TODO #9100 */)['Blob'] || noop;
|
||||
export const ArrayBuffer = (w as any /** TODO #9100 */)['ArrayBuffer'] || noop;
|
||||
export const ArrayBuffer: ArrayBufferConstructor =
|
||||
(w as any /** TODO #9100 */)['ArrayBuffer'] || noop;
|
||||
|
@ -7,10 +7,12 @@
|
||||
*/
|
||||
|
||||
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {ɵgetDOM as getDOM} from '@angular/platform-browser';
|
||||
|
||||
import {RequestOptions} from '../src/base_request_options';
|
||||
import {ContentType} from '../src/enums';
|
||||
import {Headers} from '../src/headers';
|
||||
import {stringToArrayBuffer, stringToArrayBuffer8} from '../src/http_utils';
|
||||
import {ArrayBuffer, Request} from '../src/static_request';
|
||||
|
||||
export function main() {
|
||||
@ -109,5 +111,28 @@ export function main() {
|
||||
|
||||
expect(req.text()).toEqual('');
|
||||
});
|
||||
|
||||
it('should use object params', () => {
|
||||
const req = new Request({url: 'http://test.com', params: {'a': 3, 'b': ['x', 'y']}});
|
||||
expect(req.url).toBe('http://test.com?a=3&b=x&b=y');
|
||||
});
|
||||
|
||||
it('should use search if present', () => {
|
||||
const req = new Request({url: 'http://test.com', search: 'a=1&b=2'});
|
||||
expect(req.url).toBe('http://test.com?a=1&b=2');
|
||||
});
|
||||
|
||||
if (getDOM().supportsWebAnimation()) {
|
||||
it('should serialize an ArrayBuffer to string via legacy encoding', () => {
|
||||
const str = '\u89d2\u5ea6';
|
||||
expect(new Request({body: stringToArrayBuffer(str), url: '/'}).text()).toEqual(str);
|
||||
});
|
||||
|
||||
it('should serialize an ArrayBuffer to string via iso-8859 encoding', () => {
|
||||
const str = 'abcd';
|
||||
expect(new Request({body: stringToArrayBuffer8(str), url: '/'}).text('iso-8859'))
|
||||
.toEqual(str);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
14
packages/http/testing/public_api.ts
Normal file
14
packages/http/testing/public_api.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Entry point for all public APIs of the http testing package.
|
||||
*/
|
||||
export * from './src/testing';
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig-build",
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true,
|
||||
"paths": {
|
||||
"@angular/core": ["../../dist/packages/core"],
|
||||
"@angular/http": ["../../dist/packages/http"],
|
||||
@ -8,9 +9,12 @@
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"index.ts"
|
||||
"public_api.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"strictMetadataEmit": true
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true,
|
||||
"flatModuleOutFile": "index.js",
|
||||
"flatModuleId": "@angular/http/testing"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
|
@ -13,7 +13,9 @@ export class AstPath<T> {
|
||||
get head(): T|undefined { return this.path[0]; }
|
||||
get tail(): T|undefined { return this.path[this.path.length - 1]; }
|
||||
|
||||
parentOf(node: T): T|undefined { return this.path[this.path.indexOf(node) - 1]; }
|
||||
parentOf(node: T|undefined): T|undefined {
|
||||
return node && this.path[this.path.indexOf(node) - 1];
|
||||
}
|
||||
childOf(node: T): T|undefined { return this.path[this.path.indexOf(node) + 1]; }
|
||||
|
||||
first<N extends T>(ctor: {new (...args: any[]): N}): N|undefined {
|
||||
|
@ -33,23 +33,24 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions|
|
||||
let result: Completions|undefined = undefined;
|
||||
let {htmlAst, templateAst, template} = templateInfo;
|
||||
// The templateNode starts at the delimiter character so we add 1 to skip it.
|
||||
let templatePosition = templateInfo.position ! - template.span.start;
|
||||
if (templateInfo.position != null) {
|
||||
let templatePosition = templateInfo.position - template.span.start;
|
||||
let path = new HtmlAstPath(htmlAst, templatePosition);
|
||||
let mostSpecific = path.tail;
|
||||
if (path.empty) {
|
||||
if (path.empty || !mostSpecific) {
|
||||
result = elementCompletions(templateInfo, path);
|
||||
} else {
|
||||
let astPosition = templatePosition - mostSpecific !.sourceSpan !.start.offset;
|
||||
mostSpecific !.visit(
|
||||
let astPosition = templatePosition - mostSpecific.sourceSpan.start.offset;
|
||||
mostSpecific.visit(
|
||||
{
|
||||
visitElement(ast) {
|
||||
let startTagSpan = spanOf(ast.sourceSpan);
|
||||
let tagLen = ast.name.length;
|
||||
if (templatePosition <=
|
||||
startTagSpan !.start + tagLen + 1 /* 1 for the opening angle bracked */) {
|
||||
startTagSpan.start + tagLen + 1 /* 1 for the opening angle bracked */) {
|
||||
// If we are in the tag then return the element completions.
|
||||
result = elementCompletions(templateInfo, path);
|
||||
} else if (templatePosition < startTagSpan !.end) {
|
||||
} else if (templatePosition < startTagSpan.end) {
|
||||
// We are in the attribute section of the element (but not in an attribute).
|
||||
// Return the attribute completions.
|
||||
result = attributeCompletions(templateInfo, path);
|
||||
@ -65,7 +66,7 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions|
|
||||
},
|
||||
visitText(ast) {
|
||||
// Check if we are in a entity.
|
||||
result = entityCompletions(getSourceText(template, spanOf(ast) !), astPosition);
|
||||
result = entityCompletions(getSourceText(template, spanOf(ast)), astPosition);
|
||||
if (result) return result;
|
||||
result = interpolationCompletions(templateInfo, templatePosition);
|
||||
if (result) return result;
|
||||
@ -93,11 +94,12 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions|
|
||||
},
|
||||
null);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function attributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|undefined {
|
||||
let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail !);
|
||||
let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail);
|
||||
if (item instanceof Element) {
|
||||
return attributeCompletionsForElement(info, item.name, item);
|
||||
}
|
||||
@ -213,11 +215,12 @@ function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|
|
||||
let htmlNames = elementNames().filter(name => !(name in hiddenHtmlElements));
|
||||
|
||||
// Collect the elements referenced by the selectors
|
||||
let directiveElements =
|
||||
getSelectors(info).selectors.map(selector => selector.element).filter(name => !!name);
|
||||
let directiveElements = getSelectors(info)
|
||||
.selectors.map(selector => selector.element)
|
||||
.filter(name => !!name) as string[];
|
||||
|
||||
let components =
|
||||
directiveElements.map<Completion>(name => ({kind: 'component', name: name !, sort: name !}));
|
||||
directiveElements.map<Completion>(name => ({kind: 'component', name, sort: name}));
|
||||
let htmlElements = htmlNames.map<Completion>(name => ({kind: 'element', name: name, sort: name}));
|
||||
|
||||
// Return components and html elements
|
||||
@ -262,25 +265,25 @@ function voidElementAttributeCompletions(info: TemplateInfo, path: HtmlAstPath):
|
||||
undefined {
|
||||
let tail = path.tail;
|
||||
if (tail instanceof Text) {
|
||||
let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/) !;
|
||||
let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
|
||||
// The position must be after the match, otherwise we are still in a place where elements
|
||||
// are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after).
|
||||
if (match && path.position >= match.index ! + match[0].length + tail.sourceSpan.start.offset) {
|
||||
if (match &&
|
||||
path.position >= (match.index || 0) + match[0].length + tail.sourceSpan.start.offset) {
|
||||
return attributeCompletionsForElement(info, match[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionVisitor extends NullTemplateVisitor {
|
||||
private getExpressionScope: () => SymbolTable;
|
||||
result: Completions;
|
||||
|
||||
constructor(
|
||||
private info: TemplateInfo, private position: number, private attr?: Attribute,
|
||||
private getExpressionScope?: () => SymbolTable) {
|
||||
getExpressionScope?: () => SymbolTable) {
|
||||
super();
|
||||
if (!getExpressionScope) {
|
||||
this.getExpressionScope = () => info.template.members;
|
||||
}
|
||||
this.getExpressionScope = getExpressionScope || (() => info.template.members);
|
||||
}
|
||||
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst): void {
|
||||
@ -311,7 +314,8 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
||||
this.info.expressionParser.parseTemplateBindings(key, this.attr.value, null);
|
||||
|
||||
// find the template binding that contains the position
|
||||
const valueRelativePosition = this.position - this.attr.valueSpan !.start.offset - 1;
|
||||
if (!this.attr.valueSpan) return;
|
||||
const valueRelativePosition = this.position - this.attr.valueSpan.start.offset - 1;
|
||||
const bindings = templateBindingResult.templateBindings;
|
||||
const binding =
|
||||
bindings.find(
|
||||
@ -340,11 +344,13 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
||||
// We are after the '=' in a let clause. The valid values here are the members of the
|
||||
// template reference's type parameter.
|
||||
const directiveMetadata = selectorInfo.map.get(selector);
|
||||
if (directiveMetadata) {
|
||||
const contextTable =
|
||||
this.info.template.query.getTemplateContext(directiveMetadata !.type.reference);
|
||||
this.info.template.query.getTemplateContext(directiveMetadata.type.reference);
|
||||
if (contextTable) {
|
||||
this.result = this.symbolsToCompletions(contextTable.values());
|
||||
}
|
||||
}
|
||||
} else if (binding.key && valueRelativePosition <= (binding.key.length - key.length)) {
|
||||
keyCompletions();
|
||||
}
|
||||
@ -371,7 +377,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
||||
const expressionPosition = this.position - ast.sourceSpan.start.offset;
|
||||
if (inSpan(expressionPosition, ast.value.span)) {
|
||||
const completions = getExpressionCompletions(
|
||||
this.getExpressionScope !(), ast.value, expressionPosition, this.info.template.query);
|
||||
this.getExpressionScope(), ast.value, expressionPosition, this.info.template.query);
|
||||
if (completions) {
|
||||
this.result = this.symbolsToCompletions(completions);
|
||||
}
|
||||
@ -380,8 +386,8 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
||||
|
||||
private attributeValueCompletions(value: AST, position?: number) {
|
||||
const symbols = getExpressionCompletions(
|
||||
this.getExpressionScope !(), value,
|
||||
position == null ? this.attributeValuePosition : position, this.info.template.query);
|
||||
this.getExpressionScope(), value, position == null ? this.attributeValuePosition : position,
|
||||
this.info.template.query);
|
||||
if (symbols) {
|
||||
this.result = this.symbolsToCompletions(symbols);
|
||||
}
|
||||
@ -393,12 +399,13 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
||||
}
|
||||
|
||||
private get attributeValuePosition() {
|
||||
return this.position - this.attr !.valueSpan !.start.offset - 1;
|
||||
if (this.attr && this.attr.valueSpan) {
|
||||
return this.position - this.attr.valueSpan.start.offset - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getSourceText(template: TemplateSource, span: Span): string {
|
||||
return template.source.substring(span.start, span.end);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export function getTemplateDiagnostics(
|
||||
results.push(...ast.parseErrors.map<Diagnostic>(
|
||||
e => ({
|
||||
kind: DiagnosticKind.Error,
|
||||
span: offsetSpan(spanOf(e.span) !, template.span.start),
|
||||
span: offsetSpan(spanOf(e.span), template.span.start),
|
||||
message: e.msg
|
||||
})));
|
||||
} else if (ast.templateAst) {
|
||||
@ -91,14 +91,16 @@ export function getDeclarationDiagnostics(
|
||||
|
||||
function getTemplateExpressionDiagnostics(
|
||||
template: TemplateSource, astResult: AstResult): Diagnostics {
|
||||
if (astResult.htmlAst && astResult.directive && astResult.directives && astResult.pipes &&
|
||||
astResult.templateAst && astResult.expressionParser) {
|
||||
const info: TemplateInfo = {
|
||||
template,
|
||||
htmlAst: astResult.htmlAst !,
|
||||
directive: astResult.directive !,
|
||||
directives: astResult.directives !,
|
||||
pipes: astResult.pipes !,
|
||||
templateAst: astResult.templateAst !,
|
||||
expressionParser: astResult.expressionParser !
|
||||
htmlAst: astResult.htmlAst,
|
||||
directive: astResult.directive,
|
||||
directives: astResult.directives,
|
||||
pipes: astResult.pipes,
|
||||
templateAst: astResult.templateAst,
|
||||
expressionParser: astResult.expressionParser
|
||||
};
|
||||
const visitor = new ExpressionDiagnosticsVisitor(
|
||||
info, (path: TemplateAstPath, includeEvent: boolean) =>
|
||||
@ -106,6 +108,8 @@ function getTemplateExpressionDiagnostics(
|
||||
templateVisitAll(visitor, astResult.templateAst !);
|
||||
return visitor.diagnostics;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
|
||||
private path: TemplateAstPath;
|
||||
@ -158,11 +162,11 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
|
||||
if (context && !context.has(ast.value)) {
|
||||
if (ast.value === '$implicit') {
|
||||
this.reportError(
|
||||
'The template context does not have an implicit value', spanOf(ast.sourceSpan) !);
|
||||
'The template context does not have an implicit value', spanOf(ast.sourceSpan));
|
||||
} else {
|
||||
this.reportError(
|
||||
`The template context does not defined a member called '${ast.value}'`,
|
||||
spanOf(ast.sourceSpan) !);
|
||||
spanOf(ast.sourceSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,12 +237,14 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private reportError(message: string, span: Span) {
|
||||
private reportError(message: string, span: Span|undefined) {
|
||||
if (span) {
|
||||
this.diagnostics.push({
|
||||
span: offsetSpan(span, this.info.template.span.start),
|
||||
kind: DiagnosticKind.Error, message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private reportWarning(message: string, span: Span) {
|
||||
this.diagnostics.push({
|
||||
|
@ -495,8 +495,9 @@ class AstType implements ExpressionVisitor {
|
||||
// The type of a method is the selected methods result type.
|
||||
const method = receiverType.members().get(ast.name);
|
||||
if (!method) return this.reportError(`Unknown method ${ast.name}`, ast);
|
||||
if (!method.type !.callable) return this.reportError(`Member ${ast.name} is not callable`, ast);
|
||||
const signature = method.type !.selectSignature(ast.args.map(arg => this.getType(arg)));
|
||||
if (!method.type) return this.reportError(`Could not find a type for ${ast.name}`, ast);
|
||||
if (!method.type.callable) return this.reportError(`Member ${ast.name} is not callable`, ast);
|
||||
const signature = method.type.selectSignature(ast.args.map(arg => this.getType(arg)));
|
||||
if (!signature)
|
||||
return this.reportError(`Unable to resolve signature for call of method ${ast.name}`, ast);
|
||||
return signature.result;
|
||||
@ -601,7 +602,9 @@ function visitChildren(ast: AST, visitor: ExpressionVisitor) {
|
||||
visit(ast.falseExp);
|
||||
},
|
||||
visitFunctionCall(ast) {
|
||||
visit(ast.target !);
|
||||
if (ast.target) {
|
||||
visit(ast.target);
|
||||
}
|
||||
visitAll(ast.args);
|
||||
},
|
||||
visitImplicitReceiver(ast) {},
|
||||
@ -676,7 +679,7 @@ function getReferences(info: TemplateInfo): SymbolDeclaration[] {
|
||||
|
||||
function processReferences(references: ReferenceAst[]) {
|
||||
for (const reference of references) {
|
||||
let type: Symbol = undefined !;
|
||||
let type: Symbol|undefined = undefined;
|
||||
if (reference.value) {
|
||||
type = info.template.query.getTypeSymbol(tokenReference(reference.value));
|
||||
}
|
||||
@ -721,7 +724,7 @@ function getVarDeclarations(info: TemplateInfo, path: TemplateAstPath): SymbolDe
|
||||
.find(c => !!c);
|
||||
|
||||
// Determine the type of the context field referenced by variable.value.
|
||||
let type: Symbol = undefined !;
|
||||
let type: Symbol|undefined = undefined;
|
||||
if (context) {
|
||||
const value = context.get(variable.value);
|
||||
if (value) {
|
||||
@ -762,7 +765,10 @@ function refinedVariableType(
|
||||
const bindingType =
|
||||
new AstType(info.template.members, info.template.query, {}).getType(ngForOfBinding.value);
|
||||
if (bindingType) {
|
||||
return info.template.query.getElementType(bindingType) !;
|
||||
const result = info.template.query.getElementType(bindingType);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,24 +81,25 @@ class LanguageServiceImpl implements LanguageService {
|
||||
let template = this.host.getTemplateAt(fileName, position);
|
||||
if (template) {
|
||||
let astResult = this.getTemplateAst(template, fileName);
|
||||
if (astResult && astResult.htmlAst && astResult.templateAst)
|
||||
if (astResult && astResult.htmlAst && astResult.templateAst && astResult.directive &&
|
||||
astResult.directives && astResult.pipes && astResult.expressionParser)
|
||||
return {
|
||||
position,
|
||||
fileName,
|
||||
template,
|
||||
htmlAst: astResult.htmlAst,
|
||||
directive: astResult.directive !,
|
||||
directives: astResult.directives !,
|
||||
pipes: astResult.pipes !,
|
||||
directive: astResult.directive,
|
||||
directives: astResult.directives,
|
||||
pipes: astResult.pipes,
|
||||
templateAst: astResult.templateAst,
|
||||
expressionParser: astResult.expressionParser !
|
||||
expressionParser: astResult.expressionParser
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getTemplateAst(template: TemplateSource, contextFile: string): AstResult {
|
||||
let result: AstResult = undefined !;
|
||||
let result: AstResult|undefined = undefined;
|
||||
try {
|
||||
const resolvedMetadata =
|
||||
this.metadataResolver.getNonNormalizedDirectiveMetadata(template.type as any);
|
||||
@ -112,7 +113,7 @@ class LanguageServiceImpl implements LanguageService {
|
||||
config, expressionParser, new DomElementSchemaRegistry(), htmlParser, null !, []);
|
||||
const htmlResult = htmlParser.parse(template.source, '', true);
|
||||
const analyzedModules = this.host.getAnalyzedModules();
|
||||
let errors: Diagnostic[] = undefined !;
|
||||
let errors: Diagnostic[]|undefined = undefined;
|
||||
let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type);
|
||||
if (!ngModule) {
|
||||
// Reported by the the declaration diagnostics.
|
||||
@ -121,8 +122,7 @@ class LanguageServiceImpl implements LanguageService {
|
||||
if (ngModule) {
|
||||
const resolvedDirectives = ngModule.transitiveModule.directives.map(
|
||||
d => this.host.resolver.getNonNormalizedDirectiveMetadata(d.reference));
|
||||
const directives =
|
||||
resolvedDirectives.filter(d => d !== null).map(d => d !.metadata.toSummary());
|
||||
const directives = removeMissing(resolvedDirectives).map(d => d.metadata.toSummary());
|
||||
const pipes = ngModule.transitiveModule.pipes.map(
|
||||
p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
|
||||
const schemas = ngModule.schemas;
|
||||
@ -142,10 +142,14 @@ class LanguageServiceImpl implements LanguageService {
|
||||
}
|
||||
result = {errors: [{kind: DiagnosticKind.Error, message: e.message, span}]};
|
||||
}
|
||||
return result;
|
||||
return result || {};
|
||||
}
|
||||
}
|
||||
|
||||
function removeMissing<T>(values: (T | null | undefined)[]): T[] {
|
||||
return values.filter(e => !!e) as T[];
|
||||
}
|
||||
|
||||
function uniqueBySpan < T extends {
|
||||
span: Span;
|
||||
}
|
||||
@ -169,8 +173,8 @@ function uniqueBySpan < T extends {
|
||||
}
|
||||
}
|
||||
|
||||
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata {
|
||||
let result: CompileNgModuleMetadata = undefined !;
|
||||
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata|undefined {
|
||||
let result: CompileNgModuleMetadata|undefined = undefined;
|
||||
let resultSize = 0;
|
||||
for (const module of modules.ngModules) {
|
||||
const moduleSize = module.transitiveModule.directives.length;
|
||||
|
@ -21,23 +21,26 @@ export interface SymbolInfo {
|
||||
}
|
||||
|
||||
export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
|
||||
const templatePosition = info.position ! - info.template.span.start;
|
||||
if (!info.position) return undefined;
|
||||
const templatePosition = info.position - info.template.span.start;
|
||||
const path = new TemplateAstPath(info.templateAst, templatePosition);
|
||||
if (path.tail) {
|
||||
let symbol: Symbol = undefined !;
|
||||
let span: Span = undefined !;
|
||||
let symbol: Symbol|undefined = undefined;
|
||||
let span: Span|undefined = undefined;
|
||||
const attributeValueSymbol = (ast: AST, inEvent: boolean = false): boolean => {
|
||||
const attribute = findAttribute(info);
|
||||
if (attribute) {
|
||||
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
||||
const scope = getExpressionScope(info, path, inEvent);
|
||||
const expressionOffset = attribute.valueSpan !.start.offset + 1;
|
||||
if (attribute.valueSpan) {
|
||||
const expressionOffset = attribute.valueSpan.start.offset + 1;
|
||||
const result = getExpressionSymbol(
|
||||
scope, ast, templatePosition - expressionOffset, info.template.query);
|
||||
if (result) {
|
||||
symbol = result.symbol;
|
||||
span = offsetSpan(result.span, expressionOffset);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -52,28 +55,28 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
|
||||
if (component) {
|
||||
symbol = info.template.query.getTypeSymbol(component.directive.type.reference);
|
||||
symbol = symbol && new OverrideKindSymbol(symbol, 'component');
|
||||
span = spanOf(ast) !;
|
||||
span = spanOf(ast);
|
||||
} else {
|
||||
// Find a directive that matches the element name
|
||||
const directive =
|
||||
ast.directives.find(d => d.directive.selector !.indexOf(ast.name) >= 0);
|
||||
const directive = ast.directives.find(
|
||||
d => d.directive.selector != null && d.directive.selector.indexOf(ast.name) >= 0);
|
||||
if (directive) {
|
||||
symbol = info.template.query.getTypeSymbol(directive.directive.type.reference);
|
||||
symbol = symbol && new OverrideKindSymbol(symbol, 'directive');
|
||||
span = spanOf(ast) !;
|
||||
span = spanOf(ast);
|
||||
}
|
||||
}
|
||||
},
|
||||
visitReference(ast) {
|
||||
symbol = info.template.query.getTypeSymbol(tokenReference(ast.value));
|
||||
span = spanOf(ast) !;
|
||||
span = spanOf(ast);
|
||||
},
|
||||
visitVariable(ast) {},
|
||||
visitEvent(ast) {
|
||||
if (!attributeValueSymbol(ast.handler, /* inEvent */ true)) {
|
||||
symbol = findOutputBinding(info, path, ast) !;
|
||||
symbol = findOutputBinding(info, path, ast);
|
||||
symbol = symbol && new OverrideKindSymbol(symbol, 'event');
|
||||
span = spanOf(ast) !;
|
||||
span = spanOf(ast);
|
||||
}
|
||||
},
|
||||
visitElementProperty(ast) { attributeValueSymbol(ast.value); },
|
||||
@ -93,12 +96,12 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
|
||||
visitText(ast) {},
|
||||
visitDirective(ast) {
|
||||
symbol = info.template.query.getTypeSymbol(ast.directive.type.reference);
|
||||
span = spanOf(ast) !;
|
||||
span = spanOf(ast);
|
||||
},
|
||||
visitDirectiveProperty(ast) {
|
||||
if (!attributeValueSymbol(ast.value)) {
|
||||
symbol = findInputBinding(info, path, ast) !;
|
||||
span = spanOf(ast) !;
|
||||
symbol = findInputBinding(info, path, ast);
|
||||
span = spanOf(ast);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -110,10 +113,12 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
|
||||
}
|
||||
|
||||
function findAttribute(info: TemplateInfo): Attribute|undefined {
|
||||
const templatePosition = info.position ! - info.template.span.start;
|
||||
if (info.position) {
|
||||
const templatePosition = info.position - info.template.span.start;
|
||||
const path = new HtmlAstPath(info.htmlAst, templatePosition);
|
||||
return path.first(Attribute);
|
||||
}
|
||||
}
|
||||
|
||||
function findInputBinding(
|
||||
info: TemplateInfo, path: TemplateAstPath, binding: BoundDirectivePropertyAst): Symbol|
|
||||
|
@ -22,18 +22,24 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
||||
if (snapshot) {
|
||||
return snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
|
||||
// Typescript readFile() declaration should be `readFile(fileName: string): string | undefined
|
||||
return undefined !;
|
||||
}
|
||||
|
||||
directoryExists: (directoryName: string) => boolean;
|
||||
}
|
||||
|
||||
// This reflector host's purpose is to first set verboseInvalidExpressions to true so the
|
||||
// reflector will collect errors instead of throwing, and second to all deferring the creation
|
||||
// of the program until it is actually needed.
|
||||
export class ReflectorHost extends CompilerHost {
|
||||
constructor(
|
||||
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
|
||||
options: AngularCompilerOptions) {
|
||||
super(
|
||||
null !, options,
|
||||
// The ancestor value for program is overridden below so passing null here is safe.
|
||||
/* program */ null !, options,
|
||||
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)),
|
||||
{verboseInvalidExpression: true});
|
||||
}
|
||||
|
@ -104,10 +104,10 @@ class TemplateAstPathBuilder extends TemplateAstChildVisitor {
|
||||
constructor(private position: number, private allowWidening: boolean) { super(); }
|
||||
|
||||
visit(ast: TemplateAst, context: any): any {
|
||||
let span = spanOf(ast) !;
|
||||
let span = spanOf(ast);
|
||||
if (inSpan(this.position, span)) {
|
||||
const len = this.path.length;
|
||||
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]) !)) {
|
||||
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]))) {
|
||||
this.path.push(ast);
|
||||
}
|
||||
} else {
|
||||
|
@ -25,14 +25,16 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||
}
|
||||
|
||||
function diagnosticToDiagnostic(d: Diagnostic, file: ts.SourceFile): ts.Diagnostic {
|
||||
return {
|
||||
const result = {
|
||||
file,
|
||||
start: d.span.start,
|
||||
length: d.span.end - d.span.start,
|
||||
messageText: d.message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
code: 0
|
||||
code: 0,
|
||||
source: 'ng'
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
function tryOperation(attempting: string, callback: () => void) {
|
||||
@ -78,7 +80,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||
if (ours) {
|
||||
const displayParts: typeof base.displayParts = [];
|
||||
for (const part of ours.text) {
|
||||
displayParts.push({kind: part.language !, text: part.text});
|
||||
displayParts.push({kind: part.language || 'angular', text: part.text});
|
||||
}
|
||||
base = <any>{
|
||||
displayParts,
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
import {CompileDirectiveMetadata, CompileMetadataResolver, NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The range of a span of text in a source file.
|
||||
*
|
||||
@ -455,7 +453,7 @@ export interface LanguageServiceHost {
|
||||
* refers to a template file then the `position` should be ignored. If the `position` is not in a
|
||||
* template literal string then this method should return `undefined`.
|
||||
*/
|
||||
getTemplateAt(fileName: string, position: number): TemplateSource /* |undefined */;
|
||||
getTemplateAt(fileName: string, position: number): TemplateSource|undefined;
|
||||
|
||||
/**
|
||||
* Return the template source information for all templates in `fileName` or for `fileName` if it
|
||||
|
@ -74,22 +74,22 @@ export class DummyResourceLoader extends ResourceLoader {
|
||||
* @experimental
|
||||
*/
|
||||
export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
private _resolver: CompileMetadataResolver;
|
||||
private _resolver: CompileMetadataResolver|null;
|
||||
private _staticSymbolCache = new StaticSymbolCache();
|
||||
private _summaryResolver: AotSummaryResolver;
|
||||
private _staticSymbolResolver: StaticSymbolResolver;
|
||||
private _reflector: StaticReflector;
|
||||
private _reflector: StaticReflector|null;
|
||||
private _reflectorHost: ReflectorHost;
|
||||
private _checker: ts.TypeChecker;
|
||||
private _checker: ts.TypeChecker|null;
|
||||
private _typeCache: Symbol[] = [];
|
||||
private context: string|undefined;
|
||||
private lastProgram: ts.Program|undefined;
|
||||
private modulesOutOfDate: boolean = true;
|
||||
private analyzedModules: NgAnalyzedModules;
|
||||
private analyzedModules: NgAnalyzedModules|null;
|
||||
private service: LanguageService;
|
||||
private fileToComponent: Map<string, StaticSymbol>;
|
||||
private templateReferences: string[];
|
||||
private collectedErrors: Map<string, any[]>;
|
||||
private fileToComponent: Map<string, StaticSymbol>|null;
|
||||
private templateReferences: string[]|null;
|
||||
private collectedErrors: Map<string, any[]>|null;
|
||||
private fileVersions = new Map<string, string>();
|
||||
|
||||
constructor(private host: ts.LanguageServiceHost, private tsService: ts.LanguageService) {}
|
||||
@ -127,28 +127,28 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
|
||||
getTemplateReferences(): string[] {
|
||||
this.ensureTemplateMap();
|
||||
return this.templateReferences;
|
||||
return this.templateReferences || [];
|
||||
}
|
||||
|
||||
getTemplateAt(fileName: string, position: number): TemplateSource {
|
||||
getTemplateAt(fileName: string, position: number): TemplateSource|undefined {
|
||||
let sourceFile = this.getSourceFile(fileName);
|
||||
if (sourceFile) {
|
||||
this.context = sourceFile.fileName;
|
||||
let node = this.findNode(sourceFile, position);
|
||||
if (node) {
|
||||
return this.getSourceFromNode(
|
||||
fileName, this.host.getScriptVersion(sourceFile.fileName), node) !;
|
||||
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
|
||||
}
|
||||
} else {
|
||||
this.ensureTemplateMap();
|
||||
// TODO: Cannocalize the file?
|
||||
const componentType = this.fileToComponent.get(fileName);
|
||||
const componentType = this.fileToComponent !.get(fileName);
|
||||
if (componentType) {
|
||||
return this.getSourceFromType(
|
||||
fileName, this.host.getScriptVersion(fileName), componentType) !;
|
||||
fileName, this.host.getScriptVersion(fileName), componentType);
|
||||
}
|
||||
}
|
||||
return null !;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getAnalyzedModules(): NgAnalyzedModules {
|
||||
@ -172,7 +172,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
|
||||
getTemplates(fileName: string): TemplateSources {
|
||||
this.ensureTemplateMap();
|
||||
const componentType = this.fileToComponent.get(fileName);
|
||||
const componentType = this.fileToComponent !.get(fileName);
|
||||
if (componentType) {
|
||||
const templateSource = this.getTemplateAt(fileName, 0);
|
||||
if (templateSource) {
|
||||
@ -225,10 +225,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
updateAnalyzedModules() {
|
||||
this.validate();
|
||||
if (this.modulesOutOfDate) {
|
||||
this.analyzedModules = null !;
|
||||
this._reflector = null !;
|
||||
this.templateReferences = null !;
|
||||
this.fileToComponent = null !;
|
||||
this.analyzedModules = null;
|
||||
this._reflector = null;
|
||||
this.templateReferences = null;
|
||||
this.fileToComponent = null;
|
||||
this.ensureAnalyzedModules();
|
||||
this.modulesOutOfDate = false;
|
||||
}
|
||||
@ -273,10 +273,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
}
|
||||
|
||||
private clearCaches() {
|
||||
this._checker = null !;
|
||||
this._checker = null;
|
||||
this._typeCache = [];
|
||||
this._resolver = null !;
|
||||
this.collectedErrors = null !;
|
||||
this._resolver = null;
|
||||
this.collectedErrors = null;
|
||||
this.modulesOutOfDate = true;
|
||||
}
|
||||
|
||||
@ -345,7 +345,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
if (declaration && declaration.name) {
|
||||
const sourceFile = this.getSourceFile(fileName);
|
||||
return this.getSourceFromDeclaration(
|
||||
fileName, version, this.stringOf(node) !, shrink(spanOf(node)),
|
||||
fileName, version, this.stringOf(node) || '', shrink(spanOf(node)),
|
||||
this.reflector.getStaticSymbol(sourceFile.fileName, declaration.name.text),
|
||||
declaration, node, sourceFile);
|
||||
}
|
||||
@ -359,11 +359,13 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
let result: TemplateSource|undefined = undefined;
|
||||
const declaration = this.getTemplateClassFromStaticSymbol(type);
|
||||
if (declaration) {
|
||||
const snapshot = this.host.getScriptSnapshot(fileName) !;
|
||||
const snapshot = this.host.getScriptSnapshot(fileName);
|
||||
if (snapshot) {
|
||||
const source = snapshot.getText(0, snapshot.getLength());
|
||||
result = this.getSourceFromDeclaration(
|
||||
fileName, version, source, {start: 0, end: source.length}, type, declaration, declaration,
|
||||
declaration.getSourceFile());
|
||||
fileName, version, source, {start: 0, end: source.length}, type, declaration,
|
||||
declaration, declaration.getSourceFile());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -398,9 +400,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
return result;
|
||||
}
|
||||
|
||||
private collectError(error: any, filePath: string) {
|
||||
private collectError(error: any, filePath: string|null) {
|
||||
if (filePath) {
|
||||
let errorMap = this.collectedErrors;
|
||||
if (!errorMap) {
|
||||
if (!errorMap || !this.collectedErrors) {
|
||||
errorMap = this.collectedErrors = new Map();
|
||||
}
|
||||
let errors = errorMap.get(filePath);
|
||||
@ -410,15 +413,16 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
}
|
||||
errors.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
private get staticSymbolResolver(): StaticSymbolResolver {
|
||||
let result = this._staticSymbolResolver;
|
||||
if (!result) {
|
||||
this._summaryResolver = new AotSummaryResolver(
|
||||
{
|
||||
loadSummary(filePath: string) { return null !; },
|
||||
isSourceFile(sourceFilePath: string) { return true !; },
|
||||
getOutputFileName(sourceFilePath: string) { return null !; }
|
||||
loadSummary(filePath: string) { return null; },
|
||||
isSourceFile(sourceFilePath: string) { return true; },
|
||||
getOutputFileName(sourceFilePath: string) { return sourceFilePath; }
|
||||
},
|
||||
this._staticSymbolCache);
|
||||
result = this._staticSymbolResolver = new StaticSymbolResolver(
|
||||
@ -445,7 +449,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
const declarationNode = ts.forEachChild(source, child => {
|
||||
if (child.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||
const classDeclaration = child as ts.ClassDeclaration;
|
||||
if (classDeclaration.name !.text === type.name) {
|
||||
if (classDeclaration.name != null && classDeclaration.name.text === type.name) {
|
||||
return classDeclaration;
|
||||
}
|
||||
}
|
||||
@ -614,7 +618,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
||||
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
|
||||
private fetchPipes: () => SymbolTable) {}
|
||||
|
||||
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol) !); }
|
||||
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol)); }
|
||||
|
||||
getBuiltinType(kind: BuiltinType): Symbol {
|
||||
// TODO: Replace with typeChecker API when available.
|
||||
@ -1303,7 +1307,7 @@ function getTypeParameterOf(type: ts.Type, name: string): ts.Type|undefined {
|
||||
}
|
||||
}
|
||||
|
||||
function typeKindOf(type: ts.Type): BuiltinType {
|
||||
function typeKindOf(type: ts.Type | undefined): BuiltinType {
|
||||
if (type) {
|
||||
if (type.flags & ts.TypeFlags.Any) {
|
||||
return BuiltinType.Any;
|
||||
@ -1318,17 +1322,19 @@ function typeKindOf(type: ts.Type): BuiltinType {
|
||||
return BuiltinType.Null;
|
||||
} else if (type.flags & ts.TypeFlags.Union) {
|
||||
// If all the constituent types of a union are the same kind, it is also that kind.
|
||||
let candidate: BuiltinType = undefined !;
|
||||
let candidate: BuiltinType|null = null;
|
||||
const unionType = type as ts.UnionType;
|
||||
if (unionType.types.length > 0) {
|
||||
candidate = typeKindOf(unionType.types[0]) !;
|
||||
candidate = typeKindOf(unionType.types[0]);
|
||||
for (const subType of unionType.types) {
|
||||
if (candidate != typeKindOf(subType)) {
|
||||
return BuiltinType.Other;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
} else if (type.flags & ts.TypeFlags.TypeParameter) {
|
||||
return BuiltinType.Unbound;
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ export function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
||||
return value && !!value.start;
|
||||
}
|
||||
|
||||
export function spanOf(span: SpanHolder): Span;
|
||||
export function spanOf(span: ParseSourceSpan): Span;
|
||||
export function spanOf(span: SpanHolder | ParseSourceSpan | undefined): Span|undefined;
|
||||
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
|
||||
if (!span) return undefined;
|
||||
if (isParseSourceSpan(span)) {
|
||||
@ -39,8 +42,8 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
|
||||
}
|
||||
|
||||
export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean {
|
||||
return span && exclusive ? position >= span.start && position < span.end :
|
||||
position >= span !.start && position <= span !.end;
|
||||
return span != null && (exclusive ? position >= span.start && position < span.end :
|
||||
position >= span.start && position <= span.end);
|
||||
}
|
||||
|
||||
export function offsetSpan(span: Span, amount: number): Span {
|
||||
@ -54,7 +57,8 @@ export function isNarrower(spanA: Span, spanB: Span): boolean {
|
||||
export function hasTemplateReference(type: CompileTypeMetadata): boolean {
|
||||
if (type.diDeps) {
|
||||
for (let diDep of type.diDeps) {
|
||||
if (diDep.token !.identifier && identifierName(diDep.token !.identifier !) == 'TemplateRef')
|
||||
if (diDep.token && diDep.token.identifier &&
|
||||
identifierName(diDep.token !.identifier !) == 'TemplateRef')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {APP_INITIALIZER, Inject, InjectionToken, Provider} from '@angular/core';
|
||||
import {APP_INITIALIZER, ApplicationInitStatus, Inject, InjectionToken, Injector, Provider} from '@angular/core';
|
||||
|
||||
import {getDOM} from '../dom/dom_adapter';
|
||||
import {DOCUMENT} from '../dom/dom_tokens';
|
||||
@ -17,22 +17,25 @@ import {DOCUMENT} from '../dom/dom_tokens';
|
||||
*/
|
||||
export const TRANSITION_ID = new InjectionToken('TRANSITION_ID');
|
||||
|
||||
export function bootstrapListenerFactory(transitionId: string, document: any) {
|
||||
const factory = () => {
|
||||
export function appInitializerFactory(transitionId: string, document: any, injector: Injector) {
|
||||
return () => {
|
||||
// Wait for all application initializers to be completed before removing the styles set by
|
||||
// the server.
|
||||
injector.get(ApplicationInitStatus).donePromise.then(() => {
|
||||
const dom = getDOM();
|
||||
const styles: any[] =
|
||||
Array.prototype.slice.apply(dom.querySelectorAll(document, `style[ng-transition]`));
|
||||
styles.filter(el => dom.getAttribute(el, 'ng-transition') === transitionId)
|
||||
.forEach(el => dom.remove(el));
|
||||
});
|
||||
};
|
||||
return factory;
|
||||
}
|
||||
|
||||
export const SERVER_TRANSITION_PROVIDERS: Provider[] = [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: bootstrapListenerFactory,
|
||||
deps: [TRANSITION_ID, DOCUMENT],
|
||||
useFactory: appInitializerFactory,
|
||||
deps: [TRANSITION_ID, DOCUMENT, Injector],
|
||||
multi: true
|
||||
},
|
||||
];
|
||||
|
@ -13,8 +13,9 @@ import {WebWorkerPlatformLocation} from './platform_location';
|
||||
|
||||
|
||||
/**
|
||||
* Those providers should be added when the router is used in a worker context in addition to the
|
||||
* {@link ROUTER_PROVIDERS} and after them.
|
||||
* The {@link PlatformLocation} providers that should be added when the {@link Location} is used in
|
||||
* a worker context.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const WORKER_APP_LOCATION_PROVIDERS = [
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user