2018-04-24 11:34:11 -07:00
|
|
|
/**
|
|
|
|
* @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 {ConstantPool} from '../../constant_pool';
|
|
|
|
import * as o from '../../output/output_ast';
|
2018-11-30 17:45:04 +01:00
|
|
|
import {splitAtColon} from '../../util';
|
2018-04-24 11:34:11 -07:00
|
|
|
import * as t from '../r3_ast';
|
|
|
|
import {R3QueryMetadata} from './api';
|
2018-10-18 10:08:51 -07:00
|
|
|
import {isI18nAttribute} from './i18n/util';
|
2018-04-24 11:34:11 -07:00
|
|
|
|
|
|
|
/** Name of the temporary to use during data binding */
|
|
|
|
export const TEMPORARY_NAME = '_t';
|
|
|
|
|
|
|
|
/** Name of the context parameter passed into a template function */
|
|
|
|
export const CONTEXT_NAME = 'ctx';
|
|
|
|
|
|
|
|
/** Name of the RenderFlag passed into a template function */
|
|
|
|
export const RENDER_FLAGS = 'rf';
|
|
|
|
|
|
|
|
/** The prefix reference variables */
|
|
|
|
export const REFERENCE_PREFIX = '_r';
|
|
|
|
|
|
|
|
/** The name of the implicit context reference */
|
|
|
|
export const IMPLICIT_REFERENCE = '$implicit';
|
|
|
|
|
2018-09-26 13:19:04 -07:00
|
|
|
/** Non bindable attribute name **/
|
|
|
|
export const NON_BINDABLE_ATTR = 'ngNonBindable';
|
|
|
|
|
2018-04-24 11:34:11 -07:00
|
|
|
/**
|
|
|
|
* Creates an allocator for a temporary variable.
|
|
|
|
*
|
|
|
|
* A variable declaration is added to the statements the first time the allocator is invoked.
|
|
|
|
*/
|
|
|
|
export function temporaryAllocator(statements: o.Statement[], name: string): () => o.ReadVarExpr {
|
|
|
|
let temp: o.ReadVarExpr|null = null;
|
|
|
|
return () => {
|
|
|
|
if (!temp) {
|
|
|
|
statements.push(new o.DeclareVarStmt(TEMPORARY_NAME, undefined, o.DYNAMIC_TYPE));
|
|
|
|
temp = o.variable(name);
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function unsupported(feature: string): never {
|
|
|
|
if (this) {
|
|
|
|
throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
|
|
|
|
}
|
|
|
|
throw new Error(`Feature ${feature} is not supported yet`);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function invalid<T>(arg: o.Expression | o.Statement | t.Node): never {
|
|
|
|
throw new Error(
|
|
|
|
`Invalid state: Visitor ${this.constructor.name} doesn't handle ${o.constructor.name}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function asLiteral(value: any): o.Expression {
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
return o.literalArr(value.map(asLiteral));
|
|
|
|
}
|
|
|
|
return o.literal(value, o.INFERRED_TYPE);
|
|
|
|
}
|
|
|
|
|
2018-12-17 13:17:42 -08:00
|
|
|
export function conditionallyCreateMapObjectLiteral(
|
|
|
|
keys: {[key: string]: string | string[]}, keepDeclared?: boolean): o.Expression|null {
|
2018-04-24 11:34:11 -07:00
|
|
|
if (Object.getOwnPropertyNames(keys).length > 0) {
|
2018-12-17 13:17:42 -08:00
|
|
|
return mapToExpression(keys, keepDeclared);
|
2018-04-24 11:34:11 -07:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-12-17 13:17:42 -08:00
|
|
|
function mapToExpression(
|
|
|
|
map: {[key: string]: string | string[]}, keepDeclared?: boolean): o.Expression {
|
2018-11-30 17:45:04 +01:00
|
|
|
return o.literalMap(Object.getOwnPropertyNames(map).map(key => {
|
2018-12-17 13:17:42 -08:00
|
|
|
// canonical syntax: `dirProp: publicProp`
|
2018-11-30 17:45:04 +01:00
|
|
|
// if there is no `:`, use dirProp = elProp
|
2018-12-17 13:17:42 -08:00
|
|
|
const value = map[key];
|
|
|
|
let declaredName: string;
|
|
|
|
let publicName: string;
|
|
|
|
let minifiedName: string;
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
[publicName, declaredName] = value;
|
|
|
|
} else {
|
|
|
|
[declaredName, publicName] = splitAtColon(key, [key, value]);
|
|
|
|
}
|
|
|
|
minifiedName = declaredName;
|
|
|
|
return {
|
|
|
|
key: minifiedName,
|
|
|
|
quoted: false,
|
|
|
|
value: (keepDeclared && publicName !== declaredName) ?
|
|
|
|
o.literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
|
|
|
|
asLiteral(publicName)
|
|
|
|
};
|
2018-11-30 17:45:04 +01:00
|
|
|
}));
|
2018-04-24 11:34:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove trailing null nodes as they are implied.
|
|
|
|
*/
|
|
|
|
export function trimTrailingNulls(parameters: o.Expression[]): o.Expression[] {
|
|
|
|
while (o.isNull(parameters[parameters.length - 1])) {
|
|
|
|
parameters.pop();
|
|
|
|
}
|
|
|
|
return parameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getQueryPredicate(
|
|
|
|
query: R3QueryMetadata, constantPool: ConstantPool): o.Expression {
|
|
|
|
if (Array.isArray(query.predicate)) {
|
2018-09-28 17:20:43 -07:00
|
|
|
let predicate: o.Expression[] = [];
|
|
|
|
query.predicate.forEach((selector: string): void => {
|
|
|
|
// Each item in predicates array may contain strings with comma-separated refs
|
|
|
|
// (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
|
|
|
|
// as separate array entities
|
|
|
|
const selectors = selector.split(',').map(token => o.literal(token.trim()));
|
|
|
|
predicate.push(...selectors);
|
|
|
|
});
|
2018-10-03 13:49:24 -07:00
|
|
|
return constantPool.getConstLiteral(o.literalArr(predicate), true);
|
2018-04-24 11:34:11 -07:00
|
|
|
} else {
|
|
|
|
return query.predicate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function noop() {}
|
|
|
|
|
|
|
|
export class DefinitionMap {
|
|
|
|
values: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
|
|
|
|
|
|
|
set(key: string, value: o.Expression|null): void {
|
|
|
|
if (value) {
|
|
|
|
this.values.push({key, value, quoted: false});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toLiteralMap(): o.LiteralMapExpr { return o.literalMap(this.values); }
|
|
|
|
}
|
2018-09-21 11:41:37 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract a map of properties to values for a given element or template node, which can be used
|
|
|
|
* by the directive matching machinery.
|
|
|
|
*
|
|
|
|
* @param elOrTpl the element or template in question
|
|
|
|
* @return an object set up for directive matching. For attributes on the element/template, this
|
|
|
|
* object maps a property name to its (static) value. For any bindings, this map simply maps the
|
|
|
|
* property name to an empty string.
|
|
|
|
*/
|
|
|
|
export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template):
|
|
|
|
{[name: string]: string} {
|
|
|
|
const attributesMap: {[name: string]: string} = {};
|
|
|
|
|
|
|
|
elOrTpl.attributes.forEach(a => {
|
2018-10-18 10:08:51 -07:00
|
|
|
if (!isI18nAttribute(a.name)) {
|
2018-09-21 11:41:37 -07:00
|
|
|
attributesMap[a.name] = a.value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
elOrTpl.inputs.forEach(i => { attributesMap[i.name] = ''; });
|
|
|
|
elOrTpl.outputs.forEach(o => { attributesMap[o.name] = ''; });
|
|
|
|
|
|
|
|
return attributesMap;
|
|
|
|
}
|