Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
7bd6cf9f0a | |||
70a6c54dcf | |||
9245e9ec4f | |||
e76f33f3cc | |||
b60f03e074 | |||
4571dffbc9 | |||
d9c0eb7c78 | |||
a40e883799 | |||
b225f03174 | |||
369945d4d6 | |||
7968539a17 | |||
56b6df8e78 | |||
fa4c3ab5b0 | |||
515befbefc | |||
4d8620cb91 | |||
ac801898bf | |||
e107a13aba | |||
3655182d63 | |||
927fb0493b | |||
3c28aeec4a | |||
dbd91473fc | |||
e6a5dc96b0 | |||
fae2316a7f | |||
9f65684214 | |||
6249680344 | |||
9f9949296a | |||
07ab92f037 | |||
35f551da35 | |||
621685be4b | |||
ecf71f74aa | |||
a9692b6e59 | |||
23402c4b68 | |||
ba5c6b5e7c | |||
6676073a4f | |||
1aabfbb312 | |||
124b3dfa91 | |||
6a79e6caf9 | |||
7275dfba15 | |||
19273a89b6 | |||
957192db6d | |||
4546f09b0e | |||
c7e4b86bf1 | |||
3a67339945 | |||
80fef43334 | |||
80efa4b26b | |||
92103606e1 | |||
487970f6f2 | |||
89a493934f | |||
6f54ed01a8 | |||
ef99670343 | |||
808e4cf1b6 | |||
9cb71fc7c9 | |||
6f082f93f5 | |||
0d7124e7cb | |||
52896591e6 | |||
07c44a76f9 | |||
3b5fc56cc1 | |||
2c30365496 | |||
8c6b376c07 | |||
089d4533b3 | |||
8113a03c21 | |||
93854e00e0 | |||
fddf30cfaf | |||
0fd859ca0b | |||
f274eeb9fc | |||
675e3d4244 | |||
767841b434 | |||
7bce0153bb | |||
7a1f964201 | |||
3dffbb6cf1 | |||
79f1798b68 | |||
dd08d421a1 | |||
a2d4299f2c | |||
2598b59de7 | |||
20b4617289 | |||
958bb0da04 | |||
4ba8f1989b | |||
c04b4d795a | |||
f1b5ba9231 | |||
50b524ba1e | |||
680ceb7d65 | |||
ea186d5ccd | |||
10455044f1 | |||
31150fe6e8 | |||
9223066123 |
83
CHANGELOG.md
83
CHANGELOG.md
@ -1,3 +1,86 @@
|
|||||||
|
<a name="2.1.2"></a>
|
||||||
|
## [2.1.2](https://github.com/angular/angular/compare/2.1.1...2.1.2) (2016-10-27)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** don't access view local variables nor pipes in host expressions ([#12396](https://github.com/angular/angular/issues/12396)) ([867494a](https://github.com/angular/angular/commit/867494a)), closes [#12004](https://github.com/angular/angular/issues/12004) [#12071](https://github.com/angular/angular/issues/12071)
|
||||||
|
* **compiler:** walk third party modules ([#12453](https://github.com/angular/angular/issues/12453)) ([a838aba](https://github.com/angular/angular/commit/a838aba)), closes [#11889](https://github.com/angular/angular/issues/11889) [#12428](https://github.com/angular/angular/issues/12428)
|
||||||
|
* **compiler:** remove double exports of template_ast ([7742ec0](https://github.com/angular/angular/commit/7742ec0))
|
||||||
|
* **compiler:** use Maps instead of objects in selector implementation ([d321b0e](https://github.com/angular/angular/commit/d321b0e))
|
||||||
|
* **compiler-cli:** fix types ([ef15364](https://github.com/angular/angular/commit/ef15364))
|
||||||
|
* **compiler-cli:** assert that all pipes and directives are declared by a module ([7221632](https://github.com/angular/angular/commit/7221632))
|
||||||
|
* **http:** overwrite already set xsrf header ([b4265e0](https://github.com/angular/angular/commit/b4265e0))
|
||||||
|
* **router:** add a test to make sure canDeactivate guards are called for aux routes ([fc60fa7](https://github.com/angular/angular/commit/fc60fa7)), closes [#11345](https://github.com/angular/angular/issues/11345)
|
||||||
|
* **router:** canDeactivate guards are not triggered for componentless routes ([b741853](https://github.com/angular/angular/commit/b741853)), closes [#12375](https://github.com/angular/angular/issues/12375)
|
||||||
|
* **router:** change router not to deactivate aux routes when navigating from a componentless routes ([52a853e](https://github.com/angular/angular/commit/52a853e))
|
||||||
|
* **router:** disallow component routes with named outlets ([8f2fa0f](https://github.com/angular/angular/commit/8f2fa0f)), closes [#11208](https://github.com/angular/angular/issues/11208) [#11082](https://github.com/angular/angular/issues/11082)
|
||||||
|
* **router:** preserve resolve data ([6ccbfd4](https://github.com/angular/angular/commit/6ccbfd4)), closes [#12306](https://github.com/angular/angular/issues/12306)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.0-beta.1"></a>
|
||||||
|
# [2.2.0-beta.1](https://github.com/angular/angular/compare/2.2.0-beta.0...2.2.0-beta.1) (2016-10-27)
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* upgrade: Four newly added APIs in 2.2.0-beta:
|
||||||
|
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
||||||
|
Import these from @angular/upgrade/static instead.
|
||||||
|
|
||||||
|
|
||||||
|
Note: The 2.2.0-beta.1 release also contains all the changes present in the 2.1.2 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.1.1](https://github.com/angular/angular/compare/2.1.0...2.1.1) (2016-10-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** generate aot code for animation trigger output events ([#12291](https://github.com/angular/angular/issues/12291)) ([6e5f8b5](https://github.com/angular/angular/commit/6e5f8b5)), closes [#11707](https://github.com/angular/angular/issues/11707)
|
||||||
|
* **compiler:** don't redeclare a var in the same scope ([#12386](https://github.com/angular/angular/issues/12386)) ([cca4a5c](https://github.com/angular/angular/commit/cca4a5c))
|
||||||
|
* **core:** fix decorator default values ([bd1dcb5](https://github.com/angular/angular/commit/bd1dcb5))
|
||||||
|
* **core:** fix property decorators ([3993279](https://github.com/angular/angular/commit/3993279)), closes [#12224](https://github.com/angular/angular/issues/12224)
|
||||||
|
* **http:** make normalizeMethodName optimizer-compatible. ([#12370](https://github.com/angular/angular/issues/12370)) ([8409b65](https://github.com/angular/angular/commit/8409b65))
|
||||||
|
* **router:** correctly export filter operator in es5 ([#12286](https://github.com/angular/angular/issues/12286)) ([27d7677](https://github.com/angular/angular/commit/27d7677))
|
||||||
|
* **router:** do not update primary route if only secondary outlet is given ([#11797](https://github.com/angular/angular/issues/11797)) ([da5fc69](https://github.com/angular/angular/commit/da5fc69))
|
||||||
|
* **router:** fix lazy loading triggered by redirects from wildcard routes ([5ae6915](https://github.com/angular/angular/commit/5ae6915)), closes [#12183](https://github.com/angular/angular/issues/12183)
|
||||||
|
* **router:** module loader should start compiling modules when stubbedModules are set ([#11742](https://github.com/angular/angular/issues/11742)) ([b44b6ef](https://github.com/angular/angular/commit/b44b6ef))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **common:** optimize NgSwitch default case ([fdf4309](https://github.com/angular/angular/commit/fdf4309))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.0-beta.0"></a>
|
||||||
|
# [2.2.0-beta.0](https://github.com/angular/angular/compare/2.1.0...2.2.0-beta.0) (2016-10-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
|
||||||
|
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
|
||||||
|
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
|
||||||
|
* **forms:** Added emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
|
||||||
|
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
|
||||||
|
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
|
||||||
|
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
|
||||||
|
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
|
||||||
|
|
||||||
|
Note: The 2.2.0-beta.0 release also contains all the changes present in the 2.1.1 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.1.0"></a>
|
<a name="2.1.0"></a>
|
||||||
# [2.1.0 incremental-metamorphosis](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)
|
# [2.1.0 incremental-metamorphosis](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)
|
||||||
|
|
||||||
|
1
build.sh
1
build.sh
@ -67,6 +67,7 @@ if [[ ${BUILD_ALL} == true ]]; then
|
|||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
ln -s ../../../../node_modules/rxjs .
|
ln -s ../../../../node_modules/rxjs .
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
ln -s ../../../../node_modules/hammerjs/hammer.js .
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
echo "====== Copying files needed for benchmarks ====="
|
echo "====== Copying files needed for benchmarks ====="
|
||||||
|
31
gulpfile.js
31
gulpfile.js
@ -124,38 +124,31 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
|||||||
.on('close', done);
|
.on('close', done);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
|
|
||||||
// focused tests is found.
|
|
||||||
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
|
|
||||||
// tests in our code base.
|
|
||||||
gulp.task('check-tests', function() {
|
|
||||||
const ddescribeIit = require('gulp-ddescribe-iit');
|
|
||||||
return gulp
|
|
||||||
.src([
|
|
||||||
'modules/**/*.spec.ts',
|
|
||||||
'modules/**/*_spec.ts',
|
|
||||||
])
|
|
||||||
.pipe(ddescribeIit({allowDisabledTests: true}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check the coding standards and programming errors
|
// Check the coding standards and programming errors
|
||||||
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||||
const tslint = require('gulp-tslint');
|
const tslint = require('gulp-tslint');
|
||||||
// Built-in rules are at
|
// Built-in rules are at
|
||||||
// https://github.com/palantir/tslint#supported-rules
|
// https://palantir.github.io/tslint/rules/
|
||||||
const tslintConfig = require('./tslint.json');
|
const tslintConfig = require('./tslint.json');
|
||||||
return gulp
|
return gulp
|
||||||
.src([
|
.src([
|
||||||
// todo(vicb): add .js files when supported
|
// todo(vicb): add .js files when supported
|
||||||
// see https://github.com/palantir/tslint/pull/1515
|
// see https://github.com/palantir/tslint/pull/1515
|
||||||
'modules/@angular/**/*.ts',
|
'./modules/**/*.ts',
|
||||||
'modules/benchpress/**/*.ts',
|
'./tools/**/*.ts',
|
||||||
'./*.ts',
|
'./*.ts',
|
||||||
|
|
||||||
|
// Ignore TypeScript mocks because it's not managed by us
|
||||||
|
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
||||||
|
|
||||||
|
// Ignore generated files due to lack of copyright header
|
||||||
|
// todo(alfaproject): make generated files lintable
|
||||||
|
'!**/*.d.ts',
|
||||||
|
'!**/*.ngfactory.ts',
|
||||||
])
|
])
|
||||||
.pipe(tslint({
|
.pipe(tslint({
|
||||||
tslint: require('tslint').default,
|
tslint: require('tslint').default,
|
||||||
configuration: tslintConfig,
|
configuration: tslintConfig,
|
||||||
rulesDirectory: 'dist/tools/tslint',
|
|
||||||
formatter: 'prose',
|
formatter: 'prose',
|
||||||
}))
|
}))
|
||||||
.pipe(tslint.report({emitError: true}));
|
.pipe(tslint.report({emitError: true}));
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {Inject, Injectable} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {isNumber} from '../facade/lang';
|
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
|
|
||||||
@ -44,7 +43,7 @@ export class UserMetric extends Metric {
|
|||||||
function getAndClearValues() {
|
function getAndClearValues() {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||||
.then((values: any[]) => {
|
.then((values: any[]) => {
|
||||||
if (values.every(isNumber)) {
|
if (values.every(v => typeof v === 'number')) {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||||
.then((_: any[]) => {
|
.then((_: any[]) => {
|
||||||
let map: {[k: string]: any} = {};
|
let map: {[k: string]: any} = {};
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {Json} from '../facade/lang';
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Reporter} from '../reporter';
|
import {Reporter} from '../reporter';
|
||||||
import {SampleDescription} from '../sample_description';
|
import {SampleDescription} from '../sample_description';
|
||||||
@ -39,12 +38,14 @@ export class JsonFileReporter extends Reporter {
|
|||||||
sortedProps(this._description.metrics).forEach((metricName) => {
|
sortedProps(this._description.metrics).forEach((metricName) => {
|
||||||
stats[metricName] = formatStats(validSample, metricName);
|
stats[metricName] = formatStats(validSample, metricName);
|
||||||
});
|
});
|
||||||
var content = Json.stringify({
|
var content = JSON.stringify(
|
||||||
'description': this._description,
|
{
|
||||||
'stats': stats,
|
'description': this._description,
|
||||||
'completeSample': completeSample,
|
'stats': stats,
|
||||||
'validSample': validSample,
|
'completeSample': completeSample,
|
||||||
});
|
'validSample': validSample,
|
||||||
|
},
|
||||||
|
null, 2);
|
||||||
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||||
return this._writeFile(filePath, content);
|
return this._writeFile(filePath, content);
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {NumberWrapper} from '../facade/lang';
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Statistic} from '../statistic';
|
import {Statistic} from '../statistic';
|
||||||
|
|
||||||
export function formatNum(n: number) {
|
export function formatNum(n: number) {
|
||||||
return NumberWrapper.toFixed(n, 2);
|
return n.toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sortedProps(obj: {[key: string]: any}) {
|
export function sortedProps(obj: {[key: string]: any}) {
|
||||||
var props: string[] = [];
|
return Object.keys(obj).sort();
|
||||||
props.push(...Object.keys(obj));
|
|
||||||
props.sort();
|
|
||||||
return props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
||||||
@ -29,5 +24,5 @@ export function formatStats(validSamples: MeasureValues[], metricName: string):
|
|||||||
var formattedMean = formatNum(mean);
|
var formattedMean = formatNum(mean);
|
||||||
// Note: Don't use the unicode character for +- as it might cause
|
// Note: Don't use the unicode character for +- as it might cause
|
||||||
// hickups for consoles...
|
// hickups for consoles...
|
||||||
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Math} from './facade/math';
|
|
||||||
|
|
||||||
export class Statistic {
|
export class Statistic {
|
||||||
static calculateCoefficientOfVariation(sample: number[], mean: number) {
|
static calculateCoefficientOfVariation(sample: number[], mean: number) {
|
||||||
return Statistic.calculateStandardDeviation(sample, mean) / mean * 100;
|
return Statistic.calculateStandardDeviation(sample, mean) / mean * 100;
|
||||||
|
@ -8,13 +8,10 @@
|
|||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Statistic} from '../statistic';
|
import {Statistic} from '../statistic';
|
||||||
import {Validator} from '../validator';
|
import {Validator} from '../validator';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A validator that checks the regression slope of a specific metric.
|
* A validator that checks the regression slope of a specific metric.
|
||||||
* Waits for the regression slope to be >=0.
|
* Waits for the regression slope to be >=0.
|
||||||
@ -40,17 +37,17 @@ export class RegressionSlopeValidator extends Validator {
|
|||||||
|
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||||
if (completeSample.length >= this._sampleSize) {
|
if (completeSample.length >= this._sampleSize) {
|
||||||
var latestSample = ListWrapper.slice(
|
const latestSample =
|
||||||
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
||||||
var xValues: number[] = [];
|
const xValues: number[] = [];
|
||||||
var yValues: number[] = [];
|
const yValues: number[] = [];
|
||||||
for (var i = 0; i < latestSample.length; i++) {
|
for (let i = 0; i < latestSample.length; i++) {
|
||||||
// For now, we only use the array index as x value.
|
// For now, we only use the array index as x value.
|
||||||
// TODO(tbosch): think about whether we should use time here instead
|
// TODO(tbosch): think about whether we should use time here instead
|
||||||
xValues.push(i);
|
xValues.push(i);
|
||||||
yValues.push(latestSample[i].values[this._metric]);
|
yValues.push(latestSample[i].values[this._metric]);
|
||||||
}
|
}
|
||||||
var regressionSlope = Statistic.calculateRegressionSlope(
|
const regressionSlope = Statistic.calculateRegressionSlope(
|
||||||
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
||||||
return regressionSlope >= 0 ? latestSample : null;
|
return regressionSlope >= 0 ? latestSample : null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,12 +8,9 @@
|
|||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Validator} from '../validator';
|
import {Validator} from '../validator';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A validator that waits for the sample to have a certain size.
|
* A validator that waits for the sample to have a certain size.
|
||||||
*/
|
*/
|
||||||
@ -28,8 +25,7 @@ export class SizeValidator extends Validator {
|
|||||||
|
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||||
if (completeSample.length >= this._sampleSize) {
|
if (completeSample.length >= this._sampleSize) {
|
||||||
return ListWrapper.slice(
|
return completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
||||||
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
var startTime = record['startTime'];
|
var startTime = record['startTime'];
|
||||||
var endTime = record['endTime'];
|
var endTime = record['endTime'];
|
||||||
|
|
||||||
if (type === 'FunctionCall' && (isBlank(data) || data['scriptName'] !== 'InjectedScript')) {
|
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
||||||
events.push(createStartEvent('script', startTime));
|
events.push(createStartEvent('script', startTime));
|
||||||
endEvent = createEndEvent('script', endTime);
|
endEvent = createEndEvent('script', endTime);
|
||||||
} else if (type === 'Time') {
|
} else if (type === 'Time') {
|
||||||
|
@ -28,7 +28,7 @@ export function main() {
|
|||||||
if (!descriptions) {
|
if (!descriptions) {
|
||||||
descriptions = [];
|
descriptions = [];
|
||||||
}
|
}
|
||||||
if (isBlank(sampleId)) {
|
if (sampleId == null) {
|
||||||
sampleId = 'null';
|
sampleId = 'null';
|
||||||
}
|
}
|
||||||
var providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
||||||
import {Json, isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('file reporter', () => {
|
describe('file reporter', () => {
|
||||||
@ -51,7 +51,7 @@ export function main() {
|
|||||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||||
var regExp = /somePath\/someId_\d+\.json/;
|
var regExp = /somePath\/someId_\d+\.json/;
|
||||||
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
||||||
var parsedContent = Json.parse(loggedFile['content']);
|
var parsedContent = JSON.parse(loggedFile['content']);
|
||||||
expect(parsedContent).toEqual({
|
expect(parsedContent).toEqual({
|
||||||
'description': {
|
'description': {
|
||||||
'id': 'someId',
|
'id': 'someId',
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
||||||
import {ListWrapper} from '../../src/facade/collection';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('regression slope validator', () => {
|
describe('regression slope validator', () => {
|
||||||
@ -44,17 +43,15 @@ export function main() {
|
|||||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
createValidator({size: 2, metric: 'script'});
|
||||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
createValidator({size: 2, metric: 'script'});
|
||||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||||
import {ListWrapper} from '../../src/facade/collection';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('size validator', () => {
|
describe('size validator', () => {
|
||||||
@ -37,9 +36,8 @@ export function main() {
|
|||||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||||
createValidator(2);
|
createValidator(2);
|
||||||
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||||
import {Json, isBlank} from '../../src/facade/lang';
|
import {isBlank} from '../../src/facade/lang';
|
||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -398,8 +398,8 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
if (type === 'performance') {
|
if (type === 'performance') {
|
||||||
return Promise.resolve(this._events.map(
|
return Promise.resolve(this._events.map(
|
||||||
(event) => ({
|
(event) => ({
|
||||||
'message':
|
'message': JSON.stringify(
|
||||||
Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
{'message': {'method': this._messageMethod, 'params': event}}, null, 2)
|
||||||
})));
|
})));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||||
import {Json} from '../../src/facade/lang';
|
|
||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -184,8 +183,9 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
if (type === 'performance') {
|
if (type === 'performance') {
|
||||||
return Promise.resolve(this._perfRecords.map(function(record) {
|
return Promise.resolve(this._perfRecords.map(function(record) {
|
||||||
return {
|
return {
|
||||||
'message': Json.stringify(
|
'message': JSON.stringify(
|
||||||
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}})
|
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}}, null,
|
||||||
|
2)
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,8 +11,6 @@ import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableD
|
|||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
*
|
*
|
||||||
@ -31,11 +29,11 @@ import {isPresent} from '../facade/lang';
|
|||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* The CSS classes are updated as follow depending on the type of the expression evaluation:
|
* The CSS classes are updated as follows, depending on the type of the expression evaluation:
|
||||||
* - `string` - the CSS classes listed in a string (space delimited) are added,
|
* - `string` - the CSS classes listed in the string (space delimited) are added,
|
||||||
* - `Array` - the CSS classes (Array elements) are added,
|
* - `Array` - the CSS classes declared as Array elements are added,
|
||||||
* - `Object` - keys are CSS class names that get added when the expression given in the value
|
* - `Object` - keys are CSS classes that get added when the expression given in the value
|
||||||
* evaluates to a truthy value, otherwise class are removed.
|
* evaluates to a truthy value, otherwise they are removed.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -50,7 +48,6 @@ export class NgClass implements DoCheck {
|
|||||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
|
|
||||||
|
|
||||||
@Input('class')
|
@Input('class')
|
||||||
set klass(v: string) {
|
set klass(v: string) {
|
||||||
this._applyInitialClasses(true);
|
this._applyInitialClasses(true);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isStringMap} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {NgLocalization, getPluralCategory} from '../localization';
|
import {NgLocalization, getPluralCategory} from '../localization';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ export class I18nPluralPipe implements PipeTransform {
|
|||||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||||
if (isBlank(value)) return '';
|
if (isBlank(value)) return '';
|
||||||
|
|
||||||
if (!isStringMap(pluralMap)) {
|
if (typeof pluralMap !== 'object' || pluralMap === null) {
|
||||||
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isStringMap} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,10 +31,10 @@ export class I18nSelectPipe implements PipeTransform {
|
|||||||
transform(value: string, mapping: {[key: string]: string}): string {
|
transform(value: string, mapping: {[key: string]: string}): string {
|
||||||
if (isBlank(value)) return '';
|
if (isBlank(value)) return '';
|
||||||
|
|
||||||
if (!isStringMap(mapping)) {
|
if (typeof mapping !== 'object' || mapping === null) {
|
||||||
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapping.hasOwnProperty(value) ? mapping[value] : '';
|
return mapping[value] || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,6 @@
|
|||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
|
||||||
import {Json} from '../facade/lang';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Converts value into JSON string.
|
* @whatItDoes Converts value into JSON string.
|
||||||
@ -27,5 +23,5 @@ import {Json} from '../facade/lang';
|
|||||||
*/
|
*/
|
||||||
@Pipe({name: 'json', pure: false})
|
@Pipe({name: 'json', pure: false})
|
||||||
export class JsonPipe implements PipeTransform {
|
export class JsonPipe implements PipeTransform {
|
||||||
transform(value: any): string { return Json.stringify(value); }
|
transform(value: any): string { return JSON.stringify(value, null, 2); }
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | lowercase`
|
* @howToUse `expression | lowercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -16,7 +16,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | uppercase`
|
* @howToUse `expression | uppercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -8,13 +8,12 @@
|
|||||||
|
|
||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DatePipe', () => {
|
describe('DatePipe', () => {
|
||||||
var date: Date;
|
let date: Date;
|
||||||
var pipe: DatePipe;
|
let pipe: DatePipe;
|
||||||
|
|
||||||
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
||||||
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
||||||
@ -34,7 +33,9 @@ export function main() {
|
|||||||
|
|
||||||
describe('supports', () => {
|
describe('supports', () => {
|
||||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support numeric strings',
|
it('should support numeric strings',
|
||||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||||
|
|
||||||
|
@ -11,8 +11,6 @@ import {Component} from '@angular/core';
|
|||||||
import {TestBed, async} from '@angular/core/testing';
|
import {TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {Json} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('JsonPipe', () => {
|
describe('JsonPipe', () => {
|
||||||
var regNewLine = '\n';
|
var regNewLine = '\n';
|
||||||
@ -48,7 +46,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||||
var dream2 = normalize(Json.stringify(inceptionObj));
|
var dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||||
expect(dream1).toEqual(dream2);
|
expect(dream1).toEqual(dream2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -74,7 +72,6 @@ export function main() {
|
|||||||
mutable.push(2);
|
mutable.push(2);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
||||||
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,7 +27,7 @@ Then you can add an import statement in the `bootstrap` allowing you to bootstra
|
|||||||
generated code:
|
generated code:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
main_module.ts
|
main.module.ts
|
||||||
-------------
|
-------------
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {Component, NgModule, ApplicationRef} from '@angular/core';
|
import {Component, NgModule, ApplicationRef} from '@angular/core';
|
||||||
@ -49,7 +49,7 @@ export class MainModule {
|
|||||||
bootstrap.ts
|
bootstrap.ts
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
import {MainModuleNgFactory} from './main_module.ngfactory';
|
import {MainModuleNgFactory} from './main.module.ngfactory';
|
||||||
import {platformBrowser} from '@angular/platform-browser';
|
import {platformBrowser} from '@angular/platform-browser';
|
||||||
|
|
||||||
platformBrowser().bootstrapModuleFactory(MainModuleNgFactory);
|
platformBrowser().bootstrapModuleFactory(MainModuleNgFactory);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export {CodeGenerator} from './src/codegen';
|
export {CodeGenerator} from './src/codegen';
|
||||||
|
export {Extractor} from './src/extractor';
|
||||||
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
||||||
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'use-third-party',
|
||||||
|
template: '<third-party-comp [thirdParty]="title"></third-party-comp>' +
|
||||||
|
'<another-third-party-comp></another-third-party-comp>',
|
||||||
|
})
|
||||||
|
export class ComponentUsingThirdParty {
|
||||||
|
title: string = 'from 3rd party';
|
||||||
|
}
|
@ -12,6 +12,10 @@
|
|||||||
<source>Welcome</source>
|
<source>Welcome</source>
|
||||||
<target>tervetuloa</target>
|
<target>tervetuloa</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
||||||
|
<source>other-3rdP-component</source>
|
||||||
|
<target>other-3rdP-component</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
@ -9,4 +9,5 @@
|
|||||||
<translationbundle>
|
<translationbundle>
|
||||||
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
|
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
|
||||||
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
|
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
|
||||||
|
<translation id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</translation>
|
||||||
</translationbundle>
|
</translationbundle>
|
||||||
|
@ -11,9 +11,12 @@ import {FormsModule} from '@angular/forms';
|
|||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
|
||||||
|
import {ThirdpartyModule} from '../third_party_src/module';
|
||||||
|
|
||||||
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
||||||
import {AnimateCmp} from './animate';
|
import {AnimateCmp} from './animate';
|
||||||
import {BasicComp} from './basic';
|
import {BasicComp} from './basic';
|
||||||
|
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
||||||
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||||
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
||||||
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
||||||
@ -22,35 +25,47 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
SomeDirectiveInRootModule,
|
|
||||||
SomePipeInRootModule,
|
|
||||||
AnimateCmp,
|
AnimateCmp,
|
||||||
BasicComp,
|
BasicComp,
|
||||||
|
CompConsumingEvents,
|
||||||
CompForChildQuery,
|
CompForChildQuery,
|
||||||
CompWithEntryComponents,
|
CompUsingPipes,
|
||||||
|
CompUsingRootModuleDirectiveAndPipe,
|
||||||
CompWithAnalyzeEntryComponentsProvider,
|
CompWithAnalyzeEntryComponentsProvider,
|
||||||
ProjectingComp,
|
|
||||||
CompWithChildQuery,
|
CompWithChildQuery,
|
||||||
CompWithDirectiveChild,
|
CompWithDirectiveChild,
|
||||||
|
CompWithEntryComponents,
|
||||||
CompWithNgContent,
|
CompWithNgContent,
|
||||||
CompUsingRootModuleDirectiveAndPipe,
|
|
||||||
CompWithProviders,
|
CompWithProviders,
|
||||||
CompWithReferences,
|
CompWithReferences,
|
||||||
CompUsingPipes,
|
DirectiveForQuery,
|
||||||
CompConsumingEvents,
|
|
||||||
DirPublishingEvents,
|
DirPublishingEvents,
|
||||||
MultipleComponentsMyComp,
|
MultipleComponentsMyComp,
|
||||||
DirectiveForQuery,
|
|
||||||
NextComp,
|
NextComp,
|
||||||
|
ProjectingComp,
|
||||||
|
SomeDirectiveInRootModule,
|
||||||
|
SomePipeInRootModule,
|
||||||
|
ComponentUsingThirdParty,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule, FormsModule, someLibModuleWithProviders(), ModuleUsingCustomElements,
|
BrowserModule,
|
||||||
MdButtonModule
|
FormsModule,
|
||||||
|
MdButtonModule,
|
||||||
|
ModuleUsingCustomElements,
|
||||||
|
someLibModuleWithProviders(),
|
||||||
|
ThirdpartyModule,
|
||||||
],
|
],
|
||||||
providers: [SomeService],
|
providers: [SomeService],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AnimateCmp, BasicComp, CompWithEntryComponents, CompWithAnalyzeEntryComponentsProvider,
|
AnimateCmp,
|
||||||
ProjectingComp, CompWithChildQuery, CompUsingRootModuleDirectiveAndPipe, CompWithReferences
|
BasicComp,
|
||||||
|
CompUsingRootModuleDirectiveAndPipe,
|
||||||
|
CompWithAnalyzeEntryComponentsProvider,
|
||||||
|
CompWithChildQuery,
|
||||||
|
CompWithEntryComponents,
|
||||||
|
CompWithReferences,
|
||||||
|
ProjectingComp,
|
||||||
|
ComponentUsingThirdParty,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class MainModule {
|
export class MainModule {
|
||||||
|
@ -43,13 +43,13 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to create the basic component', () => {
|
it('should be able to create the basic component', () => {
|
||||||
var compFixture = createComponent(BasicComp);
|
const compFixture = createComponent(BasicComp);
|
||||||
expect(compFixture.componentInstance).toBeTruthy();
|
expect(compFixture.componentInstance).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support ngIf', () => {
|
it('should support ngIf', () => {
|
||||||
var compFixture = createComponent(BasicComp);
|
const compFixture = createComponent(BasicComp);
|
||||||
var debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children.length).toBe(3);
|
expect(debugElement.children.length).toBe(3);
|
||||||
|
|
||||||
compFixture.componentInstance.ctxBool = true;
|
compFixture.componentInstance.ctxBool = true;
|
||||||
@ -59,8 +59,8 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support ngFor', () => {
|
it('should support ngFor', () => {
|
||||||
var compFixture = createComponent(BasicComp);
|
const compFixture = createComponent(BasicComp);
|
||||||
var debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children.length).toBe(3);
|
expect(debugElement.children.length).toBe(3);
|
||||||
|
|
||||||
// test NgFor
|
// test NgFor
|
||||||
@ -83,11 +83,9 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support i18n for content tags', () => {
|
it('should support i18n for content tags', () => {
|
||||||
const compFixture = createComponent(BasicComp);
|
const containerElement = createComponent(BasicComp).nativeElement;
|
||||||
const debugElement = compFixture.debugElement;
|
const pElement = containerElement.children.find((c: any) => c.name == 'p');
|
||||||
const containerElement = <any>debugElement.nativeElement;
|
const pText = pElement.children.map((c: any) => c.data).join('').trim();
|
||||||
const pElement = <any>containerElement.children.find((c: any) => c.name == 'p');
|
|
||||||
const pText = <string>pElement.children.map((c: any) => c.data).join('').trim();
|
|
||||||
expect(pText).toBe('tervetuloa');
|
expect(pText).toBe('tervetuloa');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,7 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<!ELEMENT ex (#PCDATA)>
|
<!ELEMENT ex (#PCDATA)>
|
||||||
]>
|
]>
|
||||||
<messagebundle>
|
<messagebundle>
|
||||||
|
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
|
||||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||||
</messagebundle>
|
</messagebundle>
|
||||||
@ -43,6 +44,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||||
<body>
|
<body>
|
||||||
|
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
||||||
|
<source>other-3rdP-component</source>
|
||||||
|
<target/>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||||
<source>translate me</source>
|
<source>translate me</source>
|
||||||
<target/>
|
<target/>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import './init';
|
import './init';
|
||||||
|
|
||||||
|
import {ComponentUsingThirdParty} from '../src/comp_using_3rdp';
|
||||||
import {MainModule} from '../src/module';
|
import {MainModule} from '../src/module';
|
||||||
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
||||||
|
|
||||||
@ -15,9 +16,9 @@ import {createComponent, createModule} from './util';
|
|||||||
describe('NgModule', () => {
|
describe('NgModule', () => {
|
||||||
it('should support providers', () => {
|
it('should support providers', () => {
|
||||||
const moduleRef = createModule();
|
const moduleRef = createModule();
|
||||||
expect(moduleRef.instance instanceof MainModule).toBe(true);
|
expect(moduleRef.instance instanceof MainModule).toEqual(true);
|
||||||
expect(moduleRef.injector.get(MainModule) instanceof MainModule).toBe(true);
|
expect(moduleRef.injector.get(MainModule) instanceof MainModule).toEqual(true);
|
||||||
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true);
|
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support entryComponents components', () => {
|
it('should support entryComponents components', () => {
|
||||||
@ -26,7 +27,7 @@ describe('NgModule', () => {
|
|||||||
CompUsingRootModuleDirectiveAndPipe);
|
CompUsingRootModuleDirectiveAndPipe);
|
||||||
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
|
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
|
||||||
const compRef = cf.create(moduleRef.injector);
|
const compRef = cf.create(moduleRef.injector);
|
||||||
expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toBe(true);
|
expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support entryComponents via the ANALYZE_FOR_ENTRY_COMPONENTS provider and function providers in components',
|
it('should support entryComponents via the ANALYZE_FOR_ENTRY_COMPONENTS provider and function providers in components',
|
||||||
@ -42,12 +43,30 @@ describe('NgModule', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('third-party modules', () => {
|
||||||
|
// https://github.com/angular/angular/issues/11889
|
||||||
|
it('should support third party entryComponents components', () => {
|
||||||
|
const fixture = createComponent(ComponentUsingThirdParty);
|
||||||
|
const thirdPComps = fixture.nativeElement.children;
|
||||||
|
expect(thirdPComps[0].children[0].children[0].data).toEqual('3rdP-component');
|
||||||
|
expect(thirdPComps[1].children[0].children[0].data).toEqual('other-3rdP-component');
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://github.com/angular/angular/issues/12428
|
||||||
|
it('should support third party directives', () => {
|
||||||
|
const fixture = createComponent(ComponentUsingThirdParty);
|
||||||
|
const debugElement = fixture.debugElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(debugElement.children[0].properties['title']).toEqual('from 3rd party');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should support module directives and pipes', () => {
|
it('should support module directives and pipes', () => {
|
||||||
const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe);
|
const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe);
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
|
||||||
const debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
|
expect(debugElement.children[0].properties['title']).toEqual('transformed someValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support module directives and pipes on lib modules', () => {
|
it('should support module directives and pipes on lib modules', () => {
|
||||||
@ -55,10 +74,10 @@ describe('NgModule', () => {
|
|||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
|
||||||
const debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
|
expect(debugElement.children[0].properties['title']).toEqual('transformed someValue');
|
||||||
|
|
||||||
expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toBe(true);
|
expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toEqual(true);
|
||||||
expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule)
|
expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule)
|
||||||
.toBe(true);
|
.toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
This folder emulates consuming precompiled modules and components.
|
||||||
|
It is compiled separately from the other sources under `src`
|
||||||
|
to only generate `*.js` / `*.d.ts` / `*.metadata.json` files,
|
||||||
|
but no `*.ngfactory.ts` files.
|
||||||
|
|
||||||
|
** WARNING **
|
||||||
|
Do not import components/directives from here directly as we want to test that ngc still compiles
|
||||||
|
them when they are not imported.
|
@ -6,7 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {global} from './lang';
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
export var Math = global.Math;
|
@Component({
|
||||||
export var NaN: any /** TODO #???? */ = typeof NaN;
|
selector: 'third-party-comp',
|
||||||
|
template: '<div>3rdP-component</div>',
|
||||||
|
})
|
||||||
|
export class ThirdPartyComponent {
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Directive, Input} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[thirdParty]',
|
||||||
|
host: {'[title]': 'thirdParty'},
|
||||||
|
})
|
||||||
|
export class ThirdPartyDirective {
|
||||||
|
@Input() thirdParty: string;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @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 {NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
import {ThirdPartyComponent} from './comp';
|
||||||
|
import {ThirdPartyDirective} from './directive';
|
||||||
|
import {AnotherThirdPartyModule} from './other_module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ThirdPartyComponent,
|
||||||
|
ThirdPartyDirective,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AnotherThirdPartyModule,
|
||||||
|
ThirdPartyComponent,
|
||||||
|
ThirdPartyDirective,
|
||||||
|
],
|
||||||
|
imports: [AnotherThirdPartyModule]
|
||||||
|
})
|
||||||
|
export class ThirdpartyModule {
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'another-third-party-comp',
|
||||||
|
template: '<div i18n>other-3rdP-component</div>',
|
||||||
|
})
|
||||||
|
export class AnotherThirdpartyComponent {
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @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 {NgModule} from '@angular/core';
|
||||||
|
import {AnotherThirdpartyComponent} from './other_comp';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AnotherThirdpartyComponent],
|
||||||
|
exports: [AnotherThirdpartyComponent],
|
||||||
|
})
|
||||||
|
export class AnotherThirdPartyModule {
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
// For TypeScript 1.8, we have to lay out generated files
|
||||||
|
// in the same source directory with your code.
|
||||||
|
"genDir": ".",
|
||||||
|
"debug": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "",
|
||||||
|
"declaration": true,
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"outDir": "../node_modules/third_party"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
// For TypeScript 1.8, we have to lay out generated files
|
||||||
|
// in the same source directory with your code.
|
||||||
|
"genDir": ".",
|
||||||
|
"debug": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "",
|
||||||
|
"declaration": true,
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
|
||||||
|
"files": [
|
||||||
|
"src/module",
|
||||||
|
"src/bootstrap",
|
||||||
|
"test/all_spec",
|
||||||
|
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
||||||
|
]
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
// For TypeScript 1.8, we have to lay out generated files
|
|
||||||
// in the same source directory with your code.
|
|
||||||
"genDir": ".",
|
|
||||||
"debug": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"rootDir": "",
|
|
||||||
"declaration": true,
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"baseUrl": "."
|
|
||||||
},
|
|
||||||
|
|
||||||
"files": [
|
|
||||||
"src/module",
|
|
||||||
"src/bootstrap",
|
|
||||||
"test/all_spec",
|
|
||||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
|
||||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
|
||||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
|
||||||
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
|
||||||
]
|
|
||||||
}
|
|
@ -11,13 +11,12 @@
|
|||||||
* Intended to be used in a build step.
|
* Intended to be used in a build step.
|
||||||
*/
|
*/
|
||||||
import * as compiler from '@angular/compiler';
|
import * as compiler from '@angular/compiler';
|
||||||
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
|
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
|
||||||
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleCompiler, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './private_import_compiler';
|
|
||||||
import {Console} from './private_import_core';
|
import {Console} from './private_import_core';
|
||||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
@ -36,73 +35,16 @@ const PREAMBLE = `/**
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export class CodeGeneratorModuleCollector {
|
|
||||||
constructor(
|
|
||||||
private staticReflector: StaticReflector, private reflectorHost: StaticReflectorHost,
|
|
||||||
private program: ts.Program, private options: AngularCompilerOptions) {}
|
|
||||||
|
|
||||||
getModuleSymbols(program: ts.Program): {fileMetas: FileMetadata[], ngModules: StaticSymbol[]} {
|
|
||||||
// Compare with false since the default should be true
|
|
||||||
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
|
||||||
GENERATED_OR_DTS_FILES :
|
|
||||||
GENERATED_FILES;
|
|
||||||
let filePaths = this.program.getSourceFiles()
|
|
||||||
.filter(sf => !skipFileNames.test(sf.fileName))
|
|
||||||
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
|
||||||
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
|
||||||
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
|
||||||
ngModules.push(...fileMeta.ngModules);
|
|
||||||
return ngModules;
|
|
||||||
}, <StaticSymbol[]>[]);
|
|
||||||
return {fileMetas, ngModules};
|
|
||||||
}
|
|
||||||
|
|
||||||
private readFileMetadata(absSourcePath: string): FileMetadata {
|
|
||||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
|
||||||
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
|
|
||||||
if (!moduleMetadata) {
|
|
||||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const metadata = moduleMetadata['metadata'];
|
|
||||||
const symbols = metadata && Object.keys(metadata);
|
|
||||||
if (!symbols || !symbols.length) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
for (const symbol of symbols) {
|
|
||||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
|
||||||
// Ignore symbols that are only included to record error information.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
|
||||||
const annotations = this.staticReflector.annotations(staticType);
|
|
||||||
annotations.forEach((annotation) => {
|
|
||||||
if (annotation instanceof NgModule) {
|
|
||||||
result.ngModules.push(staticType);
|
|
||||||
} else if (annotation instanceof Component) {
|
|
||||||
result.components.push(staticType);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CodeGenerator {
|
export class CodeGenerator {
|
||||||
private moduleCollector: CodeGeneratorModuleCollector;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private options: AngularCompilerOptions, private program: ts.Program,
|
private options: AngularCompilerOptions, private program: ts.Program,
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {
|
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {}
|
||||||
this.moduleCollector =
|
|
||||||
new CodeGeneratorModuleCollector(staticReflector, reflectorHost, program, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write codegen in a directory structure matching the sources.
|
// Write codegen in a directory structure matching the sources.
|
||||||
private calculateEmitPath(filePath: string): string {
|
private calculateEmitPath(filePath: string): string {
|
||||||
let root = this.options.basePath;
|
let root = this.options.basePath;
|
||||||
for (let eachRootDir of this.options.rootDirs || []) {
|
for (const eachRootDir of this.options.rootDirs || []) {
|
||||||
if (this.options.trace) {
|
if (this.options.trace) {
|
||||||
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||||
}
|
}
|
||||||
@ -112,31 +54,28 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transplant the codegen path to be inside the `genDir`
|
// transplant the codegen path to be inside the `genDir`
|
||||||
var relativePath: string = path.relative(root, filePath);
|
let relativePath: string = path.relative(root, filePath);
|
||||||
while (relativePath.startsWith('..' + path.sep)) {
|
while (relativePath.startsWith('..' + path.sep)) {
|
||||||
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
||||||
// into `genDir`.
|
// into `genDir`.
|
||||||
relativePath = relativePath.substr(3);
|
relativePath = relativePath.substr(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.join(this.options.genDir, relativePath);
|
return path.join(this.options.genDir, relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen(): Promise<any> {
|
codegen(options: {transitiveModules: boolean}): Promise<any> {
|
||||||
const {fileMetas, ngModules} = this.moduleCollector.getModuleSymbols(this.program);
|
const staticSymbols =
|
||||||
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||||
return Promise.all(fileMetas.map(
|
|
||||||
(fileMeta) =>
|
return this.compiler.compileModules(staticSymbols, options).then(generatedModules => {
|
||||||
this.compiler
|
generatedModules.forEach(generatedModule => {
|
||||||
.compile(
|
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
|
||||||
fileMeta.fileUrl, analyzedNgModules, fileMeta.components, fileMeta.ngModules)
|
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||||
.then((generatedModules) => {
|
this.host.writeFile(
|
||||||
generatedModules.forEach((generatedModule) => {
|
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
||||||
const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
|
});
|
||||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
});
|
||||||
this.host.writeFile(
|
|
||||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
|
||||||
});
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
@ -173,36 +112,72 @@ export class CodeGenerator {
|
|||||||
const staticReflector = new StaticReflector(reflectorHost);
|
const staticReflector = new StaticReflector(reflectorHost);
|
||||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
const htmlParser =
|
const htmlParser =
|
||||||
new compiler.I18NHtmlParser(new HtmlParser(), transContent, cliOptions.i18nFormat);
|
new compiler.I18NHtmlParser(new compiler.HtmlParser(), transContent, cliOptions.i18nFormat);
|
||||||
const config = new compiler.CompilerConfig({
|
const config = new compiler.CompilerConfig({
|
||||||
genDebugInfo: options.debug === true,
|
genDebugInfo: options.debug === true,
|
||||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||||
logBindingUpdate: false,
|
logBindingUpdate: false,
|
||||||
useJit: false
|
useJit: false
|
||||||
});
|
});
|
||||||
const normalizer = new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
const normalizer =
|
||||||
const expressionParser = new Parser(new Lexer());
|
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
const expressionParser = new compiler.Parser(new compiler.Lexer());
|
||||||
|
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
||||||
const console = new Console();
|
const console = new Console();
|
||||||
const tmplParser =
|
const tmplParser = new compiler.TemplateParser(
|
||||||
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, staticReflector);
|
elementSchemaRegistry, staticReflector);
|
||||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
|
||||||
new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost), cliOptions.locale,
|
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
||||||
cliOptions.i18nFormat);
|
new compiler.DirectiveWrapperCompiler(
|
||||||
|
config, expressionParser, elementSchemaRegistry, console),
|
||||||
|
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
||||||
|
cliOptions.locale, cliOptions.i18nFormat);
|
||||||
|
|
||||||
return new CodeGenerator(
|
return new CodeGenerator(
|
||||||
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileMetadata {
|
export function extractProgramSymbols(
|
||||||
fileUrl: string;
|
program: ts.Program, staticReflector: StaticReflector, reflectorHost: StaticReflectorHost,
|
||||||
components: StaticSymbol[];
|
options: AngularCompilerOptions): StaticSymbol[] {
|
||||||
ngModules: StaticSymbol[];
|
// Compare with false since the default should be true
|
||||||
}
|
const skipFileNames =
|
||||||
|
options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||||
|
|
||||||
|
const staticSymbols: StaticSymbol[] = [];
|
||||||
|
|
||||||
|
program.getSourceFiles()
|
||||||
|
.filter(sourceFile => !skipFileNames.test(sourceFile.fileName))
|
||||||
|
.forEach(sourceFile => {
|
||||||
|
const absSrcPath = reflectorHost.getCanonicalFileName(sourceFile.fileName);
|
||||||
|
|
||||||
|
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
|
||||||
|
if (!moduleMetadata) {
|
||||||
|
console.log(`WARNING: no metadata found for ${absSrcPath}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = moduleMetadata['metadata'];
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const symbol of Object.keys(metadata)) {
|
||||||
|
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||||
|
// Ignore symbols that are only included to record error information.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
staticSymbols.push(reflectorHost.findDeclaration(absSrcPath, symbol, absSrcPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return staticSymbols;
|
||||||
|
}
|
@ -10,28 +10,32 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract i18n messages from source code
|
* Extract i18n messages from source code
|
||||||
*
|
|
||||||
* TODO(vicb): factorize code with the CodeGenerator
|
|
||||||
*/
|
*/
|
||||||
// Must be imported first, because angular2 decorators throws on load.
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
import * as compiler from '@angular/compiler';
|
import * as compiler from '@angular/compiler';
|
||||||
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
|
import * as tsc from '@angular/tsc-wrapped';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import * as tsc from '@angular/tsc-wrapped';
|
|
||||||
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleCompiler, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler, ParseError} from './private_import_compiler';
|
import {Extractor} from './extractor';
|
||||||
import {Console} from './private_import_core';
|
|
||||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
|
||||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
|
||||||
|
|
||||||
function extract(
|
function extract(
|
||||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
|
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
|
||||||
program: ts.Program, host: ts.CompilerHost) {
|
program: ts.Program, host: ts.CompilerHost) {
|
||||||
const htmlParser = new compiler.I18NHtmlParser(new HtmlParser());
|
const resourceLoader: compiler.ResourceLoader = {
|
||||||
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, htmlParser);
|
get: (s: string) => {
|
||||||
|
if (!host.fileExists(s)) {
|
||||||
|
// TODO: We should really have a test for error cases like this!
|
||||||
|
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||||
|
}
|
||||||
|
return Promise.resolve(host.readFile(s));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const extractor =
|
||||||
|
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
|
||||||
|
|
||||||
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
|
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
|
||||||
|
|
||||||
return (bundlePromise).then(messageBundle => {
|
return (bundlePromise).then(messageBundle => {
|
||||||
@ -47,6 +51,7 @@ function extract(
|
|||||||
case 'xliff':
|
case 'xliff':
|
||||||
case 'xlf':
|
case 'xlf':
|
||||||
default:
|
default:
|
||||||
|
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||||
ext = 'xlf';
|
ext = 'xlf';
|
||||||
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
||||||
break;
|
break;
|
||||||
@ -57,146 +62,6 @@ function extract(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
|
||||||
|
|
||||||
export class Extractor {
|
|
||||||
constructor(
|
|
||||||
private program: ts.Program, public host: ts.CompilerHost,
|
|
||||||
private staticReflector: StaticReflector, private messageBundle: compiler.MessageBundle,
|
|
||||||
private reflectorHost: ReflectorHost, private metadataResolver: CompileMetadataResolver,
|
|
||||||
private directiveNormalizer: DirectiveNormalizer,
|
|
||||||
private compiler: compiler.OfflineCompiler) {}
|
|
||||||
|
|
||||||
private readFileMetadata(absSourcePath: string): FileMetadata {
|
|
||||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
|
||||||
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
|
|
||||||
if (!moduleMetadata) {
|
|
||||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const metadata = moduleMetadata['metadata'];
|
|
||||||
const symbols = metadata && Object.keys(metadata);
|
|
||||||
if (!symbols || !symbols.length) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
for (const symbol of symbols) {
|
|
||||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
|
||||||
// Ignore symbols that are only included to record error information.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
|
||||||
const annotations = this.staticReflector.annotations(staticType);
|
|
||||||
annotations.forEach((annotation) => {
|
|
||||||
if (annotation instanceof NgModule) {
|
|
||||||
result.ngModules.push(staticType);
|
|
||||||
} else if (annotation instanceof Component) {
|
|
||||||
result.components.push(staticType);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
extract(): Promise<compiler.MessageBundle> {
|
|
||||||
const filePaths =
|
|
||||||
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f));
|
|
||||||
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
|
||||||
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
|
||||||
ngModules.push(...fileMeta.ngModules);
|
|
||||||
return ngModules;
|
|
||||||
}, <StaticSymbol[]>[]);
|
|
||||||
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
|
||||||
const errors: ParseError[] = [];
|
|
||||||
|
|
||||||
let bundlePromise =
|
|
||||||
Promise
|
|
||||||
.all(fileMetas.map((fileMeta) => {
|
|
||||||
const url = fileMeta.fileUrl;
|
|
||||||
return Promise.all(fileMeta.components.map(compType => {
|
|
||||||
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
|
|
||||||
const ngModule = analyzedNgModules.ngModuleByComponent.get(compType);
|
|
||||||
if (!ngModule) {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot determine the module for component ${compMeta.type.name}!`);
|
|
||||||
}
|
|
||||||
return Promise
|
|
||||||
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
|
||||||
dirMeta =>
|
|
||||||
this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
|
||||||
.then((normalizedCompWithDirectives) => {
|
|
||||||
const compMeta = normalizedCompWithDirectives[0];
|
|
||||||
const html = compMeta.template.template;
|
|
||||||
const interpolationConfig =
|
|
||||||
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
|
||||||
errors.push(
|
|
||||||
...this.messageBundle.updateFromTemplate(html, url, interpolationConfig));
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}))
|
|
||||||
.then(_ => this.messageBundle);
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return bundlePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
static create(
|
|
||||||
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
|
||||||
compilerHost: ts.CompilerHost, htmlParser: compiler.I18NHtmlParser,
|
|
||||||
reflectorHostContext?: ReflectorHostContext): Extractor {
|
|
||||||
const resourceLoader: compiler.ResourceLoader = {
|
|
||||||
get: (s: string) => {
|
|
||||||
if (!compilerHost.fileExists(s)) {
|
|
||||||
// TODO: We should really have a test for error cases like this!
|
|
||||||
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
|
||||||
}
|
|
||||||
return Promise.resolve(compilerHost.readFile(s));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
|
||||||
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
|
||||||
const staticReflector = new StaticReflector(reflectorHost);
|
|
||||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
|
||||||
|
|
||||||
const config = new compiler.CompilerConfig({
|
|
||||||
genDebugInfo: options.debug === true,
|
|
||||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
|
||||||
logBindingUpdate: false,
|
|
||||||
useJit: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const normalizer = new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
|
||||||
const expressionParser = new Parser(new Lexer());
|
|
||||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
|
||||||
const console = new Console();
|
|
||||||
const tmplParser =
|
|
||||||
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
|
||||||
const resolver = new CompileMetadataResolver(
|
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
|
||||||
elementSchemaRegistry, staticReflector);
|
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
|
||||||
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
|
||||||
new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost), null, null);
|
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
|
||||||
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
|
||||||
|
|
||||||
return new Extractor(
|
|
||||||
program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, normalizer,
|
|
||||||
offlineCompiler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FileMetadata {
|
|
||||||
fileUrl: string;
|
|
||||||
components: StaticSymbol[];
|
|
||||||
ngModules: StaticSymbol[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry point
|
// Entry point
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const args = require('minimist')(process.argv.slice(2));
|
const args = require('minimist')(process.argv.slice(2));
|
||||||
|
110
modules/@angular/compiler-cli/src/extractor.ts
Normal file
110
modules/@angular/compiler-cli/src/extractor.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract i18n messages from source code
|
||||||
|
*/
|
||||||
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import * as compiler from '@angular/compiler';
|
||||||
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
import * as tsc from '@angular/tsc-wrapped';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {extractProgramSymbols} from './codegen';
|
||||||
|
import {ReflectorHost} from './reflector_host';
|
||||||
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
|
import {StaticReflector, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
|
export class Extractor {
|
||||||
|
constructor(
|
||||||
|
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
||||||
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
|
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
||||||
|
private metadataResolver: compiler.CompileMetadataResolver,
|
||||||
|
private directiveNormalizer: compiler.DirectiveNormalizer) {}
|
||||||
|
|
||||||
|
extract(): Promise<compiler.MessageBundle> {
|
||||||
|
const programSymbols: StaticSymbol[] =
|
||||||
|
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||||
|
|
||||||
|
const files =
|
||||||
|
compiler.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
||||||
|
.files;
|
||||||
|
const errors: compiler.ParseError[] = [];
|
||||||
|
const filePromises: Promise<any>[] = [];
|
||||||
|
|
||||||
|
files.forEach(file => {
|
||||||
|
const cmpPromises: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||||
|
file.directives.forEach(directiveType => {
|
||||||
|
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||||
|
if (dirMeta.isComponent) {
|
||||||
|
cmpPromises.push(this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cmpPromises.length) {
|
||||||
|
const done =
|
||||||
|
Promise.all(cmpPromises).then((compMetas: compiler.CompileDirectiveMetadata[]) => {
|
||||||
|
compMetas.forEach(compMeta => {
|
||||||
|
const html = compMeta.template.template;
|
||||||
|
const interpolationConfig =
|
||||||
|
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||||
|
errors.push(...this.messageBundle.updateFromTemplate(
|
||||||
|
html, file.srcUrl, interpolationConfig));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
filePromises.push(done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (errors.length) {
|
||||||
|
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(filePromises).then(_ => this.messageBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(
|
||||||
|
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
||||||
|
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
|
||||||
|
reflectorHost?: ReflectorHost): Extractor {
|
||||||
|
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||||
|
|
||||||
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||||
|
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
|
||||||
|
const staticReflector = new StaticReflector(reflectorHost);
|
||||||
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
|
|
||||||
|
const config = new compiler.CompilerConfig({
|
||||||
|
genDebugInfo: options.debug === true,
|
||||||
|
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||||
|
logBindingUpdate: false,
|
||||||
|
useJit: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizer =
|
||||||
|
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||||
|
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
||||||
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
|
elementSchemaRegistry, staticReflector);
|
||||||
|
|
||||||
|
// TODO(vicb): implicit tags & attributes
|
||||||
|
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||||
|
|
||||||
|
return new Extractor(
|
||||||
|
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
|
||||||
|
normalizer);
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,9 @@ import {CodeGenerator} from './codegen';
|
|||||||
function codegen(
|
function codegen(
|
||||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
|
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
|
||||||
host: ts.CompilerHost) {
|
host: ts.CompilerHost) {
|
||||||
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
|
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen({
|
||||||
|
transitiveModules: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLI entry point
|
// CLI entry point
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {__compiler_private__ as _} from '@angular/compiler';
|
|
||||||
|
|
||||||
export type AssetUrl = typeof _._AssetUrl;
|
|
||||||
export var AssetUrl: typeof _.AssetUrl = _.AssetUrl;
|
|
||||||
|
|
||||||
export type ImportGenerator = typeof _._ImportGenerator;
|
|
||||||
export var ImportGenerator: typeof _.ImportGenerator = _.ImportGenerator;
|
|
||||||
|
|
||||||
export type CompileMetadataResolver = typeof _._CompileMetadataResolver;
|
|
||||||
export var CompileMetadataResolver: typeof _.CompileMetadataResolver = _.CompileMetadataResolver;
|
|
||||||
|
|
||||||
export type HtmlParser = typeof _._HtmlParser;
|
|
||||||
export var HtmlParser: typeof _.HtmlParser = _.HtmlParser;
|
|
||||||
|
|
||||||
export type ParseError = typeof _._ParseError;
|
|
||||||
export var ParseError: typeof _.ParseError = _.ParseError;
|
|
||||||
|
|
||||||
export type InterpolationConfig = typeof _._InterpolationConfig;
|
|
||||||
export var InterpolationConfig: typeof _.InterpolationConfig = _.InterpolationConfig;
|
|
||||||
|
|
||||||
export type DirectiveNormalizer = typeof _._DirectiveNormalizer;
|
|
||||||
export var DirectiveNormalizer: typeof _.DirectiveNormalizer = _.DirectiveNormalizer;
|
|
||||||
|
|
||||||
export type Lexer = typeof _._Lexer;
|
|
||||||
export var Lexer: typeof _.Lexer = _.Lexer;
|
|
||||||
|
|
||||||
export type Parser = typeof _._Parser;
|
|
||||||
export var Parser: typeof _.Parser = _.Parser;
|
|
||||||
|
|
||||||
export type TemplateParser = typeof _._TemplateParser;
|
|
||||||
export var TemplateParser: typeof _.TemplateParser = _.TemplateParser;
|
|
||||||
|
|
||||||
export type DomElementSchemaRegistry = typeof _._DomElementSchemaRegistry;
|
|
||||||
export var DomElementSchemaRegistry: typeof _.DomElementSchemaRegistry = _.DomElementSchemaRegistry;
|
|
||||||
|
|
||||||
export type StyleCompiler = typeof _._StyleCompiler;
|
|
||||||
export var StyleCompiler: typeof _.StyleCompiler = _.StyleCompiler;
|
|
||||||
|
|
||||||
export type ViewCompiler = typeof _._ViewCompiler;
|
|
||||||
export var ViewCompiler: typeof _.ViewCompiler = _.ViewCompiler;
|
|
||||||
|
|
||||||
export type NgModuleCompiler = typeof _._NgModuleCompiler;
|
|
||||||
export var NgModuleCompiler: typeof _.NgModuleCompiler = _.NgModuleCompiler;
|
|
||||||
|
|
||||||
export type TypeScriptEmitter = typeof _._TypeScriptEmitter;
|
|
||||||
export var TypeScriptEmitter: typeof _.TypeScriptEmitter = _.TypeScriptEmitter;
|
|
@ -6,12 +6,12 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {AssetUrl, ImportGenerator} from '@angular/compiler';
|
||||||
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {AssetUrl, ImportGenerator} from './private_import_compiler';
|
|
||||||
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
|
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
@ -250,6 +250,12 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
} else {
|
} else {
|
||||||
const sf = this.program.getSourceFile(filePath);
|
const sf = this.program.getSourceFile(filePath);
|
||||||
if (!sf) {
|
if (!sf) {
|
||||||
|
if (this.context.fileExists(filePath)) {
|
||||||
|
const sourceText = this.context.readFile(filePath);
|
||||||
|
return this.metadataCollector.getMetadata(
|
||||||
|
ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`Source file ${filePath} not present in program.`);
|
throw new Error(`Source file ${filePath} not present in program.`);
|
||||||
}
|
}
|
||||||
return this.metadataCollector.getMetadata(sf);
|
return this.metadataCollector.getMetadata(sf);
|
||||||
|
@ -25,7 +25,7 @@ export interface StaticReflectorHost {
|
|||||||
* @param modulePath is a string identifier for a module as an absolute path.
|
* @param modulePath is a string identifier for a module as an absolute path.
|
||||||
* @returns the metadata for the given module.
|
* @returns the metadata for the given module.
|
||||||
*/
|
*/
|
||||||
getMetadataFor(modulePath: string): {[key: string]: any};
|
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||||
@ -72,13 +72,12 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||||
|
|
||||||
importUri(typeOrFunc: StaticSymbol): string {
|
importUri(typeOrFunc: StaticSymbol): string {
|
||||||
var staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||||
return staticSymbol ? staticSymbol.filePath : null;
|
return staticSymbol ? staticSymbol.filePath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
||||||
const result = this.host.findDeclaration(moduleUrl, name, '');
|
return this.host.findDeclaration(moduleUrl, name, '');
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveEnum(enumIdentifier: any, name: string): any {
|
resolveEnum(enumIdentifier: any, name: string): any {
|
||||||
@ -238,9 +237,9 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public simplify(context: StaticSymbol, value: any): any {
|
public simplify(context: StaticSymbol, value: any): any {
|
||||||
let _this = this;
|
const _this = this;
|
||||||
let scope = BindingScope.empty;
|
let scope = BindingScope.empty;
|
||||||
let calling = new Map<StaticSymbol, boolean>();
|
const calling = new Map<StaticSymbol, boolean>();
|
||||||
|
|
||||||
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
||||||
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
||||||
@ -255,16 +254,15 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||||
let result: any = staticSymbol;
|
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||||
let moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
const declarationValue =
|
||||||
let declarationValue =
|
|
||||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||||
return declarationValue;
|
return declarationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
||||||
if (value && value.__symbolic === 'new' && value.expression) {
|
if (value && value.__symbolic === 'new' && value.expression) {
|
||||||
let target = value.expression;
|
const target = value.expression;
|
||||||
if (target.__symbolic == 'reference') {
|
if (target.__symbolic == 'reference') {
|
||||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
||||||
}
|
}
|
||||||
@ -535,7 +533,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = simplifyInContext(context, value, 0);
|
const result = simplifyInContext(context, value, 0);
|
||||||
if (shouldIgnore(result)) {
|
if (shouldIgnore(result)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -550,8 +548,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
moduleMetadata = this.host.getMetadataFor(module);
|
moduleMetadata = this.host.getMetadataFor(module);
|
||||||
if (Array.isArray(moduleMetadata)) {
|
if (Array.isArray(moduleMetadata)) {
|
||||||
moduleMetadata = (<Array<any>>moduleMetadata)
|
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
|
||||||
.find(element => element.version === SUPPORTED_SCHEMA_VERSION) ||
|
|
||||||
moduleMetadata[0];
|
moduleMetadata[0];
|
||||||
}
|
}
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
@ -568,12 +565,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||||
let moduleMetadata = this.getModuleMetadata(type.filePath);
|
const moduleMetadata = this.getModuleMetadata(type.filePath);
|
||||||
let result = moduleMetadata['metadata'][type.name];
|
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'};
|
||||||
if (!result) {
|
|
||||||
result = {__symbolic: 'class'};
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +606,7 @@ function produceErrorMessage(error: any): string {
|
|||||||
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
||||||
{[key: string]: any} {
|
{[key: string]: any} {
|
||||||
if (!input) return {};
|
if (!input) return {};
|
||||||
var result: {[key: string]: any} = {};
|
const result: {[key: string]: any} = {};
|
||||||
Object.keys(input).forEach((key) => {
|
Object.keys(input).forEach((key) => {
|
||||||
let value = transform(input[key], key);
|
let value = transform(input[key], key);
|
||||||
if (!shouldIgnore(value)) {
|
if (!shouldIgnore(value)) {
|
||||||
@ -638,8 +631,7 @@ abstract class BindingScope {
|
|||||||
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
||||||
|
|
||||||
public static build(): BindingScopeBuilder {
|
public static build(): BindingScopeBuilder {
|
||||||
let current = new Map<string, any>();
|
const current = new Map<string, any>();
|
||||||
let parent: BindingScope = undefined;
|
|
||||||
return {
|
return {
|
||||||
define: function(name, value) {
|
define: function(name, value) {
|
||||||
current.set(name, value);
|
current.set(name, value);
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
||||||
import {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
import {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||||
import {ListWrapper} from '@angular/facade/src/collection';
|
|
||||||
import {MetadataCollector} from '@angular/tsc-wrapped';
|
import {MetadataCollector} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -474,7 +473,7 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
|
|
||||||
function resolvePath(pathParts: string[]): string {
|
function resolvePath(pathParts: string[]): string {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
ListWrapper.forEachWithIndex(pathParts, (part, index) => {
|
pathParts.forEach((part, index) => {
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case '':
|
case '':
|
||||||
case '.':
|
case '.':
|
||||||
|
@ -21,6 +21,36 @@
|
|||||||
* </p>
|
* </p>
|
||||||
* </div>
|
* </div>
|
||||||
*/
|
*/
|
||||||
export * from './src/index';
|
export * from './src/template_parser/template_ast';
|
||||||
|
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
|
||||||
|
export {CompilerConfig, RenderTypes} from './src/config';
|
||||||
|
export * from './src/compile_metadata';
|
||||||
|
export * from './src/offline_compiler';
|
||||||
|
export {RuntimeCompiler} from './src/runtime_compiler';
|
||||||
|
export * from './src/url_resolver';
|
||||||
|
export * from './src/resource_loader';
|
||||||
|
export * from './src/compiler';
|
||||||
|
export {DirectiveResolver} from './src/directive_resolver';
|
||||||
|
export {PipeResolver} from './src/pipe_resolver';
|
||||||
|
export {NgModuleResolver} from './src/ng_module_resolver';
|
||||||
|
export {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './src/ml_parser/interpolation_config';
|
||||||
|
export * from './src/schema/element_schema_registry';
|
||||||
|
export * from './src/i18n/index';
|
||||||
|
export * from './src/directive_normalizer';
|
||||||
|
export * from './src/expression_parser/lexer';
|
||||||
|
export * from './src/expression_parser/parser';
|
||||||
|
export * from './src/metadata_resolver';
|
||||||
|
export * from './src/ml_parser/html_parser';
|
||||||
|
export * from './src/ml_parser/interpolation_config';
|
||||||
|
export {NgModuleCompiler} from './src/ng_module_compiler';
|
||||||
|
export {DirectiveWrapperCompiler} from './src/directive_wrapper_compiler';
|
||||||
|
export * from './src/output/path_util';
|
||||||
|
export * from './src/output/ts_emitter';
|
||||||
|
export * from './src/parse_util';
|
||||||
|
export * from './src/schema/dom_element_schema_registry';
|
||||||
|
export * from './src/selector';
|
||||||
|
export * from './src/style_compiler';
|
||||||
|
export * from './src/template_parser/template_parser';
|
||||||
|
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||||
|
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {Math} from '../facade/math';
|
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||||
|
|
||||||
@ -97,7 +96,7 @@ function _parseAnimationDeclarationStates(
|
|||||||
var styleValues: Styles[] = [];
|
var styleValues: Styles[] = [];
|
||||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
stateMetadata.styles.styles.forEach(stylesEntry => {
|
||||||
// TODO (matsko): change this when we get CSS class integration support
|
// TODO (matsko): change this when we get CSS class integration support
|
||||||
if (isStringMap(stylesEntry)) {
|
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
|
||||||
styleValues.push(stylesEntry as Styles);
|
styleValues.push(stylesEntry as Styles);
|
||||||
} else {
|
} else {
|
||||||
errors.push(new AnimationParseError(
|
errors.push(new AnimationParseError(
|
||||||
@ -172,8 +171,7 @@ function _parseAnimationTransitionExpr(
|
|||||||
|
|
||||||
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
|
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
|
||||||
CompileAnimationMetadata {
|
CompileAnimationMetadata {
|
||||||
return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) :
|
return Array.isArray(entry) ? new CompileAnimationSequenceMetadata(entry) : entry;
|
||||||
<CompileAnimationMetadata>entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _normalizeStyleMetadata(
|
function _normalizeStyleMetadata(
|
||||||
@ -181,9 +179,8 @@ function _normalizeStyleMetadata(
|
|||||||
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
||||||
var normalizedStyles: {[key: string]: string | number}[] = [];
|
var normalizedStyles: {[key: string]: string | number}[] = [];
|
||||||
entry.styles.forEach(styleEntry => {
|
entry.styles.forEach(styleEntry => {
|
||||||
if (isString(styleEntry)) {
|
if (typeof styleEntry === 'string') {
|
||||||
ListWrapper.addAll(
|
normalizedStyles.push(..._resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
||||||
normalizedStyles, _resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
|
||||||
} else {
|
} else {
|
||||||
normalizedStyles.push(<{[key: string]: string | number}>styleEntry);
|
normalizedStyles.push(<{[key: string]: string | number}>styleEntry);
|
||||||
}
|
}
|
||||||
@ -202,10 +199,10 @@ function _normalizeStyleSteps(
|
|||||||
|
|
||||||
function _mergeAnimationStyles(
|
function _mergeAnimationStyles(
|
||||||
stylesList: any[], newItem: {[key: string]: string | number} | string) {
|
stylesList: any[], newItem: {[key: string]: string | number} | string) {
|
||||||
if (isStringMap(newItem) && stylesList.length > 0) {
|
if (typeof newItem === 'object' && newItem !== null && stylesList.length > 0) {
|
||||||
var lastIndex = stylesList.length - 1;
|
var lastIndex = stylesList.length - 1;
|
||||||
var lastItem = stylesList[lastIndex];
|
var lastItem = stylesList[lastIndex];
|
||||||
if (isStringMap(lastItem)) {
|
if (typeof lastItem === 'object' && lastItem !== null) {
|
||||||
stylesList[lastIndex] = StringMapWrapper.merge(
|
stylesList[lastIndex] = StringMapWrapper.merge(
|
||||||
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
|
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
|
||||||
return;
|
return;
|
||||||
@ -292,7 +289,7 @@ function _resolveStylesFromState(
|
|||||||
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
||||||
} else {
|
} else {
|
||||||
value.styles.forEach(stylesEntry => {
|
value.styles.forEach(stylesEntry => {
|
||||||
if (isStringMap(stylesEntry)) {
|
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
|
||||||
styles.push(stylesEntry as Styles);
|
styles.push(stylesEntry as Styles);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -348,12 +345,12 @@ function _parseAnimationKeyframes(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (doSortKeyframes) {
|
if (doSortKeyframes) {
|
||||||
ListWrapper.sort(rawKeyframes, (a, b) => a[0] <= b[0] ? -1 : 1);
|
rawKeyframes.sort((a, b) => a[0] <= b[0] ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstKeyframe = rawKeyframes[0];
|
var firstKeyframe = rawKeyframes[0];
|
||||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||||
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
rawKeyframes.splice(0, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstKeyframeStyles = firstKeyframe[1];
|
var firstKeyframeStyles = firstKeyframe[1];
|
||||||
@ -423,7 +420,7 @@ function _parseTransitionAnimation(
|
|||||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||||
} else {
|
} else {
|
||||||
var innerStep = <AnimationStepAst>innerAst;
|
var innerStep = <AnimationStepAst>innerAst;
|
||||||
ListWrapper.addAll(innerStep.startingStyles.styles, previousStyles);
|
innerStep.startingStyles.styles.push(...previousStyles);
|
||||||
}
|
}
|
||||||
previousStyles = null;
|
previousStyles = null;
|
||||||
}
|
}
|
||||||
@ -504,7 +501,7 @@ function _parseTimeExpression(
|
|||||||
var duration: number;
|
var duration: number;
|
||||||
var delay: number = 0;
|
var delay: number = 0;
|
||||||
var easing: string = null;
|
var easing: string = null;
|
||||||
if (isString(exp)) {
|
if (typeof exp === 'string') {
|
||||||
const matches = exp.match(regex);
|
const matches = exp.match(regex);
|
||||||
if (matches === null) {
|
if (matches === null) {
|
||||||
errors.push(new AnimationParseError(`The provided timing value "${exp}" is invalid.`));
|
errors.push(new AnimationParseError(`The provided timing value "${exp}" is invalid.`));
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
export class StylesCollectionEntry {
|
export class StylesCollectionEntry {
|
||||||
@ -37,7 +36,7 @@ export class StylesCollection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListWrapper.insert(entries, insertionIndex, tuple);
|
entries.splice(insertionIndex, 0, tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
getByIndex(property: string, index: number): StylesCollectionEntry {
|
getByIndex(property: string, index: number): StylesCollectionEntry {
|
||||||
|
@ -8,17 +8,17 @@
|
|||||||
|
|
||||||
import {isDevMode} from '@angular/core';
|
import {isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {isArray, isBlank, isPresent, isString} from '../src/facade/lang';
|
import {isBlank, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
export function assertArrayOfStrings(identifier: string, value: any) {
|
export function assertArrayOfStrings(identifier: string, value: any) {
|
||||||
if (!isDevMode() || isBlank(value)) {
|
if (!isDevMode() || isBlank(value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isArray(value)) {
|
if (!Array.isArray(value)) {
|
||||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
||||||
}
|
}
|
||||||
for (var i = 0; i < value.length; i += 1) {
|
for (var i = 0; i < value.length; i += 1) {
|
||||||
if (!isString(value[i])) {
|
if (typeof value[i] !== 'string') {
|
||||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ const INTERPOLATION_BLACKLIST_REGEXPS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function assertInterpolationSymbols(identifier: string, value: any): void {
|
export function assertInterpolationSymbols(identifier: string, value: any): void {
|
||||||
if (isPresent(value) && !(isArray(value) && value.length == 2)) {
|
if (isPresent(value) && !(Array.isArray(value) && value.length == 2)) {
|
||||||
throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
|
throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
|
||||||
} else if (isDevMode() && !isBlank(value)) {
|
} else if (isDevMode() && !isBlank(value)) {
|
||||||
const start = value[0] as string;
|
const start = value[0] as string;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper, MapWrapper} from './facade/collection';
|
import {ListWrapper, MapWrapper} from './facade/collection';
|
||||||
import {isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {LifecycleHooks} from './private_import_core';
|
import {LifecycleHooks} from './private_import_core';
|
||||||
import {CssSelector} from './selector';
|
import {CssSelector} from './selector';
|
||||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
import {sanitizeIdentifier, splitAtColon} from './util';
|
||||||
@ -23,7 +23,6 @@ function unimplemented(): any {
|
|||||||
// group 2: "event" from "(event)"
|
// group 2: "event" from "(event)"
|
||||||
// group 3: "@trigger" from "@trigger"
|
// group 3: "@trigger" from "@trigger"
|
||||||
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
||||||
const UNDEFINED = new Object();
|
|
||||||
|
|
||||||
export abstract class CompileMetadataWithIdentifier {
|
export abstract class CompileMetadataWithIdentifier {
|
||||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
||||||
@ -106,33 +105,27 @@ export class CompileDiDependencyMetadata {
|
|||||||
isSkipSelf: boolean;
|
isSkipSelf: boolean;
|
||||||
isOptional: boolean;
|
isOptional: boolean;
|
||||||
isValue: boolean;
|
isValue: boolean;
|
||||||
query: CompileQueryMetadata;
|
|
||||||
viewQuery: CompileQueryMetadata;
|
|
||||||
token: CompileTokenMetadata;
|
token: CompileTokenMetadata;
|
||||||
value: any;
|
value: any;
|
||||||
|
|
||||||
constructor(
|
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
|
||||||
{isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, query, viewQuery, token,
|
isAttribute?: boolean,
|
||||||
value}: {
|
isSelf?: boolean,
|
||||||
isAttribute?: boolean,
|
isHost?: boolean,
|
||||||
isSelf?: boolean,
|
isSkipSelf?: boolean,
|
||||||
isHost?: boolean,
|
isOptional?: boolean,
|
||||||
isSkipSelf?: boolean,
|
isValue?: boolean,
|
||||||
isOptional?: boolean,
|
query?: CompileQueryMetadata,
|
||||||
isValue?: boolean,
|
viewQuery?: CompileQueryMetadata,
|
||||||
query?: CompileQueryMetadata,
|
token?: CompileTokenMetadata,
|
||||||
viewQuery?: CompileQueryMetadata,
|
value?: any
|
||||||
token?: CompileTokenMetadata,
|
} = {}) {
|
||||||
value?: any
|
this.isAttribute = !!isAttribute;
|
||||||
} = {}) {
|
this.isSelf = !!isSelf;
|
||||||
this.isAttribute = normalizeBool(isAttribute);
|
this.isHost = !!isHost;
|
||||||
this.isSelf = normalizeBool(isSelf);
|
this.isSkipSelf = !!isSkipSelf;
|
||||||
this.isHost = normalizeBool(isHost);
|
this.isOptional = !!isOptional;
|
||||||
this.isSkipSelf = normalizeBool(isSkipSelf);
|
this.isValue = !!isValue;
|
||||||
this.isOptional = normalizeBool(isOptional);
|
|
||||||
this.isValue = normalizeBool(isValue);
|
|
||||||
this.query = query;
|
|
||||||
this.viewQuery = viewQuery;
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
@ -161,8 +154,8 @@ export class CompileProviderMetadata {
|
|||||||
this.useValue = useValue;
|
this.useValue = useValue;
|
||||||
this.useExisting = useExisting;
|
this.useExisting = useExisting;
|
||||||
this.useFactory = useFactory;
|
this.useFactory = useFactory;
|
||||||
this.deps = normalizeBlank(deps);
|
this.deps = deps || null;
|
||||||
this.multi = normalizeBool(multi);
|
this.multi = !!multi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +185,7 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
|||||||
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
|
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
this.identifierIsInstance = normalizeBool(identifierIsInstance);
|
this.identifierIsInstance = !!identifierIsInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
get reference(): any {
|
get reference(): any {
|
||||||
@ -227,7 +220,7 @@ export class CompileTypeMetadata extends CompileIdentifierMetadata {
|
|||||||
lifecycleHooks?: LifecycleHooks[];
|
lifecycleHooks?: LifecycleHooks[];
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super({reference: reference, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value});
|
super({reference: reference, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value});
|
||||||
this.isHost = normalizeBool(isHost);
|
this.isHost = !!isHost;
|
||||||
this.diDeps = _normalizeArray(diDeps);
|
this.diDeps = _normalizeArray(diDeps);
|
||||||
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
||||||
}
|
}
|
||||||
@ -248,8 +241,8 @@ export class CompileQueryMetadata {
|
|||||||
read?: CompileTokenMetadata
|
read?: CompileTokenMetadata
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.selectors = selectors;
|
this.selectors = selectors;
|
||||||
this.descendants = normalizeBool(descendants);
|
this.descendants = !!descendants;
|
||||||
this.first = normalizeBool(first);
|
this.first = !!first;
|
||||||
this.propertyName = propertyName;
|
this.propertyName = propertyName;
|
||||||
this.read = read;
|
this.read = read;
|
||||||
}
|
}
|
||||||
@ -303,9 +296,9 @@ export class CompileTemplateMetadata {
|
|||||||
this.styles = _normalizeArray(styles);
|
this.styles = _normalizeArray(styles);
|
||||||
this.styleUrls = _normalizeArray(styleUrls);
|
this.styleUrls = _normalizeArray(styleUrls);
|
||||||
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
||||||
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
this.animations = animations ? ListWrapper.flatten(animations) : [];
|
||||||
this.ngContentSelectors = ngContentSelectors || [];
|
this.ngContentSelectors = ngContentSelectors || [];
|
||||||
if (isPresent(interpolation) && interpolation.length != 2) {
|
if (interpolation && interpolation.length != 2) {
|
||||||
throw new Error(`'interpolation' should have a start and an end symbol.`);
|
throw new Error(`'interpolation' should have a start and an end symbol.`);
|
||||||
}
|
}
|
||||||
this.interpolation = interpolation;
|
this.interpolation = interpolation;
|
||||||
@ -375,7 +368,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
|
|
||||||
return new CompileDirectiveMetadata({
|
return new CompileDirectiveMetadata({
|
||||||
type,
|
type,
|
||||||
isComponent: normalizeBool(isComponent), selector, exportAs, changeDetection,
|
isComponent: !!isComponent, selector, exportAs, changeDetection,
|
||||||
inputs: inputsMap,
|
inputs: inputsMap,
|
||||||
outputs: outputsMap,
|
outputs: outputsMap,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
@ -503,13 +496,13 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.pure = normalizeBool(pure);
|
this.pure = !!pure;
|
||||||
}
|
}
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a directive.
|
* Metadata regarding compilation of a module.
|
||||||
*/
|
*/
|
||||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
@ -569,6 +562,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||||||
export class TransitiveCompileNgModuleMetadata {
|
export class TransitiveCompileNgModuleMetadata {
|
||||||
directivesSet = new Set<Type<any>>();
|
directivesSet = new Set<Type<any>>();
|
||||||
pipesSet = new Set<Type<any>>();
|
pipesSet = new Set<Type<any>>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
||||||
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
|
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
|
||||||
@ -581,11 +575,13 @@ export class TransitiveCompileNgModuleMetadata {
|
|||||||
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
|
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
|
||||||
T[] {
|
T[] {
|
||||||
const map = new Map<any, T>();
|
const map = new Map<any, T>();
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (!map.get(item.identifier.reference)) {
|
if (!map.get(item.identifier.reference)) {
|
||||||
map.set(item.identifier.reference, item);
|
map.set(item.identifier.reference, item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return MapWrapper.values(map);
|
return MapWrapper.values(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +590,7 @@ function _normalizeArray(obj: any[]): any[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||||
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
|
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StaticSymbol {
|
export interface StaticSymbol {
|
||||||
|
@ -8,39 +8,27 @@
|
|||||||
|
|
||||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||||
|
|
||||||
export * from './template_parser/template_ast';
|
|
||||||
export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser';
|
|
||||||
export {CompilerConfig, RenderTypes} from './config';
|
|
||||||
export * from './compile_metadata';
|
|
||||||
export * from './offline_compiler';
|
|
||||||
export {RuntimeCompiler} from './runtime_compiler';
|
|
||||||
export * from './url_resolver';
|
|
||||||
export * from './resource_loader';
|
|
||||||
|
|
||||||
export {DirectiveResolver} from './directive_resolver';
|
|
||||||
export {PipeResolver} from './pipe_resolver';
|
|
||||||
export {NgModuleResolver} from './ng_module_resolver';
|
|
||||||
|
|
||||||
import {TemplateParser} from './template_parser/template_parser';
|
|
||||||
import {HtmlParser} from './ml_parser/html_parser';
|
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
|
||||||
import {CompileMetadataResolver} from './metadata_resolver';
|
|
||||||
import {StyleCompiler} from './style_compiler';
|
|
||||||
import {ViewCompiler} from './view_compiler/view_compiler';
|
|
||||||
import {NgModuleCompiler} from './ng_module_compiler';
|
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {RuntimeCompiler} from './runtime_compiler';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
|
||||||
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
|
|
||||||
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from './url_resolver';
|
|
||||||
import {Parser} from './expression_parser/parser';
|
|
||||||
import {Lexer} from './expression_parser/lexer';
|
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {PipeResolver} from './pipe_resolver';
|
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {Lexer} from './expression_parser/lexer';
|
||||||
import {Console, Reflector, reflector, ReflectorReader, ReflectionCapabilities} from './private_import_core';
|
import {Parser} from './expression_parser/parser';
|
||||||
import {ResourceLoader} from './resource_loader';
|
|
||||||
import * as i18n from './i18n/index';
|
import * as i18n from './i18n/index';
|
||||||
|
import {CompileMetadataResolver} from './metadata_resolver';
|
||||||
|
import {HtmlParser} from './ml_parser/html_parser';
|
||||||
|
import {NgModuleCompiler} from './ng_module_compiler';
|
||||||
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
|
import {PipeResolver} from './pipe_resolver';
|
||||||
|
import {Console, ReflectionCapabilities, Reflector, ReflectorReader, reflector} from './private_import_core';
|
||||||
|
import {ResourceLoader} from './resource_loader';
|
||||||
|
import {RuntimeCompiler} from './runtime_compiler';
|
||||||
|
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
|
||||||
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
|
import {StyleCompiler} from './style_compiler';
|
||||||
|
import {TemplateParser} from './template_parser/template_parser';
|
||||||
|
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from './url_resolver';
|
||||||
|
import {ViewCompiler} from './view_compiler/view_compiler';
|
||||||
|
|
||||||
const _NO_RESOURCE_LOADER: ResourceLoader = {
|
const _NO_RESOURCE_LOADER: ResourceLoader = {
|
||||||
get(url: string): Promise<string>{
|
get(url: string): Promise<string>{
|
||||||
@ -77,6 +65,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
|||||||
StyleCompiler,
|
StyleCompiler,
|
||||||
ViewCompiler,
|
ViewCompiler,
|
||||||
NgModuleCompiler,
|
NgModuleCompiler,
|
||||||
|
DirectiveWrapperCompiler,
|
||||||
{provide: CompilerConfig, useValue: new CompilerConfig()},
|
{provide: CompilerConfig, useValue: new CompilerConfig()},
|
||||||
RuntimeCompiler,
|
RuntimeCompiler,
|
||||||
{provide: Compiler, useExisting: RuntimeCompiler},
|
{provide: Compiler, useExisting: RuntimeCompiler},
|
||||||
|
48
modules/@angular/compiler/src/compiler_util/binding_util.ts
Normal file
48
modules/@angular/compiler/src/compiler_util/binding_util.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
|
import {ConvertPropertyBindingResult} from './expression_converter';
|
||||||
|
|
||||||
|
export class CheckBindingField {
|
||||||
|
constructor(public expression: o.ReadPropExpr, public bindingId: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCheckBindingField(builder: ClassBuilder): CheckBindingField {
|
||||||
|
const bindingId = `${builder.fields.length}`;
|
||||||
|
const fieldExpr = createBindFieldExpr(bindingId);
|
||||||
|
// private is fine here as no child view will reference the cached value...
|
||||||
|
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||||
|
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
|
||||||
|
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
|
||||||
|
.toStmt());
|
||||||
|
return new CheckBindingField(fieldExpr, bindingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCheckBindingStmt(
|
||||||
|
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||||
|
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||||
|
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||||
|
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||||
|
]);
|
||||||
|
if (evalResult.forceUpdate) {
|
||||||
|
condition = evalResult.forceUpdate.or(condition);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
...evalResult.stmts, new o.IfStmt(condition, actions.concat([
|
||||||
|
<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
|
||||||
|
]))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
|
||||||
|
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
|
||||||
|
}
|
@ -8,54 +8,132 @@
|
|||||||
|
|
||||||
|
|
||||||
import * as cdAst from '../expression_parser/ast';
|
import * as cdAst from '../expression_parser/ast';
|
||||||
import {isArray, isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
|
import {createPureProxy} from './identifier_util';
|
||||||
|
|
||||||
|
const VAL_UNWRAPPER_VAR = o.variable(`valUnwrapper`);
|
||||||
|
|
||||||
export interface NameResolver {
|
export interface NameResolver {
|
||||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||||
getLocal(name: string): o.Expression;
|
getLocal(name: string): o.Expression;
|
||||||
createLiteralArray(values: o.Expression[]): o.Expression;
|
|
||||||
createLiteralMap(values: Array<Array<string|o.Expression>>): o.Expression;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExpressionWithWrappedValueInfo {
|
export class EventHandlerVars { static event = o.variable('$event'); }
|
||||||
|
|
||||||
|
export class ConvertPropertyBindingResult {
|
||||||
constructor(
|
constructor(
|
||||||
public expression: o.Expression, public needsValueUnwrapper: boolean,
|
public stmts: o.Statement[], public currValExpr: o.Expression,
|
||||||
public temporaryCount: number) {}
|
public forceUpdate: o.Expression) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertCdExpressionToIr(
|
/**
|
||||||
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST,
|
* Converts the given expression AST into an executable output AST, assuming the expression is
|
||||||
valueUnwrapper: o.ReadVarExpr, bindingIndex: number): ExpressionWithWrappedValueInfo {
|
* used in a property binding.
|
||||||
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper, bindingIndex);
|
*/
|
||||||
const irAst: o.Expression = expression.visit(visitor, _Mode.Expression);
|
export function convertPropertyBinding(
|
||||||
return new ExpressionWithWrappedValueInfo(
|
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||||
irAst, visitor.needsValueUnwrapper, visitor.temporaryCount);
|
expression: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
|
||||||
|
const currValExpr = createCurrValueExpr(bindingId);
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
if (!nameResolver) {
|
||||||
|
nameResolver = new DefaultNameResolver();
|
||||||
|
}
|
||||||
|
const visitor = new _AstToIrVisitor(
|
||||||
|
builder, nameResolver, implicitReceiver, VAL_UNWRAPPER_VAR, bindingId, false);
|
||||||
|
const outputExpr: o.Expression = expression.visit(visitor, _Mode.Expression);
|
||||||
|
|
||||||
|
if (!outputExpr) {
|
||||||
|
// e.g. an empty expression was given
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visitor.temporaryCount) {
|
||||||
|
for (let i = 0; i < visitor.temporaryCount; i++) {
|
||||||
|
stmts.push(temporaryDeclaration(bindingId, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visitor.needsValueUnwrapper) {
|
||||||
|
var initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
|
||||||
|
stmts.push(initValueUnwrapperStmt);
|
||||||
|
}
|
||||||
|
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
if (visitor.needsValueUnwrapper) {
|
||||||
|
return new ConvertPropertyBindingResult(
|
||||||
|
stmts, currValExpr, VAL_UNWRAPPER_VAR.prop('hasWrappedValue'));
|
||||||
|
} else {
|
||||||
|
return new ConvertPropertyBindingResult(stmts, currValExpr, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertCdStatementToIr(
|
export class ConvertActionBindingResult {
|
||||||
nameResolver: NameResolver, implicitReceiver: o.Expression, stmt: cdAst.AST,
|
constructor(public stmts: o.Statement[], public preventDefault: o.ReadVarExpr) {}
|
||||||
bindingIndex: number): o.Statement[] {
|
|
||||||
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null, bindingIndex);
|
|
||||||
let statements: o.Statement[] = [];
|
|
||||||
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
|
|
||||||
prependTemporaryDecls(visitor.temporaryCount, bindingIndex, statements);
|
|
||||||
return statements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function temporaryName(bindingIndex: number, temporaryNumber: number): string {
|
/**
|
||||||
return `tmp_${bindingIndex}_${temporaryNumber}`;
|
* Converts the given expression AST into an executable output AST, assuming the expression is
|
||||||
|
* used in an action binding (e.g. an event handler).
|
||||||
|
*/
|
||||||
|
export function convertActionBinding(
|
||||||
|
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||||
|
action: cdAst.AST, bindingId: string): ConvertActionBindingResult {
|
||||||
|
if (!nameResolver) {
|
||||||
|
nameResolver = new DefaultNameResolver();
|
||||||
|
}
|
||||||
|
const visitor =
|
||||||
|
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
|
||||||
|
let actionStmts: o.Statement[] = [];
|
||||||
|
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
|
||||||
|
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
||||||
|
var lastIndex = actionStmts.length - 1;
|
||||||
|
var preventDefaultVar: o.ReadVarExpr = null;
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
var lastStatement = actionStmts[lastIndex];
|
||||||
|
var returnExpr = convertStmtIntoExpression(lastStatement);
|
||||||
|
if (returnExpr) {
|
||||||
|
// Note: We need to cast the result of the method call to dynamic,
|
||||||
|
// as it might be a void method!
|
||||||
|
preventDefaultVar = createPreventDefaultVar(bindingId);
|
||||||
|
actionStmts[lastIndex] =
|
||||||
|
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function temporaryDeclaration(bindingIndex: number, temporaryNumber: number): o.Statement {
|
/**
|
||||||
return new o.DeclareVarStmt(temporaryName(bindingIndex, temporaryNumber), o.NULL_EXPR);
|
* Creates variables that are shared by multiple calls to `convertActionBinding` /
|
||||||
|
* `convertPropertyBinding`
|
||||||
|
*/
|
||||||
|
export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
|
||||||
|
const unwrapperStmts: o.Statement[] = [];
|
||||||
|
var readVars = o.findReadVarNames(stmts);
|
||||||
|
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
||||||
|
unwrapperStmts.push(
|
||||||
|
VAL_UNWRAPPER_VAR
|
||||||
|
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
}
|
||||||
|
return unwrapperStmts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function temporaryName(bindingId: string, temporaryNumber: number): string {
|
||||||
|
return `tmp_${bindingId}_${temporaryNumber}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function temporaryDeclaration(bindingId: string, temporaryNumber: number): o.Statement {
|
||||||
|
return new o.DeclareVarStmt(temporaryName(bindingId, temporaryNumber), o.NULL_EXPR);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prependTemporaryDecls(
|
function prependTemporaryDecls(
|
||||||
temporaryCount: number, bindingIndex: number, statements: o.Statement[]) {
|
temporaryCount: number, bindingId: string, statements: o.Statement[]) {
|
||||||
for (let i = temporaryCount - 1; i >= 0; i--) {
|
for (let i = temporaryCount - 1; i >= 0; i--) {
|
||||||
statements.unshift(temporaryDeclaration(bindingIndex, i));
|
statements.unshift(temporaryDeclaration(bindingId, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +170,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
public temporaryCount: number = 0;
|
public temporaryCount: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _nameResolver: NameResolver, private _implicitReceiver: o.Expression,
|
private _builder: ClassBuilder, private _nameResolver: NameResolver,
|
||||||
private _valueUnwrapper: o.ReadVarExpr, private bindingIndex: number) {}
|
private _implicitReceiver: o.Expression, private _valueUnwrapper: o.ReadVarExpr,
|
||||||
|
private bindingId: string, private isAction: boolean) {}
|
||||||
|
|
||||||
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||||
var op: o.BinaryOperator;
|
var op: o.BinaryOperator;
|
||||||
@ -170,6 +249,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
const input = this.visit(ast.exp, _Mode.Expression);
|
const input = this.visit(ast.exp, _Mode.Expression);
|
||||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
const value = this._nameResolver.callPipe(ast.name, input, args);
|
const value = this._nameResolver.callPipe(ast.name, input, args);
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`Illegal state: Pipe ${ast.name} is not allowed here!`);
|
||||||
|
}
|
||||||
this.needsValueUnwrapper = true;
|
this.needsValueUnwrapper = true;
|
||||||
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
||||||
}
|
}
|
||||||
@ -209,8 +291,10 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(
|
const parts = this.visitAll(ast.expressions, mode);
|
||||||
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
const literalArr =
|
||||||
|
this.isAction ? o.literalArr(parts) : createCachedLiteralArray(this._builder, parts);
|
||||||
|
return convertToStatementIfNeeded(mode, literalArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||||
@ -218,13 +302,22 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
for (let i = 0; i < ast.keys.length; i++) {
|
for (let i = 0; i < ast.keys.length; i++) {
|
||||||
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
||||||
}
|
}
|
||||||
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
const literalMap =
|
||||||
|
this.isAction ? o.literalMap(parts) : createCachedLiteralMap(this._builder, parts);
|
||||||
|
return convertToStatementIfNeeded(mode, literalMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getLocal(name: string): o.Expression {
|
||||||
|
if (this.isAction && name == EventHandlerVars.event.name) {
|
||||||
|
return EventHandlerVars.event;
|
||||||
|
}
|
||||||
|
return this._nameResolver.getLocal(name);
|
||||||
|
}
|
||||||
|
|
||||||
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||||
if (leftMostSafe) {
|
if (leftMostSafe) {
|
||||||
@ -234,7 +327,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
let result: any = null;
|
let result: any = null;
|
||||||
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
var varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
result = varExpr.callFn(args);
|
result = varExpr.callFn(args);
|
||||||
}
|
}
|
||||||
@ -258,7 +351,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
let result: any = null;
|
let result: any = null;
|
||||||
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
result = this._nameResolver.getLocal(ast.name);
|
result = this._getLocal(ast.name);
|
||||||
}
|
}
|
||||||
if (isBlank(result)) {
|
if (isBlank(result)) {
|
||||||
result = receiver.prop(ast.name);
|
result = receiver.prop(ast.name);
|
||||||
@ -270,7 +363,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||||
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
var varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
throw new Error('Cannot assign to a reference or variable!');
|
throw new Error('Cannot assign to a reference or variable!');
|
||||||
}
|
}
|
||||||
@ -459,21 +552,87 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
private allocateTemporary(): o.ReadVarExpr {
|
private allocateTemporary(): o.ReadVarExpr {
|
||||||
const tempNumber = this._currentTemporary++;
|
const tempNumber = this._currentTemporary++;
|
||||||
this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
|
this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
|
||||||
return new o.ReadVarExpr(temporaryName(this.bindingIndex, tempNumber));
|
return new o.ReadVarExpr(temporaryName(this.bindingId, tempNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
private releaseTemporary(temporary: o.ReadVarExpr) {
|
private releaseTemporary(temporary: o.ReadVarExpr) {
|
||||||
this._currentTemporary--;
|
this._currentTemporary--;
|
||||||
if (temporary.name != temporaryName(this.bindingIndex, this._currentTemporary)) {
|
if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
|
||||||
throw new Error(`Temporary ${temporary.name} released out of order`);
|
throw new Error(`Temporary ${temporary.name} released out of order`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenStatements(arg: any, output: o.Statement[]) {
|
function flattenStatements(arg: any, output: o.Statement[]) {
|
||||||
if (isArray(arg)) {
|
if (Array.isArray(arg)) {
|
||||||
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
|
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
|
||||||
} else {
|
} else {
|
||||||
output.push(arg);
|
output.push(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
||||||
|
}
|
||||||
|
var proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
||||||
|
var proxyParams: o.FnParam[] = [];
|
||||||
|
var proxyReturnEntries: o.Expression[] = [];
|
||||||
|
for (var i = 0; i < values.length; i++) {
|
||||||
|
var paramName = `p${i}`;
|
||||||
|
proxyParams.push(new o.FnParam(paramName));
|
||||||
|
proxyReturnEntries.push(o.variable(paramName));
|
||||||
|
}
|
||||||
|
createPureProxy(
|
||||||
|
o.fn(
|
||||||
|
proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))],
|
||||||
|
new o.ArrayType(o.DYNAMIC_TYPE)),
|
||||||
|
values.length, proxyExpr, builder);
|
||||||
|
return proxyExpr.callFn(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCachedLiteralMap(
|
||||||
|
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
||||||
|
if (entries.length === 0) {
|
||||||
|
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
||||||
|
}
|
||||||
|
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
|
||||||
|
const proxyParams: o.FnParam[] = [];
|
||||||
|
const proxyReturnEntries: [string, o.Expression][] = [];
|
||||||
|
const values: o.Expression[] = [];
|
||||||
|
for (var i = 0; i < entries.length; i++) {
|
||||||
|
const paramName = `p${i}`;
|
||||||
|
proxyParams.push(new o.FnParam(paramName));
|
||||||
|
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
||||||
|
values.push(<o.Expression>entries[i][1]);
|
||||||
|
}
|
||||||
|
createPureProxy(
|
||||||
|
o.fn(
|
||||||
|
proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))],
|
||||||
|
new o.MapType(o.DYNAMIC_TYPE)),
|
||||||
|
entries.length, proxyExpr, builder);
|
||||||
|
return proxyExpr.callFn(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultNameResolver implements NameResolver {
|
||||||
|
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; }
|
||||||
|
getLocal(name: string): o.Expression { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCurrValueExpr(bindingId: string): o.ReadVarExpr {
|
||||||
|
return o.variable(`currVal_${bindingId}`); // fix syntax highlighting: `
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPreventDefaultVar(bindingId: string): o.ReadVarExpr {
|
||||||
|
return o.variable(`pd_${bindingId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||||
|
if (stmt instanceof o.ExpressionStatement) {
|
||||||
|
return stmt.expr;
|
||||||
|
} else if (stmt instanceof o.ReturnStatement) {
|
||||||
|
return stmt.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @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 {CompileTokenMetadata} from '../compile_metadata';
|
||||||
|
import {isPresent} from '../facade/lang';
|
||||||
|
import {IdentifierSpec, Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
|
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||||
|
if (isPresent(token.value)) {
|
||||||
|
return o.literal(token.value);
|
||||||
|
} else if (token.identifierIsInstance) {
|
||||||
|
return o.importExpr(token.identifier)
|
||||||
|
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||||
|
} else {
|
||||||
|
return o.importExpr(token.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInlineArray(values: o.Expression[]): o.Expression {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
|
||||||
|
}
|
||||||
|
const log2 = Math.log(values.length) / Math.log(2);
|
||||||
|
const index = Math.ceil(log2);
|
||||||
|
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
|
||||||
|
Identifiers.InlineArrayDynamic;
|
||||||
|
const identifier = resolveIdentifier(identifierSpec);
|
||||||
|
return o.importExpr(identifier).instantiate([
|
||||||
|
<o.Expression>o.literal(values.length)
|
||||||
|
].concat(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPureProxy(
|
||||||
|
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
||||||
|
builder: {fields: o.ClassField[], ctorStmts: {push: (stmt: o.Statement) => void}}) {
|
||||||
|
builder.fields.push(new o.ClassField(pureProxyProp.name, null));
|
||||||
|
var pureProxyId =
|
||||||
|
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
||||||
|
if (!pureProxyId) {
|
||||||
|
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||||
|
}
|
||||||
|
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
|
||||||
|
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEnumExpression(enumType: IdentifierSpec, enumValue: any): o.Expression {
|
||||||
|
const enumName =
|
||||||
|
Object.keys(enumType.runtime).find((propName) => enumType.runtime[propName] === enumValue);
|
||||||
|
if (!enumName) {
|
||||||
|
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`);
|
||||||
|
}
|
||||||
|
return o.importExpr(resolveEnumIdentifier(resolveIdentifier(enumType), enumName));
|
||||||
|
}
|
146
modules/@angular/compiler/src/compiler_util/render_util.ts
Normal file
146
modules/@angular/compiler/src/compiler_util/render_util.ts
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
|
import {isPresent} from '../facade/lang';
|
||||||
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||||
|
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
|
import {createEnumExpression} from './identifier_util';
|
||||||
|
|
||||||
|
export function writeToRenderer(
|
||||||
|
view: o.Expression, boundProp: BoundElementPropertyAst, renderElement: o.Expression,
|
||||||
|
renderValue: o.Expression, logBindingUpdate: boolean,
|
||||||
|
securityContextExpression?: o.Expression): o.Statement[] {
|
||||||
|
const updateStmts: o.Statement[] = [];
|
||||||
|
const renderer = view.prop('renderer');
|
||||||
|
renderValue = sanitizedValue(view, boundProp, renderValue, securityContextExpression);
|
||||||
|
switch (boundProp.type) {
|
||||||
|
case PropertyBindingType.Property:
|
||||||
|
if (logBindingUpdate) {
|
||||||
|
updateStmts.push(
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
|
||||||
|
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementProperty', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Attribute:
|
||||||
|
renderValue =
|
||||||
|
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||||
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementAttribute', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Class:
|
||||||
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementClass', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Style:
|
||||||
|
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||||
|
if (isPresent(boundProp.unit)) {
|
||||||
|
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||||
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementStyle', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Animation:
|
||||||
|
throw new Error('Illegal state: Should not come here!');
|
||||||
|
}
|
||||||
|
return updateStmts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizedValue(
|
||||||
|
view: o.Expression, boundProp: BoundElementPropertyAst, renderValue: o.Expression,
|
||||||
|
securityContextExpression?: o.Expression): o.Expression {
|
||||||
|
if (boundProp.securityContext === SecurityContext.NONE) {
|
||||||
|
return renderValue; // No sanitization needed.
|
||||||
|
}
|
||||||
|
if (!boundProp.needsRuntimeSecurityContext) {
|
||||||
|
securityContextExpression =
|
||||||
|
createEnumExpression(Identifiers.SecurityContext, boundProp.securityContext);
|
||||||
|
}
|
||||||
|
if (!securityContextExpression) {
|
||||||
|
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
||||||
|
}
|
||||||
|
let ctx = view.prop('viewUtils').prop('sanitizer');
|
||||||
|
let args = [securityContextExpression, renderValue];
|
||||||
|
return ctx.callMethod('sanitize', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function triggerAnimation(
|
||||||
|
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||||
|
eventListener: o.Expression, renderElement: o.Expression, renderValue: o.Expression,
|
||||||
|
lastRenderValue: o.Expression) {
|
||||||
|
const detachStmts: o.Statement[] = [];
|
||||||
|
const updateStmts: o.Statement[] = [];
|
||||||
|
|
||||||
|
const animationName = boundProp.name;
|
||||||
|
|
||||||
|
const animationFnExpr =
|
||||||
|
componentView.prop('componentType').prop('animations').key(o.literal(animationName));
|
||||||
|
|
||||||
|
// it's important to normalize the void value as `void` explicitly
|
||||||
|
// so that the styles data can be obtained from the stringmap
|
||||||
|
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||||
|
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
|
||||||
|
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||||
|
|
||||||
|
updateStmts.push(
|
||||||
|
animationTransitionVar
|
||||||
|
.set(animationFnExpr.callFn([
|
||||||
|
view, renderElement,
|
||||||
|
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
|
||||||
|
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
||||||
|
]))
|
||||||
|
.toDeclStmt());
|
||||||
|
|
||||||
|
detachStmts.push(
|
||||||
|
animationTransitionVar
|
||||||
|
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
||||||
|
.toDeclStmt());
|
||||||
|
|
||||||
|
const registerStmts = [
|
||||||
|
animationTransitionVar
|
||||||
|
.callMethod(
|
||||||
|
'onStart',
|
||||||
|
[eventListener.callMethod(
|
||||||
|
o.BuiltinMethod.Bind,
|
||||||
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'start'))])])
|
||||||
|
.toStmt(),
|
||||||
|
animationTransitionVar
|
||||||
|
.callMethod(
|
||||||
|
'onDone',
|
||||||
|
[eventListener.callMethod(
|
||||||
|
o.BuiltinMethod.Bind,
|
||||||
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'done'))])])
|
||||||
|
.toStmt(),
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
updateStmts.push(...registerStmts);
|
||||||
|
detachStmts.push(...registerStmts);
|
||||||
|
|
||||||
|
return {updateStmts, detachStmts};
|
||||||
|
}
|
458
modules/@angular/compiler/src/directive_wrapper_compiler.ts
Normal file
458
modules/@angular/compiler/src/directive_wrapper_compiler.ts
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
|
||||||
|
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
||||||
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
|
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||||
|
import {CompilerConfig} from './config';
|
||||||
|
import {Parser} from './expression_parser/parser';
|
||||||
|
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||||
|
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
|
||||||
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
|
import * as o from './output/output_ast';
|
||||||
|
import {ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
||||||
|
import {Console, LifecycleHooks, isDefaultChangeDetectionStrategy} from './private_import_core';
|
||||||
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
|
import {BindingParser} from './template_parser/binding_parser';
|
||||||
|
import {BoundElementPropertyAst, BoundEventAst} from './template_parser/template_ast';
|
||||||
|
|
||||||
|
export class DirectiveWrapperCompileResult {
|
||||||
|
constructor(public statements: o.Statement[], public dirWrapperClassVar: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTEXT_FIELD_NAME = 'context';
|
||||||
|
const CHANGES_FIELD_NAME = '_changes';
|
||||||
|
const CHANGED_FIELD_NAME = '_changed';
|
||||||
|
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
|
||||||
|
|
||||||
|
const CURR_VALUE_VAR = o.variable('currValue');
|
||||||
|
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
|
||||||
|
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
|
||||||
|
const VIEW_VAR = o.variable('view');
|
||||||
|
const COMPONENT_VIEW_VAR = o.variable('componentView');
|
||||||
|
const RENDER_EL_VAR = o.variable('el');
|
||||||
|
const EVENT_NAME_VAR = o.variable('eventName');
|
||||||
|
|
||||||
|
const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap([])).toStmt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We generate directive wrappers to prevent code bloat when a directive is used.
|
||||||
|
* A directive wrapper encapsulates
|
||||||
|
* the dirty checking for `@Input`, the handling of `@HostListener` / `@HostBinding`
|
||||||
|
* and calling the lifecyclehooks `ngOnInit`, `ngOnChanges`, `ngDoCheck`.
|
||||||
|
*
|
||||||
|
* So far, only `@Input` and the lifecycle hooks have been implemented.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class DirectiveWrapperCompiler {
|
||||||
|
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
||||||
|
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
|
||||||
|
|
||||||
|
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
|
||||||
|
const hostParseResult = parseHostBindings(dirMeta, this._exprParser, this._schemaRegistry);
|
||||||
|
reportParseErrors(hostParseResult.errors, this._console);
|
||||||
|
|
||||||
|
const builder = new DirectiveWrapperBuilder(this.compilerConfig, dirMeta);
|
||||||
|
Object.keys(dirMeta.inputs).forEach((inputFieldName) => {
|
||||||
|
addCheckInputMethod(inputFieldName, builder);
|
||||||
|
});
|
||||||
|
addNgDoCheckMethod(builder);
|
||||||
|
addCheckHostMethod(hostParseResult.hostProps, builder);
|
||||||
|
addHandleEventMethod(hostParseResult.hostListeners, builder);
|
||||||
|
addSubscribeMethod(dirMeta, builder);
|
||||||
|
|
||||||
|
const classStmt = builder.build();
|
||||||
|
return new DirectiveWrapperCompileResult([classStmt], classStmt.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectiveWrapperBuilder implements ClassBuilder {
|
||||||
|
fields: o.ClassField[] = [];
|
||||||
|
getters: o.ClassGetter[] = [];
|
||||||
|
methods: o.ClassMethod[] = [];
|
||||||
|
ctorStmts: o.Statement[] = [];
|
||||||
|
detachStmts: o.Statement[] = [];
|
||||||
|
destroyStmts: o.Statement[] = [];
|
||||||
|
|
||||||
|
genChanges: boolean;
|
||||||
|
ngOnChanges: boolean;
|
||||||
|
ngOnInit: boolean;
|
||||||
|
ngDoCheck: boolean;
|
||||||
|
ngOnDestroy: boolean;
|
||||||
|
|
||||||
|
constructor(public compilerConfig: CompilerConfig, public dirMeta: CompileDirectiveMetadata) {
|
||||||
|
const dirLifecycleHooks = dirMeta.type.lifecycleHooks;
|
||||||
|
this.genChanges = dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 ||
|
||||||
|
this.compilerConfig.logBindingUpdate;
|
||||||
|
this.ngOnChanges = dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
||||||
|
this.ngOnInit = dirLifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1;
|
||||||
|
this.ngDoCheck = dirLifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1;
|
||||||
|
this.ngOnDestroy = dirLifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1;
|
||||||
|
if (this.ngOnDestroy) {
|
||||||
|
this.destroyStmts.push(
|
||||||
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnDestroy', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): o.ClassStmt {
|
||||||
|
const dirDepParamNames: string[] = [];
|
||||||
|
for (let i = 0; i < this.dirMeta.type.diDeps.length; i++) {
|
||||||
|
dirDepParamNames.push(`p${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const methods = [
|
||||||
|
new o.ClassMethod(
|
||||||
|
'ngOnDetach',
|
||||||
|
[
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(
|
||||||
|
COMPONENT_VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
],
|
||||||
|
this.detachStmts),
|
||||||
|
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const fields: o.ClassField[] = [
|
||||||
|
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
|
||||||
|
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
|
||||||
|
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
|
||||||
|
];
|
||||||
|
const ctorStmts: o.Statement[] =
|
||||||
|
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
|
||||||
|
if (this.genChanges) {
|
||||||
|
fields.push(new o.ClassField(
|
||||||
|
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
|
||||||
|
ctorStmts.push(RESET_CHANGES_STMT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctorStmts.push(
|
||||||
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
|
||||||
|
.set(o.importExpr(this.dirMeta.type)
|
||||||
|
.instantiate(dirDepParamNames.map((paramName) => o.variable(paramName))))
|
||||||
|
.toStmt());
|
||||||
|
|
||||||
|
return createClassStmt({
|
||||||
|
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
|
||||||
|
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
|
||||||
|
builders: [{fields, ctorStmts, methods}, this]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||||
|
const changedVar = o.variable('changed');
|
||||||
|
const stmts: o.Statement[] = [
|
||||||
|
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
|
||||||
|
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt(),
|
||||||
|
];
|
||||||
|
const lifecycleStmts: o.Statement[] = [];
|
||||||
|
|
||||||
|
if (builder.genChanges) {
|
||||||
|
const onChangesStmts: o.Statement[] = [];
|
||||||
|
if (builder.ngOnChanges) {
|
||||||
|
onChangesStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
|
||||||
|
.callMethod('ngOnChanges', [o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
if (builder.compilerConfig.logBindingUpdate) {
|
||||||
|
onChangesStmts.push(
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||||
|
.callFn(
|
||||||
|
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
onChangesStmts.push(RESET_CHANGES_STMT);
|
||||||
|
lifecycleStmts.push(new o.IfStmt(changedVar, onChangesStmts));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.ngOnInit) {
|
||||||
|
lifecycleStmts.push(new o.IfStmt(
|
||||||
|
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
|
||||||
|
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
|
||||||
|
}
|
||||||
|
if (builder.ngDoCheck) {
|
||||||
|
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
|
||||||
|
}
|
||||||
|
if (lifecycleStmts.length > 0) {
|
||||||
|
stmts.push(new o.IfStmt(o.not(THROW_ON_CHANGE_VAR), lifecycleStmts));
|
||||||
|
}
|
||||||
|
stmts.push(new o.ReturnStatement(changedVar));
|
||||||
|
|
||||||
|
builder.methods.push(new o.ClassMethod(
|
||||||
|
'ngDoCheck',
|
||||||
|
[
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
|
],
|
||||||
|
stmts, o.BOOL_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
||||||
|
const field = createCheckBindingField(builder);
|
||||||
|
var onChangeStatements: o.Statement[] = [
|
||||||
|
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
|
||||||
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
|
||||||
|
];
|
||||||
|
if (builder.genChanges) {
|
||||||
|
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
||||||
|
.key(o.literal(input))
|
||||||
|
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
|
||||||
|
.instantiate([field.expression, CURR_VALUE_VAR]))
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodBody: o.Statement[] = createCheckBindingStmt(
|
||||||
|
{currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
|
||||||
|
THROW_ON_CHANGE_VAR, onChangeStatements);
|
||||||
|
builder.methods.push(new o.ClassMethod(
|
||||||
|
`check_${input}`,
|
||||||
|
[
|
||||||
|
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
|
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
|
||||||
|
],
|
||||||
|
methodBody));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCheckHostMethod(
|
||||||
|
hostProps: BoundElementPropertyAst[], builder: DirectiveWrapperBuilder) {
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
const methodParams: o.FnParam[] = [
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(
|
||||||
|
COMPONENT_VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
|
];
|
||||||
|
hostProps.forEach((hostProp, hostPropIdx) => {
|
||||||
|
const field = createCheckBindingField(builder);
|
||||||
|
const evalResult = convertPropertyBinding(
|
||||||
|
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostProp.value, field.bindingId);
|
||||||
|
if (!evalResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let securityContextExpr: o.ReadVarExpr;
|
||||||
|
if (hostProp.needsRuntimeSecurityContext) {
|
||||||
|
securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
|
||||||
|
methodParams.push(new o.FnParam(
|
||||||
|
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
||||||
|
}
|
||||||
|
let checkBindingStmts: o.Statement[];
|
||||||
|
if (hostProp.isAnimation) {
|
||||||
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
|
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
|
||||||
|
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
|
||||||
|
.or(o.importExpr(resolveIdentifier(Identifiers.noop))),
|
||||||
|
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
|
||||||
|
checkBindingStmts = updateStmts;
|
||||||
|
builder.detachStmts.push(...detachStmts);
|
||||||
|
} else {
|
||||||
|
checkBindingStmts = writeToRenderer(
|
||||||
|
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
|
||||||
|
builder.compilerConfig.logBindingUpdate, securityContextExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
stmts.push(...createCheckBindingStmt(
|
||||||
|
evalResult, field.expression, THROW_ON_CHANGE_VAR, checkBindingStmts));
|
||||||
|
});
|
||||||
|
builder.methods.push(new o.ClassMethod('checkHost', methodParams, stmts));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHandleEventMethod(hostListeners: BoundEventAst[], builder: DirectiveWrapperBuilder) {
|
||||||
|
const resultVar = o.variable(`result`);
|
||||||
|
const actionStmts: o.Statement[] = [resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)];
|
||||||
|
hostListeners.forEach((hostListener, eventIdx) => {
|
||||||
|
const evalResult = convertActionBinding(
|
||||||
|
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
|
||||||
|
`sub_${eventIdx}`);
|
||||||
|
const trueStmts = evalResult.stmts;
|
||||||
|
if (evalResult.preventDefault) {
|
||||||
|
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
|
||||||
|
}
|
||||||
|
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
|
||||||
|
actionStmts.push(
|
||||||
|
new o.IfStmt(EVENT_NAME_VAR.equals(o.literal(hostListener.fullName)), trueStmts));
|
||||||
|
});
|
||||||
|
actionStmts.push(new o.ReturnStatement(resultVar));
|
||||||
|
builder.methods.push(new o.ClassMethod(
|
||||||
|
'handleEvent',
|
||||||
|
[
|
||||||
|
new o.FnParam(EVENT_NAME_VAR.name, o.STRING_TYPE),
|
||||||
|
new o.FnParam(EventHandlerVars.event.name, o.DYNAMIC_TYPE)
|
||||||
|
],
|
||||||
|
actionStmts, o.BOOL_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
||||||
|
const methodParams: o.FnParam[] = [
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
|
||||||
|
];
|
||||||
|
const stmts: o.Statement[] = [
|
||||||
|
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME).set(o.variable(EVENT_HANDLER_FIELD_NAME)).toStmt()
|
||||||
|
];
|
||||||
|
Object.keys(dirMeta.outputs).forEach((emitterPropName, emitterIdx) => {
|
||||||
|
const eventName = dirMeta.outputs[emitterPropName];
|
||||||
|
const paramName = `emit${emitterIdx}`;
|
||||||
|
methodParams.push(new o.FnParam(paramName, o.BOOL_TYPE));
|
||||||
|
const subscriptionFieldName = `subscription${emitterIdx}`;
|
||||||
|
builder.fields.push(new o.ClassField(subscriptionFieldName, o.DYNAMIC_TYPE));
|
||||||
|
stmts.push(new o.IfStmt(o.variable(paramName), [
|
||||||
|
o.THIS_EXPR.prop(subscriptionFieldName)
|
||||||
|
.set(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
|
||||||
|
.prop(emitterPropName)
|
||||||
|
.callMethod(
|
||||||
|
o.BuiltinMethod.SubscribeObservable,
|
||||||
|
[o.variable(EVENT_HANDLER_FIELD_NAME)
|
||||||
|
.callMethod(o.BuiltinMethod.Bind, [VIEW_VAR, o.literal(eventName)])]))
|
||||||
|
.toStmt()
|
||||||
|
]));
|
||||||
|
builder.destroyStmts.push(
|
||||||
|
o.THIS_EXPR.prop(subscriptionFieldName)
|
||||||
|
.and(o.THIS_EXPR.prop(subscriptionFieldName).callMethod('unsubscribe', []))
|
||||||
|
.toStmt());
|
||||||
|
});
|
||||||
|
builder.methods.push(new o.ClassMethod('subscribe', methodParams, stmts));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParseResult {
|
||||||
|
constructor(
|
||||||
|
public hostProps: BoundElementPropertyAst[], public hostListeners: BoundEventAst[],
|
||||||
|
public errors: ParseError[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHostBindings(
|
||||||
|
dirMeta: CompileDirectiveMetadata, exprParser: Parser,
|
||||||
|
schemaRegistry: ElementSchemaRegistry): ParseResult {
|
||||||
|
const errors: ParseError[] = [];
|
||||||
|
const parser =
|
||||||
|
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
|
||||||
|
const sourceFileName = dirMeta.type.moduleUrl ?
|
||||||
|
`in Directive ${dirMeta.type.name} in ${dirMeta.type.moduleUrl}` :
|
||||||
|
`in Directive ${dirMeta.type.name}`;
|
||||||
|
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||||
|
const sourceSpan = new ParseSourceSpan(
|
||||||
|
new ParseLocation(sourceFile, null, null, null),
|
||||||
|
new ParseLocation(sourceFile, null, null, null));
|
||||||
|
const parsedHostProps = parser.createDirectiveHostPropertyAsts(dirMeta, sourceSpan);
|
||||||
|
const parsedHostListeners = parser.createDirectiveHostEventAsts(dirMeta, sourceSpan);
|
||||||
|
|
||||||
|
return new ParseResult(parsedHostProps, parsedHostListeners, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportParseErrors(parseErrors: ParseError[], console: Console) {
|
||||||
|
const warnings = parseErrors.filter(error => error.level === ParseErrorLevel.WARNING);
|
||||||
|
const errors = parseErrors.filter(error => error.level === ParseErrorLevel.FATAL);
|
||||||
|
|
||||||
|
if (warnings.length > 0) {
|
||||||
|
this._console.warn(`Directive parse warnings:\n${warnings.join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new Error(`Directive parse errors:\n${errors.join('\n')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectiveWrapperExpressions {
|
||||||
|
static create(dir: CompileIdentifierMetadata, depsExpr: o.Expression[]): o.Expression {
|
||||||
|
return o.importExpr(dir).instantiate(depsExpr, o.importType(dir));
|
||||||
|
}
|
||||||
|
static context(dirWrapper: o.Expression): o.ReadPropExpr {
|
||||||
|
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngDoCheck(
|
||||||
|
dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression,
|
||||||
|
throwOnChange: o.Expression): o.Expression {
|
||||||
|
return dirWrapper.callMethod('ngDoCheck', [view, renderElement, throwOnChange]);
|
||||||
|
}
|
||||||
|
static checkHost(
|
||||||
|
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
||||||
|
componentView: o.Expression, renderElement: o.Expression, throwOnChange: o.Expression,
|
||||||
|
runtimeSecurityContexts: o.Expression[]): o.Statement[] {
|
||||||
|
if (hostProps.length) {
|
||||||
|
return [dirWrapper
|
||||||
|
.callMethod(
|
||||||
|
'checkHost', [view, componentView, renderElement, throwOnChange].concat(
|
||||||
|
runtimeSecurityContexts))
|
||||||
|
.toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ngOnDetach(
|
||||||
|
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
||||||
|
componentView: o.Expression, renderEl: o.Expression): o.Statement[] {
|
||||||
|
if (hostProps.some(prop => prop.isAnimation)) {
|
||||||
|
return [dirWrapper
|
||||||
|
.callMethod(
|
||||||
|
'ngOnDetach',
|
||||||
|
[
|
||||||
|
view,
|
||||||
|
componentView,
|
||||||
|
renderEl,
|
||||||
|
])
|
||||||
|
.toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ngOnDestroy(dir: CompileDirectiveMetadata, dirWrapper: o.Expression): o.Statement[] {
|
||||||
|
if (dir.type.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1 ||
|
||||||
|
Object.keys(dir.outputs).length > 0) {
|
||||||
|
return [dirWrapper.callMethod('ngOnDestroy', []).toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static subscribe(
|
||||||
|
dirMeta: CompileDirectiveMetadata, hostProps: BoundElementPropertyAst[], usedEvents: string[],
|
||||||
|
dirWrapper: o.Expression, view: o.Expression, eventListener: o.Expression): o.Statement[] {
|
||||||
|
let needsSubscribe = false;
|
||||||
|
let eventFlags: o.Expression[] = [];
|
||||||
|
Object.keys(dirMeta.outputs).forEach((propName) => {
|
||||||
|
const eventName = dirMeta.outputs[propName];
|
||||||
|
const eventUsed = usedEvents.indexOf(eventName) > -1;
|
||||||
|
needsSubscribe = needsSubscribe || eventUsed;
|
||||||
|
eventFlags.push(o.literal(eventUsed));
|
||||||
|
});
|
||||||
|
hostProps.forEach((hostProp) => {
|
||||||
|
if (hostProp.isAnimation && usedEvents.length > 0) {
|
||||||
|
needsSubscribe = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (needsSubscribe) {
|
||||||
|
return [
|
||||||
|
dirWrapper.callMethod('subscribe', [view, eventListener].concat(eventFlags)).toStmt()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static handleEvent(
|
||||||
|
hostEvents: BoundEventAst[], dirWrapper: o.Expression, eventName: o.Expression,
|
||||||
|
event: o.Expression): o.Expression {
|
||||||
|
return dirWrapper.callMethod('handleEvent', [eventName, event]);
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {NumberWrapper, StringJoiner, isPresent} from '../facade/lang';
|
import {NumberWrapper, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
Character,
|
Character,
|
||||||
@ -274,42 +274,41 @@ class _Scanner {
|
|||||||
}
|
}
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var str: string = this.input.substring(start, this.index);
|
const str: string = this.input.substring(start, this.index);
|
||||||
var value: number = simple ? NumberWrapper.parseIntAutoRadix(str) : parseFloat(str);
|
const value: number = simple ? NumberWrapper.parseIntAutoRadix(str) : parseFloat(str);
|
||||||
return newNumberToken(start, value);
|
return newNumberToken(start, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanString(): Token {
|
scanString(): Token {
|
||||||
var start: number = this.index;
|
const start: number = this.index;
|
||||||
var quote: number = this.peek;
|
const quote: number = this.peek;
|
||||||
this.advance(); // Skip initial quote.
|
this.advance(); // Skip initial quote.
|
||||||
|
|
||||||
var buffer: StringJoiner;
|
let buffer: string = '';
|
||||||
var marker: number = this.index;
|
let marker: number = this.index;
|
||||||
var input: string = this.input;
|
let input: string = this.input;
|
||||||
|
|
||||||
while (this.peek != quote) {
|
while (this.peek != quote) {
|
||||||
if (this.peek == chars.$BACKSLASH) {
|
if (this.peek == chars.$BACKSLASH) {
|
||||||
if (buffer == null) buffer = new StringJoiner();
|
buffer += input.substring(marker, this.index);
|
||||||
buffer.add(input.substring(marker, this.index));
|
|
||||||
this.advance();
|
this.advance();
|
||||||
var unescapedCode: number;
|
let unescapedCode: number;
|
||||||
if (this.peek == chars.$u) {
|
if (this.peek == chars.$u) {
|
||||||
// 4 character hex code for unicode character.
|
// 4 character hex code for unicode character.
|
||||||
var hex: string = input.substring(this.index + 1, this.index + 5);
|
const hex: string = input.substring(this.index + 1, this.index + 5);
|
||||||
try {
|
try {
|
||||||
unescapedCode = NumberWrapper.parseInt(hex, 16);
|
unescapedCode = NumberWrapper.parseInt(hex, 16);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
||||||
}
|
}
|
||||||
for (var i: number = 0; i < 5; i++) {
|
for (let i: number = 0; i < 5; i++) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unescapedCode = unescape(this.peek);
|
unescapedCode = unescape(this.peek);
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
buffer.add(String.fromCharCode(unescapedCode));
|
buffer += String.fromCharCode(unescapedCode);
|
||||||
marker = this.index;
|
marker = this.index;
|
||||||
} else if (this.peek == chars.$EOF) {
|
} else if (this.peek == chars.$EOF) {
|
||||||
return this.error('Unterminated quote', 0);
|
return this.error('Unterminated quote', 0);
|
||||||
@ -318,16 +317,10 @@ class _Scanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var last: string = input.substring(marker, this.index);
|
const last: string = input.substring(marker, this.index);
|
||||||
this.advance(); // Skip terminating quote.
|
this.advance(); // Skip terminating quote.
|
||||||
|
|
||||||
// Compute the unescaped string value.
|
return newStringToken(start, buffer + last);
|
||||||
var unescaped: string = last;
|
|
||||||
if (buffer != null) {
|
|
||||||
buffer.add(last);
|
|
||||||
unescaped = buffer.toString();
|
|
||||||
}
|
|
||||||
return newStringToken(start, unescaped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message: string, offset: number): Token {
|
error(message: string, offset: number): Token {
|
||||||
|
@ -60,10 +60,11 @@ export class Parser {
|
|||||||
parseSimpleBinding(
|
parseSimpleBinding(
|
||||||
input: string, location: string,
|
input: string, location: string,
|
||||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
||||||
var ast = this._parseBindingAst(input, location, interpolationConfig);
|
const ast = this._parseBindingAst(input, location, interpolationConfig);
|
||||||
if (!SimpleExpressionChecker.check(ast)) {
|
const errors = SimpleExpressionChecker.check(ast);
|
||||||
|
if (errors.length > 0) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
'Host binding expression can only contain field access and constants', input, location);
|
`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
|
||||||
}
|
}
|
||||||
return new ASTWithSource(ast, input, location, this.errors);
|
return new ASTWithSource(ast, input, location, this.errors);
|
||||||
}
|
}
|
||||||
@ -751,51 +752,51 @@ export class _ParseAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SimpleExpressionChecker implements AstVisitor {
|
class SimpleExpressionChecker implements AstVisitor {
|
||||||
static check(ast: AST): boolean {
|
static check(ast: AST): string[] {
|
||||||
var s = new SimpleExpressionChecker();
|
var s = new SimpleExpressionChecker();
|
||||||
ast.visit(s);
|
ast.visit(s);
|
||||||
return s.simple;
|
return s.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
simple = true;
|
errors: string[] = [];
|
||||||
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver, context: any) {}
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any) {}
|
||||||
|
|
||||||
visitInterpolation(ast: Interpolation, context: any) { this.simple = false; }
|
visitInterpolation(ast: Interpolation, context: any) {}
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {}
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {}
|
||||||
|
|
||||||
visitPropertyRead(ast: PropertyRead, context: any) {}
|
visitPropertyRead(ast: PropertyRead, context: any) {}
|
||||||
|
|
||||||
visitPropertyWrite(ast: PropertyWrite, context: any) { this.simple = false; }
|
visitPropertyWrite(ast: PropertyWrite, context: any) {}
|
||||||
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead, context: any) { this.simple = false; }
|
visitSafePropertyRead(ast: SafePropertyRead, context: any) {}
|
||||||
|
|
||||||
visitMethodCall(ast: MethodCall, context: any) { this.simple = false; }
|
visitMethodCall(ast: MethodCall, context: any) {}
|
||||||
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall, context: any) { this.simple = false; }
|
visitSafeMethodCall(ast: SafeMethodCall, context: any) {}
|
||||||
|
|
||||||
visitFunctionCall(ast: FunctionCall, context: any) { this.simple = false; }
|
visitFunctionCall(ast: FunctionCall, context: any) {}
|
||||||
|
|
||||||
visitLiteralArray(ast: LiteralArray, context: any) { this.visitAll(ast.expressions); }
|
visitLiteralArray(ast: LiteralArray, context: any) { this.visitAll(ast.expressions); }
|
||||||
|
|
||||||
visitLiteralMap(ast: LiteralMap, context: any) { this.visitAll(ast.values); }
|
visitLiteralMap(ast: LiteralMap, context: any) { this.visitAll(ast.values); }
|
||||||
|
|
||||||
visitBinary(ast: Binary, context: any) { this.simple = false; }
|
visitBinary(ast: Binary, context: any) {}
|
||||||
|
|
||||||
visitPrefixNot(ast: PrefixNot, context: any) { this.simple = false; }
|
visitPrefixNot(ast: PrefixNot, context: any) {}
|
||||||
|
|
||||||
visitConditional(ast: Conditional, context: any) { this.simple = false; }
|
visitConditional(ast: Conditional, context: any) {}
|
||||||
|
|
||||||
visitPipe(ast: BindingPipe, context: any) { this.simple = false; }
|
visitPipe(ast: BindingPipe, context: any) { this.errors.push('pipes'); }
|
||||||
|
|
||||||
visitKeyedRead(ast: KeyedRead, context: any) { this.simple = false; }
|
visitKeyedRead(ast: KeyedRead, context: any) {}
|
||||||
|
|
||||||
visitKeyedWrite(ast: KeyedWrite, context: any) { this.simple = false; }
|
visitKeyedWrite(ast: KeyedWrite, context: any) {}
|
||||||
|
|
||||||
visitAll(asts: any[]): any[] { return asts.map(node => node.visit(this)); }
|
visitAll(asts: any[]): any[] { return asts.map(node => node.visit(this)); }
|
||||||
|
|
||||||
visitChain(ast: Chain, context: any) { this.simple = false; }
|
visitChain(ast: Chain, context: any) {}
|
||||||
|
|
||||||
visitQuote(ast: Quote, context: any) { this.simple = false; }
|
visitQuote(ast: Quote, context: any) {}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
|
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
|
||||||
import {assetUrl} from './util';
|
|
||||||
|
|
||||||
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||||
var VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
|
var VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
|
||||||
@ -33,7 +32,7 @@ export class Identifiers {
|
|||||||
static ViewUtils: IdentifierSpec = {
|
static ViewUtils: IdentifierSpec = {
|
||||||
name: 'ViewUtils',
|
name: 'ViewUtils',
|
||||||
moduleUrl: assetUrl('core', 'linker/view_utils'),
|
moduleUrl: assetUrl('core', 'linker/view_utils'),
|
||||||
runtime: ViewUtils
|
runtime: view_utils.ViewUtils
|
||||||
};
|
};
|
||||||
static AppView:
|
static AppView:
|
||||||
IdentifierSpec = {name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: AppView};
|
IdentifierSpec = {name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: AppView};
|
||||||
@ -161,45 +160,52 @@ export class Identifiers {
|
|||||||
static checkBinding: IdentifierSpec = {
|
static checkBinding: IdentifierSpec = {
|
||||||
name: 'checkBinding',
|
name: 'checkBinding',
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
runtime: checkBinding
|
runtime: view_utils.checkBinding
|
||||||
};
|
|
||||||
static flattenNestedViewRenderNodes: IdentifierSpec = {
|
|
||||||
name: 'flattenNestedViewRenderNodes',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: flattenNestedViewRenderNodes
|
|
||||||
};
|
};
|
||||||
static devModeEqual:
|
static devModeEqual:
|
||||||
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
|
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
|
||||||
static interpolate: IdentifierSpec = {
|
static interpolate: IdentifierSpec = {
|
||||||
name: 'interpolate',
|
name: 'interpolate',
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
runtime: interpolate
|
runtime: view_utils.interpolate
|
||||||
};
|
};
|
||||||
static castByValue: IdentifierSpec = {
|
static castByValue: IdentifierSpec = {
|
||||||
name: 'castByValue',
|
name: 'castByValue',
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
runtime: castByValue
|
runtime: view_utils.castByValue
|
||||||
};
|
};
|
||||||
static EMPTY_ARRAY: IdentifierSpec = {
|
static EMPTY_ARRAY: IdentifierSpec = {
|
||||||
name: 'EMPTY_ARRAY',
|
name: 'EMPTY_ARRAY',
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
runtime: EMPTY_ARRAY
|
runtime: view_utils.EMPTY_ARRAY
|
||||||
};
|
};
|
||||||
static EMPTY_MAP:
|
static EMPTY_MAP: IdentifierSpec = {
|
||||||
IdentifierSpec = {name: 'EMPTY_MAP', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: EMPTY_MAP};
|
name: 'EMPTY_MAP',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
static pureProxies = [
|
runtime: view_utils.EMPTY_MAP
|
||||||
|
};
|
||||||
|
static createRenderElement: IdentifierSpec = {
|
||||||
|
name: 'createRenderElement',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.createRenderElement
|
||||||
|
};
|
||||||
|
static selectOrCreateRenderHostElement: IdentifierSpec = {
|
||||||
|
name: 'selectOrCreateRenderHostElement',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.selectOrCreateRenderHostElement
|
||||||
|
};
|
||||||
|
static pureProxies: IdentifierSpec[] = [
|
||||||
null,
|
null,
|
||||||
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy1},
|
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy1},
|
||||||
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy2},
|
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy2},
|
||||||
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy3},
|
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy3},
|
||||||
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy4},
|
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy4},
|
||||||
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy5},
|
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy5},
|
||||||
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy6},
|
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy6},
|
||||||
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy7},
|
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy7},
|
||||||
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy8},
|
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy8},
|
||||||
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy9},
|
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy9},
|
||||||
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy10},
|
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy10},
|
||||||
];
|
];
|
||||||
static SecurityContext: IdentifierSpec = {
|
static SecurityContext: IdentifierSpec = {
|
||||||
name: 'SecurityContext',
|
name: 'SecurityContext',
|
||||||
@ -259,18 +265,64 @@ export class Identifiers {
|
|||||||
static LOCALE_ID: IdentifierSpec = {
|
static LOCALE_ID: IdentifierSpec = {
|
||||||
name: 'LOCALE_ID',
|
name: 'LOCALE_ID',
|
||||||
moduleUrl: assetUrl('core', 'i18n/tokens'),
|
moduleUrl: assetUrl('core', 'i18n/tokens'),
|
||||||
runtime: LOCALE_ID_
|
runtime: LOCALE_ID
|
||||||
};
|
};
|
||||||
static TRANSLATIONS_FORMAT: IdentifierSpec = {
|
static TRANSLATIONS_FORMAT: IdentifierSpec = {
|
||||||
name: 'TRANSLATIONS_FORMAT',
|
name: 'TRANSLATIONS_FORMAT',
|
||||||
moduleUrl: assetUrl('core', 'i18n/tokens'),
|
moduleUrl: assetUrl('core', 'i18n/tokens'),
|
||||||
runtime: TRANSLATIONS_FORMAT_
|
runtime: TRANSLATIONS_FORMAT
|
||||||
|
};
|
||||||
|
static setBindingDebugInfo: IdentifierSpec = {
|
||||||
|
name: 'setBindingDebugInfo',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.setBindingDebugInfo
|
||||||
|
};
|
||||||
|
static setBindingDebugInfoForChanges: IdentifierSpec = {
|
||||||
|
name: 'setBindingDebugInfoForChanges',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.setBindingDebugInfoForChanges
|
||||||
};
|
};
|
||||||
static AnimationTransition: IdentifierSpec = {
|
static AnimationTransition: IdentifierSpec = {
|
||||||
name: 'AnimationTransition',
|
name: 'AnimationTransition',
|
||||||
moduleUrl: assetUrl('core', 'animation/animation_transition'),
|
moduleUrl: assetUrl('core', 'animation/animation_transition'),
|
||||||
runtime: AnimationTransition
|
runtime: AnimationTransition
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is just the interface!
|
||||||
|
static InlineArray:
|
||||||
|
IdentifierSpec = {name: 'InlineArray', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: null};
|
||||||
|
static inlineArrays: IdentifierSpec[] = [
|
||||||
|
{name: 'InlineArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray2},
|
||||||
|
{name: 'InlineArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray2},
|
||||||
|
{name: 'InlineArray4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray4},
|
||||||
|
{name: 'InlineArray8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray8},
|
||||||
|
{name: 'InlineArray16', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray16},
|
||||||
|
];
|
||||||
|
static EMPTY_INLINE_ARRAY: IdentifierSpec = {
|
||||||
|
name: 'EMPTY_INLINE_ARRAY',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.EMPTY_INLINE_ARRAY
|
||||||
|
};
|
||||||
|
static InlineArrayDynamic: IdentifierSpec = {
|
||||||
|
name: 'InlineArrayDynamic',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.InlineArrayDynamic
|
||||||
|
};
|
||||||
|
static subscribeToRenderElement: IdentifierSpec = {
|
||||||
|
name: 'subscribeToRenderElement',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: view_utils.subscribeToRenderElement
|
||||||
|
};
|
||||||
|
static noop:
|
||||||
|
IdentifierSpec = {name: 'noop', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.noop};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
||||||
|
if (path == null) {
|
||||||
|
return `asset:@angular/lib/${pkg}/index`;
|
||||||
|
} else {
|
||||||
|
return `asset:@angular/lib/${pkg}/src/${path}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveIdentifier(identifier: IdentifierSpec) {
|
export function resolveIdentifier(identifier: IdentifierSpec) {
|
||||||
|
@ -1,20 +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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module
|
|
||||||
* @description
|
|
||||||
* Starting point to import all compiler APIs.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NgModuleResolver, OfflineCompiler, PipeResolver, RenderTypes, ResourceLoader, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, createOfflineCompileUrlResolver, platformCoreDynamic} from './compiler';
|
|
||||||
export {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/interpolation_config';
|
|
||||||
export {ElementSchemaRegistry} from './schema/element_schema_registry';
|
|
||||||
export * from './i18n/index';
|
|
||||||
export * from './template_parser/template_ast';
|
|
||||||
export * from './private_export';
|
|
@ -11,7 +11,7 @@ import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata
|
|||||||
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
import * as cpl from './compile_metadata';
|
import * as cpl from './compile_metadata';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {isBlank, isPresent, isString, stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
@ -116,8 +116,7 @@ export class CompileMetadataResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
|
getDirectiveMetadata(directiveType: any, throwIfNotFound = true): cpl.CompileDirectiveMetadata {
|
||||||
cpl.CompileDirectiveMetadata {
|
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
let meta = this._directiveCache.get(directiveType);
|
let meta = this._directiveCache.get(directiveType);
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
@ -161,7 +160,7 @@ export class CompileMetadataResolver {
|
|||||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata =
|
entryComponentMetadata =
|
||||||
flattenArray(dirMeta.entryComponents)
|
flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
|
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
@ -229,7 +228,7 @@ export class CompileMetadataResolver {
|
|||||||
const schemas: SchemaMetadata[] = [];
|
const schemas: SchemaMetadata[] = [];
|
||||||
|
|
||||||
if (meta.imports) {
|
if (meta.imports) {
|
||||||
flattenArray(meta.imports).forEach((importedType) => {
|
flattenAndDedupeArray(meta.imports).forEach((importedType) => {
|
||||||
let importedModuleType: Type<any>;
|
let importedModuleType: Type<any>;
|
||||||
if (isValidType(importedType)) {
|
if (isValidType(importedType)) {
|
||||||
importedModuleType = importedType;
|
importedModuleType = importedType;
|
||||||
@ -258,7 +257,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (meta.exports) {
|
if (meta.exports) {
|
||||||
flattenArray(meta.exports).forEach((exportedType) => {
|
flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
|
||||||
if (!isValidType(exportedType)) {
|
if (!isValidType(exportedType)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
||||||
@ -284,7 +283,7 @@ export class CompileMetadataResolver {
|
|||||||
const transitiveModule =
|
const transitiveModule =
|
||||||
this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
||||||
if (meta.declarations) {
|
if (meta.declarations) {
|
||||||
flattenArray(meta.declarations).forEach((declaredType) => {
|
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
||||||
if (!isValidType(declaredType)) {
|
if (!isValidType(declaredType)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
||||||
@ -314,12 +313,12 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(
|
entryComponents.push(
|
||||||
...flattenArray(meta.entryComponents)
|
...flattenAndDedupeArray(meta.entryComponents)
|
||||||
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
|
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
const typeMetadata = flattenArray(meta.bootstrap).map(type => {
|
const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => {
|
||||||
if (!isValidType(type)) {
|
if (!isValidType(type)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
|
||||||
@ -332,7 +331,7 @@ export class CompileMetadataResolver {
|
|||||||
entryComponents.push(...bootstrapComponents);
|
entryComponents.push(...bootstrapComponents);
|
||||||
|
|
||||||
if (meta.schemas) {
|
if (meta.schemas) {
|
||||||
schemas.push(...flattenArray(meta.schemas));
|
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
||||||
}
|
}
|
||||||
|
|
||||||
transitiveModule.entryComponents.push(...entryComponents);
|
transitiveModule.entryComponents.push(...entryComponents);
|
||||||
@ -379,15 +378,15 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeDescriptor(type: Type<any>): string {
|
private _getTypeDescriptor(type: Type<any>): string {
|
||||||
if (this._directiveResolver.resolve(type, false) !== null) {
|
if (this._directiveResolver.resolve(type, false)) {
|
||||||
return 'directive';
|
return 'directive';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._pipeResolver.resolve(type, false) !== null) {
|
if (this._pipeResolver.resolve(type, false)) {
|
||||||
return 'pipe';
|
return 'pipe';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._ngModuleResolver.resolve(type, false) !== null) {
|
if (this._ngModuleResolver.resolve(type, false)) {
|
||||||
return 'module';
|
return 'module';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,9 +506,7 @@ export class CompileMetadataResolver {
|
|||||||
let isSelf = false;
|
let isSelf = false;
|
||||||
let isSkipSelf = false;
|
let isSkipSelf = false;
|
||||||
let isOptional = false;
|
let isOptional = false;
|
||||||
let query: Query = null;
|
let token: any = null;
|
||||||
let viewQuery: Query = null;
|
|
||||||
var token: any = null;
|
|
||||||
if (Array.isArray(param)) {
|
if (Array.isArray(param)) {
|
||||||
param.forEach((paramEntry) => {
|
param.forEach((paramEntry) => {
|
||||||
if (paramEntry instanceof Host) {
|
if (paramEntry instanceof Host) {
|
||||||
@ -523,12 +520,6 @@ export class CompileMetadataResolver {
|
|||||||
} else if (paramEntry instanceof Attribute) {
|
} else if (paramEntry instanceof Attribute) {
|
||||||
isAttribute = true;
|
isAttribute = true;
|
||||||
token = paramEntry.attributeName;
|
token = paramEntry.attributeName;
|
||||||
} else if (paramEntry instanceof Query) {
|
|
||||||
if (paramEntry.isViewQuery) {
|
|
||||||
viewQuery = paramEntry;
|
|
||||||
} else {
|
|
||||||
query = paramEntry;
|
|
||||||
}
|
|
||||||
} else if (paramEntry instanceof Inject) {
|
} else if (paramEntry instanceof Inject) {
|
||||||
token = paramEntry.token;
|
token = paramEntry.token;
|
||||||
} else if (isValidType(paramEntry) && isBlank(token)) {
|
} else if (isValidType(paramEntry) && isBlank(token)) {
|
||||||
@ -549,8 +540,6 @@ export class CompileMetadataResolver {
|
|||||||
isSelf,
|
isSelf,
|
||||||
isSkipSelf,
|
isSkipSelf,
|
||||||
isOptional,
|
isOptional,
|
||||||
query: query ? this.getQueryMetadata(query, null, typeOrFunc) : null,
|
|
||||||
viewQuery: viewQuery ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null,
|
|
||||||
token: this.getTokenMetadata(token)
|
token: this.getTokenMetadata(token)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -569,7 +558,7 @@ export class CompileMetadataResolver {
|
|||||||
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
||||||
token = resolveForwardRef(token);
|
token = resolveForwardRef(token);
|
||||||
let compileToken: cpl.CompileTokenMetadata;
|
let compileToken: cpl.CompileTokenMetadata;
|
||||||
if (isString(token)) {
|
if (typeof token === 'string') {
|
||||||
compileToken = new cpl.CompileTokenMetadata({value: token});
|
compileToken = new cpl.CompileTokenMetadata({value: token});
|
||||||
} else {
|
} else {
|
||||||
compileToken = new cpl.CompileTokenMetadata({
|
compileToken = new cpl.CompileTokenMetadata({
|
||||||
@ -606,19 +595,20 @@ export class CompileMetadataResolver {
|
|||||||
} else if (isValidType(provider)) {
|
} else if (isValidType(provider)) {
|
||||||
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
||||||
} else {
|
} else {
|
||||||
let providersInfo = (<string[]>providers.reduce(
|
const providersInfo =
|
||||||
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
(<string[]>providers.reduce(
|
||||||
if (seenProviderIdx < providerIdx) {
|
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
||||||
soFar.push(`${stringify(seenProvider)}`);
|
if (seenProviderIdx < providerIdx) {
|
||||||
} else if (seenProviderIdx == providerIdx) {
|
soFar.push(`${stringify(seenProvider)}`);
|
||||||
soFar.push(`?${stringify(seenProvider)}?`);
|
} else if (seenProviderIdx == providerIdx) {
|
||||||
} else if (seenProviderIdx == providerIdx + 1) {
|
soFar.push(`?${stringify(seenProvider)}?`);
|
||||||
soFar.push('...');
|
} else if (seenProviderIdx == providerIdx + 1) {
|
||||||
}
|
soFar.push('...');
|
||||||
return soFar;
|
}
|
||||||
},
|
return soFar;
|
||||||
[]))
|
},
|
||||||
.join(', ');
|
[]))
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
||||||
@ -736,7 +726,6 @@ function getTransitiveModules(
|
|||||||
return targetModules;
|
return targetModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
||||||
if (tree) {
|
if (tree) {
|
||||||
for (let i = 0; i < tree.length; i++) {
|
for (let i = 0; i < tree.length; i++) {
|
||||||
@ -751,6 +740,17 @@ function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dedupeArray(array: any[]): Array<any> {
|
||||||
|
if (array) {
|
||||||
|
return Array.from(new Set(array));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenAndDedupeArray(tree: any[]): Array<any> {
|
||||||
|
return dedupeArray(flattenArray(tree));
|
||||||
|
}
|
||||||
|
|
||||||
function isValidType(value: any): boolean {
|
function isValidType(value: any): boolean {
|
||||||
return cpl.isStaticSymbol(value) || (value instanceof Type);
|
return cpl.isStaticSymbol(value) || (value instanceof Type);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {ParseError, ParseSourceSpan} from '../parse_util';
|
import {ParseError, ParseSourceSpan} from '../parse_util';
|
||||||
|
|
||||||
@ -231,7 +230,7 @@ class _TreeBuilder {
|
|||||||
|
|
||||||
private _closeVoidElement(): void {
|
private _closeVoidElement(): void {
|
||||||
if (this._elementStack.length > 0) {
|
if (this._elementStack.length > 0) {
|
||||||
const el = ListWrapper.last(this._elementStack);
|
const el = this._elementStack[this._elementStack.length - 1];
|
||||||
|
|
||||||
if (this.getTagDefinition(el.name).isVoid) {
|
if (this.getTagDefinition(el.name).isVoid) {
|
||||||
this._elementStack.pop();
|
this._elementStack.pop();
|
||||||
@ -275,7 +274,7 @@ class _TreeBuilder {
|
|||||||
|
|
||||||
private _pushElement(el: html.Element) {
|
private _pushElement(el: html.Element) {
|
||||||
if (this._elementStack.length > 0) {
|
if (this._elementStack.length > 0) {
|
||||||
const parentEl = ListWrapper.last(this._elementStack);
|
const parentEl = this._elementStack[this._elementStack.length - 1];
|
||||||
if (this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
|
if (this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
|
||||||
this._elementStack.pop();
|
this._elementStack.pop();
|
||||||
}
|
}
|
||||||
@ -316,7 +315,7 @@ class _TreeBuilder {
|
|||||||
for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
|
for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
|
||||||
const el = this._elementStack[stackIndex];
|
const el = this._elementStack[stackIndex];
|
||||||
if (el.name == fullName) {
|
if (el.name == fullName) {
|
||||||
ListWrapper.splice(this._elementStack, stackIndex, this._elementStack.length - stackIndex);
|
this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +342,7 @@ class _TreeBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getParentElement(): html.Element {
|
private _getParentElement(): html.Element {
|
||||||
return this._elementStack.length > 0 ? ListWrapper.last(this._elementStack) : null;
|
return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,7 +360,7 @@ class _TreeBuilder {
|
|||||||
container = this._elementStack[i];
|
container = this._elementStack[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return {parent: ListWrapper.last(this._elementStack), container};
|
return {parent: this._elementStack[this._elementStack.length - 1], container};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addToParent(node: html.Node) {
|
private _addToParent(node: html.Node) {
|
||||||
|
@ -9,15 +9,16 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
|
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
||||||
import {isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||||
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {convertValueToOutputAst} from './output/value_util';
|
import {convertValueToOutputAst} from './output/value_util';
|
||||||
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
||||||
import {LifecycleHooks} from './private_import_core';
|
import {LifecycleHooks} from './private_import_core';
|
||||||
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
||||||
import {ProviderAst} from './template_parser/template_ast';
|
import {ProviderAst} from './template_parser/template_ast';
|
||||||
import {createDiTokenExpression} from './util';
|
|
||||||
|
|
||||||
export class ComponentFactoryDependency {
|
export class ComponentFactoryDependency {
|
||||||
constructor(
|
constructor(
|
||||||
@ -82,13 +83,15 @@ export class NgModuleCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InjectorBuilder {
|
class _InjectorBuilder implements ClassBuilder {
|
||||||
|
fields: o.ClassField[] = [];
|
||||||
|
getters: o.ClassGetter[] = [];
|
||||||
|
methods: o.ClassMethod[] = [];
|
||||||
|
ctorStmts: o.Statement[] = [];
|
||||||
private _tokens: CompileTokenMetadata[] = [];
|
private _tokens: CompileTokenMetadata[] = [];
|
||||||
private _instances = new Map<any, o.Expression>();
|
private _instances = new Map<any, o.Expression>();
|
||||||
private _fields: o.ClassField[] = [];
|
|
||||||
private _createStmts: o.Statement[] = [];
|
private _createStmts: o.Statement[] = [];
|
||||||
private _destroyStmts: o.Statement[] = [];
|
private _destroyStmts: o.Statement[] = [];
|
||||||
private _getters: o.ClassGetter[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _ngModuleMeta: CompileNgModuleMetadata,
|
private _ngModuleMeta: CompileNgModuleMetadata,
|
||||||
@ -136,26 +139,23 @@ class _InjectorBuilder {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
var ctor = new o.ClassMethod(
|
var parentArgs = [
|
||||||
null,
|
o.variable(InjectorProps.parent.name),
|
||||||
[new o.FnParam(
|
o.literalArr(
|
||||||
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))],
|
this._entryComponentFactories.map((componentFactory) => o.importExpr(componentFactory))),
|
||||||
[o.SUPER_EXPR
|
o.literalArr(this._bootstrapComponentFactories.map(
|
||||||
.callFn([
|
(componentFactory) => o.importExpr(componentFactory)))
|
||||||
o.variable(InjectorProps.parent.name),
|
];
|
||||||
o.literalArr(this._entryComponentFactories.map(
|
|
||||||
(componentFactory) => o.importExpr(componentFactory))),
|
|
||||||
o.literalArr(this._bootstrapComponentFactories.map(
|
|
||||||
(componentFactory) => o.importExpr(componentFactory)))
|
|
||||||
])
|
|
||||||
.toStmt()]);
|
|
||||||
|
|
||||||
var injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
var injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
||||||
return new o.ClassStmt(
|
return createClassStmt({
|
||||||
injClassName, o.importExpr(
|
name: injClassName,
|
||||||
resolveIdentifier(Identifiers.NgModuleInjector),
|
ctorParams: [new o.FnParam(
|
||||||
[o.importType(this._ngModuleMeta.type)]),
|
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))],
|
||||||
this._fields, this._getters, ctor, methods);
|
parent: o.importExpr(
|
||||||
|
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||||
|
parentArgs: parentArgs,
|
||||||
|
builders: [{methods}, this]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
||||||
@ -194,11 +194,11 @@ class _InjectorBuilder {
|
|||||||
type = o.DYNAMIC_TYPE;
|
type = o.DYNAMIC_TYPE;
|
||||||
}
|
}
|
||||||
if (isEager) {
|
if (isEager) {
|
||||||
this._fields.push(new o.ClassField(propName, type));
|
this.fields.push(new o.ClassField(propName, type));
|
||||||
this._createStmts.push(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
this._createStmts.push(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
||||||
} else {
|
} else {
|
||||||
var internalField = `_${propName}`;
|
var internalField = `_${propName}`;
|
||||||
this._fields.push(new o.ClassField(internalField, type));
|
this.fields.push(new o.ClassField(internalField, type));
|
||||||
// Note: Equals is important for JS so that it also checks the undefined case!
|
// Note: Equals is important for JS so that it also checks the undefined case!
|
||||||
var getterStmts = [
|
var getterStmts = [
|
||||||
new o.IfStmt(
|
new o.IfStmt(
|
||||||
@ -206,7 +206,7 @@ class _InjectorBuilder {
|
|||||||
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]),
|
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]),
|
||||||
new o.ReturnStatement(o.THIS_EXPR.prop(internalField))
|
new o.ReturnStatement(o.THIS_EXPR.prop(internalField))
|
||||||
];
|
];
|
||||||
this._getters.push(new o.ClassGetter(propName, getterStmts, type));
|
this.getters.push(new o.ClassGetter(propName, getterStmts, type));
|
||||||
}
|
}
|
||||||
return o.THIS_EXPR.prop(propName);
|
return o.THIS_EXPR.prop(propName);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ import {AnimationCompiler} from './animation/animation_compiler';
|
|||||||
import {AnimationParser} from './animation/animation_parser';
|
import {AnimationParser} from './animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
|
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||||
|
import {ListWrapper, MapWrapper} from './facade/collection';
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||||
import {CompileMetadataResolver} from './metadata_resolver';
|
import {CompileMetadataResolver} from './metadata_resolver';
|
||||||
import {NgModuleCompiler} from './ng_module_compiler';
|
import {NgModuleCompiler} from './ng_module_compiler';
|
||||||
@ -19,33 +21,91 @@ import {OutputEmitter} from './output/abstract_emitter';
|
|||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||||
import {TemplateParser} from './template_parser/template_parser';
|
import {TemplateParser} from './template_parser/template_parser';
|
||||||
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
||||||
|
|
||||||
export class SourceModule {
|
export class SourceModule {
|
||||||
constructor(public moduleUrl: string, public source: string) {}
|
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NgModulesSummary {
|
// Returns all the source files and a mapping from modules to directives
|
||||||
constructor(
|
export function analyzeNgModules(
|
||||||
public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>,
|
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||||
public ngModules: CompileNgModuleMetadata[]) {}
|
metadataResolver: CompileMetadataResolver): {
|
||||||
}
|
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
|
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
|
||||||
|
} {
|
||||||
|
const {
|
||||||
|
ngModules: programNgModules,
|
||||||
|
pipesAndDirectives: programPipesOrDirectives,
|
||||||
|
} = _extractModulesAndPipesOrDirectives(programStaticSymbols, metadataResolver);
|
||||||
|
|
||||||
export function analyzeModules(
|
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||||
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
|
||||||
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
programNgModules.forEach(modMeta => {
|
||||||
const modules: CompileNgModuleMetadata[] = [];
|
if (options.transitiveModules) {
|
||||||
|
// For every input modules add the list of transitively included modules
|
||||||
|
modMeta.transitiveModule.modules.forEach(
|
||||||
|
modMeta => { moduleMetasByRef.set(modMeta.type.reference, modMeta); });
|
||||||
|
} else {
|
||||||
|
moduleMetasByRef.set(modMeta.type.reference, modMeta);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ngModuleMetas = MapWrapper.values(moduleMetasByRef);
|
||||||
|
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||||
|
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||||
|
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||||
|
const filePaths = new Set<string>();
|
||||||
|
|
||||||
|
// Looping over all modules to construct:
|
||||||
|
// - a map from file to modules `ngModulesByFile`,
|
||||||
|
// - a map from file to directives `ngDirectivesByFile`,
|
||||||
|
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||||
|
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||||
|
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||||
|
filePaths.add(srcFileUrl);
|
||||||
|
ngModulesByFile.set(
|
||||||
|
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
||||||
|
|
||||||
ngModules.forEach((ngModule) => {
|
|
||||||
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
|
|
||||||
modules.push(ngModuleMeta);
|
|
||||||
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
|
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
|
||||||
if (dirMeta.isComponent) {
|
const fileUrl = dirMeta.type.reference.filePath;
|
||||||
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
|
filePaths.add(fileUrl);
|
||||||
}
|
ngDirectivesByFile.set(
|
||||||
|
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirMeta.type.reference));
|
||||||
|
ngModuleByPipeOrDirective.set(dirMeta.type.reference, ngModuleMeta);
|
||||||
|
});
|
||||||
|
|
||||||
|
ngModuleMeta.declaredPipes.forEach((pipeMeta: CompilePipeMetadata) => {
|
||||||
|
const fileUrl = pipeMeta.type.reference.filePath;
|
||||||
|
filePaths.add(fileUrl);
|
||||||
|
ngModuleByPipeOrDirective.set(pipeMeta.type.reference, ngModuleMeta);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return new NgModulesSummary(ngModuleByComponent, modules);
|
|
||||||
|
// Throw an error if any of the program pipe or directives is not declared by a module
|
||||||
|
const symbolsMissingModule =
|
||||||
|
programPipesOrDirectives.filter(s => !ngModuleByPipeOrDirective.has(s));
|
||||||
|
|
||||||
|
if (symbolsMissingModule.length) {
|
||||||
|
const messages = symbolsMissingModule.map(
|
||||||
|
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||||
|
throw new Error(messages.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
||||||
|
|
||||||
|
filePaths.forEach((srcUrl) => {
|
||||||
|
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||||
|
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||||
|
files.push({srcUrl, directives, ngModules});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// map directive/pipe to module
|
||||||
|
ngModuleByPipeOrDirective,
|
||||||
|
// list modules and directives for every source file
|
||||||
|
files,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OfflineCompiler {
|
export class OfflineCompiler {
|
||||||
@ -56,22 +116,32 @@ export class OfflineCompiler {
|
|||||||
private _metadataResolver: CompileMetadataResolver,
|
private _metadataResolver: CompileMetadataResolver,
|
||||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
|
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _localeId: string, private _translationFormat: string) {}
|
private _localeId: string, private _translationFormat: string) {}
|
||||||
|
|
||||||
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
|
|
||||||
return analyzeModules(ngModules, this._metadataResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this._directiveNormalizer.clearCache();
|
this._directiveNormalizer.clearCache();
|
||||||
this._metadataResolver.clearCache();
|
this._metadataResolver.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
compile(
|
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
||||||
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
|
Promise<SourceModule[]> {
|
||||||
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
const {ngModuleByPipeOrDirective, files} =
|
||||||
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
|
analyzeNgModules(staticSymbols, options, this._metadataResolver);
|
||||||
|
|
||||||
|
const sourceModules = files.map(
|
||||||
|
file => this._compileSrcFile(
|
||||||
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||||
|
|
||||||
|
return Promise.all(sourceModules)
|
||||||
|
.then((modules: SourceModule[][]) => ListWrapper.flatten(modules));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileSrcFile(
|
||||||
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
|
directives: StaticSymbol[], ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
||||||
|
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
const exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
const outputSourceModules: SourceModule[] = [];
|
const outputSourceModules: SourceModule[] = [];
|
||||||
@ -80,13 +150,21 @@ export class OfflineCompiler {
|
|||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
|
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
|
||||||
|
|
||||||
|
// compile directive wrappers
|
||||||
|
exportedVars.push(...directives.map(
|
||||||
|
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
return Promise
|
return Promise
|
||||||
.all(components.map((compType) => {
|
.all(directives.map((dirType) => {
|
||||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>compType);
|
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||||
const ngModule = ngModulesSummary.ngModuleByComponent.get(compType);
|
if (!compMeta.isComponent) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||||
if (!ngModule) {
|
if (!ngModule) {
|
||||||
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
|
throw new Error(
|
||||||
|
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
@ -99,7 +177,8 @@ export class OfflineCompiler {
|
|||||||
// compile styles
|
// compile styles
|
||||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||||
outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix));
|
outputSourceModules.push(
|
||||||
|
this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||||
});
|
});
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
@ -112,8 +191,9 @@ export class OfflineCompiler {
|
|||||||
}))
|
}))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
outputSourceModules.unshift(this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
_ngfactoryModuleUrl(moduleUrl), statements, exportedVars));
|
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||||
|
outputSourceModules.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return outputSourceModules;
|
return outputSourceModules;
|
||||||
});
|
});
|
||||||
@ -148,6 +228,15 @@ export class OfflineCompiler {
|
|||||||
return appCompileResult.ngModuleFactoryVar;
|
return appCompileResult.ngModuleFactoryVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _compileDirectiveWrapper(directiveType: StaticSymbol, targetStatements: o.Statement[]):
|
||||||
|
string {
|
||||||
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(directiveType);
|
||||||
|
const dirCompileResult = this._dirWrapperCompiler.compile(dirMeta);
|
||||||
|
|
||||||
|
targetStatements.push(...dirCompileResult.statements);
|
||||||
|
return dirCompileResult.dirWrapperClassVar;
|
||||||
|
}
|
||||||
|
|
||||||
private _compileComponentFactory(
|
private _compileComponentFactory(
|
||||||
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
||||||
targetStatements: o.Statement[]): string {
|
targetStatements: o.Statement[]): string {
|
||||||
@ -193,18 +282,21 @@ export class OfflineCompiler {
|
|||||||
return viewResult.viewFactoryVar;
|
return viewResult.viewFactoryVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codgenStyles(stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
private _codgenStyles(
|
||||||
|
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
||||||
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
||||||
return this._codegenSourceModule(
|
return this._codegenSourceModule(
|
||||||
_stylesModuleUrl(
|
fileUrl, _stylesModuleUrl(
|
||||||
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||||
stylesCompileResult.statements, [stylesCompileResult.stylesVar]);
|
stylesCompileResult.statements, [stylesCompileResult.stylesVar]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codegenSourceModule(
|
private _codegenSourceModule(
|
||||||
moduleUrl: string, statements: o.Statement[], exportedVars: string[]): SourceModule {
|
fileUrl: string, moduleUrl: string, statements: o.Statement[],
|
||||||
|
exportedVars: string[]): SourceModule {
|
||||||
return new SourceModule(
|
return new SourceModule(
|
||||||
moduleUrl, this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
fileUrl, moduleUrl,
|
||||||
|
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +309,9 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
|
|||||||
const cfd = <ComponentFactoryDependency>dep;
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
||||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
||||||
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
|
const dwd = <DirectiveWrapperDependency>dep;
|
||||||
|
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
@ -231,8 +326,8 @@ function _resolveStyleStatements(
|
|||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _ngfactoryModuleUrl(compUrl: string): string {
|
function _ngfactoryModuleUrl(dirUrl: string): string {
|
||||||
const urlWithSuffix = _splitTypescriptSuffix(compUrl);
|
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
|
||||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,3 +358,28 @@ function _splitTypescriptSuffix(path: string): string[] {
|
|||||||
|
|
||||||
return [path, ''];
|
return [path, ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group the symbols by types:
|
||||||
|
// - NgModules,
|
||||||
|
// - Pipes and Directives.
|
||||||
|
function _extractModulesAndPipesOrDirectives(
|
||||||
|
programStaticSymbols: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
||||||
|
const ngModules: CompileNgModuleMetadata[] = [];
|
||||||
|
const pipesAndDirectives: StaticSymbol[] = [];
|
||||||
|
|
||||||
|
programStaticSymbols.forEach(staticSymbol => {
|
||||||
|
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||||
|
const directive = metadataResolver.getDirectiveMetadata(staticSymbol, false);
|
||||||
|
const pipe = metadataResolver.getPipeMetadata(<any>staticSymbol, false);
|
||||||
|
|
||||||
|
if (ngModule) {
|
||||||
|
ngModules.push(ngModule);
|
||||||
|
} else if (directive) {
|
||||||
|
pipesAndDirectives.push(staticSymbol);
|
||||||
|
} else if (pipe) {
|
||||||
|
pipesAndDirectives.push(staticSymbol);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {ngModules, pipesAndDirectives};
|
||||||
|
}
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isBlank, isPresent, isString} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
|
|
||||||
var _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
||||||
var _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
|
const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
|
||||||
export var CATCH_ERROR_VAR = o.variable('error');
|
export const CATCH_ERROR_VAR = o.variable('error');
|
||||||
export var CATCH_STACK_VAR = o.variable('stack');
|
export const CATCH_STACK_VAR = o.variable('stack');
|
||||||
|
|
||||||
export abstract class OutputEmitter {
|
export abstract class OutputEmitter {
|
||||||
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
|
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
|
||||||
@ -253,7 +253,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext, absentValue: string = 'null'):
|
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext, absentValue: string = 'null'):
|
||||||
any {
|
any {
|
||||||
var value = ast.value;
|
var value = ast.value;
|
||||||
if (isString(value)) {
|
if (typeof value === 'string') {
|
||||||
ctx.print(escapeIdentifier(value, this._escapeDollarInStrings));
|
ctx.print(escapeIdentifier(value, this._escapeDollarInStrings));
|
||||||
} else if (isBlank(value)) {
|
} else if (isBlank(value)) {
|
||||||
ctx.print(absentValue);
|
ctx.print(absentValue);
|
||||||
|
60
modules/@angular/compiler/src/output/class_builder.ts
Normal file
60
modules/@angular/compiler/src/output/class_builder.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @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 o from './output_ast';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new class stmts based on the given data.
|
||||||
|
*/
|
||||||
|
export function createClassStmt(config: {
|
||||||
|
name: string,
|
||||||
|
parent?: o.Expression,
|
||||||
|
parentArgs?: o.Expression[],
|
||||||
|
ctorParams?: o.FnParam[],
|
||||||
|
builders: ClassBuilderPart | ClassBuilderPart[], modifiers?: o.StmtModifier[]
|
||||||
|
}): o.ClassStmt {
|
||||||
|
const parentArgs = config.parentArgs || [];
|
||||||
|
const superCtorStmts = config.parent ? [o.SUPER_EXPR.callFn(parentArgs).toStmt()] : [];
|
||||||
|
const builder =
|
||||||
|
concatClassBuilderParts(Array.isArray(config.builders) ? config.builders : [config.builders]);
|
||||||
|
const ctor =
|
||||||
|
new o.ClassMethod(null, config.ctorParams || [], superCtorStmts.concat(builder.ctorStmts));
|
||||||
|
|
||||||
|
return new o.ClassStmt(
|
||||||
|
config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods,
|
||||||
|
config.modifiers || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function concatClassBuilderParts(builders: ClassBuilderPart[]) {
|
||||||
|
return {
|
||||||
|
fields: [].concat(...builders.map(builder => builder.fields || [])),
|
||||||
|
methods: [].concat(...builders.map(builder => builder.methods || [])),
|
||||||
|
getters: [].concat(...builders.map(builder => builder.getters || [])),
|
||||||
|
ctorStmts: [].concat(...builders.map(builder => builder.ctorStmts || [])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects data for a generated class.
|
||||||
|
*/
|
||||||
|
export interface ClassBuilderPart {
|
||||||
|
fields?: o.ClassField[];
|
||||||
|
methods?: o.ClassMethod[];
|
||||||
|
getters?: o.ClassGetter[];
|
||||||
|
ctorStmts?: o.Statement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects data for a generated class.
|
||||||
|
*/
|
||||||
|
export interface ClassBuilder {
|
||||||
|
fields: o.ClassField[];
|
||||||
|
methods: o.ClassMethod[];
|
||||||
|
getters: o.ClassGetter[];
|
||||||
|
ctorStmts: o.Statement[];
|
||||||
|
}
|
@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
import {isPresent, isString} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//// Types
|
//// Types
|
||||||
export enum TypeModifier {
|
export enum TypeModifier {
|
||||||
@ -196,7 +194,7 @@ export class ReadVarExpr extends Expression {
|
|||||||
|
|
||||||
constructor(name: string|BuiltinVar, type: Type = null) {
|
constructor(name: string|BuiltinVar, type: Type = null) {
|
||||||
super(type);
|
super(type);
|
||||||
if (isString(name)) {
|
if (typeof name === 'string') {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.builtin = null;
|
this.builtin = null;
|
||||||
} else {
|
} else {
|
||||||
@ -267,7 +265,7 @@ export class InvokeMethodExpr extends Expression {
|
|||||||
public receiver: Expression, method: string|BuiltinMethod, public args: Expression[],
|
public receiver: Expression, method: string|BuiltinMethod, public args: Expression[],
|
||||||
type: Type = null) {
|
type: Type = null) {
|
||||||
super(type);
|
super(type);
|
||||||
if (isString(method)) {
|
if (typeof method === 'string') {
|
||||||
this.name = method;
|
this.name = method;
|
||||||
this.builtin = null;
|
this.builtin = null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
@ -81,7 +80,7 @@ function createDynamicClass(
|
|||||||
_executeFunctionStatements(
|
_executeFunctionStatements(
|
||||||
ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor);
|
ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor);
|
||||||
};
|
};
|
||||||
var superClass = _classStmt.parent.visitExpression(_visitor, _ctx);
|
var superClass = _classStmt.parent ? _classStmt.parent.visitExpression(_visitor, _ctx) : Object;
|
||||||
ctor.prototype = Object.create(superClass.prototype, propertyDescriptors);
|
ctor.prototype = Object.create(superClass.prototype, propertyDescriptors);
|
||||||
return ctor;
|
return ctor;
|
||||||
}
|
}
|
||||||
@ -153,13 +152,13 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
|
|||||||
if (isPresent(expr.builtin)) {
|
if (isPresent(expr.builtin)) {
|
||||||
switch (expr.builtin) {
|
switch (expr.builtin) {
|
||||||
case o.BuiltinMethod.ConcatArray:
|
case o.BuiltinMethod.ConcatArray:
|
||||||
result = ListWrapper.concat(receiver, args[0]);
|
result = receiver.concat(...args);
|
||||||
break;
|
break;
|
||||||
case o.BuiltinMethod.SubscribeObservable:
|
case o.BuiltinMethod.SubscribeObservable:
|
||||||
result = receiver.subscribe({next: args[0]});
|
result = receiver.subscribe({next: args[0]});
|
||||||
break;
|
break;
|
||||||
case o.BuiltinMethod.Bind:
|
case o.BuiltinMethod.Bind:
|
||||||
result = receiver.bind(args[0]);
|
result = receiver.bind(...args);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown builtin method ${expr.builtin}`);
|
throw new Error(`Unknown builtin method ${expr.builtin}`);
|
||||||
|
@ -6,13 +6,26 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {evalExpression, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {sanitizeIdentifier} from '../util';
|
import {sanitizeIdentifier} from '../util';
|
||||||
|
|
||||||
import {EmitterVisitorContext} from './abstract_emitter';
|
import {EmitterVisitorContext} from './abstract_emitter';
|
||||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
|
|
||||||
|
function evalExpression(
|
||||||
|
sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any {
|
||||||
|
const fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
|
||||||
|
const fnArgNames: string[] = [];
|
||||||
|
const fnArgValues: any[] = [];
|
||||||
|
for (const argName in vars) {
|
||||||
|
fnArgNames.push(argName);
|
||||||
|
fnArgValues.push(vars[argName]);
|
||||||
|
}
|
||||||
|
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function jitStatements(
|
export function jitStatements(
|
||||||
sourceUrl: string, statements: o.Statement[], resultVar: string): any {
|
sourceUrl: string, statements: o.Statement[], resultVar: string): any {
|
||||||
var converter = new JitEmitterVisitor();
|
var converter = new JitEmitterVisitor();
|
||||||
|
@ -8,24 +8,20 @@
|
|||||||
|
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
import {isArray, isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
import {ImportGenerator} from './path_util';
|
import {ImportGenerator} from './path_util';
|
||||||
|
|
||||||
var _debugModuleUrl = 'asset://debug/lib';
|
const _debugModuleUrl = 'asset://debug/lib';
|
||||||
|
|
||||||
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
|
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
|
||||||
string {
|
string {
|
||||||
var converter = new _TsEmitterVisitor(_debugModuleUrl);
|
const converter = new _TsEmitterVisitor(_debugModuleUrl);
|
||||||
var ctx = EmitterVisitorContext.createRoot([]);
|
const ctx = EmitterVisitorContext.createRoot([]);
|
||||||
var asts: any[];
|
const asts: any[] = Array.isArray(ast) ? ast : [ast];
|
||||||
if (isArray(ast)) {
|
|
||||||
asts = <any[]>ast;
|
|
||||||
} else {
|
|
||||||
asts = [ast];
|
|
||||||
}
|
|
||||||
asts.forEach((ast) => {
|
asts.forEach((ast) => {
|
||||||
if (ast instanceof o.Statement) {
|
if (ast instanceof o.Statement) {
|
||||||
ast.visitStatement(converter, ctx);
|
ast.visitStatement(converter, ctx);
|
||||||
|
@ -35,7 +35,7 @@ export enum ParseErrorLevel {
|
|||||||
FATAL
|
FATAL
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ParseError {
|
export class ParseError {
|
||||||
constructor(
|
constructor(
|
||||||
public span: ParseSourceSpan, public msg: string,
|
public span: ParseSourceSpan, public msg: string,
|
||||||
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
||||||
|
@ -1,112 +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 directive_normalizer from './directive_normalizer';
|
|
||||||
import * as lexer from './expression_parser/lexer';
|
|
||||||
import * as parser from './expression_parser/parser';
|
|
||||||
import * as metadata_resolver from './metadata_resolver';
|
|
||||||
import * as html_parser from './ml_parser/html_parser';
|
|
||||||
import * as interpolation_config from './ml_parser/interpolation_config';
|
|
||||||
import * as ng_module_compiler from './ng_module_compiler';
|
|
||||||
import * as path_util from './output/path_util';
|
|
||||||
import * as ts_emitter from './output/ts_emitter';
|
|
||||||
import * as parse_util from './parse_util';
|
|
||||||
import * as dom_element_schema_registry from './schema/dom_element_schema_registry';
|
|
||||||
import * as selector from './selector';
|
|
||||||
import * as style_compiler from './style_compiler';
|
|
||||||
import * as template_parser from './template_parser/template_parser';
|
|
||||||
import * as view_compiler from './view_compiler/view_compiler';
|
|
||||||
|
|
||||||
export const __compiler_private__: {
|
|
||||||
_SelectorMatcher?: selector.SelectorMatcher; SelectorMatcher: typeof selector.SelectorMatcher;
|
|
||||||
|
|
||||||
_CssSelector?: selector.CssSelector;
|
|
||||||
CssSelector: typeof selector.CssSelector;
|
|
||||||
|
|
||||||
_AssetUrl?: path_util.AssetUrl;
|
|
||||||
AssetUrl: typeof path_util.AssetUrl;
|
|
||||||
|
|
||||||
_ImportGenerator?: path_util.ImportGenerator;
|
|
||||||
ImportGenerator: typeof path_util.ImportGenerator;
|
|
||||||
|
|
||||||
_CompileMetadataResolver?: metadata_resolver.CompileMetadataResolver;
|
|
||||||
CompileMetadataResolver: typeof metadata_resolver.CompileMetadataResolver;
|
|
||||||
|
|
||||||
_HtmlParser?: html_parser.HtmlParser;
|
|
||||||
HtmlParser: typeof html_parser.HtmlParser;
|
|
||||||
|
|
||||||
_InterpolationConfig?: interpolation_config.InterpolationConfig;
|
|
||||||
InterpolationConfig: typeof interpolation_config.InterpolationConfig;
|
|
||||||
|
|
||||||
_DirectiveNormalizer?: directive_normalizer.DirectiveNormalizer;
|
|
||||||
DirectiveNormalizer: typeof directive_normalizer.DirectiveNormalizer;
|
|
||||||
|
|
||||||
_Lexer?: lexer.Lexer;
|
|
||||||
Lexer: typeof lexer.Lexer;
|
|
||||||
|
|
||||||
_Parser?: parser.Parser;
|
|
||||||
Parser: typeof parser.Parser;
|
|
||||||
|
|
||||||
_ParseLocation?: parse_util.ParseLocation;
|
|
||||||
ParseLocation: typeof parse_util.ParseLocation;
|
|
||||||
|
|
||||||
_ParseError?: parse_util.ParseError;
|
|
||||||
ParseError: typeof parse_util.ParseError;
|
|
||||||
|
|
||||||
_ParseErrorLevel?: parse_util.ParseErrorLevel;
|
|
||||||
ParseErrorLevel: typeof parse_util.ParseErrorLevel;
|
|
||||||
|
|
||||||
_ParseSourceFile?: parse_util.ParseSourceFile;
|
|
||||||
ParseSourceFile: typeof parse_util.ParseSourceFile;
|
|
||||||
|
|
||||||
_ParseSourceSpan?: parse_util.ParseSourceSpan;
|
|
||||||
ParseSourceSpan: typeof parse_util.ParseSourceSpan;
|
|
||||||
|
|
||||||
_TemplateParser?: template_parser.TemplateParser;
|
|
||||||
TemplateParser: typeof template_parser.TemplateParser;
|
|
||||||
|
|
||||||
_TemplateParseResult?: template_parser.TemplateParseResult;
|
|
||||||
|
|
||||||
_DomElementSchemaRegistry?: dom_element_schema_registry.DomElementSchemaRegistry;
|
|
||||||
DomElementSchemaRegistry: typeof dom_element_schema_registry.DomElementSchemaRegistry;
|
|
||||||
|
|
||||||
_StyleCompiler?: style_compiler.StyleCompiler;
|
|
||||||
StyleCompiler: typeof style_compiler.StyleCompiler;
|
|
||||||
|
|
||||||
_ViewCompiler?: view_compiler.ViewCompiler;
|
|
||||||
ViewCompiler: typeof view_compiler.ViewCompiler;
|
|
||||||
|
|
||||||
_NgModuleCompiler?: ng_module_compiler.NgModuleCompiler;
|
|
||||||
NgModuleCompiler: typeof ng_module_compiler.NgModuleCompiler;
|
|
||||||
|
|
||||||
_TypeScriptEmitter?: ts_emitter.TypeScriptEmitter;
|
|
||||||
TypeScriptEmitter: typeof ts_emitter.TypeScriptEmitter;
|
|
||||||
|
|
||||||
} = {
|
|
||||||
SelectorMatcher: selector.SelectorMatcher,
|
|
||||||
CssSelector: selector.CssSelector,
|
|
||||||
AssetUrl: path_util.AssetUrl,
|
|
||||||
ImportGenerator: path_util.ImportGenerator,
|
|
||||||
CompileMetadataResolver: metadata_resolver.CompileMetadataResolver,
|
|
||||||
HtmlParser: html_parser.HtmlParser,
|
|
||||||
InterpolationConfig: interpolation_config.InterpolationConfig,
|
|
||||||
DirectiveNormalizer: directive_normalizer.DirectiveNormalizer,
|
|
||||||
Lexer: lexer.Lexer,
|
|
||||||
Parser: parser.Parser,
|
|
||||||
ParseLocation: parse_util.ParseLocation,
|
|
||||||
ParseError: parse_util.ParseError,
|
|
||||||
ParseErrorLevel: parse_util.ParseErrorLevel,
|
|
||||||
ParseSourceFile: parse_util.ParseSourceFile,
|
|
||||||
ParseSourceSpan: parse_util.ParseSourceSpan,
|
|
||||||
TemplateParser: template_parser.TemplateParser,
|
|
||||||
DomElementSchemaRegistry: dom_element_schema_registry.DomElementSchemaRegistry,
|
|
||||||
StyleCompiler: style_compiler.StyleCompiler,
|
|
||||||
ViewCompiler: view_compiler.ViewCompiler,
|
|
||||||
NgModuleCompiler: ng_module_compiler.NgModuleCompiler,
|
|
||||||
TypeScriptEmitter: ts_emitter.TypeScriptEmitter
|
|
||||||
};
|
|
@ -27,13 +27,7 @@ export const NgModuleInjector: typeof r.NgModuleInjector = r.NgModuleInjector;
|
|||||||
export const registerModuleFactory: typeof r.registerModuleFactory = r.registerModuleFactory;
|
export const registerModuleFactory: typeof r.registerModuleFactory = r.registerModuleFactory;
|
||||||
export type ViewType = typeof r._ViewType;
|
export type ViewType = typeof r._ViewType;
|
||||||
export const ViewType: typeof r.ViewType = r.ViewType;
|
export const ViewType: typeof r.ViewType = r.ViewType;
|
||||||
export const MAX_INTERPOLATION_VALUES: typeof r.MAX_INTERPOLATION_VALUES =
|
export const view_utils: typeof r.view_utils = r.view_utils;
|
||||||
r.MAX_INTERPOLATION_VALUES;
|
|
||||||
export const checkBinding: typeof r.checkBinding = r.checkBinding;
|
|
||||||
export const flattenNestedViewRenderNodes: typeof r.flattenNestedViewRenderNodes =
|
|
||||||
r.flattenNestedViewRenderNodes;
|
|
||||||
export const interpolate: typeof r.interpolate = r.interpolate;
|
|
||||||
export const ViewUtils: typeof r.ViewUtils = r.ViewUtils;
|
|
||||||
export const DebugContext: typeof r.DebugContext = r.DebugContext;
|
export const DebugContext: typeof r.DebugContext = r.DebugContext;
|
||||||
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
|
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
|
||||||
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
|
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
|
||||||
@ -42,19 +36,6 @@ export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
|
|||||||
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
|
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
|
||||||
export type RenderDebugInfo = typeof r._RenderDebugInfo;
|
export type RenderDebugInfo = typeof r._RenderDebugInfo;
|
||||||
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
|
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
|
||||||
export const EMPTY_ARRAY: typeof r.EMPTY_ARRAY = r.EMPTY_ARRAY;
|
|
||||||
export const EMPTY_MAP: typeof r.EMPTY_MAP = r.EMPTY_MAP;
|
|
||||||
export const pureProxy1: typeof r.pureProxy1 = r.pureProxy1;
|
|
||||||
export const pureProxy2: typeof r.pureProxy2 = r.pureProxy2;
|
|
||||||
export const pureProxy3: typeof r.pureProxy3 = r.pureProxy3;
|
|
||||||
export const pureProxy4: typeof r.pureProxy4 = r.pureProxy4;
|
|
||||||
export const pureProxy5: typeof r.pureProxy5 = r.pureProxy5;
|
|
||||||
export const pureProxy6: typeof r.pureProxy6 = r.pureProxy6;
|
|
||||||
export const pureProxy7: typeof r.pureProxy7 = r.pureProxy7;
|
|
||||||
export const pureProxy8: typeof r.pureProxy8 = r.pureProxy8;
|
|
||||||
export const pureProxy9: typeof r.pureProxy9 = r.pureProxy9;
|
|
||||||
export const pureProxy10: typeof r.pureProxy10 = r.pureProxy10;
|
|
||||||
export const castByValue: typeof r.castByValue = r.castByValue;
|
|
||||||
export type Console = typeof r._Console;
|
export type Console = typeof r._Console;
|
||||||
export const Console: typeof r.Console = r.Console;
|
export const Console: typeof r.Console = r.Console;
|
||||||
export const reflector: typeof r.reflector = r.reflector;
|
export const reflector: typeof r.reflector = r.reflector;
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {ListWrapper, MapWrapper} from './facade/collection';
|
import {MapWrapper} from './facade/collection';
|
||||||
import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||||
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
||||||
@ -91,9 +91,9 @@ export class ProviderElementContext {
|
|||||||
|
|
||||||
get transformedDirectiveAsts(): DirectiveAst[] {
|
get transformedDirectiveAsts(): DirectiveAst[] {
|
||||||
var sortedProviderTypes = this.transformProviders.map(provider => provider.token.identifier);
|
var sortedProviderTypes = this.transformProviders.map(provider => provider.token.identifier);
|
||||||
var sortedDirectives = ListWrapper.clone(this._directiveAsts);
|
var sortedDirectives = this._directiveAsts.slice();
|
||||||
ListWrapper.sort(
|
sortedDirectives.sort(
|
||||||
sortedDirectives, (dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
|
(dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
|
||||||
sortedProviderTypes.indexOf(dir2.directive.type));
|
sortedProviderTypes.indexOf(dir2.directive.type));
|
||||||
return sortedDirectives;
|
return sortedDirectives;
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ export class ProviderElementContext {
|
|||||||
while (currentEl !== null) {
|
while (currentEl !== null) {
|
||||||
queries = currentEl._contentQueries.get(token.reference);
|
queries = currentEl._contentQueries.get(token.reference);
|
||||||
if (isPresent(queries)) {
|
if (isPresent(queries)) {
|
||||||
ListWrapper.addAll(result, queries.filter((query) => query.descendants || distance <= 1));
|
result.push(...queries.filter((query) => query.descendants || distance <= 1));
|
||||||
}
|
}
|
||||||
if (currentEl._directiveAsts.length > 0) {
|
if (currentEl._directiveAsts.length > 0) {
|
||||||
distance++;
|
distance++;
|
||||||
@ -126,7 +126,7 @@ export class ProviderElementContext {
|
|||||||
}
|
}
|
||||||
queries = this.viewContext.viewQueries.get(token.reference);
|
queries = this.viewContext.viewQueries.get(token.reference);
|
||||||
if (isPresent(queries)) {
|
if (isPresent(queries)) {
|
||||||
ListWrapper.addAll(result, queries);
|
result.push(...queries);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -194,10 +194,8 @@ export class ProviderElementContext {
|
|||||||
eager: boolean = null): CompileDiDependencyMetadata {
|
eager: boolean = null): CompileDiDependencyMetadata {
|
||||||
if (dep.isAttribute) {
|
if (dep.isAttribute) {
|
||||||
var attrValue = this._attrs[dep.token.value];
|
var attrValue = this._attrs[dep.token.value];
|
||||||
return new CompileDiDependencyMetadata({isValue: true, value: normalizeBlank(attrValue)});
|
return new CompileDiDependencyMetadata(
|
||||||
}
|
{isValue: true, value: attrValue == null ? null : attrValue});
|
||||||
if (isPresent(dep.query) || isPresent(dep.viewQuery)) {
|
|
||||||
return dep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(dep.token)) {
|
if (isPresent(dep.token)) {
|
||||||
@ -418,7 +416,7 @@ function _normalizeProviders(
|
|||||||
}
|
}
|
||||||
if (isPresent(providers)) {
|
if (isPresent(providers)) {
|
||||||
providers.forEach((provider) => {
|
providers.forEach((provider) => {
|
||||||
if (isArray(provider)) {
|
if (Array.isArray(provider)) {
|
||||||
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
|
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
|
||||||
} else {
|
} else {
|
||||||
let normalizeProvider: CompileProviderMetadata;
|
let normalizeProvider: CompileProviderMetadata;
|
||||||
@ -489,7 +487,7 @@ function _resolveProviders(
|
|||||||
targetProvidersByToken.set(provider.token.reference, resolvedProvider);
|
targetProvidersByToken.set(provider.token.reference, resolvedProvider);
|
||||||
} else {
|
} else {
|
||||||
if (!provider.multi) {
|
if (!provider.multi) {
|
||||||
ListWrapper.clear(resolvedProvider.providers);
|
resolvedProvider.providers.length = 0;
|
||||||
}
|
}
|
||||||
resolvedProvider.providers.push(provider);
|
resolvedProvider.providers.push(provider);
|
||||||
}
|
}
|
||||||
@ -502,11 +500,6 @@ function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQ
|
|||||||
if (isPresent(component.viewQueries)) {
|
if (isPresent(component.viewQueries)) {
|
||||||
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
|
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
|
||||||
}
|
}
|
||||||
component.type.diDeps.forEach((dep) => {
|
|
||||||
if (isPresent(dep.viewQuery)) {
|
|
||||||
_addQueryToTokenMap(viewQueries, dep.viewQuery);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return viewQueries;
|
return viewQueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,11 +510,6 @@ function _getContentQueries(directives: CompileDirectiveMetadata[]):
|
|||||||
if (isPresent(directive.queries)) {
|
if (isPresent(directive.queries)) {
|
||||||
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
|
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
|
||||||
}
|
}
|
||||||
directive.type.diDeps.forEach((dep) => {
|
|
||||||
if (isPresent(dep.query)) {
|
|
||||||
_addQueryToTokenMap(contentQueries, dep.query);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return contentQueries;
|
return contentQueries;
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationCompiler} from './animation/animation_compiler';
|
import {AnimationCompiler} from './animation/animation_compiler';
|
||||||
import {AnimationParser} from './animation/animation_parser';
|
import {AnimationParser} from './animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
|
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||||
import {stringify} from './facade/lang';
|
import {stringify} from './facade/lang';
|
||||||
import {CompileMetadataResolver} from './metadata_resolver';
|
import {CompileMetadataResolver} from './metadata_resolver';
|
||||||
import {NgModuleCompiler} from './ng_module_compiler';
|
import {NgModuleCompiler} from './ng_module_compiler';
|
||||||
@ -22,7 +24,8 @@ import {ComponentStillLoadingError} from './private_import_core';
|
|||||||
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||||
import {TemplateParser} from './template_parser/template_parser';
|
import {TemplateParser} from './template_parser/template_parser';
|
||||||
import {SyncAsyncResult} from './util';
|
import {SyncAsyncResult} from './util';
|
||||||
import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An internal module of the Angular compiler that begins with component types,
|
* An internal module of the Angular compiler that begins with component types,
|
||||||
@ -37,6 +40,7 @@ import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from '.
|
|||||||
export class RuntimeCompiler implements Compiler {
|
export class RuntimeCompiler implements Compiler {
|
||||||
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||||
|
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
|
||||||
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
||||||
private _animationParser = new AnimationParser();
|
private _animationParser = new AnimationParser();
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
@ -45,7 +49,9 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
||||||
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig) {}
|
private _ngModuleCompiler: NgModuleCompiler,
|
||||||
|
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
|
private _compilerConfig: CompilerConfig) {}
|
||||||
|
|
||||||
get injector(): Injector { return this._injector; }
|
get injector(): Injector { return this._injector; }
|
||||||
|
|
||||||
@ -80,10 +86,11 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
|
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
|
||||||
const componentFactories: ComponentFactory<any>[] = [];
|
const componentFactories: ComponentFactory<any>[] = [];
|
||||||
const templates = new Set<CompiledTemplate>();
|
const templates = new Set<CompiledTemplate>();
|
||||||
moduleMeta.transitiveModule.modules.forEach((moduleMeta) => {
|
moduleMeta.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
moduleMeta.declaredDirectives.forEach((dirMeta) => {
|
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
const template = this._createCompiledHostTemplate(dirMeta.type.reference);
|
const template =
|
||||||
|
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
|
||||||
templates.add(template);
|
templates.add(template);
|
||||||
componentFactories.push(template.proxyComponentFactory);
|
componentFactories.push(template.proxyComponentFactory);
|
||||||
}
|
}
|
||||||
@ -119,7 +126,7 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
||||||
} else {
|
} else {
|
||||||
ngModuleFactory = jitStatements(
|
ngModuleFactory = jitStatements(
|
||||||
`${moduleMeta.type.name}.ngfactory.js`, compileResult.statements,
|
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
|
||||||
compileResult.ngModuleFactoryVar);
|
compileResult.ngModuleFactoryVar);
|
||||||
}
|
}
|
||||||
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
||||||
@ -135,22 +142,32 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
var loadingPromises: Promise<any>[] = [];
|
var loadingPromises: Promise<any>[] = [];
|
||||||
|
|
||||||
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
||||||
|
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
|
||||||
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
|
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
||||||
|
moduleByDirective.set(dirMeta.type.reference, localModuleMeta);
|
||||||
|
this._compileDirectiveWrapper(dirMeta, localModuleMeta);
|
||||||
|
if (dirMeta.isComponent) {
|
||||||
|
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
|
||||||
dirMeta.entryComponents.forEach((entryComponentType) => {
|
dirMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
|
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
||||||
|
templates.add(
|
||||||
|
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
||||||
});
|
});
|
||||||
// TODO: what about entryComponents of entryComponents? maybe skip here and just do the
|
|
||||||
// below?
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
|
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
||||||
// TODO: what about entryComponents of entryComponents?
|
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
templates.forEach((template) => {
|
templates.forEach((template) => {
|
||||||
if (template.loading) {
|
if (template.loading) {
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
@ -189,14 +206,19 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
this._compiledNgModuleCache.clear();
|
this._compiledNgModuleCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate {
|
private _createCompiledHostTemplate(compType: Type<any>, ngModule: CompileNgModuleMetadata):
|
||||||
|
CompiledTemplate {
|
||||||
|
if (!ngModule) {
|
||||||
|
throw new Error(
|
||||||
|
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
|
||||||
|
}
|
||||||
var compiledTemplate = this._compiledHostTemplateCache.get(compType);
|
var compiledTemplate = this._compiledHostTemplateCache.get(compType);
|
||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
var hostMeta = createHostComponentMeta(compMeta);
|
var hostMeta = createHostComponentMeta(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
true, compMeta.selector, compMeta.type, [compMeta], [], [],
|
true, compMeta.selector, compMeta.type, ngModule, [compMeta],
|
||||||
this._templateNormalizer.normalizeDirective(hostMeta));
|
this._templateNormalizer.normalizeDirective(hostMeta));
|
||||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||||
}
|
}
|
||||||
@ -209,8 +231,7 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives,
|
false, compMeta.selector, compMeta.type, ngModule, ngModule.transitiveModule.directives,
|
||||||
ngModule.transitiveModule.pipes, ngModule.schemas,
|
|
||||||
this._templateNormalizer.normalizeDirective(compMeta));
|
this._templateNormalizer.normalizeDirective(compMeta));
|
||||||
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
||||||
}
|
}
|
||||||
@ -221,13 +242,8 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
|
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
|
||||||
this._compiledTemplateCache.get(compType);
|
this._compiledTemplateCache.get(compType);
|
||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
if (isHost) {
|
throw new Error(
|
||||||
throw new Error(
|
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
|
||||||
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
@ -241,6 +257,30 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _assertDirectiveWrapper(dirType: any): Type<any> {
|
||||||
|
const dirWrapper = this._compiledDirectiveWrapperCache.get(dirType);
|
||||||
|
if (!dirWrapper) {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: Directive wrapper for ${stringify(dirType)} has not been compiled!`);
|
||||||
|
}
|
||||||
|
return dirWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileDirectiveWrapper(
|
||||||
|
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
|
||||||
|
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
|
||||||
|
const statements = compileResult.statements;
|
||||||
|
let directiveWrapperClass: any;
|
||||||
|
if (!this._compilerConfig.useJit) {
|
||||||
|
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
|
||||||
|
} else {
|
||||||
|
directiveWrapperClass = jitStatements(
|
||||||
|
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
|
||||||
|
compileResult.dirWrapperClassVar);
|
||||||
|
}
|
||||||
|
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
||||||
|
}
|
||||||
|
|
||||||
private _compileTemplate(template: CompiledTemplate) {
|
private _compileTemplate(template: CompiledTemplate) {
|
||||||
if (template.isCompiled) {
|
if (template.isCompiled) {
|
||||||
return;
|
return;
|
||||||
@ -275,6 +315,9 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
depTemplate = this._assertComponentLoaded(cfd.comp.reference, true);
|
depTemplate = this._assertComponentLoaded(cfd.comp.reference, true);
|
||||||
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||||
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
||||||
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
|
let dwd = <DirectiveWrapperDependency>dep;
|
||||||
|
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const statements =
|
const statements =
|
||||||
@ -286,8 +329,8 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
factory = interpretStatements(statements, compileResult.viewFactoryVar);
|
factory = interpretStatements(statements, compileResult.viewFactoryVar);
|
||||||
} else {
|
} else {
|
||||||
factory = jitStatements(
|
factory = jitStatements(
|
||||||
`${template.compType.name}${template.isHost?'_Host':''}.ngfactory.js`, statements,
|
`/${template.ngModule.type.name}/${template.compType.name}/${template.isHost?'host':'component'}.ngfactory.js`,
|
||||||
compileResult.viewFactoryVar);
|
statements, compileResult.viewFactoryVar);
|
||||||
}
|
}
|
||||||
template.compiled(factory);
|
template.compiled(factory);
|
||||||
}
|
}
|
||||||
@ -310,7 +353,7 @@ export class RuntimeCompiler implements Compiler {
|
|||||||
if (!this._compilerConfig.useJit) {
|
if (!this._compilerConfig.useJit) {
|
||||||
return interpretStatements(result.statements, result.stylesVar);
|
return interpretStatements(result.statements, result.stylesVar);
|
||||||
} else {
|
} else {
|
||||||
return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
|
return jitStatements(`/${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,13 +368,17 @@ class CompiledTemplate {
|
|||||||
isCompiledWithDeps = false;
|
isCompiledWithDeps = false;
|
||||||
viewComponentTypes: Type<any>[] = [];
|
viewComponentTypes: Type<any>[] = [];
|
||||||
viewDirectives: CompileDirectiveMetadata[] = [];
|
viewDirectives: CompileDirectiveMetadata[] = [];
|
||||||
|
viewPipes: CompilePipeMetadata[];
|
||||||
|
schemas: SchemaMetadata[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
||||||
viewDirectivesAndComponents: CompileDirectiveMetadata[],
|
public ngModule: CompileNgModuleMetadata,
|
||||||
public viewPipes: CompilePipeMetadata[], public schemas: SchemaMetadata[],
|
viewDirectiveAndComponents: CompileDirectiveMetadata[],
|
||||||
_normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
|
_normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
|
||||||
viewDirectivesAndComponents.forEach((dirMeta) => {
|
this.viewPipes = ngModule.transitiveModule.pipes;
|
||||||
|
this.schemas = ngModule.schemas;
|
||||||
|
viewDirectiveAndComponents.forEach((dirMeta) => {
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
this.viewComponentTypes.push(dirMeta.type.reference);
|
this.viewComponentTypes.push(dirMeta.type.reference);
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,7 +328,12 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
|||||||
* 'NONE' security context, i.e. that they are safe inert string values. Only specific well known
|
* 'NONE' security context, i.e. that they are safe inert string values. Only specific well known
|
||||||
* attack vectors are assigned their appropriate context.
|
* attack vectors are assigned their appropriate context.
|
||||||
*/
|
*/
|
||||||
securityContext(tagName: string, propName: string): SecurityContext {
|
securityContext(tagName: string, propName: string, isAttribute: boolean): SecurityContext {
|
||||||
|
if (isAttribute) {
|
||||||
|
// NB: For security purposes, use the mapped property name, not the attribute name.
|
||||||
|
propName = this.getMappedPropName(propName);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure comparisons are case insensitive, so that case differences between attribute and
|
// Make sure comparisons are case insensitive, so that case differences between attribute and
|
||||||
// property names do not have a security impact.
|
// property names do not have a security impact.
|
||||||
tagName = tagName.toLowerCase();
|
tagName = tagName.toLowerCase();
|
||||||
@ -366,4 +371,6 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
|||||||
return {error: false};
|
return {error: false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allKnownElementNames(): string[] { return Object.keys(this._schema); }
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SchemaMetadata} from '@angular/core';
|
import {SchemaMetadata, SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
export abstract class ElementSchemaRegistry {
|
export abstract class ElementSchemaRegistry {
|
||||||
abstract hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;
|
abstract hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;
|
||||||
abstract hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean;
|
abstract hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean;
|
||||||
abstract securityContext(tagName: string, propName: string): any;
|
abstract securityContext(elementName: string, propName: string, isAttribute: boolean):
|
||||||
|
SecurityContext;
|
||||||
|
abstract allKnownElementNames(): string[];
|
||||||
abstract getMappedPropName(propName: string): string;
|
abstract getMappedPropName(propName: string): string;
|
||||||
abstract getDefaultComponentElementName(): string;
|
abstract getDefaultComponentElementName(): string;
|
||||||
abstract validateProperty(name: string): {error: boolean, msg?: string};
|
abstract validateProperty(name: string): {error: boolean, msg?: string};
|
||||||
|
@ -135,12 +135,12 @@ export class SelectorMatcher {
|
|||||||
return notMatcher;
|
return notMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _elementMap: {[k: string]: SelectorContext[]} = {};
|
private _elementMap = new Map<string, SelectorContext[]>();
|
||||||
private _elementPartialMap: {[k: string]: SelectorMatcher} = {};
|
private _elementPartialMap = new Map<string, SelectorMatcher>();
|
||||||
private _classMap: {[k: string]: SelectorContext[]} = {};
|
private _classMap = new Map<string, SelectorContext[]>();
|
||||||
private _classPartialMap: {[k: string]: SelectorMatcher} = {};
|
private _classPartialMap = new Map<string, SelectorMatcher>();
|
||||||
private _attrValueMap: {[k: string]: {[k: string]: SelectorContext[]}} = {};
|
private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>();
|
||||||
private _attrValuePartialMap: {[k: string]: {[k: string]: SelectorMatcher}} = {};
|
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>();
|
||||||
private _listContexts: SelectorListContext[] = [];
|
private _listContexts: SelectorListContext[] = [];
|
||||||
|
|
||||||
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
|
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
|
||||||
@ -195,18 +195,18 @@ export class SelectorMatcher {
|
|||||||
const value = attrs[i + 1];
|
const value = attrs[i + 1];
|
||||||
if (isTerminal) {
|
if (isTerminal) {
|
||||||
const terminalMap = matcher._attrValueMap;
|
const terminalMap = matcher._attrValueMap;
|
||||||
let terminalValuesMap = terminalMap[name];
|
let terminalValuesMap = terminalMap.get(name);
|
||||||
if (!terminalValuesMap) {
|
if (!terminalValuesMap) {
|
||||||
terminalValuesMap = {};
|
terminalValuesMap = new Map<string, SelectorContext[]>();
|
||||||
terminalMap[name] = terminalValuesMap;
|
terminalMap.set(name, terminalValuesMap);
|
||||||
}
|
}
|
||||||
this._addTerminal(terminalValuesMap, value, selectable);
|
this._addTerminal(terminalValuesMap, value, selectable);
|
||||||
} else {
|
} else {
|
||||||
let partialMap = matcher._attrValuePartialMap;
|
let partialMap = matcher._attrValuePartialMap;
|
||||||
let partialValuesMap = partialMap[name];
|
let partialValuesMap = partialMap.get(name);
|
||||||
if (!partialValuesMap) {
|
if (!partialValuesMap) {
|
||||||
partialValuesMap = {};
|
partialValuesMap = new Map<string, SelectorMatcher>();
|
||||||
partialMap[name] = partialValuesMap;
|
partialMap.set(name, partialValuesMap);
|
||||||
}
|
}
|
||||||
matcher = this._addPartial(partialValuesMap, value);
|
matcher = this._addPartial(partialValuesMap, value);
|
||||||
}
|
}
|
||||||
@ -215,20 +215,20 @@ export class SelectorMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addTerminal(
|
private _addTerminal(
|
||||||
map: {[k: string]: SelectorContext[]}, name: string, selectable: SelectorContext) {
|
map: Map<string, SelectorContext[]>, name: string, selectable: SelectorContext) {
|
||||||
let terminalList = map[name];
|
let terminalList = map.get(name);
|
||||||
if (!terminalList) {
|
if (!terminalList) {
|
||||||
terminalList = [];
|
terminalList = [];
|
||||||
map[name] = terminalList;
|
map.set(name, terminalList);
|
||||||
}
|
}
|
||||||
terminalList.push(selectable);
|
terminalList.push(selectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addPartial(map: {[k: string]: SelectorMatcher}, name: string): SelectorMatcher {
|
private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher {
|
||||||
let matcher = map[name];
|
let matcher = map.get(name);
|
||||||
if (!matcher) {
|
if (!matcher) {
|
||||||
matcher = new SelectorMatcher();
|
matcher = new SelectorMatcher();
|
||||||
map[name] = matcher;
|
map.set(name, matcher);
|
||||||
}
|
}
|
||||||
return matcher;
|
return matcher;
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ export class SelectorMatcher {
|
|||||||
const name = attrs[i];
|
const name = attrs[i];
|
||||||
const value = attrs[i + 1];
|
const value = attrs[i + 1];
|
||||||
|
|
||||||
const terminalValuesMap = this._attrValueMap[name];
|
const terminalValuesMap = this._attrValueMap.get(name);
|
||||||
if (value) {
|
if (value) {
|
||||||
result =
|
result =
|
||||||
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
||||||
@ -278,7 +278,7 @@ export class SelectorMatcher {
|
|||||||
result =
|
result =
|
||||||
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
||||||
|
|
||||||
const partialValuesMap = this._attrValuePartialMap[name];
|
const partialValuesMap = this._attrValuePartialMap.get(name);
|
||||||
if (value) {
|
if (value) {
|
||||||
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
@ -291,14 +291,14 @@ export class SelectorMatcher {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_matchTerminal(
|
_matchTerminal(
|
||||||
map: {[k: string]: SelectorContext[]}, name: string, cssSelector: CssSelector,
|
map: Map<string, SelectorContext[]>, name: string, cssSelector: CssSelector,
|
||||||
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
||||||
if (!map || typeof name !== 'string') {
|
if (!map || typeof name !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectables = map[name];
|
let selectables = map.get(name);
|
||||||
const starSelectables = map['*'];
|
const starSelectables = map.get('*');
|
||||||
if (starSelectables) {
|
if (starSelectables) {
|
||||||
selectables = selectables.concat(starSelectables);
|
selectables = selectables.concat(starSelectables);
|
||||||
}
|
}
|
||||||
@ -316,13 +316,13 @@ export class SelectorMatcher {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_matchPartial(
|
_matchPartial(
|
||||||
map: {[k: string]: SelectorMatcher}, name: string, cssSelector: CssSelector,
|
map: Map<string, SelectorMatcher>, name: string, cssSelector: CssSelector,
|
||||||
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
||||||
if (!map || typeof name !== 'string') {
|
if (!map || typeof name !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nestedSelector = map[name];
|
const nestedSelector = map.get(name);
|
||||||
if (!nestedSelector) {
|
if (!nestedSelector) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
439
modules/@angular/compiler/src/template_parser/binding_parser.ts
Normal file
439
modules/@angular/compiler/src/template_parser/binding_parser.ts
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
|
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, LiteralPrimitive, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||||
|
import {Parser} from '../expression_parser/parser';
|
||||||
|
import {isPresent} from '../facade/lang';
|
||||||
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
|
import {mergeNsAndName} from '../ml_parser/tags';
|
||||||
|
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
||||||
|
import {view_utils} from '../private_import_core';
|
||||||
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
import {CssSelector} from '../selector';
|
||||||
|
import {splitAtColon, splitAtPeriod} from '../util';
|
||||||
|
|
||||||
|
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType, VariableAst} from './template_ast';
|
||||||
|
|
||||||
|
const PROPERTY_PARTS_SEPARATOR = '.';
|
||||||
|
const ATTRIBUTE_PREFIX = 'attr';
|
||||||
|
const CLASS_PREFIX = 'class';
|
||||||
|
const STYLE_PREFIX = 'style';
|
||||||
|
|
||||||
|
const ANIMATE_PROP_PREFIX = 'animate-';
|
||||||
|
|
||||||
|
export enum BoundPropertyType {
|
||||||
|
DEFAULT,
|
||||||
|
LITERAL_ATTR,
|
||||||
|
ANIMATION
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed property.
|
||||||
|
*/
|
||||||
|
export class BoundProperty {
|
||||||
|
constructor(
|
||||||
|
public name: string, public expression: ASTWithSource, public type: BoundPropertyType,
|
||||||
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
|
get isLiteral() { return this.type === BoundPropertyType.LITERAL_ATTR; }
|
||||||
|
|
||||||
|
get isAnimation() { return this.type === BoundPropertyType.ANIMATION; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses bindings in templates and in the directive host area.
|
||||||
|
*/
|
||||||
|
export class BindingParser {
|
||||||
|
pipesByName: Map<string, CompilePipeMetadata> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _exprParser: Parser, private _interpolationConfig: InterpolationConfig,
|
||||||
|
private _schemaRegistry: ElementSchemaRegistry, pipes: CompilePipeMetadata[],
|
||||||
|
private _targetErrors: ParseError[]) {
|
||||||
|
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
||||||
|
}
|
||||||
|
|
||||||
|
createDirectiveHostPropertyAsts(dirMeta: CompileDirectiveMetadata, sourceSpan: ParseSourceSpan):
|
||||||
|
BoundElementPropertyAst[] {
|
||||||
|
if (dirMeta.hostProperties) {
|
||||||
|
const boundProps: BoundProperty[] = [];
|
||||||
|
Object.keys(dirMeta.hostProperties).forEach(propName => {
|
||||||
|
const expression = dirMeta.hostProperties[propName];
|
||||||
|
if (typeof expression === 'string') {
|
||||||
|
this.parsePropertyBinding(propName, expression, true, sourceSpan, [], boundProps);
|
||||||
|
} else {
|
||||||
|
this._reportError(
|
||||||
|
`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
||||||
|
sourceSpan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return boundProps.map((prop) => this.createElementPropertyAst(dirMeta.selector, prop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createDirectiveHostEventAsts(dirMeta: CompileDirectiveMetadata, sourceSpan: ParseSourceSpan):
|
||||||
|
BoundEventAst[] {
|
||||||
|
if (dirMeta.hostListeners) {
|
||||||
|
const targetEventAsts: BoundEventAst[] = [];
|
||||||
|
Object.keys(dirMeta.hostListeners).forEach(propName => {
|
||||||
|
const expression = dirMeta.hostListeners[propName];
|
||||||
|
if (typeof expression === 'string') {
|
||||||
|
this.parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
|
||||||
|
} else {
|
||||||
|
this._reportError(
|
||||||
|
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
||||||
|
sourceSpan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return targetEventAsts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||||
|
const sourceInfo = sourceSpan.start.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig);
|
||||||
|
if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
if (ast &&
|
||||||
|
(<Interpolation>ast.ast).expressions.length > view_utils.MAX_INTERPOLATION_VALUES) {
|
||||||
|
throw new Error(
|
||||||
|
`Only support at most ${view_utils.MAX_INTERPOLATION_VALUES} interpolation values!`);
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
} catch (e) {
|
||||||
|
this._reportError(`${e}`, sourceSpan);
|
||||||
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseInlineTemplateBinding(
|
||||||
|
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
||||||
|
targetProps: BoundProperty[], targetVars: VariableAst[]) {
|
||||||
|
const bindings = this._parseTemplateBindings(value, sourceSpan);
|
||||||
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
|
const binding = bindings[i];
|
||||||
|
if (binding.keyIsVar) {
|
||||||
|
targetVars.push(new VariableAst(binding.key, binding.name, sourceSpan));
|
||||||
|
} else if (isPresent(binding.expression)) {
|
||||||
|
this._parsePropertyAst(
|
||||||
|
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
} else {
|
||||||
|
targetMatchableAttrs.push([binding.key, '']);
|
||||||
|
this.parseLiteralAttr(binding.key, null, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
||||||
|
const sourceInfo = sourceSpan.start.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||||
|
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
||||||
|
bindingsResult.templateBindings.forEach((binding) => {
|
||||||
|
if (isPresent(binding.expression)) {
|
||||||
|
this._checkPipes(binding.expression, sourceSpan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bindingsResult.warnings.forEach(
|
||||||
|
(warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
|
||||||
|
return bindingsResult.templateBindings;
|
||||||
|
} catch (e) {
|
||||||
|
this._reportError(`${e}`, sourceSpan);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseLiteralAttr(
|
||||||
|
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
||||||
|
targetProps: BoundProperty[]) {
|
||||||
|
if (_isAnimationLabel(name)) {
|
||||||
|
name = name.substring(1);
|
||||||
|
if (value) {
|
||||||
|
this._reportError(
|
||||||
|
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
||||||
|
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
|
||||||
|
sourceSpan, ParseErrorLevel.FATAL);
|
||||||
|
}
|
||||||
|
this._parseAnimation(name, value, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
} else {
|
||||||
|
targetProps.push(new BoundProperty(
|
||||||
|
name, this._exprParser.wrapLiteralPrimitive(value, ''), BoundPropertyType.LITERAL_ATTR,
|
||||||
|
sourceSpan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePropertyBinding(
|
||||||
|
name: string, expression: string, isHost: boolean, sourceSpan: ParseSourceSpan,
|
||||||
|
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
||||||
|
let isAnimationProp = false;
|
||||||
|
if (name.startsWith(ANIMATE_PROP_PREFIX)) {
|
||||||
|
isAnimationProp = true;
|
||||||
|
name = name.substring(ANIMATE_PROP_PREFIX.length);
|
||||||
|
} else if (_isAnimationLabel(name)) {
|
||||||
|
isAnimationProp = true;
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAnimationProp) {
|
||||||
|
this._parseAnimation(name, expression, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
} else {
|
||||||
|
this._parsePropertyAst(
|
||||||
|
name, this._parseBinding(expression, isHost, sourceSpan), sourceSpan,
|
||||||
|
targetMatchableAttrs, targetProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePropertyInterpolation(
|
||||||
|
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
||||||
|
targetProps: BoundProperty[]): boolean {
|
||||||
|
const expr = this.parseInterpolation(value, sourceSpan);
|
||||||
|
if (isPresent(expr)) {
|
||||||
|
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parsePropertyAst(
|
||||||
|
name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan,
|
||||||
|
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
||||||
|
targetMatchableAttrs.push([name, ast.source]);
|
||||||
|
targetProps.push(new BoundProperty(name, ast, BoundPropertyType.DEFAULT, sourceSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseAnimation(
|
||||||
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
|
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
||||||
|
// This will occur when a @trigger is not paired with an expression.
|
||||||
|
// For animations it is valid to not have an expression since */void
|
||||||
|
// states will be applied by angular when the element is attached/detached
|
||||||
|
const ast = this._parseBinding(expression || 'null', false, sourceSpan);
|
||||||
|
targetMatchableAttrs.push([name, ast.source]);
|
||||||
|
targetProps.push(new BoundProperty(name, ast, BoundPropertyType.ANIMATION, sourceSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseBinding(value: string, isHostBinding: boolean, sourceSpan: ParseSourceSpan):
|
||||||
|
ASTWithSource {
|
||||||
|
const sourceInfo = sourceSpan.start.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ast = isHostBinding ?
|
||||||
|
this._exprParser.parseSimpleBinding(value, sourceInfo, this._interpolationConfig) :
|
||||||
|
this._exprParser.parseBinding(value, sourceInfo, this._interpolationConfig);
|
||||||
|
if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
return ast;
|
||||||
|
} catch (e) {
|
||||||
|
this._reportError(`${e}`, sourceSpan);
|
||||||
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createElementPropertyAst(elementSelector: string, boundProp: BoundProperty):
|
||||||
|
BoundElementPropertyAst {
|
||||||
|
if (boundProp.isAnimation) {
|
||||||
|
return new BoundElementPropertyAst(
|
||||||
|
boundProp.name, PropertyBindingType.Animation, SecurityContext.NONE, false,
|
||||||
|
boundProp.expression, null, boundProp.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
let unit: string = null;
|
||||||
|
let bindingType: PropertyBindingType;
|
||||||
|
let boundPropertyName: string;
|
||||||
|
const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
||||||
|
let securityContexts: SecurityContext[];
|
||||||
|
|
||||||
|
if (parts.length === 1) {
|
||||||
|
var partValue = parts[0];
|
||||||
|
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
||||||
|
securityContexts = calcPossibleSecurityContexts(
|
||||||
|
this._schemaRegistry, elementSelector, boundPropertyName, false);
|
||||||
|
bindingType = PropertyBindingType.Property;
|
||||||
|
this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, false);
|
||||||
|
} else {
|
||||||
|
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||||
|
boundPropertyName = parts[1];
|
||||||
|
this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
|
||||||
|
securityContexts = calcPossibleSecurityContexts(
|
||||||
|
this._schemaRegistry, elementSelector, boundPropertyName, true);
|
||||||
|
|
||||||
|
const nsSeparatorIdx = boundPropertyName.indexOf(':');
|
||||||
|
if (nsSeparatorIdx > -1) {
|
||||||
|
const ns = boundPropertyName.substring(0, nsSeparatorIdx);
|
||||||
|
const name = boundPropertyName.substring(nsSeparatorIdx + 1);
|
||||||
|
boundPropertyName = mergeNsAndName(ns, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingType = PropertyBindingType.Attribute;
|
||||||
|
} else if (parts[0] == CLASS_PREFIX) {
|
||||||
|
boundPropertyName = parts[1];
|
||||||
|
bindingType = PropertyBindingType.Class;
|
||||||
|
securityContexts = [SecurityContext.NONE];
|
||||||
|
} else if (parts[0] == STYLE_PREFIX) {
|
||||||
|
unit = parts.length > 2 ? parts[2] : null;
|
||||||
|
boundPropertyName = parts[1];
|
||||||
|
bindingType = PropertyBindingType.Style;
|
||||||
|
securityContexts = [SecurityContext.STYLE];
|
||||||
|
} else {
|
||||||
|
this._reportError(`Invalid property name '${boundProp.name}'`, boundProp.sourceSpan);
|
||||||
|
bindingType = null;
|
||||||
|
securityContexts = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new BoundElementPropertyAst(
|
||||||
|
boundPropertyName, bindingType, securityContexts.length === 1 ? securityContexts[0] : null,
|
||||||
|
securityContexts.length > 1, boundProp.expression, unit, boundProp.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseEvent(
|
||||||
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
|
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||||
|
if (_isAnimationLabel(name)) {
|
||||||
|
name = name.substr(1);
|
||||||
|
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
|
||||||
|
} else {
|
||||||
|
this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseAnimationEvent(
|
||||||
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
|
targetEvents: BoundEventAst[]) {
|
||||||
|
const matches = splitAtPeriod(name, [name, '']);
|
||||||
|
const eventName = matches[0];
|
||||||
|
const phase = matches[1].toLowerCase();
|
||||||
|
if (phase) {
|
||||||
|
switch (phase) {
|
||||||
|
case 'start':
|
||||||
|
case 'done':
|
||||||
|
const ast = this._parseAction(expression, sourceSpan);
|
||||||
|
targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this._reportError(
|
||||||
|
`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`,
|
||||||
|
sourceSpan);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._reportError(
|
||||||
|
`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`,
|
||||||
|
sourceSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseEvent(
|
||||||
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
|
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||||
|
// long format: 'target: eventName'
|
||||||
|
const [target, eventName] = splitAtColon(name, [null, name]);
|
||||||
|
const ast = this._parseAction(expression, sourceSpan);
|
||||||
|
targetMatchableAttrs.push([name, ast.source]);
|
||||||
|
targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan));
|
||||||
|
// Don't detect directives for event names for now,
|
||||||
|
// so don't add the event name to the matchableAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||||
|
const sourceInfo = sourceSpan.start.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ast = this._exprParser.parseAction(value, sourceInfo, this._interpolationConfig);
|
||||||
|
if (ast) {
|
||||||
|
this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
||||||
|
}
|
||||||
|
if (!ast || ast.ast instanceof EmptyExpr) {
|
||||||
|
this._reportError(`Empty expressions are not allowed`, sourceSpan);
|
||||||
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
|
}
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
return ast;
|
||||||
|
} catch (e) {
|
||||||
|
this._reportError(`${e}`, sourceSpan);
|
||||||
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _reportError(
|
||||||
|
message: string, sourceSpan: ParseSourceSpan,
|
||||||
|
level: ParseErrorLevel = ParseErrorLevel.FATAL) {
|
||||||
|
this._targetErrors.push(new ParseError(sourceSpan, message, level));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _reportExpressionParserErrors(errors: ParserError[], sourceSpan: ParseSourceSpan) {
|
||||||
|
for (const error of errors) {
|
||||||
|
this._reportError(error.message, sourceSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
||||||
|
if (isPresent(ast)) {
|
||||||
|
const collector = new PipeCollector();
|
||||||
|
ast.visit(collector);
|
||||||
|
collector.pipes.forEach((pipeName) => {
|
||||||
|
if (!this.pipesByName.has(pipeName)) {
|
||||||
|
this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param propName the name of the property / attribute
|
||||||
|
* @param sourceSpan
|
||||||
|
* @param isAttr true when binding to an attribute
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _validatePropertyOrAttributeName(
|
||||||
|
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
|
||||||
|
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
|
||||||
|
this._schemaRegistry.validateProperty(propName);
|
||||||
|
if (report.error) {
|
||||||
|
this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PipeCollector extends RecursiveAstVisitor {
|
||||||
|
pipes = new Set<string>();
|
||||||
|
visitPipe(ast: BindingPipe, context: any): any {
|
||||||
|
this.pipes.add(ast.name);
|
||||||
|
ast.exp.visit(this);
|
||||||
|
this.visitAll(ast.args, context);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isAnimationLabel(name: string): boolean {
|
||||||
|
return name[0] == '@';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcPossibleSecurityContexts(
|
||||||
|
registry: ElementSchemaRegistry, selector: string, propName: string,
|
||||||
|
isAttribute: boolean): SecurityContext[] {
|
||||||
|
const ctxs: SecurityContext[] = [];
|
||||||
|
CssSelector.parse(selector).forEach((selector) => {
|
||||||
|
const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
|
||||||
|
const notElementNames =
|
||||||
|
new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
|
||||||
|
.map((selector) => selector.element));
|
||||||
|
const possibleElementNames =
|
||||||
|
elementNames.filter(elementName => !notElementNames.has(elementName));
|
||||||
|
|
||||||
|
ctxs.push(...possibleElementNames.map(
|
||||||
|
elementName => registry.securityContext(elementName, propName, isAttribute)));
|
||||||
|
});
|
||||||
|
return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
|
||||||
|
}
|
@ -63,8 +63,8 @@ export class AttrAst implements TemplateAst {
|
|||||||
export class BoundElementPropertyAst implements TemplateAst {
|
export class BoundElementPropertyAst implements TemplateAst {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public type: PropertyBindingType,
|
public name: string, public type: PropertyBindingType,
|
||||||
public securityContext: SecurityContext, public value: AST, public unit: string,
|
public securityContext: SecurityContext, public needsRuntimeSecurityContext: boolean,
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public value: AST, public unit: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitElementProperty(this, context);
|
return visitor.visitElementProperty(this, context);
|
||||||
}
|
}
|
||||||
@ -76,19 +76,23 @@ export class BoundElementPropertyAst implements TemplateAst {
|
|||||||
* `(@trigger.phase)="callback($event)"`).
|
* `(@trigger.phase)="callback($event)"`).
|
||||||
*/
|
*/
|
||||||
export class BoundEventAst implements TemplateAst {
|
export class BoundEventAst implements TemplateAst {
|
||||||
|
static calcFullName(name: string, target: string, phase: string): string {
|
||||||
|
if (target) {
|
||||||
|
return `${target}:${name}`;
|
||||||
|
} else if (phase) {
|
||||||
|
return `@${name}.${phase}`;
|
||||||
|
} else {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public target: string, public phase: string, public handler: AST,
|
public name: string, public target: string, public phase: string, public handler: AST,
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitEvent(this, context);
|
return visitor.visitEvent(this, context);
|
||||||
}
|
}
|
||||||
get fullName() {
|
get fullName() { return BoundEventAst.calcFullName(this.name, this.target, this.phase); }
|
||||||
if (this.target) {
|
|
||||||
return `${this.target}:${this.name}`;
|
|
||||||
} else {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get isAnimation(): boolean { return !!this.phase; }
|
get isAnimation(): boolean { return !!this.phase; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityConte
|
|||||||
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
|
||||||
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {isPresent, isString} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||||
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
|
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
|
||||||
import * as html from '../ml_parser/ast';
|
import * as html from '../ml_parser/ast';
|
||||||
@ -20,13 +20,13 @@ import {expandNodes} from '../ml_parser/icu_ast_expander';
|
|||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {mergeNsAndName, splitNsName} from '../ml_parser/tags';
|
import {mergeNsAndName, splitNsName} from '../ml_parser/tags';
|
||||||
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
||||||
import {Console, MAX_INTERPOLATION_VALUES} from '../private_import_core';
|
import {Console, view_utils} from '../private_import_core';
|
||||||
import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer';
|
import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {CssSelector, SelectorMatcher} from '../selector';
|
import {CssSelector, SelectorMatcher} from '../selector';
|
||||||
import {isStyleUrlResolvable} from '../style_url_resolver';
|
import {isStyleUrlResolvable} from '../style_url_resolver';
|
||||||
import {splitAtColon, splitAtPeriod} from '../util';
|
|
||||||
|
|
||||||
|
import {BindingParser, BoundProperty} from './binding_parser';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
|
||||||
import {PreparsedElementType, preparseElement} from './template_preparser';
|
import {PreparsedElementType, preparseElement} from './template_preparser';
|
||||||
|
|
||||||
@ -56,17 +56,11 @@ const IDENT_BANANA_BOX_IDX = 8;
|
|||||||
const IDENT_PROPERTY_IDX = 9;
|
const IDENT_PROPERTY_IDX = 9;
|
||||||
const IDENT_EVENT_IDX = 10;
|
const IDENT_EVENT_IDX = 10;
|
||||||
|
|
||||||
const ANIMATE_PROP_PREFIX = 'animate-';
|
|
||||||
const TEMPLATE_ELEMENT = 'template';
|
const TEMPLATE_ELEMENT = 'template';
|
||||||
const TEMPLATE_ATTR = 'template';
|
const TEMPLATE_ATTR = 'template';
|
||||||
const TEMPLATE_ATTR_PREFIX = '*';
|
const TEMPLATE_ATTR_PREFIX = '*';
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
|
|
||||||
const PROPERTY_PARTS_SEPARATOR = '.';
|
|
||||||
const ATTRIBUTE_PREFIX = 'attr';
|
|
||||||
const CLASS_PREFIX = 'class';
|
|
||||||
const STYLE_PREFIX = 'style';
|
|
||||||
|
|
||||||
const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,11 +129,20 @@ export class TemplateParser {
|
|||||||
const uniqPipes = removeIdentifierDuplicates(pipes);
|
const uniqPipes = removeIdentifierDuplicates(pipes);
|
||||||
const providerViewContext =
|
const providerViewContext =
|
||||||
new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan);
|
new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan);
|
||||||
|
let interpolationConfig: InterpolationConfig;
|
||||||
|
if (component.template && component.template.interpolation) {
|
||||||
|
interpolationConfig = {
|
||||||
|
start: component.template.interpolation[0],
|
||||||
|
end: component.template.interpolation[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const bindingParser = new BindingParser(
|
||||||
|
this._exprParser, interpolationConfig, this._schemaRegistry, uniqPipes, errors);
|
||||||
const parseVisitor = new TemplateParseVisitor(
|
const parseVisitor = new TemplateParseVisitor(
|
||||||
providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser,
|
providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas,
|
||||||
this._schemaRegistry);
|
errors);
|
||||||
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
||||||
errors.push(...parseVisitor.errors, ...providerViewContext.errors);
|
errors.push(...providerViewContext.errors);
|
||||||
} else {
|
} else {
|
||||||
result = [];
|
result = [];
|
||||||
}
|
}
|
||||||
@ -197,129 +200,18 @@ export class TemplateParser {
|
|||||||
|
|
||||||
class TemplateParseVisitor implements html.Visitor {
|
class TemplateParseVisitor implements html.Visitor {
|
||||||
selectorMatcher = new SelectorMatcher();
|
selectorMatcher = new SelectorMatcher();
|
||||||
errors: TemplateParseError[] = [];
|
|
||||||
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
||||||
ngContentCount: number = 0;
|
ngContentCount: number = 0;
|
||||||
pipesByName: Map<string, CompilePipeMetadata> = new Map();
|
|
||||||
|
|
||||||
private _interpolationConfig: InterpolationConfig;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public providerViewContext: ProviderViewContext, directives: CompileDirectiveMetadata[],
|
public providerViewContext: ProviderViewContext, directives: CompileDirectiveMetadata[],
|
||||||
pipes: CompilePipeMetadata[], private _schemas: SchemaMetadata[], private _exprParser: Parser,
|
private _bindingParser: BindingParser, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _schemaRegistry: ElementSchemaRegistry) {
|
private _schemas: SchemaMetadata[], private _targetErrors: TemplateParseError[]) {
|
||||||
const tempMeta = providerViewContext.component.template;
|
|
||||||
|
|
||||||
if (tempMeta && tempMeta.interpolation) {
|
|
||||||
this._interpolationConfig = {
|
|
||||||
start: tempMeta.interpolation[0],
|
|
||||||
end: tempMeta.interpolation[1]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
directives.forEach((directive: CompileDirectiveMetadata, index: number) => {
|
directives.forEach((directive: CompileDirectiveMetadata, index: number) => {
|
||||||
const selector = CssSelector.parse(directive.selector);
|
const selector = CssSelector.parse(directive.selector);
|
||||||
this.selectorMatcher.addSelectables(selector, directive);
|
this.selectorMatcher.addSelectables(selector, directive);
|
||||||
this.directivesIndex.set(directive, index);
|
this.directivesIndex.set(directive, index);
|
||||||
});
|
});
|
||||||
|
|
||||||
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _reportError(
|
|
||||||
message: string, sourceSpan: ParseSourceSpan,
|
|
||||||
level: ParseErrorLevel = ParseErrorLevel.FATAL) {
|
|
||||||
this.errors.push(new TemplateParseError(message, sourceSpan, level));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _reportParserErrors(errors: ParserError[], sourceSpan: ParseSourceSpan) {
|
|
||||||
for (const error of errors) {
|
|
||||||
this._reportError(error.message, sourceSpan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
|
||||||
const sourceInfo = sourceSpan.start.toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig);
|
|
||||||
if (ast) this._reportParserErrors(ast.errors, sourceSpan);
|
|
||||||
this._checkPipes(ast, sourceSpan);
|
|
||||||
if (isPresent(ast) &&
|
|
||||||
(<Interpolation>ast.ast).expressions.length > MAX_INTERPOLATION_VALUES) {
|
|
||||||
throw new Error(`Only support at most ${MAX_INTERPOLATION_VALUES} interpolation values!`);
|
|
||||||
}
|
|
||||||
return ast;
|
|
||||||
} catch (e) {
|
|
||||||
this._reportError(`${e}`, sourceSpan);
|
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
|
||||||
const sourceInfo = sourceSpan.start.toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ast = this._exprParser.parseAction(value, sourceInfo, this._interpolationConfig);
|
|
||||||
if (ast) {
|
|
||||||
this._reportParserErrors(ast.errors, sourceSpan);
|
|
||||||
}
|
|
||||||
if (!ast || ast.ast instanceof EmptyExpr) {
|
|
||||||
this._reportError(`Empty expressions are not allowed`, sourceSpan);
|
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
|
||||||
}
|
|
||||||
this._checkPipes(ast, sourceSpan);
|
|
||||||
return ast;
|
|
||||||
} catch (e) {
|
|
||||||
this._reportError(`${e}`, sourceSpan);
|
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
|
||||||
const sourceInfo = sourceSpan.start.toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ast = this._exprParser.parseBinding(value, sourceInfo, this._interpolationConfig);
|
|
||||||
if (ast) this._reportParserErrors(ast.errors, sourceSpan);
|
|
||||||
this._checkPipes(ast, sourceSpan);
|
|
||||||
return ast;
|
|
||||||
} catch (e) {
|
|
||||||
this._reportError(`${e}`, sourceSpan);
|
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
|
||||||
const sourceInfo = sourceSpan.start.toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
|
||||||
this._reportParserErrors(bindingsResult.errors, sourceSpan);
|
|
||||||
bindingsResult.templateBindings.forEach((binding) => {
|
|
||||||
if (isPresent(binding.expression)) {
|
|
||||||
this._checkPipes(binding.expression, sourceSpan);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
bindingsResult.warnings.forEach(
|
|
||||||
(warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
|
|
||||||
return bindingsResult.templateBindings;
|
|
||||||
} catch (e) {
|
|
||||||
this._reportError(`${e}`, sourceSpan);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
|
||||||
if (isPresent(ast)) {
|
|
||||||
const collector = new PipeCollector();
|
|
||||||
ast.visit(collector);
|
|
||||||
collector.pipes.forEach((pipeName) => {
|
|
||||||
if (!this.pipesByName.has(pipeName)) {
|
|
||||||
this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExpansion(expansion: html.Expansion, context: any): any { return null; }
|
visitExpansion(expansion: html.Expansion, context: any): any { return null; }
|
||||||
@ -328,7 +220,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
|
|
||||||
visitText(text: html.Text, parent: ElementContext): any {
|
visitText(text: html.Text, parent: ElementContext): any {
|
||||||
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||||
const expr = this._parseInterpolation(text.value, text.sourceSpan);
|
const expr = this._bindingParser.parseInterpolation(text.value, text.sourceSpan);
|
||||||
if (isPresent(expr)) {
|
if (isPresent(expr)) {
|
||||||
return new BoundTextAst(expr, ngContentIndex, text.sourceSpan);
|
return new BoundTextAst(expr, ngContentIndex, text.sourceSpan);
|
||||||
} else {
|
} else {
|
||||||
@ -360,13 +252,12 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const matchableAttrs: string[][] = [];
|
const matchableAttrs: string[][] = [];
|
||||||
const elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
const elementOrDirectiveProps: BoundProperty[] = [];
|
||||||
const elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
const elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
||||||
const elementVars: VariableAst[] = [];
|
const elementVars: VariableAst[] = [];
|
||||||
const animationProps: BoundElementPropertyAst[] = [];
|
|
||||||
const events: BoundEventAst[] = [];
|
const events: BoundEventAst[] = [];
|
||||||
|
|
||||||
const templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
const templateElementOrDirectiveProps: BoundProperty[] = [];
|
||||||
const templateMatchableAttrs: string[][] = [];
|
const templateMatchableAttrs: string[][] = [];
|
||||||
const templateElementVars: VariableAst[] = [];
|
const templateElementVars: VariableAst[] = [];
|
||||||
|
|
||||||
@ -377,16 +268,27 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
|
|
||||||
element.attrs.forEach(attr => {
|
element.attrs.forEach(attr => {
|
||||||
const hasBinding = this._parseAttr(
|
const hasBinding = this._parseAttr(
|
||||||
isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, animationProps, events,
|
isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events,
|
||||||
elementOrDirectiveRefs, elementVars);
|
elementOrDirectiveRefs, elementVars);
|
||||||
|
|
||||||
const hasTemplateBinding = this._parseInlineTemplateBinding(
|
let templateBindingsSource: string;
|
||||||
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
|
if (this._normalizeAttributeName(attr.name) == TEMPLATE_ATTR) {
|
||||||
|
templateBindingsSource = attr.value;
|
||||||
if (hasTemplateBinding && hasInlineTemplates) {
|
} else if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
||||||
this._reportError(
|
const key = attr.name.substring(TEMPLATE_ATTR_PREFIX.length); // remove the star
|
||||||
`Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *`,
|
templateBindingsSource = (attr.value.length == 0) ? key : key + ' ' + attr.value;
|
||||||
attr.sourceSpan);
|
}
|
||||||
|
const hasTemplateBinding = isPresent(templateBindingsSource);
|
||||||
|
if (hasTemplateBinding) {
|
||||||
|
if (hasInlineTemplates) {
|
||||||
|
this._reportError(
|
||||||
|
`Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *`,
|
||||||
|
attr.sourceSpan);
|
||||||
|
}
|
||||||
|
hasInlineTemplates = true;
|
||||||
|
this._bindingParser.parseInlineTemplateBinding(
|
||||||
|
attr.name, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
|
||||||
|
templateElementOrDirectiveProps, templateElementVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasBinding && !hasTemplateBinding) {
|
if (!hasBinding && !hasTemplateBinding) {
|
||||||
@ -394,10 +296,6 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
attrs.push(this.visitAttribute(attr, null));
|
attrs.push(this.visitAttribute(attr, null));
|
||||||
matchableAttrs.push([attr.name, attr.value]);
|
matchableAttrs.push([attr.name, attr.value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasTemplateBinding) {
|
|
||||||
hasInlineTemplates = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
const elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
||||||
@ -408,8 +306,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
|
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
|
||||||
elementOrDirectiveRefs, element.sourceSpan, references);
|
elementOrDirectiveRefs, element.sourceSpan, references);
|
||||||
const elementProps: BoundElementPropertyAst[] =
|
const elementProps: BoundElementPropertyAst[] =
|
||||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts)
|
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||||
.concat(animationProps);
|
|
||||||
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||||
const providerContext = new ProviderElementContext(
|
const providerContext = new ProviderElementContext(
|
||||||
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
|
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
|
||||||
@ -518,39 +415,9 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseInlineTemplateBinding(
|
|
||||||
attr: html.Attribute, targetMatchableAttrs: string[][],
|
|
||||||
targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean {
|
|
||||||
let templateBindingsSource: string = null;
|
|
||||||
if (this._normalizeAttributeName(attr.name) == TEMPLATE_ATTR) {
|
|
||||||
templateBindingsSource = attr.value;
|
|
||||||
} else if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
|
||||||
const key = attr.name.substring(TEMPLATE_ATTR_PREFIX.length); // remove the star
|
|
||||||
templateBindingsSource = (attr.value.length == 0) ? key : key + ' ' + attr.value;
|
|
||||||
}
|
|
||||||
if (isPresent(templateBindingsSource)) {
|
|
||||||
const bindings = this._parseTemplateBindings(templateBindingsSource, attr.sourceSpan);
|
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
|
||||||
const binding = bindings[i];
|
|
||||||
if (binding.keyIsVar) {
|
|
||||||
targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan));
|
|
||||||
} else if (isPresent(binding.expression)) {
|
|
||||||
this._parsePropertyAst(
|
|
||||||
binding.key, binding.expression, attr.sourceSpan, targetMatchableAttrs, targetProps);
|
|
||||||
} else {
|
|
||||||
targetMatchableAttrs.push([binding.key, '']);
|
|
||||||
this._parseLiteralAttr(binding.key, null, attr.sourceSpan, targetProps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseAttr(
|
private _parseAttr(
|
||||||
isTemplateElement: boolean, attr: html.Attribute, targetMatchableAttrs: string[][],
|
isTemplateElement: boolean, attr: html.Attribute, targetMatchableAttrs: string[][],
|
||||||
targetProps: BoundElementOrDirectiveProperty[],
|
targetProps: BoundProperty[], targetEvents: BoundEventAst[],
|
||||||
targetAnimationProps: BoundElementPropertyAst[], targetEvents: BoundEventAst[],
|
|
||||||
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
||||||
const name = this._normalizeAttributeName(attr.name);
|
const name = this._normalizeAttributeName(attr.name);
|
||||||
const value = attr.value;
|
const value = attr.value;
|
||||||
@ -562,9 +429,8 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
if (bindParts !== null) {
|
if (bindParts !== null) {
|
||||||
hasBinding = true;
|
hasBinding = true;
|
||||||
if (isPresent(bindParts[KW_BIND_IDX])) {
|
if (isPresent(bindParts[KW_BIND_IDX])) {
|
||||||
this._parsePropertyOrAnimation(
|
this._bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
targetAnimationProps);
|
|
||||||
|
|
||||||
} else if (bindParts[KW_LET_IDX]) {
|
} else if (bindParts[KW_LET_IDX]) {
|
||||||
if (isTemplateElement) {
|
if (isTemplateElement) {
|
||||||
@ -579,48 +445,42 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
this._parseReference(identifier, value, srcSpan, targetRefs);
|
this._parseReference(identifier, value, srcSpan, targetRefs);
|
||||||
|
|
||||||
} else if (bindParts[KW_ON_IDX]) {
|
} else if (bindParts[KW_ON_IDX]) {
|
||||||
this._parseEventOrAnimationEvent(
|
this._bindingParser.parseEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||||
|
|
||||||
} else if (bindParts[KW_BINDON_IDX]) {
|
} else if (bindParts[KW_BINDON_IDX]) {
|
||||||
this._parsePropertyOrAnimation(
|
this._bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
targetAnimationProps);
|
|
||||||
this._parseAssignmentEvent(
|
this._parseAssignmentEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||||
|
|
||||||
} else if (bindParts[KW_AT_IDX]) {
|
} else if (bindParts[KW_AT_IDX]) {
|
||||||
if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) {
|
this._bindingParser.parseLiteralAttr(
|
||||||
this._reportError(
|
name, value, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
|
||||||
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
|
|
||||||
srcSpan, ParseErrorLevel.FATAL);
|
|
||||||
}
|
|
||||||
this._parseAnimation(
|
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetAnimationProps);
|
|
||||||
} else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
} else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
||||||
this._parsePropertyOrAnimation(
|
this._bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs,
|
||||||
targetAnimationProps);
|
targetProps);
|
||||||
this._parseAssignmentEvent(
|
this._parseAssignmentEvent(
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
||||||
this._parsePropertyOrAnimation(
|
this._bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_PROPERTY_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, targetMatchableAttrs,
|
||||||
targetAnimationProps);
|
targetProps);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_EVENT_IDX]) {
|
} else if (bindParts[IDENT_EVENT_IDX]) {
|
||||||
this._parseEventOrAnimationEvent(
|
this._bindingParser.parseEvent(
|
||||||
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hasBinding =
|
hasBinding = this._bindingParser.parsePropertyInterpolation(
|
||||||
this._parsePropertyInterpolation(name, value, srcSpan, targetMatchableAttrs, targetProps);
|
name, value, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasBinding) {
|
if (!hasBinding) {
|
||||||
this._parseLiteralAttr(name, value, srcSpan, targetProps);
|
this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasBinding;
|
return hasBinding;
|
||||||
@ -649,127 +509,13 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
|
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parsePropertyOrAnimation(
|
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[],
|
|
||||||
targetAnimationProps: BoundElementPropertyAst[]) {
|
|
||||||
const animatePropLength = ANIMATE_PROP_PREFIX.length;
|
|
||||||
var isAnimationProp = _isAnimationLabel(name);
|
|
||||||
var animationPrefixLength = 1;
|
|
||||||
if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) {
|
|
||||||
isAnimationProp = true;
|
|
||||||
animationPrefixLength = animatePropLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAnimationProp) {
|
|
||||||
this._parseAnimation(
|
|
||||||
name.substr(animationPrefixLength), expression, sourceSpan, targetMatchableAttrs,
|
|
||||||
targetAnimationProps);
|
|
||||||
} else {
|
|
||||||
this._parsePropertyAst(
|
|
||||||
name, this._parseBinding(expression, sourceSpan), sourceSpan, targetMatchableAttrs,
|
|
||||||
targetProps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseAnimation(
|
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetMatchableAttrs: string[][], targetAnimationProps: BoundElementPropertyAst[]) {
|
|
||||||
// This will occur when a @trigger is not paired with an expression.
|
|
||||||
// For animations it is valid to not have an expression since */void
|
|
||||||
// states will be applied by angular when the element is attached/detached
|
|
||||||
if (!isPresent(expression) || expression.length == 0) {
|
|
||||||
expression = 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
const ast = this._parseBinding(expression, sourceSpan);
|
|
||||||
targetMatchableAttrs.push([name, ast.source]);
|
|
||||||
targetAnimationProps.push(new BoundElementPropertyAst(
|
|
||||||
name, PropertyBindingType.Animation, SecurityContext.NONE, ast, null, sourceSpan));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parsePropertyInterpolation(
|
|
||||||
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
|
||||||
targetProps: BoundElementOrDirectiveProperty[]): boolean {
|
|
||||||
const expr = this._parseInterpolation(value, sourceSpan);
|
|
||||||
if (isPresent(expr)) {
|
|
||||||
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parsePropertyAst(
|
|
||||||
name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan,
|
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[]) {
|
|
||||||
targetMatchableAttrs.push([name, ast.source]);
|
|
||||||
targetProps.push(new BoundElementOrDirectiveProperty(name, ast, false, sourceSpan));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseAssignmentEvent(
|
private _parseAssignmentEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||||
this._parseEventOrAnimationEvent(
|
this._bindingParser.parseEvent(
|
||||||
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
|
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseEventOrAnimationEvent(
|
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
|
||||||
if (_isAnimationLabel(name)) {
|
|
||||||
name = name.substr(1);
|
|
||||||
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
|
|
||||||
} else {
|
|
||||||
this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseAnimationEvent(
|
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetEvents: BoundEventAst[]) {
|
|
||||||
const matches = splitAtPeriod(name, [name, '']);
|
|
||||||
const eventName = matches[0];
|
|
||||||
const phase = matches[1].toLowerCase();
|
|
||||||
if (phase) {
|
|
||||||
switch (phase) {
|
|
||||||
case 'start':
|
|
||||||
case 'done':
|
|
||||||
const ast = this._parseAction(expression, sourceSpan);
|
|
||||||
targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this._reportError(
|
|
||||||
`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`,
|
|
||||||
sourceSpan);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._reportError(
|
|
||||||
`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`,
|
|
||||||
sourceSpan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseEvent(
|
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
|
||||||
// long format: 'target: eventName'
|
|
||||||
const [target, eventName] = splitAtColon(name, [null, name]);
|
|
||||||
const ast = this._parseAction(expression, sourceSpan);
|
|
||||||
targetMatchableAttrs.push([name, ast.source]);
|
|
||||||
targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan));
|
|
||||||
// Don't detect directives for event names for now,
|
|
||||||
// so don't add the event name to the matchableAttrs
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseLiteralAttr(
|
|
||||||
name: string, value: string, sourceSpan: ParseSourceSpan,
|
|
||||||
targetProps: BoundElementOrDirectiveProperty[]) {
|
|
||||||
targetProps.push(new BoundElementOrDirectiveProperty(
|
|
||||||
name, this._exprParser.wrapLiteralPrimitive(value, ''), true, sourceSpan));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseDirectives(selectorMatcher: SelectorMatcher, elementCssSelector: CssSelector):
|
private _parseDirectives(selectorMatcher: SelectorMatcher, elementCssSelector: CssSelector):
|
||||||
{directives: CompileDirectiveMetadata[], matchElement: boolean} {
|
{directives: CompileDirectiveMetadata[], matchElement: boolean} {
|
||||||
// Need to sort the directives so that we get consistent results throughout,
|
// Need to sort the directives so that we get consistent results throughout,
|
||||||
@ -792,7 +538,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
|
|
||||||
private _createDirectiveAsts(
|
private _createDirectiveAsts(
|
||||||
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveMetadata[],
|
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveMetadata[],
|
||||||
props: BoundElementOrDirectiveProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
props: BoundProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
||||||
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] {
|
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] {
|
||||||
const matchedReferences = new Set<string>();
|
const matchedReferences = new Set<string>();
|
||||||
let component: CompileDirectiveMetadata = null;
|
let component: CompileDirectiveMetadata = null;
|
||||||
@ -802,12 +548,13 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
if (directive.isComponent) {
|
if (directive.isComponent) {
|
||||||
component = directive;
|
component = directive;
|
||||||
}
|
}
|
||||||
const hostProperties: BoundElementPropertyAst[] = [];
|
|
||||||
const hostEvents: BoundEventAst[] = [];
|
|
||||||
const directiveProperties: BoundDirectivePropertyAst[] = [];
|
const directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||||
this._createDirectiveHostPropertyAsts(
|
const hostProperties =
|
||||||
elementName, directive.hostProperties, sourceSpan, hostProperties);
|
this._bindingParser.createDirectiveHostPropertyAsts(directive, sourceSpan);
|
||||||
this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents);
|
// Note: We need to check the host properties here as well,
|
||||||
|
// as we don't know the element name in the DirectiveWrapperCompiler yet.
|
||||||
|
this._checkPropertiesInSchema(elementName, hostProperties);
|
||||||
|
const hostEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
|
||||||
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
|
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
|
||||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||||
@ -838,47 +585,11 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
return directiveAsts;
|
return directiveAsts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectiveHostPropertyAsts(
|
|
||||||
elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan,
|
|
||||||
targetPropertyAsts: BoundElementPropertyAst[]) {
|
|
||||||
if (hostProps) {
|
|
||||||
Object.keys(hostProps).forEach(propName => {
|
|
||||||
const expression = hostProps[propName];
|
|
||||||
if (isString(expression)) {
|
|
||||||
const exprAst = this._parseBinding(expression, sourceSpan);
|
|
||||||
targetPropertyAsts.push(
|
|
||||||
this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan));
|
|
||||||
} else {
|
|
||||||
this._reportError(
|
|
||||||
`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
|
||||||
sourceSpan);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createDirectiveHostEventAsts(
|
|
||||||
hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan,
|
|
||||||
targetEventAsts: BoundEventAst[]) {
|
|
||||||
if (hostListeners) {
|
|
||||||
Object.keys(hostListeners).forEach(propName => {
|
|
||||||
const expression = hostListeners[propName];
|
|
||||||
if (isString(expression)) {
|
|
||||||
this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts);
|
|
||||||
} else {
|
|
||||||
this._reportError(
|
|
||||||
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
|
||||||
sourceSpan);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createDirectivePropertyAsts(
|
private _createDirectivePropertyAsts(
|
||||||
directiveProperties: {[key: string]: string}, boundProps: BoundElementOrDirectiveProperty[],
|
directiveProperties: {[key: string]: string}, boundProps: BoundProperty[],
|
||||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
||||||
if (directiveProperties) {
|
if (directiveProperties) {
|
||||||
const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
|
const boundPropsByName = new Map<string, BoundProperty>();
|
||||||
boundProps.forEach(boundProp => {
|
boundProps.forEach(boundProp => {
|
||||||
const prevValue = boundPropsByName.get(boundProp.name);
|
const prevValue = boundPropsByName.get(boundProp.name);
|
||||||
if (!prevValue || prevValue.isLiteral) {
|
if (!prevValue || prevValue.isLiteral) {
|
||||||
@ -901,7 +612,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createElementPropertyAsts(
|
private _createElementPropertyAsts(
|
||||||
elementName: string, props: BoundElementOrDirectiveProperty[],
|
elementName: string, props: BoundProperty[],
|
||||||
directives: DirectiveAst[]): BoundElementPropertyAst[] {
|
directives: DirectiveAst[]): BoundElementPropertyAst[] {
|
||||||
const boundElementProps: BoundElementPropertyAst[] = [];
|
const boundElementProps: BoundElementPropertyAst[] = [];
|
||||||
const boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>();
|
const boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>();
|
||||||
@ -912,98 +623,15 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
props.forEach((prop: BoundElementOrDirectiveProperty) => {
|
props.forEach((prop: BoundProperty) => {
|
||||||
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
|
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
|
||||||
boundElementProps.push(this._createElementPropertyAst(
|
boundElementProps.push(this._bindingParser.createElementPropertyAst(elementName, prop));
|
||||||
elementName, prop.name, prop.expression, prop.sourceSpan));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this._checkPropertiesInSchema(elementName, boundElementProps);
|
||||||
return boundElementProps;
|
return boundElementProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createElementPropertyAst(
|
|
||||||
elementName: string, name: string, ast: AST,
|
|
||||||
sourceSpan: ParseSourceSpan): BoundElementPropertyAst {
|
|
||||||
let unit: string = null;
|
|
||||||
let bindingType: PropertyBindingType;
|
|
||||||
let boundPropertyName: string;
|
|
||||||
const parts = name.split(PROPERTY_PARTS_SEPARATOR);
|
|
||||||
let securityContext: SecurityContext;
|
|
||||||
|
|
||||||
if (parts.length === 1) {
|
|
||||||
var partValue = parts[0];
|
|
||||||
if (_isAnimationLabel(partValue)) {
|
|
||||||
boundPropertyName = partValue.substr(1);
|
|
||||||
bindingType = PropertyBindingType.Animation;
|
|
||||||
securityContext = SecurityContext.NONE;
|
|
||||||
} else {
|
|
||||||
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
|
||||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
|
||||||
bindingType = PropertyBindingType.Property;
|
|
||||||
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false);
|
|
||||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
|
|
||||||
let errorMsg =
|
|
||||||
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
|
|
||||||
if (elementName.indexOf('-') > -1) {
|
|
||||||
errorMsg +=
|
|
||||||
`\n1. If '${elementName}' is an Angular component and it has '${boundPropertyName}' input, then verify that it is part of this module.` +
|
|
||||||
`\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.\n`;
|
|
||||||
}
|
|
||||||
this._reportError(errorMsg, sourceSpan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
|
||||||
boundPropertyName = parts[1];
|
|
||||||
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, true);
|
|
||||||
// NB: For security purposes, use the mapped property name, not the attribute name.
|
|
||||||
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
|
|
||||||
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
|
|
||||||
|
|
||||||
const nsSeparatorIdx = boundPropertyName.indexOf(':');
|
|
||||||
if (nsSeparatorIdx > -1) {
|
|
||||||
const ns = boundPropertyName.substring(0, nsSeparatorIdx);
|
|
||||||
const name = boundPropertyName.substring(nsSeparatorIdx + 1);
|
|
||||||
boundPropertyName = mergeNsAndName(ns, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingType = PropertyBindingType.Attribute;
|
|
||||||
} else if (parts[0] == CLASS_PREFIX) {
|
|
||||||
boundPropertyName = parts[1];
|
|
||||||
bindingType = PropertyBindingType.Class;
|
|
||||||
securityContext = SecurityContext.NONE;
|
|
||||||
} else if (parts[0] == STYLE_PREFIX) {
|
|
||||||
unit = parts.length > 2 ? parts[2] : null;
|
|
||||||
boundPropertyName = parts[1];
|
|
||||||
bindingType = PropertyBindingType.Style;
|
|
||||||
securityContext = SecurityContext.STYLE;
|
|
||||||
} else {
|
|
||||||
this._reportError(`Invalid property name '${name}'`, sourceSpan);
|
|
||||||
bindingType = null;
|
|
||||||
securityContext = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BoundElementPropertyAst(
|
|
||||||
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param propName the name of the property / attribute
|
|
||||||
* @param sourceSpan
|
|
||||||
* @param isAttr true when binding to an attribute
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _validatePropertyOrAttributeName(
|
|
||||||
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
|
|
||||||
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
|
|
||||||
this._schemaRegistry.validateProperty(propName);
|
|
||||||
if (report.error) {
|
|
||||||
this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
|
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
|
||||||
return directives.filter(directive => directive.directive.isComponent);
|
return directives.filter(directive => directive.directive.isComponent);
|
||||||
}
|
}
|
||||||
@ -1016,7 +644,11 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
||||||
const componentTypeNames = this._findComponentDirectiveNames(directives);
|
const componentTypeNames = this._findComponentDirectiveNames(directives);
|
||||||
if (componentTypeNames.length > 1) {
|
if (componentTypeNames.length > 1) {
|
||||||
this._reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan);
|
this._reportError(
|
||||||
|
`More than one component matched on this element.\n` +
|
||||||
|
`Make sure that only one component's selector can match a given element.\n` +
|
||||||
|
`Conflicting components: ${componentTypeNames.join(',')}`,
|
||||||
|
sourceSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1074,6 +706,28 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]) {
|
||||||
|
boundProps.forEach((boundProp) => {
|
||||||
|
if (boundProp.type === PropertyBindingType.Property &&
|
||||||
|
!this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
||||||
|
let errorMsg =
|
||||||
|
`Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`;
|
||||||
|
if (elementName.indexOf('-') > -1) {
|
||||||
|
errorMsg +=
|
||||||
|
`\n1. If '${elementName}' is an Angular component and it has '${boundProp.name}' input, then verify that it is part of this module.` +
|
||||||
|
`\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.\n`;
|
||||||
|
}
|
||||||
|
this._reportError(errorMsg, boundProp.sourceSpan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _reportError(
|
||||||
|
message: string, sourceSpan: ParseSourceSpan,
|
||||||
|
level: ParseErrorLevel = ParseErrorLevel.FATAL) {
|
||||||
|
this._targetErrors.push(new ParseError(sourceSpan, message, level));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NonBindableVisitor implements html.Visitor {
|
class NonBindableVisitor implements html.Visitor {
|
||||||
@ -1112,12 +766,6 @@ class NonBindableVisitor implements html.Visitor {
|
|||||||
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; }
|
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoundElementOrDirectiveProperty {
|
|
||||||
constructor(
|
|
||||||
public name: string, public expression: AST, public isLiteral: boolean,
|
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ElementOrDirectiveRef {
|
class ElementOrDirectiveRef {
|
||||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
}
|
}
|
||||||
@ -1185,21 +833,6 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][
|
|||||||
const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
|
const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
|
||||||
const NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
const NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
||||||
|
|
||||||
|
|
||||||
export class PipeCollector extends RecursiveAstVisitor {
|
|
||||||
pipes: Set<string> = new Set<string>();
|
|
||||||
visitPipe(ast: BindingPipe, context: any): any {
|
|
||||||
this.pipes.add(ast.name);
|
|
||||||
ast.exp.visit(this);
|
|
||||||
this.visitAll(ast.args, context);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isAnimationLabel(name: string): boolean {
|
|
||||||
return name[0] == '@';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isEmptyTextNode(node: html.Node): boolean {
|
function _isEmptyTextNode(node: html.Node): boolean {
|
||||||
return node instanceof html.Text && node.value.trim().length == 0;
|
return node instanceof html.Text && node.value.trim().length == 0;
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileTokenMetadata} from './compile_metadata';
|
import {isBlank, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||||
import {isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
|
|
||||||
import * as o from './output/output_ast';
|
|
||||||
|
|
||||||
export const MODULE_SUFFIX = '';
|
export const MODULE_SUFFIX = '';
|
||||||
|
|
||||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||||
@ -37,15 +35,19 @@ export function sanitizeIdentifier(name: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
||||||
if (isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return visitor.visitArray(<any[]>value, context);
|
return visitor.visitArray(<any[]>value, context);
|
||||||
} else if (isStrictStringMap(value)) {
|
|
||||||
return visitor.visitStringMap(<{[key: string]: any}>value, context);
|
|
||||||
} else if (isBlank(value) || isPrimitive(value)) {
|
|
||||||
return visitor.visitPrimitive(value, context);
|
|
||||||
} else {
|
|
||||||
return visitor.visitOther(value, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isStrictStringMap(value)) {
|
||||||
|
return visitor.visitStringMap(<{[key: string]: any}>value, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlank(value) || isPrimitive(value)) {
|
||||||
|
return visitor.visitPrimitive(value, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return visitor.visitOther(value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValueVisitor {
|
export interface ValueVisitor {
|
||||||
@ -68,25 +70,6 @@ export class ValueTransformer implements ValueVisitor {
|
|||||||
visitOther(value: any, context: any): any { return value; }
|
visitOther(value: any, context: any): any { return value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
|
||||||
if (path == null) {
|
|
||||||
return `asset:@angular/lib/${pkg}/index`;
|
|
||||||
} else {
|
|
||||||
return `asset:@angular/lib/${pkg}/src/${path}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
|
||||||
if (isPresent(token.value)) {
|
|
||||||
return o.literal(token.value);
|
|
||||||
} else if (token.identifierIsInstance) {
|
|
||||||
return o.importExpr(token.identifier)
|
|
||||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
|
||||||
} else {
|
|
||||||
return o.importExpr(token.identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SyncAsyncResult<T> {
|
export class SyncAsyncResult<T> {
|
||||||
constructor(public syncResult: T, public asyncResult: Promise<T> = null) {
|
constructor(public syncResult: T, public asyncResult: Promise<T> = null) {
|
||||||
if (!asyncResult) {
|
if (!asyncResult) {
|
||||||
|
@ -1,15 +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 {TemplateAst} from '../template_parser/template_ast';
|
|
||||||
|
|
||||||
import {CompileNode} from './compile_element';
|
|
||||||
|
|
||||||
export class CompileBinding {
|
|
||||||
constructor(public node: CompileNode, public sourceAst: TemplateAst) {}
|
|
||||||
}
|
|
@ -8,18 +8,20 @@
|
|||||||
|
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {ListWrapper, MapWrapper} from '../facade/collection';
|
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||||
|
import {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {convertValueToOutputAst} from '../output/value_util';
|
import {convertValueToOutputAst} from '../output/value_util';
|
||||||
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
|
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
|
||||||
import {createDiTokenExpression} from '../util';
|
|
||||||
|
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView, CompileViewRootNode} from './compile_view';
|
||||||
import {InjectMethodVars} from './constants';
|
import {InjectMethodVars, ViewProperties} from './constants';
|
||||||
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
|
||||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||||
|
|
||||||
export class CompileNode {
|
export class CompileNode {
|
||||||
@ -34,7 +36,7 @@ export class CompileNode {
|
|||||||
|
|
||||||
export class CompileElement extends CompileNode {
|
export class CompileElement extends CompileNode {
|
||||||
static createNull(): CompileElement {
|
static createNull(): CompileElement {
|
||||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
|
return new CompileElement(null, null, null, null, null, null, [], [], false, false, [], []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compViewExpr: o.Expression = null;
|
private _compViewExpr: o.Expression = null;
|
||||||
@ -42,13 +44,13 @@ export class CompileElement extends CompileNode {
|
|||||||
public elementRef: o.Expression;
|
public elementRef: o.Expression;
|
||||||
public injector: o.Expression;
|
public injector: o.Expression;
|
||||||
public instances = new Map<any, o.Expression>();
|
public instances = new Map<any, o.Expression>();
|
||||||
|
public directiveWrapperInstance = new Map<any, o.Expression>();
|
||||||
private _resolvedProviders: Map<any, ProviderAst>;
|
private _resolvedProviders: Map<any, ProviderAst>;
|
||||||
|
|
||||||
private _queryCount = 0;
|
private _queryCount = 0;
|
||||||
private _queries = new Map<any, CompileQuery[]>();
|
private _queries = new Map<any, CompileQuery[]>();
|
||||||
private _componentConstructorViewQueryLists: o.Expression[] = [];
|
|
||||||
|
|
||||||
public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
|
public contentNodesByNgContentIndex: Array<CompileViewRootNode>[] = null;
|
||||||
public embeddedView: CompileView;
|
public embeddedView: CompileView;
|
||||||
public referenceTokens: {[key: string]: CompileTokenMetadata};
|
public referenceTokens: {[key: string]: CompileTokenMetadata};
|
||||||
|
|
||||||
@ -57,7 +59,9 @@ export class CompileElement extends CompileNode {
|
|||||||
sourceAst: TemplateAst, public component: CompileDirectiveMetadata,
|
sourceAst: TemplateAst, public component: CompileDirectiveMetadata,
|
||||||
private _directives: CompileDirectiveMetadata[],
|
private _directives: CompileDirectiveMetadata[],
|
||||||
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
||||||
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
|
public hasEmbeddedView: boolean, references: ReferenceAst[],
|
||||||
|
private _targetDependencies:
|
||||||
|
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
||||||
super(parent, view, nodeIndex, renderNode, sourceAst);
|
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||||
this.referenceTokens = {};
|
this.referenceTokens = {};
|
||||||
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
||||||
@ -72,6 +76,9 @@ export class CompileElement extends CompileNode {
|
|||||||
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
|
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
|
||||||
this._createAppElement();
|
this._createAppElement();
|
||||||
}
|
}
|
||||||
|
if (this.component) {
|
||||||
|
this._createComponentFactoryResolver();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createAppElement() {
|
private _createAppElement() {
|
||||||
@ -90,9 +97,18 @@ export class CompileElement extends CompileNode {
|
|||||||
this.view.createMethod.addStmt(statement);
|
this.view.createMethod.addStmt(statement);
|
||||||
this.appElement = o.THIS_EXPR.prop(fieldName);
|
this.appElement = o.THIS_EXPR.prop(fieldName);
|
||||||
this.instances.set(resolveIdentifierToken(Identifiers.AppElement).reference, this.appElement);
|
this.instances.set(resolveIdentifierToken(Identifiers.AppElement).reference, this.appElement);
|
||||||
|
if (this.hasViewContainer) {
|
||||||
|
this.view.viewContainerAppElements.push(this.appElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public createComponentFactoryResolver(entryComponents: CompileIdentifierMetadata[]) {
|
private _createComponentFactoryResolver() {
|
||||||
|
let entryComponents =
|
||||||
|
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
||||||
|
var id = new CompileIdentifierMetadata({name: entryComponent.name});
|
||||||
|
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||||
|
return id;
|
||||||
|
});
|
||||||
if (!entryComponents || entryComponents.length === 0) {
|
if (!entryComponents || entryComponents.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,6 +171,8 @@ export class CompileElement extends CompileNode {
|
|||||||
// create all the provider instances, some in the view constructor,
|
// create all the provider instances, some in the view constructor,
|
||||||
// some as getters. We rely on the fact that they are already sorted topologically.
|
// some as getters. We rely on the fact that they are already sorted topologically.
|
||||||
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
|
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
|
||||||
|
const isDirectiveWrapper = resolvedProvider.providerType === ProviderAstType.Component ||
|
||||||
|
resolvedProvider.providerType === ProviderAstType.Directive;
|
||||||
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||||
if (isPresent(provider.useExisting)) {
|
if (isPresent(provider.useExisting)) {
|
||||||
return this._getDependency(
|
return this._getDependency(
|
||||||
@ -167,8 +185,16 @@ export class CompileElement extends CompileNode {
|
|||||||
} else if (isPresent(provider.useClass)) {
|
} else if (isPresent(provider.useClass)) {
|
||||||
var deps = provider.deps || provider.useClass.diDeps;
|
var deps = provider.deps || provider.useClass.diDeps;
|
||||||
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
return o.importExpr(provider.useClass)
|
if (isDirectiveWrapper) {
|
||||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
const directiveWrapperIdentifier = new CompileIdentifierMetadata(
|
||||||
|
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)});
|
||||||
|
this._targetDependencies.push(
|
||||||
|
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier));
|
||||||
|
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
|
||||||
|
} else {
|
||||||
|
return o.importExpr(provider.useClass)
|
||||||
|
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return convertValueToOutputAst(provider.useValue);
|
return convertValueToOutputAst(provider.useValue);
|
||||||
}
|
}
|
||||||
@ -177,7 +203,13 @@ export class CompileElement extends CompileNode {
|
|||||||
var instance = createProviderProperty(
|
var instance = createProviderProperty(
|
||||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||||
resolvedProvider.eager, this);
|
resolvedProvider.eager, this);
|
||||||
this.instances.set(resolvedProvider.token.reference, instance);
|
if (isDirectiveWrapper) {
|
||||||
|
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance);
|
||||||
|
this.instances.set(
|
||||||
|
resolvedProvider.token.reference, DirectiveWrapperExpressions.context(instance));
|
||||||
|
} else {
|
||||||
|
this.instances.set(resolvedProvider.token.reference, instance);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var i = 0; i < this._directives.length; i++) {
|
for (var i = 0; i < this._directives.length; i++) {
|
||||||
@ -188,9 +220,8 @@ export class CompileElement extends CompileNode {
|
|||||||
var queriesWithReads: _QueryWithRead[] = [];
|
var queriesWithReads: _QueryWithRead[] = [];
|
||||||
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
|
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
|
||||||
var queriesForProvider = this._getQueriesFor(resolvedProvider.token);
|
var queriesForProvider = this._getQueriesFor(resolvedProvider.token);
|
||||||
ListWrapper.addAll(
|
queriesWithReads.push(
|
||||||
queriesWithReads,
|
...queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
|
||||||
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
|
|
||||||
});
|
});
|
||||||
Object.keys(this.referenceTokens).forEach(varName => {
|
Object.keys(this.referenceTokens).forEach(varName => {
|
||||||
var token = this.referenceTokens[varName];
|
var token = this.referenceTokens[varName];
|
||||||
@ -202,9 +233,8 @@ export class CompileElement extends CompileNode {
|
|||||||
}
|
}
|
||||||
this.view.locals.set(varName, varValue);
|
this.view.locals.set(varName, varValue);
|
||||||
var varToken = new CompileTokenMetadata({value: varName});
|
var varToken = new CompileTokenMetadata({value: varName});
|
||||||
ListWrapper.addAll(
|
queriesWithReads.push(
|
||||||
queriesWithReads,
|
...this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
||||||
this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
|
||||||
});
|
});
|
||||||
queriesWithReads.forEach((queryWithRead) => {
|
queriesWithReads.forEach((queryWithRead) => {
|
||||||
var value: o.Expression;
|
var value: o.Expression;
|
||||||
@ -226,16 +256,9 @@ export class CompileElement extends CompileNode {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (isPresent(this.component)) {
|
if (isPresent(this.component)) {
|
||||||
var componentConstructorViewQueryList = isPresent(this.component) ?
|
|
||||||
o.literalArr(this._componentConstructorViewQueryLists) :
|
|
||||||
o.NULL_EXPR;
|
|
||||||
var compExpr = isPresent(this.getComponent()) ? this.getComponent() : o.NULL_EXPR;
|
var compExpr = isPresent(this.getComponent()) ? this.getComponent() : o.NULL_EXPR;
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
this.appElement
|
this.appElement.callMethod('initComponent', [compExpr, this._compViewExpr]).toStmt());
|
||||||
.callMethod(
|
|
||||||
'initComponent',
|
|
||||||
[compExpr, componentConstructorViewQueryList, this._compViewExpr])
|
|
||||||
.toStmt());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +285,7 @@ export class CompileElement extends CompileNode {
|
|||||||
this.view.createMethod, this.view.updateContentQueriesMethod)));
|
this.view.createMethod, this.view.updateContentQueriesMethod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
|
addContentNode(ngContentIndex: number, nodeExpr: CompileViewRootNode) {
|
||||||
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,8 +308,7 @@ export class CompileElement extends CompileNode {
|
|||||||
while (!currentEl.isNull()) {
|
while (!currentEl.isNull()) {
|
||||||
queries = currentEl._queries.get(token.reference);
|
queries = currentEl._queries.get(token.reference);
|
||||||
if (isPresent(queries)) {
|
if (isPresent(queries)) {
|
||||||
ListWrapper.addAll(
|
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||||
result, queries.filter((query) => query.meta.descendants || distance <= 1));
|
|
||||||
}
|
}
|
||||||
if (currentEl._directives.length > 0) {
|
if (currentEl._directives.length > 0) {
|
||||||
distance++;
|
distance++;
|
||||||
@ -295,7 +317,7 @@ export class CompileElement extends CompileNode {
|
|||||||
}
|
}
|
||||||
queries = this.view.componentView.viewQueries.get(token.reference);
|
queries = this.view.componentView.viewQueries.get(token.reference);
|
||||||
if (isPresent(queries)) {
|
if (isPresent(queries)) {
|
||||||
ListWrapper.addAll(result, queries);
|
result.push(...queries);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -312,20 +334,6 @@ export class CompileElement extends CompileNode {
|
|||||||
private _getLocalDependency(
|
private _getLocalDependency(
|
||||||
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
|
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
|
||||||
var result: o.Expression = null;
|
var result: o.Expression = null;
|
||||||
// constructor content query
|
|
||||||
if (!result && isPresent(dep.query)) {
|
|
||||||
result = this._addQuery(dep.query, null).queryList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructor view query
|
|
||||||
if (!result && isPresent(dep.viewQuery)) {
|
|
||||||
result = createQueryList(
|
|
||||||
dep.viewQuery, null,
|
|
||||||
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
|
|
||||||
this.view);
|
|
||||||
this._componentConstructorViewQueryLists.push(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(dep.token)) {
|
if (isPresent(dep.token)) {
|
||||||
// access builtins with special visibility
|
// access builtins with special visibility
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {TemplateAst} from '../template_parser/template_ast';
|
import {TemplateAst} from '../template_parser/template_ast';
|
||||||
@ -66,6 +65,8 @@ export class CompileMethod {
|
|||||||
this._newState = new _DebugState(nodeIndex, templateAst);
|
this._newState = new _DebugState(nodeIndex, templateAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
push(...stmts: o.Statement[]) { this.addStmts(stmts); }
|
||||||
|
|
||||||
addStmt(stmt: o.Statement) {
|
addStmt(stmt: o.Statement) {
|
||||||
this._updateDebugContextIfNeeded();
|
this._updateDebugContextIfNeeded();
|
||||||
this._bodyStatements.push(stmt);
|
this._bodyStatements.push(stmt);
|
||||||
@ -73,7 +74,7 @@ export class CompileMethod {
|
|||||||
|
|
||||||
addStmts(stmts: o.Statement[]) {
|
addStmts(stmts: o.Statement[]) {
|
||||||
this._updateDebugContextIfNeeded();
|
this._updateDebugContextIfNeeded();
|
||||||
ListWrapper.addAll(this._bodyStatements, stmts);
|
this._bodyStatements.push(...stmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
finish(): o.Statement[] { return this._bodyStatements; }
|
finish(): o.Statement[] { return this._bodyStatements; }
|
||||||
|
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
|
|
||||||
import {CompilePipeMetadata} from '../compile_metadata';
|
import {CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
import {createPureProxy, getPropertyInView, injectFromViewParentInjector} from './util';
|
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||||
|
|
||||||
export class CompilePipe {
|
export class CompilePipe {
|
||||||
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
|
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
|
||||||
@ -65,7 +66,8 @@ export class CompilePipe {
|
|||||||
createPureProxy(
|
createPureProxy(
|
||||||
pipeInstanceSeenFromPureProxy.prop('transform')
|
pipeInstanceSeenFromPureProxy.prop('transform')
|
||||||
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
||||||
args.length, purePipeProxyInstance, callingView);
|
args.length, purePipeProxyInstance,
|
||||||
|
{fields: callingView.fields, ctorStmts: callingView.createMethod});
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.castByValue))
|
return o.importExpr(resolveIdentifier(Identifiers.castByValue))
|
||||||
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
||||||
.callFn(args);
|
.callFn(args);
|
||||||
|
@ -8,33 +8,46 @@
|
|||||||
|
|
||||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
|
||||||
|
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {ListWrapper, MapWrapper} from '../facade/collection';
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ViewType} from '../private_import_core';
|
import {ViewType} from '../private_import_core';
|
||||||
|
|
||||||
import {CompileBinding} from './compile_binding';
|
|
||||||
import {CompileElement, CompileNode} from './compile_element';
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {CompilePipe} from './compile_pipe';
|
import {CompilePipe} from './compile_pipe';
|
||||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||||
import {EventHandlerVars} from './constants';
|
import {getPropertyInView, getViewFactoryName} from './util';
|
||||||
import {NameResolver} from './expression_converter';
|
|
||||||
import {createPureProxy, getPropertyInView, getViewFactoryName} from './util';
|
export enum CompileViewRootNodeType {
|
||||||
|
Node,
|
||||||
|
ViewContainer,
|
||||||
|
NgContent
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileViewRootNode {
|
||||||
|
constructor(
|
||||||
|
public type: CompileViewRootNodeType, public expr: o.Expression,
|
||||||
|
public ngContentIndex?: number) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class CompileView implements NameResolver {
|
export class CompileView implements NameResolver {
|
||||||
public viewType: ViewType;
|
public viewType: ViewType;
|
||||||
public viewQueries: Map<any, CompileQuery[]>;
|
public viewQueries: Map<any, CompileQuery[]>;
|
||||||
|
|
||||||
|
public viewChildren: o.Expression[] = [];
|
||||||
|
|
||||||
public nodes: CompileNode[] = [];
|
public nodes: CompileNode[] = [];
|
||||||
// root nodes or AppElements for ViewContainers
|
|
||||||
public rootNodesOrAppElements: o.Expression[] = [];
|
|
||||||
|
|
||||||
public bindings: CompileBinding[] = [];
|
public rootNodes: CompileViewRootNode[] = [];
|
||||||
|
public lastRenderNode: o.Expression = o.NULL_EXPR;
|
||||||
|
|
||||||
|
public viewContainerAppElements: o.Expression[] = [];
|
||||||
|
|
||||||
public classStatements: o.Statement[] = [];
|
|
||||||
public createMethod: CompileMethod;
|
public createMethod: CompileMethod;
|
||||||
public animationBindingsMethod: CompileMethod;
|
public animationBindingsMethod: CompileMethod;
|
||||||
public injectorGetMethod: CompileMethod;
|
public injectorGetMethod: CompileMethod;
|
||||||
@ -47,12 +60,12 @@ export class CompileView implements NameResolver {
|
|||||||
public afterViewLifecycleCallbacksMethod: CompileMethod;
|
public afterViewLifecycleCallbacksMethod: CompileMethod;
|
||||||
public destroyMethod: CompileMethod;
|
public destroyMethod: CompileMethod;
|
||||||
public detachMethod: CompileMethod;
|
public detachMethod: CompileMethod;
|
||||||
public eventHandlerMethods: o.ClassMethod[] = [];
|
public methods: o.ClassMethod[] = [];
|
||||||
|
|
||||||
|
public ctorStmts: o.Statement[] = [];
|
||||||
public fields: o.ClassField[] = [];
|
public fields: o.ClassField[] = [];
|
||||||
public getters: o.ClassGetter[] = [];
|
public getters: o.ClassGetter[] = [];
|
||||||
public disposables: o.Expression[] = [];
|
public disposables: o.Expression[] = [];
|
||||||
public subscriptions: o.Expression[] = [];
|
|
||||||
|
|
||||||
public componentView: CompileView;
|
public componentView: CompileView;
|
||||||
public purePipes = new Map<string, CompilePipe>();
|
public purePipes = new Map<string, CompilePipe>();
|
||||||
@ -102,22 +115,12 @@ export class CompileView implements NameResolver {
|
|||||||
var viewQueries = new Map<any, CompileQuery[]>();
|
var viewQueries = new Map<any, CompileQuery[]>();
|
||||||
if (this.viewType === ViewType.COMPONENT) {
|
if (this.viewType === ViewType.COMPONENT) {
|
||||||
var directiveInstance = o.THIS_EXPR.prop('context');
|
var directiveInstance = o.THIS_EXPR.prop('context');
|
||||||
ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => {
|
this.component.viewQueries.forEach((queryMeta, queryIndex) => {
|
||||||
var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
|
var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
|
||||||
var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
|
var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
|
||||||
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
||||||
addQueryToTokenMap(viewQueries, query);
|
addQueryToTokenMap(viewQueries, query);
|
||||||
});
|
});
|
||||||
var constructorViewQueryCount = 0;
|
|
||||||
this.component.type.diDeps.forEach((dep) => {
|
|
||||||
if (isPresent(dep.viewQuery)) {
|
|
||||||
var queryList = o.THIS_EXPR.prop('declarationAppElement')
|
|
||||||
.prop('componentConstructorViewQueries')
|
|
||||||
.key(o.literal(constructorViewQueryCount++));
|
|
||||||
var query = new CompileQuery(dep.viewQuery, queryList, null, this);
|
|
||||||
addQueryToTokenMap(viewQueries, query);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this.viewQueries = viewQueries;
|
this.viewQueries = viewQueries;
|
||||||
templateVariableBindings.forEach(
|
templateVariableBindings.forEach(
|
||||||
@ -149,48 +152,6 @@ export class CompileView implements NameResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createLiteralArray(values: o.Expression[]): o.Expression {
|
|
||||||
if (values.length === 0) {
|
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
|
||||||
}
|
|
||||||
var proxyExpr = o.THIS_EXPR.prop(`_arr_${this.literalArrayCount++}`);
|
|
||||||
var proxyParams: o.FnParam[] = [];
|
|
||||||
var proxyReturnEntries: o.Expression[] = [];
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var paramName = `p${i}`;
|
|
||||||
proxyParams.push(new o.FnParam(paramName));
|
|
||||||
proxyReturnEntries.push(o.variable(paramName));
|
|
||||||
}
|
|
||||||
createPureProxy(
|
|
||||||
o.fn(
|
|
||||||
proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))],
|
|
||||||
new o.ArrayType(o.DYNAMIC_TYPE)),
|
|
||||||
values.length, proxyExpr, this);
|
|
||||||
return proxyExpr.callFn(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
createLiteralMap(entries: [string, o.Expression][]): o.Expression {
|
|
||||||
if (entries.length === 0) {
|
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
|
||||||
}
|
|
||||||
const proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
|
|
||||||
const proxyParams: o.FnParam[] = [];
|
|
||||||
const proxyReturnEntries: [string, o.Expression][] = [];
|
|
||||||
const values: o.Expression[] = [];
|
|
||||||
for (var i = 0; i < entries.length; i++) {
|
|
||||||
const paramName = `p${i}`;
|
|
||||||
proxyParams.push(new o.FnParam(paramName));
|
|
||||||
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
|
||||||
values.push(<o.Expression>entries[i][1]);
|
|
||||||
}
|
|
||||||
createPureProxy(
|
|
||||||
o.fn(
|
|
||||||
proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))],
|
|
||||||
new o.MapType(o.DYNAMIC_TYPE)),
|
|
||||||
entries.length, proxyExpr, this);
|
|
||||||
return proxyExpr.callFn(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
afterNodes() {
|
afterNodes() {
|
||||||
MapWrapper.values(this.viewQueries)
|
MapWrapper.values(this.viewQueries)
|
||||||
.forEach(
|
.forEach(
|
||||||
|
@ -8,81 +8,32 @@
|
|||||||
|
|
||||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||||
import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
|
import {Identifiers} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
import {ChangeDetectorStatus, ViewType} from '../private_import_core';
|
import {ChangeDetectorStatus, ViewType} from '../private_import_core';
|
||||||
|
|
||||||
function _enumExpression(classIdentifier: CompileIdentifierMetadata, name: string): o.Expression {
|
|
||||||
return o.importExpr(resolveEnumIdentifier(classIdentifier, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ViewTypeEnum {
|
export class ViewTypeEnum {
|
||||||
static fromValue(value: ViewType): o.Expression {
|
static fromValue(value: ViewType): o.Expression {
|
||||||
const viewType = resolveIdentifier(Identifiers.ViewType);
|
return createEnumExpression(Identifiers.ViewType, value);
|
||||||
switch (value) {
|
|
||||||
case ViewType.HOST:
|
|
||||||
return _enumExpression(viewType, 'HOST');
|
|
||||||
case ViewType.COMPONENT:
|
|
||||||
return _enumExpression(viewType, 'COMPONENT');
|
|
||||||
case ViewType.EMBEDDED:
|
|
||||||
return _enumExpression(viewType, 'EMBEDDED');
|
|
||||||
default:
|
|
||||||
throw Error(`Inavlid ViewType value: ${value}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewEncapsulationEnum {
|
export class ViewEncapsulationEnum {
|
||||||
static fromValue(value: ViewEncapsulation): o.Expression {
|
static fromValue(value: ViewEncapsulation): o.Expression {
|
||||||
const viewEncapsulation = resolveIdentifier(Identifiers.ViewEncapsulation);
|
return createEnumExpression(Identifiers.ViewEncapsulation, value);
|
||||||
switch (value) {
|
|
||||||
case ViewEncapsulation.Emulated:
|
|
||||||
return _enumExpression(viewEncapsulation, 'Emulated');
|
|
||||||
case ViewEncapsulation.Native:
|
|
||||||
return _enumExpression(viewEncapsulation, 'Native');
|
|
||||||
case ViewEncapsulation.None:
|
|
||||||
return _enumExpression(viewEncapsulation, 'None');
|
|
||||||
default:
|
|
||||||
throw Error(`Inavlid ViewEncapsulation value: ${value}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetectionStrategyEnum {
|
export class ChangeDetectionStrategyEnum {
|
||||||
static fromValue(value: ChangeDetectionStrategy): o.Expression {
|
static fromValue(value: ChangeDetectionStrategy): o.Expression {
|
||||||
const changeDetectionStrategy = resolveIdentifier(Identifiers.ChangeDetectionStrategy);
|
return createEnumExpression(Identifiers.ChangeDetectionStrategy, value);
|
||||||
switch (value) {
|
|
||||||
case ChangeDetectionStrategy.OnPush:
|
|
||||||
return _enumExpression(changeDetectionStrategy, 'OnPush');
|
|
||||||
case ChangeDetectionStrategy.Default:
|
|
||||||
return _enumExpression(changeDetectionStrategy, 'Default');
|
|
||||||
default:
|
|
||||||
throw Error(`Inavlid ChangeDetectionStrategy value: ${value}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetectorStatusEnum {
|
export class ChangeDetectorStatusEnum {
|
||||||
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
|
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
|
||||||
const changeDetectorStatus = resolveIdentifier(Identifiers.ChangeDetectorStatus);
|
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);
|
||||||
switch (value) {
|
|
||||||
case ChangeDetectorStatus.CheckOnce:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'CheckOnce');
|
|
||||||
case ChangeDetectorStatus.Checked:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'Checked');
|
|
||||||
case ChangeDetectorStatus.CheckAlways:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'CheckAlways');
|
|
||||||
case ChangeDetectorStatus.Detached:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'Detached');
|
|
||||||
case ChangeDetectorStatus.Errored:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'Errored');
|
|
||||||
case ChangeDetectorStatus.Destroyed:
|
|
||||||
return _enumExpression(changeDetectorStatus, 'Destroyed');
|
|
||||||
default:
|
|
||||||
throw Error(`Inavlid ChangeDetectorStatus value: ${value}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +45,9 @@ export class ViewConstructorVars {
|
|||||||
|
|
||||||
export class ViewProperties {
|
export class ViewProperties {
|
||||||
static renderer = o.THIS_EXPR.prop('renderer');
|
static renderer = o.THIS_EXPR.prop('renderer');
|
||||||
static projectableNodes = o.THIS_EXPR.prop('projectableNodes');
|
|
||||||
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventHandlerVars { static event = o.variable('$event'); }
|
|
||||||
|
|
||||||
export class InjectMethodVars {
|
export class InjectMethodVars {
|
||||||
static token = o.variable('token');
|
static token = o.variable('token');
|
||||||
static requestNodeIndex = o.variable('requestNodeIndex');
|
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||||
@ -110,5 +58,4 @@ export class DetectChangesVars {
|
|||||||
static throwOnChange = o.variable(`throwOnChange`);
|
static throwOnChange = o.variable(`throwOnChange`);
|
||||||
static changes = o.variable(`changes`);
|
static changes = o.variable(`changes`);
|
||||||
static changed = o.variable(`changed`);
|
static changed = o.variable(`changed`);
|
||||||
static valUnwrapper = o.variable(`valUnwrapper`);
|
|
||||||
}
|
}
|
||||||
|
24
modules/@angular/compiler/src/view_compiler/deps.ts
Normal file
24
modules/@angular/compiler/src/view_compiler/deps.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @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 {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
|
export class ViewFactoryDependency {
|
||||||
|
constructor(
|
||||||
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComponentFactoryDependency {
|
||||||
|
constructor(
|
||||||
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectiveWrapperDependency {
|
||||||
|
constructor(
|
||||||
|
public dir: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
|
}
|
@ -6,195 +6,134 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
import {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
|
||||||
|
import {createInlineArray} from '../compiler_util/identifier_util';
|
||||||
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {identifierToken} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {CompileBinding} from './compile_binding';
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {EventHandlerVars, ViewProperties} from './constants';
|
import {ViewProperties} from './constants';
|
||||||
import {convertCdStatementToIr} from './expression_converter';
|
import {getHandleEventMethodName} from './util';
|
||||||
|
|
||||||
export class CompileEventListener {
|
export function bindOutputs(
|
||||||
private _method: CompileMethod;
|
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement,
|
||||||
private _hasComponentHostListener: boolean = false;
|
bindToRenderer: boolean): boolean {
|
||||||
private _methodName: string;
|
const usedEvents = collectEvents(boundEvents, directives);
|
||||||
private _eventParam: o.FnParam;
|
if (!usedEvents.size) {
|
||||||
private _actionResultExprs: o.Expression[] = [];
|
return false;
|
||||||
|
}
|
||||||
|
if (bindToRenderer) {
|
||||||
|
subscribeToRenderEvents(usedEvents, compileElement);
|
||||||
|
}
|
||||||
|
subscribeToDirectiveEvents(usedEvents, directives, compileElement);
|
||||||
|
generateHandleEventMethod(boundEvents, directives, compileElement);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static getOrCreate(
|
function collectEvents(
|
||||||
compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string,
|
boundEvents: BoundEventAst[], directives: DirectiveAst[]): Map<string, EventSummary> {
|
||||||
targetEventListeners: CompileEventListener[]): CompileEventListener {
|
const usedEvents = new Map<string, EventSummary>();
|
||||||
var listener = targetEventListeners.find(
|
boundEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
||||||
listener => listener.eventTarget == eventTarget && listener.eventName == eventName &&
|
directives.forEach((dirAst) => {
|
||||||
listener.eventPhase == eventPhase);
|
dirAst.hostEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
||||||
if (!listener) {
|
});
|
||||||
listener = new CompileEventListener(
|
return usedEvents;
|
||||||
compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length);
|
}
|
||||||
targetEventListeners.push(listener);
|
|
||||||
|
function subscribeToRenderEvents(
|
||||||
|
usedEvents: Map<string, EventSummary>, compileElement: CompileElement) {
|
||||||
|
const eventAndTargetExprs: o.Expression[] = [];
|
||||||
|
usedEvents.forEach((event) => {
|
||||||
|
if (!event.phase) {
|
||||||
|
eventAndTargetExprs.push(o.literal(event.name), o.literal(event.target));
|
||||||
}
|
}
|
||||||
return listener;
|
});
|
||||||
}
|
if (eventAndTargetExprs.length) {
|
||||||
|
const disposableVar = o.variable(`disposable_${compileElement.view.disposables.length}`);
|
||||||
get methodName() { return this._methodName; }
|
compileElement.view.disposables.push(disposableVar);
|
||||||
get isAnimation() { return !!this.eventPhase; }
|
compileElement.view.createMethod.addStmt(
|
||||||
|
disposableVar
|
||||||
constructor(
|
.set(o.importExpr(resolveIdentifier(Identifiers.subscribeToRenderElement)).callFn([
|
||||||
public compileElement: CompileElement, public eventTarget: string, public eventName: string,
|
o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
|
||||||
public eventPhase: string, listenerIndex: number) {
|
handleEventExpr(compileElement)
|
||||||
this._method = new CompileMethod(compileElement.view);
|
]))
|
||||||
this._methodName =
|
.toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||||
`_handle_${sanitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
|
||||||
this._eventParam = new o.FnParam(
|
|
||||||
EventHandlerVars.event.name,
|
|
||||||
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
|
|
||||||
}
|
|
||||||
|
|
||||||
addAction(
|
|
||||||
hostEvent: BoundEventAst, directive: CompileDirectiveMetadata,
|
|
||||||
directiveInstance: o.Expression) {
|
|
||||||
if (isPresent(directive) && directive.isComponent) {
|
|
||||||
this._hasComponentHostListener = true;
|
|
||||||
}
|
|
||||||
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
|
||||||
var context = directiveInstance || this.compileElement.view.componentContext;
|
|
||||||
var actionStmts = convertCdStatementToIr(
|
|
||||||
this.compileElement.view, context, hostEvent.handler, this.compileElement.nodeIndex);
|
|
||||||
var lastIndex = actionStmts.length - 1;
|
|
||||||
if (lastIndex >= 0) {
|
|
||||||
var lastStatement = actionStmts[lastIndex];
|
|
||||||
var returnExpr = convertStmtIntoExpression(lastStatement);
|
|
||||||
var preventDefaultVar = o.variable(`pd_${this._actionResultExprs.length}`);
|
|
||||||
this._actionResultExprs.push(preventDefaultVar);
|
|
||||||
if (isPresent(returnExpr)) {
|
|
||||||
// Note: We need to cast the result of the method call to dynamic,
|
|
||||||
// as it might be a void method!
|
|
||||||
actionStmts[lastIndex] =
|
|
||||||
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
|
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._method.addStmts(actionStmts);
|
|
||||||
}
|
|
||||||
|
|
||||||
finishMethod() {
|
|
||||||
var markPathToRootStart = this._hasComponentHostListener ?
|
|
||||||
this.compileElement.appElement.prop('componentView') :
|
|
||||||
o.THIS_EXPR;
|
|
||||||
var resultExpr: o.Expression = o.literal(true);
|
|
||||||
this._actionResultExprs.forEach((expr) => { resultExpr = resultExpr.and(expr); });
|
|
||||||
var stmts =
|
|
||||||
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
|
|
||||||
.concat(this._method.finish())
|
|
||||||
.concat([new o.ReturnStatement(resultExpr)]);
|
|
||||||
// private is fine here as no child view will reference the event handler...
|
|
||||||
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
|
|
||||||
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
|
|
||||||
}
|
|
||||||
|
|
||||||
listenToRenderer() {
|
|
||||||
var listenExpr: o.Expression;
|
|
||||||
var eventListener = o.THIS_EXPR.callMethod(
|
|
||||||
'eventHandler',
|
|
||||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
|
||||||
if (isPresent(this.eventTarget)) {
|
|
||||||
listenExpr = ViewProperties.renderer.callMethod(
|
|
||||||
'listenGlobal', [o.literal(this.eventTarget), o.literal(this.eventName), eventListener]);
|
|
||||||
} else {
|
|
||||||
listenExpr = ViewProperties.renderer.callMethod(
|
|
||||||
'listen', [this.compileElement.renderNode, o.literal(this.eventName), eventListener]);
|
|
||||||
}
|
|
||||||
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
|
|
||||||
this.compileElement.view.disposables.push(disposable);
|
|
||||||
// private is fine here as no child view will reference the event handler...
|
|
||||||
this.compileElement.view.createMethod.addStmt(
|
|
||||||
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
|
||||||
}
|
|
||||||
|
|
||||||
listenToAnimation(animationTransitionVar: o.ReadVarExpr): o.Statement {
|
|
||||||
const callbackMethod = this.eventPhase == 'start' ? 'onStart' : 'onDone';
|
|
||||||
return animationTransitionVar
|
|
||||||
.callMethod(
|
|
||||||
callbackMethod,
|
|
||||||
[o.THIS_EXPR.prop(this.methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])])
|
|
||||||
.toStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
listenToDirective(directiveInstance: o.Expression, observablePropName: string) {
|
|
||||||
var subscription = o.variable(`subscription_${this.compileElement.view.subscriptions.length}`);
|
|
||||||
this.compileElement.view.subscriptions.push(subscription);
|
|
||||||
var eventListener = o.THIS_EXPR.callMethod(
|
|
||||||
'eventHandler',
|
|
||||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
|
||||||
this.compileElement.view.createMethod.addStmt(
|
|
||||||
subscription
|
|
||||||
.set(directiveInstance.prop(observablePropName)
|
|
||||||
.callMethod(o.BuiltinMethod.SubscribeObservable, [eventListener]))
|
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function collectEventListeners(
|
function subscribeToDirectiveEvents(
|
||||||
hostEvents: BoundEventAst[], dirs: DirectiveAst[],
|
usedEvents: Map<string, EventSummary>, directives: DirectiveAst[],
|
||||||
compileElement: CompileElement): CompileEventListener[] {
|
compileElement: CompileElement) {
|
||||||
const eventListeners: CompileEventListener[] = [];
|
const usedEventNames = MapWrapper.keys(usedEvents);
|
||||||
|
directives.forEach((dirAst) => {
|
||||||
hostEvents.forEach((hostEvent) => {
|
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
||||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
compileElement.view.createMethod.addStmts(DirectiveWrapperExpressions.subscribe(
|
||||||
var listener = CompileEventListener.getOrCreate(
|
dirAst.directive, dirAst.hostProperties, usedEventNames, dirWrapper, o.THIS_EXPR,
|
||||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
handleEventExpr(compileElement)));
|
||||||
listener.addAction(hostEvent, null, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
dirs.forEach((directiveAst) => {
|
|
||||||
var directiveInstance =
|
|
||||||
compileElement.instances.get(identifierToken(directiveAst.directive.type).reference);
|
|
||||||
directiveAst.hostEvents.forEach((hostEvent) => {
|
|
||||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
|
||||||
var listener = CompileEventListener.getOrCreate(
|
|
||||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
|
||||||
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.forEach((listener) => listener.finishMethod());
|
|
||||||
return eventListeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bindDirectiveOutputs(
|
|
||||||
directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
|
||||||
eventListeners: CompileEventListener[]) {
|
|
||||||
Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
|
|
||||||
const eventName = directiveAst.directive.outputs[observablePropName];
|
|
||||||
|
|
||||||
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
|
|
||||||
listener.listenToDirective(directiveInstance, observablePropName);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
|
function generateHandleEventMethod(
|
||||||
eventListeners.forEach(listener => {
|
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement) {
|
||||||
// the animation listeners are handled within property_binder.ts to
|
const hasComponentHostListener =
|
||||||
// allow them to be placed next to the animation factory statements
|
directives.some((dirAst) => dirAst.hostEvents.some((event) => dirAst.directive.isComponent));
|
||||||
if (!listener.isAnimation) {
|
|
||||||
listener.listenToRenderer();
|
const markPathToRootStart =
|
||||||
|
hasComponentHostListener ? compileElement.appElement.prop('componentView') : o.THIS_EXPR;
|
||||||
|
const handleEventStmts = new CompileMethod(compileElement.view);
|
||||||
|
handleEventStmts.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
|
handleEventStmts.push(markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt());
|
||||||
|
const eventNameVar = o.variable('eventName');
|
||||||
|
const resultVar = o.variable('result');
|
||||||
|
handleEventStmts.push(resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
||||||
|
|
||||||
|
directives.forEach((dirAst, dirIdx) => {
|
||||||
|
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
||||||
|
if (dirAst.hostEvents.length > 0) {
|
||||||
|
handleEventStmts.push(
|
||||||
|
resultVar
|
||||||
|
.set(DirectiveWrapperExpressions
|
||||||
|
.handleEvent(
|
||||||
|
dirAst.hostEvents, dirWrapper, eventNameVar, EventHandlerVars.event)
|
||||||
|
.and(resultVar))
|
||||||
|
.toStmt());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
boundEvents.forEach((renderEvent, renderEventIdx) => {
|
||||||
|
const evalResult = convertActionBinding(
|
||||||
|
compileElement.view, compileElement.view, compileElement.view.componentContext,
|
||||||
|
renderEvent.handler, `sub_${renderEventIdx}`);
|
||||||
|
const trueStmts = evalResult.stmts;
|
||||||
|
if (evalResult.preventDefault) {
|
||||||
|
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
|
||||||
|
}
|
||||||
|
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
|
||||||
|
handleEventStmts.push(
|
||||||
|
new o.IfStmt(eventNameVar.equals(o.literal(renderEvent.fullName)), trueStmts));
|
||||||
|
});
|
||||||
|
|
||||||
|
handleEventStmts.push(new o.ReturnStatement(resultVar));
|
||||||
|
compileElement.view.methods.push(new o.ClassMethod(
|
||||||
|
getHandleEventMethodName(compileElement.nodeIndex),
|
||||||
|
[
|
||||||
|
new o.FnParam(eventNameVar.name, o.STRING_TYPE),
|
||||||
|
new o.FnParam(EventHandlerVars.event.name, o.DYNAMIC_TYPE)
|
||||||
|
],
|
||||||
|
handleEventStmts.finish(), o.BOOL_TYPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
function handleEventExpr(compileElement: CompileElement) {
|
||||||
if (stmt instanceof o.ExpressionStatement) {
|
const handleEventMethodName = getHandleEventMethodName(compileElement.nodeIndex);
|
||||||
return stmt.expr;
|
return o.THIS_EXPR.callMethod('eventHandler', [o.THIS_EXPR.prop(handleEventMethodName)]);
|
||||||
} else if (stmt instanceof o.ReturnStatement) {
|
|
||||||
return stmt.value;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeEventName(name: string): string {
|
type EventSummary = {
|
||||||
return name.replace(/[^a-zA-Z_]/g, '_');
|
name: string,
|
||||||
}
|
target: string,
|
||||||
|
phase: string
|
||||||
|
}
|
@ -7,40 +7,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {LifecycleHooks} from '../private_import_core';
|
import {LifecycleHooks} from '../private_import_core';
|
||||||
import {DirectiveAst, ProviderAst} from '../template_parser/template_ast';
|
import {DirectiveAst, ProviderAst, ProviderAstType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
import {DetectChangesVars} from './constants';
|
import {DetectChangesVars} from './constants';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var STATE_IS_NEVER_CHECKED = o.THIS_EXPR.prop('numberOfChecks').identical(new o.LiteralExpr(0));
|
var STATE_IS_NEVER_CHECKED = o.THIS_EXPR.prop('numberOfChecks').identical(new o.LiteralExpr(0));
|
||||||
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
|
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
|
||||||
|
|
||||||
export function bindDirectiveDetectChangesLifecycleCallbacks(
|
|
||||||
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
|
|
||||||
var view = compileElement.view;
|
|
||||||
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
|
||||||
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
|
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
|
|
||||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
|
||||||
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
|
|
||||||
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
|
|
||||||
}
|
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
|
|
||||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
|
||||||
STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
|
|
||||||
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
|
|
||||||
}
|
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
|
|
||||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
|
||||||
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bindDirectiveAfterContentLifecycleCallbacks(
|
export function bindDirectiveAfterContentLifecycleCallbacks(
|
||||||
directiveMeta: CompileDirectiveMetadata, directiveInstance: o.Expression,
|
directiveMeta: CompileDirectiveMetadata, directiveInstance: o.Expression,
|
||||||
compileElement: CompileElement) {
|
compileElement: CompileElement) {
|
||||||
@ -77,11 +55,24 @@ export function bindDirectiveAfterViewLifecycleCallbacks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveWrapperLifecycleCallbacks(
|
||||||
|
dir: DirectiveAst, directiveWrapperIntance: o.Expression, compileElement: CompileElement) {
|
||||||
|
compileElement.view.destroyMethod.addStmts(
|
||||||
|
DirectiveWrapperExpressions.ngOnDestroy(dir.directive, directiveWrapperIntance));
|
||||||
|
compileElement.view.detachMethod.addStmts(DirectiveWrapperExpressions.ngOnDetach(
|
||||||
|
dir.hostProperties, directiveWrapperIntance, o.THIS_EXPR,
|
||||||
|
compileElement.component ? compileElement.appElement.prop('componentView') : o.THIS_EXPR,
|
||||||
|
compileElement.renderNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function bindInjectableDestroyLifecycleCallbacks(
|
export function bindInjectableDestroyLifecycleCallbacks(
|
||||||
provider: ProviderAst, providerInstance: o.Expression, compileElement: CompileElement) {
|
provider: ProviderAst, providerInstance: o.Expression, compileElement: CompileElement) {
|
||||||
var onDestroyMethod = compileElement.view.destroyMethod;
|
var onDestroyMethod = compileElement.view.destroyMethod;
|
||||||
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
if (provider.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
if (provider.providerType !== ProviderAstType.Directive &&
|
||||||
|
provider.providerType !== ProviderAstType.Component &&
|
||||||
|
provider.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||||
onDestroyMethod.addStmt(providerInstance.callMethod('ngOnDestroy', []).toStmt());
|
onDestroyMethod.addStmt(providerInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,311 +8,148 @@
|
|||||||
|
|
||||||
import {SecurityContext} from '@angular/core';
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
|
import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util';
|
||||||
|
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
|
||||||
|
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||||
|
import {triggerAnimation, writeToRenderer} from '../compiler_util/render_util';
|
||||||
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
import * as cdAst from '../expression_parser/ast';
|
import * as cdAst from '../expression_parser/ast';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||||
import {BoundElementPropertyAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
import {BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||||
import {camelCaseToDashCase} from '../util';
|
import {camelCaseToDashCase} from '../util';
|
||||||
|
|
||||||
import {CompileBinding} from './compile_binding';
|
|
||||||
import {CompileElement, CompileNode} from './compile_element';
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
import {DetectChangesVars, ViewProperties} from './constants';
|
import {DetectChangesVars, ViewProperties} from './constants';
|
||||||
import {CompileEventListener} from './event_binder';
|
import {getHandleEventMethodName} from './util';
|
||||||
import {convertCdExpressionToIr, temporaryDeclaration} from './expression_converter';
|
|
||||||
|
|
||||||
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
|
||||||
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
|
||||||
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
|
|
||||||
}
|
|
||||||
|
|
||||||
function bind(
|
|
||||||
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
|
||||||
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
|
||||||
method: CompileMethod, bindingIndex: number) {
|
|
||||||
var checkExpression = convertCdExpressionToIr(
|
|
||||||
view, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex);
|
|
||||||
if (!checkExpression.expression) {
|
|
||||||
// e.g. an empty expression was given
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkExpression.temporaryCount) {
|
|
||||||
for (let i = 0; i < checkExpression.temporaryCount; i++) {
|
|
||||||
method.addStmt(temporaryDeclaration(bindingIndex, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private is fine here as no child view will reference the cached value...
|
|
||||||
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
|
||||||
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
|
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
|
|
||||||
.toStmt());
|
|
||||||
|
|
||||||
if (checkExpression.needsValueUnwrapper) {
|
|
||||||
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
|
|
||||||
method.addStmt(initValueUnwrapperStmt);
|
|
||||||
}
|
|
||||||
method.addStmt(
|
|
||||||
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
|
|
||||||
|
|
||||||
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
|
||||||
DetectChangesVars.throwOnChange, fieldExpr, currValExpr
|
|
||||||
]);
|
|
||||||
if (checkExpression.needsValueUnwrapper) {
|
|
||||||
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
|
|
||||||
}
|
|
||||||
method.addStmt(new o.IfStmt(
|
|
||||||
condition,
|
|
||||||
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()])));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bindRenderText(
|
export function bindRenderText(
|
||||||
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView) {
|
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
|
||||||
var bindingIndex = view.bindings.length;
|
const valueField = createCheckBindingField(view);
|
||||||
view.bindings.push(new CompileBinding(compileNode, boundText));
|
const evalResult = convertPropertyBinding(
|
||||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
view, view, view.componentContext, boundText.value, valueField.bindingId);
|
||||||
var valueField = createBindFieldExpr(bindingIndex);
|
if (!evalResult) {
|
||||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
return null;
|
||||||
|
|
||||||
bind(
|
|
||||||
view, currValExpr, valueField, boundText.value, view.componentContext,
|
|
||||||
[o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod('setText', [compileNode.renderNode, currValExpr])
|
|
||||||
.toStmt()],
|
|
||||||
view.detectChangesRenderPropertiesMethod, bindingIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindAndWriteToRenderer(
|
|
||||||
boundProps: BoundElementPropertyAst[], context: o.Expression, compileElement: CompileElement,
|
|
||||||
isHostProp: boolean, eventListeners: CompileEventListener[]) {
|
|
||||||
var view = compileElement.view;
|
|
||||||
var renderNode = compileElement.renderNode;
|
|
||||||
boundProps.forEach((boundProp) => {
|
|
||||||
var bindingIndex = view.bindings.length;
|
|
||||||
view.bindings.push(new CompileBinding(compileElement, boundProp));
|
|
||||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
|
||||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
|
||||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
|
||||||
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
|
|
||||||
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
|
|
||||||
var updateStmts: o.Statement[] = [];
|
|
||||||
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
|
||||||
switch (boundProp.type) {
|
|
||||||
case PropertyBindingType.Property:
|
|
||||||
if (view.genConfig.logBindingUpdate) {
|
|
||||||
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, renderValue));
|
|
||||||
}
|
|
||||||
updateStmts.push(
|
|
||||||
o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod(
|
|
||||||
'setElementProperty', [renderNode, o.literal(boundProp.name), renderValue])
|
|
||||||
.toStmt());
|
|
||||||
break;
|
|
||||||
case PropertyBindingType.Attribute:
|
|
||||||
renderValue =
|
|
||||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
|
||||||
updateStmts.push(
|
|
||||||
o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod(
|
|
||||||
'setElementAttribute', [renderNode, o.literal(boundProp.name), renderValue])
|
|
||||||
.toStmt());
|
|
||||||
break;
|
|
||||||
case PropertyBindingType.Class:
|
|
||||||
updateStmts.push(
|
|
||||||
o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod('setElementClass', [renderNode, o.literal(boundProp.name), renderValue])
|
|
||||||
.toStmt());
|
|
||||||
break;
|
|
||||||
case PropertyBindingType.Style:
|
|
||||||
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
|
||||||
if (isPresent(boundProp.unit)) {
|
|
||||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
|
||||||
updateStmts.push(
|
|
||||||
o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
|
|
||||||
.toStmt());
|
|
||||||
break;
|
|
||||||
case PropertyBindingType.Animation:
|
|
||||||
compileMethod = view.animationBindingsMethod;
|
|
||||||
const detachStmts: o.Statement[] = [];
|
|
||||||
|
|
||||||
const animationName = boundProp.name;
|
|
||||||
const targetViewExpr: o.Expression =
|
|
||||||
isHostProp ? compileElement.appElement.prop('componentView') : o.THIS_EXPR;
|
|
||||||
|
|
||||||
const animationFnExpr =
|
|
||||||
targetViewExpr.prop('componentType').prop('animations').key(o.literal(animationName));
|
|
||||||
|
|
||||||
// it's important to normalize the void value as `void` explicitly
|
|
||||||
// so that the styles data can be obtained from the stringmap
|
|
||||||
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
|
||||||
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
|
|
||||||
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
|
||||||
|
|
||||||
updateStmts.push(
|
|
||||||
animationTransitionVar
|
|
||||||
.set(animationFnExpr.callFn([
|
|
||||||
o.THIS_EXPR, renderNode, oldRenderValue.equals(unitializedValue)
|
|
||||||
.conditional(emptyStateValue, oldRenderValue),
|
|
||||||
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
|
||||||
]))
|
|
||||||
.toDeclStmt());
|
|
||||||
|
|
||||||
detachStmts.push(animationTransitionVar
|
|
||||||
.set(animationFnExpr.callFn(
|
|
||||||
[o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue]))
|
|
||||||
.toDeclStmt());
|
|
||||||
|
|
||||||
eventListeners.forEach(listener => {
|
|
||||||
if (listener.isAnimation && listener.eventName === animationName) {
|
|
||||||
let animationStmt = listener.listenToAnimation(animationTransitionVar);
|
|
||||||
updateStmts.push(animationStmt);
|
|
||||||
detachStmts.push(animationStmt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
view.detachMethod.addStmts(detachStmts);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bind(
|
|
||||||
view, currValExpr, fieldExpr, boundProp.value, context, updateStmts, compileMethod,
|
|
||||||
view.bindings.length);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizedValue(
|
|
||||||
boundProp: BoundElementPropertyAst, renderValue: o.Expression): o.Expression {
|
|
||||||
let enumValue: string;
|
|
||||||
switch (boundProp.securityContext) {
|
|
||||||
case SecurityContext.NONE:
|
|
||||||
return renderValue; // No sanitization needed.
|
|
||||||
case SecurityContext.HTML:
|
|
||||||
enumValue = 'HTML';
|
|
||||||
break;
|
|
||||||
case SecurityContext.STYLE:
|
|
||||||
enumValue = 'STYLE';
|
|
||||||
break;
|
|
||||||
case SecurityContext.SCRIPT:
|
|
||||||
enumValue = 'SCRIPT';
|
|
||||||
break;
|
|
||||||
case SecurityContext.URL:
|
|
||||||
enumValue = 'URL';
|
|
||||||
break;
|
|
||||||
case SecurityContext.RESOURCE_URL:
|
|
||||||
enumValue = 'RESOURCE_URL';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`internal error, unexpected SecurityContext ${boundProp.securityContext}.`);
|
|
||||||
}
|
}
|
||||||
let ctx = ViewProperties.viewUtils.prop('sanitizer');
|
|
||||||
let args =
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||||
[o.importExpr(resolveIdentifier(Identifiers.SecurityContext)).prop(enumValue), renderValue];
|
view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt(
|
||||||
return ctx.callMethod('sanitize', args);
|
evalResult, valueField.expression, DetectChangesVars.throwOnChange,
|
||||||
|
[o.THIS_EXPR.prop('renderer')
|
||||||
|
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
|
||||||
|
.toStmt()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindRenderInputs(
|
export function bindRenderInputs(
|
||||||
boundProps: BoundElementPropertyAst[], compileElement: CompileElement,
|
boundProps: BoundElementPropertyAst[], hasEvents: boolean, compileElement: CompileElement) {
|
||||||
eventListeners: CompileEventListener[]): void {
|
var view = compileElement.view;
|
||||||
bindAndWriteToRenderer(
|
var renderNode = compileElement.renderNode;
|
||||||
boundProps, compileElement.view.componentContext, compileElement, false, eventListeners);
|
boundProps.forEach((boundProp) => {
|
||||||
|
const bindingField = createCheckBindingField(view);
|
||||||
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||||
|
const evalResult = convertPropertyBinding(
|
||||||
|
view, view, compileElement.view.componentContext, boundProp.value, bindingField.bindingId);
|
||||||
|
if (!evalResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var checkBindingStmts: o.Statement[] = [];
|
||||||
|
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
||||||
|
switch (boundProp.type) {
|
||||||
|
case PropertyBindingType.Property:
|
||||||
|
case PropertyBindingType.Attribute:
|
||||||
|
case PropertyBindingType.Class:
|
||||||
|
case PropertyBindingType.Style:
|
||||||
|
checkBindingStmts.push(...writeToRenderer(
|
||||||
|
o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
|
||||||
|
view.genConfig.logBindingUpdate));
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Animation:
|
||||||
|
compileMethod = view.animationBindingsMethod;
|
||||||
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
|
o.THIS_EXPR, o.THIS_EXPR, boundProp,
|
||||||
|
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.noop)))
|
||||||
|
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
|
||||||
|
compileElement.renderNode, evalResult.currValExpr, bindingField.expression);
|
||||||
|
checkBindingStmts.push(...updateStmts);
|
||||||
|
view.detachMethod.addStmts(detachStmts);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
compileMethod.addStmts(createCheckBindingStmt(
|
||||||
|
evalResult, bindingField.expression, DetectChangesVars.throwOnChange, checkBindingStmts));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindDirectiveHostProps(
|
export function bindDirectiveHostProps(
|
||||||
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement,
|
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
|
||||||
eventListeners: CompileEventListener[]): void {
|
compileElement: CompileElement, elementName: string,
|
||||||
bindAndWriteToRenderer(
|
schemaRegistry: ElementSchemaRegistry): void {
|
||||||
directiveAst.hostProperties, directiveInstance, compileElement, true, eventListeners);
|
// We need to provide the SecurityContext for properties that could need sanitization.
|
||||||
|
const runtimeSecurityCtxExprs =
|
||||||
|
directiveAst.hostProperties.filter(boundProp => boundProp.needsRuntimeSecurityContext)
|
||||||
|
.map((boundProp) => {
|
||||||
|
let ctx: SecurityContext;
|
||||||
|
switch (boundProp.type) {
|
||||||
|
case PropertyBindingType.Property:
|
||||||
|
ctx = schemaRegistry.securityContext(elementName, boundProp.name, false);
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Attribute:
|
||||||
|
ctx = schemaRegistry.securityContext(elementName, boundProp.name, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: Only property / attribute bindings can have an unknown security context! Binding ${boundProp.name}`);
|
||||||
|
}
|
||||||
|
return createEnumExpression(Identifiers.SecurityContext, ctx);
|
||||||
|
});
|
||||||
|
compileElement.view.detectChangesRenderPropertiesMethod.addStmts(
|
||||||
|
DirectiveWrapperExpressions.checkHost(
|
||||||
|
directiveAst.hostProperties, directiveWrapperInstance, o.THIS_EXPR,
|
||||||
|
compileElement.component ? compileElement.appElement.prop('componentView') : o.THIS_EXPR,
|
||||||
|
compileElement.renderNode, DetectChangesVars.throwOnChange, runtimeSecurityCtxExprs));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindDirectiveInputs(
|
export function bindDirectiveInputs(
|
||||||
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
|
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number,
|
||||||
if (directiveAst.inputs.length === 0) {
|
compileElement: CompileElement) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
var view = compileElement.view;
|
var view = compileElement.view;
|
||||||
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
|
|
||||||
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
|
directiveAst.inputs.forEach((input, inputIdx) => {
|
||||||
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
// Note: We can't use `fields.length` here, as we are not adding a field!
|
||||||
|
const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
|
||||||
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||||
|
const evalResult =
|
||||||
|
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
||||||
|
if (!evalResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
detectChangesInInputsMethod.addStmts(evalResult.stmts);
|
||||||
|
detectChangesInInputsMethod.addStmt(
|
||||||
|
directiveWrapperInstance
|
||||||
|
.callMethod(
|
||||||
|
`check_${input.directiveName}`,
|
||||||
|
[
|
||||||
|
evalResult.currValExpr, DetectChangesVars.throwOnChange,
|
||||||
|
evalResult.forceUpdate || o.literal(false)
|
||||||
|
])
|
||||||
|
.toStmt());
|
||||||
|
});
|
||||||
var isOnPushComp = directiveAst.directive.isComponent &&
|
var isOnPushComp = directiveAst.directive.isComponent &&
|
||||||
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||||
if (calcChangesMap) {
|
let directiveDetectChangesExpr = DirectiveWrapperExpressions.ngDoCheck(
|
||||||
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
|
directiveWrapperInstance, o.THIS_EXPR, compileElement.renderNode,
|
||||||
}
|
DetectChangesVars.throwOnChange);
|
||||||
if (isOnPushComp) {
|
const directiveDetectChangesStmt = isOnPushComp ?
|
||||||
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
|
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')
|
||||||
}
|
.callMethod('markAsCheckOnce', [])
|
||||||
directiveAst.inputs.forEach((input) => {
|
.toStmt()]) :
|
||||||
var bindingIndex = view.bindings.length;
|
directiveDetectChangesExpr.toStmt();
|
||||||
view.bindings.push(new CompileBinding(compileElement, input));
|
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
|
||||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
|
||||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
|
||||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
|
||||||
var statements: o.Statement[] =
|
|
||||||
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
|
|
||||||
if (calcChangesMap) {
|
|
||||||
statements.push(new o.IfStmt(
|
|
||||||
DetectChangesVars.changes.identical(o.NULL_EXPR),
|
|
||||||
[DetectChangesVars.changes
|
|
||||||
.set(o.literalMap(
|
|
||||||
[], new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))))
|
|
||||||
.toStmt()]));
|
|
||||||
statements.push(DetectChangesVars.changes.key(o.literal(input.directiveName))
|
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
|
|
||||||
.instantiate([fieldExpr, currValExpr]))
|
|
||||||
.toStmt());
|
|
||||||
}
|
|
||||||
if (isOnPushComp) {
|
|
||||||
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
|
|
||||||
}
|
|
||||||
if (view.genConfig.logBindingUpdate) {
|
|
||||||
statements.push(
|
|
||||||
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
|
|
||||||
}
|
|
||||||
bind(
|
|
||||||
view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
|
|
||||||
detectChangesInInputsMethod, bindingIndex);
|
|
||||||
});
|
|
||||||
if (isOnPushComp) {
|
|
||||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
|
|
||||||
compileElement.appElement.prop('componentView').callMethod('markAsCheckOnce', []).toStmt()
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function logBindingUpdateStmt(
|
|
||||||
renderNode: o.Expression, propName: string, value: o.Expression): o.Statement {
|
|
||||||
const tryStmt =
|
|
||||||
o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod(
|
|
||||||
'setBindingDebugInfo',
|
|
||||||
[
|
|
||||||
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
|
|
||||||
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
|
|
||||||
])
|
|
||||||
.toStmt();
|
|
||||||
|
|
||||||
const catchStmt = o.THIS_EXPR.prop('renderer')
|
|
||||||
.callMethod(
|
|
||||||
'setBindingDebugInfo',
|
|
||||||
[
|
|
||||||
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
|
|
||||||
o.literal('[ERROR] Exception while trying to serialize the value')
|
|
||||||
])
|
|
||||||
.toStmt();
|
|
||||||
|
|
||||||
return new o.TryCatchStmt([tryStmt], [catchStmt]);
|
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
|
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {createDiTokenExpression} from '../util';
|
|
||||||
|
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
|
|
||||||
@ -30,15 +30,28 @@ export function getPropertyInView(
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`Internal error: Could not calculate a property in a parent view: ${property}`);
|
`Internal error: Could not calculate a property in a parent view: ${property}`);
|
||||||
}
|
}
|
||||||
if (property instanceof o.ReadPropExpr) {
|
return property.visitExpression(new _ReplaceViewTransformer(viewProp, definedView), null);
|
||||||
let readPropExpr: o.ReadPropExpr = property;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReplaceViewTransformer extends o.ExpressionTransformer {
|
||||||
|
constructor(private _viewExpr: o.Expression, private _view: CompileView) { super(); }
|
||||||
|
private _isThis(expr: o.Expression): boolean {
|
||||||
|
return expr instanceof o.ReadVarExpr && expr.builtin === o.BuiltinVar.This;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitReadVarExpr(ast: o.ReadVarExpr, context: any): any {
|
||||||
|
return this._isThis(ast) ? this._viewExpr : ast;
|
||||||
|
}
|
||||||
|
visitReadPropExpr(ast: o.ReadPropExpr, context: any): any {
|
||||||
|
if (this._isThis(ast.receiver)) {
|
||||||
// Note: Don't cast for members of the AppView base class...
|
// Note: Don't cast for members of the AppView base class...
|
||||||
if (definedView.fields.some((field) => field.name == readPropExpr.name) ||
|
if (this._view.fields.some((field) => field.name == ast.name) ||
|
||||||
definedView.getters.some((field) => field.name == readPropExpr.name)) {
|
this._view.getters.some((field) => field.name == ast.name)) {
|
||||||
viewProp = viewProp.cast(definedView.classType);
|
return this._viewExpr.cast(this._view.classType).prop(ast.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
|
return super.visitReadPropExpr(ast, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,38 +69,6 @@ export function getViewFactoryName(
|
|||||||
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
export function getHandleEventMethodName(elementIndex: number): string {
|
||||||
var lastNonArrayExpressions: o.Expression[] = [];
|
return `handleEvent_${elementIndex}`;
|
||||||
var result: o.Expression = o.literalArr([]);
|
}
|
||||||
for (var i = 0; i < expressions.length; i++) {
|
|
||||||
var expr = expressions[i];
|
|
||||||
if (expr.type instanceof o.ArrayType) {
|
|
||||||
if (lastNonArrayExpressions.length > 0) {
|
|
||||||
result =
|
|
||||||
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
|
||||||
lastNonArrayExpressions = [];
|
|
||||||
}
|
|
||||||
result = result.callMethod(o.BuiltinMethod.ConcatArray, [expr]);
|
|
||||||
} else {
|
|
||||||
lastNonArrayExpressions.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lastNonArrayExpressions.length > 0) {
|
|
||||||
result =
|
|
||||||
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPureProxy(
|
|
||||||
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr, view: CompileView) {
|
|
||||||
view.fields.push(new o.ClassField(pureProxyProp.name, null));
|
|
||||||
var pureProxyId =
|
|
||||||
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
|
||||||
if (!pureProxyId) {
|
|
||||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
|
||||||
}
|
|
||||||
view.createMethod.addStmt(o.THIS_EXPR.prop(pureProxyProp.name)
|
|
||||||
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
|
||||||
.toStmt());
|
|
||||||
}
|
|
@ -6,16 +6,18 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
|
import {bindOutputs} from './event_binder';
|
||||||
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
|
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveWrapperLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
|
||||||
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
|
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
|
||||||
|
|
||||||
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
|
export function bindView(
|
||||||
var visitor = new ViewBinderVisitor(view);
|
view: CompileView, parsedTemplate: TemplateAst[], schemaRegistry: ElementSchemaRegistry): void {
|
||||||
|
var visitor = new ViewBinderVisitor(view, schemaRegistry);
|
||||||
templateVisitAll(visitor, parsedTemplate);
|
templateVisitAll(visitor, parsedTemplate);
|
||||||
view.pipes.forEach(
|
view.pipes.forEach(
|
||||||
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
|
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
|
||||||
@ -24,7 +26,7 @@ export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void
|
|||||||
class ViewBinderVisitor implements TemplateAstVisitor {
|
class ViewBinderVisitor implements TemplateAstVisitor {
|
||||||
private _nodeIndex: number = 0;
|
private _nodeIndex: number = 0;
|
||||||
|
|
||||||
constructor(public view: CompileView) {}
|
constructor(public view: CompileView, private _schemaRegistry: ElementSchemaRegistry) {}
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||||
var node = this.view.nodes[this._nodeIndex++];
|
var node = this.view.nodes[this._nodeIndex++];
|
||||||
@ -40,29 +42,29 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||||
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||||
var eventListeners: CompileEventListener[] = [];
|
const hasEvents = bindOutputs(ast.outputs, ast.directives, compileElement, true);
|
||||||
collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => {
|
bindRenderInputs(ast.inputs, hasEvents, compileElement);
|
||||||
eventListeners.push(entry);
|
ast.directives.forEach((directiveAst, dirIndex) => {
|
||||||
});
|
|
||||||
bindRenderInputs(ast.inputs, compileElement, eventListeners);
|
|
||||||
bindRenderOutputs(eventListeners);
|
|
||||||
ast.directives.forEach((directiveAst) => {
|
|
||||||
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
||||||
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
var directiveWrapperInstance =
|
||||||
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||||
|
bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
|
||||||
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
|
bindDirectiveHostProps(
|
||||||
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
directiveAst, directiveWrapperInstance, compileElement, ast.name, this._schemaRegistry);
|
||||||
});
|
});
|
||||||
templateVisitAll(this, ast.children, compileElement);
|
templateVisitAll(this, ast.children, compileElement);
|
||||||
// afterContent and afterView lifecycles need to be called bottom up
|
// afterContent and afterView lifecycles need to be called bottom up
|
||||||
// so that children are notified before parents
|
// so that children are notified before parents
|
||||||
ast.directives.forEach((directiveAst) => {
|
ast.directives.forEach((directiveAst) => {
|
||||||
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
||||||
|
var directiveWrapperInstance =
|
||||||
|
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||||
bindDirectiveAfterContentLifecycleCallbacks(
|
bindDirectiveAfterContentLifecycleCallbacks(
|
||||||
directiveAst.directive, directiveInstance, compileElement);
|
directiveAst.directive, directiveInstance, compileElement);
|
||||||
bindDirectiveAfterViewLifecycleCallbacks(
|
bindDirectiveAfterViewLifecycleCallbacks(
|
||||||
directiveAst.directive, directiveInstance, compileElement);
|
directiveAst.directive, directiveInstance, compileElement);
|
||||||
|
bindDirectiveWrapperLifecycleCallbacks(
|
||||||
|
directiveAst, directiveWrapperInstance, compileElement);
|
||||||
});
|
});
|
||||||
ast.providers.forEach((providerAst) => {
|
ast.providers.forEach((providerAst) => {
|
||||||
var providerInstance = compileElement.instances.get(providerAst.token.reference);
|
var providerInstance = compileElement.instances.get(providerAst.token.reference);
|
||||||
@ -73,22 +75,25 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||||
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||||
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
|
bindOutputs(ast.outputs, ast.directives, compileElement, false);
|
||||||
ast.directives.forEach((directiveAst) => {
|
ast.directives.forEach((directiveAst, dirIndex) => {
|
||||||
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
||||||
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
var directiveWrapperInstance =
|
||||||
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||||
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
|
||||||
|
|
||||||
bindDirectiveAfterContentLifecycleCallbacks(
|
bindDirectiveAfterContentLifecycleCallbacks(
|
||||||
directiveAst.directive, directiveInstance, compileElement);
|
directiveAst.directive, directiveInstance, compileElement);
|
||||||
bindDirectiveAfterViewLifecycleCallbacks(
|
bindDirectiveAfterViewLifecycleCallbacks(
|
||||||
directiveAst.directive, directiveInstance, compileElement);
|
directiveAst.directive, directiveInstance, compileElement);
|
||||||
|
bindDirectiveWrapperLifecycleCallbacks(
|
||||||
|
directiveAst, directiveWrapperInstance, compileElement);
|
||||||
});
|
});
|
||||||
ast.providers.forEach((providerAst) => {
|
ast.providers.forEach((providerAst) => {
|
||||||
var providerInstance = compileElement.instances.get(providerAst.token.reference);
|
var providerInstance = compileElement.instances.get(providerAst.token.reference);
|
||||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||||
});
|
});
|
||||||
bindView(compileElement.embeddedView, ast.children);
|
bindView(compileElement.embeddedView, ast.children, this._schemaRegistry);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,18 +9,21 @@
|
|||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||||
|
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||||
|
import {createClassStmt} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||||
import {createDiTokenExpression} from '../util';
|
|
||||||
|
|
||||||
import {CompileElement, CompileNode} from './compile_element';
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view';
|
||||||
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
|
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
|
||||||
import {createFlatArray, getViewFactoryName} from './util';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
|
||||||
|
import {getViewFactoryName} from './util';
|
||||||
|
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
@ -30,24 +33,18 @@ const NG_CONTAINER_TAG = 'ng-container';
|
|||||||
var parentRenderNodeVar = o.variable('parentRenderNode');
|
var parentRenderNodeVar = o.variable('parentRenderNode');
|
||||||
var rootSelectorVar = o.variable('rootSelector');
|
var rootSelectorVar = o.variable('rootSelector');
|
||||||
|
|
||||||
export class ViewFactoryDependency {
|
|
||||||
constructor(
|
|
||||||
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ComponentFactoryDependency {
|
|
||||||
constructor(
|
|
||||||
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function buildView(
|
export function buildView(
|
||||||
view: CompileView, template: TemplateAst[],
|
view: CompileView, template: TemplateAst[],
|
||||||
targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>): number {
|
targetDependencies:
|
||||||
|
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
|
||||||
|
number {
|
||||||
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
||||||
templateVisitAll(
|
const parentEl =
|
||||||
builderVisitor, template,
|
view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent;
|
||||||
view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent);
|
templateVisitAll(builderVisitor, template, parentEl);
|
||||||
|
if (view.viewType === ViewType.EMBEDDED || view.viewType === ViewType.HOST) {
|
||||||
|
view.lastRenderNode = builderVisitor.getOrCreateLastRenderNode();
|
||||||
|
}
|
||||||
return builderVisitor.nestedViewCount;
|
return builderVisitor.nestedViewCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +63,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public view: CompileView,
|
public view: CompileView,
|
||||||
public targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
|
public targetDependencies:
|
||||||
|
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
||||||
|
|
||||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||||
|
|
||||||
@ -79,10 +77,16 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
if (this._isRootNode(parent)) {
|
if (this._isRootNode(parent)) {
|
||||||
// store appElement as root node only for ViewContainers
|
// store appElement as root node only for ViewContainers
|
||||||
if (this.view.viewType !== ViewType.COMPONENT) {
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||||
this.view.rootNodesOrAppElements.push(vcAppEl || node.renderNode);
|
this.view.rootNodes.push(new CompileViewRootNode(
|
||||||
|
vcAppEl ? CompileViewRootNodeType.ViewContainer : CompileViewRootNodeType.Node,
|
||||||
|
vcAppEl || node.renderNode));
|
||||||
}
|
}
|
||||||
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
||||||
parent.addContentNode(ngContentIndex, vcAppEl || node.renderNode);
|
parent.addContentNode(
|
||||||
|
ngContentIndex,
|
||||||
|
new CompileViewRootNode(
|
||||||
|
vcAppEl ? CompileViewRootNodeType.ViewContainer : CompileViewRootNodeType.Node,
|
||||||
|
vcAppEl || node.renderNode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +107,23 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrCreateLastRenderNode(): o.Expression {
|
||||||
|
const view = this.view;
|
||||||
|
if (view.rootNodes.length === 0 ||
|
||||||
|
view.rootNodes[view.rootNodes.length - 1].type !== CompileViewRootNodeType.Node) {
|
||||||
|
var fieldName = `_el_${view.nodes.length}`;
|
||||||
|
view.fields.push(
|
||||||
|
new o.ClassField(fieldName, o.importType(view.genConfig.renderTypes.renderElement)));
|
||||||
|
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName)
|
||||||
|
.set(ViewProperties.renderer.callMethod(
|
||||||
|
'createTemplateAnchor', [o.NULL_EXPR, o.NULL_EXPR]))
|
||||||
|
.toStmt());
|
||||||
|
view.rootNodes.push(
|
||||||
|
new CompileViewRootNode(CompileViewRootNodeType.Node, o.THIS_EXPR.prop(fieldName)));
|
||||||
|
}
|
||||||
|
return view.rootNodes[view.rootNodes.length - 1].expr;
|
||||||
|
}
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||||
return this._visitText(ast, '', parent);
|
return this._visitText(ast, '', parent);
|
||||||
}
|
}
|
||||||
@ -135,9 +156,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
// have debug information for them...
|
// have debug information for them...
|
||||||
this.view.createMethod.resetDebugInfo(null, ast);
|
this.view.createMethod.resetDebugInfo(null, ast);
|
||||||
var parentRenderNode = this._getParentRenderNode(parent);
|
var parentRenderNode = this._getParentRenderNode(parent);
|
||||||
var nodesExpression = ViewProperties.projectableNodes.key(
|
|
||||||
o.literal(ast.index),
|
|
||||||
new o.ArrayType(o.importType(this.view.genConfig.renderTypes.renderNode)));
|
|
||||||
if (parentRenderNode !== o.NULL_EXPR) {
|
if (parentRenderNode !== o.NULL_EXPR) {
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
ViewProperties.renderer
|
ViewProperties.renderer
|
||||||
@ -145,18 +163,20 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
'projectNodes',
|
'projectNodes',
|
||||||
[
|
[
|
||||||
parentRenderNode,
|
parentRenderNode,
|
||||||
o.importExpr(resolveIdentifier(Identifiers.flattenNestedViewRenderNodes))
|
o.THIS_EXPR.callMethod('projectedNodes', [o.literal(ast.index)])
|
||||||
.callFn([nodesExpression])
|
|
||||||
])
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
} else if (this._isRootNode(parent)) {
|
} else if (this._isRootNode(parent)) {
|
||||||
if (this.view.viewType !== ViewType.COMPONENT) {
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||||
// store root nodes only for embedded/host views
|
// store root nodes only for embedded/host views
|
||||||
this.view.rootNodesOrAppElements.push(nodesExpression);
|
this.view.rootNodes.push(
|
||||||
|
new CompileViewRootNode(CompileViewRootNodeType.NgContent, null, ast.index));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) {
|
if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) {
|
||||||
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
parent.addContentNode(
|
||||||
|
ast.ngContentIndex,
|
||||||
|
new CompileViewRootNode(CompileViewRootNodeType.NgContent, null, ast.index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -164,19 +184,29 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||||
var nodeIndex = this.view.nodes.length;
|
var nodeIndex = this.view.nodes.length;
|
||||||
var createRenderNodeExpr: o.InvokeMethodExpr;
|
var createRenderNodeExpr: o.Expression;
|
||||||
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
||||||
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||||
createRenderNodeExpr = o.THIS_EXPR.callMethod(
|
var component = directives.find(directive => directive.isComponent);
|
||||||
'selectOrCreateHostElement', [o.literal(ast.name), rootSelectorVar, debugContextExpr]);
|
if (ast.name === NG_CONTAINER_TAG) {
|
||||||
|
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||||
|
'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]);
|
||||||
} else {
|
} else {
|
||||||
if (ast.name === NG_CONTAINER_TAG) {
|
const htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
const attrNameAndValues = createInlineArray(
|
||||||
'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]);
|
_mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v)));
|
||||||
|
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||||
|
createRenderNodeExpr =
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
|
||||||
|
ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar,
|
||||||
|
debugContextExpr
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
createRenderNodeExpr =
|
||||||
'createElement',
|
o.importExpr(resolveIdentifier(Identifiers.createRenderElement)).callFn([
|
||||||
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
|
ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name),
|
||||||
|
attrNameAndValues, debugContextExpr
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var fieldName = `_el_${nodeIndex}`;
|
var fieldName = `_el_${nodeIndex}`;
|
||||||
@ -186,48 +216,29 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
|
|
||||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
|
||||||
var component = directives.find(directive => directive.isComponent);
|
|
||||||
var htmlAttrs = _readHtmlAttrs(ast.attrs);
|
|
||||||
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
|
|
||||||
for (var i = 0; i < attrNameAndValues.length; i++) {
|
|
||||||
const attrName = attrNameAndValues[i][0];
|
|
||||||
if (ast.name !== NG_CONTAINER_TAG) {
|
|
||||||
// <ng-container> are not rendered in the DOM
|
|
||||||
const attrValue = attrNameAndValues[i][1];
|
|
||||||
this.view.createMethod.addStmt(
|
|
||||||
ViewProperties.renderer
|
|
||||||
.callMethod(
|
|
||||||
'setElementAttribute', [renderNode, o.literal(attrName), o.literal(attrValue)])
|
|
||||||
.toStmt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var compileElement = new CompileElement(
|
var compileElement = new CompileElement(
|
||||||
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
|
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
|
||||||
ast.hasViewContainer, false, ast.references);
|
ast.hasViewContainer, false, ast.references, this.targetDependencies);
|
||||||
this.view.nodes.push(compileElement);
|
this.view.nodes.push(compileElement);
|
||||||
var compViewExpr: o.ReadVarExpr = null;
|
var compViewExpr: o.ReadPropExpr = null;
|
||||||
if (isPresent(component)) {
|
if (isPresent(component)) {
|
||||||
let nestedComponentIdentifier =
|
let nestedComponentIdentifier =
|
||||||
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
||||||
this.targetDependencies.push(
|
this.targetDependencies.push(
|
||||||
new ViewFactoryDependency(component.type, nestedComponentIdentifier));
|
new ViewFactoryDependency(component.type, nestedComponentIdentifier));
|
||||||
let entryComponentIdentifiers =
|
|
||||||
component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
|
||||||
var id = new CompileIdentifierMetadata({name: entryComponent.name});
|
|
||||||
this.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
|
||||||
return id;
|
|
||||||
});
|
|
||||||
compileElement.createComponentFactoryResolver(entryComponentIdentifiers);
|
|
||||||
|
|
||||||
compViewExpr = o.variable(`compView_${nodeIndex}`); // fix highlighting: `
|
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
|
||||||
|
this.view.fields.push(new o.ClassField(
|
||||||
|
compViewExpr.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.importType(component.type)])));
|
||||||
|
this.view.viewChildren.push(compViewExpr);
|
||||||
compileElement.setComponentView(compViewExpr);
|
compileElement.setComponentView(compViewExpr);
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
compViewExpr
|
compViewExpr
|
||||||
.set(o.importExpr(nestedComponentIdentifier).callFn([
|
.set(o.importExpr(nestedComponentIdentifier).callFn([
|
||||||
ViewProperties.viewUtils, compileElement.injector, compileElement.appElement
|
ViewProperties.viewUtils, compileElement.injector, compileElement.appElement
|
||||||
]))
|
]))
|
||||||
.toDeclStmt());
|
.toStmt());
|
||||||
}
|
}
|
||||||
compileElement.beforeChildren();
|
compileElement.beforeChildren();
|
||||||
this._addRootNodeAndProject(compileElement);
|
this._addRootNodeAndProject(compileElement);
|
||||||
@ -235,18 +246,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1);
|
compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1);
|
||||||
|
|
||||||
if (isPresent(compViewExpr)) {
|
if (isPresent(compViewExpr)) {
|
||||||
var codeGenContentNodes: o.Expression;
|
|
||||||
if (this.view.component.type.isHost) {
|
|
||||||
codeGenContentNodes = ViewProperties.projectableNodes;
|
|
||||||
} else {
|
|
||||||
codeGenContentNodes = o.literalArr(
|
|
||||||
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
|
|
||||||
}
|
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
compViewExpr
|
compViewExpr.callMethod('create', [compileElement.getComponent(), o.NULL_EXPR]).toStmt());
|
||||||
.callMethod(
|
|
||||||
'create', [compileElement.getComponent(), codeGenContentNodes, o.NULL_EXPR])
|
|
||||||
.toStmt());
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -273,7 +274,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||||
var compileElement = new CompileElement(
|
var compileElement = new CompileElement(
|
||||||
parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers,
|
parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers,
|
||||||
ast.hasViewContainer, true, ast.references);
|
ast.hasViewContainer, true, ast.references, this.targetDependencies);
|
||||||
this.view.nodes.push(compileElement);
|
this.view.nodes.push(compileElement);
|
||||||
|
|
||||||
this.nestedViewCount++;
|
this.nestedViewCount++;
|
||||||
@ -343,18 +344,22 @@ function _isNgContainer(node: CompileNode, view: CompileView): boolean {
|
|||||||
|
|
||||||
|
|
||||||
function _mergeHtmlAndDirectiveAttrs(
|
function _mergeHtmlAndDirectiveAttrs(
|
||||||
declaredHtmlAttrs: {[key: string]: string},
|
declaredHtmlAttrs: {[key: string]: string}, directives: CompileDirectiveMetadata[]): string[] {
|
||||||
directives: CompileDirectiveMetadata[]): string[][] {
|
const mapResult: {[key: string]: string} = {};
|
||||||
var result: {[key: string]: string} = {};
|
Object.keys(declaredHtmlAttrs).forEach(key => { mapResult[key] = declaredHtmlAttrs[key]; });
|
||||||
Object.keys(declaredHtmlAttrs).forEach(key => { result[key] = declaredHtmlAttrs[key]; });
|
|
||||||
directives.forEach(directiveMeta => {
|
directives.forEach(directiveMeta => {
|
||||||
Object.keys(directiveMeta.hostAttributes).forEach(name => {
|
Object.keys(directiveMeta.hostAttributes).forEach(name => {
|
||||||
const value = directiveMeta.hostAttributes[name];
|
const value = directiveMeta.hostAttributes[name];
|
||||||
var prevValue = result[name];
|
const prevValue = mapResult[name];
|
||||||
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
mapResult[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return mapToKeyValueArray(result);
|
const arrResult: string[] = [];
|
||||||
|
// Note: We need to sort to get a defined output order
|
||||||
|
// for tests and for caching generated artifacts...
|
||||||
|
Object.keys(mapResult).sort().forEach(
|
||||||
|
(attrName) => { arrResult.push(attrName, mapResult[attrName]); });
|
||||||
|
return arrResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
||||||
@ -371,15 +376,6 @@ function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
|
||||||
var entryArray: string[][] = [];
|
|
||||||
Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); });
|
|
||||||
// We need to sort to get a defined output order
|
|
||||||
// for tests and for caching generated artifacts...
|
|
||||||
ListWrapper.sort(entryArray);
|
|
||||||
return entryArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
|
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
|
||||||
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||||
if (view.genConfig.genDebugInfo) {
|
if (view.genConfig.genDebugInfo) {
|
||||||
@ -458,9 +454,6 @@ function createViewClass(
|
|||||||
if (view.genConfig.genDebugInfo) {
|
if (view.genConfig.genDebugInfo) {
|
||||||
superConstructorArgs.push(nodeDebugInfosVar);
|
superConstructorArgs.push(nodeDebugInfosVar);
|
||||||
}
|
}
|
||||||
var viewConstructor = new o.ClassMethod(
|
|
||||||
null, viewConstructorArgs, [o.SUPER_EXPR.callFn(superConstructorArgs).toStmt()]);
|
|
||||||
|
|
||||||
var viewMethods = [
|
var viewMethods = [
|
||||||
new o.ClassMethod(
|
new o.ClassMethod(
|
||||||
'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||||
@ -479,17 +472,32 @@ function createViewClass(
|
|||||||
'detectChangesInternal', [new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
'detectChangesInternal', [new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
||||||
generateDetectChangesMethod(view)),
|
generateDetectChangesMethod(view)),
|
||||||
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||||
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish()),
|
new o.ClassMethod('destroyInternal', [], generateDestroyMethod(view)),
|
||||||
new o.ClassMethod('detachInternal', [], view.detachMethod.finish())
|
new o.ClassMethod('detachInternal', [], view.detachMethod.finish()),
|
||||||
].concat(view.eventHandlerMethods);
|
generateVisitRootNodesMethod(view), generateVisitProjectableNodesMethod(view)
|
||||||
|
].filter((method) => method.body.length > 0);
|
||||||
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
||||||
var viewClass = new o.ClassStmt(
|
|
||||||
view.className, o.importExpr(resolveIdentifier(superClass), [getContextType(view)]),
|
var viewClass = createClassStmt({
|
||||||
view.fields, view.getters, viewConstructor,
|
name: view.className,
|
||||||
viewMethods.filter((method) => method.body.length > 0));
|
parent: o.importExpr(resolveIdentifier(superClass), [getContextType(view)]),
|
||||||
|
parentArgs: superConstructorArgs,
|
||||||
|
ctorParams: viewConstructorArgs,
|
||||||
|
builders: [{methods: viewMethods}, view]
|
||||||
|
});
|
||||||
return viewClass;
|
return viewClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateDestroyMethod(view: CompileView): o.Statement[] {
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
view.viewContainerAppElements.forEach(
|
||||||
|
(appElement) => { stmts.push(appElement.callMethod('destroyNestedViews', []).toStmt()); });
|
||||||
|
view.viewChildren.forEach(
|
||||||
|
(viewChild) => { stmts.push(viewChild.callMethod('destroy', []).toStmt()); });
|
||||||
|
stmts.push(...view.destroyMethod.finish());
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
|
||||||
function createViewFactory(
|
function createViewFactory(
|
||||||
view: CompileView, viewClass: o.ClassStmt, renderCompTypeVar: o.ReadVarExpr): o.Statement {
|
view: CompileView, viewClass: o.ClassStmt, renderCompTypeVar: o.ReadVarExpr): o.Statement {
|
||||||
var viewFactoryArgs = [
|
var viewFactoryArgs = [
|
||||||
@ -563,9 +571,9 @@ function generateCreateMethod(view: CompileView): o.Statement[] {
|
|||||||
.callMethod(
|
.callMethod(
|
||||||
'init',
|
'init',
|
||||||
[
|
[
|
||||||
createFlatArray(view.rootNodesOrAppElements),
|
view.lastRenderNode,
|
||||||
o.literalArr(view.nodes.map(node => node.renderNode)), o.literalArr(view.disposables),
|
o.literalArr(view.nodes.map(node => node.renderNode)),
|
||||||
o.literalArr(view.subscriptions)
|
view.disposables.length ? o.literalArr(view.disposables) : o.NULL_EXPR,
|
||||||
])
|
])
|
||||||
.toStmt(),
|
.toStmt(),
|
||||||
new o.ReturnStatement(resultExpr)
|
new o.ReturnStatement(resultExpr)
|
||||||
@ -581,19 +589,22 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
|||||||
view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty()) {
|
view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty()) {
|
||||||
return stmts;
|
return stmts;
|
||||||
}
|
}
|
||||||
ListWrapper.addAll(stmts, view.animationBindingsMethod.finish());
|
stmts.push(...view.animationBindingsMethod.finish());
|
||||||
ListWrapper.addAll(stmts, view.detectChangesInInputsMethod.finish());
|
stmts.push(...view.detectChangesInInputsMethod.finish());
|
||||||
stmts.push(
|
view.viewContainerAppElements.forEach((appElement) => {
|
||||||
o.THIS_EXPR.callMethod('detectContentChildrenChanges', [DetectChangesVars.throwOnChange])
|
stmts.push(
|
||||||
.toStmt());
|
appElement.callMethod('detectChangesInNestedViews', [DetectChangesVars.throwOnChange])
|
||||||
|
.toStmt());
|
||||||
|
});
|
||||||
var afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
var afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
||||||
view.afterContentLifecycleCallbacksMethod.finish());
|
view.afterContentLifecycleCallbacksMethod.finish());
|
||||||
if (afterContentStmts.length > 0) {
|
if (afterContentStmts.length > 0) {
|
||||||
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
|
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
|
||||||
}
|
}
|
||||||
ListWrapper.addAll(stmts, view.detectChangesRenderPropertiesMethod.finish());
|
stmts.push(...view.detectChangesRenderPropertiesMethod.finish());
|
||||||
stmts.push(o.THIS_EXPR.callMethod('detectViewChildrenChanges', [DetectChangesVars.throwOnChange])
|
view.viewChildren.forEach((viewChild) => {
|
||||||
.toStmt());
|
stmts.push(viewChild.callMethod('detectChanges', [DetectChangesVars.throwOnChange]).toStmt());
|
||||||
|
});
|
||||||
var afterViewStmts =
|
var afterViewStmts =
|
||||||
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
||||||
if (afterViewStmts.length > 0) {
|
if (afterViewStmts.length > 0) {
|
||||||
@ -610,12 +621,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
|||||||
DetectChangesVars.changes.set(o.NULL_EXPR)
|
DetectChangesVars.changes.set(o.NULL_EXPR)
|
||||||
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))));
|
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))));
|
||||||
}
|
}
|
||||||
if (readVars.has(DetectChangesVars.valUnwrapper.name)) {
|
varStmts.push(...createSharedBindingVariablesIfNeeded(stmts));
|
||||||
varStmts.push(
|
|
||||||
DetectChangesVars.valUnwrapper
|
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
|
||||||
}
|
|
||||||
return varStmts.concat(stmts);
|
return varStmts.concat(stmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,3 +651,61 @@ function getChangeDetectionMode(view: CompileView): ChangeDetectorStatus {
|
|||||||
}
|
}
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateVisitRootNodesMethod(view: CompileView): o.ClassMethod {
|
||||||
|
const cbVar = o.variable('cb');
|
||||||
|
const ctxVar = o.variable('ctx');
|
||||||
|
const stmts: o.Statement[] = generateVisitNodesStmts(view.rootNodes, cbVar, ctxVar);
|
||||||
|
return new o.ClassMethod(
|
||||||
|
'visitRootNodesInternal',
|
||||||
|
[new o.FnParam(cbVar.name, o.DYNAMIC_TYPE), new o.FnParam(ctxVar.name, o.DYNAMIC_TYPE)],
|
||||||
|
stmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateVisitProjectableNodesMethod(view: CompileView): o.ClassMethod {
|
||||||
|
const nodeIndexVar = o.variable('nodeIndex');
|
||||||
|
const ngContentIndexVar = o.variable('ngContentIndex');
|
||||||
|
const cbVar = o.variable('cb');
|
||||||
|
const ctxVar = o.variable('ctx');
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
view.nodes.forEach((node) => {
|
||||||
|
if (node instanceof CompileElement && node.component) {
|
||||||
|
node.contentNodesByNgContentIndex.forEach((projectedNodes, ngContentIndex) => {
|
||||||
|
stmts.push(new o.IfStmt(
|
||||||
|
nodeIndexVar.equals(o.literal(node.nodeIndex))
|
||||||
|
.and(ngContentIndexVar.equals(o.literal(ngContentIndex))),
|
||||||
|
generateVisitNodesStmts(projectedNodes, cbVar, ctxVar)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new o.ClassMethod(
|
||||||
|
'visitProjectableNodesInternal',
|
||||||
|
[
|
||||||
|
new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE),
|
||||||
|
new o.FnParam(ngContentIndexVar.name, o.NUMBER_TYPE),
|
||||||
|
new o.FnParam(cbVar.name, o.DYNAMIC_TYPE), new o.FnParam(ctxVar.name, o.DYNAMIC_TYPE)
|
||||||
|
],
|
||||||
|
stmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateVisitNodesStmts(
|
||||||
|
nodes: CompileViewRootNode[], cb: o.Expression, ctx: o.Expression): o.Statement[] {
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
switch (node.type) {
|
||||||
|
case CompileViewRootNodeType.Node:
|
||||||
|
stmts.push(cb.callFn([node.expr, ctx]).toStmt());
|
||||||
|
break;
|
||||||
|
case CompileViewRootNodeType.ViewContainer:
|
||||||
|
stmts.push(cb.callFn([node.expr.prop('nativeElement'), ctx]).toStmt());
|
||||||
|
stmts.push(node.expr.callMethod('visitNestedViewRootNodes', [cb, ctx]).toStmt());
|
||||||
|
break;
|
||||||
|
case CompileViewRootNodeType.NgContent:
|
||||||
|
stmts.push(
|
||||||
|
o.THIS_EXPR.callMethod('visitProjectedNodes', [o.literal(node.ngContentIndex), cb, ctx])
|
||||||
|
.toStmt());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user