Compare commits
130 Commits
7.2.1
...
backup-7.2
Author | SHA1 | Date | |
---|---|---|---|
279c57f38d | |||
35ec2dc9bc | |||
332dcb2d47 | |||
2c3b084817 | |||
918d906a1e | |||
12e3b71740 | |||
9faad4b2b0 | |||
42ccea547e | |||
67e6b63165 | |||
88d415515a | |||
3bc02a3652 | |||
a3f72d92f2 | |||
f86eb9a133 | |||
ac3c9c1e6e | |||
c9f213c0ea | |||
23d8edb263 | |||
23e2127cee | |||
0af5b8eb7a | |||
17403a2b1d | |||
325bf558ba | |||
21a9fe64d6 | |||
961d55395d | |||
c7fe8de182 | |||
7928356a62 | |||
0316fda529 | |||
6650e525ec | |||
2558e6843c | |||
1b2ba78090 | |||
5f0d845ca9 | |||
5f616ca613 | |||
b47ea97a48 | |||
f8101804a2 | |||
5a8c14b6d7 | |||
31556fa7b0 | |||
01d475d6f3 | |||
3c08b86e27 | |||
b102835294 | |||
0465dc6637 | |||
937a856b47 | |||
eeb4c87dc0 | |||
a82ba5d4dc | |||
28c6e9d684 | |||
9ea8f7197f | |||
0d9afd85f1 | |||
d2eea769f6 | |||
7bafe180fb | |||
457781906b | |||
2530c9d2dd | |||
c18c46a976 | |||
9447b852a4 | |||
11d184aaae | |||
d4b476fb45 | |||
a64a7a4041 | |||
9bda4460fa | |||
acb69e8341 | |||
69ca7cd381 | |||
e5f5ad34a2 | |||
51f8892d9b | |||
a06469c2f5 | |||
eb3345b950 | |||
ecc304adac | |||
49138bd114 | |||
aa7f2c8dc7 | |||
84857a267c | |||
e0ed78c2d0 | |||
b00efa3399 | |||
cdfca90053 | |||
0edca7997f | |||
2ce90323b8 | |||
7227b4aa62 | |||
5a7dd73b2c | |||
e1538fdd63 | |||
81da0fe91f | |||
fa377b350f | |||
097af5ae2b | |||
f1c3eab1e0 | |||
1edb8af20f | |||
0f34e3ac15 | |||
1cca9cc9d1 | |||
575cf3da0f | |||
11dfdf7f57 | |||
0bb03aaab1 | |||
df2704dc12 | |||
7714998869 | |||
c9c06a014f | |||
204b675ea4 | |||
23c666bdf5 | |||
9202a4de81 | |||
8c51e83a46 | |||
cbc45fd59b | |||
11fe52b805 | |||
50fadfaca3 | |||
9e04284bb3 | |||
216bbd09ef | |||
0c683a73e2 | |||
998bb8382e | |||
f893e2e502 | |||
bbe656e634 | |||
17825e8f55 | |||
7242551213 | |||
1ad88e0e59 | |||
6cc864a771 | |||
3fc02ce60b | |||
0a54825b29 | |||
970e3a0f79 | |||
791d8656fd | |||
7e98eedf5e | |||
5aecb2d43d | |||
9a15a42f29 | |||
fdd8b0402c | |||
d8aeb59c1c | |||
679235a44a | |||
e9de47c969 | |||
061615c16e | |||
213e01bd38 | |||
92d3942691 | |||
4ad7e21ce8 | |||
4b78c23c65 | |||
70c184b4bd | |||
98a617e415 | |||
0ae1e93eed | |||
dd59e9a343 | |||
89fd1822d4 | |||
4ae1880642 | |||
c51b0b9564 | |||
7bb760f6ff | |||
2013ef7ed0 | |||
6a13c107cf | |||
a3debf6a72 | |||
b1560f7357 |
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,30 +1,3 @@
|
||||
<a name="7.2.1"></a>
|
||||
## [7.2.1](https://github.com/angular/angular/compare/7.2.0...7.2.1) (2019-01-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** Add [@bazel](https://github.com/bazel)/bazel to dev deps ([#28032](https://github.com/angular/angular/issues/28032)) ([21093b9](https://github.com/angular/angular/commit/21093b9))
|
||||
* **bazel:** Add /bazel-out to .gitignore ([#27874](https://github.com/angular/angular/issues/27874)) ([e4fc8ba](https://github.com/angular/angular/commit/e4fc8ba))
|
||||
* **bazel:** Add ibazel to deps of Bazel project ([#28090](https://github.com/angular/angular/issues/28090)) ([28d34b6](https://github.com/angular/angular/commit/28d34b6))
|
||||
* **bazel:** Bazel schematics should add router package ([#28141](https://github.com/angular/angular/issues/28141)) ([02a852a](https://github.com/angular/angular/commit/02a852a))
|
||||
* **bazel:** flat module misses AMD module name on windows ([#27839](https://github.com/angular/angular/issues/27839)) ([c3d8e28](https://github.com/angular/angular/commit/c3d8e28))
|
||||
* **bazel:** incorrectly always uses ngc-wrapped from "npm" workspace ([#28137](https://github.com/angular/angular/issues/28137)) ([ca3965a](https://github.com/angular/angular/commit/ca3965a))
|
||||
* **bazel:** ng_package creates invalid typings reexport on windows ([#27829](https://github.com/angular/angular/issues/27829)) ([6b394f6](https://github.com/angular/angular/commit/6b394f6))
|
||||
* **bazel:** packager not properly removing amd directives on windows ([#27829](https://github.com/angular/angular/issues/27829)) ([fad4145](https://github.com/angular/angular/commit/fad4145))
|
||||
* **bazel:** protractor rule does not run spec files with underscore ([#28022](https://github.com/angular/angular/issues/28022)) ([f05c5f8](https://github.com/angular/angular/commit/f05c5f8))
|
||||
* **bazel:** protractor utils cannot start server on windows ([#27915](https://github.com/angular/angular/issues/27915)) ([0be8487](https://github.com/angular/angular/commit/0be8487))
|
||||
* **bazel:** replay compilation uses wrong compiler for building esm5 ([#28053](https://github.com/angular/angular/issues/28053)) ([fbbdaaa](https://github.com/angular/angular/commit/fbbdaaa))
|
||||
* **router:** ensure URL is updated after second redirect with UrlUpdateStrategy="eager" ([#27680](https://github.com/angular/angular/issues/27680)) ([6ae7aee](https://github.com/angular/angular/commit/6ae7aee)), closes [#27116](https://github.com/angular/angular/issues/27116)
|
||||
* **service-worker:** navigation urls backwards compatibility ([#27244](https://github.com/angular/angular/issues/27244)) ([585e871](https://github.com/angular/angular/commit/585e871))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **platform-server:** use shared `DomElementSchemaRegistry` instance ([#28150](https://github.com/angular/angular/issues/28150)) ([#28151](https://github.com/angular/angular/issues/28151)) ([6851581](https://github.com/angular/angular/commit/6851581))
|
||||
|
||||
|
||||
|
||||
<a name="7.2.0"></a>
|
||||
# [7.2.0](https://github.com/angular/angular/compare/7.1.4...7.2.0) (2019-01-07)
|
||||
|
||||
|
@ -8,15 +8,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-hello-world-ivy": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 507677,
|
||||
"polyfills": 38390
|
||||
}
|
||||
}
|
||||
},
|
||||
"hello_world__closure": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
|
53
integration/bazel-schematics/package.json.replace
Normal file
53
integration/bazel-schematics/package.json.replace
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "demo",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../angular/dist/packages-dist/animations",
|
||||
"@angular/common": "file:../angular/dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../angular/dist/packages-dist/compiler",
|
||||
"@angular/core": "file:../angular/dist/packages-dist/core",
|
||||
"@angular/forms": "file:../angular/dist/packages-dist/forms",
|
||||
"@angular/platform-browser": "file:../angular/dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../angular/dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/router": "file:../angular/dist/packages-dist/router",
|
||||
"core-js": "^2.5.4",
|
||||
"rxjs": "~6.3.3",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "~0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.12.0",
|
||||
"@angular/bazel": "file:../angular/dist/packages-dist/bazel",
|
||||
"@angular/cli": "~7.2.1",
|
||||
"@angular/compiler-cli": "file:../angular/dist/packages-dist/compiler-cli",
|
||||
"@angular/language-service": "file:../angular/dist/packages-dist/language-service",
|
||||
"@bazel/bazel": "^0.21.0",
|
||||
"@bazel/ibazel": "^0.9.0",
|
||||
"@bazel/karma": "^0.22.1",
|
||||
"@bazel/typescript": "^0.22.1",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "~4.5.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~3.1.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.2",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"protractor": "~5.4.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "~3.2.2"
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ function testBazel() {
|
||||
# TODO(kyliau) Remove this once the type annotations are added to AppPage
|
||||
# https://github.com/angular/angular-cli/pull/13406
|
||||
cp ../app.po.ts ./e2e/src/
|
||||
cp ../package.json.replace ./package.json
|
||||
# Run build
|
||||
# TODO(kyliau): Use `bazel build` for now. Running `ng build` requires
|
||||
# node_modules to be available in project directory.
|
||||
|
@ -73,7 +73,7 @@
|
||||
"@angular-devkit/architect" "^0.10.6"
|
||||
"@angular-devkit/core" "^7.0.4"
|
||||
"@angular-devkit/schematics" "^7.0.4"
|
||||
"@bazel/typescript" "^0.21.0"
|
||||
"@bazel/typescript" "^0.22.1"
|
||||
"@schematics/angular" "^7.0.4"
|
||||
"@types/node" "6.0.84"
|
||||
semver "^5.6.0"
|
||||
@ -115,12 +115,13 @@
|
||||
"@bazel/bazel-linux_x64" "0.21.0"
|
||||
"@bazel/bazel-win32_x64" "0.21.0"
|
||||
|
||||
"@bazel/typescript@^0.21.0":
|
||||
version "0.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.21.0.tgz#41c304f77a42c6a016280d0f4c20e0749c3f4b2a"
|
||||
integrity sha512-ASXj0RFybmqoa3LwqkTU3gNkX9bY9wL/VDNo5hlp9pynYWl4RMpe9V3m/qDIdtSuLJ+qD+Z3FKT/OcpWQHMlYA==
|
||||
"@bazel/typescript@^0.22.1":
|
||||
version "0.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.22.1.tgz#b52c00e8560019e2f9d273d45c04785e0ec9d9bd"
|
||||
integrity sha512-88DaCCnNg8rPlKP0eAQEZuoiJkEPeiItpUS3oBR1sFQNBRJb56D25ahK8+N6LJk4qaH+ZQ1/AHOPDhfEEWvDzA==
|
||||
dependencies:
|
||||
protobufjs "5.0.3"
|
||||
semver "5.6.0"
|
||||
source-map-support "0.5.9"
|
||||
tsutils "2.27.2"
|
||||
|
||||
@ -1907,7 +1908,7 @@ semver-intersect@1.4.0:
|
||||
dependencies:
|
||||
semver "^5.0.0"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||
"semver@2 || 3 || 4 || 5", semver@5.6.0, semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
|
@ -1,6 +1,6 @@
|
||||
# CliHelloWorldIvy
|
||||
# CliHelloWorld
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0-rc.0.
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.6.
|
||||
|
||||
## Development server
|
||||
|
||||
@ -12,7 +12,7 @@ Run `ng generate component component-name` to generate a new component. You can
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
|
@ -1,17 +1,14 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"packageManager": "yarn"
|
||||
},
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"cli-hello-world-ivy": {
|
||||
"cli-hello-world": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
@ -22,36 +19,47 @@
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
{
|
||||
"glob": "assets",
|
||||
"input": "/src",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "favicon.ico",
|
||||
"input": "/src",
|
||||
"output": "/"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
{
|
||||
"input": "src/styles.css"
|
||||
}
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"dev": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
"from": "src/environments/environment.ts",
|
||||
"to": "dist/environments/environment.ts"
|
||||
}
|
||||
],
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
"fileReplacements": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"src": "src/environments/environment.ts",
|
||||
"replaceWith": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -60,17 +68,20 @@
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "cli-hello-world-ivy:build"
|
||||
"browserTarget": "cli-hello-world:build"
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"browserTarget": "cli-hello-world:build:dev"
|
||||
},
|
||||
"production": {
|
||||
"browserTarget": "cli-hello-world-ivy:build:production"
|
||||
"browserTarget": "cli-hello-world:build:production"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
},
|
||||
"ci-production": {
|
||||
"browserTarget": "cli-hello-world-ivy:build:production",
|
||||
"browserTarget": "cli-hello-world:build:production",
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
@ -78,7 +89,7 @@
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "cli-hello-world-ivy:build"
|
||||
"browserTarget": "cli-hello-world:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@ -86,15 +97,25 @@
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"karmaConfig": "./karma.conf.js",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"styles": [
|
||||
{
|
||||
"input": "src/styles.css"
|
||||
}
|
||||
],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
{
|
||||
"glob": "assets",
|
||||
"input": "/src",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "favicon.ico",
|
||||
"input": "/src",
|
||||
"output": "/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -112,33 +133,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-hello-world-ivy-e2e": {
|
||||
"root": "e2e/",
|
||||
"cli-hello-world-e2e": {
|
||||
"root": "",
|
||||
"projectType": "application",
|
||||
"prefix": "",
|
||||
"cli": {},
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "cli-hello-world-ivy:serve"
|
||||
"protractorConfig": "./protractor.conf.js",
|
||||
"devServerTarget": "cli-hello-world:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "cli-hello-world-ivy:serve:production"
|
||||
"devServerTarget": "cli-hello-world:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "cli-hello-world-ivy:serve:ci"
|
||||
"devServerTarget": "cli-hello-world:serve:ci"
|
||||
},
|
||||
"ci-production": {
|
||||
"devServerTarget": "cli-hello-world-ivy:serve:ci-production"
|
||||
"devServerTarget": "cli-hello-world:serve:ci-production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||
"tsConfig": [
|
||||
"e2e/tsconfig.e2e.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
@ -147,5 +171,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "cli-hello-world-ivy"
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"styleext": "css"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ describe('cli-hello-world App', () => {
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toEqual('Welcome to cli-hello-world-ivy!');
|
||||
expect(page.getParagraphText()).toEqual('Welcome to app!');
|
||||
});
|
||||
|
||||
it('the percent pipe should work', () => {
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"baseUrl": "./",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
@ -10,4 +11,4 @@
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,16 @@ module.exports = function (config) {
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, 'coverage'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
environment: 'dev'
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "cli-hello-world-ivy",
|
||||
"name": "cli-hello-world",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -29,24 +29,24 @@
|
||||
"zone.js": "file:../../node_modules/zone.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.12.0-rc.0",
|
||||
"@angular/cli": "~7.2.0-rc.0",
|
||||
"@angular-devkit/build-angular": "~0.10.3",
|
||||
"@angular/cli": "7.0.3",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "~4.5.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"@types/jasmine": "~2.8.3",
|
||||
"@types/jasminewd2": "~2.0.4",
|
||||
"@types/node": "~6.0.60",
|
||||
"codelyzer": "^4.3.0",
|
||||
"jasmine-core": "~2.8.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~3.1.1",
|
||||
"karma": "~2.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.2",
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.11.0",
|
||||
"ts-node": "~4.1.0",
|
||||
"tslint": "~5.9.1",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
'./e2e/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
@ -25,7 +25,7 @@ exports.config = {
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||
project: 'e2e/tsconfig.e2e.json'
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
|
||||
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -9,23 +8,20 @@ describe('AppComponent', () => {
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
it('should create the app', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'cli-hello-world-ivy'`, () => {
|
||||
}));
|
||||
it(`should have as title 'app'`, async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('cli-hello-world-ivy');
|
||||
});
|
||||
|
||||
it('should render title in a h1 tag', () => {
|
||||
expect(app.title).toEqual('app');
|
||||
}));
|
||||
it('should render title in a h1 tag', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to cli-hello-world-ivy!');
|
||||
});
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
||||
}));
|
||||
});
|
||||
|
@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'cli-hello-world-ivy';
|
||||
title = 'app';
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11
|
@ -1,16 +1,8 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
// The file contents for the current environment will overwrite these during build.
|
||||
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
|
||||
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
|
||||
// The list of which env maps to which file can be found in `.angular-cli.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
|
@ -2,11 +2,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CliHelloWorldIvy</title>
|
||||
<title>CliHelloWorld</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<script>window['ngDevMode'] = true;</script>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
|
@ -9,4 +9,4 @@ if (environment.production) {
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
.catch(err => console.log(err));
|
||||
|
@ -11,17 +11,14 @@
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
|
||||
* This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
|
||||
*/
|
||||
|
||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
// import 'core-js/es6/symbol';
|
||||
// import 'core-js/es6/object';
|
||||
// import 'core-js/es6/function';
|
||||
@ -43,36 +40,19 @@
|
||||
/** IE10 and IE11 requires the following for the Reflect API. */
|
||||
// import 'core-js/es6/reflect';
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags.ts';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
* Required to support Web Animations `@angular/platform-browser/animations`.
|
||||
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
|
||||
**/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
@ -80,6 +60,7 @@
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
@ -2,13 +2,12 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"baseUrl": "./",
|
||||
"module": "es2015",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"test.ts",
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": "ngtsc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"baseUrl": "./",
|
||||
"module": "commonjs",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
@ -15,4 +17,4 @@
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": "ngtsc",
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "es5",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2018",
|
||||
"es2017",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
"node_modules/codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-return-shorthand": true,
|
||||
@ -18,6 +18,7 @@
|
||||
"forin": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs",
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-spacing": true,
|
||||
@ -65,7 +66,6 @@
|
||||
],
|
||||
"no-misused-new": true,
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
@ -117,6 +117,18 @@
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"no-output-on-prefix": true,
|
||||
"use-input-property-decorator": true,
|
||||
"use-output-property-decorator": true,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -61,9 +61,9 @@ for testDir in ${TEST_DIRS}; do
|
||||
yarn install --cache-folder ../$cache
|
||||
yarn test || exit 1
|
||||
|
||||
# Track payload size for cli-hello-world, cli-hello-world-ivy and hello_world__closure
|
||||
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]] || [[ $testDir == hello_world__closure ]]); then
|
||||
if ([[ $testDir == cli-hello-world ]] || [[ $testDir == cli-hello-world-ivy ]]); then
|
||||
# Track payload size for cli-hello-world and hello_world__closure and the render3 tests
|
||||
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__closure ]]); then
|
||||
if [[ $testDir == cli-hello-world ]]; then
|
||||
yarn build
|
||||
fi
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.2.1",
|
||||
"version": "7.2.0",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -23,7 +23,7 @@ export function supportsAnimationEventCreation() {
|
||||
try {
|
||||
makeAnimationEvent('end', 'test', 0);
|
||||
supported = true;
|
||||
} catch {
|
||||
} catch (e) {
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ export class NgForOf<T> implements DoCheck {
|
||||
if (!this._differ && value) {
|
||||
try {
|
||||
this._differ = this._differs.find(value).create(this.ngForTrackBy);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ export class BrowserViewportScroller implements ViewportScroller {
|
||||
private supportScrollRestoration(): boolean {
|
||||
try {
|
||||
return !!this.window && !!this.window.scrollTo;
|
||||
} catch {
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ ts_library(
|
||||
"//packages/platform-browser",
|
||||
"//packages/platform-browser-dynamic",
|
||||
"//packages/platform-browser/testing",
|
||||
"//packages/private/testing",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -11,7 +11,6 @@ import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_out
|
||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('insert/remove', () => {
|
||||
|
||||
@ -107,20 +106,17 @@ describe('insert/remove', () => {
|
||||
|
||||
}));
|
||||
|
||||
it('should resolve a with injector', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
modifiedInIvy('Static ViewChild and ContentChild queries are resolved in update mode')
|
||||
.it('should resolve with an injector', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
// We are accessing a ViewChild (ngComponentOutlet) before change detection has run
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBeNull();
|
||||
}));
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBeNull();
|
||||
}));
|
||||
|
||||
it('should render projectable nodes, if supplied', async(() => {
|
||||
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
||||
|
@ -22,7 +22,7 @@ import {performWatchCompilation, createPerformWatchHost} from './perform_watch'
|
||||
|
||||
export function main(
|
||||
args: string[], consoleError: (s: string) => void = console.error,
|
||||
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers): number {
|
||||
config?: NgcParsedConfiguration): number {
|
||||
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
||||
config || readNgcCommandLineAndConfiguration(args);
|
||||
if (configErrors.length) {
|
||||
@ -32,12 +32,8 @@ export function main(
|
||||
const result = watchMode(project, options, consoleError);
|
||||
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
|
||||
}
|
||||
const {diagnostics: compileDiags} = performCompilation({
|
||||
rootNames,
|
||||
options,
|
||||
emitFlags,
|
||||
emitCallback: createEmitCallback(options), customTransformers
|
||||
});
|
||||
const {diagnostics: compileDiags} = performCompilation(
|
||||
{rootNames, options, emitFlags, emitCallback: createEmitCallback(options)});
|
||||
return reportErrorsAndExit(compileDiags, options, consoleError);
|
||||
}
|
||||
|
||||
|
@ -1,118 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReferencesRegistry} from '../../../ngtsc/annotations';
|
||||
import {Declaration} from '../../../ngtsc/host';
|
||||
import {ResolvedReference} from '../../../ngtsc/metadata';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
||||
|
||||
export interface ModuleWithProvidersInfo {
|
||||
/**
|
||||
* The declaration (in the .d.ts file) of the function that returns
|
||||
* a `ModuleWithProviders object, but has a signature that needs
|
||||
* a type parameter adding.
|
||||
*/
|
||||
declaration: ts.MethodDeclaration|ts.FunctionDeclaration;
|
||||
/**
|
||||
* The NgModule class declaration (in the .d.ts file) to add as a type parameter.
|
||||
*/
|
||||
ngModule: Declaration;
|
||||
}
|
||||
|
||||
export type ModuleWithProvidersAnalyses = Map<ts.SourceFile, ModuleWithProvidersInfo[]>;
|
||||
export const ModuleWithProvidersAnalyses = Map;
|
||||
|
||||
export class ModuleWithProvidersAnalyzer {
|
||||
constructor(private host: NgccReflectionHost, private referencesRegistry: ReferencesRegistry) {}
|
||||
|
||||
analyzeProgram(program: ts.Program): ModuleWithProvidersAnalyses {
|
||||
const analyses = new ModuleWithProvidersAnalyses();
|
||||
const rootFiles = this.getRootFiles(program);
|
||||
rootFiles.forEach(f => {
|
||||
const fns = this.host.getModuleWithProvidersFunctions(f);
|
||||
fns && fns.forEach(fn => {
|
||||
const dtsFn = this.getDtsDeclaration(fn.declaration);
|
||||
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
|
||||
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
|
||||
null;
|
||||
if (!typeParam || isAnyKeyword(typeParam)) {
|
||||
// Either we do not have a parameterized type or the type is `any`.
|
||||
let ngModule = this.host.getDeclarationOfIdentifier(fn.ngModule);
|
||||
if (!ngModule) {
|
||||
throw new Error(
|
||||
`Cannot find a declaration for NgModule ${fn.ngModule.text} referenced in ${fn.declaration.getText()}`);
|
||||
}
|
||||
// For internal (non-library) module references, redirect the module's value declaration
|
||||
// to its type declaration.
|
||||
if (ngModule.viaModule === null) {
|
||||
const dtsNgModule = this.host.getDtsDeclaration(ngModule.node);
|
||||
if (!dtsNgModule) {
|
||||
throw new Error(
|
||||
`No typings declaration can be found for the referenced NgModule class in ${fn.declaration.getText()}.`);
|
||||
}
|
||||
if (!ts.isClassDeclaration(dtsNgModule)) {
|
||||
throw new Error(
|
||||
`The referenced NgModule in ${fn.declaration.getText()} is not a class declaration in the typings program; instead we get ${dtsNgModule.getText()}`);
|
||||
}
|
||||
// Record the usage of the internal module as it needs to become an exported symbol
|
||||
this.referencesRegistry.add(new ResolvedReference(ngModule.node, fn.ngModule));
|
||||
|
||||
ngModule = {node: dtsNgModule, viaModule: null};
|
||||
}
|
||||
const dtsFile = dtsFn.getSourceFile();
|
||||
const analysis = analyses.get(dtsFile) || [];
|
||||
analysis.push({declaration: dtsFn, ngModule});
|
||||
analyses.set(dtsFile, analysis);
|
||||
}
|
||||
});
|
||||
});
|
||||
return analyses;
|
||||
}
|
||||
|
||||
private getRootFiles(program: ts.Program): ts.SourceFile[] {
|
||||
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
|
||||
}
|
||||
|
||||
private getDtsDeclaration(fn: ts.SignatureDeclaration) {
|
||||
let dtsFn: ts.Declaration|null = null;
|
||||
const containerClass = this.host.getClassSymbol(fn.parent);
|
||||
const fnName = fn.name && ts.isIdentifier(fn.name) && fn.name.text;
|
||||
if (containerClass && fnName) {
|
||||
const dtsClass = this.host.getDtsDeclaration(containerClass.valueDeclaration);
|
||||
// Get the declaration of the matching static method
|
||||
dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ?
|
||||
dtsClass.members
|
||||
.find(
|
||||
member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
|
||||
member.name.text === fnName) as ts.Declaration :
|
||||
null;
|
||||
} else {
|
||||
dtsFn = this.host.getDtsDeclaration(fn);
|
||||
}
|
||||
if (!dtsFn) {
|
||||
throw new Error(`Matching type declaration for ${fn.getText()} is missing`);
|
||||
}
|
||||
if (!isFunctionOrMethod(dtsFn)) {
|
||||
throw new Error(
|
||||
`Matching type declaration for ${fn.getText()} is not a function: ${dtsFn.getText()}`);
|
||||
}
|
||||
return dtsFn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isFunctionOrMethod(declaration: ts.Declaration): declaration is ts.FunctionDeclaration|
|
||||
ts.MethodDeclaration {
|
||||
return ts.isFunctionDeclaration(declaration) || ts.isMethodDeclaration(declaration);
|
||||
}
|
||||
|
||||
function isAnyKeyword(typeParam: ts.TypeNode): typeParam is ts.KeywordTypeNode {
|
||||
return typeParam.kind === ts.SyntaxKind.AnyKeyword;
|
||||
}
|
@ -15,7 +15,7 @@ import {hasNameIdentifier, isDefined} from '../utils';
|
||||
export interface ExportInfo {
|
||||
identifier: string;
|
||||
from: string;
|
||||
dtsFrom?: string|null;
|
||||
dtsFrom: string|null;
|
||||
}
|
||||
export type PrivateDeclarationsAnalyses = ExportInfo[];
|
||||
|
||||
@ -52,7 +52,7 @@ export class PrivateDeclarationsAnalyzer {
|
||||
return Array.from(privateDeclarations.keys()).map(id => {
|
||||
const from = id.getSourceFile().fileName;
|
||||
const declaration = privateDeclarations.get(id) !;
|
||||
const dtsDeclaration = this.host.getDtsDeclaration(declaration.node);
|
||||
const dtsDeclaration = this.host.getDtsDeclarationOfClass(declaration.node);
|
||||
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
|
||||
return {identifier: id.text, from, dtsFrom};
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ import {BundleProgram} from '../packages/bundle_program';
|
||||
import {findAll, getNameText, isDefined} from '../utils';
|
||||
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||
import {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||
|
||||
export const DECORATORS = 'decorators' as ts.__String;
|
||||
export const PROP_DECORATORS = 'propDecorators' as ts.__String;
|
||||
@ -49,10 +49,10 @@ export const CONSTRUCTOR_PARAMS = 'ctorParameters' as ts.__String;
|
||||
* a static method called `ctorParameters`.
|
||||
*/
|
||||
export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost {
|
||||
protected dtsDeclarationMap: Map<string, ts.Declaration>|null;
|
||||
protected dtsClassMap: Map<string, ts.ClassDeclaration>|null;
|
||||
constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) {
|
||||
super(checker);
|
||||
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
|
||||
this.dtsClassMap = dts && this.computeDtsClassMap(dts.path, dts.program) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,15 +327,15 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* is not a class or has an unknown number of type parameters.
|
||||
*/
|
||||
getGenericArityOfClass(clazz: ts.Declaration): number|null {
|
||||
const dtsDeclaration = this.getDtsDeclaration(clazz);
|
||||
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
|
||||
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
|
||||
const dtsClass = this.getDtsDeclarationOfClass(clazz);
|
||||
if (dtsClass) {
|
||||
return dtsClass.typeParameters ? dtsClass.typeParameters.length : 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an exported declaration of a class (maybe down-leveled to a variable) and look up the
|
||||
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
|
||||
* declaration of its type in a separate .d.ts tree.
|
||||
*
|
||||
* This function is allowed to return `null` if the current compilation unit does not have a
|
||||
@ -346,47 +346,19 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
|
||||
* `ts.Program` as the input declaration.
|
||||
*/
|
||||
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null {
|
||||
if (!this.dtsDeclarationMap) {
|
||||
return null;
|
||||
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null {
|
||||
if (this.dtsClassMap) {
|
||||
if (ts.isClassDeclaration(declaration)) {
|
||||
if (!declaration.name || !ts.isIdentifier(declaration.name)) {
|
||||
throw new Error(
|
||||
`Cannot get the dts file for a class declaration that has no indetifier: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
return this.dtsClassMap.get(declaration.name.text) || null;
|
||||
}
|
||||
}
|
||||
if (!isNamedDeclaration(declaration)) {
|
||||
throw new Error(
|
||||
`Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
|
||||
}
|
||||
return this.dtsDeclarationMap.get(declaration.name.text) || null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given source file for exported functions and static class methods that return
|
||||
* ModuleWithProviders objects.
|
||||
* @param f The source file to search for these functions
|
||||
* @returns An array of function declarations that look like they return ModuleWithProviders
|
||||
* objects.
|
||||
*/
|
||||
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[] {
|
||||
const exports = this.getExportsOfModule(f);
|
||||
if (!exports) return [];
|
||||
const infos: ModuleWithProvidersFunction[] = [];
|
||||
exports.forEach((declaration, name) => {
|
||||
if (this.isClass(declaration.node)) {
|
||||
this.getMembersOfClass(declaration.node).forEach(member => {
|
||||
if (member.isStatic) {
|
||||
const info = this.parseForModuleWithProviders(member.node);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const info = this.parseForModuleWithProviders(declaration.node);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
}
|
||||
}
|
||||
});
|
||||
return infos;
|
||||
}
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
@ -766,7 +738,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
if (isNamedDeclaration(node)) {
|
||||
if (isNamedDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
|
||||
name = node.name.text;
|
||||
nameNode = node.name;
|
||||
} else {
|
||||
@ -874,8 +846,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type and decorators for a class where the information is stored via
|
||||
* calls to `__decorate` helpers.
|
||||
* Get the parameter type and decorators for a class where the information is stored on
|
||||
* in calls to `__decorate` helpers.
|
||||
*
|
||||
* Reflect over the helpers to find the decorators and types about each of
|
||||
* the class's constructor parameters.
|
||||
@ -1030,9 +1002,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
* @param dtsProgram The program containing all the typings files.
|
||||
* @returns a map of class names to class declarations.
|
||||
*/
|
||||
protected computeDtsDeclarationMap(dtsRootFileName: string, dtsProgram: ts.Program):
|
||||
Map<string, ts.Declaration> {
|
||||
const dtsDeclarationMap = new Map<string, ts.Declaration>();
|
||||
protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program):
|
||||
Map<string, ts.ClassDeclaration> {
|
||||
const dtsClassMap = new Map<string, ts.ClassDeclaration>();
|
||||
const checker = dtsProgram.getTypeChecker();
|
||||
|
||||
// First add all the classes that are publicly exported from the entry-point
|
||||
@ -1040,38 +1012,13 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
if (!rootFile) {
|
||||
throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`);
|
||||
}
|
||||
collectExportedDeclarations(checker, dtsDeclarationMap, rootFile);
|
||||
collectExportedClasses(checker, dtsClassMap, rootFile);
|
||||
|
||||
// Now add any additional classes that are exported from individual dts files,
|
||||
// but are not publicly exported from the entry-point.
|
||||
dtsProgram.getSourceFiles().forEach(
|
||||
sourceFile => { collectExportedDeclarations(checker, dtsDeclarationMap, sourceFile); });
|
||||
return dtsDeclarationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given node, to see if it is a function that returns a `ModuleWithProviders` object.
|
||||
* @param node a node to check to see if it is a function that returns a `ModuleWithProviders`
|
||||
* object.
|
||||
* @returns info about the function if it does return a `ModuleWithProviders` object; `null`
|
||||
* otherwise.
|
||||
*/
|
||||
protected parseForModuleWithProviders(node: ts.Node|null): ModuleWithProvidersFunction|null {
|
||||
const declaration =
|
||||
node && (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) ? node : null;
|
||||
const body = declaration ? this.getDefinitionOfFunction(declaration).body : null;
|
||||
const lastStatement = body && body[body.length - 1];
|
||||
const returnExpression =
|
||||
lastStatement && ts.isReturnStatement(lastStatement) && lastStatement.expression || null;
|
||||
const ngModuleProperty = returnExpression && ts.isObjectLiteralExpression(returnExpression) &&
|
||||
returnExpression.properties.find(
|
||||
prop =>
|
||||
!!prop.name && ts.isIdentifier(prop.name) && prop.name.text === 'ngModule') ||
|
||||
null;
|
||||
const ngModule = ngModuleProperty && ts.isPropertyAssignment(ngModuleProperty) &&
|
||||
ts.isIdentifier(ngModuleProperty.initializer) && ngModuleProperty.initializer ||
|
||||
null;
|
||||
return ngModule && declaration && {ngModule, declaration};
|
||||
sourceFile => { collectExportedClasses(checker, dtsClassMap, sourceFile); });
|
||||
return dtsClassMap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1182,10 +1129,8 @@ function isThisAssignment(node: ts.Declaration): node is ts.BinaryExpression&
|
||||
node.left.expression.kind === ts.SyntaxKind.ThisKeyword;
|
||||
}
|
||||
|
||||
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration&
|
||||
{name: ts.Identifier} {
|
||||
const anyNode: any = node;
|
||||
return !!anyNode.name && ts.isIdentifier(anyNode.name);
|
||||
function isNamedDeclaration(node: ts.Declaration): node is ts.NamedDeclaration {
|
||||
return !!(node as any).name;
|
||||
}
|
||||
|
||||
|
||||
@ -1208,11 +1153,13 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect mappings between exported declarations in a source file and its associated
|
||||
* declaration in the typings program.
|
||||
* Search a source file for exported classes, storing them in the provided `dtsClassMap`.
|
||||
* @param checker The typechecker for the source program.
|
||||
* @param dtsClassMap The map in which to store the collected exported classes.
|
||||
* @param srcFile The source file to search for exported classes.
|
||||
*/
|
||||
function collectExportedDeclarations(
|
||||
checker: ts.TypeChecker, dtsDeclarationMap: Map<string, ts.Declaration>,
|
||||
function collectExportedClasses(
|
||||
checker: ts.TypeChecker, dtsClassMap: Map<string, ts.ClassDeclaration>,
|
||||
srcFile: ts.SourceFile): void {
|
||||
const srcModule = srcFile && checker.getSymbolAtLocation(srcFile);
|
||||
const moduleExports = srcModule && checker.getExportsOfModule(srcModule);
|
||||
@ -1223,8 +1170,8 @@ function collectExportedDeclarations(
|
||||
}
|
||||
const declaration = exportedSymbol.valueDeclaration;
|
||||
const name = exportedSymbol.name;
|
||||
if (declaration && !dtsDeclarationMap.has(name)) {
|
||||
dtsDeclarationMap.set(name, declaration);
|
||||
if (declaration && ts.isClassDeclaration(declaration) && !dtsClassMap.has(name)) {
|
||||
dtsClassMap.set(name, declaration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -19,21 +19,6 @@ export function isSwitchableVariableDeclaration(node: ts.Node):
|
||||
ts.isIdentifier(node.initializer) && node.initializer.text.endsWith(PRE_R3_MARKER);
|
||||
}
|
||||
|
||||
/**
|
||||
* A structure returned from `getModuleWithProviderInfo` that describes functions
|
||||
* that return ModuleWithProviders objects.
|
||||
*/
|
||||
export interface ModuleWithProvidersFunction {
|
||||
/**
|
||||
* The declaration of the function that returns the `ModuleWithProviders` object.
|
||||
*/
|
||||
declaration: ts.SignatureDeclaration;
|
||||
/**
|
||||
* The identifier of the `ngModule` property on the `ModuleWithProviders` object.
|
||||
*/
|
||||
ngModule: ts.Identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reflection host that has extra methods for looking at non-Typescript package formats
|
||||
*/
|
||||
@ -60,13 +45,4 @@ export interface NgccReflectionHost extends ReflectionHost {
|
||||
* @returns An array of decorated classes.
|
||||
*/
|
||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
|
||||
|
||||
/**
|
||||
* Search the given source file for exported functions and static class methods that return
|
||||
* ModuleWithProviders objects.
|
||||
* @param f The source file to search for these functions
|
||||
* @returns An array of info items about each of the functions that return ModuleWithProviders
|
||||
* objects.
|
||||
*/
|
||||
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[];
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import {mkdir, mv} from 'shelljs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../analysis/module_with_providers_analyzer';
|
||||
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
|
||||
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
|
||||
@ -26,7 +25,6 @@ import {EntryPoint} from './entry_point';
|
||||
import {EntryPointBundle} from './entry_point_bundle';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A Package is stored in a directory on disk and that directory can contain one or more package
|
||||
* formats - e.g. fesm2015, UMD, etc. Additionally, each package provides typings (`.d.ts` files).
|
||||
@ -61,14 +59,13 @@ export class Transformer {
|
||||
const reflectionHost = this.getHost(isCore, bundle);
|
||||
|
||||
// Parse and analyze the files.
|
||||
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle);
|
||||
const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
this.analyzeProgram(reflectionHost, isCore, bundle);
|
||||
|
||||
// Transform the source files and source maps.
|
||||
const renderer = this.getRenderer(reflectionHost, isCore, bundle);
|
||||
const renderedFiles = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
// Write out all the transformed files.
|
||||
renderedFiles.forEach(file => this.writeFile(file));
|
||||
@ -105,26 +102,16 @@ export class Transformer {
|
||||
ProgramAnalyses {
|
||||
const typeChecker = bundle.src.program.getTypeChecker();
|
||||
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
|
||||
|
||||
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
|
||||
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
const decorationAnalyzer = new DecorationAnalyzer(
|
||||
typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
|
||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
const moduleWithProvidersAnalyzer =
|
||||
bundle.dts && new ModuleWithProvidersAnalyzer(reflectionHost, referencesRegistry);
|
||||
const moduleWithProvidersAnalyses = moduleWithProvidersAnalyzer &&
|
||||
moduleWithProvidersAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost);
|
||||
const privateDeclarationsAnalyzer =
|
||||
new PrivateDeclarationsAnalyzer(reflectionHost, referencesRegistry);
|
||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program);
|
||||
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
||||
const privateDeclarationsAnalyses =
|
||||
privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses};
|
||||
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
|
||||
}
|
||||
|
||||
writeFile(file: FileInfo): void {
|
||||
@ -142,5 +129,4 @@ interface ProgramAnalyses {
|
||||
decorationAnalyses: Map<ts.SourceFile, CompiledFile>;
|
||||
switchMarkerAnalyses: SwitchMarkerAnalyses;
|
||||
privateDeclarationsAnalyses: ExportInfo[];
|
||||
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null;
|
||||
}
|
||||
|
@ -15,10 +15,9 @@ import * as ts from 'typescript';
|
||||
|
||||
import {Decorator} from '../../../ngtsc/host';
|
||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
|
||||
import {translateStatement, translateType} from '../../../ngtsc/translator';
|
||||
import {NgccImportManager} from './ngcc_import_manager';
|
||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
@ -50,20 +49,6 @@ interface DtsClassInfo {
|
||||
compilation: CompileResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A structure that captures information about what needs to be rendered
|
||||
* in a typings file.
|
||||
*
|
||||
* It is created as a result of processing the analysis passed to the renderer.
|
||||
*
|
||||
* The `renderDtsFile()` method consumes it when rendering a typings file.
|
||||
*/
|
||||
class DtsRenderInfo {
|
||||
classInfo: DtsClassInfo[] = [];
|
||||
moduleWithProviders: ModuleWithProvidersInfo[] = [];
|
||||
privateExports: ExportInfo[] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* The collected decorators that have become redundant after the compilation
|
||||
* of Ivy static fields. The map is keyed by the container node, such that we
|
||||
@ -86,8 +71,7 @@ export abstract class Renderer {
|
||||
|
||||
renderProgram(
|
||||
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|null): FileInfo[] {
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
||||
const renderedFiles: FileInfo[] = [];
|
||||
|
||||
// Transform the source files.
|
||||
@ -103,16 +87,16 @@ export abstract class Renderer {
|
||||
|
||||
// Transform the .d.ts files
|
||||
if (this.bundle.dts) {
|
||||
const dtsFiles = this.getTypingsFilesToRender(
|
||||
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
||||
const dtsFiles = this.getTypingsFilesToRender(decorationAnalyses);
|
||||
|
||||
// If the dts entry-point is not already there (it did not have compiled classes)
|
||||
// then add it now, to ensure it gets its extra exports rendered.
|
||||
if (!dtsFiles.has(this.bundle.dts.file)) {
|
||||
dtsFiles.set(this.bundle.dts.file, new DtsRenderInfo());
|
||||
dtsFiles.set(this.bundle.dts.file, []);
|
||||
}
|
||||
dtsFiles.forEach(
|
||||
(renderInfo, file) => renderedFiles.push(...this.renderDtsFile(file, renderInfo)));
|
||||
(classes, file) => renderedFiles.push(
|
||||
...this.renderDtsFile(file, classes, privateDeclarationsAnalyses)));
|
||||
}
|
||||
|
||||
return renderedFiles;
|
||||
@ -167,12 +151,14 @@ export abstract class Renderer {
|
||||
return this.renderSourceAndMap(sourceFile, input, outputText);
|
||||
}
|
||||
|
||||
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
|
||||
renderDtsFile(
|
||||
dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[],
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
||||
const input = this.extractSourceMap(dtsFile);
|
||||
const outputText = new MagicString(input.source);
|
||||
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
|
||||
|
||||
renderInfo.classInfo.forEach(dtsClass => {
|
||||
dtsClasses.forEach(dtsClass => {
|
||||
const endOfClass = dtsClass.dtsDeclaration.getEnd();
|
||||
dtsClass.compilation.forEach(declaration => {
|
||||
const type = translateType(declaration.type, importManager);
|
||||
@ -181,67 +167,26 @@ export abstract class Renderer {
|
||||
});
|
||||
});
|
||||
|
||||
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
|
||||
this.addImports(
|
||||
outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile));
|
||||
|
||||
this.addExports(outputText, dtsFile.fileName, renderInfo.privateExports);
|
||||
|
||||
if (dtsFile === this.bundle.dts !.file) {
|
||||
const dtsExports = privateDeclarationsAnalyses.map(e => {
|
||||
if (!e.dtsFrom) {
|
||||
throw new Error(
|
||||
`There is no typings path for ${e.identifier} in ${e.from}.\n` +
|
||||
`We need to add an export for this class to a .d.ts typings file because ` +
|
||||
`Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` +
|
||||
`The simplest fix for this is to ensure that this class is exported from the package's entry-point.`);
|
||||
}
|
||||
return {identifier: e.identifier, from: e.dtsFrom};
|
||||
});
|
||||
this.addExports(outputText, dtsFile.fileName, dtsExports);
|
||||
}
|
||||
|
||||
return this.renderSourceAndMap(dtsFile, input, outputText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the type parameters to the appropriate functions that return `ModuleWithProviders`
|
||||
* structures.
|
||||
*
|
||||
* This function only gets called on typings files, so it doesn't need different implementations
|
||||
* for each bundle format.
|
||||
*/
|
||||
protected addModuleWithProvidersParams(
|
||||
outputText: MagicString, moduleWithProviders: ModuleWithProvidersInfo[],
|
||||
importManager: NgccImportManager): void {
|
||||
moduleWithProviders.forEach(info => {
|
||||
const ngModuleName = (info.ngModule.node as ts.ClassDeclaration).name !.text;
|
||||
const declarationFile = info.declaration.getSourceFile().fileName;
|
||||
const ngModuleFile = info.ngModule.node.getSourceFile().fileName;
|
||||
const importPath = info.ngModule.viaModule ||
|
||||
(declarationFile !== ngModuleFile ?
|
||||
stripExtension(`./${relative(dirname(declarationFile), ngModuleFile)}`) :
|
||||
null);
|
||||
const ngModule = getImportString(importManager, importPath, ngModuleName);
|
||||
|
||||
if (info.declaration.type) {
|
||||
const typeName = info.declaration.type && ts.isTypeReferenceNode(info.declaration.type) ?
|
||||
info.declaration.type.typeName :
|
||||
null;
|
||||
if (this.isCoreModuleWithProvidersType(typeName)) {
|
||||
// The declaration already returns `ModuleWithProvider` but it needs the `NgModule` type
|
||||
// parameter adding.
|
||||
outputText.overwrite(
|
||||
info.declaration.type.getStart(), info.declaration.type.getEnd(),
|
||||
`ModuleWithProviders<${ngModule}>`);
|
||||
} else {
|
||||
// The declaration returns an unknown type so we need to convert it to a union that
|
||||
// includes the ngModule property.
|
||||
const originalTypeString = info.declaration.type.getText();
|
||||
outputText.overwrite(
|
||||
info.declaration.type.getStart(), info.declaration.type.getEnd(),
|
||||
`(${originalTypeString})&{ngModule:${ngModule}}`);
|
||||
}
|
||||
} else {
|
||||
// The declaration has no return type so provide one.
|
||||
const lastToken = info.declaration.getLastToken();
|
||||
const insertPoint = lastToken && lastToken.kind === ts.SyntaxKind.SemicolonToken ?
|
||||
lastToken.getStart() :
|
||||
info.declaration.getEnd();
|
||||
outputText.appendLeft(
|
||||
insertPoint,
|
||||
`: ${getImportString(importManager, '@angular/core', 'ModuleWithProviders')}<${ngModule}>`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract addConstants(output: MagicString, constants: string, file: ts.SourceFile):
|
||||
void;
|
||||
protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void;
|
||||
@ -357,67 +302,22 @@ export abstract class Renderer {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected getTypingsFilesToRender(
|
||||
decorationAnalyses: DecorationAnalyses,
|
||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses: ModuleWithProvidersAnalyses|
|
||||
null): Map<ts.SourceFile, DtsRenderInfo> {
|
||||
const dtsMap = new Map<ts.SourceFile, DtsRenderInfo>();
|
||||
|
||||
// Capture the rendering info from the decoration analyses
|
||||
decorationAnalyses.forEach(compiledFile => {
|
||||
protected getTypingsFilesToRender(analyses: DecorationAnalyses):
|
||||
Map<ts.SourceFile, DtsClassInfo[]> {
|
||||
const dtsMap = new Map<ts.SourceFile, DtsClassInfo[]>();
|
||||
analyses.forEach(compiledFile => {
|
||||
compiledFile.compiledClasses.forEach(compiledClass => {
|
||||
const dtsDeclaration = this.host.getDtsDeclaration(compiledClass.declaration);
|
||||
const dtsDeclaration = this.host.getDtsDeclarationOfClass(compiledClass.declaration);
|
||||
if (dtsDeclaration) {
|
||||
const dtsFile = dtsDeclaration.getSourceFile();
|
||||
const renderInfo = dtsMap.get(dtsFile) || new DtsRenderInfo();
|
||||
renderInfo.classInfo.push({dtsDeclaration, compilation: compiledClass.compilation});
|
||||
dtsMap.set(dtsFile, renderInfo);
|
||||
const classes = dtsMap.get(dtsFile) || [];
|
||||
classes.push({dtsDeclaration, compilation: compiledClass.compilation});
|
||||
dtsMap.set(dtsFile, classes);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Capture the ModuleWithProviders functions/methods that need updating
|
||||
if (moduleWithProvidersAnalyses !== null) {
|
||||
moduleWithProvidersAnalyses.forEach((moduleWithProvidersToFix, dtsFile) => {
|
||||
const renderInfo = dtsMap.get(dtsFile) || new DtsRenderInfo();
|
||||
renderInfo.moduleWithProviders = moduleWithProvidersToFix;
|
||||
dtsMap.set(dtsFile, renderInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Capture the private declarations that need to be re-exported
|
||||
if (privateDeclarationsAnalyses.length) {
|
||||
const dtsExports = privateDeclarationsAnalyses.map(e => {
|
||||
if (!e.dtsFrom) {
|
||||
throw new Error(
|
||||
`There is no typings path for ${e.identifier} in ${e.from}.\n` +
|
||||
`We need to add an export for this class to a .d.ts typings file because ` +
|
||||
`Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` +
|
||||
`The simplest fix for this is to ensure that this class is exported from the package's entry-point.`);
|
||||
}
|
||||
return {identifier: e.identifier, from: e.dtsFrom};
|
||||
});
|
||||
const dtsEntryPoint = this.bundle.dts !.file;
|
||||
const renderInfo = dtsMap.get(dtsEntryPoint) || new DtsRenderInfo();
|
||||
renderInfo.privateExports = dtsExports;
|
||||
dtsMap.set(dtsEntryPoint, renderInfo);
|
||||
}
|
||||
|
||||
return dtsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given type is the core Angular `ModuleWithProviders` interface.
|
||||
* @param typeName The type to check.
|
||||
* @returns true if the type is the core Angular `ModuleWithProviders` interface.
|
||||
*/
|
||||
private isCoreModuleWithProvidersType(typeName: ts.EntityName|null) {
|
||||
const id =
|
||||
typeName && ts.isIdentifier(typeName) ? this.host.getImportOfIdentifier(typeName) : null;
|
||||
return (
|
||||
id && id.name === 'ModuleWithProviders' && (this.isCore || id.from === '@angular/core'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,7 +386,7 @@ export function renderDefinitions(
|
||||
}
|
||||
|
||||
export function stripExtension(filePath: string): string {
|
||||
return filePath.replace(/\.(js|d\.ts)$/, '');
|
||||
return filePath.replace(/\.(js|d\.ts$)/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -499,9 +399,3 @@ function createAssignmentStatement(
|
||||
const receiver = new WrappedNodeExpr(receiverName);
|
||||
return new WritePropExpr(receiver, propName, initializer).toStmt();
|
||||
}
|
||||
|
||||
function getImportString(
|
||||
importManager: ImportManager, importPath: string | null, importName: string) {
|
||||
const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null;
|
||||
return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`;
|
||||
}
|
||||
|
@ -1,401 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {BundleProgram} from '../../src/packages/bundle_program';
|
||||
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
const TEST_PROGRAM = [
|
||||
{
|
||||
name: '/src/entry-point.js',
|
||||
contents: `
|
||||
export * from './explicit';
|
||||
export * from './any';
|
||||
export * from './implicit';
|
||||
export * from './no-providers';
|
||||
export * from './module';
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/explicit.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export class ExplicitInternalModule {}
|
||||
export function explicitInternalFunction() {
|
||||
return {
|
||||
ngModule: ExplicitInternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export function explicitExternalFunction() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export function explicitLibraryFunction() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export class ExplicitClass {
|
||||
static explicitInternalMethod() {
|
||||
return {
|
||||
ngModule: ExplicitInternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
static explicitExternalMethod() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
static explicitLibraryMethod() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/any.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export class AnyInternalModule {}
|
||||
export function anyInternalFunction() {
|
||||
return {
|
||||
ngModule: AnyInternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export function anyExternalFunction() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export function anyLibraryFunction() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
export class AnyClass {
|
||||
static anyInternalMethod() {
|
||||
return {
|
||||
ngModule: AnyInternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
static anyExternalMethod() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
static anyLibraryMethod() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: []
|
||||
};
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/implicit.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export class ImplicitInternalModule {}
|
||||
export function implicitInternalFunction() {
|
||||
return {
|
||||
ngModule: ImplicitInternalModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
export function implicitExternalFunction() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
export function implicitLibraryFunction() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
export class ImplicitClass {
|
||||
static implicitInternalMethod() {
|
||||
return {
|
||||
ngModule: ImplicitInternalModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
static implicitExternalMethod() {
|
||||
return {
|
||||
ngModule: ExternalModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
static implicitLibraryMethod() {
|
||||
return {
|
||||
ngModule: LibraryModule,
|
||||
providers: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/no-providers.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export class NoProvidersInternalModule {}
|
||||
export function noProvExplicitInternalFunction() {
|
||||
return {ngModule: NoProvidersInternalModule};
|
||||
}
|
||||
export function noProvExplicitExternalFunction() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
export function noProvExplicitLibraryFunction() {
|
||||
return {ngModule: LibraryModule};
|
||||
}
|
||||
export function noProvAnyInternalFunction() {
|
||||
return {ngModule: NoProvidersInternalModule};
|
||||
}
|
||||
export function noProvAnyExternalFunction() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
export function noProvAnyLibraryFunction() {
|
||||
return {ngModule: LibraryModule};
|
||||
}
|
||||
export function noProvImplicitInternalFunction() {
|
||||
return {ngModule: NoProvidersInternalModule};
|
||||
}
|
||||
export function noProvImplicitExternalFunction() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
export function noProvImplicitLibraryFunction() {
|
||||
return {ngModule: LibraryModule};
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/module.js',
|
||||
contents: `
|
||||
export class ExternalModule {}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/node_modules/some-library/index.d.ts',
|
||||
contents: 'export declare class LibraryModule {}'
|
||||
},
|
||||
];
|
||||
const TEST_DTS_PROGRAM = [
|
||||
{
|
||||
name: '/typings/entry-point.d.ts',
|
||||
contents: `
|
||||
export * from './explicit';
|
||||
export * from './any';
|
||||
export * from './implicit';
|
||||
export * from './no-providers';
|
||||
export * from './module';
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/explicit.d.ts',
|
||||
contents: `
|
||||
import {ModuleWithProviders} from './core';
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export declare class ExplicitInternalModule {}
|
||||
export declare function explicitInternalFunction(): ModuleWithProviders<ExplicitInternalModule>;
|
||||
export declare function explicitExternalFunction(): ModuleWithProviders<ExternalModule>;
|
||||
export declare function explicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
|
||||
export declare class ExplicitClass {
|
||||
static explicitInternalMethod(): ModuleWithProviders<ExplicitInternalModule>;
|
||||
static explicitExternalMethod(): ModuleWithProviders<ExternalModule>;
|
||||
static explicitLibraryMethod(): ModuleWithProviders<LibraryModule>;
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/any.d.ts',
|
||||
contents: `
|
||||
import {ModuleWithProviders} from './core';
|
||||
export declare class AnyInternalModule {}
|
||||
export declare function anyInternalFunction(): ModuleWithProviders<any>;
|
||||
export declare function anyExternalFunction(): ModuleWithProviders<any>;
|
||||
export declare function anyLibraryFunction(): ModuleWithProviders<any>;
|
||||
export declare class AnyClass {
|
||||
static anyInternalMethod(): ModuleWithProviders<any>;
|
||||
static anyExternalMethod(): ModuleWithProviders<any>;
|
||||
static anyLibraryMethod(): ModuleWithProviders<any>;
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/implicit.d.ts',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export declare class ImplicitInternalModule {}
|
||||
export declare function implicitInternalFunction(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
|
||||
export declare function implicitExternalFunction(): { ngModule: typeof ExternalModule; providers: never[]; };
|
||||
export declare function implicitLibraryFunction(): { ngModule: typeof LibraryModule; providers: never[]; };
|
||||
export declare class ImplicitClass {
|
||||
static implicitInternalMethod(): { ngModule: typeof ImplicitInternalModule; providers: never[]; };
|
||||
static implicitExternalMethod(): { ngModule: typeof ExternalModule; providers: never[]; };
|
||||
static implicitLibraryMethod(): { ngModule: typeof LibraryModule; providers: never[]; };
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/no-providers.d.ts',
|
||||
contents: `
|
||||
import {ModuleWithProviders} from './core';
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export declare class NoProvidersInternalModule {}
|
||||
export declare function noProvExplicitInternalFunction(): ModuleWithProviders<NoProvidersInternalModule>;
|
||||
export declare function noProvExplicitExternalFunction(): ModuleWithProviders<ExternalModule>;
|
||||
export declare function noProvExplicitLibraryFunction(): ModuleWithProviders<LibraryModule>;
|
||||
export declare function noProvAnyInternalFunction(): ModuleWithProviders<any>;
|
||||
export declare function noProvAnyExternalFunction(): ModuleWithProviders<any>;
|
||||
export declare function noProvAnyLibraryFunction(): ModuleWithProviders<any>;
|
||||
export declare function noProvImplicitInternalFunction(): { ngModule: typeof NoProvidersInternalModule; };
|
||||
export declare function noProvImplicitExternalFunction(): { ngModule: typeof ExternalModule; };
|
||||
export declare function noProvImplicitLibraryFunction(): { ngModule: typeof LibraryModule; };
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/module.d.ts',
|
||||
contents: `
|
||||
export declare class ExternalModule {}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/typings/core.d.ts',
|
||||
contents: `
|
||||
|
||||
export declare interface Type<T> {
|
||||
new (...args: any[]): T
|
||||
}
|
||||
export declare type Provider = any;
|
||||
export declare interface ModuleWithProviders<T> {
|
||||
ngModule: Type<T>
|
||||
providers?: Provider[]
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/node_modules/some-library/index.d.ts',
|
||||
contents: 'export declare class LibraryModule {}'
|
||||
},
|
||||
];
|
||||
|
||||
describe('ModuleWithProvidersAnalyzer', () => {
|
||||
describe('analyzeProgram()', () => {
|
||||
let analyses: ModuleWithProvidersAnalyses;
|
||||
let program: ts.Program;
|
||||
let dtsProgram: BundleProgram;
|
||||
let referencesRegistry: NgccReferencesRegistry;
|
||||
|
||||
beforeAll(() => {
|
||||
program = makeTestProgram(...TEST_PROGRAM);
|
||||
dtsProgram = makeTestBundleProgram(TEST_DTS_PROGRAM);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dtsProgram);
|
||||
referencesRegistry = new NgccReferencesRegistry(host);
|
||||
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry);
|
||||
analyses = analyzer.analyzeProgram(program);
|
||||
});
|
||||
|
||||
it('should ignore declarations that already have explicit NgModule type params',
|
||||
() => { expect(getAnalysisDescription(analyses, '/typings/explicit.d.ts')).toEqual([]); });
|
||||
|
||||
it('should find declarations that use `any` for the NgModule type param', () => {
|
||||
const anyAnalysis = getAnalysisDescription(analyses, '/typings/any.d.ts');
|
||||
expect(anyAnalysis).toContain(['anyInternalFunction', 'AnyInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyLibraryFunction', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain(['anyInternalMethod', 'AnyInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyExternalMethod', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyLibraryMethod', 'LibraryModule', 'some-library']);
|
||||
});
|
||||
|
||||
it('should track internal module references in the references registry', () => {
|
||||
const declarations = referencesRegistry.getDeclarationMap();
|
||||
const externalModuleDeclaration =
|
||||
getDeclaration(program, '/src/module.js', 'ExternalModule', ts.isClassDeclaration);
|
||||
const libraryModuleDeclaration = getDeclaration(
|
||||
program, '/node_modules/some-library/index.d.ts', 'LibraryModule', ts.isClassDeclaration);
|
||||
expect(declarations.has(externalModuleDeclaration.name !)).toBe(true);
|
||||
expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false);
|
||||
});
|
||||
|
||||
it('should find declarations that have implicit return types', () => {
|
||||
const anyAnalysis = getAnalysisDescription(analyses, '/typings/implicit.d.ts');
|
||||
expect(anyAnalysis).toContain(['implicitInternalFunction', 'ImplicitInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitLibraryFunction', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain(['implicitInternalMethod', 'ImplicitInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitExternalMethod', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitLibraryMethod', 'LibraryModule', 'some-library']);
|
||||
});
|
||||
|
||||
it('should find declarations that do not specify a `providers` property in the return type',
|
||||
() => {
|
||||
const anyAnalysis = getAnalysisDescription(analyses, '/typings/no-providers.d.ts');
|
||||
expect(anyAnalysis).not.toContain([
|
||||
'noProvExplicitInternalFunction', 'NoProvidersInternalModule'
|
||||
]);
|
||||
expect(anyAnalysis).not.toContain([
|
||||
'noProvExplicitExternalFunction', 'ExternalModule', null
|
||||
]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'noProvAnyInternalFunction', 'NoProvidersInternalModule', null
|
||||
]);
|
||||
expect(anyAnalysis).toContain(['noProvAnyExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'noProvAnyLibraryFunction', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'noProvImplicitInternalFunction', 'NoProvidersInternalModule', null
|
||||
]);
|
||||
expect(anyAnalysis).toContain(['noProvImplicitExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'noProvImplicitLibraryFunction', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
});
|
||||
|
||||
function getAnalysisDescription(analyses: ModuleWithProvidersAnalyses, fileName: string) {
|
||||
const file = dtsProgram.program.getSourceFile(fileName) !;
|
||||
const analysis = analyses.get(file);
|
||||
return analysis ?
|
||||
analysis.map(
|
||||
info =>
|
||||
[info.declaration.name !.getText(),
|
||||
(info.ngModule.node as ts.ClassDeclaration).name !.getText(),
|
||||
info.ngModule.viaModule]) :
|
||||
[];
|
||||
}
|
||||
});
|
||||
});
|
@ -84,8 +84,6 @@ export function getFakeCore() {
|
||||
export class InjectionToken {
|
||||
constructor(name: string) {}
|
||||
}
|
||||
|
||||
export interface ModuleWithProviders<T = any> {}
|
||||
`
|
||||
};
|
||||
}
|
||||
|
@ -453,7 +453,6 @@ const TYPINGS_SRC_FILES = [
|
||||
},
|
||||
{name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'},
|
||||
{name: '/src/class2.js', contents: 'export class Class2 {}'},
|
||||
{name: '/src/func1.js', contents: 'export function mooFn() {}'},
|
||||
{name: '/src/internal.js', contents: 'export class InternalClass {}\nexport class Class2 {}'},
|
||||
{name: '/src/missing-class.js', contents: 'export class MissingClass2 {}'}, {
|
||||
name: '/src/flat-file.js',
|
||||
@ -477,7 +476,6 @@ const TYPINGS_DTS_FILES = [
|
||||
contents:
|
||||
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
|
||||
},
|
||||
{name: '/typings/func1.d.ts', contents: 'export declare function mooFn(): void;'},
|
||||
{
|
||||
name: '/typings/internal.d.ts',
|
||||
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
|
||||
@ -485,54 +483,6 @@ const TYPINGS_DTS_FILES = [
|
||||
{name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
|
||||
];
|
||||
|
||||
const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||
{
|
||||
name: '/src/functions.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
export class SomeService {}
|
||||
export class InternalModule {}
|
||||
export function aNumber() { return 42; }
|
||||
export function aString() { return 'foo'; }
|
||||
export function emptyObject() { return {}; }
|
||||
export function ngModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||
export function ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||
export function ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||
export function onlyProviders() { return { providers: [SomeService] }; }
|
||||
export function ngModuleNumber() { return { ngModule: 42 }; }
|
||||
export function ngModuleString() { return { ngModule: 'foo' }; }
|
||||
export function ngModuleObject() { return { ngModule: { foo: 42 } }; }
|
||||
export function externalNgModule() { return { ngModule: ExternalModule }; }
|
||||
`
|
||||
},
|
||||
{
|
||||
name: '/src/methods.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
export class SomeService {}
|
||||
export class InternalModule {
|
||||
static aNumber() { return 42; }
|
||||
static aString() { return 'foo'; }
|
||||
static emptyObject() { return {}; }
|
||||
static ngModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||
static ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||
static ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||
static onlyProviders() { return { providers: [SomeService] }; }
|
||||
static ngModuleNumber() { return { ngModule: 42 }; }
|
||||
static ngModuleString() { return { ngModule: 'foo' }; }
|
||||
static ngModuleObject() { return { ngModule: { foo: 42 } }; }
|
||||
static externalNgModule() { return { ngModule: ExternalModule }; }
|
||||
|
||||
instanceNgModuleIdentifier() { return { ngModule: InternalModule }; }
|
||||
instanceNgModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
|
||||
instanceNgModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
|
||||
instanceExternalNgModule() { return { ngModule: ExternalModule }; }
|
||||
}
|
||||
`
|
||||
},
|
||||
{name: '/src/module', contents: 'export class ExternalModule {}'},
|
||||
];
|
||||
|
||||
describe('Fesm2015ReflectionHost', () => {
|
||||
|
||||
describe('getDecoratorsOfDeclaration()', () => {
|
||||
@ -1336,20 +1286,10 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||
});
|
||||
|
||||
it('should find the dts declaration for exported functions', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(mooFn);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/func1.d.ts');
|
||||
});
|
||||
|
||||
it('should return null if there is no matching class in the matching dts file', () => {
|
||||
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
|
||||
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
|
||||
@ -1357,7 +1297,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null if there is no matching dts file', () => {
|
||||
@ -1367,7 +1307,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
expect(host.getDtsDeclaration(missingClass)).toBe(null);
|
||||
expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null);
|
||||
});
|
||||
|
||||
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
|
||||
@ -1378,7 +1318,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class1);
|
||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
|
||||
});
|
||||
|
||||
@ -1389,7 +1329,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(class3);
|
||||
const dtsDeclaration = host.getDtsDeclarationOfClass(class3);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
|
||||
});
|
||||
|
||||
@ -1401,7 +1341,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const dtsDeclaration = host.getDtsDeclaration(internalClass);
|
||||
const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass);
|
||||
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
|
||||
});
|
||||
|
||||
@ -1415,42 +1355,12 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
|
||||
|
||||
const class2DtsDeclaration = host.getDtsDeclaration(class2);
|
||||
const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2);
|
||||
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
|
||||
|
||||
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
|
||||
const internalClass2DtsDeclaration = host.getDtsDeclarationOfClass(internalClass2);
|
||||
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
|
||||
.toEqual('/typings/class2.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModuleWithProvidersFunctions', () => {
|
||||
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker());
|
||||
const file = srcProgram.getSourceFile('/src/functions.js') !;
|
||||
const fns = host.getModuleWithProvidersFunctions(file);
|
||||
expect(fns.map(info => [info.declaration.name !.getText(), info.ngModule.text])).toEqual([
|
||||
['ngModuleIdentifier', 'InternalModule'],
|
||||
['ngModuleWithEmptyProviders', 'InternalModule'],
|
||||
['ngModuleWithProviders', 'InternalModule'],
|
||||
['externalNgModule', 'ExternalModule'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find every static method on exported classes that return an object that looks like a ModuleWithProviders object',
|
||||
() => {
|
||||
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
|
||||
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker());
|
||||
const file = srcProgram.getSourceFile('/src/methods.js') !;
|
||||
const fn = host.getModuleWithProvidersFunctions(file);
|
||||
expect(fn.map(fn => [fn.declaration.name !.getText(), fn.ngModule.text])).toEqual([
|
||||
['ngModuleIdentifier', 'InternalModule'],
|
||||
['ngModuleWithEmptyProviders', 'InternalModule'],
|
||||
['ngModuleWithProviders', 'InternalModule'],
|
||||
['externalNgModule', 'ExternalModule'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -11,7 +11,6 @@ import * as ts from 'typescript';
|
||||
import {fromObject, generateMapFileComment} from 'convert-source-map';
|
||||
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
@ -48,9 +47,9 @@ class TestRenderer extends Renderer {
|
||||
|
||||
function createTestRenderer(
|
||||
packageName: string, files: {name: string, contents: string}[],
|
||||
dtsFiles?: {name: string, contents: string}[]) {
|
||||
dtsFile?: {name: string, contents: string}) {
|
||||
const isCore = packageName === '@angular/core';
|
||||
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFiles);
|
||||
const bundle = makeTestEntryPointBundle('esm2015', files, dtsFile && [dtsFile]);
|
||||
const typeChecker = bundle.src.program.getTypeChecker();
|
||||
const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
|
||||
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||
@ -58,8 +57,6 @@ function createTestRenderer(
|
||||
new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
|
||||
.analyzeProgram(bundle.src.program);
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||
const moduleWithProvidersAnalyses =
|
||||
new ModuleWithProvidersAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||
const privateDeclarationsAnalyses =
|
||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||
const renderer = new TestRenderer(host, isCore, bundle);
|
||||
@ -67,8 +64,7 @@ function createTestRenderer(
|
||||
spyOn(renderer, 'addDefinitions').and.callThrough();
|
||||
spyOn(renderer, 'removeDecorators').and.callThrough();
|
||||
|
||||
return {renderer, decorationAnalyses, switchMarkerAnalyses, moduleWithProvidersAnalyses,
|
||||
privateDeclarationsAnalyses};
|
||||
return {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses};
|
||||
}
|
||||
|
||||
|
||||
@ -125,11 +121,10 @@ describe('Renderer', () => {
|
||||
describe('renderProgram()', () => {
|
||||
it('should render the modified contents; and a new map file, if the original provided no map file.',
|
||||
() => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
||||
@ -139,11 +134,9 @@ describe('Renderer', () => {
|
||||
|
||||
|
||||
it('should render as JavaScript', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [COMPONENT_PROGRAM]);
|
||||
renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [COMPONENT_PROGRAM]);
|
||||
renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{
|
||||
@ -161,12 +154,10 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
|
||||
describe('calling abstract methods', () => {
|
||||
it('should call addImports with the source code and info about the core Angular library.',
|
||||
() => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
expect(addImportsSpy.calls.first().args[1]).toEqual([
|
||||
@ -176,12 +167,10 @@ A.ngComponentDef = ɵngcc0.ɵdefineComponent({ type: A, selectors: [["a"]], fact
|
||||
|
||||
it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.',
|
||||
() => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({
|
||||
@ -198,12 +187,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
|
||||
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
|
||||
() => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
|
||||
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
|
||||
@ -225,16 +212,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
describe('source map merging', () => {
|
||||
it('should merge any inline source map from the original file and write the output as an inline source map',
|
||||
() => {
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer(
|
||||
'test-package', [{
|
||||
...INPUT_PROGRAM,
|
||||
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
|
||||
}]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
.toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment());
|
||||
@ -245,16 +230,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
() => {
|
||||
// Mock out reading the map file from disk
|
||||
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer(
|
||||
'test-package', [{
|
||||
...INPUT_PROGRAM,
|
||||
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
|
||||
}]);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map'));
|
||||
@ -276,12 +259,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
contents: `export const NgModule = () => null;`
|
||||
};
|
||||
// The package name of `@angular/core` indicates that we are compiling the core library.
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]);
|
||||
renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
|
||||
@ -296,11 +277,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
|
||||
};
|
||||
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} = createTestRenderer('@angular/core', [CORE_FILE]);
|
||||
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('@angular/core', [CORE_FILE]);
|
||||
renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toContain(`/*@__PURE__*/ setClassMetadata(`);
|
||||
@ -311,12 +291,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
|
||||
describe('rendering typings', () => {
|
||||
it('should render extract types into typings files', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||
expect(typingsFile.contents)
|
||||
@ -325,195 +303,30 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
});
|
||||
|
||||
it('should render imports into typings files', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||
expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`);
|
||||
});
|
||||
|
||||
it('should render exports into typings files', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} =
|
||||
createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM);
|
||||
|
||||
// Add a mock export to trigger export rendering
|
||||
privateDeclarationsAnalyses.push(
|
||||
{identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'});
|
||||
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||
expect(typingsFile.contents)
|
||||
.toContain(`// ADD EXPORTS\n\n// ADD IMPORTS\nexport declare class A`);
|
||||
});
|
||||
|
||||
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
||||
const MODULE_WITH_PROVIDERS_PROGRAM = [
|
||||
{
|
||||
name: '/src/index.js',
|
||||
contents: `
|
||||
import {ExternalModule} from './module';
|
||||
import {LibraryModule} from 'some-library';
|
||||
export class SomeClass {}
|
||||
export class SomeModule {
|
||||
static withProviders1() {
|
||||
return {ngModule: SomeModule};
|
||||
}
|
||||
static withProviders2() {
|
||||
return {ngModule: SomeModule};
|
||||
}
|
||||
static withProviders3() {
|
||||
return {ngModule: SomeClass};
|
||||
}
|
||||
static withProviders4() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
static withProviders5() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
static withProviders6() {
|
||||
return {ngModule: LibraryModule};
|
||||
}
|
||||
static withProviders7() {
|
||||
return {ngModule: SomeModule, providers: []};
|
||||
};
|
||||
static withProviders8() {
|
||||
return {ngModule: SomeModule};
|
||||
}
|
||||
}
|
||||
export function withProviders1() {
|
||||
return {ngModule: SomeModule};
|
||||
}
|
||||
export function withProviders2() {
|
||||
return {ngModule: SomeModule};
|
||||
}
|
||||
export function withProviders3() {
|
||||
return {ngModule: SomeClass};
|
||||
}
|
||||
export function withProviders4() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
export function withProviders5() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
export function withProviders6() {
|
||||
return {ngModule: LibraryModule};
|
||||
}
|
||||
export function withProviders7() {
|
||||
return {ngModule: SomeModule, providers: []};
|
||||
};
|
||||
export function withProviders8() {
|
||||
return {ngModule: SomeModule};
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: '/src/module.js',
|
||||
contents: `
|
||||
export class ExternalModule {
|
||||
static withProviders1() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
static withProviders2() {
|
||||
return {ngModule: ExternalModule};
|
||||
}
|
||||
}`
|
||||
},
|
||||
{
|
||||
name: '/node_modules/some-library/index.d.ts',
|
||||
contents: 'export declare class LibraryModule {}'
|
||||
},
|
||||
];
|
||||
const MODULE_WITH_PROVIDERS_DTS_PROGRAM = [
|
||||
{
|
||||
name: '/typings/index.d.ts',
|
||||
contents: `
|
||||
import {ModuleWithProviders} from '@angular/core';
|
||||
export declare class SomeClass {}
|
||||
export interface MyModuleWithProviders extends ModuleWithProviders {}
|
||||
export declare class SomeModule {
|
||||
static withProviders1(): ModuleWithProviders;
|
||||
static withProviders2(): ModuleWithProviders<any>;
|
||||
static withProviders3(): ModuleWithProviders<SomeClass>;
|
||||
static withProviders4(): ModuleWithProviders;
|
||||
static withProviders5();
|
||||
static withProviders6(): ModuleWithProviders;
|
||||
static withProviders7(): {ngModule: SomeModule, providers: any[]};
|
||||
static withProviders8(): MyModuleWithProviders;
|
||||
}
|
||||
export declare function withProviders1(): ModuleWithProviders;
|
||||
export declare function withProviders2(): ModuleWithProviders<any>;
|
||||
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
||||
export declare function withProviders4(): ModuleWithProviders;
|
||||
export declare function withProviders5();
|
||||
export declare function withProviders6(): ModuleWithProviders;
|
||||
export declare function withProviders7(): {ngModule: SomeModule, providers: any[]};
|
||||
export declare function withProviders8(): MyModuleWithProviders;`
|
||||
},
|
||||
{
|
||||
name: '/typings/module.d.ts',
|
||||
contents: `
|
||||
export interface ModuleWithProviders {}
|
||||
export declare class ExternalModule {
|
||||
static withProviders1(): ModuleWithProviders;
|
||||
static withProviders2(): ModuleWithProviders;
|
||||
}`
|
||||
},
|
||||
{
|
||||
name: '/node_modules/some-library/index.d.ts',
|
||||
contents: 'export declare class LibraryModule {}'
|
||||
},
|
||||
];
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses} =
|
||||
createTestRenderer(
|
||||
'test-package', MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
||||
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
moduleWithProvidersAnalyses);
|
||||
|
||||
const typingsFile = result.find(f => f.path === '/typings/index.d.ts') !;
|
||||
|
||||
expect(typingsFile.contents).toContain(`
|
||||
static withProviders1(): ModuleWithProviders<SomeModule>;
|
||||
static withProviders2(): ModuleWithProviders<SomeModule>;
|
||||
static withProviders3(): ModuleWithProviders<SomeClass>;
|
||||
static withProviders4(): ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||
static withProviders5(): ɵngcc1.ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||
static withProviders6(): ModuleWithProviders<ɵngcc2.LibraryModule>;
|
||||
static withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
||||
static withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
||||
expect(typingsFile.contents).toContain(`
|
||||
export declare function withProviders1(): ModuleWithProviders<SomeModule>;
|
||||
export declare function withProviders2(): ModuleWithProviders<SomeModule>;
|
||||
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
||||
export declare function withProviders4(): ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||
export declare function withProviders5(): ɵngcc1.ModuleWithProviders<ɵngcc0.ExternalModule>;
|
||||
export declare function withProviders6(): ModuleWithProviders<ɵngcc2.LibraryModule>;
|
||||
export declare function withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
||||
export declare function withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
||||
|
||||
expect(renderer.addImports).toHaveBeenCalledWith(jasmine.any(MagicString), [
|
||||
{name: './module', as: 'ɵngcc0'},
|
||||
{name: '@angular/core', as: 'ɵngcc1'},
|
||||
{name: 'some-library', as: 'ɵngcc2'},
|
||||
]);
|
||||
|
||||
|
||||
// The following expectation checks that we do not mistake `ModuleWithProviders` types
|
||||
// that are not imported from `@angular/core`.
|
||||
const typingsFile2 = result.find(f => f.path === '/typings/module.d.ts') !;
|
||||
expect(typingsFile2.contents).toContain(`
|
||||
static withProviders1(): (ModuleWithProviders)&{ngModule:ExternalModule};
|
||||
static withProviders2(): (ModuleWithProviders)&{ngModule:ExternalModule};`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -434,7 +434,7 @@ function extractHostBindings(
|
||||
});
|
||||
}
|
||||
|
||||
const {attributes, listeners, properties} = parseHostBindings(hostMetadata);
|
||||
const {attributes, listeners, properties, animations} = parseHostBindings(hostMetadata);
|
||||
|
||||
filterToMembersWithDecorator(members, 'HostBinding', coreModule)
|
||||
.forEach(({member, decorators}) => {
|
||||
|
@ -109,7 +109,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
||||
const valueContext = node.getSourceFile();
|
||||
|
||||
let typeContext = valueContext;
|
||||
const typeNode = this.reflector.getDtsDeclaration(node);
|
||||
const typeNode = this.reflector.getDtsDeclarationOfClass(node);
|
||||
if (typeNode !== null) {
|
||||
typeContext = typeNode.getSourceFile();
|
||||
}
|
||||
@ -183,8 +183,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
||||
return toR3Reference(valueRef, valueRef, valueContext, valueContext);
|
||||
} else {
|
||||
let typeRef = valueRef;
|
||||
let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
|
||||
if (typeNode !== null && ts.isClassDeclaration(typeNode)) {
|
||||
let typeNode = this.reflector.getDtsDeclarationOfClass(typeRef.node);
|
||||
if (typeNode !== null) {
|
||||
typeRef = new ResolvedReference(typeNode, typeNode.name !);
|
||||
}
|
||||
return toR3Reference(valueRef, typeRef, valueContext, typeContext);
|
||||
@ -197,20 +197,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
||||
*/
|
||||
private _extractModuleFromModuleWithProvidersFn(node: ts.FunctionDeclaration|
|
||||
ts.MethodDeclaration): ts.Expression|null {
|
||||
const type = node.type || null;
|
||||
return type &&
|
||||
(this._reflectModuleFromTypeParam(type) || this._reflectModuleFromLiteralType(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
|
||||
* `ModuleWithProviders<T>`
|
||||
* @param type The type to reflect on.
|
||||
* @returns the identifier of the NgModule type if found, or null otherwise.
|
||||
*/
|
||||
private _reflectModuleFromTypeParam(type: ts.TypeNode): ts.Expression|null {
|
||||
const type = node.type;
|
||||
// Examine the type of the function to see if it's a ModuleWithProviders reference.
|
||||
if (!ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
|
||||
if (type === undefined || !ts.isTypeReferenceNode(type) || !ts.isIdentifier(type.typeName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -237,32 +226,6 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
||||
return typeNodeToValueExpr(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
|
||||
* `A|B|{ngModule: T}|C`.
|
||||
* @param type The type to reflect on.
|
||||
* @returns the identifier of the NgModule type if found, or null otherwise.
|
||||
*/
|
||||
private _reflectModuleFromLiteralType(type: ts.TypeNode): ts.Expression|null {
|
||||
if (!ts.isIntersectionTypeNode(type)) {
|
||||
return null;
|
||||
}
|
||||
for (const t of type.types) {
|
||||
if (ts.isTypeLiteralNode(t)) {
|
||||
for (const m of t.members) {
|
||||
const ngModuleType = ts.isPropertySignature(m) && ts.isIdentifier(m.name) &&
|
||||
m.name.text === 'ngModule' && m.type ||
|
||||
null;
|
||||
const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
|
||||
if (ngModuleExpression) {
|
||||
return ngModuleExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a list of `Reference`s from a resolved metadata value.
|
||||
*/
|
||||
|
@ -448,7 +448,7 @@ export interface ReflectionHost {
|
||||
getVariableValue(declaration: ts.VariableDeclaration): ts.Expression|null;
|
||||
|
||||
/**
|
||||
* Take an exported declaration (maybe a class down-leveled to a variable) and look up the
|
||||
* Take an exported declaration of a class (maybe downleveled to a variable) and look up the
|
||||
* declaration of its type in a separate .d.ts tree.
|
||||
*
|
||||
* This function is allowed to return `null` if the current compilation unit does not have a
|
||||
@ -456,8 +456,8 @@ export interface ReflectionHost {
|
||||
* are produced only during the emit of such a compilation. When compiling .js code, however,
|
||||
* there is frequently a parallel .d.ts tree which this method exposes.
|
||||
*
|
||||
* Note that the `ts.Declaration` returned from this function may not be from the same
|
||||
* Note that the `ts.ClassDeclaration` returned from this function may not be from the same
|
||||
* `ts.Program` as the input declaration.
|
||||
*/
|
||||
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null;
|
||||
getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
return declaration.initializer || null;
|
||||
}
|
||||
|
||||
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; }
|
||||
getDtsDeclarationOfClass(_: ts.Declaration): ts.ClassDeclaration|null { return null; }
|
||||
|
||||
/**
|
||||
* Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
|
||||
|
@ -208,21 +208,14 @@ export class NgtscProgram implements api.Program {
|
||||
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
};
|
||||
|
||||
const customTransforms = opts && opts.customTransformers;
|
||||
const beforeTransforms =
|
||||
const transforms =
|
||||
[ivyTransformFactory(this.compilation !, this.reflector, this.coreImportsFrom)];
|
||||
|
||||
if (this.factoryToSourceInfo !== null) {
|
||||
beforeTransforms.push(
|
||||
generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
|
||||
transforms.push(generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
|
||||
}
|
||||
if (this.isCore) {
|
||||
beforeTransforms.push(ivySwitchTransform);
|
||||
transforms.push(ivySwitchTransform);
|
||||
}
|
||||
if (customTransforms && customTransforms.beforeTs) {
|
||||
beforeTransforms.push(...customTransforms.beforeTs);
|
||||
}
|
||||
|
||||
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
|
||||
const emitResult = emitCallback({
|
||||
program: this.tsProgram,
|
||||
@ -230,8 +223,7 @@ export class NgtscProgram implements api.Program {
|
||||
options: this.options,
|
||||
emitOnlyDtsFiles: false, writeFile,
|
||||
customTransformers: {
|
||||
before: beforeTransforms,
|
||||
after: customTransforms && customTransforms.afterTs,
|
||||
before: transforms,
|
||||
},
|
||||
});
|
||||
return emitResult;
|
||||
|
@ -246,7 +246,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
if (packageTypings === originalImportedFile) {
|
||||
moduleName = importedFilePackageName;
|
||||
}
|
||||
} catch {
|
||||
} catch (e) {
|
||||
// the above require() will throw if there is no package.json file
|
||||
// and this is safe to ignore and correct to keep the longer
|
||||
// moduleName in this case
|
||||
@ -582,7 +582,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} catch (e) {
|
||||
// If we encounter any errors assume we this isn't a bundle index.
|
||||
result = false;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AttributeMarker} from '@angular/compiler/src/core';
|
||||
import {AttributeMarker, InitialStylingFlags} from '@angular/compiler/src/core';
|
||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||
import {compile, expectEmit} from './mock_compile';
|
||||
|
||||
@ -48,15 +48,17 @@ describe('compiler compliance', () => {
|
||||
|
||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||
const template = `
|
||||
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||
const $c2$ = ["cx", "20", "cy", "30", "r", "50"];
|
||||
const $c1$ = ["title", "Hello"];
|
||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
||||
const $c3$ = ["cx", "20", "cy", "30", "r", "50"];
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $c1$);
|
||||
$r3$.ɵelementStyling($c2$);
|
||||
$r3$.ɵnamespaceSVG();
|
||||
$r3$.ɵelementStart(1, "svg");
|
||||
$r3$.ɵelement(2, "circle", $c2$);
|
||||
$r3$.ɵelement(2, "circle", $c3$);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵnamespaceHTML();
|
||||
$r3$.ɵelementStart(3, "p");
|
||||
@ -98,11 +100,13 @@ describe('compiler compliance', () => {
|
||||
|
||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||
const template = `
|
||||
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||
const $c1$ = ["title", "Hello"];
|
||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $c1$);
|
||||
$r3$.ɵelementStyling($c2$);
|
||||
$r3$.ɵnamespaceMathML();
|
||||
$r3$.ɵelementStart(1, "math");
|
||||
$r3$.ɵelement(2, "infinity");
|
||||
@ -146,11 +150,13 @@ describe('compiler compliance', () => {
|
||||
|
||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||
const template = `
|
||||
const $c1$ = ["title", "Hello", ${AttributeMarker.Classes}, "my-app"];
|
||||
const $c1$ = ["title", "Hello"];
|
||||
const $c2$ = ["my-app", ${InitialStylingFlags.VALUES_MODE}, "my-app", true];
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $c1$);
|
||||
$r3$.ɵelementStyling($c2$);
|
||||
$r3$.ɵtext(1, "Hello ");
|
||||
$r3$.ɵelementStart(2, "b");
|
||||
$r3$.ɵtext(3, "World");
|
||||
@ -434,14 +440,14 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵallocHostVars(14);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵcomponentHostSyntheticProperty(elIndex, "@expansionHeight",
|
||||
$r3$.ɵelementProperty(elIndex, "expansionHeight",
|
||||
$r3$.ɵbind(
|
||||
$r3$.ɵpureFunction2(5, $_c1$, ctx.getExpandedState(),
|
||||
$r3$.ɵpureFunction2(2, $_c0$, ctx.collapsedHeight, ctx.expandedHeight)
|
||||
)
|
||||
), null, true
|
||||
);
|
||||
$r3$.ɵcomponentHostSyntheticProperty(elIndex, "@expansionWidth",
|
||||
$r3$.ɵelementProperty(elIndex, "expansionWidth",
|
||||
$r3$.ɵbind(
|
||||
$r3$.ɵpureFunction2(11, $_c1$, ctx.getExpandedState(),
|
||||
$r3$.ɵpureFunction2(8, $_c2$, ctx.collapsedWidth, ctx.expandedWidth)
|
||||
@ -480,8 +486,8 @@ describe('compiler compliance', () => {
|
||||
const factory =
|
||||
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
|
||||
const template = `
|
||||
const $e0_classBindings$ = ["error"];
|
||||
const $e0_styleBindings$ = ["background-color"];
|
||||
const _c0 = ["error"];
|
||||
const _c1 = ["background-color"];
|
||||
…
|
||||
MyComponent.ngComponentDef = i0.ɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
|
||||
factory: function MyComponent_Factory(t){
|
||||
@ -492,7 +498,7 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(rf,ctx){
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$);
|
||||
$r3$.ɵelementStyling(_c0, _c1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -767,7 +773,7 @@ describe('compiler compliance', () => {
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = ["foo", ""];
|
||||
const $c2$ = ["if", ""];
|
||||
function MyComponent_li_2_Template(rf, ctx) {
|
||||
function MyComponent_li_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "li");
|
||||
$r3$.ɵtext(1);
|
||||
@ -789,7 +795,7 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "ul", null, $c1$);
|
||||
$r3$.ɵtemplate(2, MyComponent_li_2_Template, 2, 2, "li", $c2$);
|
||||
$r3$.ɵtemplate(2, MyComponent_li_Template_2, 2, 2, "li", $c2$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
@ -1086,224 +1092,156 @@ describe('compiler compliance', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('content projection', () => {
|
||||
it('should support content projection in root template', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
||||
|
||||
it('should support content projection in root template', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
||||
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
|
||||
export class SimpleComponent {}
|
||||
|
||||
@Component({selector: 'simple', template: '<div><ng-content></ng-content></div>'})
|
||||
export class SimpleComponent {}
|
||||
|
||||
@Component({
|
||||
selector: 'complex',
|
||||
template: \`
|
||||
<div id="first"><ng-content select="span[title=toFirst]"></ng-content></div>
|
||||
<div id="second"><ng-content SELECT="span[title=toSecond]"></ng-content></div>\`
|
||||
})
|
||||
export class ComplexComponent { }
|
||||
|
||||
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
|
||||
export class MyModule {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<simple>content</simple> <complex></complex>'
|
||||
@Component({
|
||||
selector: 'complex',
|
||||
template: \`
|
||||
<div id="first"><ng-content select="span[title=toFirst]"></ng-content></div>
|
||||
<div id="second"><ng-content SELECT="span[title=toSecond]"></ng-content></div>\`
|
||||
})
|
||||
export class MyApp {}
|
||||
`
|
||||
}
|
||||
};
|
||||
export class ComplexComponent { }
|
||||
|
||||
const SimpleComponentDefinition = `
|
||||
SimpleComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: SimpleComponent,
|
||||
selectors: [["simple"]],
|
||||
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function SimpleComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef();
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵprojection(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
});`;
|
||||
@NgModule({declarations: [SimpleComponent, ComplexComponent]})
|
||||
export class MyModule {}
|
||||
|
||||
const ComplexComponentDefinition = `
|
||||
const $c3$ = ["id","first"];
|
||||
const $c4$ = ["id","second"];
|
||||
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
|
||||
const $c2$ = ["span[title=toFirst]", "span[title=toSecond]"];
|
||||
…
|
||||
ComplexComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ComplexComponent,
|
||||
selectors: [["complex"]],
|
||||
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function ComplexComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($c1$, $c2$);
|
||||
$r3$.ɵelementStart(0, "div", $c3$);
|
||||
$r3$.ɵprojection(1, 1);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(2, "div", $c4$);
|
||||
$r3$.ɵprojection(3, 2);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
});
|
||||
`;
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<simple>content</simple> <complex></complex>'
|
||||
})
|
||||
export class MyApp {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
const SimpleComponentDefinition = `
|
||||
SimpleComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: SimpleComponent,
|
||||
selectors: [["simple"]],
|
||||
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function SimpleComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef();
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵprojection(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
});`;
|
||||
|
||||
expectEmit(
|
||||
result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
|
||||
expectEmit(
|
||||
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
||||
});
|
||||
|
||||
it('should support content projection in nested templates', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: \`
|
||||
<div id="second" *ngIf="visible">
|
||||
<ng-content SELECT="span[title=toFirst]"></ng-content>
|
||||
</div>
|
||||
<div id="third" *ngIf="visible">
|
||||
No ng-content, no instructions generated.
|
||||
</div>
|
||||
<ng-template>
|
||||
'*' selector: <ng-content></ng-content>
|
||||
</ng-template>
|
||||
\`,
|
||||
})
|
||||
class Cmp {}
|
||||
|
||||
@NgModule({ declarations: [Cmp] })
|
||||
class Module {}
|
||||
`
|
||||
}
|
||||
};
|
||||
const output = `
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c1$ = ["id", "second"];
|
||||
function Cmp_div_0_Template(rf, ctx) { if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c1$);
|
||||
const ComplexComponentDefinition = `
|
||||
const $c3$ = ["id","first"];
|
||||
const $c4$ = ["id","second"];
|
||||
const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]];
|
||||
const $c2$ = ["span[title=toFirst]", "span[title=toSecond]"];
|
||||
…
|
||||
ComplexComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ComplexComponent,
|
||||
selectors: [["complex"]],
|
||||
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function ComplexComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($c1$, $c2$);
|
||||
$r3$.ɵelementStart(0, "div", $c3$);
|
||||
$r3$.ɵprojection(1, 1);
|
||||
$r3$.ɵelementEnd();
|
||||
} }
|
||||
const $_c4$ = ["id", "third"];
|
||||
function Cmp_div_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c4$);
|
||||
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
|
||||
$r3$.ɵelementStart(2, "div", $c4$);
|
||||
$r3$.ɵprojection(3, 2);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
function Cmp_ng_template_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0, " '*' selector: ");
|
||||
$r3$.ɵprojection(1);
|
||||
}
|
||||
}
|
||||
const $_c2$ = [[["span", "title", "tofirst"]]];
|
||||
const $_c3$ = ["span[title=toFirst]"];
|
||||
…
|
||||
template: function Cmp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
||||
$r3$.ɵtemplate(0, Cmp_div_0_Template, 2, 0, "div", $_c0$);
|
||||
$r3$.ɵtemplate(1, Cmp_div_1_Template, 2, 0, "div", $_c0$);
|
||||
$r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
}
|
||||
}
|
||||
`;
|
||||
},
|
||||
encapsulation: 2
|
||||
});
|
||||
`;
|
||||
|
||||
const {source} = compile(files, angularFiles);
|
||||
expectEmit(source, output, 'Invalid content projection instructions generated');
|
||||
});
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
|
||||
it('should support content projection in both the root and nested templates', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
expectEmit(result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
|
||||
expectEmit(
|
||||
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
||||
});
|
||||
|
||||
@Component({
|
||||
template: \`
|
||||
<ng-content select="[id=toMainBefore]"></ng-content>
|
||||
<ng-template>
|
||||
<ng-content select="[id=toTemplate]"></ng-content>
|
||||
<ng-template>
|
||||
<ng-content select="[id=toNestedTemplate]"></ng-content>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template>
|
||||
'*' selector in a template: <ng-content></ng-content>
|
||||
</ng-template>
|
||||
<ng-content select="[id=toMainAfter]"></ng-content>
|
||||
\`,
|
||||
})
|
||||
class Cmp {}
|
||||
it('should support content projection in nested templates', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@NgModule({ declarations: [Cmp] })
|
||||
class Module {}
|
||||
`
|
||||
}
|
||||
};
|
||||
@Component({
|
||||
template: \`
|
||||
<div id="second" *ngIf="visible">
|
||||
<ng-content SELECT="span[title=toFirst]"></ng-content>
|
||||
</div>
|
||||
<div id="third" *ngIf="visible">
|
||||
No ng-content, no instructions generated.
|
||||
</div>
|
||||
<ng-template>
|
||||
'*' selector: <ng-content></ng-content>
|
||||
</ng-template>
|
||||
\`,
|
||||
})
|
||||
class Cmp {}
|
||||
|
||||
const output = `
|
||||
function Cmp_ng_template_1_ng_template_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojection(0, 4);
|
||||
}
|
||||
@NgModule({ declarations: [Cmp] })
|
||||
class Module {}
|
||||
`
|
||||
}
|
||||
};
|
||||
const output = `
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $_c1$ = ["id", "second"];
|
||||
const $_c2$ = [[["span", "title", "tofirst"]]];
|
||||
const $_c3$ = ["span[title=toFirst]"];
|
||||
function Cmp_div_Template_0(rf, ctx) { if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
||||
$r3$.ɵelementStart(0, "div", $_c1$);
|
||||
$r3$.ɵprojection(1, 1);
|
||||
$r3$.ɵelementEnd();
|
||||
} }
|
||||
const $_c4$ = ["id", "third"];
|
||||
function Cmp_div_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c4$);
|
||||
$r3$.ɵtext(1, " No ng-content, no instructions generated. ");
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
function Cmp_ng_template_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojection(0, 3);
|
||||
$r3$.ɵtemplate(1, Cmp_ng_template_1_ng_template_1_Template, 1, 0, "ng-template");
|
||||
}
|
||||
}
|
||||
function Cmp_ng_template_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef();
|
||||
$r3$.ɵtext(0, " '*' selector: ");
|
||||
$r3$.ɵprojection(1);
|
||||
}
|
||||
function Cmp_ng_template_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0, " '*' selector in a template: ");
|
||||
$r3$.ɵprojection(1);
|
||||
}
|
||||
}
|
||||
…
|
||||
template: function Cmp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, Cmp_div_Template_0, 2, 0, "div", $_c0$);
|
||||
$r3$.ɵtemplate(1, Cmp_div_Template_1, 2, 0, "div", $_c0$);
|
||||
$r3$.ɵtemplate(2, Cmp_ng_template_Template_2, 2, 0, "ng-template");
|
||||
}
|
||||
const $_c0$ = [[["", "id", "tomainbefore"]], [["", "id", "tomainafter"]], [["", "id", "totemplate"]], [["", "id", "tonestedtemplate"]]];
|
||||
const $_c1$ = ["[id=toMainBefore]", "[id=toMainAfter]", "[id=toTemplate]", "[id=toNestedTemplate]"];
|
||||
…
|
||||
template: function Cmp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($_c2$, $_c3$);
|
||||
$r3$.ɵprojection(0, 1);
|
||||
$r3$.ɵtemplate(1, Cmp_ng_template_1_Template, 2, 0, "ng-template");
|
||||
$r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
|
||||
$r3$.ɵprojection(3, 2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
}
|
||||
`;
|
||||
}
|
||||
`;
|
||||
|
||||
const {source} = compile(files, angularFiles);
|
||||
expectEmit(source, output, 'Invalid content projection instructions generated');
|
||||
});
|
||||
const {source} = compile(files, angularFiles);
|
||||
expectEmit(source, output, 'Invalid content projection instructions generated');
|
||||
});
|
||||
|
||||
describe('queries', () => {
|
||||
@ -1911,7 +1849,7 @@ describe('compiler compliance', () => {
|
||||
const $c2$ = ["if", ""];
|
||||
const $c3$ = ["baz", ""];
|
||||
const $c4$ = ["bar", ""];
|
||||
function MyComponent_div_3_span_2_Template(rf, ctx) {
|
||||
function MyComponent_div_span_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "span");
|
||||
$r3$.ɵtext(1);
|
||||
@ -1926,11 +1864,11 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation3("", $foo$, "-", $bar$, "-", $baz$, ""));
|
||||
}
|
||||
}
|
||||
function MyComponent_div_3_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_3_span_2_Template, 2, 3, "span", $c2$);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_span_Template_2, 2, 3, "span", $c2$);
|
||||
$r3$.ɵelement(3, "span", null, $c4$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -1952,7 +1890,7 @@ describe('compiler compliance', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "div", null, $c1$);
|
||||
$r3$.ɵtext(2);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 5, 2, "div", $c2$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 5, 2, "div", $c2$);
|
||||
$r3$.ɵelement(4, "div", null, $c3$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -2000,7 +1938,7 @@ describe('compiler compliance', () => {
|
||||
const $c1$ = ["foo", ""];
|
||||
const $c2$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
|
||||
function MyComponent_div_0_span_3_Template(rf, ctx) {
|
||||
function MyComponent_div_span_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "span");
|
||||
$i0$.ɵtext(1);
|
||||
@ -2013,11 +1951,11 @@ describe('compiler compliance', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div");
|
||||
$i0$.ɵelement(1, "div", null, $c1$);
|
||||
$i0$.ɵtemplate(3, MyComponent_div_0_span_3_Template, 2, 2, "span", $c2$);
|
||||
$i0$.ɵtemplate(3, MyComponent_div_span_Template_3, 2, 2, "span", $c2$);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -2029,7 +1967,7 @@ describe('compiler compliance', () => {
|
||||
// ...
|
||||
template:function MyComponent_Template(rf, ctx){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 4, 1, "div", $c0$);
|
||||
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 4, 1, "div", $c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
|
||||
@ -2224,7 +2162,7 @@ describe('compiler compliance', () => {
|
||||
|
||||
const MyComponentDefinition = `
|
||||
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
|
||||
function MyComponent__svg_g_1_Template(rf, ctx) {
|
||||
function MyComponent__svg_g_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵnamespaceSVG();
|
||||
$r3$.ɵelementStart(0,"g");
|
||||
@ -2243,7 +2181,7 @@ describe('compiler compliance', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵnamespaceSVG();
|
||||
$r3$.ɵelementStart(0,"svg");
|
||||
$r3$.ɵtemplate(1, MyComponent__svg_g_1_Template, 2, 0, ":svg:g", $t1_attrs$);
|
||||
$r3$.ɵtemplate(1, MyComponent__svg_g_Template_1, 2, 0, ":svg:g", $t1_attrs$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) { $r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items)); }
|
||||
@ -2300,7 +2238,7 @@ describe('compiler compliance', () => {
|
||||
|
||||
const MyComponentDefinition = `
|
||||
const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
|
||||
function MyComponent_li_1_Template(rf, ctx) {
|
||||
function MyComponent_li_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "li");
|
||||
$r3$.ɵtext(1);
|
||||
@ -2321,7 +2259,7 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "ul");
|
||||
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 2, 1, "li", $t1_attrs$);
|
||||
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 2, 1, "li", $t1_attrs$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -2380,7 +2318,7 @@ describe('compiler compliance', () => {
|
||||
|
||||
const MyComponentDefinition = `
|
||||
const $t4_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
|
||||
function MyComponent_li_1_li_4_Template(rf, ctx) {
|
||||
function MyComponent_li_li_Template_4(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "li");
|
||||
$r3$.ɵtext(1);
|
||||
@ -2393,14 +2331,14 @@ describe('compiler compliance', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_li_1_Template(rf, ctx) {
|
||||
function MyComponent_li_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "li");
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
$r3$.ɵtext(2);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(3, "ul");
|
||||
$r3$.ɵtemplate(4, MyComponent_li_1_li_4_Template, 2, 2, "li", $t4_attrs$);
|
||||
$r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 2, "li", $t4_attrs$);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -2421,7 +2359,7 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "ul");
|
||||
$r3$.ɵtemplate(1, MyComponent_li_1_Template, 5, 2, "li", $c1$);
|
||||
$r3$.ɵtemplate(1, MyComponent_li_Template_1, 5, 2, "li", $c1$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
|
@ -24,13 +24,13 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[i18n]'})
|
||||
export class I18nDirective {}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<div i18n></div>'})
|
||||
export class MyComponent {}
|
||||
|
||||
|
||||
@NgModule({declarations: [I18nDirective, MyComponent]})
|
||||
export class MyModule{}`
|
||||
}
|
||||
@ -39,11 +39,11 @@ describe('compiler compliance: directives', () => {
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "div");
|
||||
@ -64,7 +64,7 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[i18n]'})
|
||||
export class I18nDirective {}
|
||||
|
||||
@ -73,10 +73,10 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
@Directive({selector: '[foo]'})
|
||||
export class FooDirective {}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<div i18n-foo></div>'})
|
||||
export class MyComponent {}
|
||||
|
||||
|
||||
@NgModule({declarations: [I18nDirective, I18nFooDirective, FooDirective, MyComponent]})
|
||||
export class MyModule{}`
|
||||
}
|
||||
@ -85,11 +85,11 @@ describe('compiler compliance: directives', () => {
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "div");
|
||||
@ -111,15 +111,15 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[someDirective]'})
|
||||
export class SomeDirective {
|
||||
@Input() someDirective;
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<div [someDirective]="true"></div>'})
|
||||
export class MyComponent {}
|
||||
|
||||
|
||||
@NgModule({declarations: [SomeDirective, MyComponent]})
|
||||
export class MyModule{}
|
||||
`
|
||||
@ -129,7 +129,7 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
…
|
||||
const _c0 = [${AttributeMarker.SelectOnly}, "someDirective"];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
@ -184,7 +184,7 @@ describe('compiler compliance: directives', () => {
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
const $_c0$ = ["directiveA", ""];
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0, "Some content");
|
||||
}
|
||||
@ -194,7 +194,7 @@ describe('compiler compliance: directives', () => {
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", $_c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", $_c0$);
|
||||
}
|
||||
},
|
||||
…
|
||||
@ -236,9 +236,9 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $_c1$ = ["directiveA", ""];
|
||||
function MyComponent_ng_container_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_container_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementContainerStart(0, $_c1$);
|
||||
$r3$.ɵtext(1, "Some content");
|
||||
@ -250,7 +250,7 @@ describe('compiler compliance: directives', () => {
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_container_0_Template, 2, 0, "ng-container", $_c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_container_Template_0, 2, 0, "ng-container", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.showing));
|
||||
@ -272,15 +272,15 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[someDirective]'})
|
||||
export class SomeDirective {
|
||||
@Input() someDirective;
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<ng-template [someDirective]="true"></ng-template>'})
|
||||
export class MyComponent {}
|
||||
|
||||
|
||||
@NgModule({declarations: [SomeDirective, MyComponent]})
|
||||
export class MyModule{}
|
||||
`
|
||||
@ -290,14 +290,14 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
…
|
||||
const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", $c0_a0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 0, 0, "ng-template", $c0_a0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
|
||||
@ -321,15 +321,15 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[someDirective]'})
|
||||
export class SomeDirective {
|
||||
@Input() someDirective;
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<div *someDirective></div>'})
|
||||
export class MyComponent {}
|
||||
|
||||
|
||||
@NgModule({declarations: [SomeDirective, MyComponent]})
|
||||
export class MyModule{}
|
||||
`
|
||||
@ -338,14 +338,14 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
…
|
||||
const $c0_a0$ = ["someDirective", ""];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 1, 0, "div", $c0_a0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 1, 0, "div", $c0_a0$);
|
||||
}
|
||||
},
|
||||
…
|
||||
@ -367,17 +367,17 @@ describe('compiler compliance: directives', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, Output, EventEmitter, NgModule} from '@angular/core';
|
||||
|
||||
|
||||
@Directive({selector: '[someDirective]'})
|
||||
export class SomeDirective {
|
||||
@Output() someDirective = new EventEmitter();
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-component', template: '<div (someDirective)="noop()"></div>'})
|
||||
export class MyComponent {
|
||||
noop() {}
|
||||
}
|
||||
|
||||
|
||||
@NgModule({declarations: [SomeDirective, MyComponent]})
|
||||
export class MyModule{}
|
||||
`
|
||||
@ -387,7 +387,7 @@ describe('compiler compliance: directives', () => {
|
||||
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
…
|
||||
…
|
||||
const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
@ -395,7 +395,7 @@ describe('compiler compliance: directives', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $c0_a0$);
|
||||
$r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_0_listener($event) { return ctx.noop(); });
|
||||
$r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_listener($event) { return ctx.noop(); });
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
|
@ -6,7 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AttributeMarker} from '@angular/compiler/src/core';
|
||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../compiler/src/compiler';
|
||||
@ -394,7 +393,7 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
|
||||
/**
|
||||
* @desc d
|
||||
* @meaning m
|
||||
@ -403,7 +402,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"interpolation": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
@ -423,7 +422,7 @@ describe('i18n support in the view compiler', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
|
||||
@ -523,7 +522,7 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
const $_c0$ = ["ngFor", "", 1, "ngForOf"];
|
||||
/**
|
||||
* @desc d
|
||||
* @meaning m
|
||||
@ -532,7 +531,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"interpolation": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c1$ = ["title", $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$];
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
@ -552,7 +551,7 @@ describe('i18n support in the view compiler', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", $_c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 4, 3, "div", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngForOf", $r3$.ɵbind(ctx.items));
|
||||
@ -923,7 +922,7 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", {
|
||||
"interpolation": "\uFFFD0\uFFFD",
|
||||
"startTagDiv": "\uFFFD#3\uFFFD",
|
||||
@ -931,7 +930,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"closeTagDiv": "\uFFFD/#3\uFFFD"
|
||||
});
|
||||
…
|
||||
function MyComponent_div_2_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
@ -956,7 +955,7 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵtext(1, " Some content ");
|
||||
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 4, "div", $_c0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 5, 4, "div", $_c0$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -977,8 +976,8 @@ describe('i18n support in the view compiler', () => {
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = ["src", "logo.png"];
|
||||
const $_c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
function MyComponent_img_1_Template(rf, ctx) {
|
||||
const $_c1$ = [1, "ngIf"];
|
||||
function MyComponent_img_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "img", $_c0$);
|
||||
}
|
||||
@ -987,7 +986,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"interpolation": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c2$ = ["title", $MSG_EXTERNAL_2367729185105559721$];
|
||||
function MyComponent_img_2_Template(rf, ctx) {
|
||||
function MyComponent_img_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "img", $_c0$);
|
||||
$r3$.ɵi18nAttributes(1, $_c2$);
|
||||
@ -1005,8 +1004,8 @@ describe('i18n support in the view compiler', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "img", $_c0$);
|
||||
$r3$.ɵtemplate(1, MyComponent_img_1_Template, 1, 0, "img", $_c1$);
|
||||
$r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c1$);
|
||||
$r3$.ɵtemplate(1, MyComponent_img_Template_1, 1, 0, "img", $_c1$);
|
||||
$r3$.ɵtemplate(2, MyComponent_img_Template_2, 2, 1, "img", $_c1$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
@ -1044,8 +1043,8 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
function MyComponent_div_2_div_4_Template(rf, ctx) {
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
function MyComponent_div_div_Template_4(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2);
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
@ -1060,13 +1059,13 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵi18nApply(0);
|
||||
}
|
||||
}
|
||||
function MyComponent_div_2_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
$r3$.ɵelementStart(2, "div");
|
||||
$r3$.ɵpipe(3, "uppercase");
|
||||
$r3$.ɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", $_c1$);
|
||||
$r3$.ɵtemplate(4, MyComponent_div_div_Template_4, 3, 2, "div", $_c1$);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵi18nEnd();
|
||||
@ -1093,7 +1092,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"interpolation_5": "\uFFFD1:3\uFFFD"
|
||||
});
|
||||
const $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$);
|
||||
function MyComponent_div_3_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 3);
|
||||
$r3$.ɵelementStart(1, "div");
|
||||
@ -1116,8 +1115,8 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", $_c1$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", $_c1$);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 5, 5, "div", $_c1$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 4, "div", $_c1$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -1137,14 +1136,14 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", {
|
||||
"startTagSpan": "\uFFFD#2\uFFFD",
|
||||
"interpolation": "\uFFFD0\uFFFD",
|
||||
"closeTagSpan": "\uFFFD/#2\uFFFD"
|
||||
});
|
||||
…
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$);
|
||||
@ -1163,7 +1162,7 @@ describe('i18n support in the view compiler', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 1, "div", $_c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 3, 1, "div", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
@ -1234,7 +1233,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const output = String.raw `
|
||||
const $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #2");
|
||||
const $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$ = goog.getMsg("My i18n block #1");
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$);
|
||||
}
|
||||
@ -1242,7 +1241,7 @@ describe('i18n support in the view compiler', () => {
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template");
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template");
|
||||
$r3$.ɵelementContainerStart(1);
|
||||
$r3$.ɵi18n(2, $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵelementContainerEnd();
|
||||
@ -1260,21 +1259,23 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
const $_c0$ = [${AttributeMarker.Classes}, "myClass"];
|
||||
const $_c0$ = ["myClass", 1, "myClass", true];
|
||||
const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$ = goog.getMsg("Text #1");
|
||||
const $_c1$ = [${AttributeMarker.Styles}, "padding", "10px"];
|
||||
const $_c1$ = ["padding", 1, "padding", "10px"];
|
||||
const $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$ = goog.getMsg("Text #2");
|
||||
…
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "span", $_c0$);
|
||||
$r3$.ɵelementStart(0, "span");
|
||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵelementStyling($_c0$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(2, "span", $_c1$);
|
||||
$r3$.ɵelementStart(2, "span");
|
||||
$r3$.ɵi18nStart(3, $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_2$);
|
||||
$r3$.ɵelementStyling(null, $_c1$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -1324,7 +1325,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$ = goog.getMsg("Some content: {$interpolation}", {
|
||||
"interpolation": "\uFFFD0\uFFFD"
|
||||
});
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$);
|
||||
$r3$.ɵpipe(1, "uppercase");
|
||||
@ -1339,7 +1340,7 @@ describe('i18n support in the view compiler', () => {
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 2, 3, "ng-template");
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 2, 3, "ng-template");
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1364,7 +1365,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"closeTagNgContainer": "\uFFFD/#3\uFFFD",
|
||||
"interpolation": "\uFFFD0:1\uFFFD"
|
||||
});
|
||||
function MyComponent_ng_template_2_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵpipe(1, "uppercase");
|
||||
@ -1382,7 +1383,7 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 2, 3, "ng-template");
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 2, 3, "ng-template");
|
||||
$r3$.ɵelementContainerStart(3);
|
||||
$r3$.ɵpipe(4, "uppercase");
|
||||
$r3$.ɵelementContainerEnd();
|
||||
@ -1414,7 +1415,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$);
|
||||
}
|
||||
@ -1429,7 +1430,7 @@ describe('i18n support in the view compiler', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 1, "ng-template");
|
||||
$r3$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 1, "ng-template");
|
||||
$r3$.ɵelementContainerStart(1);
|
||||
$r3$.ɵi18n(2, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵelementContainerEnd();
|
||||
@ -1460,7 +1461,7 @@ describe('i18n support in the view compiler', () => {
|
||||
`;
|
||||
|
||||
const output = String.raw `
|
||||
function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_ng_template_ng_template_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 3);
|
||||
}
|
||||
@ -1470,10 +1471,10 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵi18nApply(0);
|
||||
}
|
||||
}
|
||||
function MyComponent_ng_template_2_ng_template_2_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_ng_template_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 2);
|
||||
$r3$.ɵtemplate(1, MyComponent_ng_template_2_ng_template_2_ng_template_1_Template, 1, 1, "ng-template");
|
||||
$r3$.ɵtemplate(1, MyComponent_ng_template_ng_template_ng_template_Template_1, 1, 1, "ng-template");
|
||||
$r3$.ɵi18nEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -1490,11 +1491,11 @@ describe('i18n support in the view compiler', () => {
|
||||
"interpolation_2": "\uFFFD0:3\uFFFD"
|
||||
});
|
||||
const $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$);
|
||||
function MyComponent_ng_template_2_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵpipe(1, "uppercase");
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_2_ng_template_2_Template, 2, 1, "ng-template");
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_ng_template_Template_2, 2, 1, "ng-template");
|
||||
$r3$.ɵi18nEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -1510,7 +1511,7 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $I18N_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 3, 3, "ng-template");
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 3, 3, "ng-template");
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -1535,7 +1536,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
function MyComponent_ng_template_2_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18n(0, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$);
|
||||
}
|
||||
@ -1553,7 +1554,7 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵelementContainerStart(0);
|
||||
$r3$.ɵi18n(1, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵelementContainerEnd();
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template");
|
||||
$r3$.ɵtemplate(2, MyComponent_ng_template_Template_2, 1, 1, "ng-template");
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
@ -1583,7 +1584,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$ = goog.getMsg("{$tagImg} is my logo #2 ", {
|
||||
"tagImg": "\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"
|
||||
});
|
||||
function MyComponent_ng_template_3_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$);
|
||||
$r3$.ɵelement(1, "img", $_c0$);
|
||||
@ -1598,7 +1599,7 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵelement(2, "img", $_c0$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementContainerEnd();
|
||||
$r3$.ɵtemplate(3, MyComponent_ng_template_3_Template, 2, 0, "ng-template");
|
||||
$r3$.ɵtemplate(3, MyComponent_ng_template_Template_3, 2, 0, "ng-template");
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1700,13 +1701,13 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $_c1$ = ["title", "icu only"];
|
||||
const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}");
|
||||
const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
function MyComponent_div_2_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c1$);
|
||||
$r3$.ɵi18n(1, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$);
|
||||
@ -1725,7 +1726,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
function MyComponent_div_3_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c2$);
|
||||
$r3$.ɵtext(1, " You have ");
|
||||
@ -1748,8 +1749,8 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18n(1, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵtemplate(2, MyComponent_div_2_Template, 2, 1, "div", $_c0$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_div_Template_2, 2, 1, "div", $_c0$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 4, 2, "div", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
@ -1941,7 +1942,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_APP_SPEC_TS_2$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_2$, {
|
||||
"VAR_SELECT": "\uFFFD1\uFFFD"
|
||||
});
|
||||
const $_c3$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c3$ = [1, "ngIf"];
|
||||
const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}");
|
||||
const $I18N_APP_SPEC_TS__4$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS__4$, {
|
||||
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
||||
@ -1955,7 +1956,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_0$, {
|
||||
"ICU": [$I18N_APP_SPEC_TS_1$, $I18N_APP_SPEC_TS_2$, $I18N_APP_SPEC_TS__4$]
|
||||
});
|
||||
function MyComponent_div_3_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_3(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $I18N_APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵelement(1, "div");
|
||||
@ -1975,7 +1976,7 @@ describe('i18n support in the view compiler', () => {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $I18N_APP_SPEC_TS_0$);
|
||||
$r3$.ɵelement(2, "div");
|
||||
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", $_c3$);
|
||||
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 2, 1, "div", $_c3$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -2049,7 +2050,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}");
|
||||
const $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$, {
|
||||
"VAR_SELECT": "\uFFFD0:1\uFFFD"
|
||||
@ -2060,7 +2061,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"icu": $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$,
|
||||
"icu_1": $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$
|
||||
});
|
||||
function MyComponent_span_2_Template(rf, ctx) {
|
||||
function MyComponent_span_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵelement(1, "span");
|
||||
@ -2079,7 +2080,7 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", $_c2$);
|
||||
$r3$.ɵtemplate(2, MyComponent_span_Template_2, 2, 1, "span", $_c2$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
@ -2112,7 +2113,7 @@ describe('i18n support in the view compiler', () => {
|
||||
const $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$, {
|
||||
"VAR_SELECT": "\uFFFD0\uFFFD"
|
||||
});
|
||||
const $_c0$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $_c0$ = [1, "ngIf"];
|
||||
const $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {$interpolation}}}", {
|
||||
"interpolation": "\uFFFD1:1\uFFFD"
|
||||
});
|
||||
@ -2125,7 +2126,7 @@ describe('i18n support in the view compiler', () => {
|
||||
"icu": $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$,
|
||||
"icu_1": $I18N_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$
|
||||
});
|
||||
function MyComponent_span_2_Template(rf, ctx) {
|
||||
function MyComponent_span_Template_2(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵi18nStart(0, $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$, 1);
|
||||
$r3$.ɵelement(1, "span");
|
||||
@ -2145,7 +2146,7 @@ describe('i18n support in the view compiler', () => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵi18nStart(1, $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$);
|
||||
$r3$.ɵtemplate(2, MyComponent_span_2_Template, 2, 2, "span", $_c2$);
|
||||
$r3$.ɵtemplate(2, MyComponent_span_Template_2, 2, 2, "span", $_c2$);
|
||||
$r3$.ɵi18nEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ describe('compiler compliance: listen()', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_div_click_0_listener($event) {
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_div_click_listener($event) {
|
||||
ctx.onClick($event);
|
||||
return (1 == 2);
|
||||
});
|
||||
@ -92,7 +92,7 @@ describe('compiler compliance: listen()', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "my-app", $e0_attrs$);
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_my_app_click_0_listener($event) {
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_my_app_click_listener($event) {
|
||||
return ctx.onClick($event);
|
||||
});
|
||||
$r3$.ɵelementEnd();
|
||||
@ -136,19 +136,19 @@ describe('compiler compliance: listen()', () => {
|
||||
const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
const $e_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
|
||||
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
const $s$ = $r3$.ɵgetCurrentView();
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStart(1, "div", $e_attrs$);
|
||||
$r3$.ɵlistener("click", function MyComponent_div_0_Template_div_click_1_listener($event) {
|
||||
$r3$.ɵlistener("click", function MyComponent_div_Template_0_div_click_listener($event) {
|
||||
$r3$.ɵrestoreView($s$);
|
||||
const $comp$ = $r3$.ɵnextContext();
|
||||
return $comp$.onClick($comp$.foo);
|
||||
});
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(2, "button", $e_attrs$);
|
||||
$r3$.ɵlistener("click", function MyComponent_div_0_Template_button_click_2_listener($event) {
|
||||
$r3$.ɵlistener("click", function MyComponent_div_Template_0_button_click_listener($event) {
|
||||
$r3$.ɵrestoreView($s$);
|
||||
const $comp2$ = $r3$.ɵnextContext();
|
||||
return $comp2$.onClick2($comp2$.bar);
|
||||
@ -160,7 +160,7 @@ describe('compiler compliance: listen()', () => {
|
||||
// ...
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 0, "div", $c0$);
|
||||
$r3$.ɵtemplate(0, MyComponent_div_Template_0, 3, 0, "div", $c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngIf", $i0$.ɵbind(ctx.showing));
|
||||
@ -208,7 +208,7 @@ describe('compiler compliance: listen()', () => {
|
||||
if (rf & 1) {
|
||||
const $s$ = $r3$.ɵgetCurrentView();
|
||||
$r3$.ɵelementStart(0, "button", $e0_attrs$);
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_button_click_0_listener($event) {
|
||||
$r3$.ɵlistener("click", function MyComponent_Template_button_click_listener($event) {
|
||||
$r3$.ɵrestoreView($s$);
|
||||
const $user$ = $r3$.ɵreference(3);
|
||||
return ctx.onClick($user$.value);
|
||||
|
@ -120,70 +120,4 @@ describe('r3_view_compiler', () => {
|
||||
expectEmit(result.source, bV_call, 'Incorrect bV call');
|
||||
});
|
||||
});
|
||||
|
||||
describe('animations', () => {
|
||||
it('should keep @attr but suppress [@attr]', () => {
|
||||
const files: MockDirectory = {
|
||||
app: {
|
||||
'example.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<div @attrOnly [@myAnimation]="exp"></div>'
|
||||
})
|
||||
export class MyApp {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyApp]})
|
||||
export class MyModule {}`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
const _c0 = ["@attrOnly", ""];
|
||||
// ...
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelement(0, "div", _c0);
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect initialization attributes');
|
||||
});
|
||||
|
||||
it('should dedup multiple [@event] listeners', () => {
|
||||
const files: MockDirectory = {
|
||||
app: {
|
||||
'example.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<div (@mySelector.start)="false" (@mySelector.done)="false" [@mySelector]="0"></div>'
|
||||
})
|
||||
export class MyApp {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyApp]})
|
||||
export class MyModule {}`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
const _c0 = [3, "@mySelector"];
|
||||
// ...
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div", _c0);
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect initialization attributes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AttributeMarker, ViewEncapsulation} from '@angular/compiler/src/core';
|
||||
import {AttributeMarker, InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
|
||||
import {setup} from '@angular/compiler/test/aot/test_util';
|
||||
import {compile, expectEmit} from './mock_compile';
|
||||
|
||||
@ -214,6 +214,7 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_attrs$ = ["@foo", ""];
|
||||
const $e1_attrs$ = ["@bar", ""];
|
||||
const $e2_attrs$ = ["@baz", ""];
|
||||
…
|
||||
@ -223,7 +224,7 @@ describe('compiler compliance: styling', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, "div");
|
||||
$r3$.ɵelement(0, "div", $e0_attrs$);
|
||||
$r3$.ɵelement(1, "div", $e1_attrs$);
|
||||
$r3$.ɵelement(2, "div", $e2_attrs$);
|
||||
}
|
||||
@ -281,8 +282,8 @@ describe('compiler compliance: styling', () => {
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", _c0);
|
||||
$r3$.ɵlistener("@myAnimation.start", function MyComponent_Template_div_animation_myAnimation_start_0_listener($event) { return ctx.onStart($event); });
|
||||
$r3$.ɵlistener("@myAnimation.done", function MyComponent_Template_div_animation_myAnimation_done_0_listener($event) { return ctx.onDone($event); });
|
||||
$r3$.ɵlistener("@myAnimation.start", function MyComponent_Template_div__myAnimation_start_listener($event) { return ctx.onStart($event); });
|
||||
$r3$.ɵlistener("@myAnimation.done", function MyComponent_Template_div__myAnimation_done_listener($event) { return ctx.onDone($event); });
|
||||
$r3$.ɵelementEnd();
|
||||
} if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "@myAnimation", $r3$.ɵbind(ctx.exp));
|
||||
@ -296,64 +297,6 @@ describe('compiler compliance: styling', () => {
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should generate animation host binding and listener code for directives', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Directive, Component, NgModule} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[my-anim-dir]',
|
||||
animations: [
|
||||
{name: 'myAnim'}
|
||||
],
|
||||
host: {
|
||||
'[@myAnim]': 'myAnimState',
|
||||
'(@myAnim.start)': 'onStart()',
|
||||
'(@myAnim.done)': 'onDone()'
|
||||
}
|
||||
})
|
||||
class MyAnimDir {
|
||||
onStart() {}
|
||||
onDone() {}
|
||||
myAnimState = '123';
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-cmp',
|
||||
template: \`
|
||||
<div my-anim-dir></div>
|
||||
\`
|
||||
})
|
||||
class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent, MyAnimDir]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
MyAnimDir.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
…
|
||||
hostBindings: function MyAnimDir_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵallocHostVars(1);
|
||||
$r3$.ɵlistener("@myAnim.start", function MyAnimDir_animation_myAnim_start_HostBindingHandler($event) { return ctx.onStart(); });
|
||||
$r3$.ɵlistener("@myAnim.done", function MyAnimDir_animation_myAnim_done_HostBindingHandler($event) { return ctx.onDone(); });
|
||||
} if (rf & 2) {
|
||||
$r3$.ɵcomponentHostSyntheticProperty(elIndex, "@myAnim", $r3$.ɵbind(ctx.myAnimState), null, true);
|
||||
}
|
||||
}
|
||||
…
|
||||
});
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[style] and [style.prop]', () => {
|
||||
@ -423,8 +366,8 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $_c0$ = [${AttributeMarker.Styles}, "opacity", "1", ${AttributeMarker.SelectOnly}, "style"];
|
||||
const $_c1$ = ["width", "height"];
|
||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
|
||||
const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -436,14 +379,14 @@ describe('compiler compliance: styling', () => {
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $_c0$);
|
||||
$r3$.ɵelementStyling(null, $_c1$, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||
$r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
|
||||
$r3$.ɵelementStyleProp(0, 0, $ctx$.myWidth);
|
||||
$r3$.ɵelementStyleProp(0, 1, $ctx$.myHeight);
|
||||
$r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
|
||||
$r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
$r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
|
||||
}
|
||||
@ -478,7 +421,7 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $_c0$ = ["background-image"];
|
||||
const _c0 = ["background-image"];
|
||||
export class MyComponent {
|
||||
constructor() {
|
||||
this.myImage = 'url(foo.jpg)';
|
||||
@ -513,6 +456,7 @@ describe('compiler compliance: styling', () => {
|
||||
});
|
||||
|
||||
it('should support [style.foo.suffix] style bindings with a suffix', () => {
|
||||
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
@ -532,7 +476,7 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_styles$ = ["font-size"];
|
||||
const $e0_styles$= ["font-size"];
|
||||
…
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
@ -620,8 +564,8 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_attrs$ = [${AttributeMarker.Classes}, "grape", ${AttributeMarker.SelectOnly}, "class"];
|
||||
const $e0_bindings$ = ["apple", "orange"];
|
||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
|
||||
const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -634,13 +578,13 @@ describe('compiler compliance: styling', () => {
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||
$r3$.ɵelementStyling($e0_bindings$);
|
||||
$r3$.ɵelementStyling($e0_cd$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
|
||||
$r3$.ɵelementClassProp(0, 0, $ctx$.yesToApple);
|
||||
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToOrange);
|
||||
$r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
|
||||
$r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
$r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
|
||||
}
|
||||
@ -662,7 +606,7 @@ describe('compiler compliance: styling', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`<div class=" foo "
|
||||
template: \`<div class="foo"
|
||||
style="width:100px"
|
||||
[attr.class]="'round'"
|
||||
[attr.style]="'height:100px'"></div>\`
|
||||
@ -676,7 +620,9 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", ${AttributeMarker.Styles}, "width", "100px", ${AttributeMarker.SelectOnly}, "class", "style"];
|
||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class", "style"];
|
||||
const $e0_cd$ = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
|
||||
const $e0_sd$ = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
|
||||
…
|
||||
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -689,6 +635,7 @@ describe('compiler compliance: styling', () => {
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div", $e0_attrs$);
|
||||
$r3$.ɵelementStyling($e0_cd$, $e0_sd$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -818,13 +765,10 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_classBindings$ = ["foo"];
|
||||
const $e0_styleBindings$ = ["bar", "baz"];
|
||||
…
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵelementStyling($e0_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵpipe(1, "pipe");
|
||||
$r3$.ɵpipe(2, "pipe");
|
||||
$r3$.ɵpipe(3, "pipe");
|
||||
@ -884,18 +828,16 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $e0_attrs$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||
const $e0_classBindings$ = ["foo"];
|
||||
const $e0_styleBindings$ = ["color"];
|
||||
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
||||
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
||||
…
|
||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementHostAttrs(ctx, $e0_attrs$);
|
||||
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myColorProp, null, ctx);
|
||||
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
@ -1017,10 +959,10 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $widthDir_classes$ = ["foo"];
|
||||
const $widthDir_styles$ = ["width"];
|
||||
const $heightDir_classes$ = ["bar"];
|
||||
const $heightDir_styles$ = ["height"];
|
||||
const _c0 = ["foo"];
|
||||
const _c1 = ["width"];
|
||||
const _c2 = ["bar"];
|
||||
const _c3 = ["height"];
|
||||
…
|
||||
function ClassDirective_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
@ -1034,7 +976,7 @@ describe('compiler compliance: styling', () => {
|
||||
…
|
||||
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling($widthDir_classes$, $widthDir_styles$, null, ctx);
|
||||
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
|
||||
@ -1045,7 +987,7 @@ describe('compiler compliance: styling', () => {
|
||||
…
|
||||
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling($heightDir_classes$, $heightDir_styles$, null, ctx);
|
||||
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
|
||||
@ -1072,8 +1014,7 @@ describe('compiler compliance: styling', () => {
|
||||
template: '',
|
||||
host: {
|
||||
'style': 'width:200px; height:500px',
|
||||
'class': 'foo baz',
|
||||
'title': 'foo title'
|
||||
'class': 'foo baz'
|
||||
}
|
||||
})
|
||||
export class MyComponent {
|
||||
@ -1088,9 +1029,6 @@ describe('compiler compliance: styling', () => {
|
||||
|
||||
@HostBinding('title')
|
||||
title = 'some title';
|
||||
|
||||
@Input('name')
|
||||
name = '';
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
@ -1100,13 +1038,13 @@ describe('compiler compliance: styling', () => {
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $_c0$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||
const $_c0$ = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
||||
const $_c1$ = ["width", "height", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
||||
…
|
||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵallocHostVars(2);
|
||||
$r3$.ɵelementHostAttrs(ctx, $_c0$);
|
||||
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
$r3$.ɵelementStyling($_c0$, $_c1$, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);
|
||||
|
@ -52,12 +52,12 @@ describe('compiler compliance: template', () => {
|
||||
const template = `
|
||||
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "title", "click"];
|
||||
function MyComponent_ul_0_li_1_div_1_Template(rf, ctx) {
|
||||
function MyComponent_ul_li_div_Template_1(rf, ctx) {
|
||||
|
||||
if (rf & 1) {
|
||||
const $s$ = $i0$.ɵgetCurrentView();
|
||||
$i0$.ɵelementStart(0, "div", $e0_attrs$);
|
||||
$i0$.ɵlistener("click", function MyComponent_ul_0_li_1_div_1_Template_div_click_0_listener($event){
|
||||
$i0$.ɵlistener("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
|
||||
$i0$.ɵrestoreView($s$);
|
||||
const $inner$ = ctx.$implicit;
|
||||
const $middle$ = $i0$.ɵnextContext().$implicit;
|
||||
@ -79,10 +79,10 @@ describe('compiler compliance: template', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_ul_0_li_1_Template(rf, ctx) {
|
||||
function MyComponent_ul_li_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "li");
|
||||
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_div_1_Template, 2, 2, "div", _c0);
|
||||
$i0$.ɵtemplate(1, MyComponent_ul_li_div_Template_1, 2, 2, "div", _c0);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -91,10 +91,10 @@ describe('compiler compliance: template', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_ul_0_Template(rf, ctx) {
|
||||
function MyComponent_ul_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "ul");
|
||||
$i0$.ɵtemplate(1, MyComponent_ul_0_li_1_Template, 2, 1, "li", _c0);
|
||||
$i0$.ɵtemplate(1, MyComponent_ul_li_Template_1, 2, 1, "li", _c0);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -105,7 +105,7 @@ describe('compiler compliance: template', () => {
|
||||
// ...
|
||||
template:function MyComponent_Template(rf, ctx){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_ul_0_Template, 2, 1, "ul", _c0);
|
||||
$i0$.ɵtemplate(0, MyComponent_ul_Template_0, 2, 1, "ul", _c0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
|
||||
@ -141,7 +141,7 @@ describe('compiler compliance: template', () => {
|
||||
const template = `
|
||||
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
|
||||
function MyComponent_span_0_Template(rf, ctx) {
|
||||
function MyComponent_span_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "span");
|
||||
$i0$.ɵtext(1);
|
||||
@ -156,7 +156,7 @@ describe('compiler compliance: template', () => {
|
||||
// ...
|
||||
template:function MyComponent_Template(rf, ctx){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_span_0_Template, 2, 2, "span", _c0);
|
||||
$i0$.ɵtemplate(0, MyComponent_span_Template_0, 2, 2, "span", _c0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
|
||||
@ -195,7 +195,7 @@ describe('compiler compliance: template', () => {
|
||||
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
const $c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
|
||||
|
||||
function MyComponent_div_0_span_1_Template(rf, ctx) {
|
||||
function MyComponent_div_span_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "span");
|
||||
$i0$.ɵtext(1);
|
||||
@ -209,10 +209,10 @@ describe('compiler compliance: template', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div");
|
||||
$i0$.ɵtemplate(1, MyComponent_div_0_span_1_Template, 2, 2, "span", $c1$);
|
||||
$i0$.ɵtemplate(1, MyComponent_div_span_Template_1, 2, 2, "span", $c1$);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -224,7 +224,7 @@ describe('compiler compliance: template', () => {
|
||||
// ...
|
||||
template:function MyComponent_Template(rf, ctx){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", $c0$);
|
||||
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 2, 1, "div", $c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
|
||||
@ -264,7 +264,7 @@ describe('compiler compliance: template', () => {
|
||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||
const template = `
|
||||
const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
|
||||
function MyComponent_div_0_div_1_div_1_Template(rf, ctx) {
|
||||
function MyComponent_div_div_div_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div");
|
||||
$i0$.ɵtext(1);
|
||||
@ -277,10 +277,10 @@ describe('compiler compliance: template', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_div_0_div_1_Template(rf, ctx) {
|
||||
function MyComponent_div_div_Template_1(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div");
|
||||
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_div_1_Template, 2, 2, "div", _c0);
|
||||
$i0$.ɵtemplate(1, MyComponent_div_div_div_Template_1, 2, 2, "div", _c0);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -289,10 +289,10 @@ describe('compiler compliance: template', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_div_0_Template(rf, ctx) {
|
||||
function MyComponent_div_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵelementStart(0, "div");
|
||||
$i0$.ɵtemplate(1, MyComponent_div_0_div_1_Template, 2, 1, "div", _c0);
|
||||
$i0$.ɵtemplate(1, MyComponent_div_div_Template_1, 2, 1, "div", _c0);
|
||||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
@ -303,7 +303,7 @@ describe('compiler compliance: template', () => {
|
||||
// ...
|
||||
template:function MyComponent_Template(rf, ctx){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", _c0);
|
||||
$i0$.ɵtemplate(0, MyComponent_div_Template_0, 2, 1, "div", _c0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
|
||||
@ -339,7 +339,7 @@ describe('compiler compliance: template', () => {
|
||||
const template = `
|
||||
const $c0$ = ["attr", "l", ${AttributeMarker.SelectOnly}, "boundAttr"];
|
||||
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtext(0, " some-content ");
|
||||
}
|
||||
@ -349,7 +349,7 @@ describe('compiler compliance: template', () => {
|
||||
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", $c0$);
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", $c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵelementProperty(0, "boundAttr", $i0$.ɵbind(ctx.b));
|
||||
@ -370,7 +370,7 @@ describe('compiler compliance: template', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<ng-template #foo>some-content</ng-template>',
|
||||
template: '<ng-template #foo>some-content</ng-template>';
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@ -383,7 +383,7 @@ describe('compiler compliance: template', () => {
|
||||
const template = `
|
||||
const $t0_refs$ = ["foo", ""];
|
||||
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) {
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtext(0, "some-content");
|
||||
}
|
||||
@ -393,7 +393,7 @@ describe('compiler compliance: template', () => {
|
||||
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 1, 0, "ng-template", null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
|
||||
}
|
||||
}`;
|
||||
|
||||
@ -411,7 +411,7 @@ describe('compiler compliance: template', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<ng-template (outDirective)="$event.doSth()"></ng-template>',
|
||||
template: '<ng-template (outDirective)="$event.doSth()"></ng-template>';
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@ -424,14 +424,14 @@ describe('compiler compliance: template', () => {
|
||||
const template = `
|
||||
const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "outDirective"];
|
||||
|
||||
function MyComponent_ng_template_0_Template(rf, ctx) { }
|
||||
function MyComponent_ng_template_Template_0(rf, ctx) { }
|
||||
|
||||
// ...
|
||||
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", $t0_attrs$);
|
||||
$i0$.ɵlistener("outDirective", function MyComponent_Template_ng_template_outDirective_0_listener($event) { return $event.doSth(); });
|
||||
$i0$.ɵtemplate(0, MyComponent_ng_template_Template_0, 0, 0, "ng-template", $t0_attrs$);
|
||||
$i0$.ɵlistener("outDirective", function MyComponent_Template_ng_template_outDirective_listener($event) { return $event.doSth(); });
|
||||
}
|
||||
}`;
|
||||
|
||||
@ -440,122 +440,4 @@ describe('compiler compliance: template', () => {
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
|
||||
});
|
||||
|
||||
it('should create unique template function names even for similar nested template structures',
|
||||
() => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec1.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'a-component',
|
||||
template: \`
|
||||
<div *ngFor="let item of items">
|
||||
<p *ngIf="item < 10">less than 10</p>
|
||||
<p *ngIf="item < 10">less than 10</p>
|
||||
</div>
|
||||
<div *ngFor="let item of items">
|
||||
<p *ngIf="item > 10">more than 10</p>
|
||||
</div>
|
||||
\`,
|
||||
})
|
||||
export class AComponent {
|
||||
items = [4, 2];
|
||||
}
|
||||
|
||||
@NgModule({declarations: [AComponent]})
|
||||
export class AModule {}
|
||||
`,
|
||||
'spec2.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'b-component',
|
||||
template: \`
|
||||
<div *ngFor="let item of items">
|
||||
<ng-container *ngFor="let subitem of item.subitems">
|
||||
<p *ngIf="subitem < 10">less than 10</p>
|
||||
<p *ngIf="subitem < 10">less than 10</p>
|
||||
</ng-container>
|
||||
<ng-container *ngFor="let subitem of item.subitems">
|
||||
<p *ngIf="subitem < 10">less than 10</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngFor="let item of items">
|
||||
<ng-container *ngFor="let subitem of item.subitems">
|
||||
<p *ngIf="subitem > 10">more than 10</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
\`,
|
||||
})
|
||||
export class BComponent {
|
||||
items = [
|
||||
{subitems: [1, 3]},
|
||||
{subitems: [3, 7]},
|
||||
];
|
||||
}
|
||||
|
||||
@NgModule({declarations: [BComponent]})
|
||||
export class BModule {}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
const allTemplateFunctionsNames = (result.source.match(/function ([^\s(]+)/g) || [])
|
||||
.map(x => x.slice(9))
|
||||
.filter(x => x.includes('Template'))
|
||||
.sort();
|
||||
const uniqueTemplateFunctionNames = Array.from(new Set(allTemplateFunctionsNames));
|
||||
|
||||
// Expected template function:
|
||||
// - 5 for AComponent's template.
|
||||
// - 9 for BComponent's template.
|
||||
// - 2 for the two components.
|
||||
expect(allTemplateFunctionsNames.length).toBe(5 + 9 + 2);
|
||||
expect(allTemplateFunctionsNames).toEqual(uniqueTemplateFunctionNames);
|
||||
});
|
||||
|
||||
it('should create unique listener function names even for similar nested template structures',
|
||||
() => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`
|
||||
<div *ngFor="let item of items">
|
||||
<p (click)="$event">{{ item }}</p>
|
||||
<p (click)="$event">{{ item }}</p>
|
||||
</div>
|
||||
<div *ngFor="let item of items">
|
||||
<p (click)="$event">{{ item }}</p>
|
||||
</div>
|
||||
\`,
|
||||
})
|
||||
export class MyComponent {
|
||||
items = [4, 2];
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
const allListenerFunctionsNames = (result.source.match(/function ([^\s(]+)/g) || [])
|
||||
.map(x => x.slice(9))
|
||||
.filter(x => x.includes('listener'))
|
||||
.sort();
|
||||
const uniqueListenerFunctionNames = Array.from(new Set(allListenerFunctionsNames));
|
||||
|
||||
expect(allListenerFunctionsNames.length).toBe(3);
|
||||
expect(allListenerFunctionsNames).toEqual(uniqueListenerFunctionNames);
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CustomTransformers} from '@angular/compiler-cli';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
@ -112,9 +111,9 @@ export class NgtscTestEnvironment {
|
||||
/**
|
||||
* Run the compiler to completion, and assert that no errors occurred.
|
||||
*/
|
||||
driveMain(customTransformers?: CustomTransformers): void {
|
||||
driveMain(): void {
|
||||
const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error);
|
||||
const exitCode = main(['-p', this.basePath], errorSpy, undefined, customTransformers);
|
||||
const exitCode = main(['-p', this.basePath], errorSpy);
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
expect(exitCode).toBe(0);
|
||||
}
|
||||
|
@ -441,39 +441,6 @@ describe('ngtsc behavioral tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should unwrap a ModuleWithProviders-like function if a matching literal type is provided for it',
|
||||
() => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from 'router';
|
||||
|
||||
@NgModule({imports: [RouterModule.forRoot()]})
|
||||
export class TestModule {}
|
||||
`);
|
||||
|
||||
env.write('node_modules/router/index.d.ts', `
|
||||
import {ModuleWithProviders} from '@angular/core';
|
||||
|
||||
export interface MyType extends ModuleWithProviders {}
|
||||
|
||||
declare class RouterModule {
|
||||
static forRoot(): (MyType)&{ngModule:RouterModule};
|
||||
}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]');
|
||||
|
||||
const dtsContents = env.getContents('test.d.ts');
|
||||
expect(dtsContents).toContain(`import * as i1 from 'router';`);
|
||||
expect(dtsContents)
|
||||
.toContain(
|
||||
'i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
|
||||
});
|
||||
|
||||
it('should inject special types according to the metadata', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
@ -1227,32 +1194,4 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(dtsContents).toContain('/// <amd-module name="@mymodule" />');
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute custom transformers', () => {
|
||||
let beforeCount = 0;
|
||||
let afterCount = 0;
|
||||
|
||||
env.tsconfig();
|
||||
env.write('test.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule({})
|
||||
class Module {}
|
||||
`);
|
||||
|
||||
env.driveMain({
|
||||
beforeTs: [() => sourceFile => {
|
||||
beforeCount++;
|
||||
return sourceFile;
|
||||
}],
|
||||
afterTs: [() => sourceFile => {
|
||||
afterCount++;
|
||||
return sourceFile;
|
||||
}],
|
||||
});
|
||||
|
||||
expect(beforeCount).toBe(1);
|
||||
expect(afterCount).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -134,13 +134,10 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||
encapsulation: ViewEncapsulation;
|
||||
viewProviders: Provider[]|null;
|
||||
interpolation?: [string, string];
|
||||
changeDetection?: ChangeDetectionStrategy;
|
||||
}
|
||||
|
||||
export type ViewEncapsulation = number;
|
||||
|
||||
export type ChangeDetectionStrategy = number;
|
||||
|
||||
export interface R3QueryMetadataFacade {
|
||||
propertyName: string;
|
||||
first: boolean;
|
||||
|
@ -379,9 +379,13 @@ export const enum RenderFlags {
|
||||
Update = 0b10
|
||||
}
|
||||
|
||||
export const enum InitialStylingFlags {
|
||||
VALUES_MODE = 0b1,
|
||||
}
|
||||
|
||||
// Pasted from render3/interfaces/node.ts
|
||||
/**
|
||||
* A set of marker values to be used in the attributes arrays. These markers indicate that some
|
||||
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
|
||||
* items are not regular attributes and the processing should be adapted accordingly.
|
||||
*/
|
||||
export const enum AttributeMarker {
|
||||
@ -392,48 +396,11 @@ export const enum AttributeMarker {
|
||||
*/
|
||||
NamespaceURI = 0,
|
||||
|
||||
/**
|
||||
* Signals class declaration.
|
||||
*
|
||||
* Each value following `Classes` designates a class name to include on the element.
|
||||
* ## Example:
|
||||
*
|
||||
* Given:
|
||||
* ```
|
||||
* <div class="foo bar baz">...<d/vi>
|
||||
* ```
|
||||
*
|
||||
* the generated code is:
|
||||
* ```
|
||||
* var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz'];
|
||||
* ```
|
||||
*/
|
||||
Classes = 1,
|
||||
|
||||
/**
|
||||
* Signals style declaration.
|
||||
*
|
||||
* Each pair of values following `Styles` designates a style name and value to include on the
|
||||
* element.
|
||||
* ## Example:
|
||||
*
|
||||
* Given:
|
||||
* ```
|
||||
* <div style="width:100px; height:200px; color:red">...</div>
|
||||
* ```
|
||||
*
|
||||
* the generated code is:
|
||||
* ```
|
||||
* var _c1 = [AttributeMarker.Styles, 'width', '100px', 'height'. '200px', 'color', 'red'];
|
||||
* ```
|
||||
*/
|
||||
Styles = 2,
|
||||
|
||||
/**
|
||||
* This marker indicates that the following attribute names were extracted from bindings (ex.:
|
||||
* [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
|
||||
* Taking the above bindings and outputs as an example an attributes array could look as follows:
|
||||
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
|
||||
*/
|
||||
SelectOnly = 3,
|
||||
}
|
||||
SelectOnly = 1
|
||||
}
|
@ -130,7 +130,6 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
||||
styles: facade.styles || [],
|
||||
encapsulation: facade.encapsulation as any,
|
||||
interpolation: interpolationConfig,
|
||||
changeDetection: facade.changeDetection,
|
||||
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
||||
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
|
||||
null,
|
||||
@ -250,7 +249,11 @@ function extractHostBindings(host: {[key: string]: string}, propMetadata: {[key:
|
||||
properties: StringMap,
|
||||
} {
|
||||
// First parse the declarations from the metadata.
|
||||
const {attributes, listeners, properties} = parseHostBindings(host || {});
|
||||
const {attributes, listeners, properties, animations} = parseHostBindings(host || {});
|
||||
|
||||
if (Object.keys(animations).length > 0) {
|
||||
throw new Error(`Animation bindings are as-of-yet unsupported in Ivy`);
|
||||
}
|
||||
|
||||
// Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
|
||||
for (const field in propMetadata) {
|
||||
|
@ -334,7 +334,7 @@ class _Tokenizer {
|
||||
try {
|
||||
const charCode = parseInt(strNum, isHex ? 16 : 10);
|
||||
return String.fromCharCode(charCode);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
const entity = this._input.substring(start.offset + 1, this._index - 1);
|
||||
throw this._createError(_unknownEntityErrorMsg(entity), this._getSpan(start));
|
||||
}
|
||||
|
@ -31,9 +31,6 @@ export class Identifiers {
|
||||
|
||||
static elementProperty: o.ExternalReference = {name: 'ɵelementProperty', moduleName: CORE};
|
||||
|
||||
static componentHostSyntheticProperty:
|
||||
o.ExternalReference = {name: 'ɵcomponentHostSyntheticProperty', moduleName: CORE};
|
||||
|
||||
static elementAttribute: o.ExternalReference = {name: 'ɵelementAttribute', moduleName: CORE};
|
||||
|
||||
static elementClassProp: o.ExternalReference = {name: 'ɵelementClassProp', moduleName: CORE};
|
||||
@ -46,8 +43,6 @@ export class Identifiers {
|
||||
|
||||
static elementStyling: o.ExternalReference = {name: 'ɵelementStyling', moduleName: CORE};
|
||||
|
||||
static elementHostAttrs: o.ExternalReference = {name: 'ɵelementHostAttrs', moduleName: CORE};
|
||||
|
||||
static elementStylingMap: o.ExternalReference = {name: 'ɵelementStylingMap', moduleName: CORE};
|
||||
|
||||
static elementStyleProp: o.ExternalReference = {name: 'ɵelementStyleProp', moduleName: CORE};
|
||||
|
@ -52,31 +52,3 @@ export interface R3Reference {
|
||||
value: o.Expression;
|
||||
type: o.Expression;
|
||||
}
|
||||
|
||||
const ANIMATE_SYMBOL_PREFIX = '@';
|
||||
export function prepareSyntheticPropertyName(name: string) {
|
||||
return `${ANIMATE_SYMBOL_PREFIX}${name}`;
|
||||
}
|
||||
|
||||
export function prepareSyntheticListenerName(name: string, phase: string) {
|
||||
return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
|
||||
}
|
||||
|
||||
export function isSyntheticPropertyOrListener(name: string) {
|
||||
return name.charAt(0) == ANIMATE_SYMBOL_PREFIX;
|
||||
}
|
||||
|
||||
export function getSyntheticPropertyName(name: string) {
|
||||
// this will strip out listener phase values...
|
||||
// @foo.start => @foo
|
||||
const i = name.indexOf('.');
|
||||
name = i > 0 ? name.substring(0, i) : name;
|
||||
if (name.charAt(0) !== ANIMATE_SYMBOL_PREFIX) {
|
||||
name = ANIMATE_SYMBOL_PREFIX + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function prepareSyntheticListenerFunctionName(name: string, phase: string) {
|
||||
return `animation_${name}_${phase}`;
|
||||
}
|
@ -6,14 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '../../core';
|
||||
import {ViewEncapsulation} from '../../core';
|
||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import * as t from '../r3_ast';
|
||||
import {R3DependencyMetadata} from '../r3_factory';
|
||||
|
||||
|
||||
/**
|
||||
* Information needed to compile a directive for the render3 runtime.
|
||||
*/
|
||||
@ -185,19 +184,14 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||
|
||||
/**
|
||||
* Whether translation variable name should contain external message id
|
||||
* (used by Closure Compiler's output of `goog.getMsg` for transition period).
|
||||
* (used by Closure Compiler's output of `goog.getMsg` for transition period)
|
||||
*/
|
||||
i18nUseExternalIds: boolean;
|
||||
|
||||
/**
|
||||
* Overrides the default interpolation start and end delimiters ({{ and }}).
|
||||
* Overrides the default interpolation start and end delimiters ({{ and }})
|
||||
*/
|
||||
interpolation: InterpolationConfig;
|
||||
|
||||
/**
|
||||
* Strategy used for detecting changes in the component.
|
||||
*/
|
||||
changeDetection?: ChangeDetectionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
|
||||
import {BindingForm, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
|
||||
import {AST, ParsedEvent} from '../../expression_parser/ast';
|
||||
import {LifecycleHooks} from '../../lifecycle_reflector';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
@ -25,10 +25,10 @@ import {OutputContext, error} from '../../util';
|
||||
import {compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
|
||||
import {Identifiers as R3} from '../r3_identifiers';
|
||||
import {Render3ParseResult} from '../r3_template_transform';
|
||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
||||
import {typeWithParameters} from '../util';
|
||||
|
||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||
import {StylingBuilder, StylingInstruction} from './styling';
|
||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
|
||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
||||
|
||||
@ -251,7 +251,6 @@ export function compileComponentFromMetadata(
|
||||
|
||||
const directivesUsed = new Set<o.Expression>();
|
||||
const pipesUsed = new Set<o.Expression>();
|
||||
const changeDetection = meta.changeDetection;
|
||||
|
||||
const template = meta.template;
|
||||
const templateBuilder = new TemplateDefinitionBuilder(
|
||||
@ -314,11 +313,6 @@ export function compileComponentFromMetadata(
|
||||
'data', o.literalMap([{key: 'animation', value: meta.animations, quoted: false}]));
|
||||
}
|
||||
|
||||
// Only set the change detection flag if it's defined and it's not the default.
|
||||
if (changeDetection != null && changeDetection !== core.ChangeDetectionStrategy.Default) {
|
||||
definitionMap.set('changeDetection', o.literal(changeDetection));
|
||||
}
|
||||
|
||||
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
||||
// string literal, which must be on one line.
|
||||
const selectorForType = (meta.selector || '').replace(/\n/g, '');
|
||||
@ -697,7 +691,7 @@ function createHostBindingsFunction(
|
||||
const value = binding.expression.visit(valueConverter);
|
||||
const bindingExpr = bindingFn(bindingContext, value);
|
||||
|
||||
const {bindingName, instruction, extraParams} = getBindingNameAndInstruction(binding);
|
||||
const {bindingName, instruction, extraParams} = getBindingNameAndInstruction(name);
|
||||
|
||||
const instructionParams: o.Expression[] = [
|
||||
elVarExp, o.literal(bindingName), o.importExpr(R3.bind).callFn([bindingExpr.currValExpr])
|
||||
@ -709,35 +703,16 @@ function createHostBindingsFunction(
|
||||
}
|
||||
}
|
||||
|
||||
if (styleBuilder.hasBindingsOrInitialValues()) {
|
||||
// since we're dealing with directives here and directives have a hostBinding
|
||||
// function, we need to generate special instructions that deal with styling
|
||||
// (both bindings and initial values). The instruction below will instruct
|
||||
// all initial styling (styling that is inside of a host binding within a
|
||||
// directive) to be attached to the host element of the directive.
|
||||
const hostAttrsInstruction =
|
||||
styleBuilder.buildDirectiveHostAttrsInstruction(null, constantPool);
|
||||
if (hostAttrsInstruction) {
|
||||
createStatements.push(createStylingStmt(hostAttrsInstruction, bindingContext, bindingFn));
|
||||
if (styleBuilder.hasBindingsOrInitialValues) {
|
||||
const createInstruction = styleBuilder.buildCreateLevelInstruction(null, constantPool);
|
||||
if (createInstruction) {
|
||||
const createStmt = createStylingStmt(createInstruction, bindingContext, bindingFn);
|
||||
createStatements.push(createStmt);
|
||||
}
|
||||
|
||||
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
|
||||
// MUST be registered on a given element within the component/directive
|
||||
// templateFn/hostBindingsFn functions. The instruction below will figure out
|
||||
// what all the bindings are and then generate the statements required to register
|
||||
// those bindings to the element via `elementStyling`.
|
||||
const elementStylingInstruction =
|
||||
styleBuilder.buildElementStylingInstruction(null, constantPool);
|
||||
if (elementStylingInstruction) {
|
||||
createStatements.push(
|
||||
createStylingStmt(elementStylingInstruction, bindingContext, bindingFn));
|
||||
}
|
||||
|
||||
// finally each binding that was registered in the statement above will need to be added to
|
||||
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
||||
// are evaluated and updated for the element.
|
||||
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => {
|
||||
updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn));
|
||||
const updateStmt = createStylingStmt(instruction, bindingContext, bindingFn);
|
||||
updateStatements.push(updateStmt);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -775,9 +750,8 @@ function createStylingStmt(
|
||||
.toStmt();
|
||||
}
|
||||
|
||||
function getBindingNameAndInstruction(binding: ParsedProperty):
|
||||
function getBindingNameAndInstruction(bindingName: string):
|
||||
{bindingName: string, instruction: o.ExternalReference, extraParams: o.Expression[]} {
|
||||
let bindingName = binding.name;
|
||||
let instruction !: o.ExternalReference;
|
||||
const extraParams: o.Expression[] = [];
|
||||
|
||||
@ -787,15 +761,7 @@ function getBindingNameAndInstruction(binding: ParsedProperty):
|
||||
bindingName = attrMatches[1];
|
||||
instruction = R3.elementAttribute;
|
||||
} else {
|
||||
if (binding.isAnimation) {
|
||||
bindingName = prepareSyntheticPropertyName(bindingName);
|
||||
// host bindings that have a synthetic property (e.g. @foo) should always be rendered
|
||||
// in the context of the component and not the parent. Therefore there is a special
|
||||
// compatibility instruction available for this purpose.
|
||||
instruction = R3.componentHostSyntheticProperty;
|
||||
} else {
|
||||
instruction = R3.elementProperty;
|
||||
}
|
||||
instruction = R3.elementProperty;
|
||||
extraParams.push(
|
||||
o.literal(null), // TODO: This should be a sanitizer fn (FW-785)
|
||||
o.literal(true) // host bindings must have nativeOnly prop set to true
|
||||
@ -811,19 +777,14 @@ function createHostListeners(
|
||||
return eventBindings.map(binding => {
|
||||
const bindingExpr = convertActionBinding(
|
||||
null, bindingContext, binding.handler, 'b', () => error('Unexpected interpolation'));
|
||||
let bindingName = binding.name && sanitizeIdentifier(binding.name);
|
||||
let bindingFnName = bindingName;
|
||||
if (binding.type === ParsedEventType.Animation) {
|
||||
bindingFnName = prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase);
|
||||
bindingName = prepareSyntheticListenerName(bindingName, binding.targetOrPhase);
|
||||
}
|
||||
const bindingName = binding.name && sanitizeIdentifier(binding.name);
|
||||
const typeName = meta.name;
|
||||
const functionName =
|
||||
typeName && bindingName ? `${typeName}_${bindingFnName}_HostBindingHandler` : null;
|
||||
typeName && bindingName ? `${typeName}_${bindingName}_HostBindingHandler` : null;
|
||||
const handler = o.fn(
|
||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...bindingExpr.render3Stmts], o.INFERRED_TYPE,
|
||||
null, functionName);
|
||||
return o.importExpr(R3.listener).callFn([o.literal(bindingName), handler]).toStmt();
|
||||
return o.importExpr(R3.listener).callFn([o.literal(binding.name), handler]).toStmt();
|
||||
});
|
||||
}
|
||||
|
||||
@ -846,24 +807,30 @@ function typeMapToExpressionMap(
|
||||
return new Map(entries);
|
||||
}
|
||||
|
||||
const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
|
||||
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
||||
|
||||
// Represents the groups in the above regex.
|
||||
const enum HostBindingGroup {
|
||||
// group 1: "prop" from "[prop]", or "attr.role" from "[attr.role]", or @anim from [@anim]
|
||||
// group 1: "prop" from "[prop]", or "attr.role" from "[attr.role]"
|
||||
Binding = 1,
|
||||
|
||||
// group 2: "event" from "(event)"
|
||||
Event = 2,
|
||||
|
||||
// group 3: "@trigger" from "@trigger"
|
||||
Animation = 3,
|
||||
}
|
||||
|
||||
export function parseHostBindings(host: {[key: string]: string}): {
|
||||
attributes: {[key: string]: string},
|
||||
listeners: {[key: string]: string},
|
||||
properties: {[key: string]: string},
|
||||
animations: {[key: string]: string},
|
||||
} {
|
||||
const attributes: {[key: string]: string} = {};
|
||||
const listeners: {[key: string]: string} = {};
|
||||
const properties: {[key: string]: string} = {};
|
||||
const animations: {[key: string]: string} = {};
|
||||
|
||||
Object.keys(host).forEach(key => {
|
||||
const value = host[key];
|
||||
@ -871,16 +838,15 @@ export function parseHostBindings(host: {[key: string]: string}): {
|
||||
if (matches === null) {
|
||||
attributes[key] = value;
|
||||
} else if (matches[HostBindingGroup.Binding] != null) {
|
||||
// synthetic properties (the ones that have a `@` as a prefix)
|
||||
// are still treated the same as regular properties. Therefore
|
||||
// there is no point in storing them in a separate map.
|
||||
properties[matches[HostBindingGroup.Binding]] = value;
|
||||
} else if (matches[HostBindingGroup.Event] != null) {
|
||||
listeners[matches[HostBindingGroup.Event]] = value;
|
||||
} else if (matches[HostBindingGroup.Animation] != null) {
|
||||
animations[matches[HostBindingGroup.Animation]] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return {attributes, listeners, properties};
|
||||
return {attributes, listeners, properties, animations};
|
||||
}
|
||||
|
||||
function compileStyles(styles: string[], selector: string, hostSelector: string): string[] {
|
||||
|
@ -23,15 +23,10 @@ const enum Char {
|
||||
*
|
||||
* @param value string representation of style as used in the `style` attribute in HTML.
|
||||
* Example: `color: red; height: auto`.
|
||||
* @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
|
||||
* 'auto']`
|
||||
* @returns an object literal. `{ color: 'red', height: 'auto'}`.
|
||||
*/
|
||||
export function parse(value: string): string[] {
|
||||
// we use a string array here instead of a string map
|
||||
// because a string-map is not guaranteed to retain the
|
||||
// order of the entries whereas a string array can be
|
||||
// construted in a [key, value, key, value] format.
|
||||
const styles: string[] = [];
|
||||
export function parse(value: string): {[key: string]: any} {
|
||||
const styles: {[key: string]: any} = {};
|
||||
|
||||
let i = 0;
|
||||
let parenDepth = 0;
|
||||
@ -77,7 +72,7 @@ export function parse(value: string): string[] {
|
||||
case Char.Semicolon:
|
||||
if (currentProp && valueStart > 0 && parenDepth === 0 && quote === Char.QuoteNone) {
|
||||
const styleVal = value.substring(valueStart, i - 1).trim();
|
||||
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
||||
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
|
||||
propStart = i;
|
||||
valueStart = 0;
|
||||
currentProp = null;
|
||||
@ -89,7 +84,7 @@ export function parse(value: string): string[] {
|
||||
|
||||
if (currentProp && valueStart) {
|
||||
const styleVal = value.substr(valueStart).trim();
|
||||
styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
||||
styles[currentProp] = valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal;
|
||||
}
|
||||
|
||||
return styles;
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ConstantPool} from '../../constant_pool';
|
||||
import {AttributeMarker} from '../../core';
|
||||
import {AST, BindingType} from '../../expression_parser/ast';
|
||||
import {InitialStylingFlags} from '../../core';
|
||||
import {AST, BindingType, ParseSpan} from '../../expression_parser/ast';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import * as t from '../r3_ast';
|
||||
@ -40,10 +40,6 @@ interface BoundStylingEntry {
|
||||
/**
|
||||
* Produces creation/update instructions for all styling bindings (class and style)
|
||||
*
|
||||
* It also produces the creation instruction to register all initial styling values
|
||||
* (which are all the static class="..." and style="..." attribute values that exist
|
||||
* on an element within a template).
|
||||
*
|
||||
* The builder class below handles producing instructions for the following cases:
|
||||
*
|
||||
* - Static style/class attributes (style="..." and class="...")
|
||||
@ -67,57 +63,25 @@ interface BoundStylingEntry {
|
||||
* The creation/update methods within the builder class produce these instructions.
|
||||
*/
|
||||
export class StylingBuilder {
|
||||
/** Whether or not there are any static styling values present */
|
||||
private _hasInitialValues = false;
|
||||
/**
|
||||
* Whether or not there are any styling bindings present
|
||||
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
||||
*/
|
||||
private _hasBindings = false;
|
||||
public readonly hasBindingsOrInitialValues = false;
|
||||
|
||||
/** the input for [class] (if it exists) */
|
||||
private _classMapInput: BoundStylingEntry|null = null;
|
||||
/** the input for [style] (if it exists) */
|
||||
private _styleMapInput: BoundStylingEntry|null = null;
|
||||
/** an array of each [style.prop] input */
|
||||
private _singleStyleInputs: BoundStylingEntry[]|null = null;
|
||||
/** an array of each [class.name] input */
|
||||
private _singleClassInputs: BoundStylingEntry[]|null = null;
|
||||
private _lastStylingInput: BoundStylingEntry|null = null;
|
||||
|
||||
// maps are used instead of hash maps because a Map will
|
||||
// retain the ordering of the keys
|
||||
|
||||
/**
|
||||
* Represents the location of each style binding in the template
|
||||
* (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
||||
* that `width=0` and `height=1`)
|
||||
*/
|
||||
private _stylesIndex = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* Represents the location of each class binding in the template
|
||||
* (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
||||
* that `big=0` and `hidden=1`)
|
||||
*/
|
||||
private _classesIndex = new Map<string, number>();
|
||||
private _initialStyleValues: string[] = [];
|
||||
private _initialClassValues: string[] = [];
|
||||
|
||||
// certain style properties ALWAYS need sanitization
|
||||
// this is checked each time new styles are encountered
|
||||
private _initialStyleValues: {[propName: string]: string} = {};
|
||||
private _initialClassValues: {[className: string]: boolean} = {};
|
||||
private _useDefaultSanitizer = false;
|
||||
private _applyFnRequired = false;
|
||||
|
||||
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
||||
|
||||
hasBindingsOrInitialValues() { return this._hasBindings || this._hasInitialValues; }
|
||||
|
||||
/**
|
||||
* Registers a given input to the styling builder to be later used when producing AOT code.
|
||||
*
|
||||
* The code below will only accept the input if it is somehow tied to styling (whether it be
|
||||
* style/class bindings or static style/class attributes).
|
||||
*/
|
||||
registerBoundInput(input: t.BoundAttribute): boolean {
|
||||
// [attr.style] or [attr.class] are skipped in the code below,
|
||||
// they should not be treated as styling-based bindings since
|
||||
@ -153,12 +117,14 @@ export class StylingBuilder {
|
||||
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
||||
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(propertyName);
|
||||
registerIntoMap(this._stylesIndex, propertyName);
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
} else {
|
||||
this._useDefaultSanitizer = true;
|
||||
this._styleMapInput = entry;
|
||||
}
|
||||
this._lastStylingInput = entry;
|
||||
this._hasBindings = true;
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
this._applyFnRequired = true;
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -167,152 +133,107 @@ export class StylingBuilder {
|
||||
const entry = { name: className, value, sourceSpan } as BoundStylingEntry;
|
||||
if (className) {
|
||||
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
registerIntoMap(this._classesIndex, className);
|
||||
} else {
|
||||
this._classMapInput = entry;
|
||||
}
|
||||
this._lastStylingInput = entry;
|
||||
this._hasBindings = true;
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
this._applyFnRequired = true;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the element's static style string value to the builder.
|
||||
*
|
||||
* @param value the style string (e.g. `width:100px; height:200px;`)
|
||||
*/
|
||||
registerStyleAttr(value: string) {
|
||||
this._initialStyleValues = parseStyle(value);
|
||||
this._hasInitialValues = true;
|
||||
Object.keys(this._initialStyleValues).forEach(prop => {
|
||||
registerIntoMap(this._stylesIndex, prop);
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the element's static class string value to the builder.
|
||||
*
|
||||
* @param value the className string (e.g. `disabled gold zoom`)
|
||||
*/
|
||||
registerClassAttr(value: string) {
|
||||
this._initialClassValues = value.trim().split(/\s+/g);
|
||||
this._hasInitialValues = true;
|
||||
this._initialClassValues = {};
|
||||
value.split(/\s+/g).forEach(className => {
|
||||
this._initialClassValues[className] = true;
|
||||
registerIntoMap(this._classesIndex, className);
|
||||
(this as any).hasBindingsOrInitialValues = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends all styling-related expressions to the provided attrs array.
|
||||
*
|
||||
* @param attrs an existing array where each of the styling expressions
|
||||
* will be inserted into.
|
||||
*/
|
||||
populateInitialStylingAttrs(attrs: o.Expression[]): void {
|
||||
// [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
|
||||
if (this._initialClassValues.length) {
|
||||
attrs.push(o.literal(AttributeMarker.Classes));
|
||||
for (let i = 0; i < this._initialClassValues.length; i++) {
|
||||
attrs.push(o.literal(this._initialClassValues[i]));
|
||||
private _buildInitExpr(registry: Map<string, number>, initialValues: {[key: string]: any}):
|
||||
o.Expression|null {
|
||||
const exprs: o.Expression[] = [];
|
||||
const nameAndValueExprs: o.Expression[] = [];
|
||||
|
||||
// _c0 = [prop, prop2, prop3, ...]
|
||||
registry.forEach((value, key) => {
|
||||
const keyLiteral = o.literal(key);
|
||||
exprs.push(keyLiteral);
|
||||
const initialValue = initialValues[key];
|
||||
if (initialValue) {
|
||||
nameAndValueExprs.push(keyLiteral, o.literal(initialValue));
|
||||
}
|
||||
});
|
||||
|
||||
if (nameAndValueExprs.length) {
|
||||
// _c0 = [... MARKER ...]
|
||||
exprs.push(o.literal(InitialStylingFlags.VALUES_MODE));
|
||||
// _c0 = [prop, VALUE, prop2, VALUE2, ...]
|
||||
exprs.push(...nameAndValueExprs);
|
||||
}
|
||||
|
||||
// [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
|
||||
if (this._initialStyleValues.length) {
|
||||
attrs.push(o.literal(AttributeMarker.Styles));
|
||||
for (let i = 0; i < this._initialStyleValues.length; i += 2) {
|
||||
attrs.push(
|
||||
o.literal(this._initialStyleValues[i]), o.literal(this._initialStyleValues[i + 1]));
|
||||
}
|
||||
}
|
||||
return exprs.length ? o.literalArr(exprs) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
||||
*
|
||||
* The instruction generation code below is used for producing the AOT statement code which is
|
||||
* responsible for registering initial styles (within a directive hostBindings' creation block)
|
||||
* to the directive host element.
|
||||
*/
|
||||
buildDirectiveHostAttrsInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||
buildCreateLevelInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||
StylingInstruction|null {
|
||||
if (this._hasInitialValues && this._directiveExpr) {
|
||||
return {
|
||||
sourceSpan,
|
||||
reference: R3.elementHostAttrs,
|
||||
buildParams: () => {
|
||||
const attrs: o.Expression[] = [];
|
||||
this.populateInitialStylingAttrs(attrs);
|
||||
return [this._directiveExpr !, getConstantLiteralFromArray(constantPool, attrs)];
|
||||
if (this.hasBindingsOrInitialValues) {
|
||||
const initialClasses = this._buildInitExpr(this._classesIndex, this._initialClassValues);
|
||||
const initialStyles = this._buildInitExpr(this._stylesIndex, this._initialStyleValues);
|
||||
|
||||
// in the event that a [style] binding is used then sanitization will
|
||||
// always be imported because it is not possible to know ahead of time
|
||||
// whether style bindings will use or not use any sanitizable properties
|
||||
// that isStyleSanitizable() will detect
|
||||
const useSanitizer = this._useDefaultSanitizer;
|
||||
const params: (o.Expression)[] = [];
|
||||
|
||||
if (initialClasses) {
|
||||
// the template compiler handles initial class styling (e.g. class="foo") values
|
||||
// in a special command called `elementClass` so that the initial class
|
||||
// can be processed during runtime. These initial class values are bound to
|
||||
// a constant because the inital class values do not change (since they're static).
|
||||
params.push(constantPool.getConstLiteral(initialClasses, true));
|
||||
} else if (initialStyles || useSanitizer || this._directiveExpr) {
|
||||
// no point in having an extra `null` value unless there are follow-up params
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
|
||||
if (initialStyles) {
|
||||
// the template compiler handles initial style (e.g. style="foo") values
|
||||
// in a special command called `elementStyle` so that the initial styles
|
||||
// can be processed during runtime. These initial styles values are bound to
|
||||
// a constant because the inital style values do not change (since they're static).
|
||||
params.push(constantPool.getConstLiteral(initialStyles, true));
|
||||
} else if (useSanitizer || this._directiveExpr) {
|
||||
// no point in having an extra `null` value unless there are follow-up params
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
|
||||
if (useSanitizer || this._directiveExpr) {
|
||||
params.push(useSanitizer ? o.importExpr(R3.defaultStyleSanitizer) : o.NULL_EXPR);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {sourceSpan, reference: R3.elementStyling, buildParams: () => params};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instruction with all the expressions and parameters for `elementStyling`.
|
||||
*
|
||||
* The instruction generation code below is used for producing the AOT statement code which is
|
||||
* responsible for registering style/class bindings to an element.
|
||||
*/
|
||||
buildElementStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||
StylingInstruction|null {
|
||||
if (this._hasBindings) {
|
||||
return {
|
||||
sourceSpan,
|
||||
reference: R3.elementStyling,
|
||||
buildParams: () => {
|
||||
// a string array of every style-based binding
|
||||
const styleBindingProps =
|
||||
this._singleStyleInputs ? this._singleStyleInputs.map(i => o.literal(i.name)) : [];
|
||||
// a string array of every class-based binding
|
||||
const classBindingNames =
|
||||
this._singleClassInputs ? this._singleClassInputs.map(i => o.literal(i.name)) : [];
|
||||
|
||||
// to salvage space in the AOT generated code, there is no point in passing
|
||||
// in `null` into a param if any follow-up params are not used. Therefore,
|
||||
// only when a trailing param is used then it will be filled with nulls in between
|
||||
// (otherwise a shorter amount of params will be filled). The code below helps
|
||||
// determine how many params are required in the expression code.
|
||||
//
|
||||
// min params => elementStyling()
|
||||
// max params => elementStyling(classBindings, styleBindings, sanitizer, directive)
|
||||
let expectedNumberOfArgs = 0;
|
||||
if (this._directiveExpr) {
|
||||
expectedNumberOfArgs = 4;
|
||||
} else if (this._useDefaultSanitizer) {
|
||||
expectedNumberOfArgs = 3;
|
||||
} else if (styleBindingProps.length) {
|
||||
expectedNumberOfArgs = 2;
|
||||
} else if (classBindingNames.length) {
|
||||
expectedNumberOfArgs = 1;
|
||||
}
|
||||
|
||||
const params: o.Expression[] = [];
|
||||
addParam(
|
||||
params, classBindingNames.length > 0,
|
||||
getConstantLiteralFromArray(constantPool, classBindingNames), 1,
|
||||
expectedNumberOfArgs);
|
||||
addParam(
|
||||
params, styleBindingProps.length > 0,
|
||||
getConstantLiteralFromArray(constantPool, styleBindingProps), 2,
|
||||
expectedNumberOfArgs);
|
||||
addParam(
|
||||
params, this._useDefaultSanitizer, o.importExpr(R3.defaultStyleSanitizer), 3,
|
||||
expectedNumberOfArgs);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instruction with all the expressions and parameters for `elementStylingMap`.
|
||||
*
|
||||
* The instruction data will contain all expressions for `elementStylingMap` to function
|
||||
* which include the `[style]` and `[class]` expression params (if they exist) as well as
|
||||
* the sanitizer and directive reference expression.
|
||||
*/
|
||||
buildElementStylingMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
|
||||
private _buildStylingMap(valueConverter: ValueConverter): StylingInstruction|null {
|
||||
if (this._classMapInput || this._styleMapInput) {
|
||||
const stylingInput = this._classMapInput ! || this._styleMapInput !;
|
||||
|
||||
@ -411,20 +332,18 @@ export class StylingBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs all instructions which contain the expressions that will be placed
|
||||
* into the update block of a template function or a directive hostBindings function.
|
||||
*/
|
||||
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
||||
const instructions: StylingInstruction[] = [];
|
||||
if (this._hasBindings) {
|
||||
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
|
||||
if (this.hasBindingsOrInitialValues) {
|
||||
const mapInstruction = this._buildStylingMap(valueConverter);
|
||||
if (mapInstruction) {
|
||||
instructions.push(mapInstruction);
|
||||
}
|
||||
instructions.push(...this._buildStyleInputs(valueConverter));
|
||||
instructions.push(...this._buildClassInputs(valueConverter));
|
||||
instructions.push(this._buildApplyFn());
|
||||
if (this._applyFnRequired) {
|
||||
instructions.push(this._buildApplyFn());
|
||||
}
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
@ -444,26 +363,3 @@ function isStyleSanitizable(prop: string): boolean {
|
||||
return prop === 'background-image' || prop === 'background' || prop === 'border-image' ||
|
||||
prop === 'filter' || prop === 'list-style' || prop === 'list-style-image';
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple helper function to either provide the constant literal that will house the value
|
||||
* here or a null value if the provided values are empty.
|
||||
*/
|
||||
function getConstantLiteralFromArray(
|
||||
constantPool: ConstantPool, values: o.Expression[]): o.Expression {
|
||||
return values.length ? constantPool.getConstLiteral(o.literalArr(values), true) : o.NULL_EXPR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple helper function that adds a parameter or does nothing at all depending on the provided
|
||||
* predicate and totalExpectedArgs values
|
||||
*/
|
||||
function addParam(
|
||||
params: o.Expression[], predicate: boolean, value: o.Expression, argNumber: number,
|
||||
totalExpectedArgs: number) {
|
||||
if (predicate) {
|
||||
params.push(value);
|
||||
} else if (argNumber < totalExpectedArgs) {
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import {flatten, sanitizeIdentifier} from '../../compile_metadata';
|
||||
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||
import {ConstantPool} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {AST, ASTWithSource, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEvent, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
||||
import {Lexer} from '../../expression_parser/lexer';
|
||||
import {Parser} from '../../expression_parser/parser';
|
||||
import * as i18n from '../../i18n/i18n_ast';
|
||||
@ -29,14 +29,13 @@ import {error} from '../../util';
|
||||
import * as t from '../r3_ast';
|
||||
import {Identifiers as R3} from '../r3_identifiers';
|
||||
import {htmlAstToRender3Ast} from '../r3_template_transform';
|
||||
import {getSyntheticPropertyName, prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName} from '../util';
|
||||
|
||||
import {R3QueryMetadata} from './api';
|
||||
import {I18nContext} from './i18n/context';
|
||||
import {I18nMetaVisitor} from './i18n/meta';
|
||||
import {getSerializedI18nContent} from './i18n/serializer';
|
||||
import {I18N_ICU_MAPPING_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||
import {StylingBuilder, StylingInstruction} from './styling';
|
||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
||||
|
||||
function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined {
|
||||
@ -115,10 +114,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// Selectors found in the <ng-content> tags in the template.
|
||||
private _ngContentSelectors: string[] = [];
|
||||
|
||||
// Number of non-default selectors found in all parent templates of this template. We need to
|
||||
// track it to properly adjust projection bucket index in the `projection` instruction.
|
||||
private _ngContentSelectorsOffset = 0;
|
||||
|
||||
constructor(
|
||||
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
||||
private contextName: string|null, private i18nContext: I18nContext|null,
|
||||
@ -171,11 +166,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
});
|
||||
}
|
||||
|
||||
buildTemplateFunction(
|
||||
nodes: t.Node[], variables: t.Variable[], ngContentSelectorsOffset: number = 0,
|
||||
i18n?: i18n.AST): o.FunctionExpr {
|
||||
this._ngContentSelectorsOffset = ngContentSelectorsOffset;
|
||||
|
||||
buildTemplateFunction(nodes: t.Node[], variables: t.Variable[], i18n?: i18n.AST): o.FunctionExpr {
|
||||
if (this._namespace !== R3.namespaceHTML) {
|
||||
this.creationInstruction(null, this._namespace);
|
||||
}
|
||||
@ -201,23 +192,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
||||
t.visitAll(this, nodes);
|
||||
|
||||
// Add total binding count to pure function count so pure function instructions are
|
||||
// generated with the correct slot offset when update instructions are processed.
|
||||
this._pureFunctionSlots += this._bindingSlots;
|
||||
|
||||
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
||||
// `pipeBind` update instructions), so we have to update the slot offsets manually
|
||||
// to account for bindings.
|
||||
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
||||
|
||||
// Nested templates must be processed before creation instructions so template()
|
||||
// instructions can be generated with the correct internal const count.
|
||||
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
||||
|
||||
// Output the `projectionDef` instruction when some `<ng-content>` are present.
|
||||
// The `projectionDef` instruction only emitted for the component template and it is skipped for
|
||||
// nested templates (<ng-template> tags).
|
||||
if (this.level === 0 && this._hasNgContent) {
|
||||
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
|
||||
if (this._hasNgContent) {
|
||||
const parameters: o.Expression[] = [];
|
||||
|
||||
// Only selectors with a non-default value are generated
|
||||
@ -236,6 +212,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
this.creationInstruction(null, R3.projectionDef, parameters, /* prepend */ true);
|
||||
}
|
||||
|
||||
// Add total binding count to pure function count so pure function instructions are
|
||||
// generated with the correct slot offset when update instructions are processed.
|
||||
this._pureFunctionSlots += this._bindingSlots;
|
||||
|
||||
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
||||
// `pipeBind` update instructions), so we have to update the slot offsets manually
|
||||
// to account for bindings.
|
||||
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
||||
|
||||
// Nested templates must be processed before creation instructions so template()
|
||||
// instructions can be generated with the correct internal const count.
|
||||
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
||||
|
||||
if (initI18nContext) {
|
||||
this.i18nEnd(null, selfClosingI18nInstruction);
|
||||
}
|
||||
@ -430,7 +419,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const slot = this.allocateDataSlot();
|
||||
let selectorIndex = ngContent.selector === DEFAULT_NG_CONTENT_SELECTOR ?
|
||||
0 :
|
||||
this._ngContentSelectors.push(ngContent.selector) + this._ngContentSelectorsOffset;
|
||||
this._ngContentSelectors.push(ngContent.selector);
|
||||
const parameters: o.Expression[] = [o.literal(slot)];
|
||||
|
||||
const attributeAsList: string[] = [];
|
||||
@ -491,9 +480,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const {name, value} = attr;
|
||||
if (name === NON_BINDABLE_ATTR) {
|
||||
isNonBindableMode = true;
|
||||
} else if (name === 'style') {
|
||||
} else if (name == 'style') {
|
||||
stylingBuilder.registerStyleAttr(value);
|
||||
} else if (name === 'class') {
|
||||
} else if (name == 'class') {
|
||||
stylingBuilder.registerClassAttr(value);
|
||||
} else if (attr.i18n) {
|
||||
i18nAttrs.push(attr);
|
||||
@ -517,7 +506,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
element.inputs.forEach((input: t.BoundAttribute) => {
|
||||
if (!stylingBuilder.registerBoundInput(input)) {
|
||||
if (input.type === BindingType.Property) {
|
||||
if (input.type == BindingType.Property) {
|
||||
if (input.i18n) {
|
||||
i18nAttrs.push(input);
|
||||
} else {
|
||||
@ -533,8 +522,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
// this will build the instructions so that they fall into the following syntax
|
||||
// add attributes for directive matching purposes
|
||||
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(
|
||||
allOtherInputs, element.outputs, stylingBuilder));
|
||||
attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(allOtherInputs, element.outputs));
|
||||
parameters.push(this.toAttrsParam(attributes));
|
||||
|
||||
// local refs (ex.: <div #foo #bar="baz">)
|
||||
@ -564,11 +552,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
return element.children.length > 0;
|
||||
};
|
||||
|
||||
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues() &&
|
||||
const createSelfClosingInstruction = !stylingBuilder.hasBindingsOrInitialValues &&
|
||||
!isNgContainer && element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren();
|
||||
|
||||
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
|
||||
!stylingBuilder.hasBindingsOrInitialValues() && hasTextChildrenOnly(element.children);
|
||||
!stylingBuilder.hasBindingsOrInitialValues && hasTextChildrenOnly(element.children);
|
||||
|
||||
if (createSelfClosingInstruction) {
|
||||
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
|
||||
@ -618,29 +606,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
}
|
||||
|
||||
// The style bindings code is placed into two distinct blocks within the template function AOT
|
||||
// code: creation and update. The creation code contains the `elementStyling` instructions
|
||||
// which will apply the collected binding values to the element. `elementStyling` is
|
||||
// designed to run inside of `elementStart` and `elementEnd`. The update instructions
|
||||
// (things like `elementStyleProp`, `elementClassProp`, etc..) are applied later on in this
|
||||
// file
|
||||
// initial styling for static style="..." and class="..." attributes
|
||||
this.processStylingInstruction(
|
||||
implicit,
|
||||
stylingBuilder.buildElementStylingInstruction(element.sourceSpan, this.constantPool),
|
||||
true);
|
||||
stylingBuilder.buildCreateLevelInstruction(element.sourceSpan, this.constantPool), true);
|
||||
|
||||
// Generate Listeners (outputs)
|
||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||
this.creationInstruction(
|
||||
outputAst.sourceSpan, R3.listener,
|
||||
this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
||||
this.prepareListenerParameter(element.name, outputAst));
|
||||
});
|
||||
}
|
||||
|
||||
// the code here will collect all update-level styling instructions and add them to the
|
||||
// update block of the template function AOT code. Instructions like `elementStyleProp`,
|
||||
// `elementStylingMap`, `elementClassProp` and `elementStylingApply` are all generated
|
||||
// and assign in the code below.
|
||||
stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => {
|
||||
this.processStylingInstruction(implicit, instruction, false);
|
||||
});
|
||||
@ -652,12 +630,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
// setProperty without a value doesn't make any sense
|
||||
if (value.name || value.value) {
|
||||
const bindingName = prepareSyntheticPropertyName(input.name);
|
||||
this.allocateBindingSlots(value);
|
||||
const name = prepareSyntheticAttributeName(input.name);
|
||||
this.updateInstruction(input.sourceSpan, R3.elementProperty, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(bindingName),
|
||||
this.convertPropertyBinding(implicit, value)
|
||||
o.literal(elementIndex), o.literal(name), this.convertPropertyBinding(implicit, value)
|
||||
];
|
||||
});
|
||||
}
|
||||
@ -709,8 +686,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
|
||||
const tagName = sanitizeIdentifier(template.tagName || '');
|
||||
const contextName = `${tagName ? this.contextName + '_' + tagName : ''}_${templateIndex}`;
|
||||
const templateName = `${contextName}_Template`;
|
||||
const contextName = tagName ? `${this.contextName}_${tagName}` : '';
|
||||
const templateName =
|
||||
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
|
||||
|
||||
const parameters: o.Expression[] = [
|
||||
o.literal(templateIndex),
|
||||
@ -734,7 +712,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
parameters.push(o.importExpr(R3.templateRefExtractor));
|
||||
}
|
||||
|
||||
// handle property bindings e.g. p(1, 'ngForOf', ɵbind(ctx.items));
|
||||
// handle property bindings e.g. p(1, 'forOf', ɵbind(ctx.items));
|
||||
const context = o.variable(CONTEXT_NAME);
|
||||
template.inputs.forEach(input => {
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
@ -757,16 +735,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// Nested templates must not be visited until after their parent templates have completed
|
||||
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
||||
// be able to support bindings in nested templates to local refs that occur after the
|
||||
// template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
|
||||
// template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
|
||||
this._nestedTemplateFns.push(() => {
|
||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
|
||||
template.children, template.variables,
|
||||
this._ngContentSelectors.length + this._ngContentSelectorsOffset, template.i18n);
|
||||
template.children, template.variables, template.i18n);
|
||||
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||
if (templateVisitor._hasNgContent) {
|
||||
this._hasNgContent = true;
|
||||
this._ngContentSelectors.push(...templateVisitor._ngContentSelectors);
|
||||
}
|
||||
});
|
||||
|
||||
// e.g. template(1, MyComp_Template_1)
|
||||
@ -781,7 +754,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
template.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||
this.creationInstruction(
|
||||
outputAst.sourceSpan, R3.listener,
|
||||
this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
||||
this.prepareListenerParameter('ng_template', outputAst));
|
||||
});
|
||||
}
|
||||
|
||||
@ -946,51 +919,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares all attribute expression values for the `TAttributes` array.
|
||||
*
|
||||
* The purpose of this function is to properly construct an attributes array that
|
||||
* is passed into the `elementStart` (or just `element`) functions. Because there
|
||||
* are many different types of attributes, the array needs to be constructed in a
|
||||
* special way so that `elementStart` can properly evaluate them.
|
||||
*
|
||||
* The format looks like this:
|
||||
*
|
||||
* ```
|
||||
* attrs = [prop, value, prop2, value2,
|
||||
* CLASSES, class1, class2,
|
||||
* STYLES, style1, value1, style2, value2,
|
||||
* SELECT_ONLY, name1, name2, name2, ...]
|
||||
* ```
|
||||
*/
|
||||
private prepareSyntheticAndSelectOnlyAttrs(
|
||||
inputs: t.BoundAttribute[], outputs: t.BoundEvent[],
|
||||
styles?: StylingBuilder): o.Expression[] {
|
||||
private prepareSyntheticAndSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]):
|
||||
o.Expression[] {
|
||||
const attrExprs: o.Expression[] = [];
|
||||
const nonSyntheticInputs: t.BoundAttribute[] = [];
|
||||
const alreadySeen = new Set<string>();
|
||||
|
||||
function isASTWithSource(ast: AST): ast is ASTWithSource {
|
||||
return ast instanceof ASTWithSource;
|
||||
}
|
||||
|
||||
function isLiteralPrimitive(ast: AST): ast is LiteralPrimitive {
|
||||
return ast instanceof LiteralPrimitive;
|
||||
}
|
||||
|
||||
function addAttrExpr(key: string | number, value?: o.Expression): void {
|
||||
if (typeof key === 'string') {
|
||||
if (!alreadySeen.has(key)) {
|
||||
attrExprs.push(o.literal(key));
|
||||
if (value !== undefined) {
|
||||
attrExprs.push(value);
|
||||
}
|
||||
alreadySeen.add(key);
|
||||
}
|
||||
} else {
|
||||
attrExprs.push(o.literal(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.length) {
|
||||
const EMPTY_STRING_EXPR = asLiteral('');
|
||||
@ -1000,34 +932,17 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// may be supported differently in future versions of angular. However,
|
||||
// @triggers should always just be treated as regular attributes (it's up
|
||||
// to the renderer to detect and use them in a special way).
|
||||
const valueExp = input.value;
|
||||
if (isASTWithSource(valueExp)) {
|
||||
const literal = valueExp.ast;
|
||||
if (isLiteralPrimitive(literal) && literal.value === undefined) {
|
||||
addAttrExpr(prepareSyntheticPropertyName(input.name), EMPTY_STRING_EXPR);
|
||||
}
|
||||
}
|
||||
attrExprs.push(asLiteral(prepareSyntheticAttributeName(input.name)), EMPTY_STRING_EXPR);
|
||||
} else {
|
||||
nonSyntheticInputs.push(input);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// it's important that this occurs before SelectOnly because once `elementStart`
|
||||
// comes across the SelectOnly marker then it will continue reading each value as
|
||||
// as single property value cell by cell.
|
||||
if (styles) {
|
||||
styles.populateInitialStylingAttrs(attrExprs);
|
||||
}
|
||||
|
||||
if (nonSyntheticInputs.length || outputs.length) {
|
||||
addAttrExpr(core.AttributeMarker.SelectOnly);
|
||||
nonSyntheticInputs.forEach((i: t.BoundAttribute) => addAttrExpr(i.name));
|
||||
outputs.forEach((o: t.BoundEvent) => {
|
||||
const name =
|
||||
o.type === ParsedEventType.Animation ? getSyntheticPropertyName(o.name) : o.name;
|
||||
addAttrExpr(name);
|
||||
});
|
||||
attrExprs.push(o.literal(core.AttributeMarker.SelectOnly));
|
||||
nonSyntheticInputs.forEach((i: t.BoundAttribute) => attrExprs.push(asLiteral(i.name)));
|
||||
outputs.forEach((o: t.BoundEvent) => attrExprs.push(asLiteral(o.name)));
|
||||
}
|
||||
|
||||
return attrExprs;
|
||||
@ -1067,22 +982,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
return this.constantPool.getConstLiteral(asLiteral(refsParam), true);
|
||||
}
|
||||
|
||||
private prepareListenerParameter(tagName: string, outputAst: t.BoundEvent, index: number):
|
||||
() => o.Expression[] {
|
||||
private prepareListenerParameter(tagName: string, outputAst: t.BoundEvent): () => o.Expression[] {
|
||||
let eventName: string = outputAst.name;
|
||||
|
||||
let bindingFnName;
|
||||
if (outputAst.type === ParsedEventType.Animation) {
|
||||
// synthetic @listener.foo values are treated the exact same as are standard listeners
|
||||
bindingFnName = prepareSyntheticListenerFunctionName(eventName, outputAst.phase !);
|
||||
eventName = prepareSyntheticListenerName(eventName, outputAst.phase !);
|
||||
} else {
|
||||
bindingFnName = sanitizeIdentifier(eventName);
|
||||
eventName = prepareSyntheticAttributeName(`${outputAst.name}.${outputAst.phase}`);
|
||||
}
|
||||
|
||||
const evNameSanitized = sanitizeIdentifier(eventName);
|
||||
const tagNameSanitized = sanitizeIdentifier(tagName);
|
||||
const functionName =
|
||||
`${this.templateName}_${tagNameSanitized}_${bindingFnName}_${index}_listener`;
|
||||
const functionName = `${this.templateName}_${tagNameSanitized}_${evNameSanitized}_listener`;
|
||||
|
||||
return () => {
|
||||
|
||||
const listenerScope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
|
||||
@ -1465,7 +1373,7 @@ function createCssSelector(tag: string, attributes: {[name: string]: string}): C
|
||||
|
||||
cssSelector.addAttribute(name, value);
|
||||
if (name.toLowerCase() === 'class') {
|
||||
const classes = value.trim().split(/\s+/);
|
||||
const classes = value.trim().split(/\s+/g);
|
||||
classes.forEach(className => cssSelector.addClassName(className));
|
||||
}
|
||||
});
|
||||
@ -1574,14 +1482,16 @@ function resolveSanitizationFn(input: t.BoundAttribute, context: core.SecurityCo
|
||||
}
|
||||
}
|
||||
|
||||
function prepareSyntheticAttributeName(name: string) {
|
||||
return '@' + name;
|
||||
}
|
||||
|
||||
function isSingleElementTemplate(children: t.Node[]): children is[t.Element] {
|
||||
return children.length === 1 && children[0] instanceof t.Element;
|
||||
}
|
||||
|
||||
function isTextNode(node: t.Node): boolean {
|
||||
return node instanceof t.Text || node instanceof t.BoundText || node instanceof t.Icu;
|
||||
}
|
||||
|
||||
function hasTextChildrenOnly(children: t.Node[]): boolean {
|
||||
return children.every(isTextNode);
|
||||
return !children.find(
|
||||
child =>
|
||||
!(child instanceof t.Text || child instanceof t.BoundText || child instanceof t.Icu));
|
||||
}
|
||||
|
@ -440,4 +440,4 @@ export function calcPossibleSecurityContexts(
|
||||
elementName => registry.securityContext(elementName, propName, isAttribute)));
|
||||
});
|
||||
return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
|
||||
}
|
||||
}
|
||||
|
@ -10,53 +10,55 @@ import {hyphenate, parse as parseStyle, stripUnnecessaryQuotes} from '../../src/
|
||||
describe('style parsing', () => {
|
||||
it('should parse empty or blank strings', () => {
|
||||
const result1 = parseStyle('');
|
||||
expect(result1).toEqual([]);
|
||||
expect(result1).toEqual({});
|
||||
|
||||
const result2 = parseStyle(' ');
|
||||
expect(result2).toEqual([]);
|
||||
expect(result2).toEqual({});
|
||||
});
|
||||
|
||||
it('should parse a string into a key/value map', () => {
|
||||
const result = parseStyle('width:100px;height:200px;opacity:0');
|
||||
expect(result).toEqual(['width', '100px', 'height', '200px', 'opacity', '0']);
|
||||
expect(result).toEqual({width: '100px', height: '200px', opacity: '0'});
|
||||
});
|
||||
|
||||
it('should trim values and properties', () => {
|
||||
const result = parseStyle('width :333px ; height:666px ; opacity: 0.5;');
|
||||
expect(result).toEqual(['width', '333px', 'height', '666px', 'opacity', '0.5']);
|
||||
expect(result).toEqual({width: '333px', height: '666px', opacity: '0.5'});
|
||||
});
|
||||
|
||||
it('should chomp out start/end quotes', () => {
|
||||
const result = parseStyle(
|
||||
'content: "foo"; opacity: \'0.5\'; font-family: "Verdana", Helvetica, "sans-serif"');
|
||||
expect(result).toEqual(
|
||||
['content', 'foo', 'opacity', '0.5', 'font-family', '"Verdana", Helvetica, "sans-serif"']);
|
||||
{content: 'foo', opacity: '0.5', 'font-family': '"Verdana", Helvetica, "sans-serif"'});
|
||||
});
|
||||
|
||||
it('should not mess up with quoted strings that contain [:;] values', () => {
|
||||
const result = parseStyle('content: "foo; man: guy"; width: 100px');
|
||||
expect(result).toEqual(['content', 'foo; man: guy', 'width', '100px']);
|
||||
expect(result).toEqual({content: 'foo; man: guy', width: '100px'});
|
||||
});
|
||||
|
||||
it('should not mess up with quoted strings that contain inner quote values', () => {
|
||||
const quoteStr = '"one \'two\' three \"four\" five"';
|
||||
const result = parseStyle(`content: ${quoteStr}; width: 123px`);
|
||||
expect(result).toEqual(['content', quoteStr, 'width', '123px']);
|
||||
expect(result).toEqual({content: quoteStr, width: '123px'});
|
||||
});
|
||||
|
||||
it('should respect parenthesis that are placed within a style', () => {
|
||||
const result = parseStyle('background-image: url("foo.jpg")');
|
||||
expect(result).toEqual(['background-image', 'url("foo.jpg")']);
|
||||
expect(result).toEqual({'background-image': 'url("foo.jpg")'});
|
||||
});
|
||||
|
||||
it('should respect multi-level parenthesis that contain special [:;] characters', () => {
|
||||
const result = parseStyle('color: rgba(calc(50 * 4), var(--cool), :5;); height: 100px;');
|
||||
expect(result).toEqual(['color', 'rgba(calc(50 * 4), var(--cool), :5;)', 'height', '100px']);
|
||||
expect(result).toEqual({color: 'rgba(calc(50 * 4), var(--cool), :5;)', height: '100px'});
|
||||
});
|
||||
|
||||
it('should hyphenate style properties from camel case', () => {
|
||||
const result = parseStyle('borderWidth: 200px');
|
||||
expect(result).toEqual(['border-width', '200px']);
|
||||
expect(result).toEqual({
|
||||
'border-width': '200px',
|
||||
});
|
||||
});
|
||||
|
||||
describe('quote chomping', () => {
|
||||
|
@ -83,7 +83,6 @@ export {
|
||||
loadQueryList as ɵloadQueryList,
|
||||
elementEnd as ɵelementEnd,
|
||||
elementProperty as ɵelementProperty,
|
||||
componentHostSyntheticProperty as ɵcomponentHostSyntheticProperty,
|
||||
projectionDef as ɵprojectionDef,
|
||||
reference as ɵreference,
|
||||
enableBindings as ɵenableBindings,
|
||||
@ -93,7 +92,6 @@ export {
|
||||
elementContainerStart as ɵelementContainerStart,
|
||||
elementContainerEnd as ɵelementContainerEnd,
|
||||
elementStyling as ɵelementStyling,
|
||||
elementHostAttrs as ɵelementHostAttrs,
|
||||
elementStylingMap as ɵelementStylingMap,
|
||||
elementStyleProp as ɵelementStyleProp,
|
||||
elementStylingApply as ɵelementStylingApply,
|
||||
|
@ -12,7 +12,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners,
|
||||
import {TNode} from '../render3/interfaces/node';
|
||||
import {StylingIndex} from '../render3/interfaces/styling';
|
||||
import {TVIEW} from '../render3/interfaces/view';
|
||||
import {getProp, getValue, isClassBasedValue} from '../render3/styling/class_and_style_bindings';
|
||||
import {getProp, getValue, isClassBased} from '../render3/styling/class_and_style_bindings';
|
||||
import {getStylingContext} from '../render3/styling/util';
|
||||
import {DebugContext} from '../view/index';
|
||||
|
||||
@ -273,7 +273,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||
if (stylingContext) {
|
||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||
i += StylingIndex.Size) {
|
||||
if (isClassBasedValue(lNode, i)) {
|
||||
if (isClassBased(lNode, i)) {
|
||||
const className = getProp(lNode, i);
|
||||
const value = getValue(lNode, i);
|
||||
if (typeof value == 'boolean') {
|
||||
@ -303,7 +303,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||
if (stylingContext) {
|
||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||
i += StylingIndex.Size) {
|
||||
if (!isClassBasedValue(lNode, i)) {
|
||||
if (!isClassBased(lNode, i)) {
|
||||
const styleName = getProp(lNode, i);
|
||||
const value = getValue(lNode, i) as string | null;
|
||||
if (value !== null) {
|
||||
|
@ -11,7 +11,7 @@ import {devModeEqual} from '../change_detection/change_detection_util';
|
||||
import {assertDataInRange, assertLessThan, assertNotEqual} from './assert';
|
||||
import {throwErrorIfNoChangesMode} from './errors';
|
||||
import {BINDING_INDEX, LView} from './interfaces/view';
|
||||
import {getCheckNoChangesMode, isCreationMode} from './state';
|
||||
import {getCheckNoChangesMode, getCreationMode} from './state';
|
||||
import {NO_CHANGE} from './tokens';
|
||||
import {isDifferent} from './util';
|
||||
|
||||
@ -44,7 +44,7 @@ export function bindingUpdated(lView: LView, bindingIndex: number, value: any):
|
||||
} else if (isDifferent(lView[bindingIndex], value)) {
|
||||
if (ngDevMode && getCheckNoChangesMode()) {
|
||||
if (!devModeEqual(lView[bindingIndex], value)) {
|
||||
throwErrorIfNoChangesMode(isCreationMode(lView), lView[bindingIndex], value);
|
||||
throwErrorIfNoChangesMode(getCreationMode(), lView[bindingIndex], value);
|
||||
}
|
||||
}
|
||||
lView[bindingIndex] = value;
|
||||
|
@ -22,7 +22,7 @@ import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'
|
||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
||||
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
|
||||
|
||||
@ -133,9 +133,7 @@ export function renderComponent<T>(
|
||||
component = createRootComponent(
|
||||
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
|
||||
|
||||
refreshDescendantViews(rootView); // creation mode pass
|
||||
rootView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
refreshDescendantViews(rootView); // update mode pass
|
||||
refreshDescendantViews(rootView, null);
|
||||
} finally {
|
||||
leaveView(oldView);
|
||||
if (rendererFactory.end) rendererFactory.end();
|
||||
|
@ -208,9 +208,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
||||
|
||||
addToViewTree(rootLView, HEADER_OFFSET, componentView);
|
||||
refreshDescendantViews(rootLView);
|
||||
|
||||
refreshDescendantViews(rootLView, RenderFlags.Create);
|
||||
} finally {
|
||||
leaveView(oldLView);
|
||||
leaveView(oldLView, true);
|
||||
if (rendererFactory.end) rendererFactory.end();
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,12 @@
|
||||
*/
|
||||
import './ng_dev_mode';
|
||||
import {assertDomNode} from './assert';
|
||||
import {EMPTY_ARRAY} from './empty';
|
||||
import {EMPTY_ARRAY} from './definition';
|
||||
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
||||
import {TNode, TNodeFlags} from './interfaces/node';
|
||||
import {RElement} from './interfaces/renderer';
|
||||
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
|
||||
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
|
||||
|
||||
import {getComponentViewByIndex, getNativeByTNode, getTNode, readElementValue, readPatchedData} from './util';
|
||||
|
||||
|
||||
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
|
||||
|
@ -9,15 +9,22 @@
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||
import {Provider} from '../di/provider';
|
||||
import {NgModuleDef} from '../metadata/ng_module';
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {Mutable, Type} from '../type';
|
||||
import {noSideEffects, stringify} from '../util';
|
||||
import {EMPTY_ARRAY, EMPTY_OBJ} from './empty';
|
||||
|
||||
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields';
|
||||
import {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
|
||||
import {CssSelectorList} from './interfaces/projection';
|
||||
import {CssSelectorList, SelectorFlags} from './interfaces/projection';
|
||||
|
||||
export const EMPTY: {} = {};
|
||||
export const EMPTY_ARRAY: any[] = [];
|
||||
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
||||
Object.freeze(EMPTY);
|
||||
Object.freeze(EMPTY_ARRAY);
|
||||
}
|
||||
let _renderCompCount = 0;
|
||||
|
||||
/**
|
||||
@ -382,7 +389,7 @@ export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T>>): nev
|
||||
|
||||
*/
|
||||
function invertObject(obj: any, secondary?: any): any {
|
||||
if (obj == null) return EMPTY_OBJ;
|
||||
if (obj == null) return EMPTY;
|
||||
const newLookup: any = {};
|
||||
for (const minifiedKey in obj) {
|
||||
if (obj.hasOwnProperty(minifiedKey)) {
|
||||
|
@ -21,8 +21,7 @@ import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TN
|
||||
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LView, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
|
||||
import {findComponentView, getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, isComponentDef, stringify} from './util';
|
||||
|
||||
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, stringify} from './util';
|
||||
|
||||
/**
|
||||
* Defines if the call to `inject` should include `viewProviders` in its resolution.
|
||||
@ -60,7 +59,7 @@ import {findComponentView, getParentInjectorIndex, getParentInjectorView, hasPar
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
let includeViewProviders = true;
|
||||
let includeViewProviders = false;
|
||||
|
||||
function setIncludeViewProviders(v: boolean): boolean {
|
||||
const oldValue = includeViewProviders;
|
||||
@ -198,7 +197,7 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number {
|
||||
*/
|
||||
export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation {
|
||||
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
||||
return tNode.parent.injectorIndex as any; // ViewOffset is 0
|
||||
return tNode.parent.injectorIndex as any; // ViewOffset is 0, AcrossHostBoundary is 0
|
||||
}
|
||||
|
||||
// For most cases, the parent injector index can be found on the host node (e.g. for component
|
||||
@ -208,12 +207,16 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn
|
||||
let viewOffset = 1;
|
||||
while (hostTNode && hostTNode.injectorIndex === -1) {
|
||||
view = view[DECLARATION_VIEW] !;
|
||||
hostTNode = view ? view[HOST_NODE] : null;
|
||||
hostTNode = view[HOST_NODE] !;
|
||||
viewOffset++;
|
||||
}
|
||||
const acrossHostBoundary = hostTNode && hostTNode.type === TNodeType.Element ?
|
||||
RelativeInjectorLocationFlags.AcrossHostBoundary :
|
||||
0;
|
||||
|
||||
return hostTNode ?
|
||||
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) :
|
||||
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) |
|
||||
acrossHostBoundary :
|
||||
-1 as any;
|
||||
}
|
||||
|
||||
@ -292,86 +295,79 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
|
||||
* @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
|
||||
*/
|
||||
export function getOrCreateInjectable<T>(
|
||||
tNode: TElementNode | TContainerNode | TElementContainerNode | null, lView: LView,
|
||||
tNode: TElementNode | TContainerNode | TElementContainerNode, lView: LView,
|
||||
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default,
|
||||
notFoundValue?: any): T|null {
|
||||
if (tNode) {
|
||||
const bloomHash = bloomHashBitOrFactory(token);
|
||||
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
||||
// so just call the factory function to create it.
|
||||
if (typeof bloomHash === 'function') {
|
||||
const savePreviousOrParentTNode = getPreviousOrParentTNode();
|
||||
const saveLView = getLView();
|
||||
setTNodeAndViewData(tNode, lView);
|
||||
try {
|
||||
const value = bloomHash();
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} finally {
|
||||
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
|
||||
const bloomHash = bloomHashBitOrFactory(token);
|
||||
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
||||
// so just call the factory function to create it.
|
||||
if (typeof bloomHash === 'function') {
|
||||
const savePreviousOrParentTNode = getPreviousOrParentTNode();
|
||||
const saveLView = getLView();
|
||||
setTNodeAndViewData(tNode, lView);
|
||||
try {
|
||||
const value = bloomHash();
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else if (typeof bloomHash == 'number') {
|
||||
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
|
||||
} finally {
|
||||
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
|
||||
}
|
||||
} else if (typeof bloomHash == 'number') {
|
||||
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
|
||||
|
||||
// A reference to the previous injector TView that was found while climbing the element
|
||||
// injector tree. This is used to know if viewProviders can be accessed on the current
|
||||
// injector.
|
||||
let previousTView: TView|null = null;
|
||||
let injectorIndex = getInjectorIndex(tNode, lView);
|
||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||
let hostTElementNode: TNode|null =
|
||||
flags & InjectFlags.Host ? findComponentView(lView)[HOST_NODE] : null;
|
||||
// A reference to the previous injector TView that was found while climbing the element injector
|
||||
// tree. This is used to know if viewProviders can be accessed on the current injector.
|
||||
let previousTView: TView|null = null;
|
||||
let injectorIndex = getInjectorIndex(tNode, lView);
|
||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||
|
||||
// If we should skip this injector, or if there is no injector on this node, start by
|
||||
// searching
|
||||
// the parent injector.
|
||||
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
||||
lView[injectorIndex + PARENT_INJECTOR];
|
||||
// If we should skip this injector, or if there is no injector on this node, start by searching
|
||||
// the parent injector.
|
||||
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
||||
lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
if (!shouldSearchParent(flags, false)) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
previousTView = lView[TVIEW];
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
if (!shouldSearchParent(flags, parentLocation)) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
previousTView = lView[TVIEW];
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse up the injector tree until we find a potential match or until we know there
|
||||
// *isn't* a match.
|
||||
while (injectorIndex !== -1) {
|
||||
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
// Check the current injector. If it matches, see if it contains token.
|
||||
const tView = lView[TVIEW];
|
||||
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
||||
// At this point, we have an injector which *may* contain the token, so we step through
|
||||
// the providers and directives associated with the injector's corresponding node to get
|
||||
// the instance.
|
||||
const instance: T|null =
|
||||
searchTokensOnInjector<T>(injectorIndex, lView, token, previousTView);
|
||||
if (instance !== NOT_FOUND) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse up the injector tree until we find a potential match or until we know there
|
||||
// *isn't* a match.
|
||||
while (injectorIndex !== -1) {
|
||||
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
// Check the current injector. If it matches, see if it contains token.
|
||||
const tView = lView[TVIEW];
|
||||
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
||||
// At this point, we have an injector which *may* contain the token, so we step through
|
||||
// the providers and directives associated with the injector's corresponding node to get
|
||||
// the instance.
|
||||
const instance: T|null = searchTokensOnInjector<T>(
|
||||
injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
||||
if (instance !== NOT_FOUND) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
if (shouldSearchParent(
|
||||
flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) &&
|
||||
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||
// Traverse up the tree and continue searching.
|
||||
previousTView = tView;
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
} else {
|
||||
// If we should not search parent OR If the ancestor bloom filter value does not have the
|
||||
// bit corresponding to the directive we can give up on traversing up to find the specific
|
||||
// injector.
|
||||
injectorIndex = -1;
|
||||
}
|
||||
if (shouldSearchParent(flags, parentLocation) &&
|
||||
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||
// Traverse up the tree and continue searching.
|
||||
previousTView = tView;
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
} else {
|
||||
// If we should not search parent OR If the ancestor bloom filter value does not have the
|
||||
// bit corresponding to the directive we can give up on traversing up to find the specific
|
||||
// injector.
|
||||
injectorIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,7 +396,7 @@ const NOT_FOUND = {};
|
||||
|
||||
function searchTokensOnInjector<T>(
|
||||
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
|
||||
previousTView: TView | null, flags: InjectFlags, hostTElementNode: TNode | null) {
|
||||
previousTView: TView | null) {
|
||||
const currentTView = lView[TVIEW];
|
||||
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
|
||||
// First, we need to determine if view providers can be accessed by the starting element.
|
||||
@ -422,12 +418,7 @@ function searchTokensOnInjector<T>(
|
||||
// into the ViewProviders.
|
||||
(previousTView != currentTView && (tNode.type === TNodeType.Element));
|
||||
|
||||
// This special case happens when there is a @host on the inject and when we are searching
|
||||
// on the host element node.
|
||||
const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
|
||||
|
||||
const injectableIdx =
|
||||
locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders, isHostSpecialCase);
|
||||
const injectableIdx = locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders);
|
||||
if (injectableIdx !== null) {
|
||||
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
|
||||
} else {
|
||||
@ -442,13 +433,13 @@ function searchTokensOnInjector<T>(
|
||||
* @param lView The view we are currently processing
|
||||
* @param token Provider token or type of a directive to look for.
|
||||
* @param canAccessViewProviders Whether view providers should be considered.
|
||||
* @param isHostSpecialCase Whether the host special case applies.
|
||||
* @returns Index of a found directive or provider, or null when none found.
|
||||
*/
|
||||
export function locateDirectiveOrProvider<T>(
|
||||
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>, canAccessViewProviders: boolean,
|
||||
isHostSpecialCase: boolean | number): number|null {
|
||||
tNode: TNode, lView: LView, token: Type<T>| InjectionToken<T>,
|
||||
canAccessViewProviders: boolean): number|null {
|
||||
const tView = lView[TVIEW];
|
||||
const nodeFlags = tNode.flags;
|
||||
const nodeProviderIndexes = tNode.providerIndexes;
|
||||
const tInjectables = tView.data;
|
||||
|
||||
@ -459,21 +450,13 @@ export function locateDirectiveOrProvider<T>(
|
||||
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
||||
const startingIndex =
|
||||
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
||||
// When the host special case applies, only the viewProviders and the component are visible
|
||||
const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
|
||||
for (let i = startingIndex; i < endIndex; i++) {
|
||||
for (let i = startingIndex; i < directiveEnd; i++) {
|
||||
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
|
||||
if (i < directivesStart && token === providerTokenOrDef ||
|
||||
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (isHostSpecialCase) {
|
||||
const dirDef = tInjectables[directivesStart] as DirectiveDef<any>;
|
||||
if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
|
||||
return directivesStart;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -563,8 +546,12 @@ export function bloomHasToken(
|
||||
}
|
||||
|
||||
/** Returns true if flags prevent parent injector from being searched for tokens */
|
||||
function shouldSearchParent(flags: InjectFlags, isFirstHostTNode: boolean): boolean|number {
|
||||
return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
|
||||
function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjectorLocation): boolean|
|
||||
number {
|
||||
return !(
|
||||
flags & InjectFlags.Self ||
|
||||
(flags & InjectFlags.Host &&
|
||||
((parentLocation as any as number) & RelativeInjectorLocationFlags.AcrossHostBoundary)));
|
||||
}
|
||||
|
||||
export function injectInjector() {
|
||||
@ -574,8 +561,7 @@ export function injectInjector() {
|
||||
|
||||
export class NodeInjector implements Injector {
|
||||
constructor(
|
||||
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,
|
||||
private _lView: LView) {}
|
||||
private _tNode: TElementNode|TContainerNode|TElementContainerNode, private _lView: LView) {}
|
||||
|
||||
get(token: any, notFoundValue?: any): any {
|
||||
return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
|
||||
import {NodeInjector} from './di';
|
||||
@ -15,8 +14,7 @@ import {LContext} from './interfaces/context';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
|
||||
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||
import {readElementValue, readPatchedLView, stringify} from './util';
|
||||
|
||||
import {readPatchedLView, stringify} from './util';
|
||||
|
||||
|
||||
/**
|
||||
@ -329,7 +327,7 @@ export function getListeners(element: Element): Listener[] {
|
||||
const secondParam = tCleanup[i++];
|
||||
if (typeof firstParam === 'string') {
|
||||
const name: string = firstParam;
|
||||
const listenerElement = readElementValue(lView[secondParam]) as any as Element;
|
||||
const listenerElement: Element = lView[secondParam];
|
||||
const callback: (value: any) => any = lCleanup[tCleanup[i++]];
|
||||
const useCaptureOrIndx = tCleanup[i++];
|
||||
// if useCaptureOrIndx is boolean then report it as is.
|
||||
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import './ng_dev_mode';
|
||||
|
||||
/**
|
||||
* This file contains reuseable "empty" symbols that can be used as default return values
|
||||
* in different parts of the rendering code. Because the same symbols are returned, this
|
||||
* allows for identity checks against these values to be consistently used by the framework
|
||||
* code.
|
||||
*/
|
||||
|
||||
export const EMPTY_OBJ: {} = {};
|
||||
export const EMPTY_ARRAY: any[] = [];
|
||||
|
||||
// freezing the values prevents any code from accidentally inserting new values in
|
||||
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
||||
Object.freeze(EMPTY_OBJ);
|
||||
Object.freeze(EMPTY_ARRAY);
|
||||
}
|
@ -8,8 +8,8 @@
|
||||
|
||||
import {Type} from '../../type';
|
||||
import {fillProperties} from '../../util/property';
|
||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||
import {ComponentDef, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
||||
import {EMPTY, EMPTY_ARRAY} from '../definition';
|
||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
||||
|
||||
|
||||
|
||||
@ -178,7 +178,7 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
|
||||
function maybeUnwrapEmpty<T>(value: T[]): T[];
|
||||
function maybeUnwrapEmpty<T>(value: T): T;
|
||||
function maybeUnwrapEmpty(value: any): any {
|
||||
if (value === EMPTY_OBJ) {
|
||||
if (value === EMPTY) {
|
||||
return {};
|
||||
} else if (value === EMPTY_ARRAY) {
|
||||
return [];
|
||||
|
@ -93,10 +93,9 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
|
||||
*
|
||||
* @param currentView The current view
|
||||
*/
|
||||
export function executeInitHooks(
|
||||
currentView: LView, tView: TView, checkNoChangesMode: boolean): void {
|
||||
if (!checkNoChangesMode && currentView[FLAGS] & LViewFlags.RunInit) {
|
||||
executeHooks(currentView, tView.initHooks, tView.checkHooks, checkNoChangesMode);
|
||||
export function executeInitHooks(currentView: LView, tView: TView, creationMode: boolean): void {
|
||||
if (currentView[FLAGS] & LViewFlags.RunInit) {
|
||||
executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
|
||||
currentView[FLAGS] &= ~LViewFlags.RunInit;
|
||||
}
|
||||
}
|
||||
@ -107,19 +106,17 @@ export function executeInitHooks(
|
||||
* @param currentView The current view
|
||||
*/
|
||||
export function executeHooks(
|
||||
currentView: LView, allHooks: HookData | null, checkHooks: HookData | null,
|
||||
checkNoChangesMode: boolean): void {
|
||||
if (checkNoChangesMode) return;
|
||||
|
||||
const hooksToCall = currentView[FLAGS] & LViewFlags.FirstLViewPass ? allHooks : checkHooks;
|
||||
data: LView, allHooks: HookData | null, checkHooks: HookData | null,
|
||||
creationMode: boolean): void {
|
||||
const hooksToCall = creationMode ? allHooks : checkHooks;
|
||||
if (hooksToCall) {
|
||||
callHooks(currentView, hooksToCall);
|
||||
callHooks(data, hooksToCall);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
||||
* the first LView pass.
|
||||
* creation mode.
|
||||
*
|
||||
* @param currentView The current view
|
||||
* @param arr The array in which the hooks are found
|
||||
|
@ -25,11 +25,11 @@ import {NO_CHANGE} from './tokens';
|
||||
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
|
||||
|
||||
const MARKER = `<EFBFBD>`;
|
||||
const ICU_BLOCK_REGEX = /^\s*(<28>\d+:?\d*<EFBFBD>)\s*,\s*(select|plural)\s*,/;
|
||||
const ICU_BLOCK_REGEX = /^\s*(<28>\d+<2B>)\s*,\s*(select|plural)\s*,/;
|
||||
const SUBTEMPLATE_REGEXP = /<2F>\/?\*(\d+:\d+)<29>/gi;
|
||||
const PH_REGEXP = /<2F>(\/?[#*]\d+):?\d*<2A>/gi;
|
||||
const BINDING_REGEXP = /<2F>(\d+):?\d*<2A>/gi;
|
||||
const ICU_REGEXP = /({\s*<2A>\d+:?\d*<EFBFBD>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
|
||||
const ICU_REGEXP = /({\s*<2A>\d+<2B>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
|
||||
|
||||
// i18nPostproocess regexps
|
||||
const PP_PLACEHOLDERS = /\[(<28>.+?<3F>?)\]/g;
|
||||
|
@ -44,13 +44,12 @@ export {
|
||||
elementClassProp,
|
||||
elementEnd,
|
||||
elementProperty,
|
||||
componentHostSyntheticProperty,
|
||||
elementStart,
|
||||
|
||||
elementContainerStart,
|
||||
elementContainerEnd,
|
||||
|
||||
elementStyling,
|
||||
elementHostAttrs,
|
||||
elementStylingMap,
|
||||
elementStyleProp,
|
||||
elementStylingApply,
|
||||
@ -80,7 +79,7 @@ export {
|
||||
|
||||
directiveInject,
|
||||
injectAttribute,
|
||||
|
||||
|
||||
getCurrentView
|
||||
} from './instructions';
|
||||
|
||||
|
@ -15,7 +15,6 @@ import {Sanitizer} from '../sanitization/security';
|
||||
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
||||
import {Type} from '../type';
|
||||
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../util/ng_reflect';
|
||||
|
||||
import {assertDataInRange, assertDefined, assertEqual, assertHasParent, assertLessThan, assertNotEqual, assertPreviousIsParent} from './assert';
|
||||
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from './bindings';
|
||||
import {attachPatchData, getComponentViewByInstance} from './context_discovery';
|
||||
@ -23,7 +22,7 @@ import {diPublicInInjector, getNodeInjectable, getOrCreateInjectable, getOrCreat
|
||||
import {throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||
import {PlayerFactory} from './interfaces/player';
|
||||
@ -31,17 +30,17 @@ import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {SanitizerFn} from './interfaces/sanitization';
|
||||
import {StylingIndex} from './interfaces/styling';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
||||
import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCreationMode, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
||||
import {createStylingContextTemplate, renderStyleAndClassBindings, setStyle, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||
import {BoundPlayerFactory} from './styling/player_factory';
|
||||
import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util';
|
||||
import {getStylingContext, isAnimationProp} from './styling/util';
|
||||
import {NO_CHANGE} from './tokens';
|
||||
import {findComponentView, getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
|
||||
|
||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
|
||||
|
||||
|
||||
/**
|
||||
@ -61,30 +60,36 @@ const enum BindingDirection {
|
||||
* bindings, refreshes child components.
|
||||
* Note: view hooks are triggered later when leaving the view.
|
||||
*/
|
||||
export function refreshDescendantViews(lView: LView) {
|
||||
export function refreshDescendantViews(lView: LView, rf: RenderFlags | null) {
|
||||
const tView = lView[TVIEW];
|
||||
// This needs to be set before children are processed to support recursive components
|
||||
tView.firstTemplatePass = false;
|
||||
setFirstTemplatePass(false);
|
||||
|
||||
// If this is a creation pass, we should not call lifecycle hooks or evaluate bindings.
|
||||
// This will be done in the update pass.
|
||||
if (!isCreationMode(lView)) {
|
||||
// Dynamically created views must run first only in creation mode. If this is a
|
||||
// creation-only pass, we should not call lifecycle hooks or evaluate bindings.
|
||||
// This will be done in the update-only pass.
|
||||
if (rf !== RenderFlags.Create) {
|
||||
const creationMode = getCreationMode();
|
||||
const checkNoChangesMode = getCheckNoChangesMode();
|
||||
|
||||
executeInitHooks(lView, tView, checkNoChangesMode);
|
||||
if (!checkNoChangesMode) {
|
||||
executeInitHooks(lView, tView, creationMode);
|
||||
}
|
||||
|
||||
refreshDynamicEmbeddedViews(lView);
|
||||
|
||||
// Content query results must be refreshed before content hooks are called.
|
||||
refreshContentQueries(tView);
|
||||
|
||||
executeHooks(lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode);
|
||||
if (!checkNoChangesMode) {
|
||||
executeHooks(lView, tView.contentHooks, tView.contentCheckHooks, creationMode);
|
||||
}
|
||||
|
||||
setHostBindings(tView, lView);
|
||||
}
|
||||
|
||||
refreshChildComponents(tView.components);
|
||||
refreshChildComponents(tView.components, rf);
|
||||
}
|
||||
|
||||
|
||||
@ -142,10 +147,10 @@ function refreshContentQueries(tView: TView): void {
|
||||
}
|
||||
|
||||
/** Refreshes child components in the current view. */
|
||||
function refreshChildComponents(components: number[] | null): void {
|
||||
function refreshChildComponents(components: number[] | null, rf: RenderFlags | null): void {
|
||||
if (components != null) {
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
componentRefresh(components[i]);
|
||||
componentRefresh(components[i], rf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,8 +160,7 @@ export function createLView<T>(
|
||||
rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null,
|
||||
sanitizer?: Sanitizer | null, injector?: Injector | null): LView {
|
||||
const lView = tView.blueprint.slice() as LView;
|
||||
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit |
|
||||
LViewFlags.FirstLViewPass;
|
||||
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
|
||||
lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
|
||||
lView[CONTEXT] = context;
|
||||
lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !;
|
||||
@ -299,7 +303,8 @@ export function renderTemplate<T>(
|
||||
renderer, sanitizer);
|
||||
hostView[HOST_NODE] = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null);
|
||||
}
|
||||
renderComponentOrTemplate(hostView, context, templateFn);
|
||||
renderComponentOrTemplate(hostView, context, null, templateFn);
|
||||
|
||||
return hostView;
|
||||
}
|
||||
|
||||
@ -343,7 +348,8 @@ export function createEmbeddedViewAndNode<T>(
|
||||
* can't store TViews in the template function itself (as we do for comps). Instead, we store the
|
||||
* TView for dynamically created views on their host TNode, which only has one instance.
|
||||
*/
|
||||
export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, context: T) {
|
||||
export function renderEmbeddedTemplate<T>(
|
||||
viewToRender: LView, tView: TView, context: T, rf: RenderFlags) {
|
||||
const _isParent = getIsParent();
|
||||
const _previousOrParentTNode = getPreviousOrParentTNode();
|
||||
setIsParent(true);
|
||||
@ -359,17 +365,22 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
|
||||
|
||||
oldView = enterView(viewToRender, viewToRender[HOST_NODE]);
|
||||
namespaceHTML();
|
||||
tView.template !(getRenderFlags(viewToRender), context);
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
// matching, etc again and again.
|
||||
viewToRender[TVIEW].firstTemplatePass = false;
|
||||
setFirstTemplatePass(false);
|
||||
|
||||
refreshDescendantViews(viewToRender);
|
||||
tView.template !(rf, context);
|
||||
if (rf & RenderFlags.Update) {
|
||||
refreshDescendantViews(viewToRender, null);
|
||||
} else {
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
// matching, etc again and again.
|
||||
viewToRender[TVIEW].firstTemplatePass = false;
|
||||
setFirstTemplatePass(false);
|
||||
}
|
||||
} finally {
|
||||
leaveView(oldView !);
|
||||
// renderEmbeddedTemplate() is called twice, once for creation only and then once for
|
||||
// update. When for creation only, leaveView() must not trigger view hooks, nor clean flags.
|
||||
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
|
||||
leaveView(oldView !, isCreationOnly);
|
||||
setIsParent(_isParent);
|
||||
setPreviousOrParentTNode(_previousOrParentTNode);
|
||||
}
|
||||
@ -391,31 +402,21 @@ export function nextContext<T = any>(level: number = 1): T {
|
||||
}
|
||||
|
||||
function renderComponentOrTemplate<T>(
|
||||
hostView: LView, context: T, templateFn?: ComponentTemplate<T>) {
|
||||
hostView: LView, componentOrContext: T, rf: RenderFlags | null,
|
||||
templateFn?: ComponentTemplate<T>) {
|
||||
const rendererFactory = hostView[RENDERER_FACTORY];
|
||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||
const normalExecutionPath = !getCheckNoChangesMode();
|
||||
try {
|
||||
if (normalExecutionPath && rendererFactory.begin) {
|
||||
if (rendererFactory.begin) {
|
||||
rendererFactory.begin();
|
||||
}
|
||||
|
||||
if (isCreationMode(hostView)) {
|
||||
// creation mode pass
|
||||
if (templateFn) {
|
||||
namespaceHTML();
|
||||
templateFn(RenderFlags.Create, context !);
|
||||
}
|
||||
|
||||
refreshDescendantViews(hostView);
|
||||
hostView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
if (templateFn) {
|
||||
namespaceHTML();
|
||||
templateFn(rf || getRenderFlags(hostView), componentOrContext !);
|
||||
}
|
||||
|
||||
// update mode pass
|
||||
templateFn && templateFn(RenderFlags.Update, context !);
|
||||
refreshDescendantViews(hostView);
|
||||
refreshDescendantViews(hostView, rf);
|
||||
} finally {
|
||||
if (normalExecutionPath && rendererFactory.end) {
|
||||
if (rendererFactory.end) {
|
||||
rendererFactory.end();
|
||||
}
|
||||
leaveView(oldView);
|
||||
@ -424,11 +425,16 @@ function renderComponentOrTemplate<T>(
|
||||
|
||||
/**
|
||||
* This function returns the default configuration of rendering flags depending on when the
|
||||
* template is in creation mode or update mode. Update block and create block are
|
||||
* always run separately.
|
||||
* template is in creation mode or update mode. By default, the update block is run with the
|
||||
* creation block when the view is in creation mode. Otherwise, the update block is run
|
||||
* alone.
|
||||
*
|
||||
* Dynamically created views do NOT use this configuration (update block and create block are
|
||||
* always run separately).
|
||||
*/
|
||||
function getRenderFlags(view: LView): RenderFlags {
|
||||
return isCreationMode(view) ? RenderFlags.Create : RenderFlags.Update;
|
||||
return view[FLAGS] & LViewFlags.CreationMode ? RenderFlags.Create | RenderFlags.Update :
|
||||
RenderFlags.Update;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
@ -458,8 +464,7 @@ export function namespaceHTML() {
|
||||
*
|
||||
* @param index Index of the element in the data array
|
||||
* @param name Name of the DOM Node
|
||||
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
|
||||
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
|
||||
* @param attrs Statically bound set of attributes to be written into the DOM element on creation.
|
||||
* @param localRefs A set of local reference bindings on the element.
|
||||
*/
|
||||
export function element(
|
||||
@ -529,8 +534,7 @@ export function elementContainerEnd(): void {
|
||||
*
|
||||
* @param index Index of the element in the LView array
|
||||
* @param name Name of the DOM Node
|
||||
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
|
||||
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
|
||||
* @param attrs Statically bound set of attributes to be written into the DOM element on creation.
|
||||
* @param localRefs A set of local reference bindings on the element.
|
||||
*
|
||||
* Attributes and localRefs are passed as an array of strings where elements with an even index
|
||||
@ -554,14 +558,6 @@ export function elementStart(
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null);
|
||||
|
||||
if (attrs) {
|
||||
// it's important to only prepare styling-related datastructures once for a given
|
||||
// tNode and not each time an element is created. Also, the styling code is designed
|
||||
// to be patched and constructed at various points, but only up until the first element
|
||||
// is created. Then the styling context is locked and can only be instantiated for each
|
||||
// successive element that is created.
|
||||
if (tView.firstTemplatePass && !tNode.stylingTemplate && hasStyling(attrs)) {
|
||||
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
|
||||
}
|
||||
setUpAttributes(native, attrs);
|
||||
}
|
||||
|
||||
@ -575,23 +571,6 @@ export function elementStart(
|
||||
attachPatchData(native, lView);
|
||||
}
|
||||
increaseElementDepthCount();
|
||||
|
||||
// if a directive contains a host binding for "class" then all class-based data will
|
||||
// flow through that (except for `[class.prop]` bindings). This also includes initial
|
||||
// static class values as well. (Note that this will be fixed once map-based `[style]`
|
||||
// and `[class]` bindings work for multiple directives.)
|
||||
if (tView.firstTemplatePass) {
|
||||
const inputData = initializeTNodeInputs(tNode);
|
||||
if (inputData && inputData.hasOwnProperty('class')) {
|
||||
tNode.flags |= TNodeFlags.hasClassInput;
|
||||
}
|
||||
}
|
||||
|
||||
// There is no point in rendering styles when a class directive is present since
|
||||
// it will take that over for us (this will be removed once #FW-882 is in).
|
||||
if (tNode.stylingTemplate && (tNode.flags & TNodeFlags.hasClassInput) === 0) {
|
||||
renderInitialStylesAndClasses(native, tNode.stylingTemplate, lView[RENDERER]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -750,28 +729,25 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
||||
let i = 0;
|
||||
|
||||
while (i < attrs.length) {
|
||||
const attrName = attrs[i++];
|
||||
if (typeof attrName == 'number') {
|
||||
const attrName = attrs[i];
|
||||
if (attrName === AttributeMarker.SelectOnly) break;
|
||||
if (attrName === NG_PROJECT_AS_ATTR_NAME) {
|
||||
i += 2;
|
||||
} else {
|
||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||
if (attrName === AttributeMarker.NamespaceURI) {
|
||||
// Namespaced attributes
|
||||
const namespaceURI = attrs[i++] as string;
|
||||
const attrName = attrs[i++] as string;
|
||||
const attrVal = attrs[i++] as string;
|
||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||
const namespaceURI = attrs[i + 1] as string;
|
||||
const attrName = attrs[i + 2] as string;
|
||||
const attrVal = attrs[i + 3] as string;
|
||||
isProc ?
|
||||
(renderer as ProceduralRenderer3)
|
||||
.setAttribute(native, attrName, attrVal, namespaceURI) :
|
||||
native.setAttributeNS(namespaceURI, attrName, attrVal);
|
||||
i += 4;
|
||||
} else {
|
||||
// All other `AttributeMarker`s are ignored here.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/// attrName is string;
|
||||
const attrVal = attrs[i++];
|
||||
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
||||
// Standard attributes
|
||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||
const attrVal = attrs[i + 1];
|
||||
if (isAnimationProp(attrName)) {
|
||||
if (isProc) {
|
||||
(renderer as ProceduralRenderer3).setProperty(native, attrName, attrVal);
|
||||
@ -782,6 +758,7 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
||||
.setAttribute(native, attrName as string, attrVal as string) :
|
||||
native.setAttribute(attrName as string, attrVal as string);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -933,15 +910,6 @@ export function elementEnd(): void {
|
||||
|
||||
queueLifecycleHooks(getLView()[TVIEW], previousOrParentTNode);
|
||||
decreaseElementDepthCount();
|
||||
|
||||
// this is fired at the end of elementEnd because ALL of the stylingBindings code
|
||||
// (for directives and the template) have now executed which means the styling
|
||||
// context can be instantiated properly.
|
||||
if (hasClassInput(previousOrParentTNode)) {
|
||||
const stylingContext = getStylingContext(previousOrParentTNode.index, lView);
|
||||
setInputsForProperty(
|
||||
lView, previousOrParentTNode.inputs !['class'] !, getInitialClassNameValue(stylingContext));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -987,48 +955,10 @@ export function elementAttribute(
|
||||
* @param nativeOnly Whether or not we should only set native properties and skip input check
|
||||
* (this is necessary for host property bindings)
|
||||
*/
|
||||
|
||||
export function elementProperty<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean): void {
|
||||
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a synthetic host binding (e.g. `[@foo]`) on a component.
|
||||
*
|
||||
* This instruction is for compatibility purposes and is designed to ensure that a
|
||||
* synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
|
||||
* the component's renderer. Normally all host bindings are evaluated with the parent
|
||||
* component's renderer, but, in the case of animation @triggers, they need to be
|
||||
* evaluated with the sub components renderer (because that's where the animation
|
||||
* triggers are defined).
|
||||
*
|
||||
* Do not use this instruction as a replacement for `elementProperty`. This instruction
|
||||
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
|
||||
*
|
||||
* @param index The index of the element to update in the data array
|
||||
* @param propName Name of property. Because it is going to DOM, this is not subject to
|
||||
* renaming as part of minification.
|
||||
* @param value New value to write.
|
||||
* @param sanitizer An optional function used to sanitize the value.
|
||||
* @param nativeOnly Whether or not we should only set native properties and skip input check
|
||||
* (this is necessary for host property bindings)
|
||||
*/
|
||||
export function componentHostSyntheticProperty<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean) {
|
||||
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly, loadComponentRenderer);
|
||||
}
|
||||
|
||||
function loadComponentRenderer(tNode: TNode, lView: LView): Renderer3 {
|
||||
const componentLView = lView[tNode.index] as LView;
|
||||
return componentLView[RENDERER];
|
||||
}
|
||||
|
||||
function elementPropertyInternal<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean,
|
||||
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const lView = getLView();
|
||||
const element = getNativeByIndex(index, lView) as RElement | RComment;
|
||||
@ -1045,7 +975,7 @@ function elementPropertyInternal<T>(
|
||||
}
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Element) {
|
||||
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
|
||||
const renderer = lView[RENDERER];
|
||||
// It is assumed that the sanitizer is only added when the compiler determines that the property
|
||||
// is risky, so sanitization can be done without further checks.
|
||||
value = sanitizer != null ? (sanitizer(value) as any) : value;
|
||||
@ -1174,84 +1104,119 @@ function generatePropertyAliases(tNode: TNode, direction: BindingDirection): Pro
|
||||
return propStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove a class in a `classList` on a DOM element.
|
||||
*
|
||||
* This instruction is meant to handle the [class.foo]="exp" case
|
||||
*
|
||||
* @param index The index of the element to update in the data array
|
||||
* @param classIndex Index of class to toggle. Because it is going to DOM, this is not subject to
|
||||
* renaming as part of minification.
|
||||
* @param value A value indicating if a given class should be added or removed.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {}): void {
|
||||
if (directive != undefined) {
|
||||
return hackImplementationOfElementClassProp(
|
||||
index, classIndex, value, directive); // proper supported in next PR
|
||||
}
|
||||
const val =
|
||||
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||
updateElementClassProp(getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign any inline style values to the element during creation mode.
|
||||
*
|
||||
* This instruction is meant to be called during creation mode to register all
|
||||
* dynamic style and class bindings on the element. Note for static values (no binding)
|
||||
* see `elementStart` and `elementHostAttrs`.
|
||||
* This instruction is meant to be called during creation mode to apply all styling
|
||||
* (e.g. `style="..."`) values to the element. This is also where the provided index
|
||||
* value is allocated for the styling details for its corresponding element (the element
|
||||
* index is the previous index value from this one).
|
||||
*
|
||||
* @param classBindingNames An array containing bindable class names.
|
||||
* The `elementClassProp` refers to the class name by index in this array.
|
||||
* (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
|
||||
* @param styleBindingNames An array containing bindable style properties.
|
||||
* The `elementStyleProp` refers to the class name by index in this array.
|
||||
* (i.e. `['width', 'height']` means `width=0` and `height=1`).
|
||||
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
|
||||
* property values that are applied to the element (during rendering).
|
||||
* Note that the sanitizer instance itself is tied to the `directive` (if provided).
|
||||
* @param directive A directive instance the styling is associated with. If not provided
|
||||
* current view's controller instance is assumed.
|
||||
* (Note this function calls `elementStylingApply` immediately when called.)
|
||||
*
|
||||
* @publicApi
|
||||
*
|
||||
* @param index Index value which will be allocated to store styling data for the element.
|
||||
* (Note that this is not the element index, but rather an index value allocated
|
||||
* specifically for element styling--the index must be the next index after the element
|
||||
* index.)
|
||||
* @param classDeclarations A key/value array of CSS classes that will be registered on the element.
|
||||
* Each individual style will be used on the element as long as it is not overridden
|
||||
* by any classes placed on the element by multiple (`[class]`) or singular (`[class.named]`)
|
||||
* bindings. If a class binding changes its value to a falsy value then the matching initial
|
||||
* class value that are passed in here will be applied to the element (if matched).
|
||||
* @param styleDeclarations A key/value array of CSS styles that will be registered on the element.
|
||||
* Each individual style will be used on the element as long as it is not overridden
|
||||
* by any styles placed on the element by multiple (`[style]`) or singular (`[style.prop]`)
|
||||
* bindings. If a style binding changes its value to null then the initial styling
|
||||
* values that are passed in here will be applied to the element (if matched).
|
||||
* @param styleSanitizer An optional sanitizer function that will be used (if provided)
|
||||
* to sanitize the any CSS property values that are applied to the element (during rendering).
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStyling(
|
||||
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
|
||||
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleSanitizer?: StyleSanitizeFn | null, directive?: {}): void {
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
if (!tNode.stylingTemplate) {
|
||||
tNode.stylingTemplate = createEmptyStylingContext();
|
||||
if (directive != undefined) {
|
||||
getCreationMode() &&
|
||||
hackImplementationOfElementStyling(
|
||||
classDeclarations || null, styleDeclarations || null, styleSanitizer || null,
|
||||
directive); // supported in next PR
|
||||
return;
|
||||
}
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
const inputData = initializeTNodeInputs(tNode);
|
||||
|
||||
if (!tNode.stylingTemplate) {
|
||||
const hasClassInput = inputData && inputData.hasOwnProperty('class') ? true : false;
|
||||
if (hasClassInput) {
|
||||
tNode.flags |= TNodeFlags.hasClassInput;
|
||||
}
|
||||
|
||||
// initialize the styling template.
|
||||
tNode.stylingTemplate = createStylingContextTemplate(
|
||||
classDeclarations, styleDeclarations, styleSanitizer, hasClassInput);
|
||||
}
|
||||
|
||||
if (styleDeclarations && styleDeclarations.length ||
|
||||
classDeclarations && classDeclarations.length) {
|
||||
const index = tNode.index;
|
||||
if (delegateToClassInput(tNode)) {
|
||||
const lView = getLView();
|
||||
const stylingContext = getStylingContext(index, lView);
|
||||
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
|
||||
setInputsForProperty(lView, tNode.inputs !['class'] !, initialClasses);
|
||||
}
|
||||
elementStylingApply(index - HEADER_OFFSET);
|
||||
}
|
||||
updateContextWithBindings(
|
||||
tNode.stylingTemplate !, directive || null, classBindingNames, styleBindingNames,
|
||||
styleSanitizer, hasClassInput(tNode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign static styling values to a host element.
|
||||
*
|
||||
* NOTE: This instruction is meant to used from `hostBindings` function only.
|
||||
*
|
||||
* @param directive A directive instance the styling is associated with.
|
||||
* @param attrs An array containing class and styling information. The values must be marked with
|
||||
* `AttributeMarker`.
|
||||
*
|
||||
* ```
|
||||
* var attrs = [AttributeMarker.Classes, 'foo', 'bar',
|
||||
* AttributeMarker.Styles, 'width', '100px', 'height, '200px']
|
||||
* elementHostAttrs(directive, attrs);
|
||||
* ```
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function elementHostAttrs(directive: any, attrs: TAttributes) {
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
if (!tNode.stylingTemplate) {
|
||||
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
|
||||
}
|
||||
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, directive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply styling binding to the element.
|
||||
* Apply all styling values to the element which have been queued by any styling instructions.
|
||||
*
|
||||
* This instruction is meant to be run after `elementStyle` and/or `elementStyleProp`.
|
||||
* if any styling bindings have changed then the changes are flushed to the element.
|
||||
* This instruction is meant to be run once one or more `elementStyle` and/or `elementStyleProp`
|
||||
* have been issued against the element. This function will also determine if any styles have
|
||||
* changed and will then skip the operation if there is nothing new to render.
|
||||
*
|
||||
* Once called then all queued styles will be flushed.
|
||||
*
|
||||
* @param index Index of the element's with which styling is associated.
|
||||
* @param directive Directive instance that is attempting to change styling. (Defaults to the
|
||||
* component of the current view).
|
||||
components
|
||||
*
|
||||
* @publicApi
|
||||
* @param index Index of the element's styling storage that will be rendered.
|
||||
* (Note that this is not the element index, but rather an index value allocated
|
||||
* specifically for element styling--the index must be the next index after the element
|
||||
* index.)
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStylingApply(index: number, directive?: any): void {
|
||||
export function elementStylingApply(index: number, directive?: {}): void {
|
||||
if (directive != undefined) {
|
||||
return hackImplementationOfElementStylingApply(index, directive); // supported in next PR
|
||||
}
|
||||
const lView = getLView();
|
||||
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
|
||||
const totalPlayersQueued = renderStyling(
|
||||
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender, null,
|
||||
null, directive);
|
||||
const isFirstRender = (lView[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||
const totalPlayersQueued = renderStyleAndClassBindings(
|
||||
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender);
|
||||
if (totalPlayersQueued > 0) {
|
||||
const rootContext = getRootContext(lView);
|
||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||
@ -1259,34 +1224,29 @@ export function elementStylingApply(index: number, directive?: any): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a style bindings value on an element.
|
||||
* Queue a given style to be rendered on an Element.
|
||||
*
|
||||
* If the style value is `null` then it will be removed from the element
|
||||
* (or assigned a different value depending if there are any styles placed
|
||||
* on the element with `elementStyle` or any styles that are present
|
||||
* from when the element was created (with `elementStyling`).
|
||||
*
|
||||
* (Note that the styling element is updated as part of `elementStylingApply`.)
|
||||
* (Note that the styling instruction will not be applied until `elementStylingApply` is called.)
|
||||
*
|
||||
* @param index Index of the element's with which styling is associated.
|
||||
* @param styleIndex Index of style to update. This index value refers to the
|
||||
* index of the style in the style bindings array that was passed into
|
||||
* `elementStlyingBindings`.
|
||||
* @param value New value to write (null to remove). Note that if a directive also
|
||||
* attempts to write to the same binding value then it will only be able to
|
||||
* do so if the template binding value is `null` (or doesn't exist at all).
|
||||
* @param index Index of the element's styling storage to change in the data array.
|
||||
* (Note that this is not the element index, but rather an index value allocated
|
||||
* specifically for element styling--the index must be the next index after the element
|
||||
* index.)
|
||||
* @param styleIndex Index of the style property on this element. (Monotonically increasing.)
|
||||
* @param value New value to write (null to remove).
|
||||
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
|
||||
* Note that when a suffix is provided then the underlying sanitizer will
|
||||
* be ignored.
|
||||
* @param directive Directive instance that is attempting to change styling. (Defaults to the
|
||||
* component of the current view).
|
||||
components
|
||||
*
|
||||
* @publicApi
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string | null, directive?: {}): void {
|
||||
suffix?: string, directive?: {}): void {
|
||||
let valueToAdd: string|null = null;
|
||||
if (value !== null) {
|
||||
if (suffix) {
|
||||
@ -1301,59 +1261,35 @@ export function elementStyleProp(
|
||||
valueToAdd = value as any as string;
|
||||
}
|
||||
}
|
||||
updateElementStyleProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd, directive);
|
||||
if (directive != undefined) {
|
||||
hackImplementationOfElementStyleProp(index, styleIndex, valueToAdd, suffix, directive);
|
||||
} else {
|
||||
updateElementStyleProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove a class via a class binding on a DOM element.
|
||||
* Queue a key/value map of styles to be rendered on an Element.
|
||||
*
|
||||
* This instruction is meant to handle the [class.foo]="exp" case and, therefore,
|
||||
* the class itself must already be applied using `elementStyling` within
|
||||
* the creation block.
|
||||
*
|
||||
* @param index Index of the element's with which styling is associated.
|
||||
* @param classIndex Index of class to toggle. This index value refers to the
|
||||
* index of the class in the class bindings array that was passed into
|
||||
* `elementStlyingBindings` (which is meant to be called before this
|
||||
* function is).
|
||||
* @param value A true/false value which will turn the class on or off.
|
||||
* @param directive Directive instance that is attempting to change styling. (Defaults to the
|
||||
* component of the current view).
|
||||
components
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function elementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {}): void {
|
||||
const onOrOffClassValue =
|
||||
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||
updateElementClassProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, onOrOffClassValue,
|
||||
directive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update style and/or class bindings using object literal.
|
||||
*
|
||||
* This instruction is meant apply styling via the `[style]="exp"` and `[class]="exp"` template
|
||||
* bindings. When styles are applied to the Element they will then be placed with respect to
|
||||
* any styles set with `elementStyleProp`. If any styles are set to `null` then they will be
|
||||
* removed from the element.
|
||||
* This instruction is meant to handle the `[style]="exp"` usage. When styles are applied to
|
||||
* the Element they will then be placed with respect to any styles set with `elementStyleProp`.
|
||||
* If any styles are set to `null` then they will be removed from the element (unless the same
|
||||
* style properties have been assigned to the element during creation using `elementStyling`).
|
||||
*
|
||||
* (Note that the styling instruction will not be applied until `elementStylingApply` is called.)
|
||||
*
|
||||
* @param index Index of the element's with which styling is associated.
|
||||
* @param index Index of the element's styling storage to change in the data array.
|
||||
* (Note that this is not the element index, but rather an index value allocated
|
||||
* specifically for element styling--the index must be the next index after the element
|
||||
* index.)
|
||||
* @param classes A key/value style map of CSS classes that will be added to the given element.
|
||||
* Any missing classes (that have already been applied to the element beforehand) will be
|
||||
* removed (unset) from the element's list of CSS classes.
|
||||
* @param styles A key/value style map of the styles that will be applied to the given element.
|
||||
* Any missing styles (that have already been applied to the element beforehand) will be
|
||||
* removed (unset) from the element's styling.
|
||||
* @param directive Directive instance that is attempting to change styling. (Defaults to the
|
||||
* component of the current view).
|
||||
*
|
||||
* @publicApi
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStylingMap<T>(
|
||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
@ -1364,24 +1300,113 @@ export function elementStylingMap<T>(
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
|
||||
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
|
||||
const initialClasses = getInitialClassNameValue(stylingContext);
|
||||
if (delegateToClassInput(tNode) && classes !== NO_CHANGE) {
|
||||
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
|
||||
const classInputVal =
|
||||
(initialClasses.length ? (initialClasses + ' ') : '') + (classes as string);
|
||||
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
|
||||
} else {
|
||||
updateStylingMap(stylingContext, classes, styles);
|
||||
}
|
||||
updateStylingMap(stylingContext, classes, styles);
|
||||
}
|
||||
|
||||
/* START OF HACK BLOCK */
|
||||
/*
|
||||
* HACK
|
||||
* The code below is a quick and dirty implementation of the host style binding so that we can make
|
||||
* progress on TestBed. Once the correct implementation is created this code should be removed.
|
||||
*/
|
||||
interface HostStylingHack {
|
||||
classDeclarations: string[];
|
||||
styleDeclarations: string[];
|
||||
styleSanitizer: StyleSanitizeFn|null;
|
||||
}
|
||||
type HostStylingHackMap = Map<{}, HostStylingHack>;
|
||||
|
||||
function hackImplementationOfElementStyling(
|
||||
classDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
|
||||
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView()) as RElement;
|
||||
ngDevMode && assertDefined(node, 'expecting parent DOM node');
|
||||
const hostStylingHackMap: HostStylingHackMap =
|
||||
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
|
||||
const squashedClassDeclarations = hackSquashDeclaration(classDeclarations);
|
||||
hostStylingHackMap.set(directive, {
|
||||
classDeclarations: squashedClassDeclarations,
|
||||
styleDeclarations: hackSquashDeclaration(styleDeclarations), styleSanitizer
|
||||
});
|
||||
hackSetStaticClasses(node, squashedClassDeclarations);
|
||||
}
|
||||
|
||||
function hackSetStaticClasses(node: RElement, classDeclarations: (string | boolean)[]) {
|
||||
// Static classes need to be set here because static classes don't generate
|
||||
// elementClassProp instructions.
|
||||
const lView = getLView();
|
||||
const staticClassStartIndex =
|
||||
classDeclarations.indexOf(InitialStylingFlags.VALUES_MODE as any) + 1;
|
||||
const renderer = lView[RENDERER];
|
||||
|
||||
for (let i = staticClassStartIndex; i < classDeclarations.length; i += 2) {
|
||||
const className = classDeclarations[i] as string;
|
||||
const value = classDeclarations[i + 1];
|
||||
// if value is true, then this is a static class and we should set it now.
|
||||
// class bindings are set separately in elementClassProp.
|
||||
if (value === true) {
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
renderer.addClass(node, className);
|
||||
} else {
|
||||
const classList = (node as HTMLElement).classList;
|
||||
classList.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hackSquashDeclaration(declarations: (string | boolean | InitialStylingFlags)[] | null):
|
||||
string[] {
|
||||
// assume the array is correct. This should be fine for View Engine compatibility.
|
||||
return declarations || [] as any;
|
||||
}
|
||||
|
||||
function hackImplementationOfElementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory, directive: {}): void {
|
||||
const lView = getLView();
|
||||
const node = getNativeByIndex(index, lView);
|
||||
ngDevMode && assertDefined(node, 'could not locate node');
|
||||
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
|
||||
const className = hostStylingHack.classDeclarations[classIndex];
|
||||
const renderer = lView[RENDERER];
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
value ? renderer.addClass(node, className) : renderer.removeClass(node, className);
|
||||
} else {
|
||||
const classList = (node as HTMLElement).classList;
|
||||
value ? classList.add(className) : classList.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStylingApply(index: number, directive?: {}): void {
|
||||
// Do nothing because the hack implementation is eager.
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStyleProp(
|
||||
index: number, styleIndex: number, value: string | null, suffix?: string,
|
||||
directive?: {}): void {
|
||||
const lView = getLView();
|
||||
const node = getNativeByIndex(index, lView);
|
||||
ngDevMode && assertDefined(node, 'could not locate node');
|
||||
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
|
||||
const styleName = hostStylingHack.styleDeclarations[styleIndex];
|
||||
const renderer = lView[RENDERER];
|
||||
setStyle(node, styleName, value as string, renderer, null);
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStylingMap<T>(
|
||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null, directive?: {}): void {
|
||||
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
|
||||
}
|
||||
/* END OF HACK BLOCK */
|
||||
|
||||
/* END OF HACK BLOCK */
|
||||
//////////////////////////
|
||||
//// Text
|
||||
//////////////////////////
|
||||
@ -1967,9 +1992,11 @@ export function containerRefreshStart(index: number): void {
|
||||
|
||||
lView[index + HEADER_OFFSET][ACTIVE_INDEX] = 0;
|
||||
|
||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||
// before they are called in embedded views (for backwards compatibility).
|
||||
executeInitHooks(lView, tView, getCheckNoChangesMode());
|
||||
if (!getCheckNoChangesMode()) {
|
||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||
// before they are called in embedded views (for backwards compatibility).
|
||||
executeInitHooks(lView, tView, getCreationMode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2014,7 +2041,9 @@ function refreshDynamicEmbeddedViews(lView: LView) {
|
||||
const dynamicViewData = container[VIEWS][i];
|
||||
// The directives and pipes are not needed here as an existing view is only being refreshed.
|
||||
ngDevMode && assertDefined(dynamicViewData[TVIEW], 'TView must be allocated');
|
||||
renderEmbeddedTemplate(dynamicViewData, dynamicViewData[TVIEW], dynamicViewData[CONTEXT] !);
|
||||
renderEmbeddedTemplate(
|
||||
dynamicViewData, dynamicViewData[TVIEW], dynamicViewData[CONTEXT] !,
|
||||
RenderFlags.Update);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2089,14 +2118,13 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
||||
enterView(viewToRender, viewToRender[TVIEW].node);
|
||||
}
|
||||
if (lContainer) {
|
||||
if (isCreationMode(viewToRender)) {
|
||||
if (getCreationMode()) {
|
||||
// it is a new view, insert it into collection of views for a given container
|
||||
insertView(viewToRender, lContainer, lView, lContainer[ACTIVE_INDEX] !, -1);
|
||||
}
|
||||
lContainer[ACTIVE_INDEX] !++;
|
||||
}
|
||||
return isCreationMode(viewToRender) ? RenderFlags.Create | RenderFlags.Update :
|
||||
RenderFlags.Update;
|
||||
return getRenderFlags(viewToRender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2130,12 +2158,7 @@ function getOrCreateEmbeddedTView(
|
||||
export function embeddedViewEnd(): void {
|
||||
const lView = getLView();
|
||||
const viewHost = lView[HOST_NODE];
|
||||
|
||||
if (isCreationMode(lView)) {
|
||||
refreshDescendantViews(lView); // creation mode pass
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
}
|
||||
refreshDescendantViews(lView); // update mode pass
|
||||
refreshDescendantViews(lView, null);
|
||||
leaveView(lView[PARENT] !);
|
||||
setPreviousOrParentTNode(viewHost !);
|
||||
setIsParent(false);
|
||||
@ -2147,8 +2170,9 @@ export function embeddedViewEnd(): void {
|
||||
* Refreshes components by entering the component view and processing its bindings, queries, etc.
|
||||
*
|
||||
* @param adjustedElementIndex Element index in LView[] (adjusted for HEADER_OFFSET)
|
||||
* @param rf The render flags that should be used to process this template
|
||||
*/
|
||||
export function componentRefresh<T>(adjustedElementIndex: number): void {
|
||||
export function componentRefresh<T>(adjustedElementIndex: number, rf: RenderFlags | null): void {
|
||||
const lView = getLView();
|
||||
ngDevMode && assertDataInRange(lView, adjustedElementIndex);
|
||||
const hostView = getComponentViewByIndex(adjustedElementIndex, lView);
|
||||
@ -2157,7 +2181,7 @@ export function componentRefresh<T>(adjustedElementIndex: number): void {
|
||||
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
||||
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||
syncViewWithBlueprint(hostView);
|
||||
checkView(hostView, hostView[CONTEXT]);
|
||||
detectChangesInternal(hostView, hostView[CONTEXT], rf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2437,7 +2461,7 @@ export function tick<T>(component: T): void {
|
||||
function tickRootContext(rootContext: RootContext) {
|
||||
for (let i = 0; i < rootContext.components.length; i++) {
|
||||
const rootComponent = rootContext.components[i];
|
||||
renderComponentOrTemplate(readPatchedLView(rootComponent) !, rootComponent);
|
||||
renderComponentOrTemplate(readPatchedLView(rootComponent) !, rootComponent, RenderFlags.Update);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2455,21 +2479,7 @@ function tickRootContext(rootContext: RootContext) {
|
||||
* @param component The component which the change detection should be performed on.
|
||||
*/
|
||||
export function detectChanges<T>(component: T): void {
|
||||
const view = getComponentViewByInstance(component) !;
|
||||
detectChangesInternal<T>(view, component);
|
||||
}
|
||||
|
||||
export function detectChangesInternal<T>(view: LView, context: T) {
|
||||
const rendererFactory = view[RENDERER_FACTORY];
|
||||
|
||||
if (rendererFactory.begin) rendererFactory.begin();
|
||||
|
||||
if (isCreationMode(view)) {
|
||||
checkView(view, context); // creation mode pass
|
||||
}
|
||||
checkView(view, context); // update mode pass
|
||||
|
||||
if (rendererFactory.end) rendererFactory.end();
|
||||
detectChangesInternal(getComponentViewByInstance(component) !, component, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2516,7 +2526,7 @@ export function checkNoChangesInRootView(lView: LView): void {
|
||||
}
|
||||
|
||||
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
||||
export function checkView<T>(hostView: LView, component: T) {
|
||||
export function detectChangesInternal<T>(hostView: LView, component: T, rf: RenderFlags | null) {
|
||||
const hostTView = hostView[TVIEW];
|
||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||
const templateFn = hostTView.template !;
|
||||
@ -2524,23 +2534,27 @@ export function checkView<T>(hostView: LView, component: T) {
|
||||
|
||||
try {
|
||||
namespaceHTML();
|
||||
createViewQuery(viewQuery, hostView, component);
|
||||
templateFn(getRenderFlags(hostView), component);
|
||||
refreshDescendantViews(hostView);
|
||||
updateViewQuery(viewQuery, hostView, component);
|
||||
createViewQuery(viewQuery, rf, hostView[FLAGS], component);
|
||||
templateFn(rf || getRenderFlags(hostView), component);
|
||||
refreshDescendantViews(hostView, rf);
|
||||
updateViewQuery(viewQuery, hostView[FLAGS], component);
|
||||
} finally {
|
||||
leaveView(oldView);
|
||||
leaveView(oldView, rf === RenderFlags.Create);
|
||||
}
|
||||
}
|
||||
|
||||
function createViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
|
||||
if (viewQuery && isCreationMode(view)) {
|
||||
function createViewQuery<T>(
|
||||
viewQuery: ComponentQuery<{}>| null, renderFlags: RenderFlags | null, viewFlags: LViewFlags,
|
||||
component: T): void {
|
||||
if (viewQuery && (renderFlags === RenderFlags.Create ||
|
||||
(renderFlags === null && (viewFlags & LViewFlags.CreationMode)))) {
|
||||
viewQuery(RenderFlags.Create, component);
|
||||
}
|
||||
}
|
||||
|
||||
function updateViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
|
||||
if (viewQuery && !isCreationMode(view)) {
|
||||
function updateViewQuery<T>(
|
||||
viewQuery: ComponentQuery<{}>| null, flags: LViewFlags, component: T): void {
|
||||
if (viewQuery && flags & RenderFlags.Update) {
|
||||
viewQuery(RenderFlags.Update, component);
|
||||
}
|
||||
}
|
||||
@ -2868,6 +2882,10 @@ function initializeTNodeInputs(tNode: TNode | null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function delegateToClassInput(tNode: TNode) {
|
||||
return tNode.flags & TNodeFlags.hasClassInput;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current OpaqueViewState instance.
|
||||
|
@ -23,14 +23,6 @@ export const VIEWS = 1;
|
||||
// As we already have these constants in LView, we don't need to re-create them.
|
||||
export const NATIVE = 6;
|
||||
export const RENDER_PARENT = 7;
|
||||
// Because interfaces in TS/JS cannot be instanceof-checked this means that we
|
||||
// need to rely on predictable characteristics of data-structures to check if they
|
||||
// are what we expect for them to be. The `LContainer` interface code below has a
|
||||
// fixed length and the constant value below references that. Using the length value
|
||||
// below we can predictably gaurantee that we are dealing with an `LContainer` array.
|
||||
// This value MUST be kept up to date with the length of the `LContainer` array
|
||||
// interface below so that runtime type checking can work.
|
||||
export const LCONTAINER_LENGTH = 8;
|
||||
|
||||
/**
|
||||
* The state associated with a container.
|
||||
|
@ -344,4 +344,8 @@ export type PipeTypeList =
|
||||
|
||||
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
||||
// failure based on types.
|
||||
export const unusedValueExportToPlacateAjd = 1;
|
||||
export const unusedValueExportToPlacateAjd = 1;
|
||||
|
||||
export const enum InitialStylingFlags {
|
||||
VALUES_MODE = 0b1,
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ export interface RelativeInjectorLocation { __brand__: 'RelativeInjectorLocation
|
||||
|
||||
export const enum RelativeInjectorLocationFlags {
|
||||
InjectorIndexMask = 0b111111111111111,
|
||||
AcrossHostBoundary = 0b1000000000000000,
|
||||
ViewOffsetShift = 16,
|
||||
NO_PARENT = -1,
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export const enum TNodeProviderIndexes {
|
||||
CptViewProvidersCountShifter = 0b00000000000000010000000000000000,
|
||||
}
|
||||
/**
|
||||
* A set of marker values to be used in the attributes arrays. These markers indicate that some
|
||||
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
|
||||
* items are not regular attributes and the processing should be adapted accordingly.
|
||||
*/
|
||||
export const enum AttributeMarker {
|
||||
@ -65,50 +65,13 @@ export const enum AttributeMarker {
|
||||
*/
|
||||
NamespaceURI = 0,
|
||||
|
||||
/**
|
||||
* Signals class declaration.
|
||||
*
|
||||
* Each value following `Classes` designates a class name to include on the element.
|
||||
* ## Example:
|
||||
*
|
||||
* Given:
|
||||
* ```
|
||||
* <div class="foo bar baz">...<d/vi>
|
||||
* ```
|
||||
*
|
||||
* the generated code is:
|
||||
* ```
|
||||
* var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz'];
|
||||
* ```
|
||||
*/
|
||||
Classes = 1,
|
||||
|
||||
/**
|
||||
* Signals style declaration.
|
||||
*
|
||||
* Each pair of values following `Styles` designates a style name and value to include on the
|
||||
* element.
|
||||
* ## Example:
|
||||
*
|
||||
* Given:
|
||||
* ```
|
||||
* <div style="width:100px; height:200px; color:red">...</div>
|
||||
* ```
|
||||
*
|
||||
* the generated code is:
|
||||
* ```
|
||||
* var _c1 = [AttributeMarker.Styles, 'width', '100px', 'height'. '200px', 'color', 'red'];
|
||||
* ```
|
||||
*/
|
||||
Styles = 2,
|
||||
|
||||
/**
|
||||
* This marker indicates that the following attribute names were extracted from bindings (ex.:
|
||||
* [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
|
||||
* Taking the above bindings and outputs as an example an attributes array could look as follows:
|
||||
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
|
||||
*/
|
||||
SelectOnly = 3,
|
||||
SelectOnly = 1
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,199 +9,130 @@ import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {RElement} from '../interfaces/renderer';
|
||||
import {PlayerContext} from './player';
|
||||
|
||||
|
||||
/**
|
||||
* The styling context acts as a styling manifest (shaped as an array) for determining which
|
||||
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
|
||||
* and `updateClassProp` functions. It also stores the static style/class values that were
|
||||
* extracted from the template by the compiler.
|
||||
* and `updateClassProp` functions. There are also two initialization functions
|
||||
* `allocStylingContext` and `createStylingContextTemplate` which are used to initialize
|
||||
* and/or clone the context.
|
||||
*
|
||||
* A context is created by Angular when:
|
||||
* 1. An element contains static styling values (like style="..." or class="...")
|
||||
* 2. An element contains single property binding values (like [style.prop]="x" or
|
||||
* [class.prop]="y")
|
||||
* 3. An element contains multi property binding values (like [style]="x" or [class]="y")
|
||||
* 4. A directive contains host bindings for static, single or multi styling properties/bindings.
|
||||
* 5. An animation player is added to an element via `addPlayer`
|
||||
* The context is an array where the first two cells are used for static data (initial styling)
|
||||
* and dirty flags / index offsets). The remaining set of cells is used for multi (map) and single
|
||||
* (prop) style values.
|
||||
*
|
||||
* Note that even if an element contains static styling then this context will be created and
|
||||
* attached to it. The reason why this happens (instead of treating styles/classes as regular
|
||||
* HTML attributes) is because the style/class bindings must be able to default themselves back
|
||||
* to their respective static values when they are set to null.
|
||||
* each value from here onwards is mapped as so:
|
||||
* [i] = mutation/type flag for the style/class value
|
||||
* [i + 1] = prop string (or null incase it has been removed)
|
||||
* [i + 2] = value string (or null incase it has been removed)
|
||||
*
|
||||
* There are three types of styling types stored in this context:
|
||||
* initial: any styles that are passed in once the context is created
|
||||
* (these are stored in the first cell of the array and the first
|
||||
* value of this array is always `null` even if no initial styling exists.
|
||||
* the `null` value is there so that any new styles have a parent to point
|
||||
* to. This way we can always assume that there is a parent.)
|
||||
*
|
||||
* single: any styles that are updated using `updateStyleProp` or `updateClassProp` (fixed set)
|
||||
*
|
||||
* multi: any styles that are updated using `updateStylingMap` (dynamic set)
|
||||
*
|
||||
* Note that context is only used to collect style information. Only when `renderStyling`
|
||||
* is called is when the styling payload will be rendered (or built as a key/value map).
|
||||
*
|
||||
* When the context is created, depending on what initial styling values are passed in, the
|
||||
* context itself will be pre-filled with slots based on the initial style properties. Say
|
||||
* for example we have a series of initial styles that look like so:
|
||||
*
|
||||
* style="width:100px; height:200px;"
|
||||
* class="foo"
|
||||
*
|
||||
* Then the initial state of the context (once initialized) will look like so:
|
||||
*
|
||||
* Say for example we have this:
|
||||
* ```
|
||||
* <!-- when myWidthExp=null then a width of "100px"
|
||||
* will be used a default value for width -->
|
||||
* <div style="width:100px" [style.width]="myWidthExp"></div>
|
||||
* ```
|
||||
*
|
||||
* Even in the situation where there are no bindings, the static styling is still placed into the
|
||||
* context because there may be another directive on the same element that has styling.
|
||||
*
|
||||
* When Angular initializes styling data for an element then it will first register the static
|
||||
* styling values on the element using one of these two instructions:
|
||||
*
|
||||
* 1. elementStart or element (within the template function of a component)
|
||||
* 2. elementHostAttrs (for directive host bindings)
|
||||
*
|
||||
* In either case, a styling context will be created and stored within an element's LViewData. Once
|
||||
* the styling context is created then single and multi properties can stored within it. For this to
|
||||
* happen, the following function needs to be called:
|
||||
*
|
||||
* `elementStyling` (called with style properties, class properties and a sanitizer + a directive
|
||||
* instance).
|
||||
*
|
||||
* When this instruction is called it will populate the styling context with the provided style
|
||||
* and class names into the context.
|
||||
*
|
||||
* The context itself looks like this:
|
||||
*
|
||||
* context = [
|
||||
* // 0-8: header values (about 8 entries of configuration data)
|
||||
* // 9+: this is where each entry is stored:
|
||||
* element,
|
||||
* playerContext | null,
|
||||
* styleSanitizer | null,
|
||||
* [null, '100px', '200px', true], // property names are not needed since they have already been
|
||||
* written to DOM.
|
||||
*
|
||||
* configMasterVal,
|
||||
* 1, // this instructs how many `style` values there are so that class index values can be
|
||||
* offsetted
|
||||
* { classOne: true, classTwo: false } | 'classOne classTwo' | null // last class value provided
|
||||
* into updateStylingMap
|
||||
* { styleOne: '100px', styleTwo: 0 } | null // last style value provided into updateStylingMap
|
||||
*
|
||||
* // 8
|
||||
* 'width',
|
||||
* pointers(1, 15); // Point to static `width`: `100px` and multi `width`.
|
||||
* null,
|
||||
*
|
||||
* // 11
|
||||
* 'height',
|
||||
* pointers(2, 18); // Point to static `height`: `200px` and multi `height`.
|
||||
* null,
|
||||
*
|
||||
* // 14
|
||||
* 'foo',
|
||||
* pointers(1, 21); // Point to static `foo`: `true` and multi `foo`.
|
||||
* null,
|
||||
*
|
||||
* // 17
|
||||
* 'width',
|
||||
* pointers(1, 6); // Point to static `width`: `100px` and single `width`.
|
||||
* null,
|
||||
*
|
||||
* // 21
|
||||
* 'height',
|
||||
* pointers(2, 9); // Point to static `height`: `200px` and single `height`.
|
||||
* null,
|
||||
*
|
||||
* // 24
|
||||
* 'foo',
|
||||
* pointers(3, 12); // Point to static `foo`: `true` and single `foo`.
|
||||
* null,
|
||||
* ]
|
||||
*
|
||||
* Let's say we have the following template code:
|
||||
*
|
||||
* ```
|
||||
* <div class="foo bar"
|
||||
* style="width:200px; color:red"
|
||||
* [style.width]="myWidthExp"
|
||||
* [style.height]="myHeightExp"
|
||||
* [class.baz]="myBazExp">
|
||||
* ```
|
||||
*
|
||||
* The context generated from these values will look like this (note that
|
||||
* for each binding name (the class and style bindings) the values will
|
||||
* be inserted twice into the array (once for single property entries) and
|
||||
* another for multi property entries).
|
||||
*
|
||||
* context = [
|
||||
* // 0-8: header values (about 8 entries of configuration data)
|
||||
* // 9+: this is where each entry is stored:
|
||||
*
|
||||
* // SINGLE PROPERTIES
|
||||
* configForWidth,
|
||||
* 'width'
|
||||
* myWidthExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForHeight,
|
||||
* 'height'
|
||||
* myHeightExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForBazClass,
|
||||
* 'baz
|
||||
* myBazClassExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* // MULTI PROPERTIES
|
||||
* configForWidth,
|
||||
* 'width'
|
||||
* myWidthExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForHeight,
|
||||
* 'height'
|
||||
* myHeightExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForBazClass,
|
||||
* 'baz
|
||||
* myBazClassExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
* ]
|
||||
*
|
||||
* The configuration values are left out of the example above because
|
||||
* the ordering of them could change between code patches. Please read the
|
||||
* documentation below to get a better understand of what the configuration
|
||||
* values are and how they work.
|
||||
*
|
||||
* Each time a binding property is updated (whether it be through a single
|
||||
* property instruction like `elementStyleProp`, `elementClassProp` or
|
||||
* `elementStylingMap`) then the values in the context will be updated as
|
||||
* well.
|
||||
*
|
||||
* If for example `[style.width]` updates to `555px` then its value will be reflected
|
||||
* in the context as so:
|
||||
*
|
||||
* context = [
|
||||
* // ...
|
||||
* configForWidth, // this will be marked DIRTY
|
||||
* 'width'
|
||||
* '555px',
|
||||
* 0,
|
||||
* //..
|
||||
* ]
|
||||
*
|
||||
* The context and directive data will also be marked dirty.
|
||||
*
|
||||
* Despite the context being updated, nothing has been rendered on screen (not styles or
|
||||
* classes have been set on the element). To kick off rendering for an element the following
|
||||
* function needs to be run `elementStylingApply`.
|
||||
*
|
||||
* `elementStylingApply` will run through the context and find each dirty value and render them onto
|
||||
* the element. Once complete, all styles/classes will be set to clean. Because of this, the render
|
||||
* function will now know not to rerun itself again if called again unless new style/class values
|
||||
* have changed.
|
||||
*
|
||||
* ## Directives
|
||||
* Directives style values (which are provided through host bindings) are also supported and
|
||||
* housed within the same styling context as are template-level style/class properties/bindings.
|
||||
* Both directive-level and template-level styling bindings share the same context.
|
||||
*
|
||||
* Each of the following instructions supports accepting a directive instance as an input parameter:
|
||||
*
|
||||
* - `elementHostAttrs`
|
||||
* - `elementStyling`
|
||||
* - `elementStyleProp`
|
||||
* - `elementClassProp`
|
||||
* - `elementStylingMap`
|
||||
* - `elementStylingApply`
|
||||
*
|
||||
* Each time a directiveRef is passed in, it will be converted into an index by examining the
|
||||
* directive registry (which lives in the context configuration area). The index is then used
|
||||
* to help single style properties figure out where a value is located in the context.
|
||||
*
|
||||
* If two directives or a directive + a template binding both write to the same style/class
|
||||
* binding then the styling context code will decide which one wins based on the following
|
||||
* rule:
|
||||
*
|
||||
* 1. If the template binding has a value then it always wins
|
||||
* 2. If not then whichever first-registered directive that has that value first will win
|
||||
*
|
||||
* The code example helps make this clear:
|
||||
*
|
||||
* ```
|
||||
* <div [style.width]="myWidth" [my-width-directive]="'600px">
|
||||
* @Directive({ selector: '[my-width-directive' ]})
|
||||
* class MyWidthDirective {
|
||||
* @Input('my-width-directive')
|
||||
* @HostBinding('style.width')
|
||||
* public width = null;
|
||||
* function pointers(staticIndex: number, dynamicIndex: number) {
|
||||
* // combine the two indices into a single word.
|
||||
* return (staticIndex << StylingFlags.BitCountSize) |
|
||||
* (dynamicIndex << (StylingIndex.BitCountSize + StylingFlags.BitCountSize));
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Since there is a style binding for width present on the element (`[style.width]`) then
|
||||
* it will always win over the width binding that is present as a host binding within
|
||||
* the `MyWidthDirective`. However, if `[style.width]` renders as `null` (so `myWidth=null`)
|
||||
* then the `MyWidthDirective` will be able to write to the `width` style within the context.
|
||||
* Simply put, whichever directive writes to a value ends up having ownership of it.
|
||||
* The values are duplicated so that space is set aside for both multi ([style] and [class])
|
||||
* and single ([style.prop] or [class.named]) values. The respective config values
|
||||
* (configValA, configValB, etc...) are a combination of the StylingFlags with two index
|
||||
* values: the `initialIndex` (which points to the index location of the style value in
|
||||
* the initial styles array in slot 0) and the `dynamicIndex` (which points to the
|
||||
* matching single/multi index position in the context array for the same prop).
|
||||
*
|
||||
* The way in which the ownership is facilitated is through index value. The earliest directives
|
||||
* get the smallest index values (with 0 being reserved for the template element bindings). Each
|
||||
* time a value is written from a directive or the template bindings, the value itself gets
|
||||
* assigned the directive index value in its data. If another directive writes a value again then
|
||||
* its directive index gets compared against the directive index that exists on the element. Only
|
||||
* when the new value's directive index is less than the existing directive index then the new
|
||||
* value will be written to the context.
|
||||
*
|
||||
* Each directive also has its own sanitizer and dirty flags. These values are consumed within the
|
||||
* rendering function.
|
||||
* This means that every time `updateStyleProp` or `updateClassProp` are called then they
|
||||
* must be called using an index value (not a property string) which references the index
|
||||
* value of the initial style prop/class when the context was created. This also means that
|
||||
* `updateStyleProp` or `updateClassProp` cannot be called with a new property (only
|
||||
* `updateStylingMap` can include new CSS properties that will be added to the context).
|
||||
*/
|
||||
export interface StylingContext extends
|
||||
Array<{[key: string]: any}|number|string|boolean|RElement|StyleSanitizeFn|PlayerContext|null> {
|
||||
export interface StylingContext extends Array<InitialStyles|{[key: string]: any}|number|string|
|
||||
boolean|RElement|StyleSanitizeFn|PlayerContext|null> {
|
||||
/**
|
||||
* Location of animation context (which contains the active players) for this element styling
|
||||
* context.
|
||||
*/
|
||||
[StylingIndex.PlayerContext]: PlayerContext|null;
|
||||
|
||||
/**
|
||||
* The style sanitizer that is used within this context
|
||||
*/
|
||||
[StylingIndex.StyleSanitizerPosition]: StyleSanitizeFn|null;
|
||||
|
||||
/**
|
||||
* Location of initial data shared by all instances of this style.
|
||||
*/
|
||||
[StylingIndex.InitialStylesPosition]: InitialStyles;
|
||||
|
||||
/**
|
||||
* A numeric value representing the configuration status (whether the context is dirty or not)
|
||||
* mixed together (using bit shifting) with a index value which tells the starting index value
|
||||
@ -209,27 +140,12 @@ export interface StylingContext extends
|
||||
*/
|
||||
[StylingIndex.MasterFlagPosition]: number;
|
||||
|
||||
/**
|
||||
* Location of the collection of directives for this context
|
||||
*/
|
||||
[StylingIndex.DirectiveRegistryPosition]: DirectiveRegistryValues;
|
||||
|
||||
/**
|
||||
* Location of all static styles values
|
||||
*/
|
||||
[StylingIndex.InitialStyleValuesPosition]: InitialStylingValues;
|
||||
|
||||
/**
|
||||
* Location of all static class values
|
||||
*/
|
||||
[StylingIndex.InitialClassValuesPosition]: InitialStylingValues;
|
||||
|
||||
/**
|
||||
* A numeric value representing the class index offset value. Whenever a single class is
|
||||
* applied (using `elementClassProp`) it should have an styling index value that doesn't
|
||||
* need to take into account any style values that exist in the context.
|
||||
*/
|
||||
[StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues;
|
||||
[StylingIndex.ClassOffsetPosition]: number;
|
||||
|
||||
/**
|
||||
* Location of element that is used as a target for this context.
|
||||
@ -240,206 +156,24 @@ export interface StylingContext extends
|
||||
* The last class value that was interpreted by elementStylingMap. This is cached
|
||||
* So that the algorithm can exit early incase the value has not changed.
|
||||
*/
|
||||
[StylingIndex.CachedClassValueOrInitialClassString]: {[key: string]: any}|string|(string)[]|null;
|
||||
[StylingIndex.PreviousOrCachedMultiClassValue]: {[key: string]: any}|string|null;
|
||||
|
||||
/**
|
||||
* The last style value that was interpreted by elementStylingMap. This is cached
|
||||
* So that the algorithm can exit early incase the value has not changed.
|
||||
*/
|
||||
[StylingIndex.CachedStyleValue]: {[key: string]: any}|(string)[]|null;
|
||||
|
||||
/**
|
||||
* Location of animation context (which contains the active players) for this element styling
|
||||
* context.
|
||||
*/
|
||||
[StylingIndex.PlayerContext]: PlayerContext|null;
|
||||
[StylingIndex.PreviousMultiStyleValue]: {[key: string]: any}|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as a styling array to house static class and style values that were extracted
|
||||
* by the compiler and placed in the animation context via `elementStart` and
|
||||
* `elementHostAttrs`.
|
||||
* The initial styles is populated whether or not there are any initial styles passed into
|
||||
* the context during allocation. The 0th value must be null so that index values of `0` within
|
||||
* the context flags can always point to a null value safely when nothing is set.
|
||||
*
|
||||
* See [InitialStylingValuesIndex] for a breakdown of how all this works.
|
||||
* All other entries in this array are of `string` value and correspond to the values that
|
||||
* were extracted from the `style=""` attribute in the HTML code for the provided template.
|
||||
*/
|
||||
export interface InitialStylingValues extends Array<string|boolean|null> { [0]: null; }
|
||||
|
||||
/**
|
||||
* Used as an offset/position index to figure out where initial styling
|
||||
* values are located.
|
||||
*
|
||||
* Used as a reference point to provide markers to all static styling
|
||||
* values (the initial style and class values on an element) within an
|
||||
* array within the StylingContext. This array contains key/value pairs
|
||||
* where the key is the style property name or className and the value is
|
||||
* the style value or whether or not a class is present on the elment.
|
||||
*
|
||||
* The first value is also always null so that a initial index value of
|
||||
* `0` will always point to a null value.
|
||||
*
|
||||
* If a <div> elements contains a list of static styling values like so:
|
||||
*
|
||||
* <div class="foo bar baz" style="width:100px; height:200px;">
|
||||
*
|
||||
* Then the initial styles for that will look like so:
|
||||
*
|
||||
* Styles:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'width', '100px', height, '200px'
|
||||
* ]
|
||||
*
|
||||
* Classes:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'foo', true, 'bar', true, 'baz', true
|
||||
* ]
|
||||
*
|
||||
* Initial style and class entries have their own arrays. This is because
|
||||
* it's easier to add to the end of one array and not then have to update
|
||||
* every context entries' pointer index to the newly offseted values.
|
||||
*
|
||||
* When property bindinds are added to a context then initial style/class
|
||||
* values will also be inserted into the array. This is to create a space
|
||||
* in the situation when a follow-up directive inserts static styling into
|
||||
* the array. By default style values are `null` and class values are
|
||||
* `false` when inserted by property bindings.
|
||||
*
|
||||
* For example:
|
||||
* <div class="foo bar baz"
|
||||
* [class.car]="myCarExp"
|
||||
* style="width:100px; height:200px;"
|
||||
* [style.opacity]="myOpacityExp">
|
||||
*
|
||||
* Will construct initial styling values that look like:
|
||||
*
|
||||
* Styles:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'width', '100px', height, '200px', 'opacity', null
|
||||
* ]
|
||||
*
|
||||
* Classes:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'foo', true, 'bar', true, 'baz', true, 'car', false
|
||||
* ]
|
||||
*
|
||||
* Now if a directive comes along and introduces `car` as a static
|
||||
* class value or `opacity` then those values will be filled into
|
||||
* the initial styles array.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @Directive({
|
||||
* selector: 'opacity-car-directive',
|
||||
* host: {
|
||||
* 'style': 'opacity:0.5',
|
||||
* 'class': 'car'
|
||||
* }
|
||||
* })
|
||||
* class OpacityCarDirective {}
|
||||
*
|
||||
* This will render itself as:
|
||||
*
|
||||
* Styles:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'width', '100px', height, '200px', 'opacity', null
|
||||
* ]
|
||||
*
|
||||
* Classes:
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, 'foo', true, 'bar', true, 'baz', true, 'car', false
|
||||
* ]
|
||||
*/
|
||||
export const enum InitialStylingValuesIndex {
|
||||
KeyValueStartPosition = 1,
|
||||
PropOffset = 0,
|
||||
ValueOffset = 1,
|
||||
Size = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* An array located in the StylingContext that houses all directive instances and additional
|
||||
* data about them.
|
||||
*
|
||||
* Each entry in this array represents a source of where style/class binding values could
|
||||
* come from. By default, there is always at least one directive here with a null value and
|
||||
* that represents bindings that live directly on an element (not host bindings).
|
||||
*
|
||||
* Each successive entry in the array is an actual instance of an array as well as some
|
||||
* additional info.
|
||||
*
|
||||
* An entry within this array has the following values:
|
||||
* [0] = The instance of the directive (or null when it is not a directive, but a template binding
|
||||
* source)
|
||||
* [1] = The pointer that tells where the single styling (stuff like [class.foo] and [style.prop])
|
||||
* offset values are located. This value will allow for a binding instruction to find exactly
|
||||
* where a style is located.
|
||||
* [2] = Whether or not the directive has any styling values that are dirty. This is used as
|
||||
* reference within the renderClassAndStyleBindings function to decide whether to skip
|
||||
* iterating through the context when rendering is executed.
|
||||
* [3] = The styleSanitizer instance that is assigned to the directive. Although it's unlikely,
|
||||
* a directive could introduce its own special style sanitizer and for this reach each
|
||||
* directive will get its own space for it (if null then the very first sanitizer is used).
|
||||
*
|
||||
* Each time a new directive is added it will insert these four values at the end of the array.
|
||||
* When this array is examined (using indexOf) then the resulting directiveIndex will be resolved
|
||||
* by dividing the index value by the size of the array entries (so if DirA is at spot 8 then its
|
||||
* index will be 2).
|
||||
*/
|
||||
export interface DirectiveRegistryValues extends Array<null|{}|boolean|number|StyleSanitizeFn> {
|
||||
[DirectiveRegistryValuesIndex.DirectiveValueOffset]: null;
|
||||
[DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset]: number;
|
||||
[DirectiveRegistryValuesIndex.DirtyFlagOffset]: boolean;
|
||||
[DirectiveRegistryValuesIndex.StyleSanitizerOffset]: StyleSanitizeFn|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the offset/position values for each directive entry and its data
|
||||
* that are housed inside of [DirectiveRegistryValues].
|
||||
*/
|
||||
export const enum DirectiveRegistryValuesIndex {
|
||||
DirectiveValueOffset = 0,
|
||||
SinglePropValuesIndexOffset = 1,
|
||||
DirtyFlagOffset = 2,
|
||||
StyleSanitizerOffset = 3,
|
||||
Size = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* An array that contains the index pointer values for every single styling property
|
||||
* that exists in the context and for every directive. It also contains the total
|
||||
* single styles and single classes that exists in the context as the first two values.
|
||||
*
|
||||
* Let's say we have the following template code:
|
||||
*
|
||||
* <div [style.width]="myWidth"
|
||||
* [style.height]="myHeight"
|
||||
* [class.flipped]="flipClass"
|
||||
* directive-with-opacity>
|
||||
* directive-with-foo-bar-classes>
|
||||
*
|
||||
* We have two directive and template-binding sources,
|
||||
* 2 + 1 styles and 1 + 1 classes. When the bindings are
|
||||
* registered the SinglePropOffsets array will look like so:
|
||||
*
|
||||
* s_0/c_0 = template directive value
|
||||
* s_1/c_1 = directive one (directive-with-opacity)
|
||||
* s_2/c_2 = directive two (directive-with-foo-bar-classes)
|
||||
*
|
||||
* [3, 2, 2, 1, s_00, s01, c_01, 1, 0, s_10, 0, 1, c_20
|
||||
*/
|
||||
export interface SinglePropOffsetValues extends Array<number> {
|
||||
[SinglePropOffsetValuesIndex.StylesCountPosition]: number;
|
||||
[SinglePropOffsetValuesIndex.ClassesCountPosition]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the offset/position values for each single prop/class entry
|
||||
* that are housed inside of [SinglePropOffsetValues].
|
||||
*/
|
||||
export const enum SinglePropOffsetValuesIndex {
|
||||
StylesCountPosition = 0,
|
||||
ClassesCountPosition = 1,
|
||||
ValueStartPosition = 2
|
||||
}
|
||||
export interface InitialStyles extends Array<string|null|boolean> { [0]: null; }
|
||||
|
||||
/**
|
||||
* Used to set the context to be dirty or not both on the master flag (position 1)
|
||||
@ -447,49 +181,47 @@ export const enum SinglePropOffsetValuesIndex {
|
||||
*/
|
||||
export const enum StylingFlags {
|
||||
// Implies no configurations
|
||||
None = 0b000000,
|
||||
None = 0b00000,
|
||||
// Whether or not the entry or context itself is dirty
|
||||
Dirty = 0b000001,
|
||||
Dirty = 0b00001,
|
||||
// Whether or not this is a class-based assignment
|
||||
Class = 0b000010,
|
||||
Class = 0b00010,
|
||||
// Whether or not a sanitizer was applied to this property
|
||||
Sanitize = 0b000100,
|
||||
Sanitize = 0b00100,
|
||||
// Whether or not any player builders within need to produce new players
|
||||
PlayerBuildersDirty = 0b001000,
|
||||
PlayerBuildersDirty = 0b01000,
|
||||
// If NgClass is present (or some other class handler) then it will handle the map expressions and
|
||||
// initial classes
|
||||
OnlyProcessSingleClasses = 0b010000,
|
||||
OnlyProcessSingleClasses = 0b10000,
|
||||
// The max amount of bits used to represent these configuration values
|
||||
BindingAllocationLocked = 0b100000,
|
||||
BitCountSize = 6,
|
||||
// There are only six bits here
|
||||
BitMask = 0b111111
|
||||
BitCountSize = 5,
|
||||
// There are only five bits here
|
||||
BitMask = 0b11111
|
||||
}
|
||||
|
||||
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
||||
export const enum StylingIndex {
|
||||
// Index of location where the start of single properties are stored. (`updateStyleProp`)
|
||||
MasterFlagPosition = 0,
|
||||
// Position of where the registered directives exist for this styling context
|
||||
DirectiveRegistryPosition = 1,
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
InitialStyleValuesPosition = 2,
|
||||
InitialClassValuesPosition = 3,
|
||||
PlayerContext = 0,
|
||||
// Position of where the style sanitizer is stored within the styling context
|
||||
StyleSanitizerPosition = 1,
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
InitialStylesPosition = 2,
|
||||
// Index of location where the start of single properties are stored. (`updateStyleProp`)
|
||||
MasterFlagPosition = 3,
|
||||
// Index of location where the class index offset value is located
|
||||
SinglePropOffsetPositions = 4,
|
||||
ClassOffsetPosition = 4,
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
// This index must align with HOST, see interfaces/view.ts
|
||||
ElementPosition = 5,
|
||||
// Position of where the last string-based CSS class value was stored (or a cached version of the
|
||||
// initial styles when a [class] directive is present)
|
||||
CachedClassValueOrInitialClassString = 6,
|
||||
PreviousOrCachedMultiClassValue = 6,
|
||||
// Position of where the last string-based CSS class value was stored
|
||||
CachedStyleValue = 7,
|
||||
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
PlayerContext = 8,
|
||||
PreviousMultiStyleValue = 7,
|
||||
// Location of single (prop) value entries are stored within the context
|
||||
SingleStylesStartPosition = 9,
|
||||
SingleStylesStartPosition = 8,
|
||||
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
|
||||
FlagsOffset = 0,
|
||||
PropertyOffset = 1,
|
||||
ValueOffset = 2,
|
||||
@ -501,16 +233,3 @@ export const enum StylingIndex {
|
||||
// The binary digit value as a mask
|
||||
BitMask = 0b11111111111111, // 14 bits
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the bit flag data for directive owner and player index
|
||||
* values that exist within en entry that lives in the StylingContext.
|
||||
*
|
||||
* The values here split a number value into two sets of bits:
|
||||
* - The first 16 bits are used to store the directiveIndex that owns this style value
|
||||
* - The other 16 bits are used to store the playerBuilderIndex that is attached to this style
|
||||
*/
|
||||
export const enum DirectiveOwnerAndPlayerBuilderIndex {
|
||||
BitCountSize = 16,
|
||||
BitMask = 0b1111111111111111
|
||||
}
|
||||
|
@ -221,25 +221,16 @@ export const enum LViewFlags {
|
||||
* back into the parent view, `data` will be defined and `creationMode` will be
|
||||
* improperly reported as false.
|
||||
*/
|
||||
CreationMode = 0b000000001,
|
||||
|
||||
/**
|
||||
* Whether or not this LView instance is on its first processing pass.
|
||||
*
|
||||
* An LView instance is considered to be on its "first pass" until it
|
||||
* has completed one creation mode run and one update mode run. At this
|
||||
* time, the flag is turned off.
|
||||
*/
|
||||
FirstLViewPass = 0b000000010,
|
||||
CreationMode = 0b0000001,
|
||||
|
||||
/** Whether this view has default change detection strategy (checks always) or onPush */
|
||||
CheckAlways = 0b000000100,
|
||||
CheckAlways = 0b0000010,
|
||||
|
||||
/** Whether or not this view is currently dirty (needing check) */
|
||||
Dirty = 0b000001000,
|
||||
Dirty = 0b0000100,
|
||||
|
||||
/** Whether or not this view is currently attached to change detection tree. */
|
||||
Attached = 0b000010000,
|
||||
Attached = 0b0001000,
|
||||
|
||||
/**
|
||||
* Whether or not the init hooks have run.
|
||||
@ -248,13 +239,13 @@ export const enum LViewFlags {
|
||||
* runs OR the first cR() instruction that runs (so inits are run for the top level view before
|
||||
* any embedded views).
|
||||
*/
|
||||
RunInit = 0b000100000,
|
||||
RunInit = 0b0010000,
|
||||
|
||||
/** Whether or not this view is destroyed. */
|
||||
Destroyed = 0b001000000,
|
||||
Destroyed = 0b0100000,
|
||||
|
||||
/** Whether or not this view is the root view */
|
||||
IsRoot = 0b010000000,
|
||||
IsRoot = 0b1000000,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,13 +134,10 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||
encapsulation: ViewEncapsulation;
|
||||
viewProviders: Provider[]|null;
|
||||
interpolation?: [string, string];
|
||||
changeDetection?: ChangeDetectionStrategy;
|
||||
}
|
||||
|
||||
export type ViewEncapsulation = number;
|
||||
|
||||
export type ChangeDetectionStrategy = number;
|
||||
|
||||
export interface R3QueryMetadataFacade {
|
||||
propertyName: string;
|
||||
first: boolean;
|
||||
|
@ -7,15 +7,14 @@
|
||||
*/
|
||||
|
||||
import {ComponentType} from '..';
|
||||
import {resolveForwardRef} from '../../di/forward_ref';
|
||||
import {Query} from '../../metadata/di';
|
||||
import {Component, Directive} from '../../metadata/directives';
|
||||
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
|
||||
import {ViewEncapsulation} from '../../metadata/view';
|
||||
import {Type} from '../../type';
|
||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||
import {stringify} from '../../util';
|
||||
import {EMPTY_ARRAY} from '../definition';
|
||||
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from '../fields';
|
||||
import {stringify} from '../util';
|
||||
|
||||
import {R3DirectiveMetadataFacade, getCompilerFacade} from './compiler_facade';
|
||||
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from './compiler_facade_interface';
|
||||
@ -62,7 +61,6 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
||||
animations: metadata.animations,
|
||||
viewQueries: extractQueriesMetadata(type, getReflect().propMetadata(type), isViewQuery),
|
||||
directives: [],
|
||||
changeDetection: metadata.changeDetection,
|
||||
pipes: new Map(),
|
||||
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated,
|
||||
interpolation: metadata.interpolation,
|
||||
@ -155,8 +153,10 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
|
||||
};
|
||||
}
|
||||
|
||||
const EMPTY_OBJ = {};
|
||||
|
||||
function convertToR3QueryPredicate(selector: any): any|string[] {
|
||||
return typeof selector === 'string' ? splitByComma(selector) : resolveForwardRef(selector);
|
||||
return typeof selector === 'string' ? splitByComma(selector) : selector;
|
||||
}
|
||||
|
||||
export function convertToR3QueryMetadata(propertyName: string, ann: Query): R3QueryMetadataFacade {
|
||||
|
@ -78,7 +78,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||
'ɵload': r3.load,
|
||||
'ɵprojection': r3.projection,
|
||||
'ɵelementProperty': r3.elementProperty,
|
||||
'ɵcomponentHostSyntheticProperty': r3.componentHostSyntheticProperty,
|
||||
'ɵpipeBind1': r3.pipeBind1,
|
||||
'ɵpipeBind2': r3.pipeBind2,
|
||||
'ɵpipeBind3': r3.pipeBind3,
|
||||
@ -91,7 +90,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||
'ɵregisterContentQuery': r3.registerContentQuery,
|
||||
'ɵreference': r3.reference,
|
||||
'ɵelementStyling': r3.elementStyling,
|
||||
'ɵelementHostAttrs': r3.elementHostAttrs,
|
||||
'ɵelementStylingMap': r3.elementStylingMap,
|
||||
'ɵelementStyleProp': r3.elementStyleProp,
|
||||
'ɵelementStylingApply': r3.elementStylingApply,
|
||||
|
@ -107,12 +107,10 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
|
||||
ngModuleDef = getCompilerFacade().compileNgModule(
|
||||
angularCoreEnv, `ng://${moduleType.name}/ngModuleDef.js`, {
|
||||
type: moduleType,
|
||||
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY, resolveForwardRef),
|
||||
declarations: declarations.map(resolveForwardRef),
|
||||
imports: flatten(ngModule.imports || EMPTY_ARRAY, resolveForwardRef)
|
||||
.map(expandModuleWithProviders),
|
||||
exports: flatten(ngModule.exports || EMPTY_ARRAY, resolveForwardRef)
|
||||
.map(expandModuleWithProviders),
|
||||
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY),
|
||||
declarations: declarations,
|
||||
imports: flatten(ngModule.imports || EMPTY_ARRAY).map(expandModuleWithProviders),
|
||||
exports: flatten(ngModule.exports || EMPTY_ARRAY).map(expandModuleWithProviders),
|
||||
emitInline: true,
|
||||
});
|
||||
}
|
||||
@ -134,8 +132,8 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
|
||||
deps: reflectDependencies(moduleType),
|
||||
providers: ngModule.providers || EMPTY_ARRAY,
|
||||
imports: [
|
||||
(ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef),
|
||||
(ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef),
|
||||
ngModule.imports || EMPTY_ARRAY,
|
||||
ngModule.exports || EMPTY_ARRAY,
|
||||
],
|
||||
};
|
||||
ngInjectorDef = getCompilerFacade().compileInjector(
|
||||
@ -156,8 +154,8 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void {
|
||||
const errors: string[] = [];
|
||||
ngModuleDef.declarations.forEach(verifyDeclarationsHaveDefinitions);
|
||||
const combinedDeclarations: Type<any>[] = [
|
||||
...ngModuleDef.declarations.map(resolveForwardRef), //
|
||||
...flatten(ngModuleDef.imports.map(computeCombinedExports), resolveForwardRef),
|
||||
...ngModuleDef.declarations, //
|
||||
...flatten(ngModuleDef.imports.map(computeCombinedExports)),
|
||||
];
|
||||
ngModuleDef.exports.forEach(verifyExportsAreDeclaredOrReExported);
|
||||
ngModuleDef.declarations.forEach(verifyDeclarationIsUnique);
|
||||
|
@ -15,7 +15,7 @@ import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||
import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeType} from './node_assert';
|
||||
import {findComponentView, getNativeByTNode, isLContainer, isRootView, readElementValue, stringify} from './util';
|
||||
import {getNativeByTNode, isLContainer, isRootView, readElementValue, stringify} from './util';
|
||||
|
||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||
|
||||
@ -195,6 +195,24 @@ function walkTNodeTree(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a current view, finds the nearest component's host (LElement).
|
||||
*
|
||||
* @param lView LView for which we want a host element node
|
||||
* @returns The host node
|
||||
*/
|
||||
export function findComponentView(lView: LView): LView {
|
||||
let rootTNode = lView[HOST_NODE];
|
||||
|
||||
while (rootTNode && rootTNode.type === TNodeType.View) {
|
||||
ngDevMode && assertDefined(lView[PARENT], 'lView.parent');
|
||||
lView = lView[PARENT] !;
|
||||
rootTNode = lView[HOST_NODE];
|
||||
}
|
||||
|
||||
return lView;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: for performance reasons, the possible actions are inlined within the function instead of
|
||||
* being passed as an argument.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user