Compare commits

...

10 Commits

Author SHA1 Message Date
0776daec88 release: cut the v8.0.0-rc.1 release 2019-04-26 13:28:49 -07:00
615e1a58b2 test(ivy): pin deps on hello world size tests (#30152)
We recently had an unexpected size regression in the hello world
tests because the CLI devkit released an RC that regressed us and
the dependencies were not pinned. This change ensures that we only
update dependencies like devkit deliberately, so we do not have
mysterious breakages caused by other packages.

PR Close #30152
2019-04-26 12:34:10 -07:00
606758357e fix(bazel): update peerDep ranges (#30155)
PR Close #30155
2019-04-26 12:32:35 -07:00
ba2a3595c6 fix(ivy): output should not be subscribe twice when 2 listeners (#30144)
PR Close #30144
2019-04-26 11:11:09 -07:00
a50bfe5054 fix(ivy): property bindings use correct indices (#30129)
- Extracts and documents code that will be common to interpolation instructions
- Ensures that binding indices are updated at the proper time during compilation
- Adds additional tests

Related #30011

PR Close #30129
2019-04-26 11:09:51 -07:00
c8983bc367 docs: remove note about ivy being coupled to dynamic import (#30151)
PR Close #30151
2019-04-26 11:08:51 -07:00
8d6d2c6704 build: bazel ts-api-guardian usage fails on workspaces which don't depend on chalk (#30138)
When using the npm package in a workspace which doesn't depend on chalk, ts-api-guardian fails with an error `Error: Cannot find module 'chalk'`

PR Close #30138
2019-04-26 11:08:05 -07:00
6711f22e62 fix(bazel): Exclude common/upgrade* in metadata.tsconfig.json (#30133)
It has a dependency on @angular/upgrade which is not part of the
dependencies in package.json, so postinstall would fail.

PR Close #30133
2019-04-26 11:07:34 -07:00
8dd9192fe3 refactor(ivy): undeprecate inject (#30132)
PR Close #30132
2019-04-26 11:06:42 -07:00
870e0dab48 fix(ivy): remove debug utilities from ivy production builds (#30130)
Prior to this commit, we were pulling DebugNode and DebugElement
into production builds because BrowserModule automatically pulled
in NgProbe and thus getDebugNode. In Ivy, this is not necessary
because Ivy has its own set of debug utilities. We should use these
existing tools instead of NgProbe.

This commit adds an Ivy switch so we do not pull in NgProbe utilities
when running with Ivy. This saves us ~8KB in prod builds.

PR Close #30130
2019-04-26 11:04:48 -07:00
24 changed files with 405 additions and 107 deletions

View File

@ -1,3 +1,14 @@
<a name="8.0.0-rc.1"></a>
# [8.0.0-rc.1](https://github.com/angular/angular/compare/8.0.0-rc.0...8.0.0-rc.1) (2019-04-26)
### Bug Fixes
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([6711f22](https://github.com/angular/angular/commit/6711f22))
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([6067583](https://github.com/angular/angular/commit/6067583))
<a name="8.0.0-rc.0"></a>
# [8.0.0-rc.0](https://github.com/angular/angular/compare/8.0.0-beta.14...8.0.0-rc.0) (2019-04-25)

View File

@ -223,9 +223,7 @@ const routes: Routes = [{
<div class="alert is-helpful">
**v8 update**: When you update to version 8, the [`ng update`](cli/update) command performs the transformation automatically. Prior to version 7, the `import()` syntax works in JIT mode (with view engine).
**Ivy:** If you are using Ivy, you must update your lazy routes to the new dynamic import syntax. See the [Ivy guide](guide/ivy) for more information.
**v8 update**: When you update to version 8, the [`ng update`](cli/update) command performs the transformation automatically. Prior to version 7, the `import()` syntax only works in JIT mode (with view engine).
</div>

View File

@ -3,7 +3,7 @@
"master": {
"uncompressed": {
"runtime": 1497,
"main": 167065,
"main": 164945,
"polyfills": 43626
}
}
@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 30932,
"main": 14487,
"polyfills": 43567
}
}
@ -21,7 +21,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 157393,
"main": 149205,
"polyfills": 43567
}
}

View File

@ -9,7 +9,7 @@ function installLocalPackages() {
readonly pwd=$(pwd)
readonly packages=(
animations common compiler core forms platform-browser
platform-browser-dynamic router bazel compiler-cli language-service upgrade
platform-browser-dynamic router bazel compiler-cli language-service
)
local local_packages=()
for package in "${packages[@]}"; do

View File

@ -23,6 +23,7 @@
"node_modules/@angular/core/schematics/**",
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**",
"node_modules/@angular/common/upgrade*",
"node_modules/@angular/router/upgrade*"
]
}

View File

@ -10,7 +10,6 @@
"@angular/platform-browser": "packages-dist:platform-browser",
"@angular/platform-browser-dynamic": "packages-dist:platform-browser-dynamic",
"@angular/router": "packages-dist:router",
"@angular/upgrade": "packages-dist:upgrade",
"reflect-metadata": "0.1.12",
"rxjs": "6.4.0",
"tslib": "1.9.3",
@ -31,4 +30,4 @@
"postinstall": "ngc -p ./angular-metadata.tsconfig.json",
"//": "TODO(gregmagolan): figure out how to keep dependencies here up to date with the root package.json"
}
}
}

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "8.0.0-rc.0",
"version": "8.0.0-rc.1",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",

View File

@ -34,11 +34,11 @@
"@types/node": "6.0.84",
"semver": "^5.6.0",
"shelljs": "0.8.2",
"tsickle": "0.34.3"
"tsickle": "^0.35.0"
},
"peerDependencies": {
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
"@bazel/typescript": "0.27.12",
"@bazel/typescript": "0.*",
"typescript": ">=3.4 <3.5"
},
"repository": {

View File

@ -20,6 +20,7 @@
"node_modules/@angular/core/schematics/**",
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**",
"node_modules/@angular/common/upgrade*",
"node_modules/@angular/router/upgrade*"
]
}

View File

@ -470,7 +470,7 @@ describe('compiler compliance: bindings', () => {
if (rf & 2) {
i0.ɵɵselect(0);
i0.ɵɵpropertyInterpolateV("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j");
i0.ɵɵpropertyInterpolateV("title", ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
i0.ɵɵselect(1);
i0.ɵɵpropertyInterpolate8("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
i0.ɵɵselect(2);

View File

@ -155,16 +155,12 @@ export function convertPropertyBinding(
localResolver = new DefaultLocalResolver();
}
const currValExpr = createCurrValueExpr(bindingId);
const stmts: o.Statement[] = [];
const visitor =
new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
const stmts: o.Statement[] = getStatementsFromVisitor(visitor, bindingId);
if (visitor.temporaryCount) {
for (let i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
} else if (form == BindingForm.TrySimple) {
if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
return new ConvertPropertyBindingResult([], outputExpr);
}
@ -172,6 +168,58 @@ export function convertPropertyBinding(
return new ConvertPropertyBindingResult(stmts, currValExpr);
}
/**
* Given some expression, such as a binding or interpolation expression, and a context expression to
* look values up on, visit each facet of the given expression resolving values from the context
* expression such that a list of arguments can be derived from the found values that can be used as
* arguments to an external update instruction.
*
* @param localResolver The resolver to use to look up expressions by name appropriately
* @param contextVariableExpression The expression representing the context variable used to create
* the final argument expressions
* @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
* be resolved and what arguments list to build.
* @param bindingId A name prefix used to create temporary variable names if they're needed for the
* arguments generated
* @returns An array of expressions that can be passed as arguments to instruction expressions like
* `o.importExpr(R3.propertyInterpolate).callFn(result)`
*/
export function convertUpdateArguments(
localResolver: LocalResolver, contextVariableExpression: o.Expression,
expressionWithArgumentsToExtract: cdAst.AST, bindingId: string) {
const visitor =
new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
const outputExpr: o.InvokeFunctionExpr =
expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
const stmts = getStatementsFromVisitor(visitor, bindingId);
// Removing the first argument, because it was a length for ViewEngine, not Ivy.
let args = outputExpr.args.slice(1);
if (expressionWithArgumentsToExtract instanceof cdAst.Interpolation) {
// If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
// args returned to just the value, because we're going to pass it to a special instruction.
const strings = expressionWithArgumentsToExtract.strings;
if (args.length === 3 && strings[0] === '' && strings[1] === '') {
// Single argument interpolate instructions.
args = [args[1]];
} else if (args.length >= 19) {
// 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
// an array of arguments
args = [o.literalArr(args)];
}
}
return {stmts, args};
}
function getStatementsFromVisitor(visitor: _AstToIrVisitor, bindingId: string) {
const stmts: o.Statement[] = [];
for (let i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
return stmts;
}
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
const visitor = new _BuiltinAstConverter(converterFactory);
return ast.visit(visitor);

View File

@ -7,7 +7,7 @@
*/
import {flatten, sanitizeIdentifier} from '../../compile_metadata';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding, convertUpdateArguments} from '../../compiler_util/expression_converter';
import {ConstantPool} from '../../constant_pool';
import * as core from '../../core';
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
@ -750,23 +750,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
if (inputType === BindingType.Property) {
if (value instanceof Interpolation) {
// Interpolated properties
const {currValExpr} = convertPropertyBinding(
this, implicit, value, this.bindingContext(), BindingForm.TrySimple);
let args: o.Expression[] = (currValExpr as any).args;
args.shift(); // ViewEngine required a count, we don't need that.
// For interpolations like attr="{{foo}}", we don't need ["", foo, ""], just [foo].
if (args.length === 3 && isEmptyStringExpression(args[0]) &&
isEmptyStringExpression(args[2])) {
args = [args[1]];
}
this.updateInstruction(
elementIndex, input.sourceSpan, propertyInterpolate(args.length), () => {
return [o.literal(attrName), ...args, ...params];
});
elementIndex, input.sourceSpan, getPropertyInterpolationExpression(value),
() =>
[o.literal(attrName),
...this.getUpdateInstructionArguments(o.variable(CONTEXT_NAME), value),
...params]);
} else {
// Bound, un-interpolated properties
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
@ -1076,6 +1066,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
o.importExpr(R3.bind).callFn([valExpr]);
}
/**
* Gets a list of argument expressions to pass to an update instruction expression. Also updates
* the temp variables state with temp variables that were identified as needing to be created
* while visiting the arguments.
* @param contextExpression The expression for the context variable used to create arguments
* @param value The original expression we will be resolving an arguments list from.
*/
private getUpdateInstructionArguments(contextExpression: o.Expression, value: AST):
o.Expression[] {
const {args, stmts} =
convertUpdateArguments(this, contextExpression, value, this.bindingContext());
this._tempVariables.push(...stmts);
return args;
}
private matchDirectives(tagName: string, elOrTpl: t.Element|t.Template) {
if (this.directiveMatcher) {
const selector = createCssSelector(tagName, getAttrsForDirectiveMatching(elOrTpl));
@ -1646,16 +1651,12 @@ function interpolate(args: o.Expression[]): o.Expression {
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
}
function isEmptyStringExpression(exp: o.Expression) {
return exp instanceof o.LiteralExpr && exp.value === '';
}
function propertyInterpolate(argsLength: number) {
if (argsLength % 2 !== 1) {
error(`Invalid propertyInterpolate argument length ${argsLength}`);
}
switch (argsLength) {
/**
* Gets the instruction to generate for an interpolated property
* @param interpolation An Interpolation AST
*/
function getPropertyInterpolationExpression(interpolation: Interpolation) {
switch (getInterpolationArgsLength(interpolation)) {
case 1:
return R3.propertyInterpolate;
case 3:
@ -1679,6 +1680,22 @@ function propertyInterpolate(argsLength: number) {
}
}
/**
* Gets the number of arguments expected to be passed to a generated instruction in the case of
* interpolation instructions.
* @param interpolation An interpolation ast
*/
function getInterpolationArgsLength(interpolation: Interpolation) {
const {expressions, strings} = interpolation;
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
// `textInterpolate`.
return 1;
} else {
return expressions.length + strings.length;
}
}
/**
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
*/

View File

@ -71,10 +71,23 @@ export function injectInjectorOnly<T>(
/**
* Generated instruction: Injects a token from the currently active injector.
*
* WARNING: This function is meant to be generated by the Ivy compiler, and is not meant for
* developer consumption!
* Must be used in the context of a factory function such as one defined for an
* `InjectionToken`. Throws an error if not called from such a context.
*
* https://github.com/angular/angular/blob/master/packages/core/src/render3/DELTA_INSTRUCTIONS.md
* (Additional documentation moved to `inject`, as it is the public API, and an alias for this instruction)
*
* @see inject
* @codeGenApi
*/
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>): T;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|
null {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* Injects a token from the currently active injector.
*
* Must be used in the context of a factory function such as one defined for an
* `InjectionToken`. Throws an error if not called from such a context.
@ -97,17 +110,6 @@ export function injectInjectorOnly<T>(
*
* @publicApi
*/
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>): T;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|
null {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* @deprecated in v8, delete after v10. This API should be used only be generated code, and that
* code should now use ɵɵinject instead.
* @publicApi
*/
export const inject = ɵɵinject;
/**

View File

@ -110,6 +110,8 @@ function listenerInternal(
ngDevMode && assertNodeOfPossibleTypes(
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
let processOutputs = true;
// add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) {
const native = getNativeByTNode(tNode, lView) as RElement;
@ -149,6 +151,7 @@ function listenerInternal(
// Attach a new listener at the head of the coalesced listeners list.
(<any>listenerFn).__ngNextListenerFn__ = (<any>existingListener).__ngNextListenerFn__;
(<any>existingListener).__ngNextListenerFn__ = listenerFn;
processOutputs = false;
} else {
// The first argument of `listen` function in Procedural Renderer is:
// - either a target name (as a string) in case of global target (window, document, body)
@ -180,7 +183,7 @@ function listenerInternal(
const outputs = tNode.outputs;
let props: PropertyAliasValue|undefined;
if (outputs && (props = outputs[eventName])) {
if (processOutputs && outputs && (props = outputs[eventName])) {
const propsLength = props.length;
if (propsLength) {
const lCleanup = getCleanup(lView);

View File

@ -7,6 +7,7 @@
*/
import {assertEqual, assertLessThan} from '../../util/assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
@ -15,6 +16,7 @@ import {renderStringify} from '../util/misc_utils';
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
/**
* Create interpolation bindings with a variable number of expressions.
*
@ -290,12 +292,6 @@ export function ɵɵinterpolation8(
/// NEW INSTRUCTIONS
/////////////////////////////////////////////////////////////////////
/**
* Shared reference to a string, used in `ɵɵpropertyInterpolate`.
*/
const EMPTY_STRING = '';
/**
*
* Update an interpolated property on an element with a lone bound value
@ -321,11 +317,13 @@ const EMPTY_STRING = '';
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009 {
ɵɵpropertyInterpolate1(propName, EMPTY_STRING, v0, EMPTY_STRING);
export function ɵɵpropertyInterpolate(
propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009 {
ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
return ɵɵpropertyInterpolate;
}
@ -354,13 +352,15 @@ export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate1(
propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009 {
propName: string, prefix: string, v0: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix));
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix), sanitizer);
return ɵɵpropertyInterpolate1;
}
@ -390,14 +390,15 @@ export function ɵɵpropertyInterpolate1(
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate2(
propName: string, prefix: string, v0: any, i0: string, v1: any,
suffix: string): TsickleIssue1009 {
propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix));
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix), sanitizer);
return ɵɵpropertyInterpolate2;
}
@ -430,14 +431,16 @@ export function ɵɵpropertyInterpolate2(
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate3(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
suffix: string): TsickleIssue1009 {
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix));
elementPropertyInternal(
index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix), sanitizer);
return ɵɵpropertyInterpolate3;
}
@ -472,15 +475,16 @@ export function ɵɵpropertyInterpolate3(
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate4(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, suffix: string): TsickleIssue1009 {
v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix));
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix), sanitizer);
return ɵɵpropertyInterpolate4;
}
@ -517,15 +521,17 @@ export function ɵɵpropertyInterpolate4(
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate5(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix));
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix),
sanitizer);
return ɵɵpropertyInterpolate5;
}
@ -564,16 +570,18 @@ export function ɵɵpropertyInterpolate5(
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate6(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix));
index, propName, ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix),
sanitizer);
return ɵɵpropertyInterpolate6;
}
@ -614,17 +622,19 @@ export function ɵɵpropertyInterpolate6(
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate7(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any,
suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix));
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix),
sanitizer);
return ɵɵpropertyInterpolate7;
}
@ -667,17 +677,19 @@ export function ɵɵpropertyInterpolate7(
* @param i6 Static value used for concatenation only.
* @param v7 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate8(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string): TsickleIssue1009 {
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix));
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix),
sanitizer);
return ɵɵpropertyInterpolate8;
}
@ -707,12 +719,14 @@ export function ɵɵpropertyInterpolate8(
* @param values The a collection of values and the strings inbetween those values, beginning with a
* string prefix and ending with a string suffix.
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009 {
export function ɵɵpropertyInterpolateV(
propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolationV(values));
elementPropertyInternal(index, propName, ɵɵinterpolationV(values), sanitizer);
return ɵɵpropertyInterpolateV;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, ErrorHandler, HostListener, QueryList, ViewChildren} from '@angular/core';
import {Component, Directive, ErrorHandler, EventEmitter, HostListener, Input, Output, QueryList, ViewChild, ViewChildren} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {onlyInIvy} from '@angular/private/testing';
@ -203,5 +203,40 @@ describe('event listeners', () => {
expect(returnsFalseDir.event.preventDefault).toHaveBeenCalled();
});
it('should not subscribe twice to the output when there are 2 coalesced listeners', () => {
@Directive({selector: '[foo]'})
class FooDirective {
@Input('foo') model: any;
@Output('fooChange') update = new EventEmitter();
updateValue(value: any) { this.update.emit(value); }
}
@Component({
selector: 'test-component',
template: `<div [(foo)]="someValue" (fooChange)="fooChange($event)"></div>`
})
class TestComponent {
count = 0;
someValue = -1;
@ViewChild(FooDirective) fooDirective: FooDirective|null = null;
fooChange() { this.count++; }
triggerUpdate(value: any) { this.fooDirective !.updateValue(value); }
}
TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const componentInstance = fixture.componentInstance;
componentInstance.triggerUpdate(42);
fixture.detectChanges();
expect(componentInstance.count).toEqual(1);
expect(componentInstance.someValue).toEqual(42);
});
});
});

View File

@ -10,8 +10,9 @@ import {Component, Input} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {of } from 'rxjs';
describe('elementProperty', () => {
describe('property instructions', () => {
it('should bind to properties whose names do not correspond to their attribute names', () => {
@Component({template: '<label [for]="forValue"></label>'})
class MyComp {
@ -33,6 +34,25 @@ describe('elementProperty', () => {
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
});
it('should not allow unsanitary urls in bound properties', () => {
@Component({
template: `
<img [src]="naughty">
`
})
class App {
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should not map properties whose names do not correspond to their attribute names, ' +
'if they correspond to inputs',
() => {
@ -60,4 +80,136 @@ describe('elementProperty', () => {
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hej');
});
it('should handle all flavors of interpolated properties', () => {
@Component({
template: `
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
<div title="a{{one}}b{{two}}c{{three}}d"></div>
<div title="a{{one}}b{{two}}c"></div>
<div title="a{{one}}b"></div>
<div title="{{one}}"></div>
`
})
class App {
one = 1;
two = 2;
three = 3;
four = 4;
five = 5;
six = 6;
seven = 7;
eight = 8;
nine = 9;
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const titles = Array.from(fixture.nativeElement.querySelectorAll('div[title]'))
.map((div: HTMLDivElement) => div.title);
expect(titles).toEqual([
'a1b2c3d4e5f6g7h8i9j',
'a1b2c3d4e5f6g7h8i',
'a1b2c3d4e5f6g7h',
'a1b2c3d4e5f6g',
'a1b2c3d4e5f',
'a1b2c3d4e',
'a1b2c3d',
'a1b2c',
'a1b',
'1',
]);
});
it('should handle pipes in interpolated properties', () => {
@Component({
template: `
<img title="{{(details | async)?.title}}" src="{{(details | async)?.url}}" />
`
})
class App {
details = of ({
title: 'cool image',
url: 'http://somecooldomain:1234/cool_image.png',
});
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
expect(img.title).toBe('cool image');
});
// From https://angular-team.atlassian.net/browse/FW-1287
it('should handle multiple elvis operators', () => {
@Component({
template: `
<img src="{{leadSurgeon?.getCommonInfo()?.getPhotoUrl() }}">
`
})
class App {
/** Clearly this is a doctor of heavy metals. */
leadSurgeon = {
getCommonInfo() {
return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }};
}
};
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
});
it('should not allow unsanitary urls in interpolated properties', () => {
@Component({
template: `
<img src="{{naughty}}">
`
})
class App {
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should not allow unsanitary urls in interpolated properties, even if you are tricky', () => {
@Component({
template: `
<img src="{{ja}}{{va}}script:{{naughty}}">
`
})
class App {
ja = 'ja';
va = 'va';
naughty = 'alert("I am a h4xx0rz1!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
});

View File

@ -36,10 +36,19 @@ function _ngProbeTokensToMap(tokens: core.NgProbeToken[]): {[name: string]: any}
return tokens.reduce((prev: any, t: any) => (prev[t.name] = t.token, prev), {});
}
/**
* In Ivy, we don't support NgProbe because we have our own set of testing utilities
* with more robust functionality.
*
* We shouldn't bring in NgProbe because it prevents DebugNode and friends from
* tree-shaking properly.
*/
export const ELEMENT_PROBE_PROVIDERS__POST_R3__ = [];
/**
* Providers which support debugging Angular applications (e.g. via `ng.probe`).
*/
export const ELEMENT_PROBE_PROVIDERS: core.Provider[] = [
export const ELEMENT_PROBE_PROVIDERS__PRE_R3__: core.Provider[] = [
{
provide: core.APP_INITIALIZER,
useFactory: _createNgProbe,
@ -49,3 +58,5 @@ export const ELEMENT_PROBE_PROVIDERS: core.Provider[] = [
multi: true,
},
];
export const ELEMENT_PROBE_PROVIDERS = ELEMENT_PROBE_PROVIDERS__PRE_R3__;

View File

@ -18,3 +18,5 @@ export {DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl,
export * from './private_export';
export {VERSION} from './version';
// This must be exported so it doesn't get tree-shaken away prematurely
export {ELEMENT_PROBE_PROVIDERS__POST_R3__ as ɵELEMENT_PROBE_PROVIDERS__POST_R3__} from './dom/debug/ng_probe';

View File

@ -383,7 +383,6 @@ export interface HostListenerDecorator {
new (eventName: string, args?: string[]): any;
}
/** @deprecated */
export declare const inject: typeof ɵɵinject;
export interface Inject {
@ -944,25 +943,25 @@ export declare function ɵɵprojectionDef(selectors?: CssSelectorList[]): void;
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate(propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate1(propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate1(propName: string, prefix: string, v0: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate2(propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate2(propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate3(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate3(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate4(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate4(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate5(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate5(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate6(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate6(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate7(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate7(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate8(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate8(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009;
export declare function ɵɵpropertyInterpolateV(propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009;
export declare function ɵɵProvidersFeature<T>(providers: Provider[], viewProviders?: Provider[]): (definition: DirectiveDef<T>) => void;

View File

@ -37,6 +37,11 @@ def ts_api_guardian_test(
# But it will replaced to @npm//ts-api-guardian when publishing
"@angular//tools/ts-api-guardian:lib",
"@angular//tools/ts-api-guardian:bin",
# The below are required during runtime
"@npm//chalk",
"@npm//diff",
"@npm//minimist",
"@npm//typescript",
]
args = [