mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
28842 lines
990 KiB
28842 lines
990 KiB
/**
|
|
* @license Angular v21.1.1
|
|
* (c) 2010-2026 Google LLC. https://angular.dev/
|
|
* License: MIT
|
|
*/
|
|
|
|
const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + '(([\\.\\#]?)[-\\w]+)|' + '(?:\\[([-.\\w*\\\\$]+)(?:=(["\']?)([^\\]"\']*)\\5)?\\])|' + '(\\))|' + '(\\s*,\\s*)', 'g');
|
|
class CssSelector {
|
|
element = null;
|
|
classNames = [];
|
|
attrs = [];
|
|
notSelectors = [];
|
|
static parse(selector) {
|
|
const results = [];
|
|
const _addResult = (res, cssSel) => {
|
|
if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 && cssSel.attrs.length == 0) {
|
|
cssSel.element = '*';
|
|
}
|
|
res.push(cssSel);
|
|
};
|
|
let cssSelector = new CssSelector();
|
|
let match;
|
|
let current = cssSelector;
|
|
let inNot = false;
|
|
_SELECTOR_REGEXP.lastIndex = 0;
|
|
while (match = _SELECTOR_REGEXP.exec(selector)) {
|
|
if (match[1]) {
|
|
if (inNot) {
|
|
throw new Error('Nesting :not in a selector is not allowed');
|
|
}
|
|
inNot = true;
|
|
current = new CssSelector();
|
|
cssSelector.notSelectors.push(current);
|
|
}
|
|
const tag = match[2];
|
|
if (tag) {
|
|
const prefix = match[3];
|
|
if (prefix === '#') {
|
|
current.addAttribute('id', tag.slice(1));
|
|
} else if (prefix === '.') {
|
|
current.addClassName(tag.slice(1));
|
|
} else {
|
|
current.setElement(tag);
|
|
}
|
|
}
|
|
const attribute = match[4];
|
|
if (attribute) {
|
|
current.addAttribute(current.unescapeAttribute(attribute), match[6]);
|
|
}
|
|
if (match[7]) {
|
|
inNot = false;
|
|
current = cssSelector;
|
|
}
|
|
if (match[8]) {
|
|
if (inNot) {
|
|
throw new Error('Multiple selectors in :not are not supported');
|
|
}
|
|
_addResult(results, cssSelector);
|
|
cssSelector = current = new CssSelector();
|
|
}
|
|
}
|
|
_addResult(results, cssSelector);
|
|
return results;
|
|
}
|
|
unescapeAttribute(attr) {
|
|
let result = '';
|
|
let escaping = false;
|
|
for (let i = 0; i < attr.length; i++) {
|
|
const char = attr.charAt(i);
|
|
if (char === '\\') {
|
|
escaping = true;
|
|
continue;
|
|
}
|
|
if (char === '$' && !escaping) {
|
|
throw new Error(`Error in attribute selector "${attr}". ` + `Unescaped "$" is not supported. Please escape with "\\$".`);
|
|
}
|
|
escaping = false;
|
|
result += char;
|
|
}
|
|
return result;
|
|
}
|
|
escapeAttribute(attr) {
|
|
return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$');
|
|
}
|
|
isElementSelector() {
|
|
return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 && this.notSelectors.length === 0;
|
|
}
|
|
hasElementSelector() {
|
|
return !!this.element;
|
|
}
|
|
setElement(element = null) {
|
|
this.element = element;
|
|
}
|
|
getAttrs() {
|
|
const result = [];
|
|
if (this.classNames.length > 0) {
|
|
result.push('class', this.classNames.join(' '));
|
|
}
|
|
return result.concat(this.attrs);
|
|
}
|
|
addAttribute(name, value = '') {
|
|
this.attrs.push(name, value && value.toLowerCase() || '');
|
|
}
|
|
addClassName(name) {
|
|
this.classNames.push(name.toLowerCase());
|
|
}
|
|
toString() {
|
|
let res = this.element || '';
|
|
if (this.classNames) {
|
|
this.classNames.forEach(klass => res += `.${klass}`);
|
|
}
|
|
if (this.attrs) {
|
|
for (let i = 0; i < this.attrs.length; i += 2) {
|
|
const name = this.escapeAttribute(this.attrs[i]);
|
|
const value = this.attrs[i + 1];
|
|
res += `[${name}${value ? '=' + value : ''}]`;
|
|
}
|
|
}
|
|
this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
|
|
return res;
|
|
}
|
|
}
|
|
class SelectorMatcher {
|
|
static createNotMatcher(notSelectors) {
|
|
const notMatcher = new SelectorMatcher();
|
|
notMatcher.addSelectables(notSelectors, null);
|
|
return notMatcher;
|
|
}
|
|
_elementMap = new Map();
|
|
_elementPartialMap = new Map();
|
|
_classMap = new Map();
|
|
_classPartialMap = new Map();
|
|
_attrValueMap = new Map();
|
|
_attrValuePartialMap = new Map();
|
|
_listContexts = [];
|
|
addSelectables(cssSelectors, callbackCtxt) {
|
|
let listContext = null;
|
|
if (cssSelectors.length > 1) {
|
|
listContext = new SelectorListContext(cssSelectors);
|
|
this._listContexts.push(listContext);
|
|
}
|
|
for (let i = 0; i < cssSelectors.length; i++) {
|
|
this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
|
}
|
|
}
|
|
_addSelectable(cssSelector, callbackCtxt, listContext) {
|
|
let matcher = this;
|
|
const element = cssSelector.element;
|
|
const classNames = cssSelector.classNames;
|
|
const attrs = cssSelector.attrs;
|
|
const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
|
if (element) {
|
|
const isTerminal = attrs.length === 0 && classNames.length === 0;
|
|
if (isTerminal) {
|
|
this._addTerminal(matcher._elementMap, element, selectable);
|
|
} else {
|
|
matcher = this._addPartial(matcher._elementPartialMap, element);
|
|
}
|
|
}
|
|
if (classNames) {
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
const isTerminal = attrs.length === 0 && i === classNames.length - 1;
|
|
const className = classNames[i];
|
|
if (isTerminal) {
|
|
this._addTerminal(matcher._classMap, className, selectable);
|
|
} else {
|
|
matcher = this._addPartial(matcher._classPartialMap, className);
|
|
}
|
|
}
|
|
}
|
|
if (attrs) {
|
|
for (let i = 0; i < attrs.length; i += 2) {
|
|
const isTerminal = i === attrs.length - 2;
|
|
const name = attrs[i];
|
|
const value = attrs[i + 1];
|
|
if (isTerminal) {
|
|
const terminalMap = matcher._attrValueMap;
|
|
let terminalValuesMap = terminalMap.get(name);
|
|
if (!terminalValuesMap) {
|
|
terminalValuesMap = new Map();
|
|
terminalMap.set(name, terminalValuesMap);
|
|
}
|
|
this._addTerminal(terminalValuesMap, value, selectable);
|
|
} else {
|
|
const partialMap = matcher._attrValuePartialMap;
|
|
let partialValuesMap = partialMap.get(name);
|
|
if (!partialValuesMap) {
|
|
partialValuesMap = new Map();
|
|
partialMap.set(name, partialValuesMap);
|
|
}
|
|
matcher = this._addPartial(partialValuesMap, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_addTerminal(map, name, selectable) {
|
|
let terminalList = map.get(name);
|
|
if (!terminalList) {
|
|
terminalList = [];
|
|
map.set(name, terminalList);
|
|
}
|
|
terminalList.push(selectable);
|
|
}
|
|
_addPartial(map, name) {
|
|
let matcher = map.get(name);
|
|
if (!matcher) {
|
|
matcher = new SelectorMatcher();
|
|
map.set(name, matcher);
|
|
}
|
|
return matcher;
|
|
}
|
|
match(cssSelector, matchedCallback) {
|
|
let result = false;
|
|
const element = cssSelector.element;
|
|
const classNames = cssSelector.classNames;
|
|
const attrs = cssSelector.attrs;
|
|
for (let i = 0; i < this._listContexts.length; i++) {
|
|
this._listContexts[i].alreadyMatched = false;
|
|
}
|
|
result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
|
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result;
|
|
if (classNames) {
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
const className = classNames[i];
|
|
result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
|
result = this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || result;
|
|
}
|
|
}
|
|
if (attrs) {
|
|
for (let i = 0; i < attrs.length; i += 2) {
|
|
const name = attrs[i];
|
|
const value = attrs[i + 1];
|
|
const terminalValuesMap = this._attrValueMap.get(name);
|
|
if (value) {
|
|
result = this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
|
}
|
|
result = this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
|
const partialValuesMap = this._attrValuePartialMap.get(name);
|
|
if (value) {
|
|
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
|
}
|
|
result = this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
_matchTerminal(map, name, cssSelector, matchedCallback) {
|
|
if (!map || typeof name !== 'string') {
|
|
return false;
|
|
}
|
|
let selectables = map.get(name) || [];
|
|
const starSelectables = map.get('*');
|
|
if (starSelectables) {
|
|
selectables = selectables.concat(starSelectables);
|
|
}
|
|
if (selectables.length === 0) {
|
|
return false;
|
|
}
|
|
let selectable;
|
|
let result = false;
|
|
for (let i = 0; i < selectables.length; i++) {
|
|
selectable = selectables[i];
|
|
result = selectable.finalize(cssSelector, matchedCallback) || result;
|
|
}
|
|
return result;
|
|
}
|
|
_matchPartial(map, name, cssSelector, matchedCallback) {
|
|
if (!map || typeof name !== 'string') {
|
|
return false;
|
|
}
|
|
const nestedSelector = map.get(name);
|
|
if (!nestedSelector) {
|
|
return false;
|
|
}
|
|
return nestedSelector.match(cssSelector, matchedCallback);
|
|
}
|
|
}
|
|
class SelectorListContext {
|
|
selectors;
|
|
alreadyMatched = false;
|
|
constructor(selectors) {
|
|
this.selectors = selectors;
|
|
}
|
|
}
|
|
class SelectorContext {
|
|
selector;
|
|
cbContext;
|
|
listContext;
|
|
notSelectors;
|
|
constructor(selector, cbContext, listContext) {
|
|
this.selector = selector;
|
|
this.cbContext = cbContext;
|
|
this.listContext = listContext;
|
|
this.notSelectors = selector.notSelectors;
|
|
}
|
|
finalize(cssSelector, callback) {
|
|
let result = true;
|
|
if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
|
const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
|
result = !notMatcher.match(cssSelector, null);
|
|
}
|
|
if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
|
|
if (this.listContext) {
|
|
this.listContext.alreadyMatched = true;
|
|
}
|
|
callback(this.selector, this.cbContext);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
class SelectorlessMatcher {
|
|
registry;
|
|
constructor(registry) {
|
|
this.registry = registry;
|
|
}
|
|
match(name) {
|
|
return this.registry.has(name) ? this.registry.get(name) : [];
|
|
}
|
|
}
|
|
|
|
const emitDistinctChangesOnlyDefaultValue = true;
|
|
var ViewEncapsulation$1;
|
|
(function (ViewEncapsulation) {
|
|
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
|
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
|
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
|
ViewEncapsulation[ViewEncapsulation["ExperimentalIsolatedShadowDom"] = 4] = "ExperimentalIsolatedShadowDom";
|
|
})(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
|
|
var ChangeDetectionStrategy;
|
|
(function (ChangeDetectionStrategy) {
|
|
ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
|
|
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
|
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
|
|
var InputFlags;
|
|
(function (InputFlags) {
|
|
InputFlags[InputFlags["None"] = 0] = "None";
|
|
InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
|
|
InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
|
|
})(InputFlags || (InputFlags = {}));
|
|
const CUSTOM_ELEMENTS_SCHEMA = {
|
|
name: 'custom-elements'
|
|
};
|
|
const NO_ERRORS_SCHEMA = {
|
|
name: 'no-errors-schema'
|
|
};
|
|
const Type$1 = Function;
|
|
var SecurityContext;
|
|
(function (SecurityContext) {
|
|
SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
|
|
SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
|
|
SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
|
|
SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
|
|
SecurityContext[SecurityContext["URL"] = 4] = "URL";
|
|
SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
|
|
SecurityContext[SecurityContext["ATTRIBUTE_NO_BINDING"] = 6] = "ATTRIBUTE_NO_BINDING";
|
|
})(SecurityContext || (SecurityContext = {}));
|
|
var MissingTranslationStrategy;
|
|
(function (MissingTranslationStrategy) {
|
|
MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
|
|
MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
|
|
MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
|
|
})(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
|
|
function parserSelectorToSimpleSelector(selector) {
|
|
const classes = selector.classNames && selector.classNames.length ? [8, ...selector.classNames] : [];
|
|
const elementName = selector.element && selector.element !== '*' ? selector.element : '';
|
|
return [elementName, ...selector.attrs, ...classes];
|
|
}
|
|
function parserSelectorToNegativeSelector(selector) {
|
|
const classes = selector.classNames && selector.classNames.length ? [8, ...selector.classNames] : [];
|
|
if (selector.element) {
|
|
return [1 | 4, selector.element, ...selector.attrs, ...classes];
|
|
} else if (selector.attrs.length) {
|
|
return [1 | 2, ...selector.attrs, ...classes];
|
|
} else {
|
|
return selector.classNames && selector.classNames.length ? [1 | 8, ...selector.classNames] : [];
|
|
}
|
|
}
|
|
function parserSelectorToR3Selector(selector) {
|
|
const positive = parserSelectorToSimpleSelector(selector);
|
|
const negative = selector.notSelectors && selector.notSelectors.length ? selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) : [];
|
|
return positive.concat(...negative);
|
|
}
|
|
function parseSelectorToR3Selector(selector) {
|
|
return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
|
|
}
|
|
|
|
var core = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
CUSTOM_ELEMENTS_SCHEMA: CUSTOM_ELEMENTS_SCHEMA,
|
|
get ChangeDetectionStrategy () { return ChangeDetectionStrategy; },
|
|
get InputFlags () { return InputFlags; },
|
|
get MissingTranslationStrategy () { return MissingTranslationStrategy; },
|
|
NO_ERRORS_SCHEMA: NO_ERRORS_SCHEMA,
|
|
get SecurityContext () { return SecurityContext; },
|
|
Type: Type$1,
|
|
get ViewEncapsulation () { return ViewEncapsulation$1; },
|
|
emitDistinctChangesOnlyDefaultValue: emitDistinctChangesOnlyDefaultValue,
|
|
parseSelectorToR3Selector: parseSelectorToR3Selector
|
|
});
|
|
|
|
var FactoryTarget;
|
|
(function (FactoryTarget) {
|
|
FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
|
|
FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
|
|
FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
|
|
FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
|
|
FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
|
|
})(FactoryTarget || (FactoryTarget = {}));
|
|
var R3TemplateDependencyKind$1;
|
|
(function (R3TemplateDependencyKind) {
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
|
|
})(R3TemplateDependencyKind$1 || (R3TemplateDependencyKind$1 = {}));
|
|
var ViewEncapsulation;
|
|
(function (ViewEncapsulation) {
|
|
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
|
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
|
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
|
ViewEncapsulation[ViewEncapsulation["ExperimentalIsolatedShadowDom"] = 4] = "ExperimentalIsolatedShadowDom";
|
|
})(ViewEncapsulation || (ViewEncapsulation = {}));
|
|
|
|
let textEncoder;
|
|
function digest$1(message) {
|
|
return message.id || computeDigest(message);
|
|
}
|
|
function computeDigest(message) {
|
|
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
|
}
|
|
function decimalDigest(message) {
|
|
return message.id || computeDecimalDigest(message);
|
|
}
|
|
function computeDecimalDigest(message) {
|
|
const visitor = new _SerializerIgnoreIcuExpVisitor();
|
|
const parts = message.nodes.map(a => a.visit(visitor, null));
|
|
return computeMsgId(parts.join(''), message.meaning);
|
|
}
|
|
class _SerializerVisitor {
|
|
visitText(text, context) {
|
|
return text.value;
|
|
}
|
|
visitContainer(container, context) {
|
|
return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
|
|
}
|
|
visitIcu(icu, context) {
|
|
const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
|
return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
return ph.isVoid ? `<ph tag name="${ph.startName}"/>` : `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
return `<ph block name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
|
|
}
|
|
}
|
|
const serializerVisitor$1 = new _SerializerVisitor();
|
|
function serializeNodes(nodes) {
|
|
return nodes.map(a => a.visit(serializerVisitor$1, null));
|
|
}
|
|
class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
|
|
visitIcu(icu) {
|
|
let strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
|
return `{${icu.type}, ${strCases.join(', ')}}`;
|
|
}
|
|
}
|
|
function sha1(str) {
|
|
textEncoder ??= new TextEncoder();
|
|
const utf8 = [...textEncoder.encode(str)];
|
|
const words32 = bytesToWords32(utf8, Endian.Big);
|
|
const len = utf8.length * 8;
|
|
const w = new Uint32Array(80);
|
|
let a = 0x67452301,
|
|
b = 0xefcdab89,
|
|
c = 0x98badcfe,
|
|
d = 0x10325476,
|
|
e = 0xc3d2e1f0;
|
|
words32[len >> 5] |= 0x80 << 24 - len % 32;
|
|
words32[(len + 64 >> 9 << 4) + 15] = len;
|
|
for (let i = 0; i < words32.length; i += 16) {
|
|
const h0 = a,
|
|
h1 = b,
|
|
h2 = c,
|
|
h3 = d,
|
|
h4 = e;
|
|
for (let j = 0; j < 80; j++) {
|
|
if (j < 16) {
|
|
w[j] = words32[i + j];
|
|
} else {
|
|
w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
|
|
}
|
|
const fkVal = fk(j, b, c, d);
|
|
const f = fkVal[0];
|
|
const k = fkVal[1];
|
|
const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
|
|
e = d;
|
|
d = c;
|
|
c = rol32(b, 30);
|
|
b = a;
|
|
a = temp;
|
|
}
|
|
a = add32(a, h0);
|
|
b = add32(b, h1);
|
|
c = add32(c, h2);
|
|
d = add32(d, h3);
|
|
e = add32(e, h4);
|
|
}
|
|
return toHexU32(a) + toHexU32(b) + toHexU32(c) + toHexU32(d) + toHexU32(e);
|
|
}
|
|
function toHexU32(value) {
|
|
return (value >>> 0).toString(16).padStart(8, '0');
|
|
}
|
|
function fk(index, b, c, d) {
|
|
if (index < 20) {
|
|
return [b & c | ~b & d, 0x5a827999];
|
|
}
|
|
if (index < 40) {
|
|
return [b ^ c ^ d, 0x6ed9eba1];
|
|
}
|
|
if (index < 60) {
|
|
return [b & c | b & d | c & d, 0x8f1bbcdc];
|
|
}
|
|
return [b ^ c ^ d, 0xca62c1d6];
|
|
}
|
|
function fingerprint(str) {
|
|
textEncoder ??= new TextEncoder();
|
|
const utf8 = textEncoder.encode(str);
|
|
const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength);
|
|
let hi = hash32(view, utf8.length, 0);
|
|
let lo = hash32(view, utf8.length, 102072);
|
|
if (hi == 0 && (lo == 0 || lo == 1)) {
|
|
hi = hi ^ 0x130f9bef;
|
|
lo = lo ^ -0x6b5f56d8;
|
|
}
|
|
return BigInt.asUintN(32, BigInt(hi)) << BigInt(32) | BigInt.asUintN(32, BigInt(lo));
|
|
}
|
|
function computeMsgId(msg, meaning = '') {
|
|
let msgFingerprint = fingerprint(msg);
|
|
if (meaning) {
|
|
msgFingerprint = BigInt.asUintN(64, msgFingerprint << BigInt(1)) | msgFingerprint >> BigInt(63) & BigInt(1);
|
|
msgFingerprint += fingerprint(meaning);
|
|
}
|
|
return BigInt.asUintN(63, msgFingerprint).toString();
|
|
}
|
|
function hash32(view, length, c) {
|
|
let a = 0x9e3779b9,
|
|
b = 0x9e3779b9;
|
|
let index = 0;
|
|
const end = length - 12;
|
|
for (; index <= end; index += 12) {
|
|
a += view.getUint32(index, true);
|
|
b += view.getUint32(index + 4, true);
|
|
c += view.getUint32(index + 8, true);
|
|
const res = mix(a, b, c);
|
|
a = res[0], b = res[1], c = res[2];
|
|
}
|
|
const remainder = length - index;
|
|
c += length;
|
|
if (remainder >= 4) {
|
|
a += view.getUint32(index, true);
|
|
index += 4;
|
|
if (remainder >= 8) {
|
|
b += view.getUint32(index, true);
|
|
index += 4;
|
|
if (remainder >= 9) {
|
|
c += view.getUint8(index++) << 8;
|
|
}
|
|
if (remainder >= 10) {
|
|
c += view.getUint8(index++) << 16;
|
|
}
|
|
if (remainder === 11) {
|
|
c += view.getUint8(index++) << 24;
|
|
}
|
|
} else {
|
|
if (remainder >= 5) {
|
|
b += view.getUint8(index++);
|
|
}
|
|
if (remainder >= 6) {
|
|
b += view.getUint8(index++) << 8;
|
|
}
|
|
if (remainder === 7) {
|
|
b += view.getUint8(index++) << 16;
|
|
}
|
|
}
|
|
} else {
|
|
if (remainder >= 1) {
|
|
a += view.getUint8(index++);
|
|
}
|
|
if (remainder >= 2) {
|
|
a += view.getUint8(index++) << 8;
|
|
}
|
|
if (remainder === 3) {
|
|
a += view.getUint8(index++) << 16;
|
|
}
|
|
}
|
|
return mix(a, b, c)[2];
|
|
}
|
|
function mix(a, b, c) {
|
|
a -= b;
|
|
a -= c;
|
|
a ^= c >>> 13;
|
|
b -= c;
|
|
b -= a;
|
|
b ^= a << 8;
|
|
c -= a;
|
|
c -= b;
|
|
c ^= b >>> 13;
|
|
a -= b;
|
|
a -= c;
|
|
a ^= c >>> 12;
|
|
b -= c;
|
|
b -= a;
|
|
b ^= a << 16;
|
|
c -= a;
|
|
c -= b;
|
|
c ^= b >>> 5;
|
|
a -= b;
|
|
a -= c;
|
|
a ^= c >>> 3;
|
|
b -= c;
|
|
b -= a;
|
|
b ^= a << 10;
|
|
c -= a;
|
|
c -= b;
|
|
c ^= b >>> 15;
|
|
return [a, b, c];
|
|
}
|
|
var Endian;
|
|
(function (Endian) {
|
|
Endian[Endian["Little"] = 0] = "Little";
|
|
Endian[Endian["Big"] = 1] = "Big";
|
|
})(Endian || (Endian = {}));
|
|
function add32(a, b) {
|
|
return add32to64(a, b)[1];
|
|
}
|
|
function add32to64(a, b) {
|
|
const low = (a & 0xffff) + (b & 0xffff);
|
|
const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
|
|
return [high >>> 16, high << 16 | low & 0xffff];
|
|
}
|
|
function rol32(a, count) {
|
|
return a << count | a >>> 32 - count;
|
|
}
|
|
function bytesToWords32(bytes, endian) {
|
|
const size = bytes.length + 3 >>> 2;
|
|
const words32 = [];
|
|
for (let i = 0; i < size; i++) {
|
|
words32[i] = wordAt(bytes, i * 4, endian);
|
|
}
|
|
return words32;
|
|
}
|
|
function byteAt(bytes, index) {
|
|
return index >= bytes.length ? 0 : bytes[index];
|
|
}
|
|
function wordAt(bytes, index, endian) {
|
|
let word = 0;
|
|
if (endian === Endian.Big) {
|
|
for (let i = 0; i < 4; i++) {
|
|
word += byteAt(bytes, index + i) << 24 - 8 * i;
|
|
}
|
|
} else {
|
|
for (let i = 0; i < 4; i++) {
|
|
word += byteAt(bytes, index + i) << 8 * i;
|
|
}
|
|
}
|
|
return word;
|
|
}
|
|
|
|
var TypeModifier;
|
|
(function (TypeModifier) {
|
|
TypeModifier[TypeModifier["None"] = 0] = "None";
|
|
TypeModifier[TypeModifier["Const"] = 1] = "Const";
|
|
})(TypeModifier || (TypeModifier = {}));
|
|
class Type {
|
|
modifiers;
|
|
constructor(modifiers = TypeModifier.None) {
|
|
this.modifiers = modifiers;
|
|
}
|
|
hasModifier(modifier) {
|
|
return (this.modifiers & modifier) !== 0;
|
|
}
|
|
}
|
|
var BuiltinTypeName;
|
|
(function (BuiltinTypeName) {
|
|
BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
|
|
BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
|
|
BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
|
|
BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
|
|
BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
|
|
BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
|
|
BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
|
|
BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
|
|
})(BuiltinTypeName || (BuiltinTypeName = {}));
|
|
class BuiltinType extends Type {
|
|
name;
|
|
constructor(name, modifiers) {
|
|
super(modifiers);
|
|
this.name = name;
|
|
}
|
|
visitType(visitor, context) {
|
|
return visitor.visitBuiltinType(this, context);
|
|
}
|
|
}
|
|
class ExpressionType extends Type {
|
|
value;
|
|
typeParams;
|
|
constructor(value, modifiers, typeParams = null) {
|
|
super(modifiers);
|
|
this.value = value;
|
|
this.typeParams = typeParams;
|
|
}
|
|
visitType(visitor, context) {
|
|
return visitor.visitExpressionType(this, context);
|
|
}
|
|
}
|
|
class ArrayType extends Type {
|
|
of;
|
|
constructor(of, modifiers) {
|
|
super(modifiers);
|
|
this.of = of;
|
|
}
|
|
visitType(visitor, context) {
|
|
return visitor.visitArrayType(this, context);
|
|
}
|
|
}
|
|
class MapType extends Type {
|
|
valueType;
|
|
constructor(valueType, modifiers) {
|
|
super(modifiers);
|
|
this.valueType = valueType || null;
|
|
}
|
|
visitType(visitor, context) {
|
|
return visitor.visitMapType(this, context);
|
|
}
|
|
}
|
|
class TransplantedType extends Type {
|
|
type;
|
|
constructor(type, modifiers) {
|
|
super(modifiers);
|
|
this.type = type;
|
|
}
|
|
visitType(visitor, context) {
|
|
return visitor.visitTransplantedType(this, context);
|
|
}
|
|
}
|
|
const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
|
|
const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
|
|
const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
|
|
const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
|
|
const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
|
|
const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
|
|
const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
|
|
const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
|
|
var UnaryOperator;
|
|
(function (UnaryOperator) {
|
|
UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
|
|
UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
|
|
})(UnaryOperator || (UnaryOperator = {}));
|
|
var BinaryOperator;
|
|
(function (BinaryOperator) {
|
|
BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
|
|
BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
|
|
BinaryOperator[BinaryOperator["Assign"] = 2] = "Assign";
|
|
BinaryOperator[BinaryOperator["Identical"] = 3] = "Identical";
|
|
BinaryOperator[BinaryOperator["NotIdentical"] = 4] = "NotIdentical";
|
|
BinaryOperator[BinaryOperator["Minus"] = 5] = "Minus";
|
|
BinaryOperator[BinaryOperator["Plus"] = 6] = "Plus";
|
|
BinaryOperator[BinaryOperator["Divide"] = 7] = "Divide";
|
|
BinaryOperator[BinaryOperator["Multiply"] = 8] = "Multiply";
|
|
BinaryOperator[BinaryOperator["Modulo"] = 9] = "Modulo";
|
|
BinaryOperator[BinaryOperator["And"] = 10] = "And";
|
|
BinaryOperator[BinaryOperator["Or"] = 11] = "Or";
|
|
BinaryOperator[BinaryOperator["BitwiseOr"] = 12] = "BitwiseOr";
|
|
BinaryOperator[BinaryOperator["BitwiseAnd"] = 13] = "BitwiseAnd";
|
|
BinaryOperator[BinaryOperator["Lower"] = 14] = "Lower";
|
|
BinaryOperator[BinaryOperator["LowerEquals"] = 15] = "LowerEquals";
|
|
BinaryOperator[BinaryOperator["Bigger"] = 16] = "Bigger";
|
|
BinaryOperator[BinaryOperator["BiggerEquals"] = 17] = "BiggerEquals";
|
|
BinaryOperator[BinaryOperator["NullishCoalesce"] = 18] = "NullishCoalesce";
|
|
BinaryOperator[BinaryOperator["Exponentiation"] = 19] = "Exponentiation";
|
|
BinaryOperator[BinaryOperator["In"] = 20] = "In";
|
|
BinaryOperator[BinaryOperator["AdditionAssignment"] = 21] = "AdditionAssignment";
|
|
BinaryOperator[BinaryOperator["SubtractionAssignment"] = 22] = "SubtractionAssignment";
|
|
BinaryOperator[BinaryOperator["MultiplicationAssignment"] = 23] = "MultiplicationAssignment";
|
|
BinaryOperator[BinaryOperator["DivisionAssignment"] = 24] = "DivisionAssignment";
|
|
BinaryOperator[BinaryOperator["RemainderAssignment"] = 25] = "RemainderAssignment";
|
|
BinaryOperator[BinaryOperator["ExponentiationAssignment"] = 26] = "ExponentiationAssignment";
|
|
BinaryOperator[BinaryOperator["AndAssignment"] = 27] = "AndAssignment";
|
|
BinaryOperator[BinaryOperator["OrAssignment"] = 28] = "OrAssignment";
|
|
BinaryOperator[BinaryOperator["NullishCoalesceAssignment"] = 29] = "NullishCoalesceAssignment";
|
|
})(BinaryOperator || (BinaryOperator = {}));
|
|
function nullSafeIsEquivalent(base, other) {
|
|
if (base == null || other == null) {
|
|
return base == other;
|
|
}
|
|
return base.isEquivalent(other);
|
|
}
|
|
function areAllEquivalentPredicate(base, other, equivalentPredicate) {
|
|
const len = base.length;
|
|
if (len !== other.length) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < len; i++) {
|
|
if (!equivalentPredicate(base[i], other[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function areAllEquivalent(base, other) {
|
|
return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
|
|
}
|
|
class Expression {
|
|
type;
|
|
sourceSpan;
|
|
constructor(type, sourceSpan) {
|
|
this.type = type || null;
|
|
this.sourceSpan = sourceSpan || null;
|
|
}
|
|
prop(name, sourceSpan) {
|
|
return new ReadPropExpr(this, name, null, sourceSpan);
|
|
}
|
|
key(index, type, sourceSpan) {
|
|
return new ReadKeyExpr(this, index, type, sourceSpan);
|
|
}
|
|
callFn(params, sourceSpan, pure) {
|
|
return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
|
|
}
|
|
instantiate(params, type, sourceSpan) {
|
|
return new InstantiateExpr(this, params, type, sourceSpan);
|
|
}
|
|
conditional(trueCase, falseCase = null, sourceSpan) {
|
|
return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
|
|
}
|
|
equals(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
|
|
}
|
|
notEquals(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
|
|
}
|
|
identical(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
|
|
}
|
|
notIdentical(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
|
|
}
|
|
minus(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
|
|
}
|
|
plus(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
|
|
}
|
|
divide(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
|
|
}
|
|
multiply(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
|
|
}
|
|
modulo(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
|
|
}
|
|
power(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Exponentiation, this, rhs, null, sourceSpan);
|
|
}
|
|
and(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
|
|
}
|
|
bitwiseOr(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.BitwiseOr, this, rhs, null, sourceSpan);
|
|
}
|
|
bitwiseAnd(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan);
|
|
}
|
|
or(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
|
|
}
|
|
lower(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
|
|
}
|
|
lowerEquals(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
|
|
}
|
|
bigger(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
|
|
}
|
|
biggerEquals(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
|
|
}
|
|
isBlank(sourceSpan) {
|
|
return this.equals(TYPED_NULL_EXPR, sourceSpan);
|
|
}
|
|
nullishCoalesce(rhs, sourceSpan) {
|
|
return new BinaryOperatorExpr(BinaryOperator.NullishCoalesce, this, rhs, null, sourceSpan);
|
|
}
|
|
toStmt() {
|
|
return new ExpressionStatement(this, null);
|
|
}
|
|
}
|
|
class ReadVarExpr extends Expression {
|
|
name;
|
|
constructor(name, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.name = name;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ReadVarExpr && this.name === e.name;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitReadVarExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new ReadVarExpr(this.name, this.type, this.sourceSpan);
|
|
}
|
|
set(value) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Assign, this, value, null, this.sourceSpan);
|
|
}
|
|
}
|
|
class TypeofExpr extends Expression {
|
|
expr;
|
|
constructor(expr, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.expr = expr;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitTypeofExpr(this, context);
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
|
|
}
|
|
isConstant() {
|
|
return this.expr.isConstant();
|
|
}
|
|
clone() {
|
|
return new TypeofExpr(this.expr.clone());
|
|
}
|
|
}
|
|
class VoidExpr extends Expression {
|
|
expr;
|
|
constructor(expr, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.expr = expr;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitVoidExpr(this, context);
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof VoidExpr && e.expr.isEquivalent(this.expr);
|
|
}
|
|
isConstant() {
|
|
return this.expr.isConstant();
|
|
}
|
|
clone() {
|
|
return new VoidExpr(this.expr.clone());
|
|
}
|
|
}
|
|
class WrappedNodeExpr extends Expression {
|
|
node;
|
|
constructor(node, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.node = node;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof WrappedNodeExpr && this.node === e.node;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitWrappedNodeExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new WrappedNodeExpr(this.node, this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class InvokeFunctionExpr extends Expression {
|
|
fn;
|
|
args;
|
|
pure;
|
|
constructor(fn, args, type, sourceSpan, pure = false) {
|
|
super(type, sourceSpan);
|
|
this.fn = fn;
|
|
this.args = args;
|
|
this.pure = pure;
|
|
}
|
|
get receiver() {
|
|
return this.fn;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) && areAllEquivalent(this.args, e.args) && this.pure === e.pure;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitInvokeFunctionExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new InvokeFunctionExpr(this.fn.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan, this.pure);
|
|
}
|
|
}
|
|
class TaggedTemplateLiteralExpr extends Expression {
|
|
tag;
|
|
template;
|
|
constructor(tag, template, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.tag = tag;
|
|
this.template = template;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof TaggedTemplateLiteralExpr && this.tag.isEquivalent(e.tag) && this.template.isEquivalent(e.template);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitTaggedTemplateLiteralExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new TaggedTemplateLiteralExpr(this.tag.clone(), this.template.clone(), this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class InstantiateExpr extends Expression {
|
|
classExpr;
|
|
args;
|
|
constructor(classExpr, args, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.classExpr = classExpr;
|
|
this.args = args;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) && areAllEquivalent(this.args, e.args);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitInstantiateExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new InstantiateExpr(this.classExpr.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class RegularExpressionLiteralExpr extends Expression {
|
|
body;
|
|
flags;
|
|
constructor(body, flags, sourceSpan) {
|
|
super(null, sourceSpan);
|
|
this.body = body;
|
|
this.flags = flags;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof RegularExpressionLiteralExpr && this.body === e.body && this.flags === e.flags;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitRegularExpressionLiteral(this, context);
|
|
}
|
|
clone() {
|
|
return new RegularExpressionLiteralExpr(this.body, this.flags, this.sourceSpan);
|
|
}
|
|
}
|
|
class LiteralExpr extends Expression {
|
|
value;
|
|
constructor(value, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.value = value;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof LiteralExpr && this.value === e.value;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitLiteralExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new LiteralExpr(this.value, this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class TemplateLiteralExpr extends Expression {
|
|
elements;
|
|
expressions;
|
|
constructor(elements, expressions, sourceSpan) {
|
|
super(null, sourceSpan);
|
|
this.elements = elements;
|
|
this.expressions = expressions;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof TemplateLiteralExpr && areAllEquivalentPredicate(this.elements, e.elements, (a, b) => a.text === b.text) && areAllEquivalent(this.expressions, e.expressions);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitTemplateLiteralExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new TemplateLiteralExpr(this.elements.map(el => el.clone()), this.expressions.map(expr => expr.clone()));
|
|
}
|
|
}
|
|
class TemplateLiteralElementExpr extends Expression {
|
|
text;
|
|
rawText;
|
|
constructor(text, sourceSpan, rawText) {
|
|
super(STRING_TYPE, sourceSpan);
|
|
this.text = text;
|
|
this.rawText = rawText ?? escapeForTemplateLiteral(escapeSlashes(text));
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitTemplateLiteralElementExpr(this, context);
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof TemplateLiteralElementExpr && e.text === this.text && e.rawText === this.rawText;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
clone() {
|
|
return new TemplateLiteralElementExpr(this.text, this.sourceSpan, this.rawText);
|
|
}
|
|
}
|
|
class LiteralPiece {
|
|
text;
|
|
sourceSpan;
|
|
constructor(text, sourceSpan) {
|
|
this.text = text;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
}
|
|
class PlaceholderPiece {
|
|
text;
|
|
sourceSpan;
|
|
associatedMessage;
|
|
constructor(text, sourceSpan, associatedMessage) {
|
|
this.text = text;
|
|
this.sourceSpan = sourceSpan;
|
|
this.associatedMessage = associatedMessage;
|
|
}
|
|
}
|
|
const MEANING_SEPARATOR$1 = '|';
|
|
const ID_SEPARATOR$1 = '@@';
|
|
const LEGACY_ID_INDICATOR = '␟';
|
|
class LocalizedString extends Expression {
|
|
metaBlock;
|
|
messageParts;
|
|
placeHolderNames;
|
|
expressions;
|
|
constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
|
|
super(STRING_TYPE, sourceSpan);
|
|
this.metaBlock = metaBlock;
|
|
this.messageParts = messageParts;
|
|
this.placeHolderNames = placeHolderNames;
|
|
this.expressions = expressions;
|
|
}
|
|
isEquivalent(e) {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitLocalizedString(this, context);
|
|
}
|
|
clone() {
|
|
return new LocalizedString(this.metaBlock, this.messageParts, this.placeHolderNames, this.expressions.map(expr => expr.clone()), this.sourceSpan);
|
|
}
|
|
serializeI18nHead() {
|
|
let metaBlock = this.metaBlock.description || '';
|
|
if (this.metaBlock.meaning) {
|
|
metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR$1}${metaBlock}`;
|
|
}
|
|
if (this.metaBlock.customId) {
|
|
metaBlock = `${metaBlock}${ID_SEPARATOR$1}${this.metaBlock.customId}`;
|
|
}
|
|
if (this.metaBlock.legacyIds) {
|
|
this.metaBlock.legacyIds.forEach(legacyId => {
|
|
metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
|
|
});
|
|
}
|
|
return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
|
|
}
|
|
getMessagePartSourceSpan(i) {
|
|
return this.messageParts[i]?.sourceSpan ?? this.sourceSpan;
|
|
}
|
|
getPlaceholderSourceSpan(i) {
|
|
return this.placeHolderNames[i]?.sourceSpan ?? this.expressions[i]?.sourceSpan ?? this.sourceSpan;
|
|
}
|
|
serializeI18nTemplatePart(partIndex) {
|
|
const placeholder = this.placeHolderNames[partIndex - 1];
|
|
const messagePart = this.messageParts[partIndex];
|
|
let metaBlock = placeholder.text;
|
|
if (placeholder.associatedMessage?.legacyIds.length === 0) {
|
|
metaBlock += `${ID_SEPARATOR$1}${computeMsgId(placeholder.associatedMessage.messageString, placeholder.associatedMessage.meaning)}`;
|
|
}
|
|
return createCookedRawString(metaBlock, messagePart.text, this.getMessagePartSourceSpan(partIndex));
|
|
}
|
|
}
|
|
const escapeSlashes = str => str.replace(/\\/g, '\\\\');
|
|
const escapeStartingColon = str => str.replace(/^:/, '\\:');
|
|
const escapeColons = str => str.replace(/:/g, '\\:');
|
|
const escapeForTemplateLiteral = str => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
|
|
function createCookedRawString(metaBlock, messagePart, range) {
|
|
if (metaBlock === '') {
|
|
return {
|
|
cooked: messagePart,
|
|
raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
|
|
range
|
|
};
|
|
} else {
|
|
return {
|
|
cooked: `:${metaBlock}:${messagePart}`,
|
|
raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
|
|
range
|
|
};
|
|
}
|
|
}
|
|
class ExternalExpr extends Expression {
|
|
value;
|
|
typeParams;
|
|
constructor(value, type, typeParams = null, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.value = value;
|
|
this.typeParams = typeParams;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ExternalExpr && this.value.name === e.value.name && this.value.moduleName === e.value.moduleName;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitExternalExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new ExternalExpr(this.value, this.type, this.typeParams, this.sourceSpan);
|
|
}
|
|
}
|
|
class ExternalReference {
|
|
moduleName;
|
|
name;
|
|
constructor(moduleName, name) {
|
|
this.moduleName = moduleName;
|
|
this.name = name;
|
|
}
|
|
}
|
|
class ConditionalExpr extends Expression {
|
|
condition;
|
|
falseCase;
|
|
trueCase;
|
|
constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
|
|
super(type || trueCase.type, sourceSpan);
|
|
this.condition = condition;
|
|
this.falseCase = falseCase;
|
|
this.trueCase = trueCase;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) && this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitConditionalExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new ConditionalExpr(this.condition.clone(), this.trueCase.clone(), this.falseCase?.clone(), this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class DynamicImportExpr extends Expression {
|
|
url;
|
|
urlComment;
|
|
constructor(url, sourceSpan, urlComment) {
|
|
super(null, sourceSpan);
|
|
this.url = url;
|
|
this.urlComment = urlComment;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof DynamicImportExpr && this.url === e.url && this.urlComment === e.urlComment;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitDynamicImportExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new DynamicImportExpr(typeof this.url === 'string' ? this.url : this.url.clone(), this.sourceSpan, this.urlComment);
|
|
}
|
|
}
|
|
class NotExpr extends Expression {
|
|
condition;
|
|
constructor(condition, sourceSpan) {
|
|
super(BOOL_TYPE, sourceSpan);
|
|
this.condition = condition;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitNotExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new NotExpr(this.condition.clone(), this.sourceSpan);
|
|
}
|
|
}
|
|
class FnParam {
|
|
name;
|
|
type;
|
|
constructor(name, type = null) {
|
|
this.name = name;
|
|
this.type = type;
|
|
}
|
|
isEquivalent(param) {
|
|
return this.name === param.name;
|
|
}
|
|
clone() {
|
|
return new FnParam(this.name, this.type);
|
|
}
|
|
}
|
|
class FunctionExpr extends Expression {
|
|
params;
|
|
statements;
|
|
name;
|
|
constructor(params, statements, type, sourceSpan, name) {
|
|
super(type, sourceSpan);
|
|
this.params = params;
|
|
this.statements = statements;
|
|
this.name = name;
|
|
}
|
|
isEquivalent(e) {
|
|
return (e instanceof FunctionExpr || e instanceof DeclareFunctionStmt) && areAllEquivalent(this.params, e.params) && areAllEquivalent(this.statements, e.statements);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitFunctionExpr(this, context);
|
|
}
|
|
toDeclStmt(name, modifiers) {
|
|
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
|
}
|
|
clone() {
|
|
return new FunctionExpr(this.params.map(p => p.clone()), this.statements, this.type, this.sourceSpan, this.name);
|
|
}
|
|
}
|
|
class ArrowFunctionExpr extends Expression {
|
|
params;
|
|
body;
|
|
constructor(params, body, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.params = params;
|
|
this.body = body;
|
|
}
|
|
isEquivalent(e) {
|
|
if (!(e instanceof ArrowFunctionExpr) || !areAllEquivalent(this.params, e.params)) {
|
|
return false;
|
|
}
|
|
if (this.body instanceof Expression && e.body instanceof Expression) {
|
|
return this.body.isEquivalent(e.body);
|
|
}
|
|
if (Array.isArray(this.body) && Array.isArray(e.body)) {
|
|
return areAllEquivalent(this.body, e.body);
|
|
}
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitArrowFunctionExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new ArrowFunctionExpr(this.params.map(p => p.clone()), Array.isArray(this.body) ? this.body : this.body.clone(), this.type, this.sourceSpan);
|
|
}
|
|
toDeclStmt(name, modifiers) {
|
|
return new DeclareVarStmt(name, this, INFERRED_TYPE, modifiers, this.sourceSpan);
|
|
}
|
|
}
|
|
class UnaryOperatorExpr extends Expression {
|
|
operator;
|
|
expr;
|
|
parens;
|
|
constructor(operator, expr, type, sourceSpan, parens = true) {
|
|
super(type || NUMBER_TYPE, sourceSpan);
|
|
this.operator = operator;
|
|
this.expr = expr;
|
|
this.parens = parens;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof UnaryOperatorExpr && this.operator === e.operator && this.expr.isEquivalent(e.expr);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitUnaryOperatorExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new UnaryOperatorExpr(this.operator, this.expr.clone(), this.type, this.sourceSpan, this.parens);
|
|
}
|
|
}
|
|
class ParenthesizedExpr extends Expression {
|
|
expr;
|
|
constructor(expr, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.expr = expr;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitParenthesizedExpr(this, context);
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ParenthesizedExpr && e.expr.isEquivalent(this.expr);
|
|
}
|
|
isConstant() {
|
|
return this.expr.isConstant();
|
|
}
|
|
clone() {
|
|
return new ParenthesizedExpr(this.expr.clone());
|
|
}
|
|
}
|
|
class BinaryOperatorExpr extends Expression {
|
|
operator;
|
|
rhs;
|
|
lhs;
|
|
constructor(operator, lhs, rhs, type, sourceSpan) {
|
|
super(type || lhs.type, sourceSpan);
|
|
this.operator = operator;
|
|
this.rhs = rhs;
|
|
this.lhs = lhs;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof BinaryOperatorExpr && this.operator === e.operator && this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitBinaryOperatorExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new BinaryOperatorExpr(this.operator, this.lhs.clone(), this.rhs.clone(), this.type, this.sourceSpan);
|
|
}
|
|
isAssignment() {
|
|
const op = this.operator;
|
|
return op === BinaryOperator.Assign || op === BinaryOperator.AdditionAssignment || op === BinaryOperator.SubtractionAssignment || op === BinaryOperator.MultiplicationAssignment || op === BinaryOperator.DivisionAssignment || op === BinaryOperator.RemainderAssignment || op === BinaryOperator.ExponentiationAssignment || op === BinaryOperator.AndAssignment || op === BinaryOperator.OrAssignment || op === BinaryOperator.NullishCoalesceAssignment;
|
|
}
|
|
}
|
|
class ReadPropExpr extends Expression {
|
|
receiver;
|
|
name;
|
|
constructor(receiver, name, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.name = name;
|
|
}
|
|
get index() {
|
|
return this.name;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) && this.name === e.name;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitReadPropExpr(this, context);
|
|
}
|
|
set(value) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Assign, this.receiver.prop(this.name), value, null, this.sourceSpan);
|
|
}
|
|
clone() {
|
|
return new ReadPropExpr(this.receiver.clone(), this.name, this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class ReadKeyExpr extends Expression {
|
|
receiver;
|
|
index;
|
|
constructor(receiver, index, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.index = index;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) && this.index.isEquivalent(e.index);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitReadKeyExpr(this, context);
|
|
}
|
|
set(value) {
|
|
return new BinaryOperatorExpr(BinaryOperator.Assign, this.receiver.key(this.index), value, null, this.sourceSpan);
|
|
}
|
|
clone() {
|
|
return new ReadKeyExpr(this.receiver.clone(), this.index.clone(), this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class LiteralArrayExpr extends Expression {
|
|
entries;
|
|
constructor(entries, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.entries = entries;
|
|
}
|
|
isConstant() {
|
|
return this.entries.every(e => e.isConstant());
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitLiteralArrayExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new LiteralArrayExpr(this.entries.map(e => e.clone()), this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class LiteralMapPropertyAssignment {
|
|
key;
|
|
value;
|
|
quoted;
|
|
constructor(key, value, quoted) {
|
|
this.key = key;
|
|
this.value = value;
|
|
this.quoted = quoted;
|
|
}
|
|
isEquivalent(e) {
|
|
return this.key === e.key && this.value.isEquivalent(e.value);
|
|
}
|
|
clone() {
|
|
return new LiteralMapPropertyAssignment(this.key, this.value.clone(), this.quoted);
|
|
}
|
|
isConstant() {
|
|
return this.value.isConstant();
|
|
}
|
|
}
|
|
class LiteralMapSpreadAssignment {
|
|
expression;
|
|
constructor(expression) {
|
|
this.expression = expression;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof LiteralMapSpreadAssignment && this.expression.isEquivalent(e.expression);
|
|
}
|
|
clone() {
|
|
return new LiteralMapSpreadAssignment(this.expression.clone());
|
|
}
|
|
isConstant() {
|
|
return this.expression.isConstant();
|
|
}
|
|
}
|
|
class LiteralMapExpr extends Expression {
|
|
entries;
|
|
valueType = null;
|
|
constructor(entries, type, sourceSpan) {
|
|
super(type, sourceSpan);
|
|
this.entries = entries;
|
|
if (type) {
|
|
this.valueType = type.valueType;
|
|
}
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
|
|
}
|
|
isConstant() {
|
|
return this.entries.every(e => e.isConstant());
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitLiteralMapExpr(this, context);
|
|
}
|
|
clone() {
|
|
const entriesClone = this.entries.map(entry => entry.clone());
|
|
return new LiteralMapExpr(entriesClone, this.type, this.sourceSpan);
|
|
}
|
|
}
|
|
class CommaExpr extends Expression {
|
|
parts;
|
|
constructor(parts, sourceSpan) {
|
|
super(parts[parts.length - 1].type, sourceSpan);
|
|
this.parts = parts;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitCommaExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new CommaExpr(this.parts.map(p => p.clone()));
|
|
}
|
|
}
|
|
class SpreadElementExpr extends Expression {
|
|
expression;
|
|
constructor(expression, sourceSpan) {
|
|
super(null, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof SpreadElementExpr && this.expression.isEquivalent(e.expression);
|
|
}
|
|
isConstant() {
|
|
return this.expression.isConstant();
|
|
}
|
|
visitExpression(visitor, context) {
|
|
return visitor.visitSpreadElementExpr(this, context);
|
|
}
|
|
clone() {
|
|
return new SpreadElementExpr(this.expression.clone(), this.sourceSpan);
|
|
}
|
|
}
|
|
const NULL_EXPR = new LiteralExpr(null, null, null);
|
|
const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
|
|
var StmtModifier;
|
|
(function (StmtModifier) {
|
|
StmtModifier[StmtModifier["None"] = 0] = "None";
|
|
StmtModifier[StmtModifier["Final"] = 1] = "Final";
|
|
StmtModifier[StmtModifier["Private"] = 2] = "Private";
|
|
StmtModifier[StmtModifier["Exported"] = 4] = "Exported";
|
|
StmtModifier[StmtModifier["Static"] = 8] = "Static";
|
|
})(StmtModifier || (StmtModifier = {}));
|
|
class LeadingComment {
|
|
text;
|
|
multiline;
|
|
trailingNewline;
|
|
constructor(text, multiline, trailingNewline) {
|
|
this.text = text;
|
|
this.multiline = multiline;
|
|
this.trailingNewline = trailingNewline;
|
|
}
|
|
toString() {
|
|
return this.multiline ? ` ${this.text} ` : this.text;
|
|
}
|
|
}
|
|
class JSDocComment extends LeadingComment {
|
|
tags;
|
|
constructor(tags) {
|
|
super('', true, true);
|
|
this.tags = tags;
|
|
}
|
|
toString() {
|
|
return serializeTags(this.tags);
|
|
}
|
|
}
|
|
class Statement {
|
|
modifiers;
|
|
sourceSpan;
|
|
leadingComments;
|
|
constructor(modifiers = StmtModifier.None, sourceSpan = null, leadingComments) {
|
|
this.modifiers = modifiers;
|
|
this.sourceSpan = sourceSpan;
|
|
this.leadingComments = leadingComments;
|
|
}
|
|
hasModifier(modifier) {
|
|
return (this.modifiers & modifier) !== 0;
|
|
}
|
|
addLeadingComment(leadingComment) {
|
|
this.leadingComments = this.leadingComments ?? [];
|
|
this.leadingComments.push(leadingComment);
|
|
}
|
|
}
|
|
class DeclareVarStmt extends Statement {
|
|
name;
|
|
value;
|
|
type;
|
|
constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
|
|
super(modifiers, sourceSpan, leadingComments);
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type || value && value.type || null;
|
|
}
|
|
isEquivalent(stmt) {
|
|
return stmt instanceof DeclareVarStmt && this.name === stmt.name && (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
|
|
}
|
|
visitStatement(visitor, context) {
|
|
return visitor.visitDeclareVarStmt(this, context);
|
|
}
|
|
}
|
|
class DeclareFunctionStmt extends Statement {
|
|
name;
|
|
params;
|
|
statements;
|
|
type;
|
|
constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
|
|
super(modifiers, sourceSpan, leadingComments);
|
|
this.name = name;
|
|
this.params = params;
|
|
this.statements = statements;
|
|
this.type = type || null;
|
|
}
|
|
isEquivalent(stmt) {
|
|
return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) && areAllEquivalent(this.statements, stmt.statements);
|
|
}
|
|
visitStatement(visitor, context) {
|
|
return visitor.visitDeclareFunctionStmt(this, context);
|
|
}
|
|
}
|
|
class ExpressionStatement extends Statement {
|
|
expr;
|
|
constructor(expr, sourceSpan, leadingComments) {
|
|
super(StmtModifier.None, sourceSpan, leadingComments);
|
|
this.expr = expr;
|
|
}
|
|
isEquivalent(stmt) {
|
|
return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
|
|
}
|
|
visitStatement(visitor, context) {
|
|
return visitor.visitExpressionStmt(this, context);
|
|
}
|
|
}
|
|
class ReturnStatement extends Statement {
|
|
value;
|
|
constructor(value, sourceSpan = null, leadingComments) {
|
|
super(StmtModifier.None, sourceSpan, leadingComments);
|
|
this.value = value;
|
|
}
|
|
isEquivalent(stmt) {
|
|
return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
|
|
}
|
|
visitStatement(visitor, context) {
|
|
return visitor.visitReturnStmt(this, context);
|
|
}
|
|
}
|
|
class IfStmt extends Statement {
|
|
condition;
|
|
trueCase;
|
|
falseCase;
|
|
constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
|
|
super(StmtModifier.None, sourceSpan, leadingComments);
|
|
this.condition = condition;
|
|
this.trueCase = trueCase;
|
|
this.falseCase = falseCase;
|
|
}
|
|
isEquivalent(stmt) {
|
|
return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) && areAllEquivalent(this.trueCase, stmt.trueCase) && areAllEquivalent(this.falseCase, stmt.falseCase);
|
|
}
|
|
visitStatement(visitor, context) {
|
|
return visitor.visitIfStmt(this, context);
|
|
}
|
|
}
|
|
let RecursiveAstVisitor$1 = class RecursiveAstVisitor {
|
|
visitType(ast, context) {
|
|
return ast;
|
|
}
|
|
visitExpression(ast, context) {
|
|
if (ast.type) {
|
|
ast.type.visitType(this, context);
|
|
}
|
|
return ast;
|
|
}
|
|
visitBuiltinType(type, context) {
|
|
return this.visitType(type, context);
|
|
}
|
|
visitExpressionType(type, context) {
|
|
type.value.visitExpression(this, context);
|
|
if (type.typeParams !== null) {
|
|
type.typeParams.forEach(param => this.visitType(param, context));
|
|
}
|
|
return this.visitType(type, context);
|
|
}
|
|
visitArrayType(type, context) {
|
|
return this.visitType(type, context);
|
|
}
|
|
visitMapType(type, context) {
|
|
return this.visitType(type, context);
|
|
}
|
|
visitTransplantedType(type, context) {
|
|
return type;
|
|
}
|
|
visitWrappedNodeExpr(ast, context) {
|
|
return ast;
|
|
}
|
|
visitReadVarExpr(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitDynamicImportExpr(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitInvokeFunctionExpr(ast, context) {
|
|
ast.fn.visitExpression(this, context);
|
|
this.visitAllExpressions(ast.args, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitTaggedTemplateLiteralExpr(ast, context) {
|
|
ast.tag.visitExpression(this, context);
|
|
ast.template.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitInstantiateExpr(ast, context) {
|
|
ast.classExpr.visitExpression(this, context);
|
|
this.visitAllExpressions(ast.args, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitLiteralExpr(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitRegularExpressionLiteral(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitLocalizedString(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitExternalExpr(ast, context) {
|
|
if (ast.typeParams) {
|
|
ast.typeParams.forEach(type => type.visitType(this, context));
|
|
}
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitConditionalExpr(ast, context) {
|
|
ast.condition.visitExpression(this, context);
|
|
ast.trueCase.visitExpression(this, context);
|
|
ast.falseCase.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitNotExpr(ast, context) {
|
|
ast.condition.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitFunctionExpr(ast, context) {
|
|
this.visitAllStatements(ast.statements, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitArrowFunctionExpr(ast, context) {
|
|
if (Array.isArray(ast.body)) {
|
|
this.visitAllStatements(ast.body, context);
|
|
} else {
|
|
ast.body.visitExpression(this, context);
|
|
}
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitUnaryOperatorExpr(ast, context) {
|
|
ast.expr.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitTypeofExpr(ast, context) {
|
|
ast.expr.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitVoidExpr(ast, context) {
|
|
ast.expr.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitBinaryOperatorExpr(ast, context) {
|
|
ast.lhs.visitExpression(this, context);
|
|
ast.rhs.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitReadPropExpr(ast, context) {
|
|
ast.receiver.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitReadKeyExpr(ast, context) {
|
|
ast.receiver.visitExpression(this, context);
|
|
ast.index.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitLiteralArrayExpr(ast, context) {
|
|
this.visitAllExpressions(ast.entries, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitLiteralMapExpr(ast, context) {
|
|
ast.entries.forEach(entry => {
|
|
if (entry instanceof LiteralMapSpreadAssignment) {
|
|
entry.expression.visitExpression(this, context);
|
|
} else {
|
|
entry.value.visitExpression(this, context);
|
|
}
|
|
});
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitCommaExpr(ast, context) {
|
|
this.visitAllExpressions(ast.parts, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitTemplateLiteralExpr(ast, context) {
|
|
this.visitAllExpressions(ast.elements, context);
|
|
this.visitAllExpressions(ast.expressions, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitTemplateLiteralElementExpr(ast, context) {
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitParenthesizedExpr(ast, context) {
|
|
ast.expr.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitSpreadElementExpr(ast, context) {
|
|
ast.expression.visitExpression(this, context);
|
|
return this.visitExpression(ast, context);
|
|
}
|
|
visitAllExpressions(exprs, context) {
|
|
exprs.forEach(expr => expr.visitExpression(this, context));
|
|
}
|
|
visitDeclareVarStmt(stmt, context) {
|
|
if (stmt.value) {
|
|
stmt.value.visitExpression(this, context);
|
|
}
|
|
if (stmt.type) {
|
|
stmt.type.visitType(this, context);
|
|
}
|
|
return stmt;
|
|
}
|
|
visitDeclareFunctionStmt(stmt, context) {
|
|
this.visitAllStatements(stmt.statements, context);
|
|
if (stmt.type) {
|
|
stmt.type.visitType(this, context);
|
|
}
|
|
return stmt;
|
|
}
|
|
visitExpressionStmt(stmt, context) {
|
|
stmt.expr.visitExpression(this, context);
|
|
return stmt;
|
|
}
|
|
visitReturnStmt(stmt, context) {
|
|
stmt.value.visitExpression(this, context);
|
|
return stmt;
|
|
}
|
|
visitIfStmt(stmt, context) {
|
|
stmt.condition.visitExpression(this, context);
|
|
this.visitAllStatements(stmt.trueCase, context);
|
|
this.visitAllStatements(stmt.falseCase, context);
|
|
return stmt;
|
|
}
|
|
visitAllStatements(stmts, context) {
|
|
stmts.forEach(stmt => stmt.visitStatement(this, context));
|
|
}
|
|
};
|
|
function leadingComment(text, multiline = false, trailingNewline = true) {
|
|
return new LeadingComment(text, multiline, trailingNewline);
|
|
}
|
|
function jsDocComment(tags = []) {
|
|
return new JSDocComment(tags);
|
|
}
|
|
function variable(name, type, sourceSpan) {
|
|
return new ReadVarExpr(name, type, sourceSpan);
|
|
}
|
|
function importExpr(id, typeParams = null, sourceSpan) {
|
|
return new ExternalExpr(id, null, typeParams, sourceSpan);
|
|
}
|
|
function importType(id, typeParams, typeModifiers) {
|
|
return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null;
|
|
}
|
|
function expressionType(expr, typeModifiers, typeParams) {
|
|
return new ExpressionType(expr, typeModifiers, typeParams);
|
|
}
|
|
function transplantedType(type, typeModifiers) {
|
|
return new TransplantedType(type, typeModifiers);
|
|
}
|
|
function typeofExpr(expr) {
|
|
return new TypeofExpr(expr);
|
|
}
|
|
function literalArr(values, type, sourceSpan) {
|
|
return new LiteralArrayExpr(values, type, sourceSpan);
|
|
}
|
|
function literalMap(values, type = null) {
|
|
return new LiteralMapExpr(values.map(e => new LiteralMapPropertyAssignment(e.key, e.value, e.quoted)), type, null);
|
|
}
|
|
function unary(operator, expr, type, sourceSpan) {
|
|
return new UnaryOperatorExpr(operator, expr, type, sourceSpan);
|
|
}
|
|
function not(expr, sourceSpan) {
|
|
return new NotExpr(expr, sourceSpan);
|
|
}
|
|
function fn(params, body, type, sourceSpan, name) {
|
|
return new FunctionExpr(params, body, type, sourceSpan, name);
|
|
}
|
|
function arrowFn(params, body, type, sourceSpan) {
|
|
return new ArrowFunctionExpr(params, body, type, sourceSpan);
|
|
}
|
|
function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
|
|
return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
|
|
}
|
|
function taggedTemplate(tag, template, type, sourceSpan) {
|
|
return new TaggedTemplateLiteralExpr(tag, template, type, sourceSpan);
|
|
}
|
|
function literal(value, type, sourceSpan) {
|
|
return new LiteralExpr(value, type, sourceSpan);
|
|
}
|
|
function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
|
|
return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
|
|
}
|
|
function isNull(exp) {
|
|
return exp instanceof LiteralExpr && exp.value === null;
|
|
}
|
|
function tagToString(tag) {
|
|
let out = '';
|
|
if (tag.tagName) {
|
|
out += ` @${tag.tagName}`;
|
|
}
|
|
if (tag.text) {
|
|
if (tag.text.match(/\/\*|\*\//)) {
|
|
throw new Error('JSDoc text cannot contain "/*" and "*/"');
|
|
}
|
|
out += ' ' + tag.text.replace(/@/g, '\\@');
|
|
}
|
|
return out;
|
|
}
|
|
function serializeTags(tags) {
|
|
if (tags.length === 0) return '';
|
|
if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
|
|
return `*${tagToString(tags[0])} `;
|
|
}
|
|
let out = '*\n';
|
|
for (const tag of tags) {
|
|
out += ' *';
|
|
out += tagToString(tag).replace(/\n/g, '\n * ');
|
|
out += '\n';
|
|
}
|
|
out += ' ';
|
|
return out;
|
|
}
|
|
|
|
var output_ast = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
ArrayType: ArrayType,
|
|
ArrowFunctionExpr: ArrowFunctionExpr,
|
|
BOOL_TYPE: BOOL_TYPE,
|
|
get BinaryOperator () { return BinaryOperator; },
|
|
BinaryOperatorExpr: BinaryOperatorExpr,
|
|
BuiltinType: BuiltinType,
|
|
get BuiltinTypeName () { return BuiltinTypeName; },
|
|
CommaExpr: CommaExpr,
|
|
ConditionalExpr: ConditionalExpr,
|
|
DYNAMIC_TYPE: DYNAMIC_TYPE,
|
|
DeclareFunctionStmt: DeclareFunctionStmt,
|
|
DeclareVarStmt: DeclareVarStmt,
|
|
DynamicImportExpr: DynamicImportExpr,
|
|
Expression: Expression,
|
|
ExpressionStatement: ExpressionStatement,
|
|
ExpressionType: ExpressionType,
|
|
ExternalExpr: ExternalExpr,
|
|
ExternalReference: ExternalReference,
|
|
FUNCTION_TYPE: FUNCTION_TYPE,
|
|
FnParam: FnParam,
|
|
FunctionExpr: FunctionExpr,
|
|
INFERRED_TYPE: INFERRED_TYPE,
|
|
INT_TYPE: INT_TYPE,
|
|
IfStmt: IfStmt,
|
|
InstantiateExpr: InstantiateExpr,
|
|
InvokeFunctionExpr: InvokeFunctionExpr,
|
|
JSDocComment: JSDocComment,
|
|
LeadingComment: LeadingComment,
|
|
LiteralArrayExpr: LiteralArrayExpr,
|
|
LiteralExpr: LiteralExpr,
|
|
LiteralMapExpr: LiteralMapExpr,
|
|
LiteralMapPropertyAssignment: LiteralMapPropertyAssignment,
|
|
LiteralMapSpreadAssignment: LiteralMapSpreadAssignment,
|
|
LiteralPiece: LiteralPiece,
|
|
LocalizedString: LocalizedString,
|
|
MapType: MapType,
|
|
NONE_TYPE: NONE_TYPE,
|
|
NULL_EXPR: NULL_EXPR,
|
|
NUMBER_TYPE: NUMBER_TYPE,
|
|
NotExpr: NotExpr,
|
|
ParenthesizedExpr: ParenthesizedExpr,
|
|
PlaceholderPiece: PlaceholderPiece,
|
|
ReadKeyExpr: ReadKeyExpr,
|
|
ReadPropExpr: ReadPropExpr,
|
|
ReadVarExpr: ReadVarExpr,
|
|
RecursiveAstVisitor: RecursiveAstVisitor$1,
|
|
RegularExpressionLiteralExpr: RegularExpressionLiteralExpr,
|
|
ReturnStatement: ReturnStatement,
|
|
STRING_TYPE: STRING_TYPE,
|
|
SpreadElementExpr: SpreadElementExpr,
|
|
Statement: Statement,
|
|
get StmtModifier () { return StmtModifier; },
|
|
TYPED_NULL_EXPR: TYPED_NULL_EXPR,
|
|
TaggedTemplateLiteralExpr: TaggedTemplateLiteralExpr,
|
|
TemplateLiteralElementExpr: TemplateLiteralElementExpr,
|
|
TemplateLiteralExpr: TemplateLiteralExpr,
|
|
TransplantedType: TransplantedType,
|
|
Type: Type,
|
|
get TypeModifier () { return TypeModifier; },
|
|
TypeofExpr: TypeofExpr,
|
|
get UnaryOperator () { return UnaryOperator; },
|
|
UnaryOperatorExpr: UnaryOperatorExpr,
|
|
VoidExpr: VoidExpr,
|
|
WrappedNodeExpr: WrappedNodeExpr,
|
|
areAllEquivalent: areAllEquivalent,
|
|
arrowFn: arrowFn,
|
|
expressionType: expressionType,
|
|
fn: fn,
|
|
ifStmt: ifStmt,
|
|
importExpr: importExpr,
|
|
importType: importType,
|
|
isNull: isNull,
|
|
jsDocComment: jsDocComment,
|
|
leadingComment: leadingComment,
|
|
literal: literal,
|
|
literalArr: literalArr,
|
|
literalMap: literalMap,
|
|
localizedString: localizedString,
|
|
not: not,
|
|
nullSafeIsEquivalent: nullSafeIsEquivalent,
|
|
taggedTemplate: taggedTemplate,
|
|
transplantedType: transplantedType,
|
|
typeofExpr: typeofExpr,
|
|
unary: unary,
|
|
variable: variable
|
|
});
|
|
|
|
const CONSTANT_PREFIX = '_c';
|
|
const KEY_CONTEXT = {};
|
|
const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
|
|
class FixupExpression extends Expression {
|
|
resolved;
|
|
original;
|
|
shared = false;
|
|
constructor(resolved) {
|
|
super(resolved.type);
|
|
this.resolved = resolved;
|
|
this.original = resolved;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
if (context === KEY_CONTEXT) {
|
|
return this.original.visitExpression(visitor, context);
|
|
} else {
|
|
return this.resolved.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
clone() {
|
|
throw new Error(`Not supported.`);
|
|
}
|
|
fixup(expression) {
|
|
this.resolved = expression;
|
|
this.shared = true;
|
|
}
|
|
}
|
|
class ConstantPool {
|
|
isClosureCompilerEnabled;
|
|
statements = [];
|
|
literals = new Map();
|
|
literalFactories = new Map();
|
|
sharedConstants = new Map();
|
|
_claimedNames = new Map();
|
|
nextNameIndex = 0;
|
|
constructor(isClosureCompilerEnabled = false) {
|
|
this.isClosureCompilerEnabled = isClosureCompilerEnabled;
|
|
}
|
|
getConstLiteral(literal, forceShared) {
|
|
if (literal instanceof LiteralExpr && !isLongStringLiteral(literal) || literal instanceof FixupExpression) {
|
|
return literal;
|
|
}
|
|
const key = GenericKeyFn.INSTANCE.keyOf(literal);
|
|
let fixup = this.literals.get(key);
|
|
let newValue = false;
|
|
if (!fixup) {
|
|
fixup = new FixupExpression(literal);
|
|
this.literals.set(key, fixup);
|
|
newValue = true;
|
|
}
|
|
if (!newValue && !fixup.shared || newValue && forceShared) {
|
|
const name = this.freshName();
|
|
let value;
|
|
let usage;
|
|
if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
|
|
value = new FunctionExpr([], [new ReturnStatement(literal)]);
|
|
usage = variable(name).callFn([]);
|
|
} else {
|
|
value = literal;
|
|
usage = variable(name);
|
|
}
|
|
this.statements.push(new DeclareVarStmt(name, value, INFERRED_TYPE, StmtModifier.Final));
|
|
fixup.fixup(usage);
|
|
}
|
|
return fixup;
|
|
}
|
|
getSharedConstant(def, expr) {
|
|
const key = def.keyOf(expr);
|
|
if (!this.sharedConstants.has(key)) {
|
|
const id = this.freshName();
|
|
this.sharedConstants.set(key, variable(id));
|
|
this.statements.push(def.toSharedConstantDeclaration(id, expr));
|
|
}
|
|
return this.sharedConstants.get(key);
|
|
}
|
|
getSharedFunctionReference(fn, prefix, useUniqueName = true) {
|
|
const isArrow = fn instanceof ArrowFunctionExpr;
|
|
for (const current of this.statements) {
|
|
if (isArrow && current instanceof DeclareVarStmt && current.value?.isEquivalent(fn)) {
|
|
return variable(current.name);
|
|
}
|
|
if (!isArrow && current instanceof DeclareFunctionStmt && fn instanceof FunctionExpr && fn.isEquivalent(current)) {
|
|
return variable(current.name);
|
|
}
|
|
}
|
|
const name = useUniqueName ? this.uniqueName(prefix) : prefix;
|
|
this.statements.push(fn instanceof FunctionExpr ? fn.toDeclStmt(name, StmtModifier.Final) : new DeclareVarStmt(name, fn, INFERRED_TYPE, StmtModifier.Final, fn.sourceSpan));
|
|
return variable(name);
|
|
}
|
|
uniqueName(name, alwaysIncludeSuffix = true) {
|
|
const count = this._claimedNames.get(name) ?? 0;
|
|
const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`;
|
|
this._claimedNames.set(name, count + 1);
|
|
return result;
|
|
}
|
|
freshName() {
|
|
return this.uniqueName(CONSTANT_PREFIX);
|
|
}
|
|
}
|
|
class GenericKeyFn {
|
|
static INSTANCE = new GenericKeyFn();
|
|
keyOf(expr) {
|
|
if (expr instanceof LiteralExpr && typeof expr.value === 'string') {
|
|
return `"${expr.value}"`;
|
|
} else if (expr instanceof LiteralExpr) {
|
|
return String(expr.value);
|
|
} else if (expr instanceof RegularExpressionLiteralExpr) {
|
|
return `/${expr.body}/${expr.flags ?? ''}`;
|
|
} else if (expr instanceof LiteralArrayExpr) {
|
|
const entries = [];
|
|
for (const entry of expr.entries) {
|
|
entries.push(this.keyOf(entry));
|
|
}
|
|
return `[${entries.join(',')}]`;
|
|
} else if (expr instanceof LiteralMapExpr) {
|
|
const entries = [];
|
|
for (const entry of expr.entries) {
|
|
if (entry instanceof LiteralMapSpreadAssignment) {
|
|
entries.push('...' + this.keyOf(entry.expression));
|
|
} else {
|
|
let key = entry.key;
|
|
if (entry.quoted) {
|
|
key = `"${key}"`;
|
|
}
|
|
entries.push(key + ':' + this.keyOf(entry.value));
|
|
}
|
|
}
|
|
return `{${entries.join(',')}}`;
|
|
} else if (expr instanceof ExternalExpr) {
|
|
return `import("${expr.value.moduleName}", ${expr.value.name})`;
|
|
} else if (expr instanceof ReadVarExpr) {
|
|
return `read(${expr.name})`;
|
|
} else if (expr instanceof TypeofExpr) {
|
|
return `typeof(${this.keyOf(expr.expr)})`;
|
|
} else if (expr instanceof SpreadElementExpr) {
|
|
return `...${this.keyOf(expr.expression)}`;
|
|
} else {
|
|
throw new Error(`${this.constructor.name} does not handle expressions of type ${expr.constructor.name}`);
|
|
}
|
|
}
|
|
}
|
|
function isLongStringLiteral(expr) {
|
|
return expr instanceof LiteralExpr && typeof expr.value === 'string' && expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
|
|
}
|
|
|
|
const CORE = '@angular/core';
|
|
class Identifiers {
|
|
static core = {
|
|
name: null,
|
|
moduleName: CORE
|
|
};
|
|
static namespaceHTML = {
|
|
name: 'ɵɵnamespaceHTML',
|
|
moduleName: CORE
|
|
};
|
|
static namespaceMathML = {
|
|
name: 'ɵɵnamespaceMathML',
|
|
moduleName: CORE
|
|
};
|
|
static namespaceSVG = {
|
|
name: 'ɵɵnamespaceSVG',
|
|
moduleName: CORE
|
|
};
|
|
static element = {
|
|
name: 'ɵɵelement',
|
|
moduleName: CORE
|
|
};
|
|
static elementStart = {
|
|
name: 'ɵɵelementStart',
|
|
moduleName: CORE
|
|
};
|
|
static elementEnd = {
|
|
name: 'ɵɵelementEnd',
|
|
moduleName: CORE
|
|
};
|
|
static domElement = {
|
|
name: 'ɵɵdomElement',
|
|
moduleName: CORE
|
|
};
|
|
static domElementStart = {
|
|
name: 'ɵɵdomElementStart',
|
|
moduleName: CORE
|
|
};
|
|
static domElementEnd = {
|
|
name: 'ɵɵdomElementEnd',
|
|
moduleName: CORE
|
|
};
|
|
static domElementContainer = {
|
|
name: 'ɵɵdomElementContainer',
|
|
moduleName: CORE
|
|
};
|
|
static domElementContainerStart = {
|
|
name: 'ɵɵdomElementContainerStart',
|
|
moduleName: CORE
|
|
};
|
|
static domElementContainerEnd = {
|
|
name: 'ɵɵdomElementContainerEnd',
|
|
moduleName: CORE
|
|
};
|
|
static domTemplate = {
|
|
name: 'ɵɵdomTemplate',
|
|
moduleName: CORE
|
|
};
|
|
static domListener = {
|
|
name: 'ɵɵdomListener',
|
|
moduleName: CORE
|
|
};
|
|
static advance = {
|
|
name: 'ɵɵadvance',
|
|
moduleName: CORE
|
|
};
|
|
static syntheticHostProperty = {
|
|
name: 'ɵɵsyntheticHostProperty',
|
|
moduleName: CORE
|
|
};
|
|
static syntheticHostListener = {
|
|
name: 'ɵɵsyntheticHostListener',
|
|
moduleName: CORE
|
|
};
|
|
static attribute = {
|
|
name: 'ɵɵattribute',
|
|
moduleName: CORE
|
|
};
|
|
static classProp = {
|
|
name: 'ɵɵclassProp',
|
|
moduleName: CORE
|
|
};
|
|
static elementContainerStart = {
|
|
name: 'ɵɵelementContainerStart',
|
|
moduleName: CORE
|
|
};
|
|
static elementContainerEnd = {
|
|
name: 'ɵɵelementContainerEnd',
|
|
moduleName: CORE
|
|
};
|
|
static elementContainer = {
|
|
name: 'ɵɵelementContainer',
|
|
moduleName: CORE
|
|
};
|
|
static styleMap = {
|
|
name: 'ɵɵstyleMap',
|
|
moduleName: CORE
|
|
};
|
|
static classMap = {
|
|
name: 'ɵɵclassMap',
|
|
moduleName: CORE
|
|
};
|
|
static styleProp = {
|
|
name: 'ɵɵstyleProp',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate = {
|
|
name: 'ɵɵinterpolate',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate1 = {
|
|
name: 'ɵɵinterpolate1',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate2 = {
|
|
name: 'ɵɵinterpolate2',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate3 = {
|
|
name: 'ɵɵinterpolate3',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate4 = {
|
|
name: 'ɵɵinterpolate4',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate5 = {
|
|
name: 'ɵɵinterpolate5',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate6 = {
|
|
name: 'ɵɵinterpolate6',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate7 = {
|
|
name: 'ɵɵinterpolate7',
|
|
moduleName: CORE
|
|
};
|
|
static interpolate8 = {
|
|
name: 'ɵɵinterpolate8',
|
|
moduleName: CORE
|
|
};
|
|
static interpolateV = {
|
|
name: 'ɵɵinterpolateV',
|
|
moduleName: CORE
|
|
};
|
|
static nextContext = {
|
|
name: 'ɵɵnextContext',
|
|
moduleName: CORE
|
|
};
|
|
static resetView = {
|
|
name: 'ɵɵresetView',
|
|
moduleName: CORE
|
|
};
|
|
static templateCreate = {
|
|
name: 'ɵɵtemplate',
|
|
moduleName: CORE
|
|
};
|
|
static defer = {
|
|
name: 'ɵɵdefer',
|
|
moduleName: CORE
|
|
};
|
|
static deferWhen = {
|
|
name: 'ɵɵdeferWhen',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnIdle = {
|
|
name: 'ɵɵdeferOnIdle',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnImmediate = {
|
|
name: 'ɵɵdeferOnImmediate',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnTimer = {
|
|
name: 'ɵɵdeferOnTimer',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnHover = {
|
|
name: 'ɵɵdeferOnHover',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnInteraction = {
|
|
name: 'ɵɵdeferOnInteraction',
|
|
moduleName: CORE
|
|
};
|
|
static deferOnViewport = {
|
|
name: 'ɵɵdeferOnViewport',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchWhen = {
|
|
name: 'ɵɵdeferPrefetchWhen',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnIdle = {
|
|
name: 'ɵɵdeferPrefetchOnIdle',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnImmediate = {
|
|
name: 'ɵɵdeferPrefetchOnImmediate',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnTimer = {
|
|
name: 'ɵɵdeferPrefetchOnTimer',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnHover = {
|
|
name: 'ɵɵdeferPrefetchOnHover',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnInteraction = {
|
|
name: 'ɵɵdeferPrefetchOnInteraction',
|
|
moduleName: CORE
|
|
};
|
|
static deferPrefetchOnViewport = {
|
|
name: 'ɵɵdeferPrefetchOnViewport',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateWhen = {
|
|
name: 'ɵɵdeferHydrateWhen',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateNever = {
|
|
name: 'ɵɵdeferHydrateNever',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnIdle = {
|
|
name: 'ɵɵdeferHydrateOnIdle',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnImmediate = {
|
|
name: 'ɵɵdeferHydrateOnImmediate',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnTimer = {
|
|
name: 'ɵɵdeferHydrateOnTimer',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnHover = {
|
|
name: 'ɵɵdeferHydrateOnHover',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnInteraction = {
|
|
name: 'ɵɵdeferHydrateOnInteraction',
|
|
moduleName: CORE
|
|
};
|
|
static deferHydrateOnViewport = {
|
|
name: 'ɵɵdeferHydrateOnViewport',
|
|
moduleName: CORE
|
|
};
|
|
static deferEnableTimerScheduling = {
|
|
name: 'ɵɵdeferEnableTimerScheduling',
|
|
moduleName: CORE
|
|
};
|
|
static conditionalCreate = {
|
|
name: 'ɵɵconditionalCreate',
|
|
moduleName: CORE
|
|
};
|
|
static conditionalBranchCreate = {
|
|
name: 'ɵɵconditionalBranchCreate',
|
|
moduleName: CORE
|
|
};
|
|
static conditional = {
|
|
name: 'ɵɵconditional',
|
|
moduleName: CORE
|
|
};
|
|
static repeater = {
|
|
name: 'ɵɵrepeater',
|
|
moduleName: CORE
|
|
};
|
|
static repeaterCreate = {
|
|
name: 'ɵɵrepeaterCreate',
|
|
moduleName: CORE
|
|
};
|
|
static repeaterTrackByIndex = {
|
|
name: 'ɵɵrepeaterTrackByIndex',
|
|
moduleName: CORE
|
|
};
|
|
static repeaterTrackByIdentity = {
|
|
name: 'ɵɵrepeaterTrackByIdentity',
|
|
moduleName: CORE
|
|
};
|
|
static componentInstance = {
|
|
name: 'ɵɵcomponentInstance',
|
|
moduleName: CORE
|
|
};
|
|
static text = {
|
|
name: 'ɵɵtext',
|
|
moduleName: CORE
|
|
};
|
|
static enableBindings = {
|
|
name: 'ɵɵenableBindings',
|
|
moduleName: CORE
|
|
};
|
|
static disableBindings = {
|
|
name: 'ɵɵdisableBindings',
|
|
moduleName: CORE
|
|
};
|
|
static getCurrentView = {
|
|
name: 'ɵɵgetCurrentView',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate = {
|
|
name: 'ɵɵtextInterpolate',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate1 = {
|
|
name: 'ɵɵtextInterpolate1',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate2 = {
|
|
name: 'ɵɵtextInterpolate2',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate3 = {
|
|
name: 'ɵɵtextInterpolate3',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate4 = {
|
|
name: 'ɵɵtextInterpolate4',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate5 = {
|
|
name: 'ɵɵtextInterpolate5',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate6 = {
|
|
name: 'ɵɵtextInterpolate6',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate7 = {
|
|
name: 'ɵɵtextInterpolate7',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolate8 = {
|
|
name: 'ɵɵtextInterpolate8',
|
|
moduleName: CORE
|
|
};
|
|
static textInterpolateV = {
|
|
name: 'ɵɵtextInterpolateV',
|
|
moduleName: CORE
|
|
};
|
|
static restoreView = {
|
|
name: 'ɵɵrestoreView',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction0 = {
|
|
name: 'ɵɵpureFunction0',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction1 = {
|
|
name: 'ɵɵpureFunction1',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction2 = {
|
|
name: 'ɵɵpureFunction2',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction3 = {
|
|
name: 'ɵɵpureFunction3',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction4 = {
|
|
name: 'ɵɵpureFunction4',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction5 = {
|
|
name: 'ɵɵpureFunction5',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction6 = {
|
|
name: 'ɵɵpureFunction6',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction7 = {
|
|
name: 'ɵɵpureFunction7',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunction8 = {
|
|
name: 'ɵɵpureFunction8',
|
|
moduleName: CORE
|
|
};
|
|
static pureFunctionV = {
|
|
name: 'ɵɵpureFunctionV',
|
|
moduleName: CORE
|
|
};
|
|
static pipeBind1 = {
|
|
name: 'ɵɵpipeBind1',
|
|
moduleName: CORE
|
|
};
|
|
static pipeBind2 = {
|
|
name: 'ɵɵpipeBind2',
|
|
moduleName: CORE
|
|
};
|
|
static pipeBind3 = {
|
|
name: 'ɵɵpipeBind3',
|
|
moduleName: CORE
|
|
};
|
|
static pipeBind4 = {
|
|
name: 'ɵɵpipeBind4',
|
|
moduleName: CORE
|
|
};
|
|
static pipeBindV = {
|
|
name: 'ɵɵpipeBindV',
|
|
moduleName: CORE
|
|
};
|
|
static domProperty = {
|
|
name: 'ɵɵdomProperty',
|
|
moduleName: CORE
|
|
};
|
|
static ariaProperty = {
|
|
name: 'ɵɵariaProperty',
|
|
moduleName: CORE
|
|
};
|
|
static property = {
|
|
name: 'ɵɵproperty',
|
|
moduleName: CORE
|
|
};
|
|
static control = {
|
|
name: 'ɵɵcontrol',
|
|
moduleName: CORE
|
|
};
|
|
static controlCreate = {
|
|
name: 'ɵɵcontrolCreate',
|
|
moduleName: CORE
|
|
};
|
|
static animationEnterListener = {
|
|
name: 'ɵɵanimateEnterListener',
|
|
moduleName: CORE
|
|
};
|
|
static animationLeaveListener = {
|
|
name: 'ɵɵanimateLeaveListener',
|
|
moduleName: CORE
|
|
};
|
|
static animationEnter = {
|
|
name: 'ɵɵanimateEnter',
|
|
moduleName: CORE
|
|
};
|
|
static animationLeave = {
|
|
name: 'ɵɵanimateLeave',
|
|
moduleName: CORE
|
|
};
|
|
static i18n = {
|
|
name: 'ɵɵi18n',
|
|
moduleName: CORE
|
|
};
|
|
static i18nAttributes = {
|
|
name: 'ɵɵi18nAttributes',
|
|
moduleName: CORE
|
|
};
|
|
static i18nExp = {
|
|
name: 'ɵɵi18nExp',
|
|
moduleName: CORE
|
|
};
|
|
static i18nStart = {
|
|
name: 'ɵɵi18nStart',
|
|
moduleName: CORE
|
|
};
|
|
static i18nEnd = {
|
|
name: 'ɵɵi18nEnd',
|
|
moduleName: CORE
|
|
};
|
|
static i18nApply = {
|
|
name: 'ɵɵi18nApply',
|
|
moduleName: CORE
|
|
};
|
|
static i18nPostprocess = {
|
|
name: 'ɵɵi18nPostprocess',
|
|
moduleName: CORE
|
|
};
|
|
static pipe = {
|
|
name: 'ɵɵpipe',
|
|
moduleName: CORE
|
|
};
|
|
static projection = {
|
|
name: 'ɵɵprojection',
|
|
moduleName: CORE
|
|
};
|
|
static projectionDef = {
|
|
name: 'ɵɵprojectionDef',
|
|
moduleName: CORE
|
|
};
|
|
static reference = {
|
|
name: 'ɵɵreference',
|
|
moduleName: CORE
|
|
};
|
|
static inject = {
|
|
name: 'ɵɵinject',
|
|
moduleName: CORE
|
|
};
|
|
static injectAttribute = {
|
|
name: 'ɵɵinjectAttribute',
|
|
moduleName: CORE
|
|
};
|
|
static directiveInject = {
|
|
name: 'ɵɵdirectiveInject',
|
|
moduleName: CORE
|
|
};
|
|
static invalidFactory = {
|
|
name: 'ɵɵinvalidFactory',
|
|
moduleName: CORE
|
|
};
|
|
static invalidFactoryDep = {
|
|
name: 'ɵɵinvalidFactoryDep',
|
|
moduleName: CORE
|
|
};
|
|
static templateRefExtractor = {
|
|
name: 'ɵɵtemplateRefExtractor',
|
|
moduleName: CORE
|
|
};
|
|
static forwardRef = {
|
|
name: 'forwardRef',
|
|
moduleName: CORE
|
|
};
|
|
static resolveForwardRef = {
|
|
name: 'resolveForwardRef',
|
|
moduleName: CORE
|
|
};
|
|
static replaceMetadata = {
|
|
name: 'ɵɵreplaceMetadata',
|
|
moduleName: CORE
|
|
};
|
|
static getReplaceMetadataURL = {
|
|
name: 'ɵɵgetReplaceMetadataURL',
|
|
moduleName: CORE
|
|
};
|
|
static ɵɵdefineInjectable = {
|
|
name: 'ɵɵdefineInjectable',
|
|
moduleName: CORE
|
|
};
|
|
static declareInjectable = {
|
|
name: 'ɵɵngDeclareInjectable',
|
|
moduleName: CORE
|
|
};
|
|
static InjectableDeclaration = {
|
|
name: 'ɵɵInjectableDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static resolveWindow = {
|
|
name: 'ɵɵresolveWindow',
|
|
moduleName: CORE
|
|
};
|
|
static resolveDocument = {
|
|
name: 'ɵɵresolveDocument',
|
|
moduleName: CORE
|
|
};
|
|
static resolveBody = {
|
|
name: 'ɵɵresolveBody',
|
|
moduleName: CORE
|
|
};
|
|
static getComponentDepsFactory = {
|
|
name: 'ɵɵgetComponentDepsFactory',
|
|
moduleName: CORE
|
|
};
|
|
static defineComponent = {
|
|
name: 'ɵɵdefineComponent',
|
|
moduleName: CORE
|
|
};
|
|
static declareComponent = {
|
|
name: 'ɵɵngDeclareComponent',
|
|
moduleName: CORE
|
|
};
|
|
static setComponentScope = {
|
|
name: 'ɵɵsetComponentScope',
|
|
moduleName: CORE
|
|
};
|
|
static ChangeDetectionStrategy = {
|
|
name: 'ChangeDetectionStrategy',
|
|
moduleName: CORE
|
|
};
|
|
static ViewEncapsulation = {
|
|
name: 'ViewEncapsulation',
|
|
moduleName: CORE
|
|
};
|
|
static ComponentDeclaration = {
|
|
name: 'ɵɵComponentDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static FactoryDeclaration = {
|
|
name: 'ɵɵFactoryDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static declareFactory = {
|
|
name: 'ɵɵngDeclareFactory',
|
|
moduleName: CORE
|
|
};
|
|
static FactoryTarget = {
|
|
name: 'ɵɵFactoryTarget',
|
|
moduleName: CORE
|
|
};
|
|
static defineDirective = {
|
|
name: 'ɵɵdefineDirective',
|
|
moduleName: CORE
|
|
};
|
|
static declareDirective = {
|
|
name: 'ɵɵngDeclareDirective',
|
|
moduleName: CORE
|
|
};
|
|
static DirectiveDeclaration = {
|
|
name: 'ɵɵDirectiveDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static InjectorDef = {
|
|
name: 'ɵɵInjectorDef',
|
|
moduleName: CORE
|
|
};
|
|
static InjectorDeclaration = {
|
|
name: 'ɵɵInjectorDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static defineInjector = {
|
|
name: 'ɵɵdefineInjector',
|
|
moduleName: CORE
|
|
};
|
|
static declareInjector = {
|
|
name: 'ɵɵngDeclareInjector',
|
|
moduleName: CORE
|
|
};
|
|
static NgModuleDeclaration = {
|
|
name: 'ɵɵNgModuleDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static ModuleWithProviders = {
|
|
name: 'ModuleWithProviders',
|
|
moduleName: CORE
|
|
};
|
|
static defineNgModule = {
|
|
name: 'ɵɵdefineNgModule',
|
|
moduleName: CORE
|
|
};
|
|
static declareNgModule = {
|
|
name: 'ɵɵngDeclareNgModule',
|
|
moduleName: CORE
|
|
};
|
|
static setNgModuleScope = {
|
|
name: 'ɵɵsetNgModuleScope',
|
|
moduleName: CORE
|
|
};
|
|
static registerNgModuleType = {
|
|
name: 'ɵɵregisterNgModuleType',
|
|
moduleName: CORE
|
|
};
|
|
static PipeDeclaration = {
|
|
name: 'ɵɵPipeDeclaration',
|
|
moduleName: CORE
|
|
};
|
|
static definePipe = {
|
|
name: 'ɵɵdefinePipe',
|
|
moduleName: CORE
|
|
};
|
|
static declarePipe = {
|
|
name: 'ɵɵngDeclarePipe',
|
|
moduleName: CORE
|
|
};
|
|
static declareClassMetadata = {
|
|
name: 'ɵɵngDeclareClassMetadata',
|
|
moduleName: CORE
|
|
};
|
|
static declareClassMetadataAsync = {
|
|
name: 'ɵɵngDeclareClassMetadataAsync',
|
|
moduleName: CORE
|
|
};
|
|
static setClassMetadata = {
|
|
name: 'ɵsetClassMetadata',
|
|
moduleName: CORE
|
|
};
|
|
static setClassMetadataAsync = {
|
|
name: 'ɵsetClassMetadataAsync',
|
|
moduleName: CORE
|
|
};
|
|
static setClassDebugInfo = {
|
|
name: 'ɵsetClassDebugInfo',
|
|
moduleName: CORE
|
|
};
|
|
static queryRefresh = {
|
|
name: 'ɵɵqueryRefresh',
|
|
moduleName: CORE
|
|
};
|
|
static viewQuery = {
|
|
name: 'ɵɵviewQuery',
|
|
moduleName: CORE
|
|
};
|
|
static loadQuery = {
|
|
name: 'ɵɵloadQuery',
|
|
moduleName: CORE
|
|
};
|
|
static contentQuery = {
|
|
name: 'ɵɵcontentQuery',
|
|
moduleName: CORE
|
|
};
|
|
static viewQuerySignal = {
|
|
name: 'ɵɵviewQuerySignal',
|
|
moduleName: CORE
|
|
};
|
|
static contentQuerySignal = {
|
|
name: 'ɵɵcontentQuerySignal',
|
|
moduleName: CORE
|
|
};
|
|
static queryAdvance = {
|
|
name: 'ɵɵqueryAdvance',
|
|
moduleName: CORE
|
|
};
|
|
static twoWayProperty = {
|
|
name: 'ɵɵtwoWayProperty',
|
|
moduleName: CORE
|
|
};
|
|
static twoWayBindingSet = {
|
|
name: 'ɵɵtwoWayBindingSet',
|
|
moduleName: CORE
|
|
};
|
|
static twoWayListener = {
|
|
name: 'ɵɵtwoWayListener',
|
|
moduleName: CORE
|
|
};
|
|
static declareLet = {
|
|
name: 'ɵɵdeclareLet',
|
|
moduleName: CORE
|
|
};
|
|
static storeLet = {
|
|
name: 'ɵɵstoreLet',
|
|
moduleName: CORE
|
|
};
|
|
static readContextLet = {
|
|
name: 'ɵɵreadContextLet',
|
|
moduleName: CORE
|
|
};
|
|
static attachSourceLocations = {
|
|
name: 'ɵɵattachSourceLocations',
|
|
moduleName: CORE
|
|
};
|
|
static NgOnChangesFeature = {
|
|
name: 'ɵɵNgOnChangesFeature',
|
|
moduleName: CORE
|
|
};
|
|
static InheritDefinitionFeature = {
|
|
name: 'ɵɵInheritDefinitionFeature',
|
|
moduleName: CORE
|
|
};
|
|
static ProvidersFeature = {
|
|
name: 'ɵɵProvidersFeature',
|
|
moduleName: CORE
|
|
};
|
|
static HostDirectivesFeature = {
|
|
name: 'ɵɵHostDirectivesFeature',
|
|
moduleName: CORE
|
|
};
|
|
static ExternalStylesFeature = {
|
|
name: 'ɵɵExternalStylesFeature',
|
|
moduleName: CORE
|
|
};
|
|
static listener = {
|
|
name: 'ɵɵlistener',
|
|
moduleName: CORE
|
|
};
|
|
static getInheritedFactory = {
|
|
name: 'ɵɵgetInheritedFactory',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeHtml = {
|
|
name: 'ɵɵsanitizeHtml',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeStyle = {
|
|
name: 'ɵɵsanitizeStyle',
|
|
moduleName: CORE
|
|
};
|
|
static validateAttribute = {
|
|
name: 'ɵɵvalidateAttribute',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeResourceUrl = {
|
|
name: 'ɵɵsanitizeResourceUrl',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeScript = {
|
|
name: 'ɵɵsanitizeScript',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeUrl = {
|
|
name: 'ɵɵsanitizeUrl',
|
|
moduleName: CORE
|
|
};
|
|
static sanitizeUrlOrResourceUrl = {
|
|
name: 'ɵɵsanitizeUrlOrResourceUrl',
|
|
moduleName: CORE
|
|
};
|
|
static trustConstantHtml = {
|
|
name: 'ɵɵtrustConstantHtml',
|
|
moduleName: CORE
|
|
};
|
|
static trustConstantResourceUrl = {
|
|
name: 'ɵɵtrustConstantResourceUrl',
|
|
moduleName: CORE
|
|
};
|
|
static inputDecorator = {
|
|
name: 'Input',
|
|
moduleName: CORE
|
|
};
|
|
static outputDecorator = {
|
|
name: 'Output',
|
|
moduleName: CORE
|
|
};
|
|
static viewChildDecorator = {
|
|
name: 'ViewChild',
|
|
moduleName: CORE
|
|
};
|
|
static viewChildrenDecorator = {
|
|
name: 'ViewChildren',
|
|
moduleName: CORE
|
|
};
|
|
static contentChildDecorator = {
|
|
name: 'ContentChild',
|
|
moduleName: CORE
|
|
};
|
|
static contentChildrenDecorator = {
|
|
name: 'ContentChildren',
|
|
moduleName: CORE
|
|
};
|
|
static InputSignalBrandWriteType = {
|
|
name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE',
|
|
moduleName: CORE
|
|
};
|
|
static UnwrapDirectiveSignalInputs = {
|
|
name: 'ɵUnwrapDirectiveSignalInputs',
|
|
moduleName: CORE
|
|
};
|
|
static unwrapWritableSignal = {
|
|
name: 'ɵunwrapWritableSignal',
|
|
moduleName: CORE
|
|
};
|
|
static assertType = {
|
|
name: 'ɵassertType',
|
|
moduleName: CORE
|
|
};
|
|
}
|
|
|
|
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
|
function dashCaseToCamelCase(input) {
|
|
return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
|
|
}
|
|
function splitAtColon(input, defaultValues) {
|
|
return _splitAt(input, ':', defaultValues);
|
|
}
|
|
function splitAtPeriod(input, defaultValues) {
|
|
return _splitAt(input, '.', defaultValues);
|
|
}
|
|
function _splitAt(input, character, defaultValues) {
|
|
const characterIndex = input.indexOf(character);
|
|
if (characterIndex == -1) return defaultValues;
|
|
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
|
|
}
|
|
function noUndefined(val) {
|
|
return val === undefined ? null : val;
|
|
}
|
|
function escapeRegExp(s) {
|
|
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
|
}
|
|
function utf8Encode(str) {
|
|
let encoded = [];
|
|
for (let index = 0; index < str.length; index++) {
|
|
let codePoint = str.charCodeAt(index);
|
|
if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > index + 1) {
|
|
const low = str.charCodeAt(index + 1);
|
|
if (low >= 0xdc00 && low <= 0xdfff) {
|
|
index++;
|
|
codePoint = (codePoint - 0xd800 << 10) + low - 0xdc00 + 0x10000;
|
|
}
|
|
}
|
|
if (codePoint <= 0x7f) {
|
|
encoded.push(codePoint);
|
|
} else if (codePoint <= 0x7ff) {
|
|
encoded.push(codePoint >> 6 & 0x1f | 0xc0, codePoint & 0x3f | 0x80);
|
|
} else if (codePoint <= 0xffff) {
|
|
encoded.push(codePoint >> 12 | 0xe0, codePoint >> 6 & 0x3f | 0x80, codePoint & 0x3f | 0x80);
|
|
} else if (codePoint <= 0x1fffff) {
|
|
encoded.push(codePoint >> 18 & 0x07 | 0xf0, codePoint >> 12 & 0x3f | 0x80, codePoint >> 6 & 0x3f | 0x80, codePoint & 0x3f | 0x80);
|
|
}
|
|
}
|
|
return encoded;
|
|
}
|
|
function stringify(token) {
|
|
if (typeof token === 'string') {
|
|
return token;
|
|
}
|
|
if (Array.isArray(token)) {
|
|
return `[${token.map(stringify).join(', ')}]`;
|
|
}
|
|
if (token == null) {
|
|
return '' + token;
|
|
}
|
|
const name = token.overriddenName || token.name;
|
|
if (name) {
|
|
return `${name}`;
|
|
}
|
|
if (!token.toString) {
|
|
return 'object';
|
|
}
|
|
const result = token.toString();
|
|
if (result == null) {
|
|
return '' + result;
|
|
}
|
|
const newLineIndex = result.indexOf('\n');
|
|
return newLineIndex >= 0 ? result.slice(0, newLineIndex) : result;
|
|
}
|
|
class Version {
|
|
full;
|
|
major;
|
|
minor;
|
|
patch;
|
|
constructor(full) {
|
|
this.full = full;
|
|
const splits = full.split('.');
|
|
this.major = splits[0];
|
|
this.minor = splits[1];
|
|
this.patch = splits.slice(2).join('.');
|
|
}
|
|
}
|
|
const _global = globalThis;
|
|
const V1_TO_18 = /^([1-9]|1[0-8])\./;
|
|
function getJitStandaloneDefaultForVersion(version) {
|
|
if (version.startsWith('0.')) {
|
|
return true;
|
|
}
|
|
if (V1_TO_18.test(version)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const VERSION$1 = 3;
|
|
const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
|
|
class SourceMapGenerator {
|
|
file;
|
|
sourcesContent = new Map();
|
|
lines = [];
|
|
lastCol0 = 0;
|
|
hasMappings = false;
|
|
constructor(file = null) {
|
|
this.file = file;
|
|
}
|
|
addSource(url, content = null) {
|
|
if (!this.sourcesContent.has(url)) {
|
|
this.sourcesContent.set(url, content);
|
|
}
|
|
return this;
|
|
}
|
|
addLine() {
|
|
this.lines.push([]);
|
|
this.lastCol0 = 0;
|
|
return this;
|
|
}
|
|
addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
|
|
if (!this.currentLine) {
|
|
throw new Error(`A line must be added before mappings can be added`);
|
|
}
|
|
if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
|
|
throw new Error(`Unknown source file "${sourceUrl}"`);
|
|
}
|
|
if (col0 == null) {
|
|
throw new Error(`The column in the generated code must be provided`);
|
|
}
|
|
if (col0 < this.lastCol0) {
|
|
throw new Error(`Mapping should be added in output order`);
|
|
}
|
|
if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
|
|
throw new Error(`The source location must be provided when a source url is provided`);
|
|
}
|
|
this.hasMappings = true;
|
|
this.lastCol0 = col0;
|
|
this.currentLine.push({
|
|
col0,
|
|
sourceUrl,
|
|
sourceLine0,
|
|
sourceCol0
|
|
});
|
|
return this;
|
|
}
|
|
get currentLine() {
|
|
return this.lines.slice(-1)[0];
|
|
}
|
|
toJSON() {
|
|
if (!this.hasMappings) {
|
|
return null;
|
|
}
|
|
const sourcesIndex = new Map();
|
|
const sources = [];
|
|
const sourcesContent = [];
|
|
Array.from(this.sourcesContent.keys()).forEach((url, i) => {
|
|
sourcesIndex.set(url, i);
|
|
sources.push(url);
|
|
sourcesContent.push(this.sourcesContent.get(url) || null);
|
|
});
|
|
let mappings = '';
|
|
let lastCol0 = 0;
|
|
let lastSourceIndex = 0;
|
|
let lastSourceLine0 = 0;
|
|
let lastSourceCol0 = 0;
|
|
this.lines.forEach(segments => {
|
|
lastCol0 = 0;
|
|
mappings += segments.map(segment => {
|
|
let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
|
|
lastCol0 = segment.col0;
|
|
if (segment.sourceUrl != null) {
|
|
segAsStr += toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
|
|
lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
|
|
segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
|
|
lastSourceLine0 = segment.sourceLine0;
|
|
segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
|
|
lastSourceCol0 = segment.sourceCol0;
|
|
}
|
|
return segAsStr;
|
|
}).join(',');
|
|
mappings += ';';
|
|
});
|
|
mappings = mappings.slice(0, -1);
|
|
return {
|
|
'file': this.file || '',
|
|
'version': VERSION$1,
|
|
'sourceRoot': '',
|
|
'sources': sources,
|
|
'sourcesContent': sourcesContent,
|
|
'mappings': mappings
|
|
};
|
|
}
|
|
toJsComment() {
|
|
return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) : '';
|
|
}
|
|
}
|
|
function toBase64String(value) {
|
|
let b64 = '';
|
|
const encoded = utf8Encode(value);
|
|
for (let i = 0; i < encoded.length;) {
|
|
const i1 = encoded[i++];
|
|
const i2 = i < encoded.length ? encoded[i++] : null;
|
|
const i3 = i < encoded.length ? encoded[i++] : null;
|
|
b64 += toBase64Digit(i1 >> 2);
|
|
b64 += toBase64Digit((i1 & 3) << 4 | (i2 === null ? 0 : i2 >> 4));
|
|
b64 += i2 === null ? '=' : toBase64Digit((i2 & 15) << 2 | (i3 === null ? 0 : i3 >> 6));
|
|
b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
|
|
}
|
|
return b64;
|
|
}
|
|
function toBase64VLQ(value) {
|
|
value = value < 0 ? (-value << 1) + 1 : value << 1;
|
|
let out = '';
|
|
do {
|
|
let digit = value & 31;
|
|
value = value >> 5;
|
|
if (value > 0) {
|
|
digit = digit | 32;
|
|
}
|
|
out += toBase64Digit(digit);
|
|
} while (value > 0);
|
|
return out;
|
|
}
|
|
const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
function toBase64Digit(value) {
|
|
if (value < 0 || value >= 64) {
|
|
throw new Error(`Can only encode value in the range [0, 63]`);
|
|
}
|
|
return B64_DIGITS[value];
|
|
}
|
|
|
|
const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
|
const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
const _INDENT_WITH = ' ';
|
|
class _EmittedLine {
|
|
indent;
|
|
partsLength = 0;
|
|
parts = [];
|
|
srcSpans = [];
|
|
constructor(indent) {
|
|
this.indent = indent;
|
|
}
|
|
}
|
|
const BINARY_OPERATORS$1 = new Map([[BinaryOperator.And, '&&'], [BinaryOperator.Bigger, '>'], [BinaryOperator.BiggerEquals, '>='], [BinaryOperator.BitwiseOr, '|'], [BinaryOperator.BitwiseAnd, '&'], [BinaryOperator.Divide, '/'], [BinaryOperator.Assign, '='], [BinaryOperator.Equals, '=='], [BinaryOperator.Identical, '==='], [BinaryOperator.Lower, '<'], [BinaryOperator.LowerEquals, '<='], [BinaryOperator.Minus, '-'], [BinaryOperator.Modulo, '%'], [BinaryOperator.Exponentiation, '**'], [BinaryOperator.Multiply, '*'], [BinaryOperator.NotEquals, '!='], [BinaryOperator.NotIdentical, '!=='], [BinaryOperator.NullishCoalesce, '??'], [BinaryOperator.Or, '||'], [BinaryOperator.Plus, '+'], [BinaryOperator.In, 'in'], [BinaryOperator.AdditionAssignment, '+='], [BinaryOperator.SubtractionAssignment, '-='], [BinaryOperator.MultiplicationAssignment, '*='], [BinaryOperator.DivisionAssignment, '/='], [BinaryOperator.RemainderAssignment, '%='], [BinaryOperator.ExponentiationAssignment, '**='], [BinaryOperator.AndAssignment, '&&='], [BinaryOperator.OrAssignment, '||='], [BinaryOperator.NullishCoalesceAssignment, '??=']]);
|
|
class EmitterVisitorContext {
|
|
_indent;
|
|
static createRoot() {
|
|
return new EmitterVisitorContext(0);
|
|
}
|
|
_lines;
|
|
constructor(_indent) {
|
|
this._indent = _indent;
|
|
this._lines = [new _EmittedLine(_indent)];
|
|
}
|
|
get _currentLine() {
|
|
return this._lines[this._lines.length - 1];
|
|
}
|
|
println(from, lastPart = '') {
|
|
this.print(from || null, lastPart, true);
|
|
}
|
|
lineIsEmpty() {
|
|
return this._currentLine.parts.length === 0;
|
|
}
|
|
lineLength() {
|
|
return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
|
|
}
|
|
print(from, part, newLine = false) {
|
|
if (part.length > 0) {
|
|
this._currentLine.parts.push(part);
|
|
this._currentLine.partsLength += part.length;
|
|
this._currentLine.srcSpans.push(from && from.sourceSpan || null);
|
|
}
|
|
if (newLine) {
|
|
this._lines.push(new _EmittedLine(this._indent));
|
|
}
|
|
}
|
|
removeEmptyLastLine() {
|
|
if (this.lineIsEmpty()) {
|
|
this._lines.pop();
|
|
}
|
|
}
|
|
incIndent() {
|
|
this._indent++;
|
|
if (this.lineIsEmpty()) {
|
|
this._currentLine.indent = this._indent;
|
|
}
|
|
}
|
|
decIndent() {
|
|
this._indent--;
|
|
if (this.lineIsEmpty()) {
|
|
this._currentLine.indent = this._indent;
|
|
}
|
|
}
|
|
toSource() {
|
|
return this.sourceLines.map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '').join('\n');
|
|
}
|
|
toSourceMapGenerator(genFilePath, startsAtLine = 0) {
|
|
const map = new SourceMapGenerator(genFilePath);
|
|
let firstOffsetMapped = false;
|
|
const mapFirstOffsetIfNeeded = () => {
|
|
if (!firstOffsetMapped) {
|
|
map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
|
|
firstOffsetMapped = true;
|
|
}
|
|
};
|
|
for (let i = 0; i < startsAtLine; i++) {
|
|
map.addLine();
|
|
mapFirstOffsetIfNeeded();
|
|
}
|
|
this.sourceLines.forEach((line, lineIdx) => {
|
|
map.addLine();
|
|
const spans = line.srcSpans;
|
|
const parts = line.parts;
|
|
let col0 = line.indent * _INDENT_WITH.length;
|
|
let spanIdx = 0;
|
|
while (spanIdx < spans.length && !spans[spanIdx]) {
|
|
col0 += parts[spanIdx].length;
|
|
spanIdx++;
|
|
}
|
|
if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
|
|
firstOffsetMapped = true;
|
|
} else {
|
|
mapFirstOffsetIfNeeded();
|
|
}
|
|
while (spanIdx < spans.length) {
|
|
const span = spans[spanIdx];
|
|
const source = span.start.file;
|
|
const sourceLine = span.start.line;
|
|
const sourceCol = span.start.col;
|
|
map.addSource(source.url, source.content).addMapping(col0, source.url, sourceLine, sourceCol);
|
|
col0 += parts[spanIdx].length;
|
|
spanIdx++;
|
|
while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
|
|
col0 += parts[spanIdx].length;
|
|
spanIdx++;
|
|
}
|
|
}
|
|
});
|
|
return map;
|
|
}
|
|
spanOf(line, column) {
|
|
const emittedLine = this._lines[line];
|
|
if (emittedLine) {
|
|
let columnsLeft = column - _createIndent(emittedLine.indent).length;
|
|
for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
|
|
const part = emittedLine.parts[partIndex];
|
|
if (part.length > columnsLeft) {
|
|
return emittedLine.srcSpans[partIndex];
|
|
}
|
|
columnsLeft -= part.length;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
get sourceLines() {
|
|
if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
|
|
return this._lines.slice(0, -1);
|
|
}
|
|
return this._lines;
|
|
}
|
|
}
|
|
class AbstractEmitterVisitor {
|
|
_escapeDollarInStrings;
|
|
lastIfCondition = null;
|
|
constructor(_escapeDollarInStrings) {
|
|
this._escapeDollarInStrings = _escapeDollarInStrings;
|
|
}
|
|
printLeadingComments(stmt, ctx) {
|
|
if (stmt.leadingComments === undefined) {
|
|
return;
|
|
}
|
|
for (const comment of stmt.leadingComments) {
|
|
if (comment instanceof JSDocComment) {
|
|
ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
|
|
} else {
|
|
if (comment.multiline) {
|
|
ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
|
|
} else {
|
|
comment.text.split('\n').forEach(line => {
|
|
ctx.println(stmt, `// ${line}`);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
visitExpressionStmt(stmt, ctx) {
|
|
this.printLeadingComments(stmt, ctx);
|
|
stmt.expr.visitExpression(this, ctx);
|
|
ctx.println(stmt, ';');
|
|
return null;
|
|
}
|
|
visitReturnStmt(stmt, ctx) {
|
|
this.printLeadingComments(stmt, ctx);
|
|
ctx.print(stmt, `return `);
|
|
stmt.value.visitExpression(this, ctx);
|
|
ctx.println(stmt, ';');
|
|
return null;
|
|
}
|
|
visitIfStmt(stmt, ctx) {
|
|
this.printLeadingComments(stmt, ctx);
|
|
ctx.print(stmt, `if (`);
|
|
this.lastIfCondition = stmt.condition;
|
|
stmt.condition.visitExpression(this, ctx);
|
|
this.lastIfCondition = null;
|
|
ctx.print(stmt, `) {`);
|
|
const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
|
|
if (stmt.trueCase.length <= 1 && !hasElseCase) {
|
|
ctx.print(stmt, ` `);
|
|
this.visitAllStatements(stmt.trueCase, ctx);
|
|
ctx.removeEmptyLastLine();
|
|
ctx.print(stmt, ` `);
|
|
} else {
|
|
ctx.println();
|
|
ctx.incIndent();
|
|
this.visitAllStatements(stmt.trueCase, ctx);
|
|
ctx.decIndent();
|
|
if (hasElseCase) {
|
|
ctx.println(stmt, `} else {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(stmt.falseCase, ctx);
|
|
ctx.decIndent();
|
|
}
|
|
}
|
|
ctx.println(stmt, `}`);
|
|
return null;
|
|
}
|
|
visitInvokeFunctionExpr(expr, ctx) {
|
|
const shouldParenthesize = expr.fn instanceof ArrowFunctionExpr;
|
|
if (shouldParenthesize) {
|
|
ctx.print(expr.fn, '(');
|
|
}
|
|
expr.fn.visitExpression(this, ctx);
|
|
if (shouldParenthesize) {
|
|
ctx.print(expr.fn, ')');
|
|
}
|
|
ctx.print(expr, `(`);
|
|
this.visitAllExpressions(expr.args, ctx, ',');
|
|
ctx.print(expr, `)`);
|
|
return null;
|
|
}
|
|
visitTaggedTemplateLiteralExpr(expr, ctx) {
|
|
expr.tag.visitExpression(this, ctx);
|
|
expr.template.visitExpression(this, ctx);
|
|
return null;
|
|
}
|
|
visitTemplateLiteralExpr(expr, ctx) {
|
|
ctx.print(expr, '`');
|
|
for (let i = 0; i < expr.elements.length; i++) {
|
|
expr.elements[i].visitExpression(this, ctx);
|
|
const expression = i < expr.expressions.length ? expr.expressions[i] : null;
|
|
if (expression !== null) {
|
|
ctx.print(expression, '${');
|
|
expression.visitExpression(this, ctx);
|
|
ctx.print(expression, '}');
|
|
}
|
|
}
|
|
ctx.print(expr, '`');
|
|
}
|
|
visitTemplateLiteralElementExpr(expr, ctx) {
|
|
ctx.print(expr, expr.rawText);
|
|
}
|
|
visitWrappedNodeExpr(ast, ctx) {
|
|
throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
|
|
}
|
|
visitTypeofExpr(expr, ctx) {
|
|
ctx.print(expr, 'typeof ');
|
|
expr.expr.visitExpression(this, ctx);
|
|
}
|
|
visitVoidExpr(expr, ctx) {
|
|
ctx.print(expr, 'void ');
|
|
expr.expr.visitExpression(this, ctx);
|
|
}
|
|
visitReadVarExpr(ast, ctx) {
|
|
ctx.print(ast, ast.name);
|
|
return null;
|
|
}
|
|
visitInstantiateExpr(ast, ctx) {
|
|
ctx.print(ast, `new `);
|
|
ast.classExpr.visitExpression(this, ctx);
|
|
ctx.print(ast, `(`);
|
|
this.visitAllExpressions(ast.args, ctx, ',');
|
|
ctx.print(ast, `)`);
|
|
return null;
|
|
}
|
|
visitLiteralExpr(ast, ctx) {
|
|
const value = ast.value;
|
|
if (typeof value === 'string') {
|
|
ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
|
|
} else {
|
|
ctx.print(ast, `${value}`);
|
|
}
|
|
return null;
|
|
}
|
|
visitRegularExpressionLiteral(ast, ctx) {
|
|
ctx.print(ast, `/${ast.body}/${ast.flags || ''}`);
|
|
return null;
|
|
}
|
|
visitLocalizedString(ast, ctx) {
|
|
const head = ast.serializeI18nHead();
|
|
ctx.print(ast, '$localize `' + head.raw);
|
|
for (let i = 1; i < ast.messageParts.length; i++) {
|
|
ctx.print(ast, '${');
|
|
ast.expressions[i - 1].visitExpression(this, ctx);
|
|
ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
|
|
}
|
|
ctx.print(ast, '`');
|
|
return null;
|
|
}
|
|
visitConditionalExpr(ast, ctx) {
|
|
ctx.print(ast, `(`);
|
|
ast.condition.visitExpression(this, ctx);
|
|
ctx.print(ast, '? ');
|
|
ast.trueCase.visitExpression(this, ctx);
|
|
ctx.print(ast, ': ');
|
|
ast.falseCase.visitExpression(this, ctx);
|
|
ctx.print(ast, `)`);
|
|
return null;
|
|
}
|
|
visitDynamicImportExpr(ast, ctx) {
|
|
ctx.print(ast, `import(${ast.url})`);
|
|
}
|
|
visitNotExpr(ast, ctx) {
|
|
ctx.print(ast, '!');
|
|
ast.condition.visitExpression(this, ctx);
|
|
return null;
|
|
}
|
|
visitUnaryOperatorExpr(ast, ctx) {
|
|
let opStr;
|
|
switch (ast.operator) {
|
|
case UnaryOperator.Plus:
|
|
opStr = '+';
|
|
break;
|
|
case UnaryOperator.Minus:
|
|
opStr = '-';
|
|
break;
|
|
default:
|
|
throw new Error(`Unknown operator ${ast.operator}`);
|
|
}
|
|
const parens = ast !== this.lastIfCondition;
|
|
if (parens) ctx.print(ast, `(`);
|
|
ctx.print(ast, opStr);
|
|
ast.expr.visitExpression(this, ctx);
|
|
if (parens) ctx.print(ast, `)`);
|
|
return null;
|
|
}
|
|
visitBinaryOperatorExpr(ast, ctx) {
|
|
const operator = BINARY_OPERATORS$1.get(ast.operator);
|
|
if (!operator) {
|
|
throw new Error(`Unknown operator ${ast.operator}`);
|
|
}
|
|
const parens = ast !== this.lastIfCondition;
|
|
if (parens) ctx.print(ast, `(`);
|
|
ast.lhs.visitExpression(this, ctx);
|
|
ctx.print(ast, ` ${operator} `);
|
|
ast.rhs.visitExpression(this, ctx);
|
|
if (parens) ctx.print(ast, `)`);
|
|
return null;
|
|
}
|
|
visitReadPropExpr(ast, ctx) {
|
|
ast.receiver.visitExpression(this, ctx);
|
|
ctx.print(ast, `.`);
|
|
ctx.print(ast, ast.name);
|
|
return null;
|
|
}
|
|
visitReadKeyExpr(ast, ctx) {
|
|
ast.receiver.visitExpression(this, ctx);
|
|
ctx.print(ast, `[`);
|
|
ast.index.visitExpression(this, ctx);
|
|
ctx.print(ast, `]`);
|
|
return null;
|
|
}
|
|
visitLiteralArrayExpr(ast, ctx) {
|
|
ctx.print(ast, `[`);
|
|
this.visitAllExpressions(ast.entries, ctx, ',');
|
|
ctx.print(ast, `]`);
|
|
return null;
|
|
}
|
|
visitLiteralMapExpr(ast, ctx) {
|
|
ctx.print(ast, `{`);
|
|
this.visitAllObjects(entry => {
|
|
if (entry instanceof LiteralMapSpreadAssignment) {
|
|
ctx.print(ast, '...');
|
|
entry.expression.visitExpression(this, ctx);
|
|
} else {
|
|
ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
|
|
entry.value.visitExpression(this, ctx);
|
|
}
|
|
}, ast.entries, ctx, ',');
|
|
ctx.print(ast, `}`);
|
|
return null;
|
|
}
|
|
visitCommaExpr(ast, ctx) {
|
|
ctx.print(ast, '(');
|
|
this.visitAllExpressions(ast.parts, ctx, ',');
|
|
ctx.print(ast, ')');
|
|
return null;
|
|
}
|
|
visitParenthesizedExpr(ast, ctx) {
|
|
ast.expr.visitExpression(this, ctx);
|
|
}
|
|
visitSpreadElementExpr(ast, ctx) {
|
|
ctx.print(ast, '...');
|
|
ast.expression.visitExpression(this, ctx);
|
|
}
|
|
visitAllExpressions(expressions, ctx, separator) {
|
|
this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
|
|
}
|
|
visitAllObjects(handler, expressions, ctx, separator) {
|
|
let incrementedIndent = false;
|
|
for (let i = 0; i < expressions.length; i++) {
|
|
if (i > 0) {
|
|
if (ctx.lineLength() > 80) {
|
|
ctx.print(null, separator, true);
|
|
if (!incrementedIndent) {
|
|
ctx.incIndent();
|
|
ctx.incIndent();
|
|
incrementedIndent = true;
|
|
}
|
|
} else {
|
|
ctx.print(null, separator, false);
|
|
}
|
|
}
|
|
handler(expressions[i]);
|
|
}
|
|
if (incrementedIndent) {
|
|
ctx.decIndent();
|
|
ctx.decIndent();
|
|
}
|
|
}
|
|
visitAllStatements(statements, ctx) {
|
|
statements.forEach(stmt => stmt.visitStatement(this, ctx));
|
|
}
|
|
}
|
|
function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
|
|
if (input == null) {
|
|
return null;
|
|
}
|
|
const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
|
|
if (match[0] == '$') {
|
|
return escapeDollar ? '\\$' : '$';
|
|
} else if (match[0] == '\n') {
|
|
return '\\n';
|
|
} else if (match[0] == '\r') {
|
|
return '\\r';
|
|
} else {
|
|
return `\\${match[0]}`;
|
|
}
|
|
});
|
|
const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
|
return requiresQuotes ? `'${body}'` : body;
|
|
}
|
|
function _createIndent(count) {
|
|
let res = '';
|
|
for (let i = 0; i < count; i++) {
|
|
res += _INDENT_WITH;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function typeWithParameters(type, numParams) {
|
|
if (numParams === 0) {
|
|
return expressionType(type);
|
|
}
|
|
const params = [];
|
|
for (let i = 0; i < numParams; i++) {
|
|
params.push(DYNAMIC_TYPE);
|
|
}
|
|
return expressionType(type, undefined, params);
|
|
}
|
|
function getSafePropertyAccessString(accessor, name) {
|
|
const escapedName = escapeIdentifier(name, false, false);
|
|
return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
|
|
}
|
|
function jitOnlyGuardedExpression(expr) {
|
|
return guardedExpression('ngJitMode', expr);
|
|
}
|
|
function devOnlyGuardedExpression(expr) {
|
|
return guardedExpression('ngDevMode', expr);
|
|
}
|
|
function guardedExpression(guard, expr) {
|
|
const guardExpr = new ExternalExpr({
|
|
name: guard,
|
|
moduleName: null
|
|
});
|
|
const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined'));
|
|
const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, undefined, undefined);
|
|
return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
|
|
}
|
|
function wrapReference(value) {
|
|
const wrapped = new WrappedNodeExpr(value);
|
|
return {
|
|
value: wrapped,
|
|
type: wrapped
|
|
};
|
|
}
|
|
function refsToArray(refs, shouldForwardDeclare) {
|
|
const values = literalArr(refs.map(ref => ref.value));
|
|
return shouldForwardDeclare ? arrowFn([], values) : values;
|
|
}
|
|
function createMayBeForwardRefExpression(expression, forwardRef) {
|
|
return {
|
|
expression,
|
|
forwardRef
|
|
};
|
|
}
|
|
function convertFromMaybeForwardRefExpression({
|
|
expression,
|
|
forwardRef
|
|
}) {
|
|
switch (forwardRef) {
|
|
case 0:
|
|
case 1:
|
|
return expression;
|
|
case 2:
|
|
return generateForwardRef(expression);
|
|
}
|
|
}
|
|
function generateForwardRef(expr) {
|
|
return importExpr(Identifiers.forwardRef).callFn([arrowFn([], expr)]);
|
|
}
|
|
|
|
var R3FactoryDelegateType;
|
|
(function (R3FactoryDelegateType) {
|
|
R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
|
|
R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
|
|
})(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
|
|
function compileFactoryFunction(meta) {
|
|
const t = variable('__ngFactoryType__');
|
|
let baseFactoryVar = null;
|
|
const typeForCtor = !isDelegatedFactoryMetadata(meta) ? new BinaryOperatorExpr(BinaryOperator.Or, t, meta.type.value) : t;
|
|
let ctorExpr = null;
|
|
if (meta.deps !== null) {
|
|
if (meta.deps !== 'invalid') {
|
|
ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.target));
|
|
}
|
|
} else {
|
|
baseFactoryVar = variable(`ɵ${meta.name}_BaseFactory`);
|
|
ctorExpr = baseFactoryVar.callFn([typeForCtor]);
|
|
}
|
|
const body = [];
|
|
let retExpr = null;
|
|
function makeConditionalFactory(nonCtorExpr) {
|
|
const r = variable('__ngConditionalFactory__');
|
|
body.push(new DeclareVarStmt(r.name, NULL_EXPR, INFERRED_TYPE));
|
|
const ctorStmt = ctorExpr !== null ? r.set(ctorExpr).toStmt() : importExpr(Identifiers.invalidFactory).callFn([]).toStmt();
|
|
body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
|
|
return r;
|
|
}
|
|
if (isDelegatedFactoryMetadata(meta)) {
|
|
const delegateArgs = injectDependencies(meta.delegateDeps, meta.target);
|
|
const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ? InstantiateExpr : InvokeFunctionExpr)(meta.delegate, delegateArgs);
|
|
retExpr = makeConditionalFactory(factoryExpr);
|
|
} else if (isExpressionFactoryMetadata(meta)) {
|
|
retExpr = makeConditionalFactory(meta.expression);
|
|
} else {
|
|
retExpr = ctorExpr;
|
|
}
|
|
if (retExpr === null) {
|
|
body.push(importExpr(Identifiers.invalidFactory).callFn([]).toStmt());
|
|
} else if (baseFactoryVar !== null) {
|
|
const getInheritedFactoryCall = importExpr(Identifiers.getInheritedFactory).callFn([meta.type.value]);
|
|
const baseFactory = new BinaryOperatorExpr(BinaryOperator.Or, baseFactoryVar, baseFactoryVar.set(getInheritedFactoryCall));
|
|
body.push(new ReturnStatement(baseFactory.callFn([typeForCtor])));
|
|
} else {
|
|
body.push(new ReturnStatement(retExpr));
|
|
}
|
|
let factoryFn = fn([new FnParam(t.name, DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`);
|
|
if (baseFactoryVar !== null) {
|
|
factoryFn = arrowFn([], [new DeclareVarStmt(baseFactoryVar.name), new ReturnStatement(factoryFn)]).callFn([], undefined, true);
|
|
}
|
|
return {
|
|
expression: factoryFn,
|
|
statements: [],
|
|
type: createFactoryType(meta)
|
|
};
|
|
}
|
|
function createFactoryType(meta) {
|
|
const ctorDepsType = meta.deps !== null && meta.deps !== 'invalid' ? createCtorDepsType(meta.deps) : NONE_TYPE;
|
|
return expressionType(importExpr(Identifiers.FactoryDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]));
|
|
}
|
|
function injectDependencies(deps, target) {
|
|
return deps.map((dep, index) => compileInjectDependency(dep, target, index));
|
|
}
|
|
function compileInjectDependency(dep, target, index) {
|
|
if (dep.token === null) {
|
|
return importExpr(Identifiers.invalidFactoryDep).callFn([literal(index)]);
|
|
} else if (dep.attributeNameType === null) {
|
|
const flags = 0 | (dep.self ? 2 : 0) | (dep.skipSelf ? 4 : 0) | (dep.host ? 1 : 0) | (dep.optional ? 8 : 0) | (target === FactoryTarget.Pipe ? 16 : 0);
|
|
let flagsParam = flags !== 0 || dep.optional ? literal(flags) : null;
|
|
const injectArgs = [dep.token];
|
|
if (flagsParam) {
|
|
injectArgs.push(flagsParam);
|
|
}
|
|
const injectFn = getInjectFn(target);
|
|
return importExpr(injectFn).callFn(injectArgs);
|
|
} else {
|
|
return importExpr(Identifiers.injectAttribute).callFn([dep.token]);
|
|
}
|
|
}
|
|
function createCtorDepsType(deps) {
|
|
let hasTypes = false;
|
|
const attributeTypes = deps.map(dep => {
|
|
const type = createCtorDepType(dep);
|
|
if (type !== null) {
|
|
hasTypes = true;
|
|
return type;
|
|
} else {
|
|
return literal(null);
|
|
}
|
|
});
|
|
if (hasTypes) {
|
|
return expressionType(literalArr(attributeTypes));
|
|
} else {
|
|
return NONE_TYPE;
|
|
}
|
|
}
|
|
function createCtorDepType(dep) {
|
|
const entries = [];
|
|
if (dep.attributeNameType !== null) {
|
|
entries.push({
|
|
key: 'attribute',
|
|
value: dep.attributeNameType,
|
|
quoted: false
|
|
});
|
|
}
|
|
if (dep.optional) {
|
|
entries.push({
|
|
key: 'optional',
|
|
value: literal(true),
|
|
quoted: false
|
|
});
|
|
}
|
|
if (dep.host) {
|
|
entries.push({
|
|
key: 'host',
|
|
value: literal(true),
|
|
quoted: false
|
|
});
|
|
}
|
|
if (dep.self) {
|
|
entries.push({
|
|
key: 'self',
|
|
value: literal(true),
|
|
quoted: false
|
|
});
|
|
}
|
|
if (dep.skipSelf) {
|
|
entries.push({
|
|
key: 'skipSelf',
|
|
value: literal(true),
|
|
quoted: false
|
|
});
|
|
}
|
|
return entries.length > 0 ? literalMap(entries) : null;
|
|
}
|
|
function isDelegatedFactoryMetadata(meta) {
|
|
return meta.delegateType !== undefined;
|
|
}
|
|
function isExpressionFactoryMetadata(meta) {
|
|
return meta.expression !== undefined;
|
|
}
|
|
function getInjectFn(target) {
|
|
switch (target) {
|
|
case FactoryTarget.Component:
|
|
case FactoryTarget.Directive:
|
|
case FactoryTarget.Pipe:
|
|
return Identifiers.directiveInject;
|
|
case FactoryTarget.NgModule:
|
|
case FactoryTarget.Injectable:
|
|
default:
|
|
return Identifiers.inject;
|
|
}
|
|
}
|
|
|
|
class ParseSpan {
|
|
start;
|
|
end;
|
|
constructor(start, end) {
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
toAbsolute(absoluteOffset) {
|
|
return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end);
|
|
}
|
|
}
|
|
class AST {
|
|
span;
|
|
sourceSpan;
|
|
constructor(span, sourceSpan) {
|
|
this.span = span;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
toString() {
|
|
return 'AST';
|
|
}
|
|
}
|
|
class ASTWithName extends AST {
|
|
nameSpan;
|
|
constructor(span, sourceSpan, nameSpan) {
|
|
super(span, sourceSpan);
|
|
this.nameSpan = nameSpan;
|
|
}
|
|
}
|
|
let EmptyExpr$1 = class EmptyExpr extends AST {
|
|
visit(visitor, context = null) {}
|
|
};
|
|
class ImplicitReceiver extends AST {
|
|
visit(visitor, context = null) {
|
|
return visitor.visitImplicitReceiver(this, context);
|
|
}
|
|
}
|
|
class ThisReceiver extends AST {
|
|
visit(visitor, context = null) {
|
|
return visitor.visitThisReceiver?.(this, context);
|
|
}
|
|
}
|
|
class Chain extends AST {
|
|
expressions;
|
|
constructor(span, sourceSpan, expressions) {
|
|
super(span, sourceSpan);
|
|
this.expressions = expressions;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitChain(this, context);
|
|
}
|
|
}
|
|
class Conditional extends AST {
|
|
condition;
|
|
trueExp;
|
|
falseExp;
|
|
constructor(span, sourceSpan, condition, trueExp, falseExp) {
|
|
super(span, sourceSpan);
|
|
this.condition = condition;
|
|
this.trueExp = trueExp;
|
|
this.falseExp = falseExp;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitConditional(this, context);
|
|
}
|
|
}
|
|
class PropertyRead extends ASTWithName {
|
|
receiver;
|
|
name;
|
|
constructor(span, sourceSpan, nameSpan, receiver, name) {
|
|
super(span, sourceSpan, nameSpan);
|
|
this.receiver = receiver;
|
|
this.name = name;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitPropertyRead(this, context);
|
|
}
|
|
}
|
|
class SafePropertyRead extends ASTWithName {
|
|
receiver;
|
|
name;
|
|
constructor(span, sourceSpan, nameSpan, receiver, name) {
|
|
super(span, sourceSpan, nameSpan);
|
|
this.receiver = receiver;
|
|
this.name = name;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitSafePropertyRead(this, context);
|
|
}
|
|
}
|
|
class KeyedRead extends AST {
|
|
receiver;
|
|
key;
|
|
constructor(span, sourceSpan, receiver, key) {
|
|
super(span, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.key = key;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitKeyedRead(this, context);
|
|
}
|
|
}
|
|
class SafeKeyedRead extends AST {
|
|
receiver;
|
|
key;
|
|
constructor(span, sourceSpan, receiver, key) {
|
|
super(span, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.key = key;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitSafeKeyedRead(this, context);
|
|
}
|
|
}
|
|
var BindingPipeType;
|
|
(function (BindingPipeType) {
|
|
BindingPipeType[BindingPipeType["ReferencedByName"] = 0] = "ReferencedByName";
|
|
BindingPipeType[BindingPipeType["ReferencedDirectly"] = 1] = "ReferencedDirectly";
|
|
})(BindingPipeType || (BindingPipeType = {}));
|
|
class BindingPipe extends ASTWithName {
|
|
exp;
|
|
name;
|
|
args;
|
|
type;
|
|
constructor(span, sourceSpan, exp, name, args, type, nameSpan) {
|
|
super(span, sourceSpan, nameSpan);
|
|
this.exp = exp;
|
|
this.name = name;
|
|
this.args = args;
|
|
this.type = type;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitPipe(this, context);
|
|
}
|
|
}
|
|
class LiteralPrimitive extends AST {
|
|
value;
|
|
constructor(span, sourceSpan, value) {
|
|
super(span, sourceSpan);
|
|
this.value = value;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitLiteralPrimitive(this, context);
|
|
}
|
|
}
|
|
class LiteralArray extends AST {
|
|
expressions;
|
|
constructor(span, sourceSpan, expressions) {
|
|
super(span, sourceSpan);
|
|
this.expressions = expressions;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitLiteralArray(this, context);
|
|
}
|
|
}
|
|
class SpreadElement extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitSpreadElement(this, context);
|
|
}
|
|
}
|
|
class LiteralMap extends AST {
|
|
keys;
|
|
values;
|
|
constructor(span, sourceSpan, keys, values) {
|
|
super(span, sourceSpan);
|
|
this.keys = keys;
|
|
this.values = values;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitLiteralMap(this, context);
|
|
}
|
|
}
|
|
let Interpolation$1 = class Interpolation extends AST {
|
|
strings;
|
|
expressions;
|
|
constructor(span, sourceSpan, strings, expressions) {
|
|
super(span, sourceSpan);
|
|
this.strings = strings;
|
|
this.expressions = expressions;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitInterpolation(this, context);
|
|
}
|
|
};
|
|
class Binary extends AST {
|
|
operation;
|
|
left;
|
|
right;
|
|
constructor(span, sourceSpan, operation, left, right) {
|
|
super(span, sourceSpan);
|
|
this.operation = operation;
|
|
this.left = left;
|
|
this.right = right;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitBinary(this, context);
|
|
}
|
|
static isAssignmentOperation(op) {
|
|
return op === '=' || op === '+=' || op === '-=' || op === '*=' || op === '/=' || op === '%=' || op === '**=' || op === '&&=' || op === '||=' || op === '??=';
|
|
}
|
|
}
|
|
class Unary extends Binary {
|
|
operator;
|
|
expr;
|
|
left = null;
|
|
right = null;
|
|
operation = null;
|
|
static createMinus(span, sourceSpan, expr) {
|
|
return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
|
|
}
|
|
static createPlus(span, sourceSpan, expr) {
|
|
return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
|
|
}
|
|
constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
|
|
super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
|
|
this.operator = operator;
|
|
this.expr = expr;
|
|
}
|
|
visit(visitor, context = null) {
|
|
if (visitor.visitUnary !== undefined) {
|
|
return visitor.visitUnary(this, context);
|
|
}
|
|
return visitor.visitBinary(this, context);
|
|
}
|
|
}
|
|
class PrefixNot extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitPrefixNot(this, context);
|
|
}
|
|
}
|
|
class TypeofExpression extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitTypeofExpression(this, context);
|
|
}
|
|
}
|
|
class VoidExpression extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitVoidExpression(this, context);
|
|
}
|
|
}
|
|
class NonNullAssert extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitNonNullAssert(this, context);
|
|
}
|
|
}
|
|
class Call extends AST {
|
|
receiver;
|
|
args;
|
|
argumentSpan;
|
|
constructor(span, sourceSpan, receiver, args, argumentSpan) {
|
|
super(span, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.args = args;
|
|
this.argumentSpan = argumentSpan;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitCall(this, context);
|
|
}
|
|
}
|
|
class SafeCall extends AST {
|
|
receiver;
|
|
args;
|
|
argumentSpan;
|
|
constructor(span, sourceSpan, receiver, args, argumentSpan) {
|
|
super(span, sourceSpan);
|
|
this.receiver = receiver;
|
|
this.args = args;
|
|
this.argumentSpan = argumentSpan;
|
|
}
|
|
visit(visitor, context = null) {
|
|
return visitor.visitSafeCall(this, context);
|
|
}
|
|
}
|
|
class TaggedTemplateLiteral extends AST {
|
|
tag;
|
|
template;
|
|
constructor(span, sourceSpan, tag, template) {
|
|
super(span, sourceSpan);
|
|
this.tag = tag;
|
|
this.template = template;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitTaggedTemplateLiteral(this, context);
|
|
}
|
|
}
|
|
class TemplateLiteral extends AST {
|
|
elements;
|
|
expressions;
|
|
constructor(span, sourceSpan, elements, expressions) {
|
|
super(span, sourceSpan);
|
|
this.elements = elements;
|
|
this.expressions = expressions;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitTemplateLiteral(this, context);
|
|
}
|
|
}
|
|
class TemplateLiteralElement extends AST {
|
|
text;
|
|
constructor(span, sourceSpan, text) {
|
|
super(span, sourceSpan);
|
|
this.text = text;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitTemplateLiteralElement(this, context);
|
|
}
|
|
}
|
|
class ParenthesizedExpression extends AST {
|
|
expression;
|
|
constructor(span, sourceSpan, expression) {
|
|
super(span, sourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitParenthesizedExpression(this, context);
|
|
}
|
|
}
|
|
class RegularExpressionLiteral extends AST {
|
|
body;
|
|
flags;
|
|
constructor(span, sourceSpan, body, flags) {
|
|
super(span, sourceSpan);
|
|
this.body = body;
|
|
this.flags = flags;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitRegularExpressionLiteral(this, context);
|
|
}
|
|
}
|
|
class AbsoluteSourceSpan {
|
|
start;
|
|
end;
|
|
constructor(start, end) {
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
}
|
|
class ASTWithSource extends AST {
|
|
ast;
|
|
source;
|
|
location;
|
|
errors;
|
|
constructor(ast, source, location, absoluteOffset, errors) {
|
|
super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
|
|
this.ast = ast;
|
|
this.source = source;
|
|
this.location = location;
|
|
this.errors = errors;
|
|
}
|
|
visit(visitor, context = null) {
|
|
if (visitor.visitASTWithSource) {
|
|
return visitor.visitASTWithSource(this, context);
|
|
}
|
|
return this.ast.visit(visitor, context);
|
|
}
|
|
toString() {
|
|
return `${this.source} in ${this.location}`;
|
|
}
|
|
}
|
|
class VariableBinding {
|
|
sourceSpan;
|
|
key;
|
|
value;
|
|
constructor(sourceSpan, key, value) {
|
|
this.sourceSpan = sourceSpan;
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
}
|
|
class ExpressionBinding {
|
|
sourceSpan;
|
|
key;
|
|
value;
|
|
constructor(sourceSpan, key, value) {
|
|
this.sourceSpan = sourceSpan;
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
}
|
|
class RecursiveAstVisitor {
|
|
visit(ast, context) {
|
|
ast.visit(this, context);
|
|
}
|
|
visitUnary(ast, context) {
|
|
this.visit(ast.expr, context);
|
|
}
|
|
visitBinary(ast, context) {
|
|
this.visit(ast.left, context);
|
|
this.visit(ast.right, context);
|
|
}
|
|
visitChain(ast, context) {
|
|
this.visitAll(ast.expressions, context);
|
|
}
|
|
visitConditional(ast, context) {
|
|
this.visit(ast.condition, context);
|
|
this.visit(ast.trueExp, context);
|
|
this.visit(ast.falseExp, context);
|
|
}
|
|
visitPipe(ast, context) {
|
|
this.visit(ast.exp, context);
|
|
this.visitAll(ast.args, context);
|
|
}
|
|
visitImplicitReceiver(ast, context) {}
|
|
visitThisReceiver(ast, context) {}
|
|
visitInterpolation(ast, context) {
|
|
this.visitAll(ast.expressions, context);
|
|
}
|
|
visitKeyedRead(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
this.visit(ast.key, context);
|
|
}
|
|
visitLiteralArray(ast, context) {
|
|
this.visitAll(ast.expressions, context);
|
|
}
|
|
visitLiteralMap(ast, context) {
|
|
this.visitAll(ast.values, context);
|
|
}
|
|
visitLiteralPrimitive(ast, context) {}
|
|
visitPrefixNot(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitTypeofExpression(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitVoidExpression(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitNonNullAssert(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitPropertyRead(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
}
|
|
visitSafePropertyRead(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
}
|
|
visitSafeKeyedRead(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
this.visit(ast.key, context);
|
|
}
|
|
visitCall(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
this.visitAll(ast.args, context);
|
|
}
|
|
visitSafeCall(ast, context) {
|
|
this.visit(ast.receiver, context);
|
|
this.visitAll(ast.args, context);
|
|
}
|
|
visitTemplateLiteral(ast, context) {
|
|
for (let i = 0; i < ast.elements.length; i++) {
|
|
this.visit(ast.elements[i], context);
|
|
const expression = i < ast.expressions.length ? ast.expressions[i] : null;
|
|
if (expression !== null) {
|
|
this.visit(expression, context);
|
|
}
|
|
}
|
|
}
|
|
visitTemplateLiteralElement(ast, context) {}
|
|
visitTaggedTemplateLiteral(ast, context) {
|
|
this.visit(ast.tag, context);
|
|
this.visit(ast.template, context);
|
|
}
|
|
visitParenthesizedExpression(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitRegularExpressionLiteral(ast, context) {}
|
|
visitSpreadElement(ast, context) {
|
|
this.visit(ast.expression, context);
|
|
}
|
|
visitAll(asts, context) {
|
|
for (const ast of asts) {
|
|
this.visit(ast, context);
|
|
}
|
|
}
|
|
}
|
|
class ParsedProperty {
|
|
name;
|
|
expression;
|
|
type;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
isLiteral;
|
|
isLegacyAnimation;
|
|
isAnimation;
|
|
constructor(name, expression, type, sourceSpan, keySpan, valueSpan) {
|
|
this.name = name;
|
|
this.expression = expression;
|
|
this.type = type;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
|
|
this.isLegacyAnimation = this.type === ParsedPropertyType.LEGACY_ANIMATION;
|
|
this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
|
|
}
|
|
}
|
|
var ParsedPropertyType;
|
|
(function (ParsedPropertyType) {
|
|
ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
|
|
ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
|
|
ParsedPropertyType[ParsedPropertyType["LEGACY_ANIMATION"] = 2] = "LEGACY_ANIMATION";
|
|
ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY";
|
|
ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 4] = "ANIMATION";
|
|
})(ParsedPropertyType || (ParsedPropertyType = {}));
|
|
var ParsedEventType;
|
|
(function (ParsedEventType) {
|
|
ParsedEventType[ParsedEventType["Regular"] = 0] = "Regular";
|
|
ParsedEventType[ParsedEventType["LegacyAnimation"] = 1] = "LegacyAnimation";
|
|
ParsedEventType[ParsedEventType["TwoWay"] = 2] = "TwoWay";
|
|
ParsedEventType[ParsedEventType["Animation"] = 3] = "Animation";
|
|
})(ParsedEventType || (ParsedEventType = {}));
|
|
class ParsedEvent {
|
|
name;
|
|
targetOrPhase;
|
|
type;
|
|
handler;
|
|
sourceSpan;
|
|
handlerSpan;
|
|
keySpan;
|
|
constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) {
|
|
this.name = name;
|
|
this.targetOrPhase = targetOrPhase;
|
|
this.type = type;
|
|
this.handler = handler;
|
|
this.sourceSpan = sourceSpan;
|
|
this.handlerSpan = handlerSpan;
|
|
this.keySpan = keySpan;
|
|
}
|
|
}
|
|
class ParsedVariable {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
}
|
|
var BindingType;
|
|
(function (BindingType) {
|
|
BindingType[BindingType["Property"] = 0] = "Property";
|
|
BindingType[BindingType["Attribute"] = 1] = "Attribute";
|
|
BindingType[BindingType["Class"] = 2] = "Class";
|
|
BindingType[BindingType["Style"] = 3] = "Style";
|
|
BindingType[BindingType["LegacyAnimation"] = 4] = "LegacyAnimation";
|
|
BindingType[BindingType["TwoWay"] = 5] = "TwoWay";
|
|
BindingType[BindingType["Animation"] = 6] = "Animation";
|
|
})(BindingType || (BindingType = {}));
|
|
class BoundElementProperty {
|
|
name;
|
|
type;
|
|
securityContext;
|
|
value;
|
|
unit;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.securityContext = securityContext;
|
|
this.value = value;
|
|
this.unit = unit;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
}
|
|
|
|
var TagContentType;
|
|
(function (TagContentType) {
|
|
TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
|
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
})(TagContentType || (TagContentType = {}));
|
|
function splitNsName(elementName, fatal = true) {
|
|
if (elementName[0] != ':') {
|
|
return [null, elementName];
|
|
}
|
|
const colonIndex = elementName.indexOf(':', 1);
|
|
if (colonIndex === -1) {
|
|
if (fatal) {
|
|
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
} else {
|
|
return [null, elementName];
|
|
}
|
|
}
|
|
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
}
|
|
function isNgContainer(tagName) {
|
|
return splitNsName(tagName)[1] === 'ng-container';
|
|
}
|
|
function isNgContent(tagName) {
|
|
return splitNsName(tagName)[1] === 'ng-content';
|
|
}
|
|
function isNgTemplate(tagName) {
|
|
return splitNsName(tagName)[1] === 'ng-template';
|
|
}
|
|
function getNsPrefix(fullName) {
|
|
return fullName === null ? null : splitNsName(fullName)[0];
|
|
}
|
|
function mergeNsAndName(prefix, localName) {
|
|
return prefix ? `:${prefix}:${localName}` : localName;
|
|
}
|
|
|
|
let Comment$1 = class Comment {
|
|
value;
|
|
sourceSpan;
|
|
constructor(value, sourceSpan) {
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(_visitor) {
|
|
throw new Error('visit() not implemented for Comment');
|
|
}
|
|
};
|
|
let Text$3 = class Text {
|
|
value;
|
|
sourceSpan;
|
|
constructor(value, sourceSpan) {
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitText(this);
|
|
}
|
|
};
|
|
class BoundText {
|
|
value;
|
|
sourceSpan;
|
|
i18n;
|
|
constructor(value, sourceSpan, i18n) {
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitBoundText(this);
|
|
}
|
|
}
|
|
class TextAttribute {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
i18n;
|
|
constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitTextAttribute(this);
|
|
}
|
|
}
|
|
class BoundAttribute {
|
|
name;
|
|
type;
|
|
securityContext;
|
|
value;
|
|
unit;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
i18n;
|
|
constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.securityContext = securityContext;
|
|
this.value = value;
|
|
this.unit = unit;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
static fromBoundElementProperty(prop, i18n) {
|
|
if (prop.keySpan === undefined) {
|
|
throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
|
|
}
|
|
return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitBoundAttribute(this);
|
|
}
|
|
}
|
|
class BoundEvent {
|
|
name;
|
|
type;
|
|
handler;
|
|
target;
|
|
phase;
|
|
sourceSpan;
|
|
handlerSpan;
|
|
keySpan;
|
|
constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.handler = handler;
|
|
this.target = target;
|
|
this.phase = phase;
|
|
this.sourceSpan = sourceSpan;
|
|
this.handlerSpan = handlerSpan;
|
|
this.keySpan = keySpan;
|
|
}
|
|
static fromParsedEvent(event) {
|
|
const target = event.type === ParsedEventType.Regular ? event.targetOrPhase : null;
|
|
const phase = event.type === ParsedEventType.LegacyAnimation ? event.targetOrPhase : null;
|
|
if (event.keySpan === undefined) {
|
|
throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
|
|
}
|
|
return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitBoundEvent(this);
|
|
}
|
|
}
|
|
let Element$1 = class Element {
|
|
name;
|
|
attributes;
|
|
inputs;
|
|
outputs;
|
|
directives;
|
|
children;
|
|
references;
|
|
isSelfClosing;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
isVoid;
|
|
i18n;
|
|
constructor(name, attributes, inputs, outputs, directives, children, references, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan, isVoid, i18n) {
|
|
this.name = name;
|
|
this.attributes = attributes;
|
|
this.inputs = inputs;
|
|
this.outputs = outputs;
|
|
this.directives = directives;
|
|
this.children = children;
|
|
this.references = references;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.isVoid = isVoid;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitElement(this);
|
|
}
|
|
};
|
|
class DeferredTrigger {
|
|
nameSpan;
|
|
sourceSpan;
|
|
prefetchSpan;
|
|
whenOrOnSourceSpan;
|
|
hydrateSpan;
|
|
constructor(nameSpan, sourceSpan, prefetchSpan, whenOrOnSourceSpan, hydrateSpan) {
|
|
this.nameSpan = nameSpan;
|
|
this.sourceSpan = sourceSpan;
|
|
this.prefetchSpan = prefetchSpan;
|
|
this.whenOrOnSourceSpan = whenOrOnSourceSpan;
|
|
this.hydrateSpan = hydrateSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeferredTrigger(this);
|
|
}
|
|
}
|
|
class BoundDeferredTrigger extends DeferredTrigger {
|
|
value;
|
|
constructor(value, sourceSpan, prefetchSpan, whenSourceSpan, hydrateSpan) {
|
|
super(null, sourceSpan, prefetchSpan, whenSourceSpan, hydrateSpan);
|
|
this.value = value;
|
|
}
|
|
}
|
|
class NeverDeferredTrigger extends DeferredTrigger {}
|
|
class IdleDeferredTrigger extends DeferredTrigger {}
|
|
class ImmediateDeferredTrigger extends DeferredTrigger {}
|
|
class HoverDeferredTrigger extends DeferredTrigger {
|
|
reference;
|
|
constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
this.reference = reference;
|
|
}
|
|
}
|
|
class TimerDeferredTrigger extends DeferredTrigger {
|
|
delay;
|
|
constructor(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
this.delay = delay;
|
|
}
|
|
}
|
|
class InteractionDeferredTrigger extends DeferredTrigger {
|
|
reference;
|
|
constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
this.reference = reference;
|
|
}
|
|
}
|
|
class ViewportDeferredTrigger extends DeferredTrigger {
|
|
reference;
|
|
options;
|
|
constructor(reference, options, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
this.reference = reference;
|
|
this.options = options;
|
|
}
|
|
}
|
|
class BlockNode {
|
|
nameSpan;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
|
|
this.nameSpan = nameSpan;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
}
|
|
class DeferredBlockPlaceholder extends BlockNode {
|
|
children;
|
|
minimumTime;
|
|
i18n;
|
|
constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.children = children;
|
|
this.minimumTime = minimumTime;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeferredBlockPlaceholder(this);
|
|
}
|
|
}
|
|
class DeferredBlockLoading extends BlockNode {
|
|
children;
|
|
afterTime;
|
|
minimumTime;
|
|
i18n;
|
|
constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.children = children;
|
|
this.afterTime = afterTime;
|
|
this.minimumTime = minimumTime;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeferredBlockLoading(this);
|
|
}
|
|
}
|
|
class DeferredBlockError extends BlockNode {
|
|
children;
|
|
i18n;
|
|
constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.children = children;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeferredBlockError(this);
|
|
}
|
|
}
|
|
class DeferredBlock extends BlockNode {
|
|
children;
|
|
placeholder;
|
|
loading;
|
|
error;
|
|
mainBlockSpan;
|
|
i18n;
|
|
triggers;
|
|
prefetchTriggers;
|
|
hydrateTriggers;
|
|
definedTriggers;
|
|
definedPrefetchTriggers;
|
|
definedHydrateTriggers;
|
|
constructor(children, triggers, prefetchTriggers, hydrateTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.children = children;
|
|
this.placeholder = placeholder;
|
|
this.loading = loading;
|
|
this.error = error;
|
|
this.mainBlockSpan = mainBlockSpan;
|
|
this.i18n = i18n;
|
|
this.triggers = triggers;
|
|
this.prefetchTriggers = prefetchTriggers;
|
|
this.hydrateTriggers = hydrateTriggers;
|
|
this.definedTriggers = Object.keys(triggers);
|
|
this.definedPrefetchTriggers = Object.keys(prefetchTriggers);
|
|
this.definedHydrateTriggers = Object.keys(hydrateTriggers);
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeferredBlock(this);
|
|
}
|
|
visitAll(visitor) {
|
|
this.visitTriggers(this.definedHydrateTriggers, this.hydrateTriggers, visitor);
|
|
this.visitTriggers(this.definedTriggers, this.triggers, visitor);
|
|
this.visitTriggers(this.definedPrefetchTriggers, this.prefetchTriggers, visitor);
|
|
visitAll$1(visitor, this.children);
|
|
const remainingBlocks = [this.placeholder, this.loading, this.error].filter(x => x !== null);
|
|
visitAll$1(visitor, remainingBlocks);
|
|
}
|
|
visitTriggers(keys, triggers, visitor) {
|
|
visitAll$1(visitor, keys.map(k => triggers[k]));
|
|
}
|
|
}
|
|
class SwitchBlock extends BlockNode {
|
|
expression;
|
|
groups;
|
|
unknownBlocks;
|
|
constructor(expression, groups, unknownBlocks, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.expression = expression;
|
|
this.groups = groups;
|
|
this.unknownBlocks = unknownBlocks;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitSwitchBlock(this);
|
|
}
|
|
}
|
|
class SwitchBlockCase extends BlockNode {
|
|
expression;
|
|
constructor(expression, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.expression = expression;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitSwitchBlockCase(this);
|
|
}
|
|
}
|
|
class SwitchBlockCaseGroup extends BlockNode {
|
|
cases;
|
|
children;
|
|
i18n;
|
|
constructor(cases, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.cases = cases;
|
|
this.children = children;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitSwitchBlockCaseGroup(this);
|
|
}
|
|
}
|
|
class ForLoopBlock extends BlockNode {
|
|
item;
|
|
expression;
|
|
trackBy;
|
|
trackKeywordSpan;
|
|
contextVariables;
|
|
children;
|
|
empty;
|
|
mainBlockSpan;
|
|
i18n;
|
|
constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.item = item;
|
|
this.expression = expression;
|
|
this.trackBy = trackBy;
|
|
this.trackKeywordSpan = trackKeywordSpan;
|
|
this.contextVariables = contextVariables;
|
|
this.children = children;
|
|
this.empty = empty;
|
|
this.mainBlockSpan = mainBlockSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitForLoopBlock(this);
|
|
}
|
|
}
|
|
class ForLoopBlockEmpty extends BlockNode {
|
|
children;
|
|
i18n;
|
|
constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.children = children;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitForLoopBlockEmpty(this);
|
|
}
|
|
}
|
|
class IfBlock extends BlockNode {
|
|
branches;
|
|
constructor(branches, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.branches = branches;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitIfBlock(this);
|
|
}
|
|
}
|
|
class IfBlockBranch extends BlockNode {
|
|
expression;
|
|
children;
|
|
expressionAlias;
|
|
i18n;
|
|
constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
|
|
super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
|
|
this.expression = expression;
|
|
this.children = children;
|
|
this.expressionAlias = expressionAlias;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitIfBlockBranch(this);
|
|
}
|
|
}
|
|
class UnknownBlock {
|
|
name;
|
|
sourceSpan;
|
|
nameSpan;
|
|
constructor(name, sourceSpan, nameSpan) {
|
|
this.name = name;
|
|
this.sourceSpan = sourceSpan;
|
|
this.nameSpan = nameSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitUnknownBlock(this);
|
|
}
|
|
}
|
|
let LetDeclaration$1 = class LetDeclaration {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
nameSpan;
|
|
valueSpan;
|
|
constructor(name, value, sourceSpan, nameSpan, valueSpan) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.nameSpan = nameSpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitLetDeclaration(this);
|
|
}
|
|
};
|
|
let Component$1 = class Component {
|
|
componentName;
|
|
tagName;
|
|
fullName;
|
|
attributes;
|
|
inputs;
|
|
outputs;
|
|
directives;
|
|
children;
|
|
references;
|
|
isSelfClosing;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
i18n;
|
|
constructor(componentName, tagName, fullName, attributes, inputs, outputs, directives, children, references, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
this.componentName = componentName;
|
|
this.tagName = tagName;
|
|
this.fullName = fullName;
|
|
this.attributes = attributes;
|
|
this.inputs = inputs;
|
|
this.outputs = outputs;
|
|
this.directives = directives;
|
|
this.children = children;
|
|
this.references = references;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitComponent(this);
|
|
}
|
|
};
|
|
let Directive$1 = class Directive {
|
|
name;
|
|
attributes;
|
|
inputs;
|
|
outputs;
|
|
references;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
i18n;
|
|
constructor(name, attributes, inputs, outputs, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
this.name = name;
|
|
this.attributes = attributes;
|
|
this.inputs = inputs;
|
|
this.outputs = outputs;
|
|
this.references = references;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDirective(this);
|
|
}
|
|
};
|
|
class Template {
|
|
tagName;
|
|
attributes;
|
|
inputs;
|
|
outputs;
|
|
directives;
|
|
templateAttrs;
|
|
children;
|
|
references;
|
|
variables;
|
|
isSelfClosing;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
i18n;
|
|
constructor(tagName, attributes, inputs, outputs, directives, templateAttrs, children, references, variables, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
this.tagName = tagName;
|
|
this.attributes = attributes;
|
|
this.inputs = inputs;
|
|
this.outputs = outputs;
|
|
this.directives = directives;
|
|
this.templateAttrs = templateAttrs;
|
|
this.children = children;
|
|
this.references = references;
|
|
this.variables = variables;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitTemplate(this);
|
|
}
|
|
}
|
|
class Content {
|
|
selector;
|
|
attributes;
|
|
children;
|
|
isSelfClosing;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
i18n;
|
|
name = 'ng-content';
|
|
constructor(selector, attributes, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
|
this.selector = selector;
|
|
this.attributes = attributes;
|
|
this.children = children;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitContent(this);
|
|
}
|
|
}
|
|
class Variable {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitVariable(this);
|
|
}
|
|
}
|
|
class Reference {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
keySpan;
|
|
valueSpan;
|
|
constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitReference(this);
|
|
}
|
|
}
|
|
let Icu$1 = class Icu {
|
|
vars;
|
|
placeholders;
|
|
sourceSpan;
|
|
i18n;
|
|
constructor(vars, placeholders, sourceSpan, i18n) {
|
|
this.vars = vars;
|
|
this.placeholders = placeholders;
|
|
this.sourceSpan = sourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitIcu(this);
|
|
}
|
|
};
|
|
class HostElement {
|
|
tagNames;
|
|
bindings;
|
|
listeners;
|
|
sourceSpan;
|
|
constructor(tagNames, bindings, listeners, sourceSpan) {
|
|
this.tagNames = tagNames;
|
|
this.bindings = bindings;
|
|
this.listeners = listeners;
|
|
this.sourceSpan = sourceSpan;
|
|
if (tagNames.length === 0) {
|
|
throw new Error('HostElement must have at least one tag name.');
|
|
}
|
|
}
|
|
visit() {
|
|
throw new Error(`HostElement cannot be visited`);
|
|
}
|
|
}
|
|
let RecursiveVisitor$1 = class RecursiveVisitor {
|
|
visitElement(element) {
|
|
visitAll$1(this, element.attributes);
|
|
visitAll$1(this, element.inputs);
|
|
visitAll$1(this, element.outputs);
|
|
visitAll$1(this, element.directives);
|
|
visitAll$1(this, element.children);
|
|
visitAll$1(this, element.references);
|
|
}
|
|
visitTemplate(template) {
|
|
visitAll$1(this, template.attributes);
|
|
visitAll$1(this, template.inputs);
|
|
visitAll$1(this, template.outputs);
|
|
visitAll$1(this, template.directives);
|
|
visitAll$1(this, template.children);
|
|
visitAll$1(this, template.references);
|
|
visitAll$1(this, template.variables);
|
|
}
|
|
visitDeferredBlock(deferred) {
|
|
deferred.visitAll(this);
|
|
}
|
|
visitDeferredBlockPlaceholder(block) {
|
|
visitAll$1(this, block.children);
|
|
}
|
|
visitDeferredBlockError(block) {
|
|
visitAll$1(this, block.children);
|
|
}
|
|
visitDeferredBlockLoading(block) {
|
|
visitAll$1(this, block.children);
|
|
}
|
|
visitSwitchBlock(block) {
|
|
visitAll$1(this, block.groups);
|
|
}
|
|
visitSwitchBlockCase(block) {}
|
|
visitSwitchBlockCaseGroup(block) {
|
|
visitAll$1(this, block.cases);
|
|
visitAll$1(this, block.children);
|
|
}
|
|
visitForLoopBlock(block) {
|
|
const blockItems = [block.item, ...block.contextVariables, ...block.children];
|
|
block.empty && blockItems.push(block.empty);
|
|
visitAll$1(this, blockItems);
|
|
}
|
|
visitForLoopBlockEmpty(block) {
|
|
visitAll$1(this, block.children);
|
|
}
|
|
visitIfBlock(block) {
|
|
visitAll$1(this, block.branches);
|
|
}
|
|
visitIfBlockBranch(block) {
|
|
const blockItems = block.children;
|
|
block.expressionAlias && blockItems.push(block.expressionAlias);
|
|
visitAll$1(this, blockItems);
|
|
}
|
|
visitContent(content) {
|
|
visitAll$1(this, content.children);
|
|
}
|
|
visitComponent(component) {
|
|
visitAll$1(this, component.attributes);
|
|
visitAll$1(this, component.inputs);
|
|
visitAll$1(this, component.outputs);
|
|
visitAll$1(this, component.directives);
|
|
visitAll$1(this, component.children);
|
|
visitAll$1(this, component.references);
|
|
}
|
|
visitDirective(directive) {
|
|
visitAll$1(this, directive.attributes);
|
|
visitAll$1(this, directive.inputs);
|
|
visitAll$1(this, directive.outputs);
|
|
visitAll$1(this, directive.references);
|
|
}
|
|
visitVariable(variable) {}
|
|
visitReference(reference) {}
|
|
visitTextAttribute(attribute) {}
|
|
visitBoundAttribute(attribute) {}
|
|
visitBoundEvent(attribute) {}
|
|
visitText(text) {}
|
|
visitBoundText(text) {}
|
|
visitIcu(icu) {}
|
|
visitDeferredTrigger(trigger) {}
|
|
visitUnknownBlock(block) {}
|
|
visitLetDeclaration(decl) {}
|
|
};
|
|
function visitAll$1(visitor, nodes) {
|
|
const result = [];
|
|
if (visitor.visit) {
|
|
for (const node of nodes) {
|
|
visitor.visit(node);
|
|
}
|
|
} else {
|
|
for (const node of nodes) {
|
|
const newNode = node.visit(visitor);
|
|
if (newNode) {
|
|
result.push(newNode);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class Message {
|
|
nodes;
|
|
placeholders;
|
|
placeholderToMessage;
|
|
meaning;
|
|
description;
|
|
customId;
|
|
sources;
|
|
id;
|
|
legacyIds = [];
|
|
messageString;
|
|
constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
|
|
this.nodes = nodes;
|
|
this.placeholders = placeholders;
|
|
this.placeholderToMessage = placeholderToMessage;
|
|
this.meaning = meaning;
|
|
this.description = description;
|
|
this.customId = customId;
|
|
this.id = this.customId;
|
|
this.messageString = serializeMessage(this.nodes);
|
|
if (nodes.length) {
|
|
this.sources = [{
|
|
filePath: nodes[0].sourceSpan.start.file.url,
|
|
startLine: nodes[0].sourceSpan.start.line + 1,
|
|
startCol: nodes[0].sourceSpan.start.col + 1,
|
|
endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
|
|
endCol: nodes[0].sourceSpan.start.col + 1
|
|
}];
|
|
} else {
|
|
this.sources = [];
|
|
}
|
|
}
|
|
}
|
|
let Text$2 = class Text {
|
|
value;
|
|
sourceSpan;
|
|
constructor(value, sourceSpan) {
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitText(this, context);
|
|
}
|
|
};
|
|
class Container {
|
|
children;
|
|
sourceSpan;
|
|
constructor(children, sourceSpan) {
|
|
this.children = children;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitContainer(this, context);
|
|
}
|
|
}
|
|
class Icu {
|
|
expression;
|
|
type;
|
|
cases;
|
|
sourceSpan;
|
|
expressionPlaceholder;
|
|
constructor(expression, type, cases, sourceSpan, expressionPlaceholder) {
|
|
this.expression = expression;
|
|
this.type = type;
|
|
this.cases = cases;
|
|
this.sourceSpan = sourceSpan;
|
|
this.expressionPlaceholder = expressionPlaceholder;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitIcu(this, context);
|
|
}
|
|
}
|
|
class TagPlaceholder {
|
|
tag;
|
|
attrs;
|
|
startName;
|
|
closeName;
|
|
children;
|
|
isVoid;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(tag, attrs, startName, closeName, children, isVoid, sourceSpan, startSourceSpan, endSourceSpan) {
|
|
this.tag = tag;
|
|
this.attrs = attrs;
|
|
this.startName = startName;
|
|
this.closeName = closeName;
|
|
this.children = children;
|
|
this.isVoid = isVoid;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitTagPlaceholder(this, context);
|
|
}
|
|
}
|
|
class Placeholder {
|
|
value;
|
|
name;
|
|
sourceSpan;
|
|
constructor(value, name, sourceSpan) {
|
|
this.value = value;
|
|
this.name = name;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitPlaceholder(this, context);
|
|
}
|
|
}
|
|
class IcuPlaceholder {
|
|
value;
|
|
name;
|
|
sourceSpan;
|
|
previousMessage;
|
|
constructor(value, name, sourceSpan) {
|
|
this.value = value;
|
|
this.name = name;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitIcuPlaceholder(this, context);
|
|
}
|
|
}
|
|
class BlockPlaceholder {
|
|
name;
|
|
parameters;
|
|
startName;
|
|
closeName;
|
|
children;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(name, parameters, startName, closeName, children, sourceSpan, startSourceSpan, endSourceSpan) {
|
|
this.name = name;
|
|
this.parameters = parameters;
|
|
this.startName = startName;
|
|
this.closeName = closeName;
|
|
this.children = children;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitBlockPlaceholder(this, context);
|
|
}
|
|
}
|
|
class CloneVisitor {
|
|
visitText(text, context) {
|
|
return new Text$2(text.value, text.sourceSpan);
|
|
}
|
|
visitContainer(container, context) {
|
|
const children = container.children.map(n => n.visit(this, context));
|
|
return new Container(children, container.sourceSpan);
|
|
}
|
|
visitIcu(icu, context) {
|
|
const cases = {};
|
|
Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, context));
|
|
const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan, icu.expressionPlaceholder);
|
|
return msg;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
const children = ph.children.map(n => n.visit(this, context));
|
|
return new TagPlaceholder(ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
return new Placeholder(ph.value, ph.name, ph.sourceSpan);
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan);
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
const children = ph.children.map(n => n.visit(this, context));
|
|
return new BlockPlaceholder(ph.name, ph.parameters, ph.startName, ph.closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
|
|
}
|
|
}
|
|
class RecurseVisitor {
|
|
visitText(text, context) {}
|
|
visitContainer(container, context) {
|
|
container.children.forEach(child => child.visit(this));
|
|
}
|
|
visitIcu(icu, context) {
|
|
Object.keys(icu.cases).forEach(k => {
|
|
icu.cases[k].visit(this);
|
|
});
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
ph.children.forEach(child => child.visit(this));
|
|
}
|
|
visitPlaceholder(ph, context) {}
|
|
visitIcuPlaceholder(ph, context) {}
|
|
visitBlockPlaceholder(ph, context) {
|
|
ph.children.forEach(child => child.visit(this));
|
|
}
|
|
}
|
|
function serializeMessage(messageNodes) {
|
|
const visitor = new LocalizeMessageStringVisitor();
|
|
const str = messageNodes.map(n => n.visit(visitor)).join('');
|
|
return str;
|
|
}
|
|
class LocalizeMessageStringVisitor {
|
|
visitText(text) {
|
|
return text.value;
|
|
}
|
|
visitContainer(container) {
|
|
return container.children.map(child => child.visit(this)).join('');
|
|
}
|
|
visitIcu(icu) {
|
|
const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
|
return `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
|
|
}
|
|
visitTagPlaceholder(ph) {
|
|
const children = ph.children.map(child => child.visit(this)).join('');
|
|
return `{$${ph.startName}}${children}{$${ph.closeName}}`;
|
|
}
|
|
visitPlaceholder(ph) {
|
|
return `{$${ph.name}}`;
|
|
}
|
|
visitIcuPlaceholder(ph) {
|
|
return `{$${ph.name}}`;
|
|
}
|
|
visitBlockPlaceholder(ph) {
|
|
const children = ph.children.map(child => child.visit(this)).join('');
|
|
return `{$${ph.startName}}${children}{$${ph.closeName}}`;
|
|
}
|
|
}
|
|
|
|
class Serializer {
|
|
createNameMapper(message) {
|
|
return null;
|
|
}
|
|
}
|
|
class SimplePlaceholderMapper extends RecurseVisitor {
|
|
mapName;
|
|
internalToPublic = {};
|
|
publicToNextId = {};
|
|
publicToInternal = {};
|
|
constructor(message, mapName) {
|
|
super();
|
|
this.mapName = mapName;
|
|
message.nodes.forEach(node => node.visit(this));
|
|
}
|
|
toPublicName(internalName) {
|
|
return this.internalToPublic.hasOwnProperty(internalName) ? this.internalToPublic[internalName] : null;
|
|
}
|
|
toInternalName(publicName) {
|
|
return this.publicToInternal.hasOwnProperty(publicName) ? this.publicToInternal[publicName] : null;
|
|
}
|
|
visitText(text, context) {
|
|
return null;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
this.visitPlaceholderName(ph.startName);
|
|
super.visitTagPlaceholder(ph, context);
|
|
this.visitPlaceholderName(ph.closeName);
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
this.visitPlaceholderName(ph.name);
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
this.visitPlaceholderName(ph.startName);
|
|
super.visitBlockPlaceholder(ph, context);
|
|
this.visitPlaceholderName(ph.closeName);
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
this.visitPlaceholderName(ph.name);
|
|
}
|
|
visitPlaceholderName(internalName) {
|
|
if (!internalName || this.internalToPublic.hasOwnProperty(internalName)) {
|
|
return;
|
|
}
|
|
let publicName = this.mapName(internalName);
|
|
if (this.publicToInternal.hasOwnProperty(publicName)) {
|
|
const nextId = this.publicToNextId[publicName];
|
|
this.publicToNextId[publicName] = nextId + 1;
|
|
publicName = `${publicName}_${nextId}`;
|
|
} else {
|
|
this.publicToNextId[publicName] = 1;
|
|
}
|
|
this.internalToPublic[internalName] = publicName;
|
|
this.publicToInternal[publicName] = internalName;
|
|
}
|
|
}
|
|
|
|
let _Visitor$2 = class _Visitor {
|
|
visitTag(tag) {
|
|
const strAttrs = this._serializeAttributes(tag.attrs);
|
|
if (tag.children.length == 0) {
|
|
return `<${tag.name}${strAttrs}/>`;
|
|
}
|
|
const strChildren = tag.children.map(node => node.visit(this));
|
|
return `<${tag.name}${strAttrs}>${strChildren.join('')}</${tag.name}>`;
|
|
}
|
|
visitText(text) {
|
|
return text.value;
|
|
}
|
|
visitDeclaration(decl) {
|
|
return `<?xml${this._serializeAttributes(decl.attrs)} ?>`;
|
|
}
|
|
_serializeAttributes(attrs) {
|
|
const strAttrs = Object.keys(attrs).map(name => `${name}="${attrs[name]}"`).join(' ');
|
|
return strAttrs.length > 0 ? ' ' + strAttrs : '';
|
|
}
|
|
visitDoctype(doctype) {
|
|
return `<!DOCTYPE ${doctype.rootTag} [\n${doctype.dtd}\n]>`;
|
|
}
|
|
};
|
|
const _visitor = new _Visitor$2();
|
|
function serialize$1(nodes) {
|
|
return nodes.map(node => node.visit(_visitor)).join('');
|
|
}
|
|
class Declaration {
|
|
attrs = {};
|
|
constructor(unescapedAttrs) {
|
|
Object.keys(unescapedAttrs).forEach(k => {
|
|
this.attrs[k] = escapeXml(unescapedAttrs[k]);
|
|
});
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDeclaration(this);
|
|
}
|
|
}
|
|
class Doctype {
|
|
rootTag;
|
|
dtd;
|
|
constructor(rootTag, dtd) {
|
|
this.rootTag = rootTag;
|
|
this.dtd = dtd;
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitDoctype(this);
|
|
}
|
|
}
|
|
class Tag {
|
|
name;
|
|
children;
|
|
attrs = {};
|
|
constructor(name, unescapedAttrs = {}, children = []) {
|
|
this.name = name;
|
|
this.children = children;
|
|
Object.keys(unescapedAttrs).forEach(k => {
|
|
this.attrs[k] = escapeXml(unescapedAttrs[k]);
|
|
});
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitTag(this);
|
|
}
|
|
}
|
|
let Text$1 = class Text {
|
|
value;
|
|
constructor(unescapedValue) {
|
|
this.value = escapeXml(unescapedValue);
|
|
}
|
|
visit(visitor) {
|
|
return visitor.visitText(this);
|
|
}
|
|
};
|
|
class CR extends Text$1 {
|
|
constructor(ws = 0) {
|
|
super(`\n${new Array(ws + 1).join(' ')}`);
|
|
}
|
|
}
|
|
const _ESCAPED_CHARS = [[/&/g, '&'], [/"/g, '"'], [/'/g, '''], [/</g, '<'], [/>/g, '>']];
|
|
function escapeXml(text) {
|
|
return _ESCAPED_CHARS.reduce((text, entry) => text.replace(entry[0], entry[1]), text);
|
|
}
|
|
|
|
const _XMB_HANDLER = 'angular';
|
|
const _MESSAGES_TAG = 'messagebundle';
|
|
const _MESSAGE_TAG = 'msg';
|
|
const _PLACEHOLDER_TAG$3 = 'ph';
|
|
const _EXAMPLE_TAG = 'ex';
|
|
const _SOURCE_TAG$2 = 'source';
|
|
const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
|
<!ATTLIST messagebundle class CDATA #IMPLIED>
|
|
|
|
<!ELEMENT msg (#PCDATA|ph|source)*>
|
|
<!ATTLIST msg id CDATA #IMPLIED>
|
|
<!ATTLIST msg seq CDATA #IMPLIED>
|
|
<!ATTLIST msg name CDATA #IMPLIED>
|
|
<!ATTLIST msg desc CDATA #IMPLIED>
|
|
<!ATTLIST msg meaning CDATA #IMPLIED>
|
|
<!ATTLIST msg obsolete (obsolete) #IMPLIED>
|
|
<!ATTLIST msg xml:space (default|preserve) "default">
|
|
<!ATTLIST msg is_hidden CDATA #IMPLIED>
|
|
|
|
<!ELEMENT source (#PCDATA)>
|
|
|
|
<!ELEMENT ph (#PCDATA|ex)*>
|
|
<!ATTLIST ph name CDATA #REQUIRED>
|
|
|
|
<!ELEMENT ex (#PCDATA)>`;
|
|
class Xmb extends Serializer {
|
|
write(messages, locale) {
|
|
const exampleVisitor = new ExampleVisitor();
|
|
const visitor = new _Visitor$1();
|
|
const rootNode = new Tag(_MESSAGES_TAG);
|
|
rootNode.attrs['handler'] = _XMB_HANDLER;
|
|
messages.forEach(message => {
|
|
const attrs = {
|
|
id: message.id
|
|
};
|
|
if (message.description) {
|
|
attrs['desc'] = message.description;
|
|
}
|
|
if (message.meaning) {
|
|
attrs['meaning'] = message.meaning;
|
|
}
|
|
let sourceTags = [];
|
|
message.sources.forEach(source => {
|
|
sourceTags.push(new Tag(_SOURCE_TAG$2, {}, [new Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)]));
|
|
});
|
|
rootNode.children.push(new CR(2), new Tag(_MESSAGE_TAG, attrs, [...sourceTags, ...visitor.serialize(message.nodes)]));
|
|
});
|
|
rootNode.children.push(new CR());
|
|
return serialize$1([new Declaration({
|
|
version: '1.0',
|
|
encoding: 'UTF-8'
|
|
}), new CR(), new Doctype(_MESSAGES_TAG, _DOCTYPE), new CR(), exampleVisitor.addDefaultExamples(rootNode), new CR()]);
|
|
}
|
|
load(content, url) {
|
|
throw new Error('Unsupported');
|
|
}
|
|
digest(message) {
|
|
return digest(message);
|
|
}
|
|
createNameMapper(message) {
|
|
return new SimplePlaceholderMapper(message, toPublicName);
|
|
}
|
|
}
|
|
let _Visitor$1 = class _Visitor {
|
|
visitText(text, context) {
|
|
return [new Text$1(text.value)];
|
|
}
|
|
visitContainer(container, context) {
|
|
const nodes = [];
|
|
container.children.forEach(node => nodes.push(...node.visit(this)));
|
|
return nodes;
|
|
}
|
|
visitIcu(icu, context) {
|
|
const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
|
Object.keys(icu.cases).forEach(c => {
|
|
nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `));
|
|
});
|
|
nodes.push(new Text$1(`}`));
|
|
return nodes;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
const startTagAsText = new Text$1(`<${ph.tag}>`);
|
|
const startEx = new Tag(_EXAMPLE_TAG, {}, [startTagAsText]);
|
|
const startTagPh = new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.startName
|
|
}, [startEx, startTagAsText]);
|
|
if (ph.isVoid) {
|
|
return [startTagPh];
|
|
}
|
|
const closeTagAsText = new Text$1(`</${ph.tag}>`);
|
|
const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeTagAsText]);
|
|
const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.closeName
|
|
}, [closeEx, closeTagAsText]);
|
|
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
const interpolationAsText = new Text$1(`{{${ph.value}}}`);
|
|
const exTag = new Tag(_EXAMPLE_TAG, {}, [interpolationAsText]);
|
|
return [new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.name
|
|
}, [exTag, interpolationAsText])];
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
const startAsText = new Text$1(`@${ph.name}`);
|
|
const startEx = new Tag(_EXAMPLE_TAG, {}, [startAsText]);
|
|
const startTagPh = new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.startName
|
|
}, [startEx, startAsText]);
|
|
const closeAsText = new Text$1(`}`);
|
|
const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeAsText]);
|
|
const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.closeName
|
|
}, [closeEx, closeAsText]);
|
|
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
const icuExpression = ph.value.expression;
|
|
const icuType = ph.value.type;
|
|
const icuCases = Object.keys(ph.value.cases).map(value => value + ' {...}').join(' ');
|
|
const icuAsText = new Text$1(`{${icuExpression}, ${icuType}, ${icuCases}}`);
|
|
const exTag = new Tag(_EXAMPLE_TAG, {}, [icuAsText]);
|
|
return [new Tag(_PLACEHOLDER_TAG$3, {
|
|
name: ph.name
|
|
}, [exTag, icuAsText])];
|
|
}
|
|
serialize(nodes) {
|
|
return [].concat(...nodes.map(node => node.visit(this)));
|
|
}
|
|
};
|
|
function digest(message) {
|
|
return decimalDigest(message);
|
|
}
|
|
class ExampleVisitor {
|
|
addDefaultExamples(node) {
|
|
node.visit(this);
|
|
return node;
|
|
}
|
|
visitTag(tag) {
|
|
if (tag.name === _PLACEHOLDER_TAG$3) {
|
|
if (!tag.children || tag.children.length == 0) {
|
|
const exText = new Text$1(tag.attrs['name'] || '...');
|
|
tag.children = [new Tag(_EXAMPLE_TAG, {}, [exText])];
|
|
}
|
|
} else if (tag.children) {
|
|
tag.children.forEach(node => node.visit(this));
|
|
}
|
|
}
|
|
visitText(text) {}
|
|
visitDeclaration(decl) {}
|
|
visitDoctype(doctype) {}
|
|
}
|
|
function toPublicName(internalName) {
|
|
return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
|
}
|
|
|
|
const I18N_ATTR = 'i18n';
|
|
const I18N_ATTR_PREFIX = 'i18n-';
|
|
const I18N_ICU_VAR_PREFIX = 'VAR_';
|
|
function isI18nAttribute(name) {
|
|
return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
|
|
}
|
|
function hasI18nAttrs(node) {
|
|
return node.attrs.some(attr => isI18nAttribute(attr.name));
|
|
}
|
|
function icuFromI18nMessage(message) {
|
|
return message.nodes[0];
|
|
}
|
|
function formatI18nPlaceholderNamesInMap(params = {}, useCamelCase) {
|
|
const _params = {};
|
|
if (params && Object.keys(params).length) {
|
|
Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
|
|
}
|
|
return _params;
|
|
}
|
|
function formatI18nPlaceholderName(name, useCamelCase = true) {
|
|
const publicName = toPublicName(name);
|
|
if (!useCamelCase) {
|
|
return publicName;
|
|
}
|
|
const chunks = publicName.split('_');
|
|
if (chunks.length === 1) {
|
|
return name.toLowerCase();
|
|
}
|
|
let postfix;
|
|
if (/^\d+$/.test(chunks[chunks.length - 1])) {
|
|
postfix = chunks.pop();
|
|
}
|
|
let raw = chunks.shift().toLowerCase();
|
|
if (chunks.length) {
|
|
raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
|
|
}
|
|
return postfix ? `${raw}_${postfix}` : raw;
|
|
}
|
|
|
|
const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
|
|
const TEMPORARY_NAME = '_t';
|
|
const CONTEXT_NAME = 'ctx';
|
|
const RENDER_FLAGS = 'rf';
|
|
function temporaryAllocator(pushStatement, name) {
|
|
let temp = null;
|
|
return () => {
|
|
if (!temp) {
|
|
pushStatement(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
|
|
temp = variable(name);
|
|
}
|
|
return temp;
|
|
};
|
|
}
|
|
function asLiteral(value) {
|
|
if (Array.isArray(value)) {
|
|
return literalArr(value.map(asLiteral));
|
|
}
|
|
return literal(value, INFERRED_TYPE);
|
|
}
|
|
function conditionallyCreateDirectiveBindingLiteral(map, forInputs) {
|
|
const keys = Object.getOwnPropertyNames(map);
|
|
if (keys.length === 0) {
|
|
return null;
|
|
}
|
|
return literalMap(keys.map(key => {
|
|
const value = map[key];
|
|
let declaredName;
|
|
let publicName;
|
|
let minifiedName;
|
|
let expressionValue;
|
|
if (typeof value === 'string') {
|
|
declaredName = key;
|
|
minifiedName = key;
|
|
publicName = value;
|
|
expressionValue = asLiteral(publicName);
|
|
} else {
|
|
minifiedName = key;
|
|
declaredName = value.classPropertyName;
|
|
publicName = value.bindingPropertyName;
|
|
const differentDeclaringName = publicName !== declaredName;
|
|
const hasDecoratorInputTransform = value.transformFunction !== null;
|
|
let flags = InputFlags.None;
|
|
if (value.isSignal) {
|
|
flags |= InputFlags.SignalBased;
|
|
}
|
|
if (hasDecoratorInputTransform) {
|
|
flags |= InputFlags.HasDecoratorInputTransform;
|
|
}
|
|
if (forInputs && (differentDeclaringName || hasDecoratorInputTransform || flags !== InputFlags.None)) {
|
|
const result = [literal(flags), asLiteral(publicName)];
|
|
if (differentDeclaringName || hasDecoratorInputTransform) {
|
|
result.push(asLiteral(declaredName));
|
|
if (hasDecoratorInputTransform) {
|
|
result.push(value.transformFunction);
|
|
}
|
|
}
|
|
expressionValue = literalArr(result);
|
|
} else {
|
|
expressionValue = asLiteral(publicName);
|
|
}
|
|
}
|
|
return {
|
|
key: minifiedName,
|
|
quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
|
|
value: expressionValue
|
|
};
|
|
}));
|
|
}
|
|
class DefinitionMap {
|
|
values = [];
|
|
set(key, value) {
|
|
if (value) {
|
|
const existing = this.values.find(value => value.key === key);
|
|
if (existing) {
|
|
existing.value = value;
|
|
} else {
|
|
this.values.push({
|
|
key: key,
|
|
value,
|
|
quoted: false
|
|
});
|
|
}
|
|
}
|
|
}
|
|
toLiteralMap() {
|
|
return literalMap(this.values);
|
|
}
|
|
}
|
|
function createCssSelectorFromNode(node) {
|
|
const elementName = node instanceof Element$1 ? node.name : 'ng-template';
|
|
const attributes = getAttrsForDirectiveMatching(node);
|
|
const cssSelector = new CssSelector();
|
|
const elementNameNoNs = splitNsName(elementName)[1];
|
|
cssSelector.setElement(elementNameNoNs);
|
|
Object.getOwnPropertyNames(attributes).forEach(name => {
|
|
const nameNoNs = splitNsName(name)[1];
|
|
const value = attributes[name];
|
|
cssSelector.addAttribute(nameNoNs, value);
|
|
if (name.toLowerCase() === 'class') {
|
|
const classes = value.trim().split(/\s+/);
|
|
classes.forEach(className => cssSelector.addClassName(className));
|
|
}
|
|
});
|
|
return cssSelector;
|
|
}
|
|
function getAttrsForDirectiveMatching(elOrTpl) {
|
|
const attributesMap = {};
|
|
if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
|
|
elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
|
|
} else {
|
|
elOrTpl.attributes.forEach(a => {
|
|
if (!isI18nAttribute(a.name)) {
|
|
attributesMap[a.name] = a.value;
|
|
}
|
|
});
|
|
elOrTpl.inputs.forEach(i => {
|
|
if (i.type === BindingType.Property || i.type === BindingType.TwoWay) {
|
|
attributesMap[i.name] = '';
|
|
}
|
|
});
|
|
elOrTpl.outputs.forEach(o => {
|
|
attributesMap[o.name] = '';
|
|
});
|
|
}
|
|
return attributesMap;
|
|
}
|
|
|
|
function compileInjectable(meta, resolveForwardRefs) {
|
|
let result = null;
|
|
const factoryMeta = {
|
|
name: meta.name,
|
|
type: meta.type,
|
|
typeArgumentCount: meta.typeArgumentCount,
|
|
deps: [],
|
|
target: FactoryTarget.Injectable
|
|
};
|
|
if (meta.useClass !== undefined) {
|
|
const useClassOnSelf = meta.useClass.expression.isEquivalent(meta.type.value);
|
|
let deps = undefined;
|
|
if (meta.deps !== undefined) {
|
|
deps = meta.deps;
|
|
}
|
|
if (deps !== undefined) {
|
|
result = compileFactoryFunction({
|
|
...factoryMeta,
|
|
delegate: meta.useClass.expression,
|
|
delegateDeps: deps,
|
|
delegateType: R3FactoryDelegateType.Class
|
|
});
|
|
} else if (useClassOnSelf) {
|
|
result = compileFactoryFunction(factoryMeta);
|
|
} else {
|
|
result = {
|
|
statements: [],
|
|
expression: delegateToFactory(meta.type.value, meta.useClass.expression, resolveForwardRefs)
|
|
};
|
|
}
|
|
} else if (meta.useFactory !== undefined) {
|
|
if (meta.deps !== undefined) {
|
|
result = compileFactoryFunction({
|
|
...factoryMeta,
|
|
delegate: meta.useFactory,
|
|
delegateDeps: meta.deps || [],
|
|
delegateType: R3FactoryDelegateType.Function
|
|
});
|
|
} else {
|
|
result = {
|
|
statements: [],
|
|
expression: arrowFn([], meta.useFactory.callFn([]))
|
|
};
|
|
}
|
|
} else if (meta.useValue !== undefined) {
|
|
result = compileFactoryFunction({
|
|
...factoryMeta,
|
|
expression: meta.useValue.expression
|
|
});
|
|
} else if (meta.useExisting !== undefined) {
|
|
result = compileFactoryFunction({
|
|
...factoryMeta,
|
|
expression: importExpr(Identifiers.inject).callFn([meta.useExisting.expression])
|
|
});
|
|
} else {
|
|
result = {
|
|
statements: [],
|
|
expression: delegateToFactory(meta.type.value, meta.type.value, resolveForwardRefs)
|
|
};
|
|
}
|
|
const token = meta.type.value;
|
|
const injectableProps = new DefinitionMap();
|
|
injectableProps.set('token', token);
|
|
injectableProps.set('factory', result.expression);
|
|
if (meta.providedIn.expression.value !== null) {
|
|
injectableProps.set('providedIn', convertFromMaybeForwardRefExpression(meta.providedIn));
|
|
}
|
|
const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([injectableProps.toLiteralMap()], undefined, true);
|
|
return {
|
|
expression,
|
|
type: createInjectableType(meta),
|
|
statements: result.statements
|
|
};
|
|
}
|
|
function createInjectableType(meta) {
|
|
return new ExpressionType(importExpr(Identifiers.InjectableDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
|
|
}
|
|
function delegateToFactory(type, useType, unwrapForwardRefs) {
|
|
if (type.node === useType.node) {
|
|
return useType.prop('ɵfac');
|
|
}
|
|
if (!unwrapForwardRefs) {
|
|
return createFactoryFunction(useType);
|
|
}
|
|
const unwrappedType = importExpr(Identifiers.resolveForwardRef).callFn([useType]);
|
|
return createFactoryFunction(unwrappedType);
|
|
}
|
|
function createFactoryFunction(type) {
|
|
const t = new FnParam('__ngFactoryType__', DYNAMIC_TYPE);
|
|
return arrowFn([t], type.prop('ɵfac').callFn([variable(t.name)]));
|
|
}
|
|
|
|
const $EOF = 0;
|
|
const $BSPACE = 8;
|
|
const $TAB = 9;
|
|
const $LF = 10;
|
|
const $VTAB = 11;
|
|
const $FF = 12;
|
|
const $CR = 13;
|
|
const $SPACE = 32;
|
|
const $BANG = 33;
|
|
const $DQ = 34;
|
|
const $HASH = 35;
|
|
const $$ = 36;
|
|
const $PERCENT = 37;
|
|
const $AMPERSAND = 38;
|
|
const $SQ = 39;
|
|
const $LPAREN = 40;
|
|
const $RPAREN = 41;
|
|
const $STAR = 42;
|
|
const $PLUS = 43;
|
|
const $COMMA = 44;
|
|
const $MINUS = 45;
|
|
const $PERIOD = 46;
|
|
const $SLASH = 47;
|
|
const $COLON = 58;
|
|
const $SEMICOLON = 59;
|
|
const $LT = 60;
|
|
const $EQ = 61;
|
|
const $GT = 62;
|
|
const $QUESTION = 63;
|
|
const $0 = 48;
|
|
const $7 = 55;
|
|
const $9 = 57;
|
|
const $A = 65;
|
|
const $E = 69;
|
|
const $F = 70;
|
|
const $X = 88;
|
|
const $Z = 90;
|
|
const $LBRACKET = 91;
|
|
const $BACKSLASH = 92;
|
|
const $RBRACKET = 93;
|
|
const $CARET = 94;
|
|
const $_ = 95;
|
|
const $a = 97;
|
|
const $b = 98;
|
|
const $e = 101;
|
|
const $f = 102;
|
|
const $n = 110;
|
|
const $r = 114;
|
|
const $t = 116;
|
|
const $u = 117;
|
|
const $v = 118;
|
|
const $x = 120;
|
|
const $z = 122;
|
|
const $LBRACE = 123;
|
|
const $BAR = 124;
|
|
const $RBRACE = 125;
|
|
const $NBSP = 160;
|
|
const $AT = 64;
|
|
const $BT = 96;
|
|
function isWhitespace(code) {
|
|
return code >= $TAB && code <= $SPACE || code == $NBSP;
|
|
}
|
|
function isDigit(code) {
|
|
return $0 <= code && code <= $9;
|
|
}
|
|
function isAsciiLetter(code) {
|
|
return code >= $a && code <= $z || code >= $A && code <= $Z;
|
|
}
|
|
function isAsciiHexDigit(code) {
|
|
return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
|
|
}
|
|
function isNewLine(code) {
|
|
return code === $LF || code === $CR;
|
|
}
|
|
function isOctalDigit(code) {
|
|
return $0 <= code && code <= $7;
|
|
}
|
|
function isQuote(code) {
|
|
return code === $SQ || code === $DQ || code === $BT;
|
|
}
|
|
|
|
class ParseLocation {
|
|
file;
|
|
offset;
|
|
line;
|
|
col;
|
|
constructor(file, offset, line, col) {
|
|
this.file = file;
|
|
this.offset = offset;
|
|
this.line = line;
|
|
this.col = col;
|
|
}
|
|
toString() {
|
|
return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
|
|
}
|
|
moveBy(delta) {
|
|
const source = this.file.content;
|
|
const len = source.length;
|
|
let offset = this.offset;
|
|
let line = this.line;
|
|
let col = this.col;
|
|
while (offset > 0 && delta < 0) {
|
|
offset--;
|
|
delta++;
|
|
const ch = source.charCodeAt(offset);
|
|
if (ch == $LF) {
|
|
line--;
|
|
const priorLine = source.substring(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
|
|
col = priorLine > 0 ? offset - priorLine : offset;
|
|
} else {
|
|
col--;
|
|
}
|
|
}
|
|
while (offset < len && delta > 0) {
|
|
const ch = source.charCodeAt(offset);
|
|
offset++;
|
|
delta--;
|
|
if (ch == $LF) {
|
|
line++;
|
|
col = 0;
|
|
} else {
|
|
col++;
|
|
}
|
|
}
|
|
return new ParseLocation(this.file, offset, line, col);
|
|
}
|
|
getContext(maxChars, maxLines) {
|
|
const content = this.file.content;
|
|
let startOffset = this.offset;
|
|
if (startOffset != null) {
|
|
if (startOffset > content.length - 1) {
|
|
startOffset = content.length - 1;
|
|
}
|
|
let endOffset = startOffset;
|
|
let ctxChars = 0;
|
|
let ctxLines = 0;
|
|
while (ctxChars < maxChars && startOffset > 0) {
|
|
startOffset--;
|
|
ctxChars++;
|
|
if (content[startOffset] == '\n') {
|
|
if (++ctxLines == maxLines) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ctxChars = 0;
|
|
ctxLines = 0;
|
|
while (ctxChars < maxChars && endOffset < content.length - 1) {
|
|
endOffset++;
|
|
ctxChars++;
|
|
if (content[endOffset] == '\n') {
|
|
if (++ctxLines == maxLines) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
before: content.substring(startOffset, this.offset),
|
|
after: content.substring(this.offset, endOffset + 1)
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
class ParseSourceFile {
|
|
content;
|
|
url;
|
|
constructor(content, url) {
|
|
this.content = content;
|
|
this.url = url;
|
|
}
|
|
}
|
|
class ParseSourceSpan {
|
|
start;
|
|
end;
|
|
fullStart;
|
|
details;
|
|
constructor(start, end, fullStart = start, details = null) {
|
|
this.start = start;
|
|
this.end = end;
|
|
this.fullStart = fullStart;
|
|
this.details = details;
|
|
}
|
|
toString() {
|
|
return this.start.file.content.substring(this.start.offset, this.end.offset);
|
|
}
|
|
}
|
|
var ParseErrorLevel;
|
|
(function (ParseErrorLevel) {
|
|
ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
|
|
ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
|
|
})(ParseErrorLevel || (ParseErrorLevel = {}));
|
|
class ParseError extends Error {
|
|
span;
|
|
msg;
|
|
level;
|
|
relatedError;
|
|
constructor(span, msg, level = ParseErrorLevel.ERROR, relatedError) {
|
|
super(msg);
|
|
this.span = span;
|
|
this.msg = msg;
|
|
this.level = level;
|
|
this.relatedError = relatedError;
|
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
}
|
|
contextualMessage() {
|
|
const ctx = this.span.start.getContext(100, 3);
|
|
return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` : this.msg;
|
|
}
|
|
toString() {
|
|
const details = this.span.details ? `, ${this.span.details}` : '';
|
|
return `${this.contextualMessage()}: ${this.span.start}${details}`;
|
|
}
|
|
}
|
|
function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
|
|
const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
|
|
const sourceFile = new ParseSourceFile('', sourceFileName);
|
|
return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
|
|
}
|
|
let _anonymousTypeIndex = 0;
|
|
function identifierName(compileIdentifier) {
|
|
if (!compileIdentifier || !compileIdentifier.reference) {
|
|
return null;
|
|
}
|
|
const ref = compileIdentifier.reference;
|
|
if (ref['__anonymousType']) {
|
|
return ref['__anonymousType'];
|
|
}
|
|
if (ref['__forward_ref__']) {
|
|
return '__forward_ref__';
|
|
}
|
|
let identifier = stringify(ref);
|
|
if (identifier.indexOf('(') >= 0) {
|
|
identifier = `anonymous_${_anonymousTypeIndex++}`;
|
|
ref['__anonymousType'] = identifier;
|
|
} else {
|
|
identifier = sanitizeIdentifier(identifier);
|
|
}
|
|
return identifier;
|
|
}
|
|
function sanitizeIdentifier(name) {
|
|
return name.replace(/\W/g, '_');
|
|
}
|
|
|
|
const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
|
|
class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
|
constructor() {
|
|
super(false);
|
|
}
|
|
visitWrappedNodeExpr(ast, ctx) {
|
|
throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
|
|
}
|
|
visitDeclareVarStmt(stmt, ctx) {
|
|
ctx.print(stmt, `var ${stmt.name}`);
|
|
if (stmt.value) {
|
|
ctx.print(stmt, ' = ');
|
|
stmt.value.visitExpression(this, ctx);
|
|
}
|
|
ctx.println(stmt, `;`);
|
|
return null;
|
|
}
|
|
visitTaggedTemplateLiteralExpr(ast, ctx) {
|
|
const elements = ast.template.elements;
|
|
ast.tag.visitExpression(this, ctx);
|
|
ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
|
|
ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
|
|
ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
|
|
ast.template.expressions.forEach(expression => {
|
|
ctx.print(ast, ', ');
|
|
expression.visitExpression(this, ctx);
|
|
});
|
|
ctx.print(ast, ')');
|
|
return null;
|
|
}
|
|
visitTemplateLiteralExpr(expr, ctx) {
|
|
ctx.print(expr, '`');
|
|
for (let i = 0; i < expr.elements.length; i++) {
|
|
expr.elements[i].visitExpression(this, ctx);
|
|
const expression = i < expr.expressions.length ? expr.expressions[i] : null;
|
|
if (expression !== null) {
|
|
ctx.print(expression, '${');
|
|
expression.visitExpression(this, ctx);
|
|
ctx.print(expression, '}');
|
|
}
|
|
}
|
|
ctx.print(expr, '`');
|
|
}
|
|
visitTemplateLiteralElementExpr(expr, ctx) {
|
|
ctx.print(expr, expr.rawText);
|
|
return null;
|
|
}
|
|
visitFunctionExpr(ast, ctx) {
|
|
ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
|
|
this._visitParams(ast.params, ctx);
|
|
ctx.println(ast, `) {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(ast.statements, ctx);
|
|
ctx.decIndent();
|
|
ctx.print(ast, `}`);
|
|
return null;
|
|
}
|
|
visitArrowFunctionExpr(ast, ctx) {
|
|
ctx.print(ast, '(');
|
|
this._visitParams(ast.params, ctx);
|
|
ctx.print(ast, ') =>');
|
|
if (Array.isArray(ast.body)) {
|
|
ctx.println(ast, `{`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(ast.body, ctx);
|
|
ctx.decIndent();
|
|
ctx.print(ast, `}`);
|
|
} else {
|
|
const isObjectLiteral = ast.body instanceof LiteralMapExpr;
|
|
if (isObjectLiteral) {
|
|
ctx.print(ast, '(');
|
|
}
|
|
ast.body.visitExpression(this, ctx);
|
|
if (isObjectLiteral) {
|
|
ctx.print(ast, ')');
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
visitDeclareFunctionStmt(stmt, ctx) {
|
|
ctx.print(stmt, `function ${stmt.name}(`);
|
|
this._visitParams(stmt.params, ctx);
|
|
ctx.println(stmt, `) {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(stmt.statements, ctx);
|
|
ctx.decIndent();
|
|
ctx.println(stmt, `}`);
|
|
return null;
|
|
}
|
|
visitLocalizedString(ast, ctx) {
|
|
ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
|
|
const parts = [ast.serializeI18nHead()];
|
|
for (let i = 1; i < ast.messageParts.length; i++) {
|
|
parts.push(ast.serializeI18nTemplatePart(i));
|
|
}
|
|
ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
|
|
ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
|
|
ast.expressions.forEach(expression => {
|
|
ctx.print(ast, ', ');
|
|
expression.visitExpression(this, ctx);
|
|
});
|
|
ctx.print(ast, ')');
|
|
return null;
|
|
}
|
|
_visitParams(params, ctx) {
|
|
this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
|
|
}
|
|
}
|
|
|
|
let policy;
|
|
function getPolicy() {
|
|
if (policy === undefined) {
|
|
const trustedTypes = _global['trustedTypes'];
|
|
policy = null;
|
|
if (trustedTypes) {
|
|
try {
|
|
policy = trustedTypes.createPolicy('angular#unsafe-jit', {
|
|
createScript: s => s
|
|
});
|
|
} catch {}
|
|
}
|
|
}
|
|
return policy;
|
|
}
|
|
function trustedScriptFromString(script) {
|
|
return getPolicy()?.createScript(script) || script;
|
|
}
|
|
function newTrustedFunctionForJIT(...args) {
|
|
if (!_global['trustedTypes']) {
|
|
return new Function(...args);
|
|
}
|
|
const fnArgs = args.slice(0, -1).join(',');
|
|
const fnBody = args[args.length - 1];
|
|
const body = `(function anonymous(${fnArgs}
|
|
) { ${fnBody}
|
|
})`;
|
|
const fn = _global['eval'](trustedScriptFromString(body));
|
|
if (fn.bind === undefined) {
|
|
return new Function(...args);
|
|
}
|
|
fn.toString = () => body;
|
|
return fn.bind(_global);
|
|
}
|
|
|
|
class JitEvaluator {
|
|
evaluateStatements(sourceUrl, statements, refResolver, createSourceMaps) {
|
|
const converter = new JitEmitterVisitor(refResolver);
|
|
const ctx = EmitterVisitorContext.createRoot();
|
|
if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
|
|
statements = [literal('use strict').toStmt(), ...statements];
|
|
}
|
|
converter.visitAllStatements(statements, ctx);
|
|
converter.createReturnStmt(ctx);
|
|
return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
|
|
}
|
|
evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
|
|
let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
|
|
const fnArgNames = [];
|
|
const fnArgValues = [];
|
|
for (const argName in vars) {
|
|
fnArgValues.push(vars[argName]);
|
|
fnArgNames.push(argName);
|
|
}
|
|
if (createSourceMap) {
|
|
const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
|
|
const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
|
|
fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
|
|
}
|
|
const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
|
|
return this.executeFunction(fn, fnArgValues);
|
|
}
|
|
executeFunction(fn, args) {
|
|
return fn(...args);
|
|
}
|
|
}
|
|
class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
|
refResolver;
|
|
_evalArgNames = [];
|
|
_evalArgValues = [];
|
|
_evalExportedVars = [];
|
|
constructor(refResolver) {
|
|
super();
|
|
this.refResolver = refResolver;
|
|
}
|
|
createReturnStmt(ctx) {
|
|
const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapPropertyAssignment(resultVar, variable(resultVar), false))));
|
|
stmt.visitStatement(this, ctx);
|
|
}
|
|
getArgs() {
|
|
const result = {};
|
|
for (let i = 0; i < this._evalArgNames.length; i++) {
|
|
result[this._evalArgNames[i]] = this._evalArgValues[i];
|
|
}
|
|
return result;
|
|
}
|
|
visitExternalExpr(ast, ctx) {
|
|
this._emitReferenceToExternal(ast, this.refResolver.resolveExternalReference(ast.value), ctx);
|
|
return null;
|
|
}
|
|
visitWrappedNodeExpr(ast, ctx) {
|
|
this._emitReferenceToExternal(ast, ast.node, ctx);
|
|
return null;
|
|
}
|
|
visitDeclareVarStmt(stmt, ctx) {
|
|
if (stmt.hasModifier(StmtModifier.Exported)) {
|
|
this._evalExportedVars.push(stmt.name);
|
|
}
|
|
return super.visitDeclareVarStmt(stmt, ctx);
|
|
}
|
|
visitDeclareFunctionStmt(stmt, ctx) {
|
|
if (stmt.hasModifier(StmtModifier.Exported)) {
|
|
this._evalExportedVars.push(stmt.name);
|
|
}
|
|
return super.visitDeclareFunctionStmt(stmt, ctx);
|
|
}
|
|
_emitReferenceToExternal(ast, value, ctx) {
|
|
let id = this._evalArgValues.indexOf(value);
|
|
if (id === -1) {
|
|
id = this._evalArgValues.length;
|
|
this._evalArgValues.push(value);
|
|
const name = identifierName({
|
|
reference: value
|
|
}) || 'val';
|
|
this._evalArgNames.push(`jit_${name}_${id}`);
|
|
}
|
|
ctx.print(ast, this._evalArgNames[id]);
|
|
}
|
|
}
|
|
function isUseStrictStatement(statement) {
|
|
return statement.isEquivalent(literal('use strict').toStmt());
|
|
}
|
|
|
|
function compileInjector(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
if (meta.providers !== null) {
|
|
definitionMap.set('providers', meta.providers);
|
|
}
|
|
if (meta.imports.length > 0) {
|
|
definitionMap.set('imports', literalArr(meta.imports));
|
|
}
|
|
const expression = importExpr(Identifiers.defineInjector).callFn([definitionMap.toLiteralMap()], undefined, true);
|
|
const type = createInjectorType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createInjectorType(meta) {
|
|
return new ExpressionType(importExpr(Identifiers.InjectorDeclaration, [new ExpressionType(meta.type.type)]));
|
|
}
|
|
|
|
class R3JitReflector {
|
|
context;
|
|
constructor(context) {
|
|
this.context = context;
|
|
}
|
|
resolveExternalReference(ref) {
|
|
if (ref.moduleName !== '@angular/core') {
|
|
throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
|
|
}
|
|
if (!this.context.hasOwnProperty(ref.name)) {
|
|
throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
|
|
}
|
|
return this.context[ref.name];
|
|
}
|
|
}
|
|
|
|
var R3SelectorScopeMode;
|
|
(function (R3SelectorScopeMode) {
|
|
R3SelectorScopeMode[R3SelectorScopeMode["Inline"] = 0] = "Inline";
|
|
R3SelectorScopeMode[R3SelectorScopeMode["SideEffect"] = 1] = "SideEffect";
|
|
R3SelectorScopeMode[R3SelectorScopeMode["Omit"] = 2] = "Omit";
|
|
})(R3SelectorScopeMode || (R3SelectorScopeMode = {}));
|
|
var R3NgModuleMetadataKind;
|
|
(function (R3NgModuleMetadataKind) {
|
|
R3NgModuleMetadataKind[R3NgModuleMetadataKind["Global"] = 0] = "Global";
|
|
R3NgModuleMetadataKind[R3NgModuleMetadataKind["Local"] = 1] = "Local";
|
|
})(R3NgModuleMetadataKind || (R3NgModuleMetadataKind = {}));
|
|
function compileNgModule(meta) {
|
|
const statements = [];
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('type', meta.type.value);
|
|
if (meta.kind === R3NgModuleMetadataKind.Global && meta.bootstrap.length > 0) {
|
|
definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls));
|
|
}
|
|
if (meta.selectorScopeMode === R3SelectorScopeMode.Inline) {
|
|
if (meta.declarations.length > 0) {
|
|
definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
|
|
}
|
|
if (meta.imports.length > 0) {
|
|
definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
|
|
}
|
|
if (meta.exports.length > 0) {
|
|
definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
|
|
}
|
|
} else if (meta.selectorScopeMode === R3SelectorScopeMode.SideEffect) {
|
|
const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
|
|
if (setNgModuleScopeCall !== null) {
|
|
statements.push(setNgModuleScopeCall);
|
|
}
|
|
} else ;
|
|
if (meta.schemas !== null && meta.schemas.length > 0) {
|
|
definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value)));
|
|
}
|
|
if (meta.id !== null) {
|
|
definitionMap.set('id', meta.id);
|
|
statements.push(importExpr(Identifiers.registerNgModuleType).callFn([meta.type.value, meta.id]).toStmt());
|
|
}
|
|
const expression = importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true);
|
|
const type = createNgModuleType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements
|
|
};
|
|
}
|
|
function compileNgModuleDeclarationExpression(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('type', new WrappedNodeExpr(meta.type));
|
|
if (meta.bootstrap !== undefined) {
|
|
definitionMap.set('bootstrap', new WrappedNodeExpr(meta.bootstrap));
|
|
}
|
|
if (meta.declarations !== undefined) {
|
|
definitionMap.set('declarations', new WrappedNodeExpr(meta.declarations));
|
|
}
|
|
if (meta.imports !== undefined) {
|
|
definitionMap.set('imports', new WrappedNodeExpr(meta.imports));
|
|
}
|
|
if (meta.exports !== undefined) {
|
|
definitionMap.set('exports', new WrappedNodeExpr(meta.exports));
|
|
}
|
|
if (meta.schemas !== undefined) {
|
|
definitionMap.set('schemas', new WrappedNodeExpr(meta.schemas));
|
|
}
|
|
if (meta.id !== undefined) {
|
|
definitionMap.set('id', new WrappedNodeExpr(meta.id));
|
|
}
|
|
return importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()]);
|
|
}
|
|
function createNgModuleType(meta) {
|
|
if (meta.kind === R3NgModuleMetadataKind.Local) {
|
|
return new ExpressionType(meta.type.value);
|
|
}
|
|
const {
|
|
type: moduleType,
|
|
declarations,
|
|
exports,
|
|
imports,
|
|
includeImportTypes,
|
|
publicDeclarationTypes
|
|
} = meta;
|
|
return new ExpressionType(importExpr(Identifiers.NgModuleDeclaration, [new ExpressionType(moduleType.type), publicDeclarationTypes === null ? tupleTypeOf(declarations) : tupleOfTypes(publicDeclarationTypes), includeImportTypes ? tupleTypeOf(imports) : NONE_TYPE, tupleTypeOf(exports)]));
|
|
}
|
|
function generateSetNgModuleScopeCall(meta) {
|
|
const scopeMap = new DefinitionMap();
|
|
if (meta.kind === R3NgModuleMetadataKind.Global) {
|
|
if (meta.declarations.length > 0) {
|
|
scopeMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
|
|
}
|
|
} else {
|
|
if (meta.declarationsExpression) {
|
|
scopeMap.set('declarations', meta.declarationsExpression);
|
|
}
|
|
}
|
|
if (meta.kind === R3NgModuleMetadataKind.Global) {
|
|
if (meta.imports.length > 0) {
|
|
scopeMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
|
|
}
|
|
} else {
|
|
if (meta.importsExpression) {
|
|
scopeMap.set('imports', meta.importsExpression);
|
|
}
|
|
}
|
|
if (meta.kind === R3NgModuleMetadataKind.Global) {
|
|
if (meta.exports.length > 0) {
|
|
scopeMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
|
|
}
|
|
} else {
|
|
if (meta.exportsExpression) {
|
|
scopeMap.set('exports', meta.exportsExpression);
|
|
}
|
|
}
|
|
if (meta.kind === R3NgModuleMetadataKind.Local && meta.bootstrapExpression) {
|
|
scopeMap.set('bootstrap', meta.bootstrapExpression);
|
|
}
|
|
if (Object.keys(scopeMap.values).length === 0) {
|
|
return null;
|
|
}
|
|
const fnCall = new InvokeFunctionExpr(importExpr(Identifiers.setNgModuleScope), [meta.type.value, scopeMap.toLiteralMap()]);
|
|
const guardedCall = jitOnlyGuardedExpression(fnCall);
|
|
const iife = new FunctionExpr([], [guardedCall.toStmt()]);
|
|
const iifeCall = new InvokeFunctionExpr(iife, []);
|
|
return iifeCall.toStmt();
|
|
}
|
|
function tupleTypeOf(exp) {
|
|
const types = exp.map(ref => typeofExpr(ref.type));
|
|
return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
|
|
}
|
|
function tupleOfTypes(types) {
|
|
const typeofTypes = types.map(type => typeofExpr(type));
|
|
return types.length > 0 ? expressionType(literalArr(typeofTypes)) : NONE_TYPE;
|
|
}
|
|
|
|
function compilePipeFromMetadata(metadata) {
|
|
const definitionMapValues = [];
|
|
definitionMapValues.push({
|
|
key: 'name',
|
|
value: literal(metadata.pipeName ?? metadata.name),
|
|
quoted: false
|
|
});
|
|
definitionMapValues.push({
|
|
key: 'type',
|
|
value: metadata.type.value,
|
|
quoted: false
|
|
});
|
|
definitionMapValues.push({
|
|
key: 'pure',
|
|
value: literal(metadata.pure),
|
|
quoted: false
|
|
});
|
|
if (metadata.isStandalone === false) {
|
|
definitionMapValues.push({
|
|
key: 'standalone',
|
|
value: literal(false),
|
|
quoted: false
|
|
});
|
|
}
|
|
const expression = importExpr(Identifiers.definePipe).callFn([literalMap(definitionMapValues)], undefined, true);
|
|
const type = createPipeType(metadata);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createPipeType(metadata) {
|
|
return new ExpressionType(importExpr(Identifiers.PipeDeclaration, [typeWithParameters(metadata.type.type, metadata.typeArgumentCount), new ExpressionType(new LiteralExpr(metadata.pipeName)), new ExpressionType(new LiteralExpr(metadata.isStandalone))]));
|
|
}
|
|
|
|
var R3TemplateDependencyKind;
|
|
(function (R3TemplateDependencyKind) {
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
|
|
R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
|
|
})(R3TemplateDependencyKind || (R3TemplateDependencyKind = {}));
|
|
|
|
const animationKeywords = new Set(['inherit', 'initial', 'revert', 'unset', 'alternate', 'alternate-reverse', 'normal', 'reverse', 'backwards', 'both', 'forwards', 'none', 'paused', 'running', 'ease', 'ease-in', 'ease-in-out', 'ease-out', 'linear', 'step-start', 'step-end', 'end', 'jump-both', 'jump-end', 'jump-none', 'jump-start', 'start']);
|
|
const scopedAtRuleIdentifiers = ['@media', '@supports', '@document', '@layer', '@container', '@scope', '@starting-style'];
|
|
class ShadowCss {
|
|
shimCssText(cssText, selector, hostSelector = '') {
|
|
const comments = [];
|
|
cssText = cssText.replace(_commentRe, m => {
|
|
if (m.match(_commentWithHashRe)) {
|
|
comments.push(m);
|
|
} else {
|
|
const newLinesMatches = m.match(_newLinesRe);
|
|
comments.push((newLinesMatches?.join('') ?? '') + '\n');
|
|
}
|
|
return COMMENT_PLACEHOLDER;
|
|
});
|
|
cssText = this._insertDirectives(cssText);
|
|
const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
|
|
let commentIdx = 0;
|
|
return scopedCssText.replace(_commentWithHashPlaceHolderRe, () => comments[commentIdx++]);
|
|
}
|
|
_insertDirectives(cssText) {
|
|
cssText = this._insertPolyfillDirectivesInCssText(cssText);
|
|
return this._insertPolyfillRulesInCssText(cssText);
|
|
}
|
|
_scopeKeyframesRelatedCss(cssText, scopeSelector) {
|
|
const unscopedKeyframesSet = new Set();
|
|
const scopedKeyframesCssText = processRules(cssText, rule => this._scopeLocalKeyframeDeclarations(rule, scopeSelector, unscopedKeyframesSet));
|
|
return processRules(scopedKeyframesCssText, rule => this._scopeAnimationRule(rule, scopeSelector, unscopedKeyframesSet));
|
|
}
|
|
_scopeLocalKeyframeDeclarations(rule, scopeSelector, unscopedKeyframesSet) {
|
|
return {
|
|
...rule,
|
|
selector: rule.selector.replace(/(^@(?:-webkit-)?keyframes(?:\s+))(['"]?)(.+)\2(\s*)$/, (_, start, quote, keyframeName, endSpaces) => {
|
|
unscopedKeyframesSet.add(unescapeQuotes(keyframeName, quote));
|
|
return `${start}${quote}${scopeSelector}_${keyframeName}${quote}${endSpaces}`;
|
|
})
|
|
};
|
|
}
|
|
_scopeAnimationKeyframe(keyframe, scopeSelector, unscopedKeyframesSet) {
|
|
return keyframe.replace(/^(\s*)(['"]?)(.+?)\2(\s*)$/, (_, spaces1, quote, name, spaces2) => {
|
|
name = `${unscopedKeyframesSet.has(unescapeQuotes(name, quote)) ? scopeSelector + '_' : ''}${name}`;
|
|
return `${spaces1}${quote}${name}${quote}${spaces2}`;
|
|
});
|
|
}
|
|
_animationDeclarationKeyframesRe = /(^|\s+|,)(?:(?:(['"])((?:\\\\|\\\2|(?!\2).)+)\2)|(-?[A-Za-z][\w\-]*))(?=[,\s]|$)/g;
|
|
_scopeAnimationRule(rule, scopeSelector, unscopedKeyframesSet) {
|
|
let content = rule.content.replace(/((?:^|\s+|;)(?:-webkit-)?animation\s*:\s*),*([^;]+)/g, (_, start, animationDeclarations) => start + animationDeclarations.replace(this._animationDeclarationKeyframesRe, (original, leadingSpaces, quote = '', quotedName, nonQuotedName) => {
|
|
if (quotedName) {
|
|
return `${leadingSpaces}${this._scopeAnimationKeyframe(`${quote}${quotedName}${quote}`, scopeSelector, unscopedKeyframesSet)}`;
|
|
} else {
|
|
return animationKeywords.has(nonQuotedName) ? original : `${leadingSpaces}${this._scopeAnimationKeyframe(nonQuotedName, scopeSelector, unscopedKeyframesSet)}`;
|
|
}
|
|
}));
|
|
content = content.replace(/((?:^|\s+|;)(?:-webkit-)?animation-name(?:\s*):(?:\s*))([^;]+)/g, (_match, start, commaSeparatedKeyframes) => `${start}${commaSeparatedKeyframes.split(',').map(keyframe => this._scopeAnimationKeyframe(keyframe, scopeSelector, unscopedKeyframesSet)).join(',')}`);
|
|
return {
|
|
...rule,
|
|
content
|
|
};
|
|
}
|
|
_insertPolyfillDirectivesInCssText(cssText) {
|
|
return cssText.replace(_cssContentNextSelectorRe, function (...m) {
|
|
return m[2] + '{';
|
|
});
|
|
}
|
|
_insertPolyfillRulesInCssText(cssText) {
|
|
return cssText.replace(_cssContentRuleRe, (...m) => {
|
|
const rule = m[0].replace(m[1], '').replace(m[2], '');
|
|
return m[4] + rule;
|
|
});
|
|
}
|
|
_scopeCssText(cssText, scopeSelector, hostSelector) {
|
|
const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
|
|
cssText = this._insertPolyfillHostInCssText(cssText);
|
|
cssText = this._convertColonHost(cssText);
|
|
cssText = this._convertColonHostContext(cssText);
|
|
cssText = this._convertShadowDOMSelectors(cssText);
|
|
if (scopeSelector) {
|
|
cssText = this._scopeKeyframesRelatedCss(cssText, scopeSelector);
|
|
cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
|
|
}
|
|
cssText = cssText + '\n' + unscopedRules;
|
|
return cssText.trim();
|
|
}
|
|
_extractUnscopedRulesFromCssText(cssText) {
|
|
let r = '';
|
|
let m;
|
|
_cssContentUnscopedRuleRe.lastIndex = 0;
|
|
while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
|
|
const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
|
|
r += rule + '\n\n';
|
|
}
|
|
return r;
|
|
}
|
|
_convertColonHost(cssText) {
|
|
return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => {
|
|
if (hostSelectors) {
|
|
const convertedSelectors = [];
|
|
for (const hostSelector of this._splitOnTopLevelCommas(hostSelectors, true)) {
|
|
const trimmedHostSelector = hostSelector.trim();
|
|
if (!trimmedHostSelector) break;
|
|
const convertedSelector = _polyfillHostNoCombinator + trimmedHostSelector.replace(_polyfillHost, '') + otherSelectors;
|
|
convertedSelectors.push(convertedSelector);
|
|
}
|
|
return convertedSelectors.join(',');
|
|
} else {
|
|
return _polyfillHostNoCombinator + otherSelectors;
|
|
}
|
|
});
|
|
}
|
|
*_splitOnTopLevelCommas(text, returnOnClosingParen) {
|
|
const length = text.length;
|
|
let parens = 0;
|
|
let prev = 0;
|
|
for (let i = 0; i < length; i++) {
|
|
const charCode = text.charCodeAt(i);
|
|
if (charCode === $LPAREN) {
|
|
parens++;
|
|
} else if (charCode === $RPAREN) {
|
|
parens--;
|
|
if (parens < 0 && returnOnClosingParen) {
|
|
yield text.slice(prev, i);
|
|
return;
|
|
}
|
|
} else if (charCode === $COMMA && parens === 0) {
|
|
yield text.slice(prev, i);
|
|
prev = i + 1;
|
|
}
|
|
}
|
|
yield text.slice(prev);
|
|
}
|
|
_convertColonHostContext(cssText) {
|
|
const results = [];
|
|
for (const part of this._splitOnTopLevelCommas(cssText, false)) {
|
|
results.push(this._convertColonHostContextInSelectorPart(part));
|
|
}
|
|
return results.join(',');
|
|
}
|
|
_convertColonHostContextInSelectorPart(cssText) {
|
|
return cssText.replace(_cssColonHostContextReGlobal, (selectorText, pseudoPrefix) => {
|
|
const contextSelectorGroups = [[]];
|
|
let startIndex = selectorText.indexOf(_polyfillHostContext);
|
|
while (startIndex !== -1) {
|
|
const afterPrefix = selectorText.substring(startIndex + _polyfillHostContext.length);
|
|
if (!afterPrefix || afterPrefix[0] !== '(') {
|
|
selectorText = afterPrefix;
|
|
startIndex = selectorText.indexOf(_polyfillHostContext);
|
|
continue;
|
|
}
|
|
const newContextSelectors = [];
|
|
let endIndex = 0;
|
|
for (const selector of this._splitOnTopLevelCommas(afterPrefix.substring(1), true)) {
|
|
endIndex = endIndex + selector.length + 1;
|
|
const trimmed = selector.trim();
|
|
if (trimmed) {
|
|
newContextSelectors.push(trimmed);
|
|
}
|
|
}
|
|
const contextSelectorGroupsLength = contextSelectorGroups.length;
|
|
repeatGroups(contextSelectorGroups, newContextSelectors.length);
|
|
for (let i = 0; i < newContextSelectors.length; i++) {
|
|
for (let j = 0; j < contextSelectorGroupsLength; j++) {
|
|
contextSelectorGroups[j + i * contextSelectorGroupsLength].push(newContextSelectors[i]);
|
|
}
|
|
}
|
|
selectorText = afterPrefix.substring(endIndex + 1);
|
|
startIndex = selectorText.indexOf(_polyfillHostContext);
|
|
}
|
|
return contextSelectorGroups.map(contextSelectors => _combineHostContextSelectors(contextSelectors, selectorText, pseudoPrefix)).join(', ');
|
|
});
|
|
}
|
|
_convertShadowDOMSelectors(cssText) {
|
|
return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
|
|
}
|
|
_scopeSelectors(cssText, scopeSelector, hostSelector) {
|
|
return processRules(cssText, rule => {
|
|
let selector = rule.selector;
|
|
let content = rule.content;
|
|
if (rule.selector[0] !== '@') {
|
|
selector = this._scopeSelector({
|
|
selector,
|
|
scopeSelector,
|
|
hostSelector,
|
|
isParentSelector: true
|
|
});
|
|
} else if (scopedAtRuleIdentifiers.some(atRule => rule.selector.startsWith(atRule))) {
|
|
content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
|
|
} else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
|
|
content = this._stripScopingSelectors(rule.content);
|
|
}
|
|
return new CssRule(selector, content);
|
|
});
|
|
}
|
|
_stripScopingSelectors(cssText) {
|
|
return processRules(cssText, rule => {
|
|
const selector = rule.selector.replace(_shadowDeepSelectors, ' ').replace(_polyfillHostNoCombinatorRe, ' ');
|
|
return new CssRule(selector, rule.content);
|
|
});
|
|
}
|
|
_safeSelector;
|
|
_shouldScopeIndicator;
|
|
_scopeSelector({
|
|
selector,
|
|
scopeSelector,
|
|
hostSelector,
|
|
isParentSelector = false
|
|
}) {
|
|
const selectorSplitRe = / ?,(?!(?:[^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\))) ?/;
|
|
return selector.split(selectorSplitRe).map(part => part.split(_shadowDeepSelectors)).map(deepParts => {
|
|
const [shallowPart, ...otherParts] = deepParts;
|
|
const applyScope = shallowPart => {
|
|
if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
|
|
return this._applySelectorScope({
|
|
selector: shallowPart,
|
|
scopeSelector,
|
|
hostSelector,
|
|
isParentSelector
|
|
});
|
|
} else {
|
|
return shallowPart;
|
|
}
|
|
};
|
|
return [applyScope(shallowPart), ...otherParts].join(' ');
|
|
}).join(', ');
|
|
}
|
|
_selectorNeedsScoping(selector, scopeSelector) {
|
|
const re = this._makeScopeMatcher(scopeSelector);
|
|
return !re.test(selector);
|
|
}
|
|
_makeScopeMatcher(scopeSelector) {
|
|
const lre = /\[/g;
|
|
const rre = /\]/g;
|
|
scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
|
|
return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
|
|
}
|
|
_applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
|
|
_polyfillHostRe.lastIndex = 0;
|
|
if (_polyfillHostRe.test(selector)) {
|
|
const replaceBy = `[${hostSelector}]`;
|
|
let result = selector;
|
|
while (result.match(_polyfillHostNoCombinatorRe)) {
|
|
result = result.replace(_polyfillHostNoCombinatorRe, (_hnc, selector) => {
|
|
return selector.replace(/([^:\)]*)(:*)(.*)/, (_, before, colon, after) => {
|
|
return before + replaceBy + colon + after;
|
|
});
|
|
});
|
|
}
|
|
return result.replace(_polyfillHostRe, replaceBy);
|
|
}
|
|
return scopeSelector + ' ' + selector;
|
|
}
|
|
_applySelectorScope({
|
|
selector,
|
|
scopeSelector,
|
|
hostSelector,
|
|
isParentSelector
|
|
}) {
|
|
const isRe = /\[is=([^\]]*)\]/g;
|
|
scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
|
|
const attrName = `[${scopeSelector}]`;
|
|
const _scopeSelectorPart = p => {
|
|
let scopedP = p.trim();
|
|
if (!scopedP) {
|
|
return p;
|
|
}
|
|
if (p.includes(_polyfillHostNoCombinator)) {
|
|
scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
|
|
if (!p.match(_polyfillHostNoCombinatorOutsidePseudoFunction)) {
|
|
const [_, before, colon, after] = scopedP.match(/([^:]*)(:*)([\s\S]*)/);
|
|
scopedP = before + attrName + colon + after;
|
|
}
|
|
} else {
|
|
const t = p.replace(_polyfillHostRe, '');
|
|
if (t.length > 0) {
|
|
const matches = t.match(/([^:]*)(:*)([\s\S]*)/);
|
|
if (matches) {
|
|
scopedP = matches[1] + attrName + matches[2] + matches[3];
|
|
}
|
|
}
|
|
}
|
|
return scopedP;
|
|
};
|
|
const _pseudoFunctionAwareScopeSelectorPart = selectorPart => {
|
|
let scopedPart = '';
|
|
const pseudoSelectorParts = [];
|
|
let pseudoSelectorMatch;
|
|
while ((pseudoSelectorMatch = _cssPrefixWithPseudoSelectorFunction.exec(selectorPart)) !== null) {
|
|
let openedBrackets = 1;
|
|
let index = _cssPrefixWithPseudoSelectorFunction.lastIndex;
|
|
while (index < selectorPart.length) {
|
|
const currentSymbol = selectorPart[index];
|
|
index++;
|
|
if (currentSymbol === '(') {
|
|
openedBrackets++;
|
|
continue;
|
|
}
|
|
if (currentSymbol === ')') {
|
|
openedBrackets--;
|
|
if (openedBrackets === 0) {
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
pseudoSelectorParts.push(`${pseudoSelectorMatch[0]}${selectorPart.slice(_cssPrefixWithPseudoSelectorFunction.lastIndex, index)}`);
|
|
_cssPrefixWithPseudoSelectorFunction.lastIndex = index;
|
|
}
|
|
if (pseudoSelectorParts.join('') === selectorPart) {
|
|
scopedPart = pseudoSelectorParts.map(selectorPart => {
|
|
const [cssPseudoSelectorFunction] = selectorPart.match(_cssPrefixWithPseudoSelectorFunction) ?? [];
|
|
const selectorToScope = selectorPart.slice(cssPseudoSelectorFunction?.length, -1);
|
|
if (selectorToScope.includes(_polyfillHostNoCombinator)) {
|
|
this._shouldScopeIndicator = true;
|
|
}
|
|
const scopedInnerPart = this._scopeSelector({
|
|
selector: selectorToScope,
|
|
scopeSelector,
|
|
hostSelector
|
|
});
|
|
return `${cssPseudoSelectorFunction}${scopedInnerPart})`;
|
|
}).join('');
|
|
} else {
|
|
this._shouldScopeIndicator = this._shouldScopeIndicator || selectorPart.includes(_polyfillHostNoCombinator);
|
|
scopedPart = this._shouldScopeIndicator ? _scopeSelectorPart(selectorPart) : selectorPart;
|
|
}
|
|
return scopedPart;
|
|
};
|
|
if (isParentSelector) {
|
|
this._safeSelector = new SafeSelector(selector);
|
|
selector = this._safeSelector.content();
|
|
}
|
|
let scopedSelector = '';
|
|
let startIndex = 0;
|
|
let res;
|
|
const sep = /( |>|\+|~(?!=))(?!([^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\)))\s*/g;
|
|
const hasHost = selector.includes(_polyfillHostNoCombinator);
|
|
if (isParentSelector || this._shouldScopeIndicator) {
|
|
this._shouldScopeIndicator = !hasHost;
|
|
}
|
|
while ((res = sep.exec(selector)) !== null) {
|
|
const separator = res[1];
|
|
const part = selector.slice(startIndex, res.index);
|
|
if (part.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
|
|
continue;
|
|
}
|
|
const scopedPart = _pseudoFunctionAwareScopeSelectorPart(part);
|
|
scopedSelector += `${scopedPart} ${separator} `;
|
|
startIndex = sep.lastIndex;
|
|
}
|
|
const part = selector.substring(startIndex);
|
|
scopedSelector += _pseudoFunctionAwareScopeSelectorPart(part);
|
|
return this._safeSelector.restore(scopedSelector);
|
|
}
|
|
_insertPolyfillHostInCssText(selector) {
|
|
return selector.replace(_colonHostContextRe, _polyfillHostContext).replace(_colonHostRe, _polyfillHost);
|
|
}
|
|
}
|
|
class SafeSelector {
|
|
placeholders = [];
|
|
index = 0;
|
|
_content;
|
|
constructor(selector) {
|
|
selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
|
|
selector = selector.replace(/(\\.)/g, (_, keep) => {
|
|
const replaceBy = `__esc-ph-${this.index}__`;
|
|
this.placeholders.push(keep);
|
|
this.index++;
|
|
return replaceBy;
|
|
});
|
|
this._content = selector.replace(nthRegex, (_, pseudo, exp) => {
|
|
const replaceBy = `__ph-${this.index}__`;
|
|
this.placeholders.push(`(${exp})`);
|
|
this.index++;
|
|
return pseudo + replaceBy;
|
|
});
|
|
}
|
|
restore(content) {
|
|
return content.replace(/__(?:ph|esc-ph)-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
|
|
}
|
|
content() {
|
|
return this._content;
|
|
}
|
|
_escapeRegexMatches(content, pattern) {
|
|
return content.replace(pattern, (_, keep) => {
|
|
const replaceBy = `__ph-${this.index}__`;
|
|
this.placeholders.push(keep);
|
|
this.index++;
|
|
return replaceBy;
|
|
});
|
|
}
|
|
}
|
|
const _cssScopedPseudoFunctionPrefix = '(:(where|is)\\()?';
|
|
const _cssPrefixWithPseudoSelectorFunction = /:(where|is)\(/gi;
|
|
const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
|
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
|
const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
|
const _polyfillHost = '-shadowcsshost';
|
|
const _polyfillHostContext = '-shadowcsscontext';
|
|
const _noParens = '[^)(]*';
|
|
const _level1Parens = String.raw`(?:\(${_noParens}\)|${_noParens})+?`;
|
|
const _level2Parens = String.raw`(?:\(${_level1Parens}\)|${_noParens})+?`;
|
|
const _parenSuffix = String.raw`(?:\((${_level2Parens})\))`;
|
|
const nthRegex = new RegExp(String.raw`(:nth-[-\w]+)` + _parenSuffix, 'g');
|
|
const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix + '?([^,{]*)', 'gim');
|
|
const _hostContextPattern = _polyfillHostContext + _parenSuffix + '?([^{]*)';
|
|
const _cssColonHostContextReGlobal = new RegExp(`${_cssScopedPseudoFunctionPrefix}(${_hostContextPattern})`, 'gim');
|
|
const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
|
|
const _polyfillHostNoCombinatorOutsidePseudoFunction = new RegExp(`${_polyfillHostNoCombinator}(?![^(]*\\))`, 'g');
|
|
const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s,]*)/;
|
|
const _shadowDOMSelectorsRe = [/::shadow/g, /::content/g, /\/shadow-deep\//g, /\/shadow\//g];
|
|
const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
|
|
const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$';
|
|
const _polyfillHostRe = /-shadowcsshost/gim;
|
|
const _colonHostRe = /:host/gim;
|
|
const _colonHostContextRe = /:host-context/gim;
|
|
const _newLinesRe = /\r?\n/g;
|
|
const _commentRe = /\/\*[\s\S]*?\*\//g;
|
|
const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=/g;
|
|
const COMMENT_PLACEHOLDER = '%COMMENT%';
|
|
const _commentWithHashPlaceHolderRe = new RegExp(COMMENT_PLACEHOLDER, 'g');
|
|
const BLOCK_PLACEHOLDER = '%BLOCK%';
|
|
const _ruleRe = new RegExp(`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`, 'g');
|
|
const CONTENT_PAIRS = new Map([['{', '}']]);
|
|
const COMMA_IN_PLACEHOLDER = '%COMMA_IN_PLACEHOLDER%';
|
|
const SEMI_IN_PLACEHOLDER = '%SEMI_IN_PLACEHOLDER%';
|
|
const COLON_IN_PLACEHOLDER = '%COLON_IN_PLACEHOLDER%';
|
|
const _cssCommaInPlaceholderReGlobal = new RegExp(COMMA_IN_PLACEHOLDER, 'g');
|
|
const _cssSemiInPlaceholderReGlobal = new RegExp(SEMI_IN_PLACEHOLDER, 'g');
|
|
const _cssColonInPlaceholderReGlobal = new RegExp(COLON_IN_PLACEHOLDER, 'g');
|
|
class CssRule {
|
|
selector;
|
|
content;
|
|
constructor(selector, content) {
|
|
this.selector = selector;
|
|
this.content = content;
|
|
}
|
|
}
|
|
function processRules(input, ruleCallback) {
|
|
const escaped = escapeInStrings(input);
|
|
const inputWithEscapedBlocks = escapeBlocks(escaped, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
|
|
let nextBlockIndex = 0;
|
|
const escapedResult = inputWithEscapedBlocks.escapedString.replace(_ruleRe, (...m) => {
|
|
const selector = m[2];
|
|
let content = '';
|
|
let suffix = m[4];
|
|
let contentPrefix = '';
|
|
if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
|
|
content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
|
|
suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
|
|
contentPrefix = '{';
|
|
}
|
|
const rule = ruleCallback(new CssRule(selector, content));
|
|
return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
|
|
});
|
|
return unescapeInStrings(escapedResult);
|
|
}
|
|
class StringWithEscapedBlocks {
|
|
escapedString;
|
|
blocks;
|
|
constructor(escapedString, blocks) {
|
|
this.escapedString = escapedString;
|
|
this.blocks = blocks;
|
|
}
|
|
}
|
|
function escapeBlocks(input, charPairs, placeholder) {
|
|
const resultParts = [];
|
|
const escapedBlocks = [];
|
|
let openCharCount = 0;
|
|
let nonBlockStartIndex = 0;
|
|
let blockStartIndex = -1;
|
|
let openChar;
|
|
let closeChar;
|
|
for (let i = 0; i < input.length; i++) {
|
|
const char = input[i];
|
|
if (char === '\\') {
|
|
i++;
|
|
} else if (char === closeChar) {
|
|
openCharCount--;
|
|
if (openCharCount === 0) {
|
|
escapedBlocks.push(input.substring(blockStartIndex, i));
|
|
resultParts.push(placeholder);
|
|
nonBlockStartIndex = i;
|
|
blockStartIndex = -1;
|
|
openChar = closeChar = undefined;
|
|
}
|
|
} else if (char === openChar) {
|
|
openCharCount++;
|
|
} else if (openCharCount === 0 && charPairs.has(char)) {
|
|
openChar = char;
|
|
closeChar = charPairs.get(char);
|
|
openCharCount = 1;
|
|
blockStartIndex = i + 1;
|
|
resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
|
|
}
|
|
}
|
|
if (blockStartIndex !== -1) {
|
|
escapedBlocks.push(input.substring(blockStartIndex));
|
|
resultParts.push(placeholder);
|
|
} else {
|
|
resultParts.push(input.substring(nonBlockStartIndex));
|
|
}
|
|
return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
|
|
}
|
|
const ESCAPE_IN_STRING_MAP = {
|
|
';': SEMI_IN_PLACEHOLDER,
|
|
',': COMMA_IN_PLACEHOLDER,
|
|
':': COLON_IN_PLACEHOLDER
|
|
};
|
|
function escapeInStrings(input) {
|
|
let result = input;
|
|
let currentQuoteChar = null;
|
|
for (let i = 0; i < result.length; i++) {
|
|
const char = result[i];
|
|
if (char === '\\') {
|
|
i++;
|
|
} else {
|
|
if (currentQuoteChar !== null) {
|
|
if (char === currentQuoteChar) {
|
|
currentQuoteChar = null;
|
|
} else {
|
|
const placeholder = ESCAPE_IN_STRING_MAP[char];
|
|
if (placeholder) {
|
|
result = `${result.substr(0, i)}${placeholder}${result.substr(i + 1)}`;
|
|
i += placeholder.length - 1;
|
|
}
|
|
}
|
|
} else if (char === "'" || char === '"') {
|
|
currentQuoteChar = char;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function unescapeInStrings(input) {
|
|
let result = input.replace(_cssCommaInPlaceholderReGlobal, ',');
|
|
result = result.replace(_cssSemiInPlaceholderReGlobal, ';');
|
|
result = result.replace(_cssColonInPlaceholderReGlobal, ':');
|
|
return result;
|
|
}
|
|
function unescapeQuotes(str, isQuoted) {
|
|
return !isQuoted ? str : str.replace(/((?:^|[^\\])(?:\\\\)*)\\(?=['"])/g, '$1');
|
|
}
|
|
function _combineHostContextSelectors(contextSelectors, otherSelectors, pseudoPrefix = '') {
|
|
const hostMarker = _polyfillHostNoCombinator;
|
|
_polyfillHostRe.lastIndex = 0;
|
|
const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
|
|
if (contextSelectors.length === 0) {
|
|
return hostMarker + otherSelectors;
|
|
}
|
|
const combined = [contextSelectors.pop() || ''];
|
|
while (contextSelectors.length > 0) {
|
|
const length = combined.length;
|
|
const contextSelector = contextSelectors.pop();
|
|
for (let i = 0; i < length; i++) {
|
|
const previousSelectors = combined[i];
|
|
combined[length * 2 + i] = previousSelectors + ' ' + contextSelector;
|
|
combined[length + i] = contextSelector + ' ' + previousSelectors;
|
|
combined[i] = contextSelector + previousSelectors;
|
|
}
|
|
}
|
|
return combined.map(s => otherSelectorsHasHost ? `${pseudoPrefix}${s}${otherSelectors}` : `${pseudoPrefix}${s}${hostMarker}${otherSelectors}, ${pseudoPrefix}${s} ${hostMarker}${otherSelectors}`).join(',');
|
|
}
|
|
function repeatGroups(groups, multiples) {
|
|
const length = groups.length;
|
|
for (let i = 1; i < multiples; i++) {
|
|
for (let j = 0; j < length; j++) {
|
|
groups[j + i * length] = groups[j].slice(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
var OpKind;
|
|
(function (OpKind) {
|
|
OpKind[OpKind["ListEnd"] = 0] = "ListEnd";
|
|
OpKind[OpKind["Statement"] = 1] = "Statement";
|
|
OpKind[OpKind["Variable"] = 2] = "Variable";
|
|
OpKind[OpKind["ElementStart"] = 3] = "ElementStart";
|
|
OpKind[OpKind["Element"] = 4] = "Element";
|
|
OpKind[OpKind["Template"] = 5] = "Template";
|
|
OpKind[OpKind["ElementEnd"] = 6] = "ElementEnd";
|
|
OpKind[OpKind["ContainerStart"] = 7] = "ContainerStart";
|
|
OpKind[OpKind["Container"] = 8] = "Container";
|
|
OpKind[OpKind["ContainerEnd"] = 9] = "ContainerEnd";
|
|
OpKind[OpKind["DisableBindings"] = 10] = "DisableBindings";
|
|
OpKind[OpKind["ConditionalCreate"] = 11] = "ConditionalCreate";
|
|
OpKind[OpKind["ConditionalBranchCreate"] = 12] = "ConditionalBranchCreate";
|
|
OpKind[OpKind["Conditional"] = 13] = "Conditional";
|
|
OpKind[OpKind["EnableBindings"] = 14] = "EnableBindings";
|
|
OpKind[OpKind["Text"] = 15] = "Text";
|
|
OpKind[OpKind["Listener"] = 16] = "Listener";
|
|
OpKind[OpKind["InterpolateText"] = 17] = "InterpolateText";
|
|
OpKind[OpKind["Binding"] = 18] = "Binding";
|
|
OpKind[OpKind["Property"] = 19] = "Property";
|
|
OpKind[OpKind["StyleProp"] = 20] = "StyleProp";
|
|
OpKind[OpKind["ClassProp"] = 21] = "ClassProp";
|
|
OpKind[OpKind["StyleMap"] = 22] = "StyleMap";
|
|
OpKind[OpKind["ClassMap"] = 23] = "ClassMap";
|
|
OpKind[OpKind["Advance"] = 24] = "Advance";
|
|
OpKind[OpKind["Pipe"] = 25] = "Pipe";
|
|
OpKind[OpKind["Attribute"] = 26] = "Attribute";
|
|
OpKind[OpKind["ExtractedAttribute"] = 27] = "ExtractedAttribute";
|
|
OpKind[OpKind["Defer"] = 28] = "Defer";
|
|
OpKind[OpKind["DeferOn"] = 29] = "DeferOn";
|
|
OpKind[OpKind["DeferWhen"] = 30] = "DeferWhen";
|
|
OpKind[OpKind["I18nMessage"] = 31] = "I18nMessage";
|
|
OpKind[OpKind["DomProperty"] = 32] = "DomProperty";
|
|
OpKind[OpKind["Namespace"] = 33] = "Namespace";
|
|
OpKind[OpKind["ProjectionDef"] = 34] = "ProjectionDef";
|
|
OpKind[OpKind["Projection"] = 35] = "Projection";
|
|
OpKind[OpKind["RepeaterCreate"] = 36] = "RepeaterCreate";
|
|
OpKind[OpKind["Repeater"] = 37] = "Repeater";
|
|
OpKind[OpKind["TwoWayProperty"] = 38] = "TwoWayProperty";
|
|
OpKind[OpKind["TwoWayListener"] = 39] = "TwoWayListener";
|
|
OpKind[OpKind["DeclareLet"] = 40] = "DeclareLet";
|
|
OpKind[OpKind["StoreLet"] = 41] = "StoreLet";
|
|
OpKind[OpKind["I18nStart"] = 42] = "I18nStart";
|
|
OpKind[OpKind["I18n"] = 43] = "I18n";
|
|
OpKind[OpKind["I18nEnd"] = 44] = "I18nEnd";
|
|
OpKind[OpKind["I18nExpression"] = 45] = "I18nExpression";
|
|
OpKind[OpKind["I18nApply"] = 46] = "I18nApply";
|
|
OpKind[OpKind["IcuStart"] = 47] = "IcuStart";
|
|
OpKind[OpKind["IcuEnd"] = 48] = "IcuEnd";
|
|
OpKind[OpKind["IcuPlaceholder"] = 49] = "IcuPlaceholder";
|
|
OpKind[OpKind["I18nContext"] = 50] = "I18nContext";
|
|
OpKind[OpKind["I18nAttributes"] = 51] = "I18nAttributes";
|
|
OpKind[OpKind["SourceLocation"] = 52] = "SourceLocation";
|
|
OpKind[OpKind["Animation"] = 53] = "Animation";
|
|
OpKind[OpKind["AnimationString"] = 54] = "AnimationString";
|
|
OpKind[OpKind["AnimationBinding"] = 55] = "AnimationBinding";
|
|
OpKind[OpKind["AnimationListener"] = 56] = "AnimationListener";
|
|
OpKind[OpKind["Control"] = 57] = "Control";
|
|
OpKind[OpKind["ControlCreate"] = 58] = "ControlCreate";
|
|
})(OpKind || (OpKind = {}));
|
|
var ExpressionKind;
|
|
(function (ExpressionKind) {
|
|
ExpressionKind[ExpressionKind["LexicalRead"] = 0] = "LexicalRead";
|
|
ExpressionKind[ExpressionKind["Context"] = 1] = "Context";
|
|
ExpressionKind[ExpressionKind["TrackContext"] = 2] = "TrackContext";
|
|
ExpressionKind[ExpressionKind["ReadVariable"] = 3] = "ReadVariable";
|
|
ExpressionKind[ExpressionKind["NextContext"] = 4] = "NextContext";
|
|
ExpressionKind[ExpressionKind["Reference"] = 5] = "Reference";
|
|
ExpressionKind[ExpressionKind["StoreLet"] = 6] = "StoreLet";
|
|
ExpressionKind[ExpressionKind["ContextLetReference"] = 7] = "ContextLetReference";
|
|
ExpressionKind[ExpressionKind["GetCurrentView"] = 8] = "GetCurrentView";
|
|
ExpressionKind[ExpressionKind["RestoreView"] = 9] = "RestoreView";
|
|
ExpressionKind[ExpressionKind["ResetView"] = 10] = "ResetView";
|
|
ExpressionKind[ExpressionKind["PureFunctionExpr"] = 11] = "PureFunctionExpr";
|
|
ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 12] = "PureFunctionParameterExpr";
|
|
ExpressionKind[ExpressionKind["PipeBinding"] = 13] = "PipeBinding";
|
|
ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 14] = "PipeBindingVariadic";
|
|
ExpressionKind[ExpressionKind["SafePropertyRead"] = 15] = "SafePropertyRead";
|
|
ExpressionKind[ExpressionKind["SafeKeyedRead"] = 16] = "SafeKeyedRead";
|
|
ExpressionKind[ExpressionKind["SafeInvokeFunction"] = 17] = "SafeInvokeFunction";
|
|
ExpressionKind[ExpressionKind["SafeTernaryExpr"] = 18] = "SafeTernaryExpr";
|
|
ExpressionKind[ExpressionKind["EmptyExpr"] = 19] = "EmptyExpr";
|
|
ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 20] = "AssignTemporaryExpr";
|
|
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 21] = "ReadTemporaryExpr";
|
|
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
|
|
ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
|
|
ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
|
|
ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 25] = "TwoWayBindingSet";
|
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
var VariableFlags;
|
|
(function (VariableFlags) {
|
|
VariableFlags[VariableFlags["None"] = 0] = "None";
|
|
VariableFlags[VariableFlags["AlwaysInline"] = 1] = "AlwaysInline";
|
|
})(VariableFlags || (VariableFlags = {}));
|
|
var SemanticVariableKind;
|
|
(function (SemanticVariableKind) {
|
|
SemanticVariableKind[SemanticVariableKind["Context"] = 0] = "Context";
|
|
SemanticVariableKind[SemanticVariableKind["Identifier"] = 1] = "Identifier";
|
|
SemanticVariableKind[SemanticVariableKind["SavedView"] = 2] = "SavedView";
|
|
SemanticVariableKind[SemanticVariableKind["Alias"] = 3] = "Alias";
|
|
})(SemanticVariableKind || (SemanticVariableKind = {}));
|
|
var CompatibilityMode;
|
|
(function (CompatibilityMode) {
|
|
CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
|
|
CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
|
|
})(CompatibilityMode || (CompatibilityMode = {}));
|
|
var BindingKind;
|
|
(function (BindingKind) {
|
|
BindingKind[BindingKind["Attribute"] = 0] = "Attribute";
|
|
BindingKind[BindingKind["ClassName"] = 1] = "ClassName";
|
|
BindingKind[BindingKind["StyleProperty"] = 2] = "StyleProperty";
|
|
BindingKind[BindingKind["Property"] = 3] = "Property";
|
|
BindingKind[BindingKind["Template"] = 4] = "Template";
|
|
BindingKind[BindingKind["I18n"] = 5] = "I18n";
|
|
BindingKind[BindingKind["LegacyAnimation"] = 6] = "LegacyAnimation";
|
|
BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty";
|
|
BindingKind[BindingKind["Animation"] = 8] = "Animation";
|
|
})(BindingKind || (BindingKind = {}));
|
|
var I18nParamResolutionTime;
|
|
(function (I18nParamResolutionTime) {
|
|
I18nParamResolutionTime[I18nParamResolutionTime["Creation"] = 0] = "Creation";
|
|
I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
|
|
})(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
|
|
var I18nExpressionFor;
|
|
(function (I18nExpressionFor) {
|
|
I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
|
|
I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
|
|
})(I18nExpressionFor || (I18nExpressionFor = {}));
|
|
var I18nParamValueFlags;
|
|
(function (I18nParamValueFlags) {
|
|
I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
|
|
I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
|
|
I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag";
|
|
I18nParamValueFlags[I18nParamValueFlags["OpenTag"] = 4] = "OpenTag";
|
|
I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
|
|
I18nParamValueFlags[I18nParamValueFlags["ExpressionIndex"] = 16] = "ExpressionIndex";
|
|
})(I18nParamValueFlags || (I18nParamValueFlags = {}));
|
|
var Namespace;
|
|
(function (Namespace) {
|
|
Namespace[Namespace["HTML"] = 0] = "HTML";
|
|
Namespace[Namespace["SVG"] = 1] = "SVG";
|
|
Namespace[Namespace["Math"] = 2] = "Math";
|
|
})(Namespace || (Namespace = {}));
|
|
var DeferTriggerKind;
|
|
(function (DeferTriggerKind) {
|
|
DeferTriggerKind[DeferTriggerKind["Idle"] = 0] = "Idle";
|
|
DeferTriggerKind[DeferTriggerKind["Immediate"] = 1] = "Immediate";
|
|
DeferTriggerKind[DeferTriggerKind["Timer"] = 2] = "Timer";
|
|
DeferTriggerKind[DeferTriggerKind["Hover"] = 3] = "Hover";
|
|
DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction";
|
|
DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport";
|
|
DeferTriggerKind[DeferTriggerKind["Never"] = 6] = "Never";
|
|
})(DeferTriggerKind || (DeferTriggerKind = {}));
|
|
var I18nContextKind;
|
|
(function (I18nContextKind) {
|
|
I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
|
|
I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
|
|
I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
|
|
})(I18nContextKind || (I18nContextKind = {}));
|
|
var TemplateKind;
|
|
(function (TemplateKind) {
|
|
TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
|
|
TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
|
|
TemplateKind[TemplateKind["Block"] = 2] = "Block";
|
|
})(TemplateKind || (TemplateKind = {}));
|
|
|
|
const ConsumesSlot = Symbol('ConsumesSlot');
|
|
const DependsOnSlotContext = Symbol('DependsOnSlotContext');
|
|
const ConsumesVarsTrait = Symbol('ConsumesVars');
|
|
const UsesVarOffset = Symbol('UsesVarOffset');
|
|
const TRAIT_CONSUMES_SLOT = {
|
|
[ConsumesSlot]: true,
|
|
numSlotsUsed: 1
|
|
};
|
|
const TRAIT_DEPENDS_ON_SLOT_CONTEXT = {
|
|
[DependsOnSlotContext]: true
|
|
};
|
|
const TRAIT_CONSUMES_VARS = {
|
|
[ConsumesVarsTrait]: true
|
|
};
|
|
function hasConsumesSlotTrait(op) {
|
|
return op[ConsumesSlot] === true;
|
|
}
|
|
function hasDependsOnSlotContextTrait(value) {
|
|
return value[DependsOnSlotContext] === true;
|
|
}
|
|
function hasConsumesVarsTrait(value) {
|
|
return value[ConsumesVarsTrait] === true;
|
|
}
|
|
function hasUsesVarOffsetTrait(expr) {
|
|
return expr[UsesVarOffset] === true;
|
|
}
|
|
|
|
function createStatementOp(statement) {
|
|
return {
|
|
kind: OpKind.Statement,
|
|
statement,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createVariableOp(xref, variable, initializer, flags) {
|
|
return {
|
|
kind: OpKind.Variable,
|
|
xref,
|
|
variable,
|
|
initializer,
|
|
flags,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
const NEW_OP = {
|
|
debugListId: null,
|
|
prev: null,
|
|
next: null
|
|
};
|
|
|
|
function createInterpolateTextOp(xref, interpolation, sourceSpan) {
|
|
return {
|
|
kind: OpKind.InterpolateText,
|
|
target: xref,
|
|
interpolation,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
class Interpolation {
|
|
strings;
|
|
expressions;
|
|
i18nPlaceholders;
|
|
constructor(strings, expressions, i18nPlaceholders) {
|
|
this.strings = strings;
|
|
this.expressions = expressions;
|
|
this.i18nPlaceholders = i18nPlaceholders;
|
|
if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
|
|
throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
|
|
}
|
|
}
|
|
}
|
|
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Binding,
|
|
bindingKind: kind,
|
|
target,
|
|
name,
|
|
expression,
|
|
unit,
|
|
securityContext,
|
|
isTextAttribute,
|
|
isStructuralTemplateAttribute,
|
|
templateKind,
|
|
i18nContext: null,
|
|
i18nMessage,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createPropertyOp(target, name, expression, bindingKind, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Property,
|
|
target,
|
|
name,
|
|
expression,
|
|
bindingKind,
|
|
securityContext,
|
|
sanitizer: null,
|
|
isStructuralTemplateAttribute,
|
|
templateKind,
|
|
i18nContext,
|
|
i18nMessage,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
return {
|
|
kind: OpKind.TwoWayProperty,
|
|
target,
|
|
name,
|
|
expression,
|
|
securityContext,
|
|
sanitizer: null,
|
|
isStructuralTemplateAttribute,
|
|
templateKind,
|
|
i18nContext,
|
|
i18nMessage,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createStylePropOp(xref, name, expression, unit, sourceSpan) {
|
|
return {
|
|
kind: OpKind.StyleProp,
|
|
target: xref,
|
|
name,
|
|
expression,
|
|
unit,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createClassPropOp(xref, name, expression, sourceSpan) {
|
|
return {
|
|
kind: OpKind.ClassProp,
|
|
target: xref,
|
|
name,
|
|
expression,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createStyleMapOp(xref, expression, sourceSpan) {
|
|
return {
|
|
kind: OpKind.StyleMap,
|
|
target: xref,
|
|
expression,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createClassMapOp(xref, expression, sourceSpan) {
|
|
return {
|
|
kind: OpKind.ClassMap,
|
|
target: xref,
|
|
expression,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createAttributeOp(target, namespace, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Attribute,
|
|
target,
|
|
namespace,
|
|
name,
|
|
expression,
|
|
securityContext,
|
|
sanitizer: null,
|
|
isTextAttribute,
|
|
isStructuralTemplateAttribute,
|
|
templateKind,
|
|
i18nContext: null,
|
|
i18nMessage,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createAdvanceOp(delta, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Advance,
|
|
delta,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createConditionalOp(target, test, conditions, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Conditional,
|
|
target,
|
|
test,
|
|
conditions,
|
|
processed: null,
|
|
sourceSpan,
|
|
contextValue: null,
|
|
...NEW_OP,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS
|
|
};
|
|
}
|
|
function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Repeater,
|
|
target: repeaterCreate,
|
|
targetSlot,
|
|
collection,
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT
|
|
};
|
|
}
|
|
function createAnimationBindingOp(name, target, animationKind, expression, securityContext, sourceSpan, animationBindingKind) {
|
|
return {
|
|
kind: OpKind.AnimationBinding,
|
|
name,
|
|
target,
|
|
animationKind,
|
|
expression,
|
|
i18nMessage: null,
|
|
securityContext,
|
|
sanitizer: null,
|
|
sourceSpan,
|
|
animationBindingKind,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createDeferWhenOp(target, expr, modifier, sourceSpan) {
|
|
return {
|
|
kind: OpKind.DeferWhen,
|
|
target,
|
|
expr,
|
|
modifier,
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS
|
|
};
|
|
}
|
|
function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
|
|
return {
|
|
kind: OpKind.I18nExpression,
|
|
context,
|
|
target,
|
|
i18nOwner,
|
|
handle,
|
|
expression,
|
|
icuPlaceholder,
|
|
i18nPlaceholder,
|
|
resolutionTime,
|
|
usage,
|
|
name,
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT
|
|
};
|
|
}
|
|
function createI18nApplyOp(owner, handle, sourceSpan) {
|
|
return {
|
|
kind: OpKind.I18nApply,
|
|
owner,
|
|
handle,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createStoreLetOp(target, declaredName, value, sourceSpan) {
|
|
return {
|
|
kind: OpKind.StoreLet,
|
|
target,
|
|
declaredName,
|
|
value,
|
|
sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createControlOp(op) {
|
|
return {
|
|
kind: OpKind.Control,
|
|
target: op.target,
|
|
name: op.name,
|
|
expression: op.expression,
|
|
bindingKind: op.bindingKind,
|
|
securityContext: op.securityContext,
|
|
sanitizer: null,
|
|
isStructuralTemplateAttribute: op.isStructuralTemplateAttribute,
|
|
templateKind: op.templateKind,
|
|
i18nContext: op.i18nContext,
|
|
i18nMessage: op.i18nMessage,
|
|
sourceSpan: op.sourceSpan,
|
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
|
|
function isIrExpression(expr) {
|
|
return expr instanceof ExpressionBase;
|
|
}
|
|
class ExpressionBase extends Expression {
|
|
constructor(sourceSpan = null) {
|
|
super(null, sourceSpan);
|
|
}
|
|
}
|
|
class LexicalReadExpr extends ExpressionBase {
|
|
name;
|
|
kind = ExpressionKind.LexicalRead;
|
|
constructor(name) {
|
|
super();
|
|
this.name = name;
|
|
}
|
|
visitExpression(visitor, context) {}
|
|
isEquivalent(other) {
|
|
return this.name === other.name;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new LexicalReadExpr(this.name);
|
|
}
|
|
}
|
|
class ReferenceExpr extends ExpressionBase {
|
|
target;
|
|
targetSlot;
|
|
offset;
|
|
kind = ExpressionKind.Reference;
|
|
constructor(target, targetSlot, offset) {
|
|
super();
|
|
this.target = target;
|
|
this.targetSlot = targetSlot;
|
|
this.offset = offset;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof ReferenceExpr && e.target === this.target;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new ReferenceExpr(this.target, this.targetSlot, this.offset);
|
|
}
|
|
}
|
|
class StoreLetExpr extends ExpressionBase {
|
|
target;
|
|
value;
|
|
sourceSpan;
|
|
kind = ExpressionKind.StoreLet;
|
|
[ConsumesVarsTrait] = true;
|
|
[DependsOnSlotContext] = true;
|
|
constructor(target, value, sourceSpan) {
|
|
super();
|
|
this.target = target;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof StoreLetExpr && e.target === this.target && e.value.isEquivalent(this.value);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.value = transformExpressionsInExpression(this.value, transform, flags);
|
|
}
|
|
clone() {
|
|
return new StoreLetExpr(this.target, this.value, this.sourceSpan);
|
|
}
|
|
}
|
|
class ContextLetReferenceExpr extends ExpressionBase {
|
|
target;
|
|
targetSlot;
|
|
kind = ExpressionKind.ContextLetReference;
|
|
constructor(target, targetSlot) {
|
|
super();
|
|
this.target = target;
|
|
this.targetSlot = targetSlot;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof ContextLetReferenceExpr && e.target === this.target;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new ContextLetReferenceExpr(this.target, this.targetSlot);
|
|
}
|
|
}
|
|
class ContextExpr extends ExpressionBase {
|
|
view;
|
|
kind = ExpressionKind.Context;
|
|
constructor(view) {
|
|
super();
|
|
this.view = view;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof ContextExpr && e.view === this.view;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new ContextExpr(this.view);
|
|
}
|
|
}
|
|
class TrackContextExpr extends ExpressionBase {
|
|
view;
|
|
kind = ExpressionKind.TrackContext;
|
|
constructor(view) {
|
|
super();
|
|
this.view = view;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof TrackContextExpr && e.view === this.view;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new TrackContextExpr(this.view);
|
|
}
|
|
}
|
|
class NextContextExpr extends ExpressionBase {
|
|
kind = ExpressionKind.NextContext;
|
|
steps = 1;
|
|
constructor() {
|
|
super();
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof NextContextExpr && e.steps === this.steps;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
const expr = new NextContextExpr();
|
|
expr.steps = this.steps;
|
|
return expr;
|
|
}
|
|
}
|
|
class GetCurrentViewExpr extends ExpressionBase {
|
|
kind = ExpressionKind.GetCurrentView;
|
|
constructor() {
|
|
super();
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(e) {
|
|
return e instanceof GetCurrentViewExpr;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new GetCurrentViewExpr();
|
|
}
|
|
}
|
|
class RestoreViewExpr extends ExpressionBase {
|
|
view;
|
|
kind = ExpressionKind.RestoreView;
|
|
constructor(view) {
|
|
super();
|
|
this.view = view;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
if (typeof this.view !== 'number') {
|
|
this.view.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent(e) {
|
|
if (!(e instanceof RestoreViewExpr) || typeof e.view !== typeof this.view) {
|
|
return false;
|
|
}
|
|
if (typeof this.view === 'number') {
|
|
return this.view === e.view;
|
|
} else {
|
|
return this.view.isEquivalent(e.view);
|
|
}
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
if (typeof this.view !== 'number') {
|
|
this.view = transformExpressionsInExpression(this.view, transform, flags);
|
|
}
|
|
}
|
|
clone() {
|
|
return new RestoreViewExpr(this.view instanceof Expression ? this.view.clone() : this.view);
|
|
}
|
|
}
|
|
class ResetViewExpr extends ExpressionBase {
|
|
expr;
|
|
kind = ExpressionKind.ResetView;
|
|
constructor(expr) {
|
|
super();
|
|
this.expr = expr;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.expr.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ResetViewExpr && this.expr.isEquivalent(e.expr);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
}
|
|
clone() {
|
|
return new ResetViewExpr(this.expr.clone());
|
|
}
|
|
}
|
|
class TwoWayBindingSetExpr extends ExpressionBase {
|
|
target;
|
|
value;
|
|
kind = ExpressionKind.TwoWayBindingSet;
|
|
constructor(target, value) {
|
|
super();
|
|
this.target = target;
|
|
this.value = value;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.target.visitExpression(visitor, context);
|
|
this.value.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent(other) {
|
|
return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value);
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.target = transformExpressionsInExpression(this.target, transform, flags);
|
|
this.value = transformExpressionsInExpression(this.value, transform, flags);
|
|
}
|
|
clone() {
|
|
return new TwoWayBindingSetExpr(this.target, this.value);
|
|
}
|
|
}
|
|
class ReadVariableExpr extends ExpressionBase {
|
|
xref;
|
|
kind = ExpressionKind.ReadVariable;
|
|
name = null;
|
|
constructor(xref) {
|
|
super();
|
|
this.xref = xref;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(other) {
|
|
return other instanceof ReadVariableExpr && other.xref === this.xref;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
const expr = new ReadVariableExpr(this.xref);
|
|
expr.name = this.name;
|
|
return expr;
|
|
}
|
|
}
|
|
class PureFunctionExpr extends ExpressionBase {
|
|
kind = ExpressionKind.PureFunctionExpr;
|
|
[ConsumesVarsTrait] = true;
|
|
[UsesVarOffset] = true;
|
|
varOffset = null;
|
|
body;
|
|
args;
|
|
fn = null;
|
|
constructor(expression, args) {
|
|
super();
|
|
this.body = expression;
|
|
this.args = args;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.body?.visitExpression(visitor, context);
|
|
for (const arg of this.args) {
|
|
arg.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent(other) {
|
|
if (!(other instanceof PureFunctionExpr) || other.args.length !== this.args.length) {
|
|
return false;
|
|
}
|
|
return other.body !== null && this.body !== null && other.body.isEquivalent(this.body) && other.args.every((arg, idx) => arg.isEquivalent(this.args[idx]));
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
if (this.body !== null) {
|
|
this.body = transformExpressionsInExpression(this.body, transform, flags | VisitorContextFlag.InChildOperation);
|
|
} else if (this.fn !== null) {
|
|
this.fn = transformExpressionsInExpression(this.fn, transform, flags);
|
|
}
|
|
for (let i = 0; i < this.args.length; i++) {
|
|
this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags);
|
|
}
|
|
}
|
|
clone() {
|
|
const expr = new PureFunctionExpr(this.body?.clone() ?? null, this.args.map(arg => arg.clone()));
|
|
expr.fn = this.fn?.clone() ?? null;
|
|
expr.varOffset = this.varOffset;
|
|
return expr;
|
|
}
|
|
}
|
|
class PureFunctionParameterExpr extends ExpressionBase {
|
|
index;
|
|
kind = ExpressionKind.PureFunctionParameterExpr;
|
|
constructor(index) {
|
|
super();
|
|
this.index = index;
|
|
}
|
|
visitExpression() {}
|
|
isEquivalent(other) {
|
|
return other instanceof PureFunctionParameterExpr && other.index === this.index;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
transformInternalExpressions() {}
|
|
clone() {
|
|
return new PureFunctionParameterExpr(this.index);
|
|
}
|
|
}
|
|
class PipeBindingExpr extends ExpressionBase {
|
|
target;
|
|
targetSlot;
|
|
name;
|
|
args;
|
|
kind = ExpressionKind.PipeBinding;
|
|
[ConsumesVarsTrait] = true;
|
|
[UsesVarOffset] = true;
|
|
varOffset = null;
|
|
constructor(target, targetSlot, name, args) {
|
|
super();
|
|
this.target = target;
|
|
this.targetSlot = targetSlot;
|
|
this.name = name;
|
|
this.args = args;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
for (const arg of this.args) {
|
|
arg.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
for (let idx = 0; idx < this.args.length; idx++) {
|
|
this.args[idx] = transformExpressionsInExpression(this.args[idx], transform, flags);
|
|
}
|
|
}
|
|
clone() {
|
|
const r = new PipeBindingExpr(this.target, this.targetSlot, this.name, this.args.map(a => a.clone()));
|
|
r.varOffset = this.varOffset;
|
|
return r;
|
|
}
|
|
}
|
|
class PipeBindingVariadicExpr extends ExpressionBase {
|
|
target;
|
|
targetSlot;
|
|
name;
|
|
args;
|
|
numArgs;
|
|
kind = ExpressionKind.PipeBindingVariadic;
|
|
[ConsumesVarsTrait] = true;
|
|
[UsesVarOffset] = true;
|
|
varOffset = null;
|
|
constructor(target, targetSlot, name, args, numArgs) {
|
|
super();
|
|
this.target = target;
|
|
this.targetSlot = targetSlot;
|
|
this.name = name;
|
|
this.args = args;
|
|
this.numArgs = numArgs;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.args.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.args = transformExpressionsInExpression(this.args, transform, flags);
|
|
}
|
|
clone() {
|
|
const r = new PipeBindingVariadicExpr(this.target, this.targetSlot, this.name, this.args.clone(), this.numArgs);
|
|
r.varOffset = this.varOffset;
|
|
return r;
|
|
}
|
|
}
|
|
class SafePropertyReadExpr extends ExpressionBase {
|
|
receiver;
|
|
name;
|
|
kind = ExpressionKind.SafePropertyRead;
|
|
constructor(receiver, name) {
|
|
super();
|
|
this.receiver = receiver;
|
|
this.name = name;
|
|
}
|
|
get index() {
|
|
return this.name;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.receiver.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
}
|
|
clone() {
|
|
return new SafePropertyReadExpr(this.receiver.clone(), this.name);
|
|
}
|
|
}
|
|
class SafeKeyedReadExpr extends ExpressionBase {
|
|
receiver;
|
|
index;
|
|
kind = ExpressionKind.SafeKeyedRead;
|
|
constructor(receiver, index, sourceSpan) {
|
|
super(sourceSpan);
|
|
this.receiver = receiver;
|
|
this.index = index;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.receiver.visitExpression(visitor, context);
|
|
this.index.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
this.index = transformExpressionsInExpression(this.index, transform, flags);
|
|
}
|
|
clone() {
|
|
return new SafeKeyedReadExpr(this.receiver.clone(), this.index.clone(), this.sourceSpan);
|
|
}
|
|
}
|
|
class SafeInvokeFunctionExpr extends ExpressionBase {
|
|
receiver;
|
|
args;
|
|
kind = ExpressionKind.SafeInvokeFunction;
|
|
constructor(receiver, args) {
|
|
super();
|
|
this.receiver = receiver;
|
|
this.args = args;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.receiver.visitExpression(visitor, context);
|
|
for (const a of this.args) {
|
|
a.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
for (let i = 0; i < this.args.length; i++) {
|
|
this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags);
|
|
}
|
|
}
|
|
clone() {
|
|
return new SafeInvokeFunctionExpr(this.receiver.clone(), this.args.map(a => a.clone()));
|
|
}
|
|
}
|
|
class SafeTernaryExpr extends ExpressionBase {
|
|
guard;
|
|
expr;
|
|
kind = ExpressionKind.SafeTernaryExpr;
|
|
constructor(guard, expr) {
|
|
super();
|
|
this.guard = guard;
|
|
this.expr = expr;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.guard.visitExpression(visitor, context);
|
|
this.expr.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.guard = transformExpressionsInExpression(this.guard, transform, flags);
|
|
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
}
|
|
clone() {
|
|
return new SafeTernaryExpr(this.guard.clone(), this.expr.clone());
|
|
}
|
|
}
|
|
class EmptyExpr extends ExpressionBase {
|
|
kind = ExpressionKind.EmptyExpr;
|
|
visitExpression(visitor, context) {}
|
|
isEquivalent(e) {
|
|
return e instanceof EmptyExpr;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
clone() {
|
|
return new EmptyExpr();
|
|
}
|
|
transformInternalExpressions() {}
|
|
}
|
|
class AssignTemporaryExpr extends ExpressionBase {
|
|
expr;
|
|
xref;
|
|
kind = ExpressionKind.AssignTemporaryExpr;
|
|
name = null;
|
|
constructor(expr, xref) {
|
|
super();
|
|
this.expr = expr;
|
|
this.xref = xref;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.expr.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent() {
|
|
return false;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
}
|
|
clone() {
|
|
const a = new AssignTemporaryExpr(this.expr.clone(), this.xref);
|
|
a.name = this.name;
|
|
return a;
|
|
}
|
|
}
|
|
class ReadTemporaryExpr extends ExpressionBase {
|
|
xref;
|
|
kind = ExpressionKind.ReadTemporaryExpr;
|
|
name = null;
|
|
constructor(xref) {
|
|
super();
|
|
this.xref = xref;
|
|
}
|
|
visitExpression(visitor, context) {}
|
|
isEquivalent() {
|
|
return this.xref === this.xref;
|
|
}
|
|
isConstant() {
|
|
return false;
|
|
}
|
|
transformInternalExpressions(transform, flags) {}
|
|
clone() {
|
|
const r = new ReadTemporaryExpr(this.xref);
|
|
r.name = this.name;
|
|
return r;
|
|
}
|
|
}
|
|
class SlotLiteralExpr extends ExpressionBase {
|
|
slot;
|
|
kind = ExpressionKind.SlotLiteralExpr;
|
|
constructor(slot) {
|
|
super();
|
|
this.slot = slot;
|
|
}
|
|
visitExpression(visitor, context) {}
|
|
isEquivalent(e) {
|
|
return e instanceof SlotLiteralExpr && e.slot === this.slot;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
clone() {
|
|
return new SlotLiteralExpr(this.slot);
|
|
}
|
|
transformInternalExpressions() {}
|
|
}
|
|
class ConditionalCaseExpr extends ExpressionBase {
|
|
expr;
|
|
target;
|
|
targetSlot;
|
|
alias;
|
|
kind = ExpressionKind.ConditionalCase;
|
|
constructor(expr, target, targetSlot, alias = null) {
|
|
super();
|
|
this.expr = expr;
|
|
this.target = target;
|
|
this.targetSlot = targetSlot;
|
|
this.alias = alias;
|
|
}
|
|
visitExpression(visitor, context) {
|
|
if (this.expr !== null) {
|
|
this.expr.visitExpression(visitor, context);
|
|
}
|
|
}
|
|
isEquivalent(e) {
|
|
return e instanceof ConditionalCaseExpr && e.expr === this.expr;
|
|
}
|
|
isConstant() {
|
|
return true;
|
|
}
|
|
clone() {
|
|
return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot);
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
if (this.expr !== null) {
|
|
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
}
|
|
}
|
|
}
|
|
class ConstCollectedExpr extends ExpressionBase {
|
|
expr;
|
|
kind = ExpressionKind.ConstCollected;
|
|
constructor(expr) {
|
|
super();
|
|
this.expr = expr;
|
|
}
|
|
transformInternalExpressions(transform, flags) {
|
|
this.expr = transform(this.expr, flags);
|
|
}
|
|
visitExpression(visitor, context) {
|
|
this.expr.visitExpression(visitor, context);
|
|
}
|
|
isEquivalent(e) {
|
|
if (!(e instanceof ConstCollectedExpr)) {
|
|
return false;
|
|
}
|
|
return this.expr.isEquivalent(e.expr);
|
|
}
|
|
isConstant() {
|
|
return this.expr.isConstant();
|
|
}
|
|
clone() {
|
|
return new ConstCollectedExpr(this.expr);
|
|
}
|
|
}
|
|
function visitExpressionsInOp(op, visitor) {
|
|
transformExpressionsInOp(op, (expr, flags) => {
|
|
visitor(expr, flags);
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
var VisitorContextFlag;
|
|
(function (VisitorContextFlag) {
|
|
VisitorContextFlag[VisitorContextFlag["None"] = 0] = "None";
|
|
VisitorContextFlag[VisitorContextFlag["InChildOperation"] = 1] = "InChildOperation";
|
|
})(VisitorContextFlag || (VisitorContextFlag = {}));
|
|
function transformExpressionsInInterpolation(interpolation, transform, flags) {
|
|
for (let i = 0; i < interpolation.expressions.length; i++) {
|
|
interpolation.expressions[i] = transformExpressionsInExpression(interpolation.expressions[i], transform, flags);
|
|
}
|
|
}
|
|
function transformExpressionsInOp(op, transform, flags) {
|
|
switch (op.kind) {
|
|
case OpKind.StyleProp:
|
|
case OpKind.StyleMap:
|
|
case OpKind.ClassProp:
|
|
case OpKind.ClassMap:
|
|
case OpKind.AnimationString:
|
|
case OpKind.AnimationBinding:
|
|
case OpKind.Binding:
|
|
if (op.expression instanceof Interpolation) {
|
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
} else {
|
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
}
|
|
break;
|
|
case OpKind.Property:
|
|
case OpKind.DomProperty:
|
|
case OpKind.Attribute:
|
|
case OpKind.Control:
|
|
if (op.expression instanceof Interpolation) {
|
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
} else {
|
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
}
|
|
op.sanitizer = op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
break;
|
|
case OpKind.TwoWayProperty:
|
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
op.sanitizer = op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
break;
|
|
case OpKind.I18nExpression:
|
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
break;
|
|
case OpKind.InterpolateText:
|
|
transformExpressionsInInterpolation(op.interpolation, transform, flags);
|
|
break;
|
|
case OpKind.Statement:
|
|
transformExpressionsInStatement(op.statement, transform, flags);
|
|
break;
|
|
case OpKind.Variable:
|
|
op.initializer = transformExpressionsInExpression(op.initializer, transform, flags);
|
|
break;
|
|
case OpKind.Conditional:
|
|
for (const condition of op.conditions) {
|
|
if (condition.expr === null) {
|
|
continue;
|
|
}
|
|
condition.expr = transformExpressionsInExpression(condition.expr, transform, flags);
|
|
}
|
|
if (op.processed !== null) {
|
|
op.processed = transformExpressionsInExpression(op.processed, transform, flags);
|
|
}
|
|
if (op.contextValue !== null) {
|
|
op.contextValue = transformExpressionsInExpression(op.contextValue, transform, flags);
|
|
}
|
|
break;
|
|
case OpKind.Animation:
|
|
case OpKind.AnimationListener:
|
|
case OpKind.Listener:
|
|
case OpKind.TwoWayListener:
|
|
for (const innerOp of op.handlerOps) {
|
|
transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
|
|
}
|
|
break;
|
|
case OpKind.ExtractedAttribute:
|
|
op.expression = op.expression && transformExpressionsInExpression(op.expression, transform, flags);
|
|
op.trustedValueFn = op.trustedValueFn && transformExpressionsInExpression(op.trustedValueFn, transform, flags);
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (op.trackByOps === null) {
|
|
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
} else {
|
|
for (const innerOp of op.trackByOps) {
|
|
transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
|
|
}
|
|
}
|
|
if (op.trackByFn !== null) {
|
|
op.trackByFn = transformExpressionsInExpression(op.trackByFn, transform, flags);
|
|
}
|
|
break;
|
|
case OpKind.Repeater:
|
|
op.collection = transformExpressionsInExpression(op.collection, transform, flags);
|
|
break;
|
|
case OpKind.Defer:
|
|
if (op.loadingConfig !== null) {
|
|
op.loadingConfig = transformExpressionsInExpression(op.loadingConfig, transform, flags);
|
|
}
|
|
if (op.placeholderConfig !== null) {
|
|
op.placeholderConfig = transformExpressionsInExpression(op.placeholderConfig, transform, flags);
|
|
}
|
|
if (op.resolverFn !== null) {
|
|
op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
|
|
}
|
|
break;
|
|
case OpKind.I18nMessage:
|
|
for (const [placeholder, expr] of op.params) {
|
|
op.params.set(placeholder, transformExpressionsInExpression(expr, transform, flags));
|
|
}
|
|
for (const [placeholder, expr] of op.postprocessingParams) {
|
|
op.postprocessingParams.set(placeholder, transformExpressionsInExpression(expr, transform, flags));
|
|
}
|
|
break;
|
|
case OpKind.DeferWhen:
|
|
op.expr = transformExpressionsInExpression(op.expr, transform, flags);
|
|
break;
|
|
case OpKind.StoreLet:
|
|
op.value = transformExpressionsInExpression(op.value, transform, flags);
|
|
break;
|
|
case OpKind.Advance:
|
|
case OpKind.Container:
|
|
case OpKind.ContainerEnd:
|
|
case OpKind.ContainerStart:
|
|
case OpKind.DeferOn:
|
|
case OpKind.DisableBindings:
|
|
case OpKind.Element:
|
|
case OpKind.ElementEnd:
|
|
case OpKind.ElementStart:
|
|
case OpKind.EnableBindings:
|
|
case OpKind.I18n:
|
|
case OpKind.I18nApply:
|
|
case OpKind.I18nContext:
|
|
case OpKind.I18nEnd:
|
|
case OpKind.I18nStart:
|
|
case OpKind.IcuEnd:
|
|
case OpKind.IcuStart:
|
|
case OpKind.Namespace:
|
|
case OpKind.Pipe:
|
|
case OpKind.Projection:
|
|
case OpKind.ProjectionDef:
|
|
case OpKind.Template:
|
|
case OpKind.Text:
|
|
case OpKind.I18nAttributes:
|
|
case OpKind.IcuPlaceholder:
|
|
case OpKind.DeclareLet:
|
|
case OpKind.SourceLocation:
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.ControlCreate:
|
|
break;
|
|
default:
|
|
throw new Error(`AssertionError: transformExpressionsInOp doesn't handle ${OpKind[op.kind]}`);
|
|
}
|
|
}
|
|
function transformExpressionsInExpression(expr, transform, flags) {
|
|
if (expr instanceof ExpressionBase) {
|
|
expr.transformInternalExpressions(transform, flags);
|
|
} else if (expr instanceof BinaryOperatorExpr) {
|
|
expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
|
|
expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
|
|
} else if (expr instanceof UnaryOperatorExpr) {
|
|
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
} else if (expr instanceof ReadPropExpr) {
|
|
expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
|
|
} else if (expr instanceof ReadKeyExpr) {
|
|
expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
|
|
expr.index = transformExpressionsInExpression(expr.index, transform, flags);
|
|
} else if (expr instanceof InvokeFunctionExpr) {
|
|
expr.fn = transformExpressionsInExpression(expr.fn, transform, flags);
|
|
for (let i = 0; i < expr.args.length; i++) {
|
|
expr.args[i] = transformExpressionsInExpression(expr.args[i], transform, flags);
|
|
}
|
|
} else if (expr instanceof LiteralArrayExpr) {
|
|
for (let i = 0; i < expr.entries.length; i++) {
|
|
expr.entries[i] = transformExpressionsInExpression(expr.entries[i], transform, flags);
|
|
}
|
|
} else if (expr instanceof LiteralMapExpr) {
|
|
for (const entry of expr.entries) {
|
|
if (entry instanceof LiteralMapSpreadAssignment) {
|
|
entry.expression = transformExpressionsInExpression(entry.expression, transform, flags);
|
|
} else {
|
|
entry.value = transformExpressionsInExpression(entry.value, transform, flags);
|
|
}
|
|
}
|
|
} else if (expr instanceof ConditionalExpr) {
|
|
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
expr.trueCase = transformExpressionsInExpression(expr.trueCase, transform, flags);
|
|
if (expr.falseCase !== null) {
|
|
expr.falseCase = transformExpressionsInExpression(expr.falseCase, transform, flags);
|
|
}
|
|
} else if (expr instanceof TypeofExpr) {
|
|
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
} else if (expr instanceof VoidExpr) {
|
|
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
} else if (expr instanceof LocalizedString) {
|
|
for (let i = 0; i < expr.expressions.length; i++) {
|
|
expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
|
|
}
|
|
} else if (expr instanceof NotExpr) {
|
|
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
} else if (expr instanceof TaggedTemplateLiteralExpr) {
|
|
expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
|
|
expr.template.expressions = expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
|
|
} else if (expr instanceof ArrowFunctionExpr) {
|
|
if (Array.isArray(expr.body)) {
|
|
for (let i = 0; i < expr.body.length; i++) {
|
|
transformExpressionsInStatement(expr.body[i], transform, flags);
|
|
}
|
|
} else {
|
|
expr.body = transformExpressionsInExpression(expr.body, transform, flags);
|
|
}
|
|
} else if (expr instanceof WrappedNodeExpr) ; else if (expr instanceof TemplateLiteralExpr) {
|
|
for (let i = 0; i < expr.expressions.length; i++) {
|
|
expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
|
|
}
|
|
} else if (expr instanceof ParenthesizedExpr) {
|
|
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
} else if (expr instanceof SpreadElementExpr) {
|
|
expr.expression = transformExpressionsInExpression(expr.expression, transform, flags);
|
|
} else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr || expr instanceof LiteralExpr || expr instanceof RegularExpressionLiteralExpr) ; else {
|
|
throw new Error(`Unhandled expression kind: ${expr.constructor.name}`);
|
|
}
|
|
return transform(expr, flags);
|
|
}
|
|
function transformExpressionsInStatement(stmt, transform, flags) {
|
|
if (stmt instanceof ExpressionStatement) {
|
|
stmt.expr = transformExpressionsInExpression(stmt.expr, transform, flags);
|
|
} else if (stmt instanceof ReturnStatement) {
|
|
stmt.value = transformExpressionsInExpression(stmt.value, transform, flags);
|
|
} else if (stmt instanceof DeclareVarStmt) {
|
|
if (stmt.value !== undefined) {
|
|
stmt.value = transformExpressionsInExpression(stmt.value, transform, flags);
|
|
}
|
|
} else if (stmt instanceof IfStmt) {
|
|
stmt.condition = transformExpressionsInExpression(stmt.condition, transform, flags);
|
|
for (const caseStatement of stmt.trueCase) {
|
|
transformExpressionsInStatement(caseStatement, transform, flags);
|
|
}
|
|
for (const caseStatement of stmt.falseCase) {
|
|
transformExpressionsInStatement(caseStatement, transform, flags);
|
|
}
|
|
} else {
|
|
throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
|
|
}
|
|
}
|
|
function isStringLiteral(expr) {
|
|
return expr instanceof LiteralExpr && typeof expr.value === 'string';
|
|
}
|
|
|
|
class OpList {
|
|
static nextListId = 0;
|
|
debugListId = OpList.nextListId++;
|
|
head = {
|
|
kind: OpKind.ListEnd,
|
|
next: null,
|
|
prev: null,
|
|
debugListId: this.debugListId
|
|
};
|
|
tail = {
|
|
kind: OpKind.ListEnd,
|
|
next: null,
|
|
prev: null,
|
|
debugListId: this.debugListId
|
|
};
|
|
constructor() {
|
|
this.head.next = this.tail;
|
|
this.tail.prev = this.head;
|
|
}
|
|
push(op) {
|
|
if (Array.isArray(op)) {
|
|
for (const o of op) {
|
|
this.push(o);
|
|
}
|
|
return;
|
|
}
|
|
OpList.assertIsNotEnd(op);
|
|
OpList.assertIsUnowned(op);
|
|
op.debugListId = this.debugListId;
|
|
const oldLast = this.tail.prev;
|
|
op.prev = oldLast;
|
|
oldLast.next = op;
|
|
op.next = this.tail;
|
|
this.tail.prev = op;
|
|
}
|
|
prepend(ops) {
|
|
if (ops.length === 0) {
|
|
return;
|
|
}
|
|
for (const op of ops) {
|
|
OpList.assertIsNotEnd(op);
|
|
OpList.assertIsUnowned(op);
|
|
op.debugListId = this.debugListId;
|
|
}
|
|
const first = this.head.next;
|
|
let prev = this.head;
|
|
for (const op of ops) {
|
|
prev.next = op;
|
|
op.prev = prev;
|
|
prev = op;
|
|
}
|
|
prev.next = first;
|
|
first.prev = prev;
|
|
}
|
|
*[Symbol.iterator]() {
|
|
let current = this.head.next;
|
|
while (current !== this.tail) {
|
|
OpList.assertIsOwned(current, this.debugListId);
|
|
const next = current.next;
|
|
yield current;
|
|
current = next;
|
|
}
|
|
}
|
|
*reversed() {
|
|
let current = this.tail.prev;
|
|
while (current !== this.head) {
|
|
OpList.assertIsOwned(current, this.debugListId);
|
|
const prev = current.prev;
|
|
yield current;
|
|
current = prev;
|
|
}
|
|
}
|
|
static replace(oldOp, newOp) {
|
|
OpList.assertIsNotEnd(oldOp);
|
|
OpList.assertIsNotEnd(newOp);
|
|
OpList.assertIsOwned(oldOp);
|
|
OpList.assertIsUnowned(newOp);
|
|
newOp.debugListId = oldOp.debugListId;
|
|
if (oldOp.prev !== null) {
|
|
oldOp.prev.next = newOp;
|
|
newOp.prev = oldOp.prev;
|
|
}
|
|
if (oldOp.next !== null) {
|
|
oldOp.next.prev = newOp;
|
|
newOp.next = oldOp.next;
|
|
}
|
|
oldOp.debugListId = null;
|
|
oldOp.prev = null;
|
|
oldOp.next = null;
|
|
}
|
|
static replaceWithMany(oldOp, newOps) {
|
|
if (newOps.length === 0) {
|
|
OpList.remove(oldOp);
|
|
return;
|
|
}
|
|
OpList.assertIsNotEnd(oldOp);
|
|
OpList.assertIsOwned(oldOp);
|
|
const listId = oldOp.debugListId;
|
|
oldOp.debugListId = null;
|
|
for (const newOp of newOps) {
|
|
OpList.assertIsNotEnd(newOp);
|
|
OpList.assertIsUnowned(newOp);
|
|
}
|
|
const {
|
|
prev: oldPrev,
|
|
next: oldNext
|
|
} = oldOp;
|
|
oldOp.prev = null;
|
|
oldOp.next = null;
|
|
let prev = oldPrev;
|
|
for (const newOp of newOps) {
|
|
OpList.assertIsUnowned(newOp);
|
|
newOp.debugListId = listId;
|
|
prev.next = newOp;
|
|
newOp.prev = prev;
|
|
newOp.next = null;
|
|
prev = newOp;
|
|
}
|
|
const first = newOps[0];
|
|
const last = prev;
|
|
if (oldPrev !== null) {
|
|
oldPrev.next = first;
|
|
first.prev = oldPrev;
|
|
}
|
|
if (oldNext !== null) {
|
|
oldNext.prev = last;
|
|
last.next = oldNext;
|
|
}
|
|
}
|
|
static remove(op) {
|
|
OpList.assertIsNotEnd(op);
|
|
OpList.assertIsOwned(op);
|
|
op.prev.next = op.next;
|
|
op.next.prev = op.prev;
|
|
op.debugListId = null;
|
|
op.prev = null;
|
|
op.next = null;
|
|
}
|
|
static insertBefore(op, target) {
|
|
if (Array.isArray(op)) {
|
|
for (const o of op) {
|
|
OpList.insertBefore(o, target);
|
|
}
|
|
return;
|
|
}
|
|
OpList.assertIsOwned(target);
|
|
if (target.prev === null) {
|
|
throw new Error(`AssertionError: illegal operation on list start`);
|
|
}
|
|
OpList.assertIsNotEnd(op);
|
|
OpList.assertIsUnowned(op);
|
|
op.debugListId = target.debugListId;
|
|
op.prev = null;
|
|
target.prev.next = op;
|
|
op.prev = target.prev;
|
|
op.next = target;
|
|
target.prev = op;
|
|
}
|
|
static insertAfter(op, target) {
|
|
OpList.assertIsOwned(target);
|
|
if (target.next === null) {
|
|
throw new Error(`AssertionError: illegal operation on list end`);
|
|
}
|
|
OpList.assertIsNotEnd(op);
|
|
OpList.assertIsUnowned(op);
|
|
op.debugListId = target.debugListId;
|
|
target.next.prev = op;
|
|
op.next = target.next;
|
|
op.prev = target;
|
|
target.next = op;
|
|
}
|
|
static assertIsUnowned(op) {
|
|
if (op.debugListId !== null) {
|
|
throw new Error(`AssertionError: illegal operation on owned node: ${OpKind[op.kind]}`);
|
|
}
|
|
}
|
|
static assertIsOwned(op, byList) {
|
|
if (op.debugListId === null) {
|
|
throw new Error(`AssertionError: illegal operation on unowned node: ${OpKind[op.kind]}`);
|
|
} else if (byList !== undefined && op.debugListId !== byList) {
|
|
throw new Error(`AssertionError: node belongs to the wrong list (expected ${byList}, actual ${op.debugListId})`);
|
|
}
|
|
}
|
|
static assertIsNotEnd(op) {
|
|
if (op.kind === OpKind.ListEnd) {
|
|
throw new Error(`AssertionError: illegal operation on list head or tail`);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SlotHandle {
|
|
slot = null;
|
|
}
|
|
|
|
const elementContainerOpKinds = new Set([OpKind.Element, OpKind.ElementStart, OpKind.Container, OpKind.ContainerStart, OpKind.Template, OpKind.RepeaterCreate, OpKind.ConditionalCreate, OpKind.ConditionalBranchCreate]);
|
|
function isElementOrContainerOp(op) {
|
|
return elementContainerOpKinds.has(op.kind);
|
|
}
|
|
function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
return {
|
|
kind: OpKind.ElementStart,
|
|
xref,
|
|
tag,
|
|
handle: new SlotHandle(),
|
|
attributes: null,
|
|
localRefs: [],
|
|
nonBindable: false,
|
|
namespace,
|
|
i18nPlaceholder,
|
|
startSourceSpan,
|
|
wholeSourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
return {
|
|
kind: OpKind.Template,
|
|
xref,
|
|
templateKind,
|
|
attributes: null,
|
|
tag,
|
|
handle: new SlotHandle(),
|
|
functionNameSuffix,
|
|
decls: null,
|
|
vars: null,
|
|
localRefs: [],
|
|
nonBindable: false,
|
|
namespace,
|
|
i18nPlaceholder,
|
|
startSourceSpan,
|
|
wholeSourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createConditionalCreateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
return {
|
|
kind: OpKind.ConditionalCreate,
|
|
xref,
|
|
templateKind,
|
|
attributes: null,
|
|
tag,
|
|
handle: new SlotHandle(),
|
|
functionNameSuffix,
|
|
decls: null,
|
|
vars: null,
|
|
localRefs: [],
|
|
nonBindable: false,
|
|
namespace,
|
|
i18nPlaceholder,
|
|
startSourceSpan,
|
|
wholeSourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createConditionalBranchCreateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
return {
|
|
kind: OpKind.ConditionalBranchCreate,
|
|
xref,
|
|
templateKind,
|
|
attributes: null,
|
|
tag,
|
|
handle: new SlotHandle(),
|
|
functionNameSuffix,
|
|
decls: null,
|
|
vars: null,
|
|
localRefs: [],
|
|
nonBindable: false,
|
|
namespace,
|
|
i18nPlaceholder,
|
|
startSourceSpan,
|
|
wholeSourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
return {
|
|
kind: OpKind.RepeaterCreate,
|
|
attributes: null,
|
|
xref: primaryView,
|
|
handle: new SlotHandle(),
|
|
emptyView,
|
|
track,
|
|
trackByFn: null,
|
|
trackByOps: null,
|
|
tag,
|
|
emptyTag,
|
|
emptyAttributes: null,
|
|
functionNameSuffix: 'For',
|
|
namespace: Namespace.HTML,
|
|
nonBindable: false,
|
|
localRefs: [],
|
|
decls: null,
|
|
vars: null,
|
|
varNames,
|
|
usesComponentInstance: false,
|
|
i18nPlaceholder,
|
|
emptyI18nPlaceholder,
|
|
startSourceSpan,
|
|
wholeSourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_VARS,
|
|
numSlotsUsed: emptyView === null ? 2 : 3
|
|
};
|
|
}
|
|
function createElementEndOp(xref, sourceSpan) {
|
|
return {
|
|
kind: OpKind.ElementEnd,
|
|
xref,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createDisableBindingsOp(xref) {
|
|
return {
|
|
kind: OpKind.DisableBindings,
|
|
xref,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createEnableBindingsOp(xref) {
|
|
return {
|
|
kind: OpKind.EnableBindings,
|
|
xref,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Text,
|
|
xref,
|
|
handle: new SlotHandle(),
|
|
initialValue,
|
|
icuPlaceholder,
|
|
sourceSpan,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createAnimationStringOp(name, target, animationKind, expression, securityContext, sourceSpan) {
|
|
return {
|
|
kind: OpKind.AnimationString,
|
|
name,
|
|
target,
|
|
animationKind,
|
|
expression,
|
|
i18nMessage: null,
|
|
securityContext,
|
|
sanitizer: null,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createAnimationOp(name, target, animationKind, callbackOps, securityContext, sourceSpan) {
|
|
const handlerOps = new OpList();
|
|
handlerOps.push(callbackOps);
|
|
return {
|
|
kind: OpKind.Animation,
|
|
name,
|
|
target,
|
|
animationKind,
|
|
handlerOps,
|
|
handlerFnName: null,
|
|
i18nMessage: null,
|
|
securityContext,
|
|
sanitizer: null,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createListenerOp(target, targetSlot, name, tag, handlerOps, legacyAnimationPhase, eventTarget, hostListener, sourceSpan) {
|
|
const handlerList = new OpList();
|
|
handlerList.push(handlerOps);
|
|
return {
|
|
kind: OpKind.Listener,
|
|
target,
|
|
targetSlot,
|
|
tag,
|
|
hostListener,
|
|
name,
|
|
handlerOps: handlerList,
|
|
handlerFnName: null,
|
|
consumesDollarEvent: false,
|
|
isLegacyAnimationListener: legacyAnimationPhase !== null,
|
|
legacyAnimationPhase: legacyAnimationPhase,
|
|
eventTarget,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createAnimationListenerOp(target, targetSlot, name, tag, handlerOps, animationKind, eventTarget, hostListener, sourceSpan) {
|
|
const handlerList = new OpList();
|
|
handlerList.push(handlerOps);
|
|
return {
|
|
kind: OpKind.AnimationListener,
|
|
target,
|
|
targetSlot,
|
|
tag,
|
|
hostListener,
|
|
name,
|
|
animationKind,
|
|
handlerOps: handlerList,
|
|
handlerFnName: null,
|
|
consumesDollarEvent: false,
|
|
eventTarget,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) {
|
|
const handlerList = new OpList();
|
|
handlerList.push(handlerOps);
|
|
return {
|
|
kind: OpKind.TwoWayListener,
|
|
target,
|
|
targetSlot,
|
|
tag,
|
|
name,
|
|
handlerOps: handlerList,
|
|
handlerFnName: null,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createPipeOp(xref, slot, name) {
|
|
return {
|
|
kind: OpKind.Pipe,
|
|
xref,
|
|
handle: slot,
|
|
name,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_SLOT
|
|
};
|
|
}
|
|
function createNamespaceOp(namespace) {
|
|
return {
|
|
kind: OpKind.Namespace,
|
|
active: namespace,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createProjectionDefOp(def) {
|
|
return {
|
|
kind: OpKind.ProjectionDef,
|
|
def,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createProjectionOp(xref, selector, i18nPlaceholder, fallbackView, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Projection,
|
|
xref,
|
|
handle: new SlotHandle(),
|
|
selector,
|
|
i18nPlaceholder,
|
|
fallbackView,
|
|
projectionSlotIndex: 0,
|
|
attributes: null,
|
|
localRefs: [],
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
numSlotsUsed: fallbackView === null ? 1 : 2
|
|
};
|
|
}
|
|
function createExtractedAttributeOp(target, bindingKind, namespace, name, expression, i18nContext, i18nMessage, securityContext) {
|
|
return {
|
|
kind: OpKind.ExtractedAttribute,
|
|
target,
|
|
bindingKind,
|
|
namespace,
|
|
name,
|
|
expression,
|
|
i18nContext,
|
|
i18nMessage,
|
|
securityContext,
|
|
trustedValueFn: null,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createDeferOp(xref, main, mainSlot, ownResolverFn, resolverFn, sourceSpan) {
|
|
return {
|
|
kind: OpKind.Defer,
|
|
xref,
|
|
handle: new SlotHandle(),
|
|
mainView: main,
|
|
mainSlot,
|
|
loadingView: null,
|
|
loadingSlot: null,
|
|
loadingConfig: null,
|
|
loadingMinimumTime: null,
|
|
loadingAfterTime: null,
|
|
placeholderView: null,
|
|
placeholderSlot: null,
|
|
placeholderConfig: null,
|
|
placeholderMinimumTime: null,
|
|
errorView: null,
|
|
errorSlot: null,
|
|
ownResolverFn,
|
|
resolverFn,
|
|
flags: null,
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_SLOT,
|
|
numSlotsUsed: 2
|
|
};
|
|
}
|
|
function createDeferOnOp(defer, trigger, modifier, sourceSpan) {
|
|
return {
|
|
kind: OpKind.DeferOn,
|
|
defer,
|
|
trigger,
|
|
modifier,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createDeclareLetOp(xref, declaredName, sourceSpan) {
|
|
return {
|
|
kind: OpKind.DeclareLet,
|
|
xref,
|
|
declaredName,
|
|
sourceSpan,
|
|
handle: new SlotHandle(),
|
|
...TRAIT_CONSUMES_SLOT,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
|
|
return {
|
|
kind: OpKind.I18nMessage,
|
|
xref,
|
|
i18nContext,
|
|
i18nBlock,
|
|
message,
|
|
messagePlaceholder,
|
|
params,
|
|
postprocessingParams,
|
|
needsPostprocessing,
|
|
subMessages: [],
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createI18nStartOp(xref, message, root, sourceSpan) {
|
|
return {
|
|
kind: OpKind.I18nStart,
|
|
xref,
|
|
handle: new SlotHandle(),
|
|
root: root ?? xref,
|
|
message,
|
|
messageIndex: null,
|
|
subTemplateIndex: null,
|
|
context: null,
|
|
sourceSpan,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_SLOT
|
|
};
|
|
}
|
|
function createI18nEndOp(xref, sourceSpan) {
|
|
return {
|
|
kind: OpKind.I18nEnd,
|
|
xref,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createIcuStartOp(xref, message, messagePlaceholder, sourceSpan) {
|
|
return {
|
|
kind: OpKind.IcuStart,
|
|
xref,
|
|
message,
|
|
messagePlaceholder,
|
|
context: null,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createIcuEndOp(xref) {
|
|
return {
|
|
kind: OpKind.IcuEnd,
|
|
xref,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createIcuPlaceholderOp(xref, name, strings) {
|
|
return {
|
|
kind: OpKind.IcuPlaceholder,
|
|
xref,
|
|
name,
|
|
strings,
|
|
expressionPlaceholders: [],
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
|
|
if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
|
|
throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
|
|
}
|
|
return {
|
|
kind: OpKind.I18nContext,
|
|
contextKind,
|
|
xref,
|
|
i18nBlock,
|
|
message,
|
|
sourceSpan,
|
|
params: new Map(),
|
|
postprocessingParams: new Map(),
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createI18nAttributesOp(xref, handle, target) {
|
|
return {
|
|
kind: OpKind.I18nAttributes,
|
|
xref,
|
|
handle,
|
|
target,
|
|
i18nAttributesConfig: null,
|
|
...NEW_OP,
|
|
...TRAIT_CONSUMES_SLOT
|
|
};
|
|
}
|
|
function createSourceLocationOp(templatePath, locations) {
|
|
return {
|
|
kind: OpKind.SourceLocation,
|
|
templatePath,
|
|
locations,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
function createControlCreateOp(sourceSpan) {
|
|
return {
|
|
kind: OpKind.ControlCreate,
|
|
sourceSpan,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
|
|
function createDomPropertyOp(name, expression, bindingKind, i18nContext, securityContext, sourceSpan) {
|
|
return {
|
|
kind: OpKind.DomProperty,
|
|
name,
|
|
expression,
|
|
bindingKind,
|
|
i18nContext,
|
|
securityContext,
|
|
sanitizer: null,
|
|
sourceSpan,
|
|
...TRAIT_CONSUMES_VARS,
|
|
...NEW_OP
|
|
};
|
|
}
|
|
|
|
const CTX_REF = 'CTX_REF_MARKER';
|
|
|
|
var CompilationJobKind;
|
|
(function (CompilationJobKind) {
|
|
CompilationJobKind[CompilationJobKind["Tmpl"] = 0] = "Tmpl";
|
|
CompilationJobKind[CompilationJobKind["Host"] = 1] = "Host";
|
|
CompilationJobKind[CompilationJobKind["Both"] = 2] = "Both";
|
|
})(CompilationJobKind || (CompilationJobKind = {}));
|
|
var TemplateCompilationMode;
|
|
(function (TemplateCompilationMode) {
|
|
TemplateCompilationMode[TemplateCompilationMode["Full"] = 0] = "Full";
|
|
TemplateCompilationMode[TemplateCompilationMode["DomOnly"] = 1] = "DomOnly";
|
|
})(TemplateCompilationMode || (TemplateCompilationMode = {}));
|
|
class CompilationJob {
|
|
componentName;
|
|
pool;
|
|
compatibility;
|
|
mode;
|
|
constructor(componentName, pool, compatibility, mode) {
|
|
this.componentName = componentName;
|
|
this.pool = pool;
|
|
this.compatibility = compatibility;
|
|
this.mode = mode;
|
|
}
|
|
kind = CompilationJobKind.Both;
|
|
allocateXrefId() {
|
|
return this.nextXrefId++;
|
|
}
|
|
nextXrefId = 0;
|
|
}
|
|
class ComponentCompilationJob extends CompilationJob {
|
|
relativeContextFilePath;
|
|
i18nUseExternalIds;
|
|
deferMeta;
|
|
allDeferrableDepsFn;
|
|
relativeTemplatePath;
|
|
enableDebugLocations;
|
|
constructor(componentName, pool, compatibility, mode, relativeContextFilePath, i18nUseExternalIds, deferMeta, allDeferrableDepsFn, relativeTemplatePath, enableDebugLocations) {
|
|
super(componentName, pool, compatibility, mode);
|
|
this.relativeContextFilePath = relativeContextFilePath;
|
|
this.i18nUseExternalIds = i18nUseExternalIds;
|
|
this.deferMeta = deferMeta;
|
|
this.allDeferrableDepsFn = allDeferrableDepsFn;
|
|
this.relativeTemplatePath = relativeTemplatePath;
|
|
this.enableDebugLocations = enableDebugLocations;
|
|
this.root = new ViewCompilationUnit(this, this.allocateXrefId(), null);
|
|
this.views.set(this.root.xref, this.root);
|
|
}
|
|
kind = CompilationJobKind.Tmpl;
|
|
fnSuffix = 'Template';
|
|
root;
|
|
views = new Map();
|
|
contentSelectors = null;
|
|
allocateView(parent) {
|
|
const view = new ViewCompilationUnit(this, this.allocateXrefId(), parent);
|
|
this.views.set(view.xref, view);
|
|
return view;
|
|
}
|
|
get units() {
|
|
return this.views.values();
|
|
}
|
|
addConst(newConst, initializers) {
|
|
for (let idx = 0; idx < this.consts.length; idx++) {
|
|
if (this.consts[idx].isEquivalent(newConst)) {
|
|
return idx;
|
|
}
|
|
}
|
|
const idx = this.consts.length;
|
|
this.consts.push(newConst);
|
|
if (initializers) {
|
|
this.constsInitializers.push(...initializers);
|
|
}
|
|
return idx;
|
|
}
|
|
consts = [];
|
|
constsInitializers = [];
|
|
}
|
|
class CompilationUnit {
|
|
xref;
|
|
constructor(xref) {
|
|
this.xref = xref;
|
|
}
|
|
create = new OpList();
|
|
update = new OpList();
|
|
fnName = null;
|
|
vars = null;
|
|
*ops() {
|
|
for (const op of this.create) {
|
|
yield op;
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener || op.kind === OpKind.TwoWayListener) {
|
|
for (const listenerOp of op.handlerOps) {
|
|
yield listenerOp;
|
|
}
|
|
} else if (op.kind === OpKind.RepeaterCreate && op.trackByOps !== null) {
|
|
for (const trackOp of op.trackByOps) {
|
|
yield trackOp;
|
|
}
|
|
}
|
|
}
|
|
for (const op of this.update) {
|
|
yield op;
|
|
}
|
|
}
|
|
}
|
|
class ViewCompilationUnit extends CompilationUnit {
|
|
job;
|
|
parent;
|
|
constructor(job, xref, parent) {
|
|
super(xref);
|
|
this.job = job;
|
|
this.parent = parent;
|
|
}
|
|
contextVariables = new Map();
|
|
aliases = new Set();
|
|
decls = null;
|
|
}
|
|
class HostBindingCompilationJob extends CompilationJob {
|
|
constructor(componentName, pool, compatibility, mode) {
|
|
super(componentName, pool, compatibility, mode);
|
|
this.root = new HostBindingCompilationUnit(this);
|
|
}
|
|
kind = CompilationJobKind.Host;
|
|
fnSuffix = 'HostBindings';
|
|
root;
|
|
get units() {
|
|
return [this.root];
|
|
}
|
|
}
|
|
class HostBindingCompilationUnit extends CompilationUnit {
|
|
job;
|
|
constructor(job) {
|
|
super(0);
|
|
this.job = job;
|
|
}
|
|
attributes = null;
|
|
}
|
|
|
|
function deleteAnyCasts(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
transformExpressionsInOp(op, removeAnys, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
function removeAnys(e) {
|
|
if (e instanceof InvokeFunctionExpr && e.fn instanceof LexicalReadExpr && e.fn.name === '$any') {
|
|
if (e.args.length !== 1) {
|
|
throw new Error('The $any builtin function expects exactly one argument.');
|
|
}
|
|
return e.args[0];
|
|
}
|
|
return e;
|
|
}
|
|
|
|
function applyI18nExpressions(job) {
|
|
const i18nContexts = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.I18nContext) {
|
|
i18nContexts.set(op.xref, op);
|
|
}
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
|
|
OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function needsApplication(i18nContexts, op) {
|
|
if (op.next?.kind !== OpKind.I18nExpression) {
|
|
return true;
|
|
}
|
|
const context = i18nContexts.get(op.context);
|
|
const nextContext = i18nContexts.get(op.next.context);
|
|
if (context === undefined) {
|
|
throw new Error("AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp's context");
|
|
}
|
|
if (nextContext === undefined) {
|
|
throw new Error("AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp's context");
|
|
}
|
|
if (context.i18nBlock !== null) {
|
|
if (context.i18nBlock !== nextContext.i18nBlock) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (op.i18nOwner !== op.next.i18nOwner) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function assignI18nSlotDependencies(job) {
|
|
for (const unit of job.units) {
|
|
let updateOp = unit.update.head;
|
|
let i18nExpressionsInProgress = [];
|
|
let state = null;
|
|
for (const createOp of unit.create) {
|
|
if (createOp.kind === OpKind.I18nStart) {
|
|
state = {
|
|
blockXref: createOp.xref,
|
|
lastSlotConsumer: createOp.xref
|
|
};
|
|
} else if (createOp.kind === OpKind.I18nEnd) {
|
|
for (const op of i18nExpressionsInProgress) {
|
|
op.target = state.lastSlotConsumer;
|
|
OpList.insertBefore(op, updateOp);
|
|
}
|
|
i18nExpressionsInProgress.length = 0;
|
|
state = null;
|
|
}
|
|
if (hasConsumesSlotTrait(createOp)) {
|
|
if (state !== null) {
|
|
state.lastSlotConsumer = createOp.xref;
|
|
}
|
|
while (true) {
|
|
if (updateOp.next === null) {
|
|
break;
|
|
}
|
|
if (state !== null && updateOp.kind === OpKind.I18nExpression && updateOp.usage === I18nExpressionFor.I18nText && updateOp.i18nOwner === state.blockXref) {
|
|
const opToRemove = updateOp;
|
|
updateOp = updateOp.next;
|
|
OpList.remove(opToRemove);
|
|
i18nExpressionsInProgress.push(opToRemove);
|
|
continue;
|
|
}
|
|
let hasDifferentTarget = false;
|
|
if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
|
|
hasDifferentTarget = true;
|
|
} else if (updateOp.kind === OpKind.Statement || updateOp.kind === OpKind.Variable) {
|
|
visitExpressionsInOp(updateOp, expr => {
|
|
if (!hasDifferentTarget && hasDependsOnSlotContextTrait(expr) && expr.target !== createOp.xref) {
|
|
hasDifferentTarget = true;
|
|
}
|
|
});
|
|
}
|
|
if (hasDifferentTarget) {
|
|
break;
|
|
}
|
|
updateOp = updateOp.next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function attachSourceLocations(job) {
|
|
if (!job.enableDebugLocations || job.relativeTemplatePath === null) {
|
|
return;
|
|
}
|
|
for (const unit of job.units) {
|
|
const locations = [];
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.ElementStart || op.kind === OpKind.Element) {
|
|
const start = op.startSourceSpan.start;
|
|
locations.push({
|
|
targetSlot: op.handle,
|
|
offset: start.offset,
|
|
line: start.line,
|
|
column: start.col
|
|
});
|
|
}
|
|
}
|
|
if (locations.length > 0) {
|
|
unit.create.push(createSourceLocationOp(job.relativeTemplatePath, locations));
|
|
}
|
|
}
|
|
}
|
|
|
|
function createOpXrefMap(unit) {
|
|
const map = new Map();
|
|
for (const op of unit.create) {
|
|
if (!hasConsumesSlotTrait(op)) {
|
|
continue;
|
|
}
|
|
map.set(op.xref, op);
|
|
if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
|
|
map.set(op.emptyView, op);
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function extractAttributes(job) {
|
|
for (const unit of job.units) {
|
|
const elements = createOpXrefMap(unit);
|
|
for (const op of unit.ops()) {
|
|
switch (op.kind) {
|
|
case OpKind.Attribute:
|
|
extractAttributeOp(unit, op, elements);
|
|
break;
|
|
case OpKind.Property:
|
|
if (op.bindingKind !== BindingKind.LegacyAnimation && op.bindingKind !== BindingKind.Animation) {
|
|
let bindingKind;
|
|
if (op.i18nMessage !== null && op.templateKind === null) {
|
|
bindingKind = BindingKind.I18n;
|
|
} else if (op.isStructuralTemplateAttribute) {
|
|
bindingKind = BindingKind.Template;
|
|
} else {
|
|
bindingKind = BindingKind.Property;
|
|
}
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, bindingKind, null, op.name, null, null, null, op.securityContext), lookupElement$3(elements, op.target));
|
|
}
|
|
break;
|
|
case OpKind.Control:
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, null, null, null, op.securityContext), lookupElement$3(elements, op.target));
|
|
break;
|
|
case OpKind.TwoWayProperty:
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, null, null, null, op.securityContext), lookupElement$3(elements, op.target));
|
|
break;
|
|
case OpKind.StyleProp:
|
|
case OpKind.ClassProp:
|
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder && op.expression instanceof EmptyExpr) {
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, null, null, null, SecurityContext.STYLE), lookupElement$3(elements, op.target));
|
|
}
|
|
break;
|
|
case OpKind.Listener:
|
|
if (!op.isLegacyAnimationListener) {
|
|
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, null, null, null, SecurityContext.NONE);
|
|
if (job.kind === CompilationJobKind.Host) {
|
|
if (job.compatibility) {
|
|
break;
|
|
}
|
|
unit.create.push(extractedAttributeOp);
|
|
} else {
|
|
OpList.insertBefore(extractedAttributeOp, lookupElement$3(elements, op.target));
|
|
}
|
|
}
|
|
break;
|
|
case OpKind.TwoWayListener:
|
|
if (job.kind !== CompilationJobKind.Host) {
|
|
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, null, null, null, SecurityContext.NONE);
|
|
OpList.insertBefore(extractedAttributeOp, lookupElement$3(elements, op.target));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function lookupElement$3(elements, xref) {
|
|
const el = elements.get(xref);
|
|
if (el === undefined) {
|
|
throw new Error('All attributes should have an element-like target.');
|
|
}
|
|
return el;
|
|
}
|
|
function extractAttributeOp(unit, op, elements) {
|
|
if (op.expression instanceof Interpolation) {
|
|
return;
|
|
}
|
|
let extractable = op.isTextAttribute || op.expression.isConstant();
|
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
extractable &&= op.isTextAttribute;
|
|
}
|
|
if (extractable) {
|
|
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.namespace, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
|
|
if (unit.job.kind === CompilationJobKind.Host) {
|
|
unit.create.push(extractedAttributeOp);
|
|
} else {
|
|
const ownerOp = lookupElement$3(elements, op.target);
|
|
OpList.insertBefore(extractedAttributeOp, ownerOp);
|
|
}
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
|
|
const ARIA_PREFIX = 'aria-';
|
|
function isAriaAttribute(name) {
|
|
return name.startsWith(ARIA_PREFIX) && name.length > ARIA_PREFIX.length;
|
|
}
|
|
|
|
function lookupElement$2(elements, xref) {
|
|
const el = elements.get(xref);
|
|
if (el === undefined) {
|
|
throw new Error('All attributes should have an element-like target.');
|
|
}
|
|
return el;
|
|
}
|
|
function specializeBindings(job) {
|
|
const elements = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (!isElementOrContainerOp(op)) {
|
|
continue;
|
|
}
|
|
elements.set(op.xref, op);
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind !== OpKind.Binding) {
|
|
continue;
|
|
}
|
|
switch (op.bindingKind) {
|
|
case BindingKind.Attribute:
|
|
if (op.name === 'ngNonBindable') {
|
|
OpList.remove(op);
|
|
const target = lookupElement$2(elements, op.target);
|
|
target.nonBindable = true;
|
|
} else if (op.name.startsWith('animate.')) {
|
|
OpList.replace(op, createAnimationBindingOp(op.name, op.target, op.name === 'animate.enter' ? "enter" : "leave", op.expression, op.securityContext, op.sourceSpan, 0));
|
|
} else {
|
|
const [namespace, name] = splitNsName(op.name);
|
|
OpList.replace(op, createAttributeOp(op.target, namespace, name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
|
|
}
|
|
break;
|
|
case BindingKind.Animation:
|
|
OpList.replace(op, createAnimationBindingOp(op.name, op.target, op.name === 'animate.enter' ? "enter" : "leave", op.expression, op.securityContext, op.sourceSpan, 1));
|
|
break;
|
|
case BindingKind.Property:
|
|
case BindingKind.LegacyAnimation:
|
|
if (job.mode === TemplateCompilationMode.DomOnly && isAriaAttribute(op.name)) {
|
|
OpList.replace(op, createAttributeOp(op.target, null, op.name, op.expression, op.securityContext, false, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
|
|
} else if (job.kind === CompilationJobKind.Host) {
|
|
OpList.replace(op, createDomPropertyOp(op.name, op.expression, op.bindingKind, op.i18nContext, op.securityContext, op.sourceSpan));
|
|
} else if (op.name === 'formField') {
|
|
OpList.replace(op, createControlOp(op));
|
|
} else {
|
|
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
}
|
|
break;
|
|
case BindingKind.TwoWayProperty:
|
|
if (!(op.expression instanceof Expression)) {
|
|
throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`);
|
|
}
|
|
OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
break;
|
|
case BindingKind.I18n:
|
|
case BindingKind.ClassName:
|
|
case BindingKind.StyleProperty:
|
|
throw new Error(`Unhandled binding of kind ${BindingKind[op.bindingKind]}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const CHAIN_COMPATIBILITY = new Map([[Identifiers.ariaProperty, Identifiers.ariaProperty], [Identifiers.attribute, Identifiers.attribute], [Identifiers.classProp, Identifiers.classProp], [Identifiers.element, Identifiers.element], [Identifiers.elementContainer, Identifiers.elementContainer], [Identifiers.elementContainerEnd, Identifiers.elementContainerEnd], [Identifiers.elementContainerStart, Identifiers.elementContainerStart], [Identifiers.elementEnd, Identifiers.elementEnd], [Identifiers.elementStart, Identifiers.elementStart], [Identifiers.domProperty, Identifiers.domProperty], [Identifiers.i18nExp, Identifiers.i18nExp], [Identifiers.listener, Identifiers.listener], [Identifiers.listener, Identifiers.listener], [Identifiers.property, Identifiers.property], [Identifiers.styleProp, Identifiers.styleProp], [Identifiers.syntheticHostListener, Identifiers.syntheticHostListener], [Identifiers.syntheticHostProperty, Identifiers.syntheticHostProperty], [Identifiers.templateCreate, Identifiers.templateCreate], [Identifiers.twoWayProperty, Identifiers.twoWayProperty], [Identifiers.twoWayListener, Identifiers.twoWayListener], [Identifiers.declareLet, Identifiers.declareLet], [Identifiers.conditionalCreate, Identifiers.conditionalBranchCreate], [Identifiers.conditionalBranchCreate, Identifiers.conditionalBranchCreate], [Identifiers.domElement, Identifiers.domElement], [Identifiers.domElementStart, Identifiers.domElementStart], [Identifiers.domElementEnd, Identifiers.domElementEnd], [Identifiers.domElementContainer, Identifiers.domElementContainer], [Identifiers.domElementContainerStart, Identifiers.domElementContainerStart], [Identifiers.domElementContainerEnd, Identifiers.domElementContainerEnd], [Identifiers.domListener, Identifiers.domListener], [Identifiers.domTemplate, Identifiers.domTemplate], [Identifiers.animationEnter, Identifiers.animationEnter], [Identifiers.animationLeave, Identifiers.animationLeave], [Identifiers.animationEnterListener, Identifiers.animationEnterListener], [Identifiers.animationLeaveListener, Identifiers.animationLeaveListener]]);
|
|
const MAX_CHAIN_LENGTH = 256;
|
|
function chain(job) {
|
|
for (const unit of job.units) {
|
|
chainOperationsInList(unit.create);
|
|
chainOperationsInList(unit.update);
|
|
}
|
|
}
|
|
function chainOperationsInList(opList) {
|
|
let chain = null;
|
|
for (const op of opList) {
|
|
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement)) {
|
|
chain = null;
|
|
continue;
|
|
}
|
|
if (!(op.statement.expr instanceof InvokeFunctionExpr) || !(op.statement.expr.fn instanceof ExternalExpr)) {
|
|
chain = null;
|
|
continue;
|
|
}
|
|
const instruction = op.statement.expr.fn.value;
|
|
if (!CHAIN_COMPATIBILITY.has(instruction)) {
|
|
chain = null;
|
|
continue;
|
|
}
|
|
if (chain !== null && CHAIN_COMPATIBILITY.get(chain.instruction) === instruction && chain.length < MAX_CHAIN_LENGTH) {
|
|
const expression = chain.expression.callFn(op.statement.expr.args, op.statement.expr.sourceSpan, op.statement.expr.pure);
|
|
chain.expression = expression;
|
|
chain.op.statement = expression.toStmt();
|
|
chain.length++;
|
|
OpList.remove(op);
|
|
} else {
|
|
chain = {
|
|
op,
|
|
instruction,
|
|
expression: op.statement.expr,
|
|
length: 1
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
function collapseSingletonInterpolations(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
const eligibleOpKind = op.kind === OpKind.Attribute || op.kind === OpKind.StyleProp || op.kind == OpKind.StyleMap || op.kind === OpKind.ClassMap;
|
|
if (eligibleOpKind && op.expression instanceof Interpolation && op.expression.strings.length === 2 && op.expression.strings.every(s => s === '')) {
|
|
op.expression = op.expression.expressions[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateConditionalExpressions(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind !== OpKind.Conditional) {
|
|
continue;
|
|
}
|
|
let test;
|
|
const defaultCase = op.conditions.findIndex(cond => cond.expr === null);
|
|
if (defaultCase >= 0) {
|
|
const slot = op.conditions.splice(defaultCase, 1)[0].targetSlot;
|
|
test = new SlotLiteralExpr(slot);
|
|
} else {
|
|
test = literal(-1);
|
|
}
|
|
let tmp = op.test == null ? null : new AssignTemporaryExpr(op.test, job.allocateXrefId());
|
|
let caseExpressionTemporaryXref = null;
|
|
for (let i = op.conditions.length - 1; i >= 0; i--) {
|
|
let conditionalCase = op.conditions[i];
|
|
if (conditionalCase.expr === null) {
|
|
continue;
|
|
}
|
|
if (tmp !== null) {
|
|
const useTmp = i === 0 ? tmp : new ReadTemporaryExpr(tmp.xref);
|
|
conditionalCase.expr = new BinaryOperatorExpr(BinaryOperator.Identical, useTmp, conditionalCase.expr);
|
|
} else if (conditionalCase.alias !== null) {
|
|
caseExpressionTemporaryXref ??= job.allocateXrefId();
|
|
conditionalCase.expr = new AssignTemporaryExpr(conditionalCase.expr, caseExpressionTemporaryXref);
|
|
op.contextValue = new ReadTemporaryExpr(caseExpressionTemporaryXref);
|
|
}
|
|
test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.targetSlot), test);
|
|
}
|
|
op.processed = test;
|
|
op.conditions = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
const BINARY_OPERATORS = new Map([['&&', BinaryOperator.And], ['>', BinaryOperator.Bigger], ['>=', BinaryOperator.BiggerEquals], ['|', BinaryOperator.BitwiseOr], ['&', BinaryOperator.BitwiseAnd], ['/', BinaryOperator.Divide], ['=', BinaryOperator.Assign], ['==', BinaryOperator.Equals], ['===', BinaryOperator.Identical], ['<', BinaryOperator.Lower], ['<=', BinaryOperator.LowerEquals], ['-', BinaryOperator.Minus], ['%', BinaryOperator.Modulo], ['**', BinaryOperator.Exponentiation], ['*', BinaryOperator.Multiply], ['!=', BinaryOperator.NotEquals], ['!==', BinaryOperator.NotIdentical], ['??', BinaryOperator.NullishCoalesce], ['||', BinaryOperator.Or], ['+', BinaryOperator.Plus], ['in', BinaryOperator.In], ['+=', BinaryOperator.AdditionAssignment], ['-=', BinaryOperator.SubtractionAssignment], ['*=', BinaryOperator.MultiplicationAssignment], ['/=', BinaryOperator.DivisionAssignment], ['%=', BinaryOperator.RemainderAssignment], ['**=', BinaryOperator.ExponentiationAssignment], ['&&=', BinaryOperator.AndAssignment], ['||=', BinaryOperator.OrAssignment], ['??=', BinaryOperator.NullishCoalesceAssignment]]);
|
|
function namespaceForKey(namespacePrefixKey) {
|
|
const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
|
|
if (namespacePrefixKey === null) {
|
|
return Namespace.HTML;
|
|
}
|
|
return NAMESPACES.get(namespacePrefixKey) ?? Namespace.HTML;
|
|
}
|
|
function keyForNamespace(namespace) {
|
|
const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
|
|
for (const [k, n] of NAMESPACES.entries()) {
|
|
if (n === namespace) {
|
|
return k;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function prefixWithNamespace(strippedTag, namespace) {
|
|
if (namespace === Namespace.HTML) {
|
|
return strippedTag;
|
|
}
|
|
return `:${keyForNamespace(namespace)}:${strippedTag}`;
|
|
}
|
|
function literalOrArrayLiteral(value) {
|
|
if (Array.isArray(value)) {
|
|
return literalArr(value.map(literalOrArrayLiteral));
|
|
}
|
|
return literal(value);
|
|
}
|
|
|
|
function collectElementConsts(job) {
|
|
const allElementAttributes = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.ExtractedAttribute) {
|
|
const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
|
|
allElementAttributes.set(op.target, attributes);
|
|
attributes.add(op.bindingKind, op.name, op.expression, op.namespace, op.trustedValueFn);
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
if (job instanceof ComponentCompilationJob) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind == OpKind.Projection) {
|
|
const attributes = allElementAttributes.get(op.xref);
|
|
if (attributes !== undefined) {
|
|
const attrArray = serializeAttributes(attributes);
|
|
if (attrArray.entries.length > 0) {
|
|
op.attributes = attrArray;
|
|
}
|
|
}
|
|
} else if (isElementOrContainerOp(op)) {
|
|
op.attributes = getConstIndex(job, allElementAttributes, op.xref);
|
|
if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
|
|
op.emptyAttributes = getConstIndex(job, allElementAttributes, op.emptyView);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (job instanceof HostBindingCompilationJob) {
|
|
for (const [xref, attributes] of allElementAttributes.entries()) {
|
|
if (xref !== job.root.xref) {
|
|
throw new Error(`An attribute would be const collected into the host binding's template function, but is not associated with the root xref.`);
|
|
}
|
|
const attrArray = serializeAttributes(attributes);
|
|
if (attrArray.entries.length > 0) {
|
|
job.root.attributes = attrArray;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function getConstIndex(job, allElementAttributes, xref) {
|
|
const attributes = allElementAttributes.get(xref);
|
|
if (attributes !== undefined) {
|
|
const attrArray = serializeAttributes(attributes);
|
|
if (attrArray.entries.length > 0) {
|
|
return job.addConst(attrArray);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
const FLYWEIGHT_ARRAY = Object.freeze([]);
|
|
class ElementAttributes {
|
|
compatibility;
|
|
known = new Map();
|
|
byKind = new Map();
|
|
propertyBindings = null;
|
|
projectAs = null;
|
|
get attributes() {
|
|
return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
get classes() {
|
|
return this.byKind.get(BindingKind.ClassName) ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
get styles() {
|
|
return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
get bindings() {
|
|
return this.propertyBindings ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
get template() {
|
|
return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
get i18n() {
|
|
return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
|
|
}
|
|
constructor(compatibility) {
|
|
this.compatibility = compatibility;
|
|
}
|
|
isKnown(kind, name) {
|
|
const nameToValue = this.known.get(kind) ?? new Set();
|
|
this.known.set(kind, nameToValue);
|
|
if (nameToValue.has(name)) {
|
|
return true;
|
|
}
|
|
nameToValue.add(name);
|
|
return false;
|
|
}
|
|
add(kind, name, value, namespace, trustedValueFn) {
|
|
const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder && (kind === BindingKind.Attribute || kind === BindingKind.ClassName || kind === BindingKind.StyleProperty);
|
|
if (!allowDuplicates && this.isKnown(kind, name)) {
|
|
return;
|
|
}
|
|
if (name === 'ngProjectAs') {
|
|
if (value === null || !(value instanceof LiteralExpr) || value.value == null || typeof value.value?.toString() !== 'string') {
|
|
throw Error('ngProjectAs must have a string literal value');
|
|
}
|
|
this.projectAs = value.value.toString();
|
|
}
|
|
const array = this.arrayFor(kind);
|
|
array.push(...getAttributeNameLiterals(namespace, name));
|
|
if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
|
|
if (value === null) {
|
|
throw Error('Attribute, i18n attribute, & style element attributes must have a value');
|
|
}
|
|
if (trustedValueFn !== null) {
|
|
if (!isStringLiteral(value)) {
|
|
throw Error('AssertionError: extracted attribute value should be string literal');
|
|
}
|
|
array.push(taggedTemplate(trustedValueFn, new TemplateLiteralExpr([new TemplateLiteralElementExpr(value.value)], []), undefined, value.sourceSpan));
|
|
} else {
|
|
array.push(value);
|
|
}
|
|
}
|
|
}
|
|
arrayFor(kind) {
|
|
if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) {
|
|
this.propertyBindings ??= [];
|
|
return this.propertyBindings;
|
|
} else {
|
|
if (!this.byKind.has(kind)) {
|
|
this.byKind.set(kind, []);
|
|
}
|
|
return this.byKind.get(kind);
|
|
}
|
|
}
|
|
}
|
|
function getAttributeNameLiterals(namespace, name) {
|
|
const nameLiteral = literal(name);
|
|
if (namespace) {
|
|
return [literal(0), literal(namespace), nameLiteral];
|
|
}
|
|
return [nameLiteral];
|
|
}
|
|
function serializeAttributes({
|
|
attributes,
|
|
bindings,
|
|
classes,
|
|
i18n,
|
|
projectAs,
|
|
styles,
|
|
template
|
|
}) {
|
|
const attrArray = [...attributes];
|
|
if (projectAs !== null) {
|
|
const parsedR3Selector = parseSelectorToR3Selector(projectAs)[0];
|
|
attrArray.push(literal(5), literalOrArrayLiteral(parsedR3Selector));
|
|
}
|
|
if (classes.length > 0) {
|
|
attrArray.push(literal(1), ...classes);
|
|
}
|
|
if (styles.length > 0) {
|
|
attrArray.push(literal(2), ...styles);
|
|
}
|
|
if (bindings.length > 0) {
|
|
attrArray.push(literal(3), ...bindings);
|
|
}
|
|
if (template.length > 0) {
|
|
attrArray.push(literal(4), ...template);
|
|
}
|
|
if (i18n.length > 0) {
|
|
attrArray.push(literal(6), ...i18n);
|
|
}
|
|
return literalArr(attrArray);
|
|
}
|
|
|
|
function lookupElement$1(elements, xref) {
|
|
const el = elements.get(xref);
|
|
if (el === undefined) {
|
|
throw new Error('All attributes should have an element-like target.');
|
|
}
|
|
return el;
|
|
}
|
|
function convertAnimations(job) {
|
|
const elements = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (!isElementOrContainerOp(op)) {
|
|
continue;
|
|
}
|
|
elements.set(op.xref, op);
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind === OpKind.AnimationBinding) {
|
|
const createAnimationOp = getAnimationOp(op);
|
|
if (job.kind === CompilationJobKind.Host) {
|
|
unit.create.push(createAnimationOp);
|
|
} else {
|
|
OpList.insertAfter(createAnimationOp, lookupElement$1(elements, op.target));
|
|
}
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function getAnimationOp(op) {
|
|
if (op.animationBindingKind === 0) {
|
|
return createAnimationStringOp(op.name, op.target, op.name === 'animate.enter' ? "enter" : "leave", op.expression, op.securityContext, op.sourceSpan);
|
|
} else {
|
|
const expression = op.expression;
|
|
return createAnimationOp(op.name, op.target, op.name === 'animate.enter' ? "enter" : "leave", [createStatementOp(new ReturnStatement(expression, expression.sourceSpan))], op.securityContext, op.sourceSpan);
|
|
}
|
|
}
|
|
|
|
function convertI18nBindings(job) {
|
|
const i18nAttributesByElem = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.I18nAttributes) {
|
|
i18nAttributesByElem.set(op.target, op);
|
|
}
|
|
}
|
|
for (const op of unit.update) {
|
|
switch (op.kind) {
|
|
case OpKind.Property:
|
|
case OpKind.Attribute:
|
|
if (op.i18nContext === null) {
|
|
continue;
|
|
}
|
|
if (!(op.expression instanceof Interpolation)) {
|
|
continue;
|
|
}
|
|
const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
|
|
if (i18nAttributesForElem === undefined) {
|
|
throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
|
|
}
|
|
if (i18nAttributesForElem.target !== op.target) {
|
|
throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
|
|
}
|
|
const ops = [];
|
|
for (let i = 0; i < op.expression.expressions.length; i++) {
|
|
const expr = op.expression.expressions[i];
|
|
if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
|
|
throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
|
|
}
|
|
ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
|
|
}
|
|
OpList.replaceWithMany(op, ops);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function createI18nContexts(job) {
|
|
const attrContextByMessage = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
switch (op.kind) {
|
|
case OpKind.Binding:
|
|
case OpKind.Property:
|
|
case OpKind.Attribute:
|
|
case OpKind.ExtractedAttribute:
|
|
if (op.i18nMessage === null) {
|
|
continue;
|
|
}
|
|
if (!attrContextByMessage.has(op.i18nMessage)) {
|
|
const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
|
|
unit.create.push(i18nContext);
|
|
attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
|
|
}
|
|
op.i18nContext = attrContextByMessage.get(op.i18nMessage);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const blockContextByI18nBlock = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
if (op.xref === op.root) {
|
|
const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
|
|
unit.create.push(contextOp);
|
|
op.context = contextOp.xref;
|
|
blockContextByI18nBlock.set(op.xref, contextOp);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
|
|
const rootContext = blockContextByI18nBlock.get(op.root);
|
|
if (rootContext === undefined) {
|
|
throw Error('AssertionError: Root i18n block i18n context should have been created.');
|
|
}
|
|
op.context = rootContext.xref;
|
|
blockContextByI18nBlock.set(op.xref, rootContext);
|
|
}
|
|
}
|
|
}
|
|
let currentI18nOp = null;
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
currentI18nOp = op;
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
currentI18nOp = null;
|
|
break;
|
|
case OpKind.IcuStart:
|
|
if (currentI18nOp === null) {
|
|
throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
|
|
}
|
|
if (op.message.id !== currentI18nOp.message.id) {
|
|
const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.root, op.message, null);
|
|
unit.create.push(contextOp);
|
|
op.context = contextOp.xref;
|
|
} else {
|
|
op.context = currentI18nOp.context;
|
|
blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function deduplicateTextBindings(job) {
|
|
const seen = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update.reversed()) {
|
|
if (op.kind === OpKind.Binding && op.isTextAttribute) {
|
|
const seenForElement = seen.get(op.target) || new Set();
|
|
if (seenForElement.has(op.name)) {
|
|
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
if (op.name === 'style' || op.name === 'class') {
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
seenForElement.add(op.name);
|
|
seen.set(op.target, seenForElement);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function configureDeferInstructions(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.Defer) {
|
|
continue;
|
|
}
|
|
if (op.placeholderMinimumTime !== null) {
|
|
op.placeholderConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.placeholderMinimumTime]));
|
|
}
|
|
if (op.loadingMinimumTime !== null || op.loadingAfterTime !== null) {
|
|
op.loadingConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.loadingMinimumTime, op.loadingAfterTime]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveDeferTargetNames(job) {
|
|
const scopes = new Map();
|
|
function getScopeForView(view) {
|
|
if (scopes.has(view.xref)) {
|
|
return scopes.get(view.xref);
|
|
}
|
|
const scope = new Scope$1();
|
|
for (const op of view.create) {
|
|
if (!isElementOrContainerOp(op) || op.localRefs === null) {
|
|
continue;
|
|
}
|
|
if (!Array.isArray(op.localRefs)) {
|
|
throw new Error('LocalRefs were already processed, but were needed to resolve defer targets.');
|
|
}
|
|
for (const ref of op.localRefs) {
|
|
if (ref.target !== '') {
|
|
continue;
|
|
}
|
|
scope.targets.set(ref.name, {
|
|
xref: op.xref,
|
|
slot: op.handle
|
|
});
|
|
}
|
|
}
|
|
scopes.set(view.xref, scope);
|
|
return scope;
|
|
}
|
|
function resolveTrigger(deferOwnerView, op, placeholderView) {
|
|
switch (op.trigger.kind) {
|
|
case DeferTriggerKind.Idle:
|
|
case DeferTriggerKind.Never:
|
|
case DeferTriggerKind.Immediate:
|
|
case DeferTriggerKind.Timer:
|
|
return;
|
|
case DeferTriggerKind.Hover:
|
|
case DeferTriggerKind.Interaction:
|
|
case DeferTriggerKind.Viewport:
|
|
if (op.trigger.targetName === null) {
|
|
if (placeholderView === null) {
|
|
throw new Error('defer on trigger with no target name must have a placeholder block');
|
|
}
|
|
const placeholder = job.views.get(placeholderView);
|
|
if (placeholder == undefined) {
|
|
throw new Error('AssertionError: could not find placeholder view for defer on trigger');
|
|
}
|
|
for (const placeholderOp of placeholder.create) {
|
|
if (hasConsumesSlotTrait(placeholderOp) && (isElementOrContainerOp(placeholderOp) || placeholderOp.kind === OpKind.Projection)) {
|
|
op.trigger.targetXref = placeholderOp.xref;
|
|
op.trigger.targetView = placeholderView;
|
|
op.trigger.targetSlotViewSteps = -1;
|
|
op.trigger.targetSlot = placeholderOp.handle;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
let view = placeholderView !== null ? job.views.get(placeholderView) : deferOwnerView;
|
|
let step = placeholderView !== null ? -1 : 0;
|
|
while (view !== null) {
|
|
const scope = getScopeForView(view);
|
|
if (scope.targets.has(op.trigger.targetName)) {
|
|
const {
|
|
xref,
|
|
slot
|
|
} = scope.targets.get(op.trigger.targetName);
|
|
op.trigger.targetXref = xref;
|
|
op.trigger.targetView = view.xref;
|
|
op.trigger.targetSlotViewSteps = step;
|
|
op.trigger.targetSlot = slot;
|
|
return;
|
|
}
|
|
view = view.parent !== null ? job.views.get(view.parent) : null;
|
|
step++;
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Trigger kind ${op.trigger.kind} not handled`);
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
const defers = new Map();
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.Defer:
|
|
defers.set(op.xref, op);
|
|
break;
|
|
case OpKind.DeferOn:
|
|
const deferOp = defers.get(op.defer);
|
|
resolveTrigger(unit, op, op.modifier === "hydrate" ? deferOp.mainView : deferOp.placeholderView);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let Scope$1 = class Scope {
|
|
targets = new Map();
|
|
};
|
|
|
|
const REPLACEMENTS = new Map([[OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]], [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]], [OpKind.I18nEnd, [OpKind.I18nStart, OpKind.I18n]]]);
|
|
const IGNORED_OP_KINDS = new Set([OpKind.Pipe]);
|
|
function collapseEmptyInstructions(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
const opReplacements = REPLACEMENTS.get(op.kind);
|
|
if (opReplacements === undefined) {
|
|
continue;
|
|
}
|
|
const [startKind, mergedKind] = opReplacements;
|
|
let prevOp = op.prev;
|
|
while (prevOp !== null && IGNORED_OP_KINDS.has(prevOp.kind)) {
|
|
prevOp = prevOp.prev;
|
|
}
|
|
if (prevOp !== null && prevOp.kind === startKind) {
|
|
prevOp.kind = mergedKind;
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function expandSafeReads(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
transformExpressionsInOp(op, e => safeTransform(e, {
|
|
job
|
|
}), VisitorContextFlag.None);
|
|
transformExpressionsInOp(op, ternaryTransform, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
function needsTemporaryInSafeAccess(e) {
|
|
if (e instanceof UnaryOperatorExpr) {
|
|
return needsTemporaryInSafeAccess(e.expr);
|
|
} else if (e instanceof BinaryOperatorExpr) {
|
|
return needsTemporaryInSafeAccess(e.lhs) || needsTemporaryInSafeAccess(e.rhs);
|
|
} else if (e instanceof ConditionalExpr) {
|
|
if (e.falseCase && needsTemporaryInSafeAccess(e.falseCase)) return true;
|
|
return needsTemporaryInSafeAccess(e.condition) || needsTemporaryInSafeAccess(e.trueCase);
|
|
} else if (e instanceof NotExpr) {
|
|
return needsTemporaryInSafeAccess(e.condition);
|
|
} else if (e instanceof AssignTemporaryExpr) {
|
|
return needsTemporaryInSafeAccess(e.expr);
|
|
} else if (e instanceof ReadPropExpr) {
|
|
return needsTemporaryInSafeAccess(e.receiver);
|
|
} else if (e instanceof ReadKeyExpr) {
|
|
return needsTemporaryInSafeAccess(e.receiver) || needsTemporaryInSafeAccess(e.index);
|
|
} else if (e instanceof ParenthesizedExpr) {
|
|
return needsTemporaryInSafeAccess(e.expr);
|
|
}
|
|
return e instanceof InvokeFunctionExpr || e instanceof LiteralArrayExpr || e instanceof LiteralMapExpr || e instanceof SafeInvokeFunctionExpr || e instanceof PipeBindingExpr;
|
|
}
|
|
function temporariesIn(e) {
|
|
const temporaries = new Set();
|
|
transformExpressionsInExpression(e, e => {
|
|
if (e instanceof AssignTemporaryExpr) {
|
|
temporaries.add(e.xref);
|
|
}
|
|
return e;
|
|
}, VisitorContextFlag.None);
|
|
return temporaries;
|
|
}
|
|
function eliminateTemporaryAssignments(e, tmps, ctx) {
|
|
transformExpressionsInExpression(e, e => {
|
|
if (e instanceof AssignTemporaryExpr && tmps.has(e.xref)) {
|
|
const read = new ReadTemporaryExpr(e.xref);
|
|
return ctx.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder ? new AssignTemporaryExpr(read, read.xref) : read;
|
|
}
|
|
return e;
|
|
}, VisitorContextFlag.None);
|
|
return e;
|
|
}
|
|
function safeTernaryWithTemporary(guard, body, ctx) {
|
|
let result;
|
|
if (needsTemporaryInSafeAccess(guard)) {
|
|
const xref = ctx.job.allocateXrefId();
|
|
result = [new AssignTemporaryExpr(guard, xref), new ReadTemporaryExpr(xref)];
|
|
} else {
|
|
result = [guard, guard.clone()];
|
|
eliminateTemporaryAssignments(result[1], temporariesIn(result[0]), ctx);
|
|
}
|
|
return new SafeTernaryExpr(result[0], body(result[1]));
|
|
}
|
|
function isSafeAccessExpression(e) {
|
|
return e instanceof SafePropertyReadExpr || e instanceof SafeKeyedReadExpr || e instanceof SafeInvokeFunctionExpr;
|
|
}
|
|
function isUnsafeAccessExpression(e) {
|
|
return e instanceof ReadPropExpr || e instanceof ReadKeyExpr || e instanceof InvokeFunctionExpr;
|
|
}
|
|
function isAccessExpression(e) {
|
|
return isSafeAccessExpression(e) || isUnsafeAccessExpression(e);
|
|
}
|
|
function deepestSafeTernary(e) {
|
|
if (isAccessExpression(e) && e.receiver instanceof SafeTernaryExpr) {
|
|
let st = e.receiver;
|
|
while (st.expr instanceof SafeTernaryExpr) {
|
|
st = st.expr;
|
|
}
|
|
return st;
|
|
}
|
|
return null;
|
|
}
|
|
function safeTransform(e, ctx) {
|
|
if (!isAccessExpression(e)) {
|
|
return e;
|
|
}
|
|
const dst = deepestSafeTernary(e);
|
|
if (dst) {
|
|
if (e instanceof InvokeFunctionExpr) {
|
|
dst.expr = dst.expr.callFn(e.args);
|
|
return e.receiver;
|
|
}
|
|
if (e instanceof ReadPropExpr) {
|
|
dst.expr = dst.expr.prop(e.name);
|
|
return e.receiver;
|
|
}
|
|
if (e instanceof ReadKeyExpr) {
|
|
dst.expr = dst.expr.key(e.index);
|
|
return e.receiver;
|
|
}
|
|
if (e instanceof SafeInvokeFunctionExpr) {
|
|
dst.expr = safeTernaryWithTemporary(dst.expr, r => r.callFn(e.args), ctx);
|
|
return e.receiver;
|
|
}
|
|
if (e instanceof SafePropertyReadExpr) {
|
|
dst.expr = safeTernaryWithTemporary(dst.expr, r => r.prop(e.name), ctx);
|
|
return e.receiver;
|
|
}
|
|
if (e instanceof SafeKeyedReadExpr) {
|
|
dst.expr = safeTernaryWithTemporary(dst.expr, r => r.key(e.index), ctx);
|
|
return e.receiver;
|
|
}
|
|
} else {
|
|
if (e instanceof SafeInvokeFunctionExpr) {
|
|
return safeTernaryWithTemporary(e.receiver, r => r.callFn(e.args), ctx);
|
|
}
|
|
if (e instanceof SafePropertyReadExpr) {
|
|
return safeTernaryWithTemporary(e.receiver, r => r.prop(e.name), ctx);
|
|
}
|
|
if (e instanceof SafeKeyedReadExpr) {
|
|
return safeTernaryWithTemporary(e.receiver, r => r.key(e.index), ctx);
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
function ternaryTransform(e) {
|
|
if (!(e instanceof SafeTernaryExpr)) {
|
|
return e;
|
|
}
|
|
return new ParenthesizedExpr(new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr));
|
|
}
|
|
|
|
const ESCAPE$1 = '\uFFFD';
|
|
const ELEMENT_MARKER = '#';
|
|
const TEMPLATE_MARKER = '*';
|
|
const TAG_CLOSE_MARKER = '/';
|
|
const CONTEXT_MARKER = ':';
|
|
const LIST_START_MARKER = '[';
|
|
const LIST_END_MARKER = ']';
|
|
const LIST_DELIMITER = '|';
|
|
function extractI18nMessages(job) {
|
|
const i18nMessagesByContext = new Map();
|
|
const i18nBlocks = new Map();
|
|
const i18nContexts = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nContext:
|
|
const i18nMessageOp = createI18nMessage(job, op);
|
|
unit.create.push(i18nMessageOp);
|
|
i18nMessagesByContext.set(op.xref, i18nMessageOp);
|
|
i18nContexts.set(op.xref, op);
|
|
break;
|
|
case OpKind.I18nStart:
|
|
i18nBlocks.set(op.xref, op);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let currentIcu = null;
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.IcuStart:
|
|
currentIcu = op;
|
|
OpList.remove(op);
|
|
const icuContext = i18nContexts.get(op.context);
|
|
if (icuContext.contextKind !== I18nContextKind.Icu) {
|
|
continue;
|
|
}
|
|
const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
|
|
if (i18nBlock.context === icuContext.xref) {
|
|
continue;
|
|
}
|
|
const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
|
|
const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
|
|
if (rootMessage === undefined) {
|
|
throw Error('AssertionError: ICU sub-message should belong to a root message.');
|
|
}
|
|
const subMessage = i18nMessagesByContext.get(icuContext.xref);
|
|
subMessage.messagePlaceholder = op.messagePlaceholder;
|
|
rootMessage.subMessages.push(subMessage.xref);
|
|
break;
|
|
case OpKind.IcuEnd:
|
|
currentIcu = null;
|
|
OpList.remove(op);
|
|
break;
|
|
case OpKind.IcuPlaceholder:
|
|
if (currentIcu === null || currentIcu.context == null) {
|
|
throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
|
|
}
|
|
const msg = i18nMessagesByContext.get(currentIcu.context);
|
|
msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
|
|
OpList.remove(op);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function createI18nMessage(job, context, messagePlaceholder) {
|
|
let formattedParams = formatParams(context.params);
|
|
const formattedPostprocessingParams = formatParams(context.postprocessingParams);
|
|
let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
|
|
return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
|
|
}
|
|
function formatIcuPlaceholder(op) {
|
|
if (op.strings.length !== op.expressionPlaceholders.length + 1) {
|
|
throw Error(`AssertionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
|
|
}
|
|
const values = op.expressionPlaceholders.map(formatValue);
|
|
return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
|
|
}
|
|
function formatParams(params) {
|
|
const formattedParams = new Map();
|
|
for (const [placeholder, placeholderValues] of params) {
|
|
const serializedValues = formatParamValues(placeholderValues);
|
|
if (serializedValues !== null) {
|
|
formattedParams.set(placeholder, literal(serializedValues));
|
|
}
|
|
}
|
|
return formattedParams;
|
|
}
|
|
function formatParamValues(values) {
|
|
if (values.length === 0) {
|
|
return null;
|
|
}
|
|
const serializedValues = values.map(value => formatValue(value));
|
|
return serializedValues.length === 1 ? serializedValues[0] : `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
|
|
}
|
|
function formatValue(value) {
|
|
if (value.flags & I18nParamValueFlags.ElementTag && value.flags & I18nParamValueFlags.TemplateTag) {
|
|
if (typeof value.value !== 'object') {
|
|
throw Error('AssertionError: Expected i18n param value to have an element and template slot');
|
|
}
|
|
const elementValue = formatValue({
|
|
...value,
|
|
value: value.value.element,
|
|
flags: value.flags & ~I18nParamValueFlags.TemplateTag
|
|
});
|
|
const templateValue = formatValue({
|
|
...value,
|
|
value: value.value.template,
|
|
flags: value.flags & ~I18nParamValueFlags.ElementTag
|
|
});
|
|
if (value.flags & I18nParamValueFlags.OpenTag && value.flags & I18nParamValueFlags.CloseTag) {
|
|
return `${templateValue}${elementValue}${templateValue}`;
|
|
}
|
|
return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` : `${templateValue}${elementValue}`;
|
|
}
|
|
if (value.flags & I18nParamValueFlags.OpenTag && value.flags & I18nParamValueFlags.CloseTag) {
|
|
return `${formatValue({
|
|
...value,
|
|
flags: value.flags & ~I18nParamValueFlags.CloseTag
|
|
})}${formatValue({
|
|
...value,
|
|
flags: value.flags & ~I18nParamValueFlags.OpenTag
|
|
})}`;
|
|
}
|
|
if (value.flags === I18nParamValueFlags.None) {
|
|
return `${value.value}`;
|
|
}
|
|
let tagMarker = '';
|
|
let closeMarker = '';
|
|
if (value.flags & I18nParamValueFlags.ElementTag) {
|
|
tagMarker = ELEMENT_MARKER;
|
|
} else if (value.flags & I18nParamValueFlags.TemplateTag) {
|
|
tagMarker = TEMPLATE_MARKER;
|
|
}
|
|
if (tagMarker !== '') {
|
|
closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
|
|
}
|
|
const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
|
|
return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
|
|
}
|
|
|
|
function generateAdvance(job) {
|
|
for (const unit of job.units) {
|
|
const slotMap = new Map();
|
|
for (const op of unit.create) {
|
|
if (!hasConsumesSlotTrait(op)) {
|
|
continue;
|
|
} else if (op.handle.slot === null) {
|
|
throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`);
|
|
}
|
|
slotMap.set(op.xref, op.handle.slot);
|
|
}
|
|
let slotContext = 0;
|
|
for (const op of unit.update) {
|
|
let consumer = null;
|
|
if (hasDependsOnSlotContextTrait(op)) {
|
|
consumer = op;
|
|
} else {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (consumer === null && hasDependsOnSlotContextTrait(expr)) {
|
|
consumer = expr;
|
|
}
|
|
});
|
|
}
|
|
if (consumer === null) {
|
|
continue;
|
|
}
|
|
if (!slotMap.has(consumer.target)) {
|
|
throw new Error(`AssertionError: reference to unknown slot for target ${consumer.target}`);
|
|
}
|
|
const slot = slotMap.get(consumer.target);
|
|
if (slotContext !== slot) {
|
|
const delta = slot - slotContext;
|
|
if (delta < 0) {
|
|
throw new Error(`AssertionError: slot counter should never need to move backwards`);
|
|
}
|
|
OpList.insertBefore(createAdvanceOp(delta, consumer.sourceSpan), op);
|
|
slotContext = slot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateLocalLetReferences(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
if (op.kind !== OpKind.StoreLet) {
|
|
continue;
|
|
}
|
|
const variable = {
|
|
kind: SemanticVariableKind.Identifier,
|
|
name: null,
|
|
identifier: op.declaredName,
|
|
local: true
|
|
};
|
|
OpList.replace(op, createVariableOp(job.allocateXrefId(), variable, new StoreLetExpr(op.target, op.value, op.sourceSpan), VariableFlags.None));
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateProjectionDefs(job) {
|
|
const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder;
|
|
const selectors = [];
|
|
let projectionSlotIndex = 0;
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.Projection) {
|
|
selectors.push(op.selector);
|
|
op.projectionSlotIndex = projectionSlotIndex++;
|
|
}
|
|
}
|
|
}
|
|
if (selectors.length > 0) {
|
|
let defExpr = null;
|
|
if (selectors.length > 1 || selectors[0] !== '*') {
|
|
const def = selectors.map(s => s === '*' ? s : parseSelectorToR3Selector(s));
|
|
defExpr = job.pool.getConstLiteral(literalOrArrayLiteral(def), share);
|
|
}
|
|
job.contentSelectors = job.pool.getConstLiteral(literalOrArrayLiteral(selectors), share);
|
|
job.root.create.prepend([createProjectionDefOp(defExpr)]);
|
|
}
|
|
}
|
|
|
|
function generateVariables(job) {
|
|
recursivelyProcessView(job.root, null);
|
|
}
|
|
function recursivelyProcessView(view, parentScope) {
|
|
const scope = getScopeForView(view, parentScope);
|
|
for (const op of view.create) {
|
|
switch (op.kind) {
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
break;
|
|
case OpKind.Projection:
|
|
if (op.fallbackView !== null) {
|
|
recursivelyProcessView(view.job.views.get(op.fallbackView), scope);
|
|
}
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
if (op.emptyView) {
|
|
recursivelyProcessView(view.job.views.get(op.emptyView), scope);
|
|
}
|
|
if (op.trackByOps !== null) {
|
|
op.trackByOps.prepend(generateVariablesInScopeForView(view, scope, false));
|
|
}
|
|
break;
|
|
case OpKind.Animation:
|
|
case OpKind.AnimationListener:
|
|
case OpKind.Listener:
|
|
case OpKind.TwoWayListener:
|
|
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope, true));
|
|
break;
|
|
}
|
|
}
|
|
view.update.prepend(generateVariablesInScopeForView(view, scope, false));
|
|
}
|
|
function getScopeForView(view, parent) {
|
|
const scope = {
|
|
view: view.xref,
|
|
viewContextVariable: {
|
|
kind: SemanticVariableKind.Context,
|
|
name: null,
|
|
view: view.xref
|
|
},
|
|
contextVariables: new Map(),
|
|
aliases: view.aliases,
|
|
references: [],
|
|
letDeclarations: [],
|
|
parent
|
|
};
|
|
for (const identifier of view.contextVariables.keys()) {
|
|
scope.contextVariables.set(identifier, {
|
|
kind: SemanticVariableKind.Identifier,
|
|
name: null,
|
|
identifier,
|
|
local: false
|
|
});
|
|
}
|
|
for (const op of view.create) {
|
|
switch (op.kind) {
|
|
case OpKind.ElementStart:
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
if (!Array.isArray(op.localRefs)) {
|
|
throw new Error(`AssertionError: expected localRefs to be an array`);
|
|
}
|
|
for (let offset = 0; offset < op.localRefs.length; offset++) {
|
|
scope.references.push({
|
|
name: op.localRefs[offset].name,
|
|
targetId: op.xref,
|
|
targetSlot: op.handle,
|
|
offset,
|
|
variable: {
|
|
kind: SemanticVariableKind.Identifier,
|
|
name: null,
|
|
identifier: op.localRefs[offset].name,
|
|
local: false
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case OpKind.DeclareLet:
|
|
scope.letDeclarations.push({
|
|
targetId: op.xref,
|
|
targetSlot: op.handle,
|
|
variable: {
|
|
kind: SemanticVariableKind.Identifier,
|
|
name: null,
|
|
identifier: op.declaredName,
|
|
local: false
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return scope;
|
|
}
|
|
function generateVariablesInScopeForView(view, scope, isCallback) {
|
|
const newOps = [];
|
|
if (scope.view !== view.xref) {
|
|
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr(), VariableFlags.None));
|
|
}
|
|
const scopeView = view.job.views.get(scope.view);
|
|
for (const [name, value] of scopeView.contextVariables) {
|
|
const context = new ContextExpr(scope.view);
|
|
const variable = value === CTX_REF ? context : new ReadPropExpr(context, value);
|
|
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable, VariableFlags.None));
|
|
}
|
|
for (const alias of scopeView.aliases) {
|
|
newOps.push(createVariableOp(view.job.allocateXrefId(), alias, alias.expression.clone(), VariableFlags.AlwaysInline));
|
|
}
|
|
for (const ref of scope.references) {
|
|
newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.targetSlot, ref.offset), VariableFlags.None));
|
|
}
|
|
if (scope.view !== view.xref || isCallback) {
|
|
for (const decl of scope.letDeclarations) {
|
|
newOps.push(createVariableOp(view.job.allocateXrefId(), decl.variable, new ContextLetReferenceExpr(decl.targetId, decl.targetSlot), VariableFlags.None));
|
|
}
|
|
}
|
|
if (scope.parent !== null) {
|
|
newOps.push(...generateVariablesInScopeForView(view, scope.parent, false));
|
|
}
|
|
return newOps;
|
|
}
|
|
|
|
function collectConstExpressions(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (!(expr instanceof ConstCollectedExpr)) {
|
|
return expr;
|
|
}
|
|
return literal(job.addConst(expr.expr));
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
|
|
const STYLE_DOT = 'style.';
|
|
const CLASS_DOT = 'class.';
|
|
const STYLE_BANG = 'style!';
|
|
const CLASS_BANG = 'class!';
|
|
const BANG_IMPORTANT = '!important';
|
|
function parseHostStyleProperties(job) {
|
|
for (const op of job.root.update) {
|
|
if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
|
|
continue;
|
|
}
|
|
if (op.name.endsWith(BANG_IMPORTANT)) {
|
|
op.name = op.name.substring(0, op.name.length - BANG_IMPORTANT.length);
|
|
}
|
|
if (op.name.startsWith(STYLE_DOT)) {
|
|
op.bindingKind = BindingKind.StyleProperty;
|
|
op.name = op.name.substring(STYLE_DOT.length);
|
|
if (!isCssCustomProperty(op.name)) {
|
|
op.name = hyphenate$1(op.name);
|
|
}
|
|
const {
|
|
property,
|
|
suffix
|
|
} = parseProperty(op.name);
|
|
op.name = property;
|
|
op.unit = suffix;
|
|
} else if (op.name.startsWith(STYLE_BANG)) {
|
|
op.bindingKind = BindingKind.StyleProperty;
|
|
op.name = 'style';
|
|
} else if (op.name.startsWith(CLASS_DOT)) {
|
|
op.bindingKind = BindingKind.ClassName;
|
|
op.name = parseProperty(op.name.substring(CLASS_DOT.length)).property;
|
|
} else if (op.name.startsWith(CLASS_BANG)) {
|
|
op.bindingKind = BindingKind.ClassName;
|
|
op.name = parseProperty(op.name.substring(CLASS_BANG.length)).property;
|
|
}
|
|
}
|
|
}
|
|
function isCssCustomProperty(name) {
|
|
return name.startsWith('--');
|
|
}
|
|
function hyphenate$1(value) {
|
|
return value.replace(/[a-z][A-Z]/g, v => {
|
|
return v.charAt(0) + '-' + v.charAt(1);
|
|
}).toLowerCase();
|
|
}
|
|
function parseProperty(name) {
|
|
const overrideIndex = name.indexOf('!important');
|
|
if (overrideIndex !== -1) {
|
|
name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
|
|
}
|
|
let suffix = null;
|
|
let property = name;
|
|
const unitIndex = name.lastIndexOf('.');
|
|
if (unitIndex > 0) {
|
|
suffix = name.slice(unitIndex + 1);
|
|
property = name.substring(0, unitIndex);
|
|
}
|
|
return {
|
|
property,
|
|
suffix
|
|
};
|
|
}
|
|
|
|
function mapLiteral(obj, quoted = false) {
|
|
return literalMap(Object.keys(obj).map(key => ({
|
|
key,
|
|
quoted,
|
|
value: obj[key]
|
|
})));
|
|
}
|
|
|
|
class IcuSerializerVisitor {
|
|
visitText(text) {
|
|
return text.value;
|
|
}
|
|
visitContainer(container) {
|
|
return container.children.map(child => child.visit(this)).join('');
|
|
}
|
|
visitIcu(icu) {
|
|
const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
|
const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
|
|
return result;
|
|
}
|
|
visitTagPlaceholder(ph) {
|
|
return ph.isVoid ? this.formatPh(ph.startName) : `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
|
}
|
|
visitPlaceholder(ph) {
|
|
return this.formatPh(ph.name);
|
|
}
|
|
visitBlockPlaceholder(ph) {
|
|
return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
return this.formatPh(ph.name);
|
|
}
|
|
formatPh(value) {
|
|
return `{${formatI18nPlaceholderName(value, false)}}`;
|
|
}
|
|
}
|
|
const serializer = new IcuSerializerVisitor();
|
|
function serializeIcuNode(icu) {
|
|
return icu.visit(serializer);
|
|
}
|
|
|
|
class NodeWithI18n {
|
|
sourceSpan;
|
|
i18n;
|
|
constructor(sourceSpan, i18n) {
|
|
this.sourceSpan = sourceSpan;
|
|
this.i18n = i18n;
|
|
}
|
|
}
|
|
class Text extends NodeWithI18n {
|
|
value;
|
|
tokens;
|
|
constructor(value, sourceSpan, tokens, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.value = value;
|
|
this.tokens = tokens;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitText(this, context);
|
|
}
|
|
}
|
|
class Expansion extends NodeWithI18n {
|
|
switchValue;
|
|
type;
|
|
cases;
|
|
switchValueSourceSpan;
|
|
constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.switchValue = switchValue;
|
|
this.type = type;
|
|
this.cases = cases;
|
|
this.switchValueSourceSpan = switchValueSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitExpansion(this, context);
|
|
}
|
|
}
|
|
class ExpansionCase {
|
|
value;
|
|
expression;
|
|
sourceSpan;
|
|
valueSourceSpan;
|
|
expSourceSpan;
|
|
constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
|
|
this.value = value;
|
|
this.expression = expression;
|
|
this.sourceSpan = sourceSpan;
|
|
this.valueSourceSpan = valueSourceSpan;
|
|
this.expSourceSpan = expSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitExpansionCase(this, context);
|
|
}
|
|
}
|
|
class Attribute extends NodeWithI18n {
|
|
name;
|
|
value;
|
|
keySpan;
|
|
valueSpan;
|
|
valueTokens;
|
|
constructor(name, value, sourceSpan, keySpan, valueSpan, valueTokens, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.name = name;
|
|
this.value = value;
|
|
this.keySpan = keySpan;
|
|
this.valueSpan = valueSpan;
|
|
this.valueTokens = valueTokens;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitAttribute(this, context);
|
|
}
|
|
}
|
|
class Element extends NodeWithI18n {
|
|
name;
|
|
attrs;
|
|
directives;
|
|
children;
|
|
isSelfClosing;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
isVoid;
|
|
constructor(name, attrs, directives, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan = null, isVoid, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.name = name;
|
|
this.attrs = attrs;
|
|
this.directives = directives;
|
|
this.children = children;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
this.isVoid = isVoid;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitElement(this, context);
|
|
}
|
|
}
|
|
class Comment {
|
|
value;
|
|
sourceSpan;
|
|
constructor(value, sourceSpan) {
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitComment(this, context);
|
|
}
|
|
}
|
|
class Block extends NodeWithI18n {
|
|
name;
|
|
parameters;
|
|
children;
|
|
nameSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.name = name;
|
|
this.parameters = parameters;
|
|
this.children = children;
|
|
this.nameSpan = nameSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitBlock(this, context);
|
|
}
|
|
}
|
|
class Component extends NodeWithI18n {
|
|
componentName;
|
|
tagName;
|
|
fullName;
|
|
attrs;
|
|
directives;
|
|
children;
|
|
isSelfClosing;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(componentName, tagName, fullName, attrs, directives, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
|
|
super(sourceSpan, i18n);
|
|
this.componentName = componentName;
|
|
this.tagName = tagName;
|
|
this.fullName = fullName;
|
|
this.attrs = attrs;
|
|
this.directives = directives;
|
|
this.children = children;
|
|
this.isSelfClosing = isSelfClosing;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitComponent(this, context);
|
|
}
|
|
}
|
|
class Directive {
|
|
name;
|
|
attrs;
|
|
sourceSpan;
|
|
startSourceSpan;
|
|
endSourceSpan;
|
|
constructor(name, attrs, sourceSpan, startSourceSpan, endSourceSpan = null) {
|
|
this.name = name;
|
|
this.attrs = attrs;
|
|
this.sourceSpan = sourceSpan;
|
|
this.startSourceSpan = startSourceSpan;
|
|
this.endSourceSpan = endSourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitDirective(this, context);
|
|
}
|
|
}
|
|
class BlockParameter {
|
|
expression;
|
|
sourceSpan;
|
|
constructor(expression, sourceSpan) {
|
|
this.expression = expression;
|
|
this.sourceSpan = sourceSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitBlockParameter(this, context);
|
|
}
|
|
}
|
|
class LetDeclaration {
|
|
name;
|
|
value;
|
|
sourceSpan;
|
|
nameSpan;
|
|
valueSpan;
|
|
constructor(name, value, sourceSpan, nameSpan, valueSpan) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.sourceSpan = sourceSpan;
|
|
this.nameSpan = nameSpan;
|
|
this.valueSpan = valueSpan;
|
|
}
|
|
visit(visitor, context) {
|
|
return visitor.visitLetDeclaration(this, context);
|
|
}
|
|
}
|
|
function visitAll(visitor, nodes, context = null) {
|
|
const result = [];
|
|
const visit = visitor.visit ? ast => visitor.visit(ast, context) || ast.visit(visitor, context) : ast => ast.visit(visitor, context);
|
|
nodes.forEach(ast => {
|
|
const astResult = visit(ast);
|
|
if (astResult) {
|
|
result.push(astResult);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
class RecursiveVisitor {
|
|
constructor() {}
|
|
visitElement(ast, context) {
|
|
this.visitChildren(context, visit => {
|
|
visit(ast.attrs);
|
|
visit(ast.directives);
|
|
visit(ast.children);
|
|
});
|
|
}
|
|
visitAttribute(ast, context) {}
|
|
visitText(ast, context) {}
|
|
visitComment(ast, context) {}
|
|
visitExpansion(ast, context) {
|
|
return this.visitChildren(context, visit => {
|
|
visit(ast.cases);
|
|
});
|
|
}
|
|
visitExpansionCase(ast, context) {}
|
|
visitBlock(block, context) {
|
|
this.visitChildren(context, visit => {
|
|
visit(block.parameters);
|
|
visit(block.children);
|
|
});
|
|
}
|
|
visitBlockParameter(ast, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
this.visitChildren(context, visit => {
|
|
visit(component.attrs);
|
|
visit(component.children);
|
|
});
|
|
}
|
|
visitDirective(directive, context) {
|
|
this.visitChildren(context, visit => {
|
|
visit(directive.attrs);
|
|
});
|
|
}
|
|
visitChildren(context, cb) {
|
|
let results = [];
|
|
let t = this;
|
|
function visit(children) {
|
|
if (children) results.push(visitAll(t, children, context));
|
|
}
|
|
cb(visit);
|
|
return Array.prototype.concat.apply([], results);
|
|
}
|
|
}
|
|
|
|
const NAMED_ENTITIES = {
|
|
'AElig': '\u00C6',
|
|
'AMP': '\u0026',
|
|
'amp': '\u0026',
|
|
'Aacute': '\u00C1',
|
|
'Abreve': '\u0102',
|
|
'Acirc': '\u00C2',
|
|
'Acy': '\u0410',
|
|
'Afr': '\uD835\uDD04',
|
|
'Agrave': '\u00C0',
|
|
'Alpha': '\u0391',
|
|
'Amacr': '\u0100',
|
|
'And': '\u2A53',
|
|
'Aogon': '\u0104',
|
|
'Aopf': '\uD835\uDD38',
|
|
'ApplyFunction': '\u2061',
|
|
'af': '\u2061',
|
|
'Aring': '\u00C5',
|
|
'angst': '\u00C5',
|
|
'Ascr': '\uD835\uDC9C',
|
|
'Assign': '\u2254',
|
|
'colone': '\u2254',
|
|
'coloneq': '\u2254',
|
|
'Atilde': '\u00C3',
|
|
'Auml': '\u00C4',
|
|
'Backslash': '\u2216',
|
|
'setminus': '\u2216',
|
|
'setmn': '\u2216',
|
|
'smallsetminus': '\u2216',
|
|
'ssetmn': '\u2216',
|
|
'Barv': '\u2AE7',
|
|
'Barwed': '\u2306',
|
|
'doublebarwedge': '\u2306',
|
|
'Bcy': '\u0411',
|
|
'Because': '\u2235',
|
|
'becaus': '\u2235',
|
|
'because': '\u2235',
|
|
'Bernoullis': '\u212C',
|
|
'Bscr': '\u212C',
|
|
'bernou': '\u212C',
|
|
'Beta': '\u0392',
|
|
'Bfr': '\uD835\uDD05',
|
|
'Bopf': '\uD835\uDD39',
|
|
'Breve': '\u02D8',
|
|
'breve': '\u02D8',
|
|
'Bumpeq': '\u224E',
|
|
'HumpDownHump': '\u224E',
|
|
'bump': '\u224E',
|
|
'CHcy': '\u0427',
|
|
'COPY': '\u00A9',
|
|
'copy': '\u00A9',
|
|
'Cacute': '\u0106',
|
|
'Cap': '\u22D2',
|
|
'CapitalDifferentialD': '\u2145',
|
|
'DD': '\u2145',
|
|
'Cayleys': '\u212D',
|
|
'Cfr': '\u212D',
|
|
'Ccaron': '\u010C',
|
|
'Ccedil': '\u00C7',
|
|
'Ccirc': '\u0108',
|
|
'Cconint': '\u2230',
|
|
'Cdot': '\u010A',
|
|
'Cedilla': '\u00B8',
|
|
'cedil': '\u00B8',
|
|
'CenterDot': '\u00B7',
|
|
'centerdot': '\u00B7',
|
|
'middot': '\u00B7',
|
|
'Chi': '\u03A7',
|
|
'CircleDot': '\u2299',
|
|
'odot': '\u2299',
|
|
'CircleMinus': '\u2296',
|
|
'ominus': '\u2296',
|
|
'CirclePlus': '\u2295',
|
|
'oplus': '\u2295',
|
|
'CircleTimes': '\u2297',
|
|
'otimes': '\u2297',
|
|
'ClockwiseContourIntegral': '\u2232',
|
|
'cwconint': '\u2232',
|
|
'CloseCurlyDoubleQuote': '\u201D',
|
|
'rdquo': '\u201D',
|
|
'rdquor': '\u201D',
|
|
'CloseCurlyQuote': '\u2019',
|
|
'rsquo': '\u2019',
|
|
'rsquor': '\u2019',
|
|
'Colon': '\u2237',
|
|
'Proportion': '\u2237',
|
|
'Colone': '\u2A74',
|
|
'Congruent': '\u2261',
|
|
'equiv': '\u2261',
|
|
'Conint': '\u222F',
|
|
'DoubleContourIntegral': '\u222F',
|
|
'ContourIntegral': '\u222E',
|
|
'conint': '\u222E',
|
|
'oint': '\u222E',
|
|
'Copf': '\u2102',
|
|
'complexes': '\u2102',
|
|
'Coproduct': '\u2210',
|
|
'coprod': '\u2210',
|
|
'CounterClockwiseContourIntegral': '\u2233',
|
|
'awconint': '\u2233',
|
|
'Cross': '\u2A2F',
|
|
'Cscr': '\uD835\uDC9E',
|
|
'Cup': '\u22D3',
|
|
'CupCap': '\u224D',
|
|
'asympeq': '\u224D',
|
|
'DDotrahd': '\u2911',
|
|
'DJcy': '\u0402',
|
|
'DScy': '\u0405',
|
|
'DZcy': '\u040F',
|
|
'Dagger': '\u2021',
|
|
'ddagger': '\u2021',
|
|
'Darr': '\u21A1',
|
|
'Dashv': '\u2AE4',
|
|
'DoubleLeftTee': '\u2AE4',
|
|
'Dcaron': '\u010E',
|
|
'Dcy': '\u0414',
|
|
'Del': '\u2207',
|
|
'nabla': '\u2207',
|
|
'Delta': '\u0394',
|
|
'Dfr': '\uD835\uDD07',
|
|
'DiacriticalAcute': '\u00B4',
|
|
'acute': '\u00B4',
|
|
'DiacriticalDot': '\u02D9',
|
|
'dot': '\u02D9',
|
|
'DiacriticalDoubleAcute': '\u02DD',
|
|
'dblac': '\u02DD',
|
|
'DiacriticalGrave': '\u0060',
|
|
'grave': '\u0060',
|
|
'DiacriticalTilde': '\u02DC',
|
|
'tilde': '\u02DC',
|
|
'Diamond': '\u22C4',
|
|
'diam': '\u22C4',
|
|
'diamond': '\u22C4',
|
|
'DifferentialD': '\u2146',
|
|
'dd': '\u2146',
|
|
'Dopf': '\uD835\uDD3B',
|
|
'Dot': '\u00A8',
|
|
'DoubleDot': '\u00A8',
|
|
'die': '\u00A8',
|
|
'uml': '\u00A8',
|
|
'DotDot': '\u20DC',
|
|
'DotEqual': '\u2250',
|
|
'doteq': '\u2250',
|
|
'esdot': '\u2250',
|
|
'DoubleDownArrow': '\u21D3',
|
|
'Downarrow': '\u21D3',
|
|
'dArr': '\u21D3',
|
|
'DoubleLeftArrow': '\u21D0',
|
|
'Leftarrow': '\u21D0',
|
|
'lArr': '\u21D0',
|
|
'DoubleLeftRightArrow': '\u21D4',
|
|
'Leftrightarrow': '\u21D4',
|
|
'hArr': '\u21D4',
|
|
'iff': '\u21D4',
|
|
'DoubleLongLeftArrow': '\u27F8',
|
|
'Longleftarrow': '\u27F8',
|
|
'xlArr': '\u27F8',
|
|
'DoubleLongLeftRightArrow': '\u27FA',
|
|
'Longleftrightarrow': '\u27FA',
|
|
'xhArr': '\u27FA',
|
|
'DoubleLongRightArrow': '\u27F9',
|
|
'Longrightarrow': '\u27F9',
|
|
'xrArr': '\u27F9',
|
|
'DoubleRightArrow': '\u21D2',
|
|
'Implies': '\u21D2',
|
|
'Rightarrow': '\u21D2',
|
|
'rArr': '\u21D2',
|
|
'DoubleRightTee': '\u22A8',
|
|
'vDash': '\u22A8',
|
|
'DoubleUpArrow': '\u21D1',
|
|
'Uparrow': '\u21D1',
|
|
'uArr': '\u21D1',
|
|
'DoubleUpDownArrow': '\u21D5',
|
|
'Updownarrow': '\u21D5',
|
|
'vArr': '\u21D5',
|
|
'DoubleVerticalBar': '\u2225',
|
|
'par': '\u2225',
|
|
'parallel': '\u2225',
|
|
'shortparallel': '\u2225',
|
|
'spar': '\u2225',
|
|
'DownArrow': '\u2193',
|
|
'ShortDownArrow': '\u2193',
|
|
'darr': '\u2193',
|
|
'downarrow': '\u2193',
|
|
'DownArrowBar': '\u2913',
|
|
'DownArrowUpArrow': '\u21F5',
|
|
'duarr': '\u21F5',
|
|
'DownBreve': '\u0311',
|
|
'DownLeftRightVector': '\u2950',
|
|
'DownLeftTeeVector': '\u295E',
|
|
'DownLeftVector': '\u21BD',
|
|
'leftharpoondown': '\u21BD',
|
|
'lhard': '\u21BD',
|
|
'DownLeftVectorBar': '\u2956',
|
|
'DownRightTeeVector': '\u295F',
|
|
'DownRightVector': '\u21C1',
|
|
'rhard': '\u21C1',
|
|
'rightharpoondown': '\u21C1',
|
|
'DownRightVectorBar': '\u2957',
|
|
'DownTee': '\u22A4',
|
|
'top': '\u22A4',
|
|
'DownTeeArrow': '\u21A7',
|
|
'mapstodown': '\u21A7',
|
|
'Dscr': '\uD835\uDC9F',
|
|
'Dstrok': '\u0110',
|
|
'ENG': '\u014A',
|
|
'ETH': '\u00D0',
|
|
'Eacute': '\u00C9',
|
|
'Ecaron': '\u011A',
|
|
'Ecirc': '\u00CA',
|
|
'Ecy': '\u042D',
|
|
'Edot': '\u0116',
|
|
'Efr': '\uD835\uDD08',
|
|
'Egrave': '\u00C8',
|
|
'Element': '\u2208',
|
|
'in': '\u2208',
|
|
'isin': '\u2208',
|
|
'isinv': '\u2208',
|
|
'Emacr': '\u0112',
|
|
'EmptySmallSquare': '\u25FB',
|
|
'EmptyVerySmallSquare': '\u25AB',
|
|
'Eogon': '\u0118',
|
|
'Eopf': '\uD835\uDD3C',
|
|
'Epsilon': '\u0395',
|
|
'Equal': '\u2A75',
|
|
'EqualTilde': '\u2242',
|
|
'eqsim': '\u2242',
|
|
'esim': '\u2242',
|
|
'Equilibrium': '\u21CC',
|
|
'rightleftharpoons': '\u21CC',
|
|
'rlhar': '\u21CC',
|
|
'Escr': '\u2130',
|
|
'expectation': '\u2130',
|
|
'Esim': '\u2A73',
|
|
'Eta': '\u0397',
|
|
'Euml': '\u00CB',
|
|
'Exists': '\u2203',
|
|
'exist': '\u2203',
|
|
'ExponentialE': '\u2147',
|
|
'ee': '\u2147',
|
|
'exponentiale': '\u2147',
|
|
'Fcy': '\u0424',
|
|
'Ffr': '\uD835\uDD09',
|
|
'FilledSmallSquare': '\u25FC',
|
|
'FilledVerySmallSquare': '\u25AA',
|
|
'blacksquare': '\u25AA',
|
|
'squarf': '\u25AA',
|
|
'squf': '\u25AA',
|
|
'Fopf': '\uD835\uDD3D',
|
|
'ForAll': '\u2200',
|
|
'forall': '\u2200',
|
|
'Fouriertrf': '\u2131',
|
|
'Fscr': '\u2131',
|
|
'GJcy': '\u0403',
|
|
'GT': '\u003E',
|
|
'gt': '\u003E',
|
|
'Gamma': '\u0393',
|
|
'Gammad': '\u03DC',
|
|
'Gbreve': '\u011E',
|
|
'Gcedil': '\u0122',
|
|
'Gcirc': '\u011C',
|
|
'Gcy': '\u0413',
|
|
'Gdot': '\u0120',
|
|
'Gfr': '\uD835\uDD0A',
|
|
'Gg': '\u22D9',
|
|
'ggg': '\u22D9',
|
|
'Gopf': '\uD835\uDD3E',
|
|
'GreaterEqual': '\u2265',
|
|
'ge': '\u2265',
|
|
'geq': '\u2265',
|
|
'GreaterEqualLess': '\u22DB',
|
|
'gel': '\u22DB',
|
|
'gtreqless': '\u22DB',
|
|
'GreaterFullEqual': '\u2267',
|
|
'gE': '\u2267',
|
|
'geqq': '\u2267',
|
|
'GreaterGreater': '\u2AA2',
|
|
'GreaterLess': '\u2277',
|
|
'gl': '\u2277',
|
|
'gtrless': '\u2277',
|
|
'GreaterSlantEqual': '\u2A7E',
|
|
'geqslant': '\u2A7E',
|
|
'ges': '\u2A7E',
|
|
'GreaterTilde': '\u2273',
|
|
'gsim': '\u2273',
|
|
'gtrsim': '\u2273',
|
|
'Gscr': '\uD835\uDCA2',
|
|
'Gt': '\u226B',
|
|
'NestedGreaterGreater': '\u226B',
|
|
'gg': '\u226B',
|
|
'HARDcy': '\u042A',
|
|
'Hacek': '\u02C7',
|
|
'caron': '\u02C7',
|
|
'Hat': '\u005E',
|
|
'Hcirc': '\u0124',
|
|
'Hfr': '\u210C',
|
|
'Poincareplane': '\u210C',
|
|
'HilbertSpace': '\u210B',
|
|
'Hscr': '\u210B',
|
|
'hamilt': '\u210B',
|
|
'Hopf': '\u210D',
|
|
'quaternions': '\u210D',
|
|
'HorizontalLine': '\u2500',
|
|
'boxh': '\u2500',
|
|
'Hstrok': '\u0126',
|
|
'HumpEqual': '\u224F',
|
|
'bumpe': '\u224F',
|
|
'bumpeq': '\u224F',
|
|
'IEcy': '\u0415',
|
|
'IJlig': '\u0132',
|
|
'IOcy': '\u0401',
|
|
'Iacute': '\u00CD',
|
|
'Icirc': '\u00CE',
|
|
'Icy': '\u0418',
|
|
'Idot': '\u0130',
|
|
'Ifr': '\u2111',
|
|
'Im': '\u2111',
|
|
'image': '\u2111',
|
|
'imagpart': '\u2111',
|
|
'Igrave': '\u00CC',
|
|
'Imacr': '\u012A',
|
|
'ImaginaryI': '\u2148',
|
|
'ii': '\u2148',
|
|
'Int': '\u222C',
|
|
'Integral': '\u222B',
|
|
'int': '\u222B',
|
|
'Intersection': '\u22C2',
|
|
'bigcap': '\u22C2',
|
|
'xcap': '\u22C2',
|
|
'InvisibleComma': '\u2063',
|
|
'ic': '\u2063',
|
|
'InvisibleTimes': '\u2062',
|
|
'it': '\u2062',
|
|
'Iogon': '\u012E',
|
|
'Iopf': '\uD835\uDD40',
|
|
'Iota': '\u0399',
|
|
'Iscr': '\u2110',
|
|
'imagline': '\u2110',
|
|
'Itilde': '\u0128',
|
|
'Iukcy': '\u0406',
|
|
'Iuml': '\u00CF',
|
|
'Jcirc': '\u0134',
|
|
'Jcy': '\u0419',
|
|
'Jfr': '\uD835\uDD0D',
|
|
'Jopf': '\uD835\uDD41',
|
|
'Jscr': '\uD835\uDCA5',
|
|
'Jsercy': '\u0408',
|
|
'Jukcy': '\u0404',
|
|
'KHcy': '\u0425',
|
|
'KJcy': '\u040C',
|
|
'Kappa': '\u039A',
|
|
'Kcedil': '\u0136',
|
|
'Kcy': '\u041A',
|
|
'Kfr': '\uD835\uDD0E',
|
|
'Kopf': '\uD835\uDD42',
|
|
'Kscr': '\uD835\uDCA6',
|
|
'LJcy': '\u0409',
|
|
'LT': '\u003C',
|
|
'lt': '\u003C',
|
|
'Lacute': '\u0139',
|
|
'Lambda': '\u039B',
|
|
'Lang': '\u27EA',
|
|
'Laplacetrf': '\u2112',
|
|
'Lscr': '\u2112',
|
|
'lagran': '\u2112',
|
|
'Larr': '\u219E',
|
|
'twoheadleftarrow': '\u219E',
|
|
'Lcaron': '\u013D',
|
|
'Lcedil': '\u013B',
|
|
'Lcy': '\u041B',
|
|
'LeftAngleBracket': '\u27E8',
|
|
'lang': '\u27E8',
|
|
'langle': '\u27E8',
|
|
'LeftArrow': '\u2190',
|
|
'ShortLeftArrow': '\u2190',
|
|
'larr': '\u2190',
|
|
'leftarrow': '\u2190',
|
|
'slarr': '\u2190',
|
|
'LeftArrowBar': '\u21E4',
|
|
'larrb': '\u21E4',
|
|
'LeftArrowRightArrow': '\u21C6',
|
|
'leftrightarrows': '\u21C6',
|
|
'lrarr': '\u21C6',
|
|
'LeftCeiling': '\u2308',
|
|
'lceil': '\u2308',
|
|
'LeftDoubleBracket': '\u27E6',
|
|
'lobrk': '\u27E6',
|
|
'LeftDownTeeVector': '\u2961',
|
|
'LeftDownVector': '\u21C3',
|
|
'dharl': '\u21C3',
|
|
'downharpoonleft': '\u21C3',
|
|
'LeftDownVectorBar': '\u2959',
|
|
'LeftFloor': '\u230A',
|
|
'lfloor': '\u230A',
|
|
'LeftRightArrow': '\u2194',
|
|
'harr': '\u2194',
|
|
'leftrightarrow': '\u2194',
|
|
'LeftRightVector': '\u294E',
|
|
'LeftTee': '\u22A3',
|
|
'dashv': '\u22A3',
|
|
'LeftTeeArrow': '\u21A4',
|
|
'mapstoleft': '\u21A4',
|
|
'LeftTeeVector': '\u295A',
|
|
'LeftTriangle': '\u22B2',
|
|
'vartriangleleft': '\u22B2',
|
|
'vltri': '\u22B2',
|
|
'LeftTriangleBar': '\u29CF',
|
|
'LeftTriangleEqual': '\u22B4',
|
|
'ltrie': '\u22B4',
|
|
'trianglelefteq': '\u22B4',
|
|
'LeftUpDownVector': '\u2951',
|
|
'LeftUpTeeVector': '\u2960',
|
|
'LeftUpVector': '\u21BF',
|
|
'uharl': '\u21BF',
|
|
'upharpoonleft': '\u21BF',
|
|
'LeftUpVectorBar': '\u2958',
|
|
'LeftVector': '\u21BC',
|
|
'leftharpoonup': '\u21BC',
|
|
'lharu': '\u21BC',
|
|
'LeftVectorBar': '\u2952',
|
|
'LessEqualGreater': '\u22DA',
|
|
'leg': '\u22DA',
|
|
'lesseqgtr': '\u22DA',
|
|
'LessFullEqual': '\u2266',
|
|
'lE': '\u2266',
|
|
'leqq': '\u2266',
|
|
'LessGreater': '\u2276',
|
|
'lessgtr': '\u2276',
|
|
'lg': '\u2276',
|
|
'LessLess': '\u2AA1',
|
|
'LessSlantEqual': '\u2A7D',
|
|
'leqslant': '\u2A7D',
|
|
'les': '\u2A7D',
|
|
'LessTilde': '\u2272',
|
|
'lesssim': '\u2272',
|
|
'lsim': '\u2272',
|
|
'Lfr': '\uD835\uDD0F',
|
|
'Ll': '\u22D8',
|
|
'Lleftarrow': '\u21DA',
|
|
'lAarr': '\u21DA',
|
|
'Lmidot': '\u013F',
|
|
'LongLeftArrow': '\u27F5',
|
|
'longleftarrow': '\u27F5',
|
|
'xlarr': '\u27F5',
|
|
'LongLeftRightArrow': '\u27F7',
|
|
'longleftrightarrow': '\u27F7',
|
|
'xharr': '\u27F7',
|
|
'LongRightArrow': '\u27F6',
|
|
'longrightarrow': '\u27F6',
|
|
'xrarr': '\u27F6',
|
|
'Lopf': '\uD835\uDD43',
|
|
'LowerLeftArrow': '\u2199',
|
|
'swarr': '\u2199',
|
|
'swarrow': '\u2199',
|
|
'LowerRightArrow': '\u2198',
|
|
'searr': '\u2198',
|
|
'searrow': '\u2198',
|
|
'Lsh': '\u21B0',
|
|
'lsh': '\u21B0',
|
|
'Lstrok': '\u0141',
|
|
'Lt': '\u226A',
|
|
'NestedLessLess': '\u226A',
|
|
'll': '\u226A',
|
|
'Map': '\u2905',
|
|
'Mcy': '\u041C',
|
|
'MediumSpace': '\u205F',
|
|
'Mellintrf': '\u2133',
|
|
'Mscr': '\u2133',
|
|
'phmmat': '\u2133',
|
|
'Mfr': '\uD835\uDD10',
|
|
'MinusPlus': '\u2213',
|
|
'mnplus': '\u2213',
|
|
'mp': '\u2213',
|
|
'Mopf': '\uD835\uDD44',
|
|
'Mu': '\u039C',
|
|
'NJcy': '\u040A',
|
|
'Nacute': '\u0143',
|
|
'Ncaron': '\u0147',
|
|
'Ncedil': '\u0145',
|
|
'Ncy': '\u041D',
|
|
'NegativeMediumSpace': '\u200B',
|
|
'NegativeThickSpace': '\u200B',
|
|
'NegativeThinSpace': '\u200B',
|
|
'NegativeVeryThinSpace': '\u200B',
|
|
'ZeroWidthSpace': '\u200B',
|
|
'NewLine': '\u000A',
|
|
'Nfr': '\uD835\uDD11',
|
|
'NoBreak': '\u2060',
|
|
'NonBreakingSpace': '\u00A0',
|
|
'nbsp': '\u00A0',
|
|
'Nopf': '\u2115',
|
|
'naturals': '\u2115',
|
|
'Not': '\u2AEC',
|
|
'NotCongruent': '\u2262',
|
|
'nequiv': '\u2262',
|
|
'NotCupCap': '\u226D',
|
|
'NotDoubleVerticalBar': '\u2226',
|
|
'npar': '\u2226',
|
|
'nparallel': '\u2226',
|
|
'nshortparallel': '\u2226',
|
|
'nspar': '\u2226',
|
|
'NotElement': '\u2209',
|
|
'notin': '\u2209',
|
|
'notinva': '\u2209',
|
|
'NotEqual': '\u2260',
|
|
'ne': '\u2260',
|
|
'NotEqualTilde': '\u2242\u0338',
|
|
'nesim': '\u2242\u0338',
|
|
'NotExists': '\u2204',
|
|
'nexist': '\u2204',
|
|
'nexists': '\u2204',
|
|
'NotGreater': '\u226F',
|
|
'ngt': '\u226F',
|
|
'ngtr': '\u226F',
|
|
'NotGreaterEqual': '\u2271',
|
|
'nge': '\u2271',
|
|
'ngeq': '\u2271',
|
|
'NotGreaterFullEqual': '\u2267\u0338',
|
|
'ngE': '\u2267\u0338',
|
|
'ngeqq': '\u2267\u0338',
|
|
'NotGreaterGreater': '\u226B\u0338',
|
|
'nGtv': '\u226B\u0338',
|
|
'NotGreaterLess': '\u2279',
|
|
'ntgl': '\u2279',
|
|
'NotGreaterSlantEqual': '\u2A7E\u0338',
|
|
'ngeqslant': '\u2A7E\u0338',
|
|
'nges': '\u2A7E\u0338',
|
|
'NotGreaterTilde': '\u2275',
|
|
'ngsim': '\u2275',
|
|
'NotHumpDownHump': '\u224E\u0338',
|
|
'nbump': '\u224E\u0338',
|
|
'NotHumpEqual': '\u224F\u0338',
|
|
'nbumpe': '\u224F\u0338',
|
|
'NotLeftTriangle': '\u22EA',
|
|
'nltri': '\u22EA',
|
|
'ntriangleleft': '\u22EA',
|
|
'NotLeftTriangleBar': '\u29CF\u0338',
|
|
'NotLeftTriangleEqual': '\u22EC',
|
|
'nltrie': '\u22EC',
|
|
'ntrianglelefteq': '\u22EC',
|
|
'NotLess': '\u226E',
|
|
'nless': '\u226E',
|
|
'nlt': '\u226E',
|
|
'NotLessEqual': '\u2270',
|
|
'nle': '\u2270',
|
|
'nleq': '\u2270',
|
|
'NotLessGreater': '\u2278',
|
|
'ntlg': '\u2278',
|
|
'NotLessLess': '\u226A\u0338',
|
|
'nLtv': '\u226A\u0338',
|
|
'NotLessSlantEqual': '\u2A7D\u0338',
|
|
'nleqslant': '\u2A7D\u0338',
|
|
'nles': '\u2A7D\u0338',
|
|
'NotLessTilde': '\u2274',
|
|
'nlsim': '\u2274',
|
|
'NotNestedGreaterGreater': '\u2AA2\u0338',
|
|
'NotNestedLessLess': '\u2AA1\u0338',
|
|
'NotPrecedes': '\u2280',
|
|
'npr': '\u2280',
|
|
'nprec': '\u2280',
|
|
'NotPrecedesEqual': '\u2AAF\u0338',
|
|
'npre': '\u2AAF\u0338',
|
|
'npreceq': '\u2AAF\u0338',
|
|
'NotPrecedesSlantEqual': '\u22E0',
|
|
'nprcue': '\u22E0',
|
|
'NotReverseElement': '\u220C',
|
|
'notni': '\u220C',
|
|
'notniva': '\u220C',
|
|
'NotRightTriangle': '\u22EB',
|
|
'nrtri': '\u22EB',
|
|
'ntriangleright': '\u22EB',
|
|
'NotRightTriangleBar': '\u29D0\u0338',
|
|
'NotRightTriangleEqual': '\u22ED',
|
|
'nrtrie': '\u22ED',
|
|
'ntrianglerighteq': '\u22ED',
|
|
'NotSquareSubset': '\u228F\u0338',
|
|
'NotSquareSubsetEqual': '\u22E2',
|
|
'nsqsube': '\u22E2',
|
|
'NotSquareSuperset': '\u2290\u0338',
|
|
'NotSquareSupersetEqual': '\u22E3',
|
|
'nsqsupe': '\u22E3',
|
|
'NotSubset': '\u2282\u20D2',
|
|
'nsubset': '\u2282\u20D2',
|
|
'vnsub': '\u2282\u20D2',
|
|
'NotSubsetEqual': '\u2288',
|
|
'nsube': '\u2288',
|
|
'nsubseteq': '\u2288',
|
|
'NotSucceeds': '\u2281',
|
|
'nsc': '\u2281',
|
|
'nsucc': '\u2281',
|
|
'NotSucceedsEqual': '\u2AB0\u0338',
|
|
'nsce': '\u2AB0\u0338',
|
|
'nsucceq': '\u2AB0\u0338',
|
|
'NotSucceedsSlantEqual': '\u22E1',
|
|
'nsccue': '\u22E1',
|
|
'NotSucceedsTilde': '\u227F\u0338',
|
|
'NotSuperset': '\u2283\u20D2',
|
|
'nsupset': '\u2283\u20D2',
|
|
'vnsup': '\u2283\u20D2',
|
|
'NotSupersetEqual': '\u2289',
|
|
'nsupe': '\u2289',
|
|
'nsupseteq': '\u2289',
|
|
'NotTilde': '\u2241',
|
|
'nsim': '\u2241',
|
|
'NotTildeEqual': '\u2244',
|
|
'nsime': '\u2244',
|
|
'nsimeq': '\u2244',
|
|
'NotTildeFullEqual': '\u2247',
|
|
'ncong': '\u2247',
|
|
'NotTildeTilde': '\u2249',
|
|
'nap': '\u2249',
|
|
'napprox': '\u2249',
|
|
'NotVerticalBar': '\u2224',
|
|
'nmid': '\u2224',
|
|
'nshortmid': '\u2224',
|
|
'nsmid': '\u2224',
|
|
'Nscr': '\uD835\uDCA9',
|
|
'Ntilde': '\u00D1',
|
|
'Nu': '\u039D',
|
|
'OElig': '\u0152',
|
|
'Oacute': '\u00D3',
|
|
'Ocirc': '\u00D4',
|
|
'Ocy': '\u041E',
|
|
'Odblac': '\u0150',
|
|
'Ofr': '\uD835\uDD12',
|
|
'Ograve': '\u00D2',
|
|
'Omacr': '\u014C',
|
|
'Omega': '\u03A9',
|
|
'ohm': '\u03A9',
|
|
'Omicron': '\u039F',
|
|
'Oopf': '\uD835\uDD46',
|
|
'OpenCurlyDoubleQuote': '\u201C',
|
|
'ldquo': '\u201C',
|
|
'OpenCurlyQuote': '\u2018',
|
|
'lsquo': '\u2018',
|
|
'Or': '\u2A54',
|
|
'Oscr': '\uD835\uDCAA',
|
|
'Oslash': '\u00D8',
|
|
'Otilde': '\u00D5',
|
|
'Otimes': '\u2A37',
|
|
'Ouml': '\u00D6',
|
|
'OverBar': '\u203E',
|
|
'oline': '\u203E',
|
|
'OverBrace': '\u23DE',
|
|
'OverBracket': '\u23B4',
|
|
'tbrk': '\u23B4',
|
|
'OverParenthesis': '\u23DC',
|
|
'PartialD': '\u2202',
|
|
'part': '\u2202',
|
|
'Pcy': '\u041F',
|
|
'Pfr': '\uD835\uDD13',
|
|
'Phi': '\u03A6',
|
|
'Pi': '\u03A0',
|
|
'PlusMinus': '\u00B1',
|
|
'plusmn': '\u00B1',
|
|
'pm': '\u00B1',
|
|
'Popf': '\u2119',
|
|
'primes': '\u2119',
|
|
'Pr': '\u2ABB',
|
|
'Precedes': '\u227A',
|
|
'pr': '\u227A',
|
|
'prec': '\u227A',
|
|
'PrecedesEqual': '\u2AAF',
|
|
'pre': '\u2AAF',
|
|
'preceq': '\u2AAF',
|
|
'PrecedesSlantEqual': '\u227C',
|
|
'prcue': '\u227C',
|
|
'preccurlyeq': '\u227C',
|
|
'PrecedesTilde': '\u227E',
|
|
'precsim': '\u227E',
|
|
'prsim': '\u227E',
|
|
'Prime': '\u2033',
|
|
'Product': '\u220F',
|
|
'prod': '\u220F',
|
|
'Proportional': '\u221D',
|
|
'prop': '\u221D',
|
|
'propto': '\u221D',
|
|
'varpropto': '\u221D',
|
|
'vprop': '\u221D',
|
|
'Pscr': '\uD835\uDCAB',
|
|
'Psi': '\u03A8',
|
|
'QUOT': '\u0022',
|
|
'quot': '\u0022',
|
|
'Qfr': '\uD835\uDD14',
|
|
'Qopf': '\u211A',
|
|
'rationals': '\u211A',
|
|
'Qscr': '\uD835\uDCAC',
|
|
'RBarr': '\u2910',
|
|
'drbkarow': '\u2910',
|
|
'REG': '\u00AE',
|
|
'circledR': '\u00AE',
|
|
'reg': '\u00AE',
|
|
'Racute': '\u0154',
|
|
'Rang': '\u27EB',
|
|
'Rarr': '\u21A0',
|
|
'twoheadrightarrow': '\u21A0',
|
|
'Rarrtl': '\u2916',
|
|
'Rcaron': '\u0158',
|
|
'Rcedil': '\u0156',
|
|
'Rcy': '\u0420',
|
|
'Re': '\u211C',
|
|
'Rfr': '\u211C',
|
|
'real': '\u211C',
|
|
'realpart': '\u211C',
|
|
'ReverseElement': '\u220B',
|
|
'SuchThat': '\u220B',
|
|
'ni': '\u220B',
|
|
'niv': '\u220B',
|
|
'ReverseEquilibrium': '\u21CB',
|
|
'leftrightharpoons': '\u21CB',
|
|
'lrhar': '\u21CB',
|
|
'ReverseUpEquilibrium': '\u296F',
|
|
'duhar': '\u296F',
|
|
'Rho': '\u03A1',
|
|
'RightAngleBracket': '\u27E9',
|
|
'rang': '\u27E9',
|
|
'rangle': '\u27E9',
|
|
'RightArrow': '\u2192',
|
|
'ShortRightArrow': '\u2192',
|
|
'rarr': '\u2192',
|
|
'rightarrow': '\u2192',
|
|
'srarr': '\u2192',
|
|
'RightArrowBar': '\u21E5',
|
|
'rarrb': '\u21E5',
|
|
'RightArrowLeftArrow': '\u21C4',
|
|
'rightleftarrows': '\u21C4',
|
|
'rlarr': '\u21C4',
|
|
'RightCeiling': '\u2309',
|
|
'rceil': '\u2309',
|
|
'RightDoubleBracket': '\u27E7',
|
|
'robrk': '\u27E7',
|
|
'RightDownTeeVector': '\u295D',
|
|
'RightDownVector': '\u21C2',
|
|
'dharr': '\u21C2',
|
|
'downharpoonright': '\u21C2',
|
|
'RightDownVectorBar': '\u2955',
|
|
'RightFloor': '\u230B',
|
|
'rfloor': '\u230B',
|
|
'RightTee': '\u22A2',
|
|
'vdash': '\u22A2',
|
|
'RightTeeArrow': '\u21A6',
|
|
'map': '\u21A6',
|
|
'mapsto': '\u21A6',
|
|
'RightTeeVector': '\u295B',
|
|
'RightTriangle': '\u22B3',
|
|
'vartriangleright': '\u22B3',
|
|
'vrtri': '\u22B3',
|
|
'RightTriangleBar': '\u29D0',
|
|
'RightTriangleEqual': '\u22B5',
|
|
'rtrie': '\u22B5',
|
|
'trianglerighteq': '\u22B5',
|
|
'RightUpDownVector': '\u294F',
|
|
'RightUpTeeVector': '\u295C',
|
|
'RightUpVector': '\u21BE',
|
|
'uharr': '\u21BE',
|
|
'upharpoonright': '\u21BE',
|
|
'RightUpVectorBar': '\u2954',
|
|
'RightVector': '\u21C0',
|
|
'rharu': '\u21C0',
|
|
'rightharpoonup': '\u21C0',
|
|
'RightVectorBar': '\u2953',
|
|
'Ropf': '\u211D',
|
|
'reals': '\u211D',
|
|
'RoundImplies': '\u2970',
|
|
'Rrightarrow': '\u21DB',
|
|
'rAarr': '\u21DB',
|
|
'Rscr': '\u211B',
|
|
'realine': '\u211B',
|
|
'Rsh': '\u21B1',
|
|
'rsh': '\u21B1',
|
|
'RuleDelayed': '\u29F4',
|
|
'SHCHcy': '\u0429',
|
|
'SHcy': '\u0428',
|
|
'SOFTcy': '\u042C',
|
|
'Sacute': '\u015A',
|
|
'Sc': '\u2ABC',
|
|
'Scaron': '\u0160',
|
|
'Scedil': '\u015E',
|
|
'Scirc': '\u015C',
|
|
'Scy': '\u0421',
|
|
'Sfr': '\uD835\uDD16',
|
|
'ShortUpArrow': '\u2191',
|
|
'UpArrow': '\u2191',
|
|
'uarr': '\u2191',
|
|
'uparrow': '\u2191',
|
|
'Sigma': '\u03A3',
|
|
'SmallCircle': '\u2218',
|
|
'compfn': '\u2218',
|
|
'Sopf': '\uD835\uDD4A',
|
|
'Sqrt': '\u221A',
|
|
'radic': '\u221A',
|
|
'Square': '\u25A1',
|
|
'squ': '\u25A1',
|
|
'square': '\u25A1',
|
|
'SquareIntersection': '\u2293',
|
|
'sqcap': '\u2293',
|
|
'SquareSubset': '\u228F',
|
|
'sqsub': '\u228F',
|
|
'sqsubset': '\u228F',
|
|
'SquareSubsetEqual': '\u2291',
|
|
'sqsube': '\u2291',
|
|
'sqsubseteq': '\u2291',
|
|
'SquareSuperset': '\u2290',
|
|
'sqsup': '\u2290',
|
|
'sqsupset': '\u2290',
|
|
'SquareSupersetEqual': '\u2292',
|
|
'sqsupe': '\u2292',
|
|
'sqsupseteq': '\u2292',
|
|
'SquareUnion': '\u2294',
|
|
'sqcup': '\u2294',
|
|
'Sscr': '\uD835\uDCAE',
|
|
'Star': '\u22C6',
|
|
'sstarf': '\u22C6',
|
|
'Sub': '\u22D0',
|
|
'Subset': '\u22D0',
|
|
'SubsetEqual': '\u2286',
|
|
'sube': '\u2286',
|
|
'subseteq': '\u2286',
|
|
'Succeeds': '\u227B',
|
|
'sc': '\u227B',
|
|
'succ': '\u227B',
|
|
'SucceedsEqual': '\u2AB0',
|
|
'sce': '\u2AB0',
|
|
'succeq': '\u2AB0',
|
|
'SucceedsSlantEqual': '\u227D',
|
|
'sccue': '\u227D',
|
|
'succcurlyeq': '\u227D',
|
|
'SucceedsTilde': '\u227F',
|
|
'scsim': '\u227F',
|
|
'succsim': '\u227F',
|
|
'Sum': '\u2211',
|
|
'sum': '\u2211',
|
|
'Sup': '\u22D1',
|
|
'Supset': '\u22D1',
|
|
'Superset': '\u2283',
|
|
'sup': '\u2283',
|
|
'supset': '\u2283',
|
|
'SupersetEqual': '\u2287',
|
|
'supe': '\u2287',
|
|
'supseteq': '\u2287',
|
|
'THORN': '\u00DE',
|
|
'TRADE': '\u2122',
|
|
'trade': '\u2122',
|
|
'TSHcy': '\u040B',
|
|
'TScy': '\u0426',
|
|
'Tab': '\u0009',
|
|
'Tau': '\u03A4',
|
|
'Tcaron': '\u0164',
|
|
'Tcedil': '\u0162',
|
|
'Tcy': '\u0422',
|
|
'Tfr': '\uD835\uDD17',
|
|
'Therefore': '\u2234',
|
|
'there4': '\u2234',
|
|
'therefore': '\u2234',
|
|
'Theta': '\u0398',
|
|
'ThickSpace': '\u205F\u200A',
|
|
'ThinSpace': '\u2009',
|
|
'thinsp': '\u2009',
|
|
'Tilde': '\u223C',
|
|
'sim': '\u223C',
|
|
'thicksim': '\u223C',
|
|
'thksim': '\u223C',
|
|
'TildeEqual': '\u2243',
|
|
'sime': '\u2243',
|
|
'simeq': '\u2243',
|
|
'TildeFullEqual': '\u2245',
|
|
'cong': '\u2245',
|
|
'TildeTilde': '\u2248',
|
|
'ap': '\u2248',
|
|
'approx': '\u2248',
|
|
'asymp': '\u2248',
|
|
'thickapprox': '\u2248',
|
|
'thkap': '\u2248',
|
|
'Topf': '\uD835\uDD4B',
|
|
'TripleDot': '\u20DB',
|
|
'tdot': '\u20DB',
|
|
'Tscr': '\uD835\uDCAF',
|
|
'Tstrok': '\u0166',
|
|
'Uacute': '\u00DA',
|
|
'Uarr': '\u219F',
|
|
'Uarrocir': '\u2949',
|
|
'Ubrcy': '\u040E',
|
|
'Ubreve': '\u016C',
|
|
'Ucirc': '\u00DB',
|
|
'Ucy': '\u0423',
|
|
'Udblac': '\u0170',
|
|
'Ufr': '\uD835\uDD18',
|
|
'Ugrave': '\u00D9',
|
|
'Umacr': '\u016A',
|
|
'UnderBar': '\u005F',
|
|
'lowbar': '\u005F',
|
|
'UnderBrace': '\u23DF',
|
|
'UnderBracket': '\u23B5',
|
|
'bbrk': '\u23B5',
|
|
'UnderParenthesis': '\u23DD',
|
|
'Union': '\u22C3',
|
|
'bigcup': '\u22C3',
|
|
'xcup': '\u22C3',
|
|
'UnionPlus': '\u228E',
|
|
'uplus': '\u228E',
|
|
'Uogon': '\u0172',
|
|
'Uopf': '\uD835\uDD4C',
|
|
'UpArrowBar': '\u2912',
|
|
'UpArrowDownArrow': '\u21C5',
|
|
'udarr': '\u21C5',
|
|
'UpDownArrow': '\u2195',
|
|
'updownarrow': '\u2195',
|
|
'varr': '\u2195',
|
|
'UpEquilibrium': '\u296E',
|
|
'udhar': '\u296E',
|
|
'UpTee': '\u22A5',
|
|
'bot': '\u22A5',
|
|
'bottom': '\u22A5',
|
|
'perp': '\u22A5',
|
|
'UpTeeArrow': '\u21A5',
|
|
'mapstoup': '\u21A5',
|
|
'UpperLeftArrow': '\u2196',
|
|
'nwarr': '\u2196',
|
|
'nwarrow': '\u2196',
|
|
'UpperRightArrow': '\u2197',
|
|
'nearr': '\u2197',
|
|
'nearrow': '\u2197',
|
|
'Upsi': '\u03D2',
|
|
'upsih': '\u03D2',
|
|
'Upsilon': '\u03A5',
|
|
'Uring': '\u016E',
|
|
'Uscr': '\uD835\uDCB0',
|
|
'Utilde': '\u0168',
|
|
'Uuml': '\u00DC',
|
|
'VDash': '\u22AB',
|
|
'Vbar': '\u2AEB',
|
|
'Vcy': '\u0412',
|
|
'Vdash': '\u22A9',
|
|
'Vdashl': '\u2AE6',
|
|
'Vee': '\u22C1',
|
|
'bigvee': '\u22C1',
|
|
'xvee': '\u22C1',
|
|
'Verbar': '\u2016',
|
|
'Vert': '\u2016',
|
|
'VerticalBar': '\u2223',
|
|
'mid': '\u2223',
|
|
'shortmid': '\u2223',
|
|
'smid': '\u2223',
|
|
'VerticalLine': '\u007C',
|
|
'verbar': '\u007C',
|
|
'vert': '\u007C',
|
|
'VerticalSeparator': '\u2758',
|
|
'VerticalTilde': '\u2240',
|
|
'wr': '\u2240',
|
|
'wreath': '\u2240',
|
|
'VeryThinSpace': '\u200A',
|
|
'hairsp': '\u200A',
|
|
'Vfr': '\uD835\uDD19',
|
|
'Vopf': '\uD835\uDD4D',
|
|
'Vscr': '\uD835\uDCB1',
|
|
'Vvdash': '\u22AA',
|
|
'Wcirc': '\u0174',
|
|
'Wedge': '\u22C0',
|
|
'bigwedge': '\u22C0',
|
|
'xwedge': '\u22C0',
|
|
'Wfr': '\uD835\uDD1A',
|
|
'Wopf': '\uD835\uDD4E',
|
|
'Wscr': '\uD835\uDCB2',
|
|
'Xfr': '\uD835\uDD1B',
|
|
'Xi': '\u039E',
|
|
'Xopf': '\uD835\uDD4F',
|
|
'Xscr': '\uD835\uDCB3',
|
|
'YAcy': '\u042F',
|
|
'YIcy': '\u0407',
|
|
'YUcy': '\u042E',
|
|
'Yacute': '\u00DD',
|
|
'Ycirc': '\u0176',
|
|
'Ycy': '\u042B',
|
|
'Yfr': '\uD835\uDD1C',
|
|
'Yopf': '\uD835\uDD50',
|
|
'Yscr': '\uD835\uDCB4',
|
|
'Yuml': '\u0178',
|
|
'ZHcy': '\u0416',
|
|
'Zacute': '\u0179',
|
|
'Zcaron': '\u017D',
|
|
'Zcy': '\u0417',
|
|
'Zdot': '\u017B',
|
|
'Zeta': '\u0396',
|
|
'Zfr': '\u2128',
|
|
'zeetrf': '\u2128',
|
|
'Zopf': '\u2124',
|
|
'integers': '\u2124',
|
|
'Zscr': '\uD835\uDCB5',
|
|
'aacute': '\u00E1',
|
|
'abreve': '\u0103',
|
|
'ac': '\u223E',
|
|
'mstpos': '\u223E',
|
|
'acE': '\u223E\u0333',
|
|
'acd': '\u223F',
|
|
'acirc': '\u00E2',
|
|
'acy': '\u0430',
|
|
'aelig': '\u00E6',
|
|
'afr': '\uD835\uDD1E',
|
|
'agrave': '\u00E0',
|
|
'alefsym': '\u2135',
|
|
'aleph': '\u2135',
|
|
'alpha': '\u03B1',
|
|
'amacr': '\u0101',
|
|
'amalg': '\u2A3F',
|
|
'and': '\u2227',
|
|
'wedge': '\u2227',
|
|
'andand': '\u2A55',
|
|
'andd': '\u2A5C',
|
|
'andslope': '\u2A58',
|
|
'andv': '\u2A5A',
|
|
'ang': '\u2220',
|
|
'angle': '\u2220',
|
|
'ange': '\u29A4',
|
|
'angmsd': '\u2221',
|
|
'measuredangle': '\u2221',
|
|
'angmsdaa': '\u29A8',
|
|
'angmsdab': '\u29A9',
|
|
'angmsdac': '\u29AA',
|
|
'angmsdad': '\u29AB',
|
|
'angmsdae': '\u29AC',
|
|
'angmsdaf': '\u29AD',
|
|
'angmsdag': '\u29AE',
|
|
'angmsdah': '\u29AF',
|
|
'angrt': '\u221F',
|
|
'angrtvb': '\u22BE',
|
|
'angrtvbd': '\u299D',
|
|
'angsph': '\u2222',
|
|
'angzarr': '\u237C',
|
|
'aogon': '\u0105',
|
|
'aopf': '\uD835\uDD52',
|
|
'apE': '\u2A70',
|
|
'apacir': '\u2A6F',
|
|
'ape': '\u224A',
|
|
'approxeq': '\u224A',
|
|
'apid': '\u224B',
|
|
'apos': '\u0027',
|
|
'aring': '\u00E5',
|
|
'ascr': '\uD835\uDCB6',
|
|
'ast': '\u002A',
|
|
'midast': '\u002A',
|
|
'atilde': '\u00E3',
|
|
'auml': '\u00E4',
|
|
'awint': '\u2A11',
|
|
'bNot': '\u2AED',
|
|
'backcong': '\u224C',
|
|
'bcong': '\u224C',
|
|
'backepsilon': '\u03F6',
|
|
'bepsi': '\u03F6',
|
|
'backprime': '\u2035',
|
|
'bprime': '\u2035',
|
|
'backsim': '\u223D',
|
|
'bsim': '\u223D',
|
|
'backsimeq': '\u22CD',
|
|
'bsime': '\u22CD',
|
|
'barvee': '\u22BD',
|
|
'barwed': '\u2305',
|
|
'barwedge': '\u2305',
|
|
'bbrktbrk': '\u23B6',
|
|
'bcy': '\u0431',
|
|
'bdquo': '\u201E',
|
|
'ldquor': '\u201E',
|
|
'bemptyv': '\u29B0',
|
|
'beta': '\u03B2',
|
|
'beth': '\u2136',
|
|
'between': '\u226C',
|
|
'twixt': '\u226C',
|
|
'bfr': '\uD835\uDD1F',
|
|
'bigcirc': '\u25EF',
|
|
'xcirc': '\u25EF',
|
|
'bigodot': '\u2A00',
|
|
'xodot': '\u2A00',
|
|
'bigoplus': '\u2A01',
|
|
'xoplus': '\u2A01',
|
|
'bigotimes': '\u2A02',
|
|
'xotime': '\u2A02',
|
|
'bigsqcup': '\u2A06',
|
|
'xsqcup': '\u2A06',
|
|
'bigstar': '\u2605',
|
|
'starf': '\u2605',
|
|
'bigtriangledown': '\u25BD',
|
|
'xdtri': '\u25BD',
|
|
'bigtriangleup': '\u25B3',
|
|
'xutri': '\u25B3',
|
|
'biguplus': '\u2A04',
|
|
'xuplus': '\u2A04',
|
|
'bkarow': '\u290D',
|
|
'rbarr': '\u290D',
|
|
'blacklozenge': '\u29EB',
|
|
'lozf': '\u29EB',
|
|
'blacktriangle': '\u25B4',
|
|
'utrif': '\u25B4',
|
|
'blacktriangledown': '\u25BE',
|
|
'dtrif': '\u25BE',
|
|
'blacktriangleleft': '\u25C2',
|
|
'ltrif': '\u25C2',
|
|
'blacktriangleright': '\u25B8',
|
|
'rtrif': '\u25B8',
|
|
'blank': '\u2423',
|
|
'blk12': '\u2592',
|
|
'blk14': '\u2591',
|
|
'blk34': '\u2593',
|
|
'block': '\u2588',
|
|
'bne': '\u003D\u20E5',
|
|
'bnequiv': '\u2261\u20E5',
|
|
'bnot': '\u2310',
|
|
'bopf': '\uD835\uDD53',
|
|
'bowtie': '\u22C8',
|
|
'boxDL': '\u2557',
|
|
'boxDR': '\u2554',
|
|
'boxDl': '\u2556',
|
|
'boxDr': '\u2553',
|
|
'boxH': '\u2550',
|
|
'boxHD': '\u2566',
|
|
'boxHU': '\u2569',
|
|
'boxHd': '\u2564',
|
|
'boxHu': '\u2567',
|
|
'boxUL': '\u255D',
|
|
'boxUR': '\u255A',
|
|
'boxUl': '\u255C',
|
|
'boxUr': '\u2559',
|
|
'boxV': '\u2551',
|
|
'boxVH': '\u256C',
|
|
'boxVL': '\u2563',
|
|
'boxVR': '\u2560',
|
|
'boxVh': '\u256B',
|
|
'boxVl': '\u2562',
|
|
'boxVr': '\u255F',
|
|
'boxbox': '\u29C9',
|
|
'boxdL': '\u2555',
|
|
'boxdR': '\u2552',
|
|
'boxdl': '\u2510',
|
|
'boxdr': '\u250C',
|
|
'boxhD': '\u2565',
|
|
'boxhU': '\u2568',
|
|
'boxhd': '\u252C',
|
|
'boxhu': '\u2534',
|
|
'boxminus': '\u229F',
|
|
'minusb': '\u229F',
|
|
'boxplus': '\u229E',
|
|
'plusb': '\u229E',
|
|
'boxtimes': '\u22A0',
|
|
'timesb': '\u22A0',
|
|
'boxuL': '\u255B',
|
|
'boxuR': '\u2558',
|
|
'boxul': '\u2518',
|
|
'boxur': '\u2514',
|
|
'boxv': '\u2502',
|
|
'boxvH': '\u256A',
|
|
'boxvL': '\u2561',
|
|
'boxvR': '\u255E',
|
|
'boxvh': '\u253C',
|
|
'boxvl': '\u2524',
|
|
'boxvr': '\u251C',
|
|
'brvbar': '\u00A6',
|
|
'bscr': '\uD835\uDCB7',
|
|
'bsemi': '\u204F',
|
|
'bsol': '\u005C',
|
|
'bsolb': '\u29C5',
|
|
'bsolhsub': '\u27C8',
|
|
'bull': '\u2022',
|
|
'bullet': '\u2022',
|
|
'bumpE': '\u2AAE',
|
|
'cacute': '\u0107',
|
|
'cap': '\u2229',
|
|
'capand': '\u2A44',
|
|
'capbrcup': '\u2A49',
|
|
'capcap': '\u2A4B',
|
|
'capcup': '\u2A47',
|
|
'capdot': '\u2A40',
|
|
'caps': '\u2229\uFE00',
|
|
'caret': '\u2041',
|
|
'ccaps': '\u2A4D',
|
|
'ccaron': '\u010D',
|
|
'ccedil': '\u00E7',
|
|
'ccirc': '\u0109',
|
|
'ccups': '\u2A4C',
|
|
'ccupssm': '\u2A50',
|
|
'cdot': '\u010B',
|
|
'cemptyv': '\u29B2',
|
|
'cent': '\u00A2',
|
|
'cfr': '\uD835\uDD20',
|
|
'chcy': '\u0447',
|
|
'check': '\u2713',
|
|
'checkmark': '\u2713',
|
|
'chi': '\u03C7',
|
|
'cir': '\u25CB',
|
|
'cirE': '\u29C3',
|
|
'circ': '\u02C6',
|
|
'circeq': '\u2257',
|
|
'cire': '\u2257',
|
|
'circlearrowleft': '\u21BA',
|
|
'olarr': '\u21BA',
|
|
'circlearrowright': '\u21BB',
|
|
'orarr': '\u21BB',
|
|
'circledS': '\u24C8',
|
|
'oS': '\u24C8',
|
|
'circledast': '\u229B',
|
|
'oast': '\u229B',
|
|
'circledcirc': '\u229A',
|
|
'ocir': '\u229A',
|
|
'circleddash': '\u229D',
|
|
'odash': '\u229D',
|
|
'cirfnint': '\u2A10',
|
|
'cirmid': '\u2AEF',
|
|
'cirscir': '\u29C2',
|
|
'clubs': '\u2663',
|
|
'clubsuit': '\u2663',
|
|
'colon': '\u003A',
|
|
'comma': '\u002C',
|
|
'commat': '\u0040',
|
|
'comp': '\u2201',
|
|
'complement': '\u2201',
|
|
'congdot': '\u2A6D',
|
|
'copf': '\uD835\uDD54',
|
|
'copysr': '\u2117',
|
|
'crarr': '\u21B5',
|
|
'cross': '\u2717',
|
|
'cscr': '\uD835\uDCB8',
|
|
'csub': '\u2ACF',
|
|
'csube': '\u2AD1',
|
|
'csup': '\u2AD0',
|
|
'csupe': '\u2AD2',
|
|
'ctdot': '\u22EF',
|
|
'cudarrl': '\u2938',
|
|
'cudarrr': '\u2935',
|
|
'cuepr': '\u22DE',
|
|
'curlyeqprec': '\u22DE',
|
|
'cuesc': '\u22DF',
|
|
'curlyeqsucc': '\u22DF',
|
|
'cularr': '\u21B6',
|
|
'curvearrowleft': '\u21B6',
|
|
'cularrp': '\u293D',
|
|
'cup': '\u222A',
|
|
'cupbrcap': '\u2A48',
|
|
'cupcap': '\u2A46',
|
|
'cupcup': '\u2A4A',
|
|
'cupdot': '\u228D',
|
|
'cupor': '\u2A45',
|
|
'cups': '\u222A\uFE00',
|
|
'curarr': '\u21B7',
|
|
'curvearrowright': '\u21B7',
|
|
'curarrm': '\u293C',
|
|
'curlyvee': '\u22CE',
|
|
'cuvee': '\u22CE',
|
|
'curlywedge': '\u22CF',
|
|
'cuwed': '\u22CF',
|
|
'curren': '\u00A4',
|
|
'cwint': '\u2231',
|
|
'cylcty': '\u232D',
|
|
'dHar': '\u2965',
|
|
'dagger': '\u2020',
|
|
'daleth': '\u2138',
|
|
'dash': '\u2010',
|
|
'hyphen': '\u2010',
|
|
'dbkarow': '\u290F',
|
|
'rBarr': '\u290F',
|
|
'dcaron': '\u010F',
|
|
'dcy': '\u0434',
|
|
'ddarr': '\u21CA',
|
|
'downdownarrows': '\u21CA',
|
|
'ddotseq': '\u2A77',
|
|
'eDDot': '\u2A77',
|
|
'deg': '\u00B0',
|
|
'delta': '\u03B4',
|
|
'demptyv': '\u29B1',
|
|
'dfisht': '\u297F',
|
|
'dfr': '\uD835\uDD21',
|
|
'diamondsuit': '\u2666',
|
|
'diams': '\u2666',
|
|
'digamma': '\u03DD',
|
|
'gammad': '\u03DD',
|
|
'disin': '\u22F2',
|
|
'div': '\u00F7',
|
|
'divide': '\u00F7',
|
|
'divideontimes': '\u22C7',
|
|
'divonx': '\u22C7',
|
|
'djcy': '\u0452',
|
|
'dlcorn': '\u231E',
|
|
'llcorner': '\u231E',
|
|
'dlcrop': '\u230D',
|
|
'dollar': '\u0024',
|
|
'dopf': '\uD835\uDD55',
|
|
'doteqdot': '\u2251',
|
|
'eDot': '\u2251',
|
|
'dotminus': '\u2238',
|
|
'minusd': '\u2238',
|
|
'dotplus': '\u2214',
|
|
'plusdo': '\u2214',
|
|
'dotsquare': '\u22A1',
|
|
'sdotb': '\u22A1',
|
|
'drcorn': '\u231F',
|
|
'lrcorner': '\u231F',
|
|
'drcrop': '\u230C',
|
|
'dscr': '\uD835\uDCB9',
|
|
'dscy': '\u0455',
|
|
'dsol': '\u29F6',
|
|
'dstrok': '\u0111',
|
|
'dtdot': '\u22F1',
|
|
'dtri': '\u25BF',
|
|
'triangledown': '\u25BF',
|
|
'dwangle': '\u29A6',
|
|
'dzcy': '\u045F',
|
|
'dzigrarr': '\u27FF',
|
|
'eacute': '\u00E9',
|
|
'easter': '\u2A6E',
|
|
'ecaron': '\u011B',
|
|
'ecir': '\u2256',
|
|
'eqcirc': '\u2256',
|
|
'ecirc': '\u00EA',
|
|
'ecolon': '\u2255',
|
|
'eqcolon': '\u2255',
|
|
'ecy': '\u044D',
|
|
'edot': '\u0117',
|
|
'efDot': '\u2252',
|
|
'fallingdotseq': '\u2252',
|
|
'efr': '\uD835\uDD22',
|
|
'eg': '\u2A9A',
|
|
'egrave': '\u00E8',
|
|
'egs': '\u2A96',
|
|
'eqslantgtr': '\u2A96',
|
|
'egsdot': '\u2A98',
|
|
'el': '\u2A99',
|
|
'elinters': '\u23E7',
|
|
'ell': '\u2113',
|
|
'els': '\u2A95',
|
|
'eqslantless': '\u2A95',
|
|
'elsdot': '\u2A97',
|
|
'emacr': '\u0113',
|
|
'empty': '\u2205',
|
|
'emptyset': '\u2205',
|
|
'emptyv': '\u2205',
|
|
'varnothing': '\u2205',
|
|
'emsp13': '\u2004',
|
|
'emsp14': '\u2005',
|
|
'emsp': '\u2003',
|
|
'eng': '\u014B',
|
|
'ensp': '\u2002',
|
|
'eogon': '\u0119',
|
|
'eopf': '\uD835\uDD56',
|
|
'epar': '\u22D5',
|
|
'eparsl': '\u29E3',
|
|
'eplus': '\u2A71',
|
|
'epsi': '\u03B5',
|
|
'epsilon': '\u03B5',
|
|
'epsiv': '\u03F5',
|
|
'straightepsilon': '\u03F5',
|
|
'varepsilon': '\u03F5',
|
|
'equals': '\u003D',
|
|
'equest': '\u225F',
|
|
'questeq': '\u225F',
|
|
'equivDD': '\u2A78',
|
|
'eqvparsl': '\u29E5',
|
|
'erDot': '\u2253',
|
|
'risingdotseq': '\u2253',
|
|
'erarr': '\u2971',
|
|
'escr': '\u212F',
|
|
'eta': '\u03B7',
|
|
'eth': '\u00F0',
|
|
'euml': '\u00EB',
|
|
'euro': '\u20AC',
|
|
'excl': '\u0021',
|
|
'fcy': '\u0444',
|
|
'female': '\u2640',
|
|
'ffilig': '\uFB03',
|
|
'fflig': '\uFB00',
|
|
'ffllig': '\uFB04',
|
|
'ffr': '\uD835\uDD23',
|
|
'filig': '\uFB01',
|
|
'fjlig': '\u0066\u006A',
|
|
'flat': '\u266D',
|
|
'fllig': '\uFB02',
|
|
'fltns': '\u25B1',
|
|
'fnof': '\u0192',
|
|
'fopf': '\uD835\uDD57',
|
|
'fork': '\u22D4',
|
|
'pitchfork': '\u22D4',
|
|
'forkv': '\u2AD9',
|
|
'fpartint': '\u2A0D',
|
|
'frac12': '\u00BD',
|
|
'half': '\u00BD',
|
|
'frac13': '\u2153',
|
|
'frac14': '\u00BC',
|
|
'frac15': '\u2155',
|
|
'frac16': '\u2159',
|
|
'frac18': '\u215B',
|
|
'frac23': '\u2154',
|
|
'frac25': '\u2156',
|
|
'frac34': '\u00BE',
|
|
'frac35': '\u2157',
|
|
'frac38': '\u215C',
|
|
'frac45': '\u2158',
|
|
'frac56': '\u215A',
|
|
'frac58': '\u215D',
|
|
'frac78': '\u215E',
|
|
'frasl': '\u2044',
|
|
'frown': '\u2322',
|
|
'sfrown': '\u2322',
|
|
'fscr': '\uD835\uDCBB',
|
|
'gEl': '\u2A8C',
|
|
'gtreqqless': '\u2A8C',
|
|
'gacute': '\u01F5',
|
|
'gamma': '\u03B3',
|
|
'gap': '\u2A86',
|
|
'gtrapprox': '\u2A86',
|
|
'gbreve': '\u011F',
|
|
'gcirc': '\u011D',
|
|
'gcy': '\u0433',
|
|
'gdot': '\u0121',
|
|
'gescc': '\u2AA9',
|
|
'gesdot': '\u2A80',
|
|
'gesdoto': '\u2A82',
|
|
'gesdotol': '\u2A84',
|
|
'gesl': '\u22DB\uFE00',
|
|
'gesles': '\u2A94',
|
|
'gfr': '\uD835\uDD24',
|
|
'gimel': '\u2137',
|
|
'gjcy': '\u0453',
|
|
'glE': '\u2A92',
|
|
'gla': '\u2AA5',
|
|
'glj': '\u2AA4',
|
|
'gnE': '\u2269',
|
|
'gneqq': '\u2269',
|
|
'gnap': '\u2A8A',
|
|
'gnapprox': '\u2A8A',
|
|
'gne': '\u2A88',
|
|
'gneq': '\u2A88',
|
|
'gnsim': '\u22E7',
|
|
'gopf': '\uD835\uDD58',
|
|
'gscr': '\u210A',
|
|
'gsime': '\u2A8E',
|
|
'gsiml': '\u2A90',
|
|
'gtcc': '\u2AA7',
|
|
'gtcir': '\u2A7A',
|
|
'gtdot': '\u22D7',
|
|
'gtrdot': '\u22D7',
|
|
'gtlPar': '\u2995',
|
|
'gtquest': '\u2A7C',
|
|
'gtrarr': '\u2978',
|
|
'gvertneqq': '\u2269\uFE00',
|
|
'gvnE': '\u2269\uFE00',
|
|
'hardcy': '\u044A',
|
|
'harrcir': '\u2948',
|
|
'harrw': '\u21AD',
|
|
'leftrightsquigarrow': '\u21AD',
|
|
'hbar': '\u210F',
|
|
'hslash': '\u210F',
|
|
'planck': '\u210F',
|
|
'plankv': '\u210F',
|
|
'hcirc': '\u0125',
|
|
'hearts': '\u2665',
|
|
'heartsuit': '\u2665',
|
|
'hellip': '\u2026',
|
|
'mldr': '\u2026',
|
|
'hercon': '\u22B9',
|
|
'hfr': '\uD835\uDD25',
|
|
'hksearow': '\u2925',
|
|
'searhk': '\u2925',
|
|
'hkswarow': '\u2926',
|
|
'swarhk': '\u2926',
|
|
'hoarr': '\u21FF',
|
|
'homtht': '\u223B',
|
|
'hookleftarrow': '\u21A9',
|
|
'larrhk': '\u21A9',
|
|
'hookrightarrow': '\u21AA',
|
|
'rarrhk': '\u21AA',
|
|
'hopf': '\uD835\uDD59',
|
|
'horbar': '\u2015',
|
|
'hscr': '\uD835\uDCBD',
|
|
'hstrok': '\u0127',
|
|
'hybull': '\u2043',
|
|
'iacute': '\u00ED',
|
|
'icirc': '\u00EE',
|
|
'icy': '\u0438',
|
|
'iecy': '\u0435',
|
|
'iexcl': '\u00A1',
|
|
'ifr': '\uD835\uDD26',
|
|
'igrave': '\u00EC',
|
|
'iiiint': '\u2A0C',
|
|
'qint': '\u2A0C',
|
|
'iiint': '\u222D',
|
|
'tint': '\u222D',
|
|
'iinfin': '\u29DC',
|
|
'iiota': '\u2129',
|
|
'ijlig': '\u0133',
|
|
'imacr': '\u012B',
|
|
'imath': '\u0131',
|
|
'inodot': '\u0131',
|
|
'imof': '\u22B7',
|
|
'imped': '\u01B5',
|
|
'incare': '\u2105',
|
|
'infin': '\u221E',
|
|
'infintie': '\u29DD',
|
|
'intcal': '\u22BA',
|
|
'intercal': '\u22BA',
|
|
'intlarhk': '\u2A17',
|
|
'intprod': '\u2A3C',
|
|
'iprod': '\u2A3C',
|
|
'iocy': '\u0451',
|
|
'iogon': '\u012F',
|
|
'iopf': '\uD835\uDD5A',
|
|
'iota': '\u03B9',
|
|
'iquest': '\u00BF',
|
|
'iscr': '\uD835\uDCBE',
|
|
'isinE': '\u22F9',
|
|
'isindot': '\u22F5',
|
|
'isins': '\u22F4',
|
|
'isinsv': '\u22F3',
|
|
'itilde': '\u0129',
|
|
'iukcy': '\u0456',
|
|
'iuml': '\u00EF',
|
|
'jcirc': '\u0135',
|
|
'jcy': '\u0439',
|
|
'jfr': '\uD835\uDD27',
|
|
'jmath': '\u0237',
|
|
'jopf': '\uD835\uDD5B',
|
|
'jscr': '\uD835\uDCBF',
|
|
'jsercy': '\u0458',
|
|
'jukcy': '\u0454',
|
|
'kappa': '\u03BA',
|
|
'kappav': '\u03F0',
|
|
'varkappa': '\u03F0',
|
|
'kcedil': '\u0137',
|
|
'kcy': '\u043A',
|
|
'kfr': '\uD835\uDD28',
|
|
'kgreen': '\u0138',
|
|
'khcy': '\u0445',
|
|
'kjcy': '\u045C',
|
|
'kopf': '\uD835\uDD5C',
|
|
'kscr': '\uD835\uDCC0',
|
|
'lAtail': '\u291B',
|
|
'lBarr': '\u290E',
|
|
'lEg': '\u2A8B',
|
|
'lesseqqgtr': '\u2A8B',
|
|
'lHar': '\u2962',
|
|
'lacute': '\u013A',
|
|
'laemptyv': '\u29B4',
|
|
'lambda': '\u03BB',
|
|
'langd': '\u2991',
|
|
'lap': '\u2A85',
|
|
'lessapprox': '\u2A85',
|
|
'laquo': '\u00AB',
|
|
'larrbfs': '\u291F',
|
|
'larrfs': '\u291D',
|
|
'larrlp': '\u21AB',
|
|
'looparrowleft': '\u21AB',
|
|
'larrpl': '\u2939',
|
|
'larrsim': '\u2973',
|
|
'larrtl': '\u21A2',
|
|
'leftarrowtail': '\u21A2',
|
|
'lat': '\u2AAB',
|
|
'latail': '\u2919',
|
|
'late': '\u2AAD',
|
|
'lates': '\u2AAD\uFE00',
|
|
'lbarr': '\u290C',
|
|
'lbbrk': '\u2772',
|
|
'lbrace': '\u007B',
|
|
'lcub': '\u007B',
|
|
'lbrack': '\u005B',
|
|
'lsqb': '\u005B',
|
|
'lbrke': '\u298B',
|
|
'lbrksld': '\u298F',
|
|
'lbrkslu': '\u298D',
|
|
'lcaron': '\u013E',
|
|
'lcedil': '\u013C',
|
|
'lcy': '\u043B',
|
|
'ldca': '\u2936',
|
|
'ldrdhar': '\u2967',
|
|
'ldrushar': '\u294B',
|
|
'ldsh': '\u21B2',
|
|
'le': '\u2264',
|
|
'leq': '\u2264',
|
|
'leftleftarrows': '\u21C7',
|
|
'llarr': '\u21C7',
|
|
'leftthreetimes': '\u22CB',
|
|
'lthree': '\u22CB',
|
|
'lescc': '\u2AA8',
|
|
'lesdot': '\u2A7F',
|
|
'lesdoto': '\u2A81',
|
|
'lesdotor': '\u2A83',
|
|
'lesg': '\u22DA\uFE00',
|
|
'lesges': '\u2A93',
|
|
'lessdot': '\u22D6',
|
|
'ltdot': '\u22D6',
|
|
'lfisht': '\u297C',
|
|
'lfr': '\uD835\uDD29',
|
|
'lgE': '\u2A91',
|
|
'lharul': '\u296A',
|
|
'lhblk': '\u2584',
|
|
'ljcy': '\u0459',
|
|
'llhard': '\u296B',
|
|
'lltri': '\u25FA',
|
|
'lmidot': '\u0140',
|
|
'lmoust': '\u23B0',
|
|
'lmoustache': '\u23B0',
|
|
'lnE': '\u2268',
|
|
'lneqq': '\u2268',
|
|
'lnap': '\u2A89',
|
|
'lnapprox': '\u2A89',
|
|
'lne': '\u2A87',
|
|
'lneq': '\u2A87',
|
|
'lnsim': '\u22E6',
|
|
'loang': '\u27EC',
|
|
'loarr': '\u21FD',
|
|
'longmapsto': '\u27FC',
|
|
'xmap': '\u27FC',
|
|
'looparrowright': '\u21AC',
|
|
'rarrlp': '\u21AC',
|
|
'lopar': '\u2985',
|
|
'lopf': '\uD835\uDD5D',
|
|
'loplus': '\u2A2D',
|
|
'lotimes': '\u2A34',
|
|
'lowast': '\u2217',
|
|
'loz': '\u25CA',
|
|
'lozenge': '\u25CA',
|
|
'lpar': '\u0028',
|
|
'lparlt': '\u2993',
|
|
'lrhard': '\u296D',
|
|
'lrm': '\u200E',
|
|
'lrtri': '\u22BF',
|
|
'lsaquo': '\u2039',
|
|
'lscr': '\uD835\uDCC1',
|
|
'lsime': '\u2A8D',
|
|
'lsimg': '\u2A8F',
|
|
'lsquor': '\u201A',
|
|
'sbquo': '\u201A',
|
|
'lstrok': '\u0142',
|
|
'ltcc': '\u2AA6',
|
|
'ltcir': '\u2A79',
|
|
'ltimes': '\u22C9',
|
|
'ltlarr': '\u2976',
|
|
'ltquest': '\u2A7B',
|
|
'ltrPar': '\u2996',
|
|
'ltri': '\u25C3',
|
|
'triangleleft': '\u25C3',
|
|
'lurdshar': '\u294A',
|
|
'luruhar': '\u2966',
|
|
'lvertneqq': '\u2268\uFE00',
|
|
'lvnE': '\u2268\uFE00',
|
|
'mDDot': '\u223A',
|
|
'macr': '\u00AF',
|
|
'strns': '\u00AF',
|
|
'male': '\u2642',
|
|
'malt': '\u2720',
|
|
'maltese': '\u2720',
|
|
'marker': '\u25AE',
|
|
'mcomma': '\u2A29',
|
|
'mcy': '\u043C',
|
|
'mdash': '\u2014',
|
|
'mfr': '\uD835\uDD2A',
|
|
'mho': '\u2127',
|
|
'micro': '\u00B5',
|
|
'midcir': '\u2AF0',
|
|
'minus': '\u2212',
|
|
'minusdu': '\u2A2A',
|
|
'mlcp': '\u2ADB',
|
|
'models': '\u22A7',
|
|
'mopf': '\uD835\uDD5E',
|
|
'mscr': '\uD835\uDCC2',
|
|
'mu': '\u03BC',
|
|
'multimap': '\u22B8',
|
|
'mumap': '\u22B8',
|
|
'nGg': '\u22D9\u0338',
|
|
'nGt': '\u226B\u20D2',
|
|
'nLeftarrow': '\u21CD',
|
|
'nlArr': '\u21CD',
|
|
'nLeftrightarrow': '\u21CE',
|
|
'nhArr': '\u21CE',
|
|
'nLl': '\u22D8\u0338',
|
|
'nLt': '\u226A\u20D2',
|
|
'nRightarrow': '\u21CF',
|
|
'nrArr': '\u21CF',
|
|
'nVDash': '\u22AF',
|
|
'nVdash': '\u22AE',
|
|
'nacute': '\u0144',
|
|
'nang': '\u2220\u20D2',
|
|
'napE': '\u2A70\u0338',
|
|
'napid': '\u224B\u0338',
|
|
'napos': '\u0149',
|
|
'natur': '\u266E',
|
|
'natural': '\u266E',
|
|
'ncap': '\u2A43',
|
|
'ncaron': '\u0148',
|
|
'ncedil': '\u0146',
|
|
'ncongdot': '\u2A6D\u0338',
|
|
'ncup': '\u2A42',
|
|
'ncy': '\u043D',
|
|
'ndash': '\u2013',
|
|
'neArr': '\u21D7',
|
|
'nearhk': '\u2924',
|
|
'nedot': '\u2250\u0338',
|
|
'nesear': '\u2928',
|
|
'toea': '\u2928',
|
|
'nfr': '\uD835\uDD2B',
|
|
'nharr': '\u21AE',
|
|
'nleftrightarrow': '\u21AE',
|
|
'nhpar': '\u2AF2',
|
|
'nis': '\u22FC',
|
|
'nisd': '\u22FA',
|
|
'njcy': '\u045A',
|
|
'nlE': '\u2266\u0338',
|
|
'nleqq': '\u2266\u0338',
|
|
'nlarr': '\u219A',
|
|
'nleftarrow': '\u219A',
|
|
'nldr': '\u2025',
|
|
'nopf': '\uD835\uDD5F',
|
|
'not': '\u00AC',
|
|
'notinE': '\u22F9\u0338',
|
|
'notindot': '\u22F5\u0338',
|
|
'notinvb': '\u22F7',
|
|
'notinvc': '\u22F6',
|
|
'notnivb': '\u22FE',
|
|
'notnivc': '\u22FD',
|
|
'nparsl': '\u2AFD\u20E5',
|
|
'npart': '\u2202\u0338',
|
|
'npolint': '\u2A14',
|
|
'nrarr': '\u219B',
|
|
'nrightarrow': '\u219B',
|
|
'nrarrc': '\u2933\u0338',
|
|
'nrarrw': '\u219D\u0338',
|
|
'nscr': '\uD835\uDCC3',
|
|
'nsub': '\u2284',
|
|
'nsubE': '\u2AC5\u0338',
|
|
'nsubseteqq': '\u2AC5\u0338',
|
|
'nsup': '\u2285',
|
|
'nsupE': '\u2AC6\u0338',
|
|
'nsupseteqq': '\u2AC6\u0338',
|
|
'ntilde': '\u00F1',
|
|
'nu': '\u03BD',
|
|
'num': '\u0023',
|
|
'numero': '\u2116',
|
|
'numsp': '\u2007',
|
|
'nvDash': '\u22AD',
|
|
'nvHarr': '\u2904',
|
|
'nvap': '\u224D\u20D2',
|
|
'nvdash': '\u22AC',
|
|
'nvge': '\u2265\u20D2',
|
|
'nvgt': '\u003E\u20D2',
|
|
'nvinfin': '\u29DE',
|
|
'nvlArr': '\u2902',
|
|
'nvle': '\u2264\u20D2',
|
|
'nvlt': '\u003C\u20D2',
|
|
'nvltrie': '\u22B4\u20D2',
|
|
'nvrArr': '\u2903',
|
|
'nvrtrie': '\u22B5\u20D2',
|
|
'nvsim': '\u223C\u20D2',
|
|
'nwArr': '\u21D6',
|
|
'nwarhk': '\u2923',
|
|
'nwnear': '\u2927',
|
|
'oacute': '\u00F3',
|
|
'ocirc': '\u00F4',
|
|
'ocy': '\u043E',
|
|
'odblac': '\u0151',
|
|
'odiv': '\u2A38',
|
|
'odsold': '\u29BC',
|
|
'oelig': '\u0153',
|
|
'ofcir': '\u29BF',
|
|
'ofr': '\uD835\uDD2C',
|
|
'ogon': '\u02DB',
|
|
'ograve': '\u00F2',
|
|
'ogt': '\u29C1',
|
|
'ohbar': '\u29B5',
|
|
'olcir': '\u29BE',
|
|
'olcross': '\u29BB',
|
|
'olt': '\u29C0',
|
|
'omacr': '\u014D',
|
|
'omega': '\u03C9',
|
|
'omicron': '\u03BF',
|
|
'omid': '\u29B6',
|
|
'oopf': '\uD835\uDD60',
|
|
'opar': '\u29B7',
|
|
'operp': '\u29B9',
|
|
'or': '\u2228',
|
|
'vee': '\u2228',
|
|
'ord': '\u2A5D',
|
|
'order': '\u2134',
|
|
'orderof': '\u2134',
|
|
'oscr': '\u2134',
|
|
'ordf': '\u00AA',
|
|
'ordm': '\u00BA',
|
|
'origof': '\u22B6',
|
|
'oror': '\u2A56',
|
|
'orslope': '\u2A57',
|
|
'orv': '\u2A5B',
|
|
'oslash': '\u00F8',
|
|
'osol': '\u2298',
|
|
'otilde': '\u00F5',
|
|
'otimesas': '\u2A36',
|
|
'ouml': '\u00F6',
|
|
'ovbar': '\u233D',
|
|
'para': '\u00B6',
|
|
'parsim': '\u2AF3',
|
|
'parsl': '\u2AFD',
|
|
'pcy': '\u043F',
|
|
'percnt': '\u0025',
|
|
'period': '\u002E',
|
|
'permil': '\u2030',
|
|
'pertenk': '\u2031',
|
|
'pfr': '\uD835\uDD2D',
|
|
'phi': '\u03C6',
|
|
'phiv': '\u03D5',
|
|
'straightphi': '\u03D5',
|
|
'varphi': '\u03D5',
|
|
'phone': '\u260E',
|
|
'pi': '\u03C0',
|
|
'piv': '\u03D6',
|
|
'varpi': '\u03D6',
|
|
'planckh': '\u210E',
|
|
'plus': '\u002B',
|
|
'plusacir': '\u2A23',
|
|
'pluscir': '\u2A22',
|
|
'plusdu': '\u2A25',
|
|
'pluse': '\u2A72',
|
|
'plussim': '\u2A26',
|
|
'plustwo': '\u2A27',
|
|
'pointint': '\u2A15',
|
|
'popf': '\uD835\uDD61',
|
|
'pound': '\u00A3',
|
|
'prE': '\u2AB3',
|
|
'prap': '\u2AB7',
|
|
'precapprox': '\u2AB7',
|
|
'precnapprox': '\u2AB9',
|
|
'prnap': '\u2AB9',
|
|
'precneqq': '\u2AB5',
|
|
'prnE': '\u2AB5',
|
|
'precnsim': '\u22E8',
|
|
'prnsim': '\u22E8',
|
|
'prime': '\u2032',
|
|
'profalar': '\u232E',
|
|
'profline': '\u2312',
|
|
'profsurf': '\u2313',
|
|
'prurel': '\u22B0',
|
|
'pscr': '\uD835\uDCC5',
|
|
'psi': '\u03C8',
|
|
'puncsp': '\u2008',
|
|
'qfr': '\uD835\uDD2E',
|
|
'qopf': '\uD835\uDD62',
|
|
'qprime': '\u2057',
|
|
'qscr': '\uD835\uDCC6',
|
|
'quatint': '\u2A16',
|
|
'quest': '\u003F',
|
|
'rAtail': '\u291C',
|
|
'rHar': '\u2964',
|
|
'race': '\u223D\u0331',
|
|
'racute': '\u0155',
|
|
'raemptyv': '\u29B3',
|
|
'rangd': '\u2992',
|
|
'range': '\u29A5',
|
|
'raquo': '\u00BB',
|
|
'rarrap': '\u2975',
|
|
'rarrbfs': '\u2920',
|
|
'rarrc': '\u2933',
|
|
'rarrfs': '\u291E',
|
|
'rarrpl': '\u2945',
|
|
'rarrsim': '\u2974',
|
|
'rarrtl': '\u21A3',
|
|
'rightarrowtail': '\u21A3',
|
|
'rarrw': '\u219D',
|
|
'rightsquigarrow': '\u219D',
|
|
'ratail': '\u291A',
|
|
'ratio': '\u2236',
|
|
'rbbrk': '\u2773',
|
|
'rbrace': '\u007D',
|
|
'rcub': '\u007D',
|
|
'rbrack': '\u005D',
|
|
'rsqb': '\u005D',
|
|
'rbrke': '\u298C',
|
|
'rbrksld': '\u298E',
|
|
'rbrkslu': '\u2990',
|
|
'rcaron': '\u0159',
|
|
'rcedil': '\u0157',
|
|
'rcy': '\u0440',
|
|
'rdca': '\u2937',
|
|
'rdldhar': '\u2969',
|
|
'rdsh': '\u21B3',
|
|
'rect': '\u25AD',
|
|
'rfisht': '\u297D',
|
|
'rfr': '\uD835\uDD2F',
|
|
'rharul': '\u296C',
|
|
'rho': '\u03C1',
|
|
'rhov': '\u03F1',
|
|
'varrho': '\u03F1',
|
|
'rightrightarrows': '\u21C9',
|
|
'rrarr': '\u21C9',
|
|
'rightthreetimes': '\u22CC',
|
|
'rthree': '\u22CC',
|
|
'ring': '\u02DA',
|
|
'rlm': '\u200F',
|
|
'rmoust': '\u23B1',
|
|
'rmoustache': '\u23B1',
|
|
'rnmid': '\u2AEE',
|
|
'roang': '\u27ED',
|
|
'roarr': '\u21FE',
|
|
'ropar': '\u2986',
|
|
'ropf': '\uD835\uDD63',
|
|
'roplus': '\u2A2E',
|
|
'rotimes': '\u2A35',
|
|
'rpar': '\u0029',
|
|
'rpargt': '\u2994',
|
|
'rppolint': '\u2A12',
|
|
'rsaquo': '\u203A',
|
|
'rscr': '\uD835\uDCC7',
|
|
'rtimes': '\u22CA',
|
|
'rtri': '\u25B9',
|
|
'triangleright': '\u25B9',
|
|
'rtriltri': '\u29CE',
|
|
'ruluhar': '\u2968',
|
|
'rx': '\u211E',
|
|
'sacute': '\u015B',
|
|
'scE': '\u2AB4',
|
|
'scap': '\u2AB8',
|
|
'succapprox': '\u2AB8',
|
|
'scaron': '\u0161',
|
|
'scedil': '\u015F',
|
|
'scirc': '\u015D',
|
|
'scnE': '\u2AB6',
|
|
'succneqq': '\u2AB6',
|
|
'scnap': '\u2ABA',
|
|
'succnapprox': '\u2ABA',
|
|
'scnsim': '\u22E9',
|
|
'succnsim': '\u22E9',
|
|
'scpolint': '\u2A13',
|
|
'scy': '\u0441',
|
|
'sdot': '\u22C5',
|
|
'sdote': '\u2A66',
|
|
'seArr': '\u21D8',
|
|
'sect': '\u00A7',
|
|
'semi': '\u003B',
|
|
'seswar': '\u2929',
|
|
'tosa': '\u2929',
|
|
'sext': '\u2736',
|
|
'sfr': '\uD835\uDD30',
|
|
'sharp': '\u266F',
|
|
'shchcy': '\u0449',
|
|
'shcy': '\u0448',
|
|
'shy': '\u00AD',
|
|
'sigma': '\u03C3',
|
|
'sigmaf': '\u03C2',
|
|
'sigmav': '\u03C2',
|
|
'varsigma': '\u03C2',
|
|
'simdot': '\u2A6A',
|
|
'simg': '\u2A9E',
|
|
'simgE': '\u2AA0',
|
|
'siml': '\u2A9D',
|
|
'simlE': '\u2A9F',
|
|
'simne': '\u2246',
|
|
'simplus': '\u2A24',
|
|
'simrarr': '\u2972',
|
|
'smashp': '\u2A33',
|
|
'smeparsl': '\u29E4',
|
|
'smile': '\u2323',
|
|
'ssmile': '\u2323',
|
|
'smt': '\u2AAA',
|
|
'smte': '\u2AAC',
|
|
'smtes': '\u2AAC\uFE00',
|
|
'softcy': '\u044C',
|
|
'sol': '\u002F',
|
|
'solb': '\u29C4',
|
|
'solbar': '\u233F',
|
|
'sopf': '\uD835\uDD64',
|
|
'spades': '\u2660',
|
|
'spadesuit': '\u2660',
|
|
'sqcaps': '\u2293\uFE00',
|
|
'sqcups': '\u2294\uFE00',
|
|
'sscr': '\uD835\uDCC8',
|
|
'star': '\u2606',
|
|
'sub': '\u2282',
|
|
'subset': '\u2282',
|
|
'subE': '\u2AC5',
|
|
'subseteqq': '\u2AC5',
|
|
'subdot': '\u2ABD',
|
|
'subedot': '\u2AC3',
|
|
'submult': '\u2AC1',
|
|
'subnE': '\u2ACB',
|
|
'subsetneqq': '\u2ACB',
|
|
'subne': '\u228A',
|
|
'subsetneq': '\u228A',
|
|
'subplus': '\u2ABF',
|
|
'subrarr': '\u2979',
|
|
'subsim': '\u2AC7',
|
|
'subsub': '\u2AD5',
|
|
'subsup': '\u2AD3',
|
|
'sung': '\u266A',
|
|
'sup1': '\u00B9',
|
|
'sup2': '\u00B2',
|
|
'sup3': '\u00B3',
|
|
'supE': '\u2AC6',
|
|
'supseteqq': '\u2AC6',
|
|
'supdot': '\u2ABE',
|
|
'supdsub': '\u2AD8',
|
|
'supedot': '\u2AC4',
|
|
'suphsol': '\u27C9',
|
|
'suphsub': '\u2AD7',
|
|
'suplarr': '\u297B',
|
|
'supmult': '\u2AC2',
|
|
'supnE': '\u2ACC',
|
|
'supsetneqq': '\u2ACC',
|
|
'supne': '\u228B',
|
|
'supsetneq': '\u228B',
|
|
'supplus': '\u2AC0',
|
|
'supsim': '\u2AC8',
|
|
'supsub': '\u2AD4',
|
|
'supsup': '\u2AD6',
|
|
'swArr': '\u21D9',
|
|
'swnwar': '\u292A',
|
|
'szlig': '\u00DF',
|
|
'target': '\u2316',
|
|
'tau': '\u03C4',
|
|
'tcaron': '\u0165',
|
|
'tcedil': '\u0163',
|
|
'tcy': '\u0442',
|
|
'telrec': '\u2315',
|
|
'tfr': '\uD835\uDD31',
|
|
'theta': '\u03B8',
|
|
'thetasym': '\u03D1',
|
|
'thetav': '\u03D1',
|
|
'vartheta': '\u03D1',
|
|
'thorn': '\u00FE',
|
|
'times': '\u00D7',
|
|
'timesbar': '\u2A31',
|
|
'timesd': '\u2A30',
|
|
'topbot': '\u2336',
|
|
'topcir': '\u2AF1',
|
|
'topf': '\uD835\uDD65',
|
|
'topfork': '\u2ADA',
|
|
'tprime': '\u2034',
|
|
'triangle': '\u25B5',
|
|
'utri': '\u25B5',
|
|
'triangleq': '\u225C',
|
|
'trie': '\u225C',
|
|
'tridot': '\u25EC',
|
|
'triminus': '\u2A3A',
|
|
'triplus': '\u2A39',
|
|
'trisb': '\u29CD',
|
|
'tritime': '\u2A3B',
|
|
'trpezium': '\u23E2',
|
|
'tscr': '\uD835\uDCC9',
|
|
'tscy': '\u0446',
|
|
'tshcy': '\u045B',
|
|
'tstrok': '\u0167',
|
|
'uHar': '\u2963',
|
|
'uacute': '\u00FA',
|
|
'ubrcy': '\u045E',
|
|
'ubreve': '\u016D',
|
|
'ucirc': '\u00FB',
|
|
'ucy': '\u0443',
|
|
'udblac': '\u0171',
|
|
'ufisht': '\u297E',
|
|
'ufr': '\uD835\uDD32',
|
|
'ugrave': '\u00F9',
|
|
'uhblk': '\u2580',
|
|
'ulcorn': '\u231C',
|
|
'ulcorner': '\u231C',
|
|
'ulcrop': '\u230F',
|
|
'ultri': '\u25F8',
|
|
'umacr': '\u016B',
|
|
'uogon': '\u0173',
|
|
'uopf': '\uD835\uDD66',
|
|
'upsi': '\u03C5',
|
|
'upsilon': '\u03C5',
|
|
'upuparrows': '\u21C8',
|
|
'uuarr': '\u21C8',
|
|
'urcorn': '\u231D',
|
|
'urcorner': '\u231D',
|
|
'urcrop': '\u230E',
|
|
'uring': '\u016F',
|
|
'urtri': '\u25F9',
|
|
'uscr': '\uD835\uDCCA',
|
|
'utdot': '\u22F0',
|
|
'utilde': '\u0169',
|
|
'uuml': '\u00FC',
|
|
'uwangle': '\u29A7',
|
|
'vBar': '\u2AE8',
|
|
'vBarv': '\u2AE9',
|
|
'vangrt': '\u299C',
|
|
'varsubsetneq': '\u228A\uFE00',
|
|
'vsubne': '\u228A\uFE00',
|
|
'varsubsetneqq': '\u2ACB\uFE00',
|
|
'vsubnE': '\u2ACB\uFE00',
|
|
'varsupsetneq': '\u228B\uFE00',
|
|
'vsupne': '\u228B\uFE00',
|
|
'varsupsetneqq': '\u2ACC\uFE00',
|
|
'vsupnE': '\u2ACC\uFE00',
|
|
'vcy': '\u0432',
|
|
'veebar': '\u22BB',
|
|
'veeeq': '\u225A',
|
|
'vellip': '\u22EE',
|
|
'vfr': '\uD835\uDD33',
|
|
'vopf': '\uD835\uDD67',
|
|
'vscr': '\uD835\uDCCB',
|
|
'vzigzag': '\u299A',
|
|
'wcirc': '\u0175',
|
|
'wedbar': '\u2A5F',
|
|
'wedgeq': '\u2259',
|
|
'weierp': '\u2118',
|
|
'wp': '\u2118',
|
|
'wfr': '\uD835\uDD34',
|
|
'wopf': '\uD835\uDD68',
|
|
'wscr': '\uD835\uDCCC',
|
|
'xfr': '\uD835\uDD35',
|
|
'xi': '\u03BE',
|
|
'xnis': '\u22FB',
|
|
'xopf': '\uD835\uDD69',
|
|
'xscr': '\uD835\uDCCD',
|
|
'yacute': '\u00FD',
|
|
'yacy': '\u044F',
|
|
'ycirc': '\u0177',
|
|
'ycy': '\u044B',
|
|
'yen': '\u00A5',
|
|
'yfr': '\uD835\uDD36',
|
|
'yicy': '\u0457',
|
|
'yopf': '\uD835\uDD6A',
|
|
'yscr': '\uD835\uDCCE',
|
|
'yucy': '\u044E',
|
|
'yuml': '\u00FF',
|
|
'zacute': '\u017A',
|
|
'zcaron': '\u017E',
|
|
'zcy': '\u0437',
|
|
'zdot': '\u017C',
|
|
'zeta': '\u03B6',
|
|
'zfr': '\uD835\uDD37',
|
|
'zhcy': '\u0436',
|
|
'zigrarr': '\u21DD',
|
|
'zopf': '\uD835\uDD6B',
|
|
'zscr': '\uD835\uDCCF',
|
|
'zwj': '\u200D',
|
|
'zwnj': '\u200C'
|
|
};
|
|
const NGSP_UNICODE = '\uE500';
|
|
NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
|
|
|
|
class TokenizeResult {
|
|
tokens;
|
|
errors;
|
|
nonNormalizedIcuExpressions;
|
|
constructor(tokens, errors, nonNormalizedIcuExpressions) {
|
|
this.tokens = tokens;
|
|
this.errors = errors;
|
|
this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
|
|
}
|
|
}
|
|
function tokenize(source, url, getTagDefinition, options = {}) {
|
|
const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
|
|
tokenizer.tokenize();
|
|
return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
|
|
}
|
|
const _CR_OR_CRLF_REGEXP = /\r\n?/g;
|
|
function _unexpectedCharacterErrorMsg(charCode) {
|
|
const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
|
|
return `Unexpected character "${char}"`;
|
|
}
|
|
function _unknownEntityErrorMsg(entitySrc) {
|
|
return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
|
|
}
|
|
function _unparsableEntityErrorMsg(type, entityStr) {
|
|
return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
|
|
}
|
|
var CharacterReferenceType;
|
|
(function (CharacterReferenceType) {
|
|
CharacterReferenceType["HEX"] = "hexadecimal";
|
|
CharacterReferenceType["DEC"] = "decimal";
|
|
})(CharacterReferenceType || (CharacterReferenceType = {}));
|
|
const SUPPORTED_BLOCKS = ['@if', '@else', '@for', '@switch', '@case', '@default', '@empty', '@defer', '@placeholder', '@loading', '@error'];
|
|
const INTERPOLATION = {
|
|
start: '{{',
|
|
end: '}}'
|
|
};
|
|
class _Tokenizer {
|
|
_getTagDefinition;
|
|
_cursor;
|
|
_tokenizeIcu;
|
|
_leadingTriviaCodePoints;
|
|
_currentTokenStart = null;
|
|
_currentTokenType = null;
|
|
_expansionCaseStack = [];
|
|
_openDirectiveCount = 0;
|
|
_inInterpolation = false;
|
|
_preserveLineEndings;
|
|
_i18nNormalizeLineEndingsInICUs;
|
|
_tokenizeBlocks;
|
|
_tokenizeLet;
|
|
_selectorlessEnabled;
|
|
tokens = [];
|
|
errors = [];
|
|
nonNormalizedIcuExpressions = [];
|
|
constructor(_file, _getTagDefinition, options) {
|
|
this._getTagDefinition = _getTagDefinition;
|
|
this._tokenizeIcu = options.tokenizeExpansionForms || false;
|
|
this._leadingTriviaCodePoints = options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
|
|
const range = options.range || {
|
|
endPos: _file.content.length,
|
|
startPos: 0,
|
|
startLine: 0,
|
|
startCol: 0
|
|
};
|
|
this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) : new PlainCharacterCursor(_file, range);
|
|
this._preserveLineEndings = options.preserveLineEndings || false;
|
|
this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
|
|
this._tokenizeBlocks = options.tokenizeBlocks ?? true;
|
|
this._tokenizeLet = options.tokenizeLet ?? true;
|
|
this._selectorlessEnabled = options.selectorlessEnabled ?? false;
|
|
try {
|
|
this._cursor.init();
|
|
} catch (e) {
|
|
this.handleError(e);
|
|
}
|
|
}
|
|
_processCarriageReturns(content) {
|
|
if (this._preserveLineEndings) {
|
|
return content;
|
|
}
|
|
return content.replace(_CR_OR_CRLF_REGEXP, '\n');
|
|
}
|
|
tokenize() {
|
|
while (this._cursor.peek() !== $EOF) {
|
|
const start = this._cursor.clone();
|
|
try {
|
|
if (this._attemptCharCode($LT)) {
|
|
if (this._attemptCharCode($BANG)) {
|
|
if (this._attemptCharCode($LBRACKET)) {
|
|
this._consumeCdata(start);
|
|
} else if (this._attemptCharCode($MINUS)) {
|
|
this._consumeComment(start);
|
|
} else {
|
|
this._consumeDocType(start);
|
|
}
|
|
} else if (this._attemptCharCode($SLASH)) {
|
|
this._consumeTagClose(start);
|
|
} else {
|
|
this._consumeTagOpen(start);
|
|
}
|
|
} else if (this._tokenizeLet && this._cursor.peek() === $AT && !this._inInterpolation && this._isLetStart()) {
|
|
this._consumeLetDeclaration(start);
|
|
} else if (this._tokenizeBlocks && this._isBlockStart()) {
|
|
this._consumeBlockStart(start);
|
|
} else if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansionCase() && !this._isInExpansionForm() && this._attemptCharCode($RBRACE)) {
|
|
this._consumeBlockEnd(start);
|
|
} else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
|
|
this._consumeWithInterpolation(5, 8, () => this._isTextEnd(), () => this._isTagStart());
|
|
}
|
|
} catch (e) {
|
|
this.handleError(e);
|
|
}
|
|
}
|
|
this._beginToken(41);
|
|
this._endToken([]);
|
|
}
|
|
_getBlockName() {
|
|
let spacesInNameAllowed = false;
|
|
const nameCursor = this._cursor.clone();
|
|
this._attemptCharCodeUntilFn(code => {
|
|
if (isWhitespace(code)) {
|
|
return !spacesInNameAllowed;
|
|
}
|
|
if (isBlockNameChar(code)) {
|
|
spacesInNameAllowed = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return this._cursor.getChars(nameCursor).trim();
|
|
}
|
|
_consumeBlockStart(start) {
|
|
this._requireCharCode($AT);
|
|
this._beginToken(24, start);
|
|
const startToken = this._endToken([this._getBlockName()]);
|
|
if (this._cursor.peek() === $LPAREN) {
|
|
this._cursor.advance();
|
|
this._consumeBlockParameters();
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
if (this._attemptCharCode($RPAREN)) {
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
} else {
|
|
startToken.type = 28;
|
|
return;
|
|
}
|
|
}
|
|
if (this._attemptCharCode($LBRACE)) {
|
|
this._beginToken(25);
|
|
this._endToken([]);
|
|
} else if (this._isBlockStart() && (startToken.parts[0] === 'case' || startToken.parts[0] === 'default')) {
|
|
this._beginToken(25);
|
|
this._endToken([]);
|
|
this._beginToken(26);
|
|
this._endToken([]);
|
|
} else {
|
|
startToken.type = 28;
|
|
}
|
|
}
|
|
_consumeBlockEnd(start) {
|
|
this._beginToken(26, start);
|
|
this._endToken([]);
|
|
}
|
|
_consumeBlockParameters() {
|
|
this._attemptCharCodeUntilFn(isBlockParameterChar);
|
|
while (this._cursor.peek() !== $RPAREN && this._cursor.peek() !== $EOF) {
|
|
this._beginToken(27);
|
|
const start = this._cursor.clone();
|
|
let inQuote = null;
|
|
let openParens = 0;
|
|
while (this._cursor.peek() !== $SEMICOLON && this._cursor.peek() !== $EOF || inQuote !== null) {
|
|
const char = this._cursor.peek();
|
|
if (char === $BACKSLASH) {
|
|
this._cursor.advance();
|
|
} else if (char === inQuote) {
|
|
inQuote = null;
|
|
} else if (inQuote === null && isQuote(char)) {
|
|
inQuote = char;
|
|
} else if (char === $LPAREN && inQuote === null) {
|
|
openParens++;
|
|
} else if (char === $RPAREN && inQuote === null) {
|
|
if (openParens === 0) {
|
|
break;
|
|
} else if (openParens > 0) {
|
|
openParens--;
|
|
}
|
|
}
|
|
this._cursor.advance();
|
|
}
|
|
this._endToken([this._cursor.getChars(start)]);
|
|
this._attemptCharCodeUntilFn(isBlockParameterChar);
|
|
}
|
|
}
|
|
_consumeLetDeclaration(start) {
|
|
this._requireStr('@let');
|
|
this._beginToken(29, start);
|
|
if (isWhitespace(this._cursor.peek())) {
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
} else {
|
|
const token = this._endToken([this._cursor.getChars(start)]);
|
|
token.type = 32;
|
|
return;
|
|
}
|
|
const startToken = this._endToken([this._getLetDeclarationName()]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
if (!this._attemptCharCode($EQ)) {
|
|
startToken.type = 32;
|
|
return;
|
|
}
|
|
this._attemptCharCodeUntilFn(code => isNotWhitespace(code) && !isNewLine(code));
|
|
this._consumeLetDeclarationValue();
|
|
const endChar = this._cursor.peek();
|
|
if (endChar === $SEMICOLON) {
|
|
this._beginToken(31);
|
|
this._endToken([]);
|
|
this._cursor.advance();
|
|
} else {
|
|
startToken.type = 32;
|
|
startToken.sourceSpan = this._cursor.getSpan(start);
|
|
}
|
|
}
|
|
_getLetDeclarationName() {
|
|
const nameCursor = this._cursor.clone();
|
|
let allowDigit = false;
|
|
this._attemptCharCodeUntilFn(code => {
|
|
if (isAsciiLetter(code) || code === $$ || code === $_ || allowDigit && isDigit(code)) {
|
|
allowDigit = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return this._cursor.getChars(nameCursor).trim();
|
|
}
|
|
_consumeLetDeclarationValue() {
|
|
const start = this._cursor.clone();
|
|
this._beginToken(30, start);
|
|
while (this._cursor.peek() !== $EOF) {
|
|
const char = this._cursor.peek();
|
|
if (char === $SEMICOLON) {
|
|
break;
|
|
}
|
|
if (isQuote(char)) {
|
|
this._cursor.advance();
|
|
this._attemptCharCodeUntilFn(inner => {
|
|
if (inner === $BACKSLASH) {
|
|
this._cursor.advance();
|
|
return false;
|
|
}
|
|
return inner === char;
|
|
});
|
|
}
|
|
this._cursor.advance();
|
|
}
|
|
this._endToken([this._cursor.getChars(start)]);
|
|
}
|
|
_tokenizeExpansionForm() {
|
|
if (this.isExpansionFormStart()) {
|
|
this._consumeExpansionFormStart();
|
|
return true;
|
|
}
|
|
if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
|
|
this._consumeExpansionCaseStart();
|
|
return true;
|
|
}
|
|
if (this._cursor.peek() === $RBRACE) {
|
|
if (this._isInExpansionCase()) {
|
|
this._consumeExpansionCaseEnd();
|
|
return true;
|
|
}
|
|
if (this._isInExpansionForm()) {
|
|
this._consumeExpansionFormEnd();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
_beginToken(type, start = this._cursor.clone()) {
|
|
this._currentTokenStart = start;
|
|
this._currentTokenType = type;
|
|
}
|
|
_endToken(parts, end) {
|
|
if (this._currentTokenStart === null) {
|
|
throw new ParseError(this._cursor.getSpan(end), 'Programming error - attempted to end a token when there was no start to the token');
|
|
}
|
|
if (this._currentTokenType === null) {
|
|
throw new ParseError(this._cursor.getSpan(this._currentTokenStart), 'Programming error - attempted to end a token which has no token type');
|
|
}
|
|
const token = {
|
|
type: this._currentTokenType,
|
|
parts,
|
|
sourceSpan: (end ?? this._cursor).getSpan(this._currentTokenStart, this._leadingTriviaCodePoints)
|
|
};
|
|
this.tokens.push(token);
|
|
this._currentTokenStart = null;
|
|
this._currentTokenType = null;
|
|
return token;
|
|
}
|
|
_createError(msg, span) {
|
|
if (this._isInExpansionForm()) {
|
|
msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
|
|
}
|
|
const error = new ParseError(span, msg);
|
|
this._currentTokenStart = null;
|
|
this._currentTokenType = null;
|
|
return error;
|
|
}
|
|
handleError(e) {
|
|
if (e instanceof CursorError) {
|
|
e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
|
|
}
|
|
if (e instanceof ParseError) {
|
|
this.errors.push(e);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
_attemptCharCode(charCode) {
|
|
if (this._cursor.peek() === charCode) {
|
|
this._cursor.advance();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
_attemptCharCodeCaseInsensitive(charCode) {
|
|
if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
|
|
this._cursor.advance();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
_requireCharCode(charCode) {
|
|
const location = this._cursor.clone();
|
|
if (!this._attemptCharCode(charCode)) {
|
|
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
|
}
|
|
}
|
|
_attemptStr(chars) {
|
|
const len = chars.length;
|
|
if (this._cursor.charsLeft() < len) {
|
|
return false;
|
|
}
|
|
const initialPosition = this._cursor.clone();
|
|
for (let i = 0; i < len; i++) {
|
|
if (!this._attemptCharCode(chars.charCodeAt(i))) {
|
|
this._cursor = initialPosition;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
_attemptStrCaseInsensitive(chars) {
|
|
for (let i = 0; i < chars.length; i++) {
|
|
if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
_requireStr(chars) {
|
|
const location = this._cursor.clone();
|
|
if (!this._attemptStr(chars)) {
|
|
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
|
}
|
|
}
|
|
_attemptCharCodeUntilFn(predicate) {
|
|
while (!predicate(this._cursor.peek())) {
|
|
this._cursor.advance();
|
|
}
|
|
}
|
|
_requireCharCodeUntilFn(predicate, len) {
|
|
const start = this._cursor.clone();
|
|
this._attemptCharCodeUntilFn(predicate);
|
|
if (this._cursor.diff(start) < len) {
|
|
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
|
}
|
|
}
|
|
_attemptUntilChar(char) {
|
|
while (this._cursor.peek() !== char) {
|
|
this._cursor.advance();
|
|
}
|
|
}
|
|
_readChar() {
|
|
const char = String.fromCodePoint(this._cursor.peek());
|
|
this._cursor.advance();
|
|
return char;
|
|
}
|
|
_peekStr(chars) {
|
|
const len = chars.length;
|
|
if (this._cursor.charsLeft() < len) {
|
|
return false;
|
|
}
|
|
const cursor = this._cursor.clone();
|
|
for (let i = 0; i < len; i++) {
|
|
if (cursor.peek() !== chars.charCodeAt(i)) {
|
|
return false;
|
|
}
|
|
cursor.advance();
|
|
}
|
|
return true;
|
|
}
|
|
_isBlockStart() {
|
|
return this._cursor.peek() === $AT && SUPPORTED_BLOCKS.some(blockName => this._peekStr(blockName));
|
|
}
|
|
_isLetStart() {
|
|
return this._cursor.peek() === $AT && this._peekStr('@let');
|
|
}
|
|
_consumeEntity(textTokenType) {
|
|
this._beginToken(9);
|
|
const start = this._cursor.clone();
|
|
this._cursor.advance();
|
|
if (this._attemptCharCode($HASH)) {
|
|
const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
|
|
const codeStart = this._cursor.clone();
|
|
this._attemptCharCodeUntilFn(isDigitEntityEnd);
|
|
if (this._cursor.peek() != $SEMICOLON) {
|
|
this._cursor.advance();
|
|
const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
|
|
throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
|
|
}
|
|
const strNum = this._cursor.getChars(codeStart);
|
|
this._cursor.advance();
|
|
try {
|
|
const charCode = parseInt(strNum, isHex ? 16 : 10);
|
|
this._endToken([String.fromCodePoint(charCode), this._cursor.getChars(start)]);
|
|
} catch {
|
|
throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
|
|
}
|
|
} else {
|
|
const nameStart = this._cursor.clone();
|
|
this._attemptCharCodeUntilFn(isNamedEntityEnd);
|
|
if (this._cursor.peek() != $SEMICOLON) {
|
|
this._beginToken(textTokenType, start);
|
|
this._cursor = nameStart;
|
|
this._endToken(['&']);
|
|
} else {
|
|
const name = this._cursor.getChars(nameStart);
|
|
this._cursor.advance();
|
|
const char = NAMED_ENTITIES.hasOwnProperty(name) && NAMED_ENTITIES[name];
|
|
if (!char) {
|
|
throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
|
|
}
|
|
this._endToken([char, `&${name};`]);
|
|
}
|
|
}
|
|
}
|
|
_consumeRawText(consumeEntities, endMarkerPredicate) {
|
|
this._beginToken(consumeEntities ? 6 : 7);
|
|
const parts = [];
|
|
while (true) {
|
|
const tagCloseStart = this._cursor.clone();
|
|
const foundEndMarker = endMarkerPredicate();
|
|
this._cursor = tagCloseStart;
|
|
if (foundEndMarker) {
|
|
break;
|
|
}
|
|
if (consumeEntities && this._cursor.peek() === $AMPERSAND) {
|
|
this._endToken([this._processCarriageReturns(parts.join(''))]);
|
|
parts.length = 0;
|
|
this._consumeEntity(6);
|
|
this._beginToken(6);
|
|
} else {
|
|
parts.push(this._readChar());
|
|
}
|
|
}
|
|
this._endToken([this._processCarriageReturns(parts.join(''))]);
|
|
}
|
|
_consumeComment(start) {
|
|
this._beginToken(10, start);
|
|
this._requireCharCode($MINUS);
|
|
this._endToken([]);
|
|
this._consumeRawText(false, () => this._attemptStr('-->'));
|
|
this._beginToken(11);
|
|
this._requireStr('-->');
|
|
this._endToken([]);
|
|
}
|
|
_consumeCdata(start) {
|
|
this._beginToken(12, start);
|
|
this._requireStr('CDATA[');
|
|
this._endToken([]);
|
|
this._consumeRawText(false, () => this._attemptStr(']]>'));
|
|
this._beginToken(13);
|
|
this._requireStr(']]>');
|
|
this._endToken([]);
|
|
}
|
|
_consumeDocType(start) {
|
|
this._beginToken(18, start);
|
|
const contentStart = this._cursor.clone();
|
|
this._attemptUntilChar($GT);
|
|
const content = this._cursor.getChars(contentStart);
|
|
this._cursor.advance();
|
|
this._endToken([content]);
|
|
}
|
|
_consumePrefixAndName(endPredicate) {
|
|
const nameOrPrefixStart = this._cursor.clone();
|
|
let prefix = '';
|
|
while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
|
|
this._cursor.advance();
|
|
}
|
|
let nameStart;
|
|
if (this._cursor.peek() === $COLON) {
|
|
prefix = this._cursor.getChars(nameOrPrefixStart);
|
|
this._cursor.advance();
|
|
nameStart = this._cursor.clone();
|
|
} else {
|
|
nameStart = nameOrPrefixStart;
|
|
}
|
|
this._requireCharCodeUntilFn(endPredicate, prefix === '' ? 0 : 1);
|
|
const name = this._cursor.getChars(nameStart);
|
|
return [prefix, name];
|
|
}
|
|
_consumeTagOpen(start) {
|
|
let tagName;
|
|
let prefix;
|
|
let closingTagName;
|
|
let openToken;
|
|
try {
|
|
if (this._selectorlessEnabled && isSelectorlessNameStart(this._cursor.peek())) {
|
|
openToken = this._consumeComponentOpenStart(start);
|
|
[closingTagName, prefix, tagName] = openToken.parts;
|
|
if (prefix) {
|
|
closingTagName += `:${prefix}`;
|
|
}
|
|
if (tagName) {
|
|
closingTagName += `:${tagName}`;
|
|
}
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
} else {
|
|
if (!isAsciiLetter(this._cursor.peek())) {
|
|
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
|
}
|
|
openToken = this._consumeTagOpenStart(start);
|
|
prefix = openToken.parts[0];
|
|
tagName = closingTagName = openToken.parts[1];
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
}
|
|
while (!isAttributeTerminator(this._cursor.peek())) {
|
|
if (this._selectorlessEnabled && this._cursor.peek() === $AT) {
|
|
const start = this._cursor.clone();
|
|
const nameStart = start.clone();
|
|
nameStart.advance();
|
|
if (isSelectorlessNameStart(nameStart.peek())) {
|
|
this._consumeDirective(start, nameStart);
|
|
}
|
|
} else {
|
|
this._consumeAttribute();
|
|
}
|
|
}
|
|
if (openToken.type === 33) {
|
|
this._consumeComponentOpenEnd();
|
|
} else {
|
|
this._consumeTagOpenEnd();
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof ParseError) {
|
|
if (openToken) {
|
|
openToken.type = openToken.type === 33 ? 37 : 4;
|
|
} else {
|
|
this._beginToken(5, start);
|
|
this._endToken(['<']);
|
|
}
|
|
return;
|
|
}
|
|
throw e;
|
|
}
|
|
const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
|
|
if (contentTokenType === TagContentType.RAW_TEXT) {
|
|
this._consumeRawTextWithTagClose(openToken, closingTagName, false);
|
|
} else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
|
|
this._consumeRawTextWithTagClose(openToken, closingTagName, true);
|
|
}
|
|
}
|
|
_consumeRawTextWithTagClose(openToken, tagName, consumeEntities) {
|
|
this._consumeRawText(consumeEntities, () => {
|
|
if (!this._attemptCharCode($LT)) return false;
|
|
if (!this._attemptCharCode($SLASH)) return false;
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
if (!this._attemptStrCaseInsensitive(tagName)) return false;
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
return this._attemptCharCode($GT);
|
|
});
|
|
this._beginToken(openToken.type === 33 ? 36 : 3);
|
|
this._requireCharCodeUntilFn(code => code === $GT, 3);
|
|
this._cursor.advance();
|
|
this._endToken(openToken.parts);
|
|
}
|
|
_consumeTagOpenStart(start) {
|
|
this._beginToken(0, start);
|
|
const parts = this._consumePrefixAndName(isNameEnd);
|
|
return this._endToken(parts);
|
|
}
|
|
_consumeComponentOpenStart(start) {
|
|
this._beginToken(33, start);
|
|
const parts = this._consumeComponentName();
|
|
return this._endToken(parts);
|
|
}
|
|
_consumeComponentName() {
|
|
const nameStart = this._cursor.clone();
|
|
while (isSelectorlessNameChar(this._cursor.peek())) {
|
|
this._cursor.advance();
|
|
}
|
|
const name = this._cursor.getChars(nameStart);
|
|
let prefix = '';
|
|
let tagName = '';
|
|
if (this._cursor.peek() === $COLON) {
|
|
this._cursor.advance();
|
|
[prefix, tagName] = this._consumePrefixAndName(isNameEnd);
|
|
}
|
|
return [name, prefix, tagName];
|
|
}
|
|
_consumeAttribute() {
|
|
this._consumeAttributeName();
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
if (this._attemptCharCode($EQ)) {
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._consumeAttributeValue();
|
|
}
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
}
|
|
_consumeAttributeName() {
|
|
const attrNameStart = this._cursor.peek();
|
|
if (attrNameStart === $SQ || attrNameStart === $DQ) {
|
|
throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
|
|
}
|
|
this._beginToken(14);
|
|
let nameEndPredicate;
|
|
if (this._openDirectiveCount > 0) {
|
|
let openParens = 0;
|
|
nameEndPredicate = code => {
|
|
if (this._openDirectiveCount > 0) {
|
|
if (code === $LPAREN) {
|
|
openParens++;
|
|
} else if (code === $RPAREN) {
|
|
if (openParens === 0) {
|
|
return true;
|
|
}
|
|
openParens--;
|
|
}
|
|
}
|
|
return isNameEnd(code);
|
|
};
|
|
} else if (attrNameStart === $LBRACKET) {
|
|
let openBrackets = 0;
|
|
nameEndPredicate = code => {
|
|
if (code === $LBRACKET) {
|
|
openBrackets++;
|
|
} else if (code === $RBRACKET) {
|
|
openBrackets--;
|
|
}
|
|
return openBrackets <= 0 ? isNameEnd(code) : isNewLine(code);
|
|
};
|
|
} else {
|
|
nameEndPredicate = isNameEnd;
|
|
}
|
|
const prefixAndName = this._consumePrefixAndName(nameEndPredicate);
|
|
this._endToken(prefixAndName);
|
|
}
|
|
_consumeAttributeValue() {
|
|
if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
|
|
const quoteChar = this._cursor.peek();
|
|
this._consumeQuote(quoteChar);
|
|
const endPredicate = () => this._cursor.peek() === quoteChar;
|
|
this._consumeWithInterpolation(16, 17, endPredicate, endPredicate);
|
|
this._consumeQuote(quoteChar);
|
|
} else {
|
|
const endPredicate = () => isNameEnd(this._cursor.peek());
|
|
this._consumeWithInterpolation(16, 17, endPredicate, endPredicate);
|
|
}
|
|
}
|
|
_consumeQuote(quoteChar) {
|
|
this._beginToken(15);
|
|
this._requireCharCode(quoteChar);
|
|
this._endToken([String.fromCodePoint(quoteChar)]);
|
|
}
|
|
_consumeTagOpenEnd() {
|
|
const tokenType = this._attemptCharCode($SLASH) ? 2 : 1;
|
|
this._beginToken(tokenType);
|
|
this._requireCharCode($GT);
|
|
this._endToken([]);
|
|
}
|
|
_consumeComponentOpenEnd() {
|
|
const tokenType = this._attemptCharCode($SLASH) ? 35 : 34;
|
|
this._beginToken(tokenType);
|
|
this._requireCharCode($GT);
|
|
this._endToken([]);
|
|
}
|
|
_consumeTagClose(start) {
|
|
if (this._selectorlessEnabled) {
|
|
const clone = start.clone();
|
|
while (clone.peek() !== $GT && !isSelectorlessNameStart(clone.peek())) {
|
|
clone.advance();
|
|
}
|
|
if (isSelectorlessNameStart(clone.peek())) {
|
|
this._beginToken(36, start);
|
|
const parts = this._consumeComponentName();
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._requireCharCode($GT);
|
|
this._endToken(parts);
|
|
return;
|
|
}
|
|
}
|
|
this._beginToken(3, start);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
const prefixAndName = this._consumePrefixAndName(isNameEnd);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._requireCharCode($GT);
|
|
this._endToken(prefixAndName);
|
|
}
|
|
_consumeExpansionFormStart() {
|
|
this._beginToken(19);
|
|
this._requireCharCode($LBRACE);
|
|
this._endToken([]);
|
|
this._expansionCaseStack.push(19);
|
|
this._beginToken(7);
|
|
const condition = this._readUntil($COMMA);
|
|
const normalizedCondition = this._processCarriageReturns(condition);
|
|
if (this._i18nNormalizeLineEndingsInICUs) {
|
|
this._endToken([normalizedCondition]);
|
|
} else {
|
|
const conditionToken = this._endToken([condition]);
|
|
if (normalizedCondition !== condition) {
|
|
this.nonNormalizedIcuExpressions.push(conditionToken);
|
|
}
|
|
}
|
|
this._requireCharCode($COMMA);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._beginToken(7);
|
|
const type = this._readUntil($COMMA);
|
|
this._endToken([type]);
|
|
this._requireCharCode($COMMA);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
}
|
|
_consumeExpansionCaseStart() {
|
|
this._beginToken(20);
|
|
const value = this._readUntil($LBRACE).trim();
|
|
this._endToken([value]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._beginToken(21);
|
|
this._requireCharCode($LBRACE);
|
|
this._endToken([]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._expansionCaseStack.push(21);
|
|
}
|
|
_consumeExpansionCaseEnd() {
|
|
this._beginToken(22);
|
|
this._requireCharCode($RBRACE);
|
|
this._endToken([]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._expansionCaseStack.pop();
|
|
}
|
|
_consumeExpansionFormEnd() {
|
|
this._beginToken(23);
|
|
this._requireCharCode($RBRACE);
|
|
this._endToken([]);
|
|
this._expansionCaseStack.pop();
|
|
}
|
|
_consumeWithInterpolation(textTokenType, interpolationTokenType, endPredicate, endInterpolation) {
|
|
this._beginToken(textTokenType);
|
|
const parts = [];
|
|
while (!endPredicate()) {
|
|
const current = this._cursor.clone();
|
|
if (this._attemptStr(INTERPOLATION.start)) {
|
|
this._endToken([this._processCarriageReturns(parts.join(''))], current);
|
|
parts.length = 0;
|
|
this._consumeInterpolation(interpolationTokenType, current, endInterpolation);
|
|
this._beginToken(textTokenType);
|
|
} else if (this._cursor.peek() === $AMPERSAND) {
|
|
this._endToken([this._processCarriageReturns(parts.join(''))]);
|
|
parts.length = 0;
|
|
this._consumeEntity(textTokenType);
|
|
this._beginToken(textTokenType);
|
|
} else {
|
|
parts.push(this._readChar());
|
|
}
|
|
}
|
|
this._inInterpolation = false;
|
|
this._endToken([this._processCarriageReturns(parts.join(''))]);
|
|
}
|
|
_consumeInterpolation(interpolationTokenType, interpolationStart, prematureEndPredicate) {
|
|
const parts = [];
|
|
this._beginToken(interpolationTokenType, interpolationStart);
|
|
parts.push(INTERPOLATION.start);
|
|
const expressionStart = this._cursor.clone();
|
|
let inQuote = null;
|
|
let inComment = false;
|
|
while (this._cursor.peek() !== $EOF && (prematureEndPredicate === null || !prematureEndPredicate())) {
|
|
const current = this._cursor.clone();
|
|
if (this._isTagStart()) {
|
|
this._cursor = current;
|
|
parts.push(this._getProcessedChars(expressionStart, current));
|
|
this._endToken(parts);
|
|
return;
|
|
}
|
|
if (inQuote === null) {
|
|
if (this._attemptStr(INTERPOLATION.end)) {
|
|
parts.push(this._getProcessedChars(expressionStart, current));
|
|
parts.push(INTERPOLATION.end);
|
|
this._endToken(parts);
|
|
return;
|
|
} else if (this._attemptStr('//')) {
|
|
inComment = true;
|
|
}
|
|
}
|
|
const char = this._cursor.peek();
|
|
this._cursor.advance();
|
|
if (char === $BACKSLASH) {
|
|
this._cursor.advance();
|
|
} else if (char === inQuote) {
|
|
inQuote = null;
|
|
} else if (!inComment && inQuote === null && isQuote(char)) {
|
|
inQuote = char;
|
|
}
|
|
}
|
|
parts.push(this._getProcessedChars(expressionStart, this._cursor));
|
|
this._endToken(parts);
|
|
}
|
|
_consumeDirective(start, nameStart) {
|
|
this._requireCharCode($AT);
|
|
this._cursor.advance();
|
|
while (isSelectorlessNameChar(this._cursor.peek())) {
|
|
this._cursor.advance();
|
|
}
|
|
this._beginToken(38, start);
|
|
const name = this._cursor.getChars(nameStart);
|
|
this._endToken([name]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
if (this._cursor.peek() !== $LPAREN) {
|
|
return;
|
|
}
|
|
this._openDirectiveCount++;
|
|
this._beginToken(39);
|
|
this._cursor.advance();
|
|
this._endToken([]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
while (!isAttributeTerminator(this._cursor.peek()) && this._cursor.peek() !== $RPAREN) {
|
|
this._consumeAttribute();
|
|
}
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
this._openDirectiveCount--;
|
|
if (this._cursor.peek() !== $RPAREN) {
|
|
if (this._cursor.peek() === $GT || this._cursor.peek() === $SLASH) {
|
|
return;
|
|
}
|
|
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
|
}
|
|
this._beginToken(40);
|
|
this._cursor.advance();
|
|
this._endToken([]);
|
|
this._attemptCharCodeUntilFn(isNotWhitespace);
|
|
}
|
|
_getProcessedChars(start, end) {
|
|
return this._processCarriageReturns(end.getChars(start));
|
|
}
|
|
_isTextEnd() {
|
|
if (this._isTagStart() || this._cursor.peek() === $EOF) {
|
|
return true;
|
|
}
|
|
if (this._tokenizeIcu && !this._inInterpolation) {
|
|
if (this.isExpansionFormStart()) {
|
|
return true;
|
|
}
|
|
if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
|
|
return true;
|
|
}
|
|
}
|
|
if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansion() && (this._isBlockStart() || this._isLetStart() || this._cursor.peek() === $RBRACE)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
_isTagStart() {
|
|
if (this._cursor.peek() === $LT) {
|
|
const tmp = this._cursor.clone();
|
|
tmp.advance();
|
|
const code = tmp.peek();
|
|
if ($a <= code && code <= $z || $A <= code && code <= $Z || code === $SLASH || code === $BANG) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
_readUntil(char) {
|
|
const start = this._cursor.clone();
|
|
this._attemptUntilChar(char);
|
|
return this._cursor.getChars(start);
|
|
}
|
|
_isInExpansion() {
|
|
return this._isInExpansionCase() || this._isInExpansionForm();
|
|
}
|
|
_isInExpansionCase() {
|
|
return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 21;
|
|
}
|
|
_isInExpansionForm() {
|
|
return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 19;
|
|
}
|
|
isExpansionFormStart() {
|
|
if (this._cursor.peek() !== $LBRACE) {
|
|
return false;
|
|
}
|
|
const start = this._cursor.clone();
|
|
const isInterpolation = this._attemptStr(INTERPOLATION.start);
|
|
this._cursor = start;
|
|
return !isInterpolation;
|
|
}
|
|
}
|
|
function isNotWhitespace(code) {
|
|
return !isWhitespace(code) || code === $EOF;
|
|
}
|
|
function isNameEnd(code) {
|
|
return isWhitespace(code) || code === $GT || code === $LT || code === $SLASH || code === $SQ || code === $DQ || code === $EQ || code === $EOF;
|
|
}
|
|
function isPrefixEnd(code) {
|
|
return (code < $a || $z < code) && (code < $A || $Z < code) && (code < $0 || code > $9);
|
|
}
|
|
function isDigitEntityEnd(code) {
|
|
return code === $SEMICOLON || code === $EOF || !isAsciiHexDigit(code);
|
|
}
|
|
function isNamedEntityEnd(code) {
|
|
return code === $SEMICOLON || code === $EOF || !isAsciiLetter(code);
|
|
}
|
|
function isExpansionCaseStart(peek) {
|
|
return peek !== $RBRACE;
|
|
}
|
|
function compareCharCodeCaseInsensitive(code1, code2) {
|
|
return toUpperCaseCharCode(code1) === toUpperCaseCharCode(code2);
|
|
}
|
|
function toUpperCaseCharCode(code) {
|
|
return code >= $a && code <= $z ? code - $a + $A : code;
|
|
}
|
|
function isBlockNameChar(code) {
|
|
return isAsciiLetter(code) || isDigit(code) || code === $_;
|
|
}
|
|
function isBlockParameterChar(code) {
|
|
return code !== $SEMICOLON && isNotWhitespace(code);
|
|
}
|
|
function isSelectorlessNameStart(code) {
|
|
return code === $_ || code >= $A && code <= $Z;
|
|
}
|
|
function isSelectorlessNameChar(code) {
|
|
return isAsciiLetter(code) || isDigit(code) || code === $_;
|
|
}
|
|
function isAttributeTerminator(code) {
|
|
return code === $SLASH || code === $GT || code === $LT || code === $EOF;
|
|
}
|
|
function mergeTextTokens(srcTokens) {
|
|
const dstTokens = [];
|
|
let lastDstToken = undefined;
|
|
for (let i = 0; i < srcTokens.length; i++) {
|
|
const token = srcTokens[i];
|
|
if (lastDstToken && lastDstToken.type === 5 && token.type === 5 || lastDstToken && lastDstToken.type === 16 && token.type === 16) {
|
|
lastDstToken.parts[0] += token.parts[0];
|
|
lastDstToken.sourceSpan.end = token.sourceSpan.end;
|
|
} else {
|
|
lastDstToken = token;
|
|
dstTokens.push(lastDstToken);
|
|
}
|
|
}
|
|
return dstTokens;
|
|
}
|
|
class PlainCharacterCursor {
|
|
state;
|
|
file;
|
|
input;
|
|
end;
|
|
constructor(fileOrCursor, range) {
|
|
if (fileOrCursor instanceof PlainCharacterCursor) {
|
|
this.file = fileOrCursor.file;
|
|
this.input = fileOrCursor.input;
|
|
this.end = fileOrCursor.end;
|
|
const state = fileOrCursor.state;
|
|
this.state = {
|
|
peek: state.peek,
|
|
offset: state.offset,
|
|
line: state.line,
|
|
column: state.column
|
|
};
|
|
} else {
|
|
if (!range) {
|
|
throw new Error('Programming error: the range argument must be provided with a file argument.');
|
|
}
|
|
this.file = fileOrCursor;
|
|
this.input = fileOrCursor.content;
|
|
this.end = range.endPos;
|
|
this.state = {
|
|
peek: -1,
|
|
offset: range.startPos,
|
|
line: range.startLine,
|
|
column: range.startCol
|
|
};
|
|
}
|
|
}
|
|
clone() {
|
|
return new PlainCharacterCursor(this);
|
|
}
|
|
peek() {
|
|
return this.state.peek;
|
|
}
|
|
charsLeft() {
|
|
return this.end - this.state.offset;
|
|
}
|
|
diff(other) {
|
|
return this.state.offset - other.state.offset;
|
|
}
|
|
advance() {
|
|
this.advanceState(this.state);
|
|
}
|
|
init() {
|
|
this.updatePeek(this.state);
|
|
}
|
|
getSpan(start, leadingTriviaCodePoints) {
|
|
start = start || this;
|
|
let fullStart = start;
|
|
if (leadingTriviaCodePoints) {
|
|
while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
|
|
if (fullStart === start) {
|
|
start = start.clone();
|
|
}
|
|
start.advance();
|
|
}
|
|
}
|
|
const startLocation = this.locationFromCursor(start);
|
|
const endLocation = this.locationFromCursor(this);
|
|
const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
|
|
return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
|
|
}
|
|
getChars(start) {
|
|
return this.input.substring(start.state.offset, this.state.offset);
|
|
}
|
|
charAt(pos) {
|
|
return this.input.charCodeAt(pos);
|
|
}
|
|
advanceState(state) {
|
|
if (state.offset >= this.end) {
|
|
this.state = state;
|
|
throw new CursorError('Unexpected character "EOF"', this);
|
|
}
|
|
const currentChar = this.charAt(state.offset);
|
|
if (currentChar === $LF) {
|
|
state.line++;
|
|
state.column = 0;
|
|
} else if (!isNewLine(currentChar)) {
|
|
state.column++;
|
|
}
|
|
state.offset++;
|
|
this.updatePeek(state);
|
|
}
|
|
updatePeek(state) {
|
|
state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
|
|
}
|
|
locationFromCursor(cursor) {
|
|
return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
|
|
}
|
|
}
|
|
class EscapedCharacterCursor extends PlainCharacterCursor {
|
|
internalState;
|
|
constructor(fileOrCursor, range) {
|
|
if (fileOrCursor instanceof EscapedCharacterCursor) {
|
|
super(fileOrCursor);
|
|
this.internalState = {
|
|
...fileOrCursor.internalState
|
|
};
|
|
} else {
|
|
super(fileOrCursor, range);
|
|
this.internalState = this.state;
|
|
}
|
|
}
|
|
advance() {
|
|
this.state = this.internalState;
|
|
super.advance();
|
|
this.processEscapeSequence();
|
|
}
|
|
init() {
|
|
super.init();
|
|
this.processEscapeSequence();
|
|
}
|
|
clone() {
|
|
return new EscapedCharacterCursor(this);
|
|
}
|
|
getChars(start) {
|
|
const cursor = start.clone();
|
|
let chars = '';
|
|
while (cursor.internalState.offset < this.internalState.offset) {
|
|
chars += String.fromCodePoint(cursor.peek());
|
|
cursor.advance();
|
|
}
|
|
return chars;
|
|
}
|
|
processEscapeSequence() {
|
|
const peek = () => this.internalState.peek;
|
|
if (peek() === $BACKSLASH) {
|
|
this.internalState = {
|
|
...this.state
|
|
};
|
|
this.advanceState(this.internalState);
|
|
if (peek() === $n) {
|
|
this.state.peek = $LF;
|
|
} else if (peek() === $r) {
|
|
this.state.peek = $CR;
|
|
} else if (peek() === $v) {
|
|
this.state.peek = $VTAB;
|
|
} else if (peek() === $t) {
|
|
this.state.peek = $TAB;
|
|
} else if (peek() === $b) {
|
|
this.state.peek = $BSPACE;
|
|
} else if (peek() === $f) {
|
|
this.state.peek = $FF;
|
|
} else if (peek() === $u) {
|
|
this.advanceState(this.internalState);
|
|
if (peek() === $LBRACE) {
|
|
this.advanceState(this.internalState);
|
|
const digitStart = this.clone();
|
|
let length = 0;
|
|
while (peek() !== $RBRACE) {
|
|
this.advanceState(this.internalState);
|
|
length++;
|
|
}
|
|
this.state.peek = this.decodeHexDigits(digitStart, length);
|
|
} else {
|
|
const digitStart = this.clone();
|
|
this.advanceState(this.internalState);
|
|
this.advanceState(this.internalState);
|
|
this.advanceState(this.internalState);
|
|
this.state.peek = this.decodeHexDigits(digitStart, 4);
|
|
}
|
|
} else if (peek() === $x) {
|
|
this.advanceState(this.internalState);
|
|
const digitStart = this.clone();
|
|
this.advanceState(this.internalState);
|
|
this.state.peek = this.decodeHexDigits(digitStart, 2);
|
|
} else if (isOctalDigit(peek())) {
|
|
let octal = '';
|
|
let length = 0;
|
|
let previous = this.clone();
|
|
while (isOctalDigit(peek()) && length < 3) {
|
|
previous = this.clone();
|
|
octal += String.fromCodePoint(peek());
|
|
this.advanceState(this.internalState);
|
|
length++;
|
|
}
|
|
this.state.peek = parseInt(octal, 8);
|
|
this.internalState = previous.internalState;
|
|
} else if (isNewLine(this.internalState.peek)) {
|
|
this.advanceState(this.internalState);
|
|
this.state = this.internalState;
|
|
} else {
|
|
this.state.peek = this.internalState.peek;
|
|
}
|
|
}
|
|
}
|
|
decodeHexDigits(start, length) {
|
|
const hex = this.input.slice(start.internalState.offset, start.internalState.offset + length);
|
|
const charCode = parseInt(hex, 16);
|
|
if (!isNaN(charCode)) {
|
|
return charCode;
|
|
} else {
|
|
start.state = start.internalState;
|
|
throw new CursorError('Invalid hexadecimal escape sequence', start);
|
|
}
|
|
}
|
|
}
|
|
class CursorError extends Error {
|
|
msg;
|
|
cursor;
|
|
constructor(msg, cursor) {
|
|
super(msg);
|
|
this.msg = msg;
|
|
this.cursor = cursor;
|
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
}
|
|
}
|
|
|
|
class TreeError extends ParseError {
|
|
elementName;
|
|
static create(elementName, span, msg) {
|
|
return new TreeError(elementName, span, msg);
|
|
}
|
|
constructor(elementName, span, msg) {
|
|
super(span, msg);
|
|
this.elementName = elementName;
|
|
}
|
|
}
|
|
class ParseTreeResult {
|
|
rootNodes;
|
|
errors;
|
|
constructor(rootNodes, errors) {
|
|
this.rootNodes = rootNodes;
|
|
this.errors = errors;
|
|
}
|
|
}
|
|
let Parser$1 = class Parser {
|
|
getTagDefinition;
|
|
constructor(getTagDefinition) {
|
|
this.getTagDefinition = getTagDefinition;
|
|
}
|
|
parse(source, url, options) {
|
|
const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
|
|
const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
|
|
parser.build();
|
|
return new ParseTreeResult(parser.rootNodes, [...tokenizeResult.errors, ...parser.errors]);
|
|
}
|
|
};
|
|
class _TreeBuilder {
|
|
tokens;
|
|
tagDefinitionResolver;
|
|
_index = -1;
|
|
_peek;
|
|
_containerStack = [];
|
|
rootNodes = [];
|
|
errors = [];
|
|
constructor(tokens, tagDefinitionResolver) {
|
|
this.tokens = tokens;
|
|
this.tagDefinitionResolver = tagDefinitionResolver;
|
|
this._advance();
|
|
}
|
|
build() {
|
|
while (this._peek.type !== 41) {
|
|
if (this._peek.type === 0 || this._peek.type === 4) {
|
|
this._consumeElementStartTag(this._advance());
|
|
} else if (this._peek.type === 3) {
|
|
this._consumeElementEndTag(this._advance());
|
|
} else if (this._peek.type === 12) {
|
|
this._closeVoidElement();
|
|
this._consumeCdata(this._advance());
|
|
} else if (this._peek.type === 10) {
|
|
this._closeVoidElement();
|
|
this._consumeComment(this._advance());
|
|
} else if (this._peek.type === 5 || this._peek.type === 7 || this._peek.type === 6) {
|
|
this._closeVoidElement();
|
|
this._consumeText(this._advance());
|
|
} else if (this._peek.type === 19) {
|
|
this._consumeExpansion(this._advance());
|
|
} else if (this._peek.type === 24) {
|
|
this._closeVoidElement();
|
|
this._consumeBlockOpen(this._advance());
|
|
} else if (this._peek.type === 26) {
|
|
this._closeVoidElement();
|
|
this._consumeBlockClose(this._advance());
|
|
} else if (this._peek.type === 28) {
|
|
this._closeVoidElement();
|
|
this._consumeIncompleteBlock(this._advance());
|
|
} else if (this._peek.type === 29) {
|
|
this._closeVoidElement();
|
|
this._consumeLet(this._advance());
|
|
} else if (this._peek.type === 32) {
|
|
this._closeVoidElement();
|
|
this._consumeIncompleteLet(this._advance());
|
|
} else if (this._peek.type === 33 || this._peek.type === 37) {
|
|
this._consumeComponentStartTag(this._advance());
|
|
} else if (this._peek.type === 36) {
|
|
this._consumeComponentEndTag(this._advance());
|
|
} else {
|
|
this._advance();
|
|
}
|
|
}
|
|
for (const leftoverContainer of this._containerStack) {
|
|
if (leftoverContainer instanceof Block) {
|
|
this.errors.push(TreeError.create(leftoverContainer.name, leftoverContainer.sourceSpan, `Unclosed block "${leftoverContainer.name}"`));
|
|
}
|
|
}
|
|
}
|
|
_advance() {
|
|
const prev = this._peek;
|
|
if (this._index < this.tokens.length - 1) {
|
|
this._index++;
|
|
}
|
|
this._peek = this.tokens[this._index];
|
|
return prev;
|
|
}
|
|
_advanceIf(type) {
|
|
if (this._peek.type === type) {
|
|
return this._advance();
|
|
}
|
|
return null;
|
|
}
|
|
_consumeCdata(_startToken) {
|
|
this._consumeText(this._advance());
|
|
this._advanceIf(13);
|
|
}
|
|
_consumeComment(token) {
|
|
const text = this._advanceIf(7);
|
|
const endToken = this._advanceIf(11);
|
|
const value = text != null ? text.parts[0].trim() : null;
|
|
const sourceSpan = endToken == null ? token.sourceSpan : new ParseSourceSpan(token.sourceSpan.start, endToken.sourceSpan.end, token.sourceSpan.fullStart);
|
|
this._addToParent(new Comment(value, sourceSpan));
|
|
}
|
|
_consumeExpansion(token) {
|
|
const switchValue = this._advance();
|
|
const type = this._advance();
|
|
const cases = [];
|
|
while (this._peek.type === 20) {
|
|
const expCase = this._parseExpansionCase();
|
|
if (!expCase) return;
|
|
cases.push(expCase);
|
|
}
|
|
if (this._peek.type !== 23) {
|
|
this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
|
return;
|
|
}
|
|
const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
|
|
this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
|
|
this._advance();
|
|
}
|
|
_parseExpansionCase() {
|
|
const value = this._advance();
|
|
if (this._peek.type !== 21) {
|
|
this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
|
|
return null;
|
|
}
|
|
const start = this._advance();
|
|
const exp = this._collectExpansionExpTokens(start);
|
|
if (!exp) return null;
|
|
const end = this._advance();
|
|
exp.push({
|
|
type: 41,
|
|
parts: [],
|
|
sourceSpan: end.sourceSpan
|
|
});
|
|
const expansionCaseParser = new _TreeBuilder(exp, this.tagDefinitionResolver);
|
|
expansionCaseParser.build();
|
|
if (expansionCaseParser.errors.length > 0) {
|
|
this.errors = this.errors.concat(expansionCaseParser.errors);
|
|
return null;
|
|
}
|
|
const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
|
|
const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
|
|
return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
|
|
}
|
|
_collectExpansionExpTokens(start) {
|
|
const exp = [];
|
|
const expansionFormStack = [21];
|
|
while (true) {
|
|
if (this._peek.type === 19 || this._peek.type === 21) {
|
|
expansionFormStack.push(this._peek.type);
|
|
}
|
|
if (this._peek.type === 22) {
|
|
if (lastOnStack(expansionFormStack, 21)) {
|
|
expansionFormStack.pop();
|
|
if (expansionFormStack.length === 0) return exp;
|
|
} else {
|
|
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
|
return null;
|
|
}
|
|
}
|
|
if (this._peek.type === 23) {
|
|
if (lastOnStack(expansionFormStack, 19)) {
|
|
expansionFormStack.pop();
|
|
} else {
|
|
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
|
return null;
|
|
}
|
|
}
|
|
if (this._peek.type === 41) {
|
|
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
|
return null;
|
|
}
|
|
exp.push(this._advance());
|
|
}
|
|
}
|
|
_consumeText(token) {
|
|
const tokens = [token];
|
|
const startSpan = token.sourceSpan;
|
|
let text = token.parts[0];
|
|
if (text.length > 0 && text[0] === '\n') {
|
|
const parent = this._getContainer();
|
|
if (parent != null && parent.children.length === 0 && this._getTagDefinition(parent)?.ignoreFirstLf) {
|
|
text = text.substring(1);
|
|
tokens[0] = {
|
|
type: token.type,
|
|
sourceSpan: token.sourceSpan,
|
|
parts: [text]
|
|
};
|
|
}
|
|
}
|
|
while (this._peek.type === 8 || this._peek.type === 5 || this._peek.type === 9) {
|
|
token = this._advance();
|
|
tokens.push(token);
|
|
if (token.type === 8) {
|
|
text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity);
|
|
} else if (token.type === 9) {
|
|
text += token.parts[0];
|
|
} else {
|
|
text += token.parts.join('');
|
|
}
|
|
}
|
|
if (text.length > 0) {
|
|
const endSpan = token.sourceSpan;
|
|
this._addToParent(new Text(text, new ParseSourceSpan(startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details), tokens));
|
|
}
|
|
}
|
|
_closeVoidElement() {
|
|
const el = this._getContainer();
|
|
if (el !== null && this._getTagDefinition(el)?.isVoid) {
|
|
this._containerStack.pop();
|
|
}
|
|
}
|
|
_consumeElementStartTag(startTagToken) {
|
|
const attrs = [];
|
|
const directives = [];
|
|
this._consumeAttributesAndDirectives(attrs, directives);
|
|
const fullName = this._getElementFullName(startTagToken, this._getClosestElementLikeParent());
|
|
const tagDef = this._getTagDefinition(fullName);
|
|
let selfClosing = false;
|
|
if (this._peek.type === 2) {
|
|
this._advance();
|
|
selfClosing = true;
|
|
if (!(tagDef?.canSelfClose || getNsPrefix(fullName) !== null || tagDef?.isVoid)) {
|
|
this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void, custom and foreign elements can be self closed "${startTagToken.parts[1]}"`));
|
|
}
|
|
} else if (this._peek.type === 1) {
|
|
this._advance();
|
|
selfClosing = false;
|
|
}
|
|
const end = this._peek.sourceSpan.fullStart;
|
|
const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
|
const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
|
const el = new Element(fullName, attrs, directives, [], selfClosing, span, startSpan, undefined, tagDef?.isVoid ?? false);
|
|
const parent = this._getContainer();
|
|
const isClosedByChild = parent !== null && !!this._getTagDefinition(parent)?.isClosedByChild(el.name);
|
|
this._pushContainer(el, isClosedByChild);
|
|
if (selfClosing) {
|
|
this._popContainer(fullName, Element, span);
|
|
} else if (startTagToken.type === 4) {
|
|
this._popContainer(fullName, Element, null);
|
|
this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
|
|
}
|
|
}
|
|
_consumeComponentStartTag(startToken) {
|
|
const componentName = startToken.parts[0];
|
|
const attrs = [];
|
|
const directives = [];
|
|
this._consumeAttributesAndDirectives(attrs, directives);
|
|
const closestElement = this._getClosestElementLikeParent();
|
|
const tagName = this._getComponentTagName(startToken, closestElement);
|
|
const fullName = this._getComponentFullName(startToken, closestElement);
|
|
const selfClosing = this._peek.type === 35;
|
|
this._advance();
|
|
const end = this._peek.sourceSpan.fullStart;
|
|
const span = new ParseSourceSpan(startToken.sourceSpan.start, end, startToken.sourceSpan.fullStart);
|
|
const startSpan = new ParseSourceSpan(startToken.sourceSpan.start, end, startToken.sourceSpan.fullStart);
|
|
const node = new Component(componentName, tagName, fullName, attrs, directives, [], selfClosing, span, startSpan, undefined);
|
|
const parent = this._getContainer();
|
|
const isClosedByChild = parent !== null && node.tagName !== null && !!this._getTagDefinition(parent)?.isClosedByChild(node.tagName);
|
|
this._pushContainer(node, isClosedByChild);
|
|
if (selfClosing) {
|
|
this._popContainer(fullName, Component, span);
|
|
} else if (startToken.type === 37) {
|
|
this._popContainer(fullName, Component, null);
|
|
this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
|
|
}
|
|
}
|
|
_consumeAttributesAndDirectives(attributesResult, directivesResult) {
|
|
while (this._peek.type === 14 || this._peek.type === 38) {
|
|
if (this._peek.type === 38) {
|
|
directivesResult.push(this._consumeDirective(this._peek));
|
|
} else {
|
|
attributesResult.push(this._consumeAttr(this._advance()));
|
|
}
|
|
}
|
|
}
|
|
_consumeComponentEndTag(endToken) {
|
|
const fullName = this._getComponentFullName(endToken, this._getClosestElementLikeParent());
|
|
if (!this._popContainer(fullName, Component, endToken.sourceSpan)) {
|
|
const container = this._containerStack[this._containerStack.length - 1];
|
|
let suffix;
|
|
if (container instanceof Component && container.componentName === endToken.parts[0]) {
|
|
suffix = `, did you mean "${container.fullName}"?`;
|
|
} else {
|
|
suffix = '. It may happen when the tag has already been closed by another tag.';
|
|
}
|
|
const errMsg = `Unexpected closing tag "${fullName}"${suffix}`;
|
|
this.errors.push(TreeError.create(fullName, endToken.sourceSpan, errMsg));
|
|
}
|
|
}
|
|
_getTagDefinition(nodeOrName) {
|
|
if (typeof nodeOrName === 'string') {
|
|
return this.tagDefinitionResolver(nodeOrName);
|
|
} else if (nodeOrName instanceof Element) {
|
|
return this.tagDefinitionResolver(nodeOrName.name);
|
|
} else if (nodeOrName instanceof Component && nodeOrName.tagName !== null) {
|
|
return this.tagDefinitionResolver(nodeOrName.tagName);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
_pushContainer(node, isClosedByChild) {
|
|
if (isClosedByChild) {
|
|
this._containerStack.pop();
|
|
}
|
|
this._addToParent(node);
|
|
this._containerStack.push(node);
|
|
}
|
|
_consumeElementEndTag(endTagToken) {
|
|
const fullName = this._getElementFullName(endTagToken, this._getClosestElementLikeParent());
|
|
if (this._getTagDefinition(fullName)?.isVoid) {
|
|
this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
|
|
} else if (!this._popContainer(fullName, Element, endTagToken.sourceSpan)) {
|
|
const errMsg = `Unexpected closing tag "${fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`;
|
|
this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
|
|
}
|
|
}
|
|
_popContainer(expectedName, expectedType, endSourceSpan) {
|
|
let unexpectedCloseTagDetected = false;
|
|
for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) {
|
|
const node = this._containerStack[stackIndex];
|
|
const nodeName = node instanceof Component ? node.fullName : node.name;
|
|
if ((nodeName === expectedName || expectedName === null) && node instanceof expectedType) {
|
|
node.endSourceSpan = endSourceSpan;
|
|
node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end;
|
|
this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex);
|
|
return !unexpectedCloseTagDetected;
|
|
}
|
|
if (node instanceof Block || !this._getTagDefinition(node)?.closedByParent) {
|
|
unexpectedCloseTagDetected = true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
_consumeAttr(attrName) {
|
|
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
|
let attrEnd = attrName.sourceSpan.end;
|
|
if (this._peek.type === 15) {
|
|
this._advance();
|
|
}
|
|
let value = '';
|
|
const valueTokens = [];
|
|
let valueStartSpan = undefined;
|
|
let valueEnd = undefined;
|
|
const nextTokenType = this._peek.type;
|
|
if (nextTokenType === 16) {
|
|
valueStartSpan = this._peek.sourceSpan;
|
|
valueEnd = this._peek.sourceSpan.end;
|
|
while (this._peek.type === 16 || this._peek.type === 17 || this._peek.type === 9) {
|
|
const valueToken = this._advance();
|
|
valueTokens.push(valueToken);
|
|
if (valueToken.type === 17) {
|
|
value += valueToken.parts.join('').replace(/&([^;]+);/g, decodeEntity);
|
|
} else if (valueToken.type === 9) {
|
|
value += valueToken.parts[0];
|
|
} else {
|
|
value += valueToken.parts.join('');
|
|
}
|
|
valueEnd = attrEnd = valueToken.sourceSpan.end;
|
|
}
|
|
}
|
|
if (this._peek.type === 15) {
|
|
const quoteToken = this._advance();
|
|
attrEnd = quoteToken.sourceSpan.end;
|
|
}
|
|
const valueSpan = valueStartSpan && valueEnd && new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart);
|
|
return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined);
|
|
}
|
|
_consumeDirective(nameToken) {
|
|
const attributes = [];
|
|
let startSourceSpanEnd = nameToken.sourceSpan.end;
|
|
let endSourceSpan = null;
|
|
this._advance();
|
|
if (this._peek.type === 39) {
|
|
startSourceSpanEnd = this._peek.sourceSpan.end;
|
|
this._advance();
|
|
while (this._peek.type === 14) {
|
|
attributes.push(this._consumeAttr(this._advance()));
|
|
}
|
|
if (this._peek.type === 40) {
|
|
endSourceSpan = this._peek.sourceSpan;
|
|
this._advance();
|
|
} else {
|
|
this.errors.push(TreeError.create(null, nameToken.sourceSpan, 'Unterminated directive definition'));
|
|
}
|
|
}
|
|
const startSourceSpan = new ParseSourceSpan(nameToken.sourceSpan.start, startSourceSpanEnd, nameToken.sourceSpan.fullStart);
|
|
const sourceSpan = new ParseSourceSpan(startSourceSpan.start, endSourceSpan === null ? nameToken.sourceSpan.end : endSourceSpan.end, startSourceSpan.fullStart);
|
|
return new Directive(nameToken.parts[0], attributes, sourceSpan, startSourceSpan, endSourceSpan);
|
|
}
|
|
_consumeBlockOpen(token) {
|
|
const parameters = [];
|
|
while (this._peek.type === 27) {
|
|
const paramToken = this._advance();
|
|
parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan));
|
|
}
|
|
if (this._peek.type === 25) {
|
|
this._advance();
|
|
}
|
|
const end = this._peek.sourceSpan.fullStart;
|
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
this._pushContainer(block, false);
|
|
}
|
|
_consumeBlockClose(token) {
|
|
if (!this._popContainer(null, Block, token.sourceSpan)) {
|
|
this.errors.push(TreeError.create(null, token.sourceSpan, `Unexpected closing block. The block may have been closed earlier. ` + `If you meant to write the } character, you should use the "}" ` + `HTML entity instead.`));
|
|
}
|
|
}
|
|
_consumeIncompleteBlock(token) {
|
|
const parameters = [];
|
|
while (this._peek.type === 27) {
|
|
const paramToken = this._advance();
|
|
parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan));
|
|
}
|
|
const end = this._peek.sourceSpan.fullStart;
|
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
this._pushContainer(block, false);
|
|
this._popContainer(null, Block, null);
|
|
this.errors.push(TreeError.create(token.parts[0], span, `Incomplete block "${token.parts[0]}". If you meant to write the @ character, ` + `you should use the "@" HTML entity instead.`));
|
|
}
|
|
_consumeLet(startToken) {
|
|
const name = startToken.parts[0];
|
|
let valueToken;
|
|
let endToken;
|
|
if (this._peek.type !== 30) {
|
|
this.errors.push(TreeError.create(startToken.parts[0], startToken.sourceSpan, `Invalid @let declaration "${name}". Declaration must have a value.`));
|
|
return;
|
|
} else {
|
|
valueToken = this._advance();
|
|
}
|
|
if (this._peek.type !== 31) {
|
|
this.errors.push(TreeError.create(startToken.parts[0], startToken.sourceSpan, `Unterminated @let declaration "${name}". Declaration must be terminated with a semicolon.`));
|
|
return;
|
|
} else {
|
|
endToken = this._advance();
|
|
}
|
|
const end = endToken.sourceSpan.fullStart;
|
|
const span = new ParseSourceSpan(startToken.sourceSpan.start, end, startToken.sourceSpan.fullStart);
|
|
const startOffset = startToken.sourceSpan.toString().lastIndexOf(name);
|
|
const nameStart = startToken.sourceSpan.start.moveBy(startOffset);
|
|
const nameSpan = new ParseSourceSpan(nameStart, startToken.sourceSpan.end);
|
|
const node = new LetDeclaration(name, valueToken.parts[0], span, nameSpan, valueToken.sourceSpan);
|
|
this._addToParent(node);
|
|
}
|
|
_consumeIncompleteLet(token) {
|
|
const name = token.parts[0] ?? '';
|
|
const nameString = name ? ` "${name}"` : '';
|
|
if (name.length > 0) {
|
|
const startOffset = token.sourceSpan.toString().lastIndexOf(name);
|
|
const nameStart = token.sourceSpan.start.moveBy(startOffset);
|
|
const nameSpan = new ParseSourceSpan(nameStart, token.sourceSpan.end);
|
|
const valueSpan = new ParseSourceSpan(token.sourceSpan.start, token.sourceSpan.start.moveBy(0));
|
|
const node = new LetDeclaration(name, '', token.sourceSpan, nameSpan, valueSpan);
|
|
this._addToParent(node);
|
|
}
|
|
this.errors.push(TreeError.create(token.parts[0], token.sourceSpan, `Incomplete @let declaration${nameString}. ` + `@let declarations must be written as \`@let <name> = <value>;\``));
|
|
}
|
|
_getContainer() {
|
|
return this._containerStack.length > 0 ? this._containerStack[this._containerStack.length - 1] : null;
|
|
}
|
|
_getClosestElementLikeParent() {
|
|
for (let i = this._containerStack.length - 1; i > -1; i--) {
|
|
const current = this._containerStack[i];
|
|
if (current instanceof Element || current instanceof Component) {
|
|
return current;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
_addToParent(node) {
|
|
const parent = this._getContainer();
|
|
if (parent === null) {
|
|
this.rootNodes.push(node);
|
|
} else {
|
|
parent.children.push(node);
|
|
}
|
|
}
|
|
_getElementFullName(token, parent) {
|
|
const prefix = this._getPrefix(token, parent);
|
|
return mergeNsAndName(prefix, token.parts[1]);
|
|
}
|
|
_getComponentFullName(token, parent) {
|
|
const componentName = token.parts[0];
|
|
const tagName = this._getComponentTagName(token, parent);
|
|
if (tagName === null) {
|
|
return componentName;
|
|
}
|
|
return tagName.startsWith(':') ? componentName + tagName : `${componentName}:${tagName}`;
|
|
}
|
|
_getComponentTagName(token, parent) {
|
|
const prefix = this._getPrefix(token, parent);
|
|
const tagName = token.parts[2];
|
|
if (!prefix && !tagName) {
|
|
return null;
|
|
} else if (!prefix && tagName) {
|
|
return tagName;
|
|
} else {
|
|
return mergeNsAndName(prefix, tagName || 'ng-component');
|
|
}
|
|
}
|
|
_getPrefix(token, parent) {
|
|
let prefix;
|
|
let tagName;
|
|
if (token.type === 33 || token.type === 37 || token.type === 36) {
|
|
prefix = token.parts[1];
|
|
tagName = token.parts[2];
|
|
} else {
|
|
prefix = token.parts[0];
|
|
tagName = token.parts[1];
|
|
}
|
|
prefix = prefix || this._getTagDefinition(tagName)?.implicitNamespacePrefix || '';
|
|
if (!prefix && parent) {
|
|
const parentName = parent instanceof Element ? parent.name : parent.tagName;
|
|
if (parentName !== null) {
|
|
const parentTagName = splitNsName(parentName)[1];
|
|
const parentTagDefinition = this._getTagDefinition(parentTagName);
|
|
if (parentTagDefinition !== null && !parentTagDefinition.preventNamespaceInheritance) {
|
|
prefix = getNsPrefix(parentName);
|
|
}
|
|
}
|
|
}
|
|
return prefix;
|
|
}
|
|
}
|
|
function lastOnStack(stack, element) {
|
|
return stack.length > 0 && stack[stack.length - 1] === element;
|
|
}
|
|
function decodeEntity(match, entity) {
|
|
if (NAMED_ENTITIES[entity] !== undefined) {
|
|
return NAMED_ENTITIES[entity] || match;
|
|
}
|
|
if (/^#x[a-f0-9]+$/i.test(entity)) {
|
|
return String.fromCodePoint(parseInt(entity.slice(2), 16));
|
|
}
|
|
if (/^#\d+$/.test(entity)) {
|
|
return String.fromCodePoint(parseInt(entity.slice(1), 10));
|
|
}
|
|
return match;
|
|
}
|
|
|
|
const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
|
|
const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
|
|
const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
|
|
const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
|
|
const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
|
|
function hasPreserveWhitespacesAttr(attrs) {
|
|
return attrs.some(attr => attr.name === PRESERVE_WS_ATTR_NAME);
|
|
}
|
|
function replaceNgsp(value) {
|
|
return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
|
|
}
|
|
class WhitespaceVisitor {
|
|
preserveSignificantWhitespace;
|
|
originalNodeMap;
|
|
requireContext;
|
|
icuExpansionDepth = 0;
|
|
constructor(preserveSignificantWhitespace, originalNodeMap, requireContext = true) {
|
|
this.preserveSignificantWhitespace = preserveSignificantWhitespace;
|
|
this.originalNodeMap = originalNodeMap;
|
|
this.requireContext = requireContext;
|
|
}
|
|
visitElement(element, context) {
|
|
if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
|
|
const newElement = new Element(element.name, visitAllWithSiblings(this, element.attrs), visitAllWithSiblings(this, element.directives), element.children, element.isSelfClosing, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.isVoid, element.i18n);
|
|
this.originalNodeMap?.set(newElement, element);
|
|
return newElement;
|
|
}
|
|
const newElement = new Element(element.name, element.attrs, element.directives, visitAllWithSiblings(this, element.children), element.isSelfClosing, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.isVoid, element.i18n);
|
|
this.originalNodeMap?.set(newElement, element);
|
|
return newElement;
|
|
}
|
|
visitAttribute(attribute, context) {
|
|
return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
|
|
}
|
|
visitText(text, context) {
|
|
const isNotBlank = text.value.match(NO_WS_REGEXP);
|
|
const hasExpansionSibling = context && (context.prev instanceof Expansion || context.next instanceof Expansion);
|
|
const inIcuExpansion = this.icuExpansionDepth > 0;
|
|
if (inIcuExpansion && this.preserveSignificantWhitespace) return text;
|
|
if (isNotBlank || hasExpansionSibling) {
|
|
const tokens = text.tokens.map(token => token.type === 5 ? createWhitespaceProcessedTextToken(token) : token);
|
|
if (!this.preserveSignificantWhitespace && tokens.length > 0) {
|
|
const firstToken = tokens[0];
|
|
tokens.splice(0, 1, trimLeadingWhitespace(firstToken, context));
|
|
const lastToken = tokens[tokens.length - 1];
|
|
tokens.splice(tokens.length - 1, 1, trimTrailingWhitespace(lastToken, context));
|
|
}
|
|
const processed = processWhitespace(text.value);
|
|
const value = this.preserveSignificantWhitespace ? processed : trimLeadingAndTrailingWhitespace(processed, context);
|
|
const result = new Text(value, text.sourceSpan, tokens, text.i18n);
|
|
this.originalNodeMap?.set(result, text);
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
visitComment(comment, context) {
|
|
return comment;
|
|
}
|
|
visitExpansion(expansion, context) {
|
|
this.icuExpansionDepth++;
|
|
let newExpansion;
|
|
try {
|
|
newExpansion = new Expansion(expansion.switchValue, expansion.type, visitAllWithSiblings(this, expansion.cases), expansion.sourceSpan, expansion.switchValueSourceSpan, expansion.i18n);
|
|
} finally {
|
|
this.icuExpansionDepth--;
|
|
}
|
|
this.originalNodeMap?.set(newExpansion, expansion);
|
|
return newExpansion;
|
|
}
|
|
visitExpansionCase(expansionCase, context) {
|
|
const newExpansionCase = new ExpansionCase(expansionCase.value, visitAllWithSiblings(this, expansionCase.expression), expansionCase.sourceSpan, expansionCase.valueSourceSpan, expansionCase.expSourceSpan);
|
|
this.originalNodeMap?.set(newExpansionCase, expansionCase);
|
|
return newExpansionCase;
|
|
}
|
|
visitBlock(block, context) {
|
|
const newBlock = new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.nameSpan, block.startSourceSpan, block.endSourceSpan);
|
|
this.originalNodeMap?.set(newBlock, block);
|
|
return newBlock;
|
|
}
|
|
visitBlockParameter(parameter, context) {
|
|
return parameter;
|
|
}
|
|
visitLetDeclaration(decl, context) {
|
|
return decl;
|
|
}
|
|
visitComponent(node, context) {
|
|
if (node.tagName && SKIP_WS_TRIM_TAGS.has(node.tagName) || hasPreserveWhitespacesAttr(node.attrs)) {
|
|
const newElement = new Component(node.componentName, node.tagName, node.fullName, visitAllWithSiblings(this, node.attrs), visitAllWithSiblings(this, node.directives), node.children, node.isSelfClosing, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.i18n);
|
|
this.originalNodeMap?.set(newElement, node);
|
|
return newElement;
|
|
}
|
|
const newElement = new Component(node.componentName, node.tagName, node.fullName, node.attrs, node.directives, visitAllWithSiblings(this, node.children), node.isSelfClosing, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.i18n);
|
|
this.originalNodeMap?.set(newElement, node);
|
|
return newElement;
|
|
}
|
|
visitDirective(directive, context) {
|
|
return directive;
|
|
}
|
|
visit(_node, context) {
|
|
if (this.requireContext && !context) {
|
|
throw new Error(`WhitespaceVisitor requires context. Visit via \`visitAllWithSiblings\` to get this context.`);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
function trimLeadingWhitespace(token, context) {
|
|
if (token.type !== 5) return token;
|
|
const isFirstTokenInTag = !context?.prev;
|
|
if (!isFirstTokenInTag) return token;
|
|
return transformTextToken(token, text => text.trimStart());
|
|
}
|
|
function trimTrailingWhitespace(token, context) {
|
|
if (token.type !== 5) return token;
|
|
const isLastTokenInTag = !context?.next;
|
|
if (!isLastTokenInTag) return token;
|
|
return transformTextToken(token, text => text.trimEnd());
|
|
}
|
|
function trimLeadingAndTrailingWhitespace(text, context) {
|
|
const isFirstTokenInTag = !context?.prev;
|
|
const isLastTokenInTag = !context?.next;
|
|
const maybeTrimmedStart = isFirstTokenInTag ? text.trimStart() : text;
|
|
const maybeTrimmed = isLastTokenInTag ? maybeTrimmedStart.trimEnd() : maybeTrimmedStart;
|
|
return maybeTrimmed;
|
|
}
|
|
function createWhitespaceProcessedTextToken({
|
|
type,
|
|
parts,
|
|
sourceSpan
|
|
}) {
|
|
return {
|
|
type,
|
|
parts: [processWhitespace(parts[0])],
|
|
sourceSpan
|
|
};
|
|
}
|
|
function transformTextToken({
|
|
type,
|
|
parts,
|
|
sourceSpan
|
|
}, transform) {
|
|
return {
|
|
type,
|
|
parts: [transform(parts[0])],
|
|
sourceSpan
|
|
};
|
|
}
|
|
function processWhitespace(text) {
|
|
return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' ');
|
|
}
|
|
function visitAllWithSiblings(visitor, nodes) {
|
|
const result = [];
|
|
nodes.forEach((ast, i) => {
|
|
const context = {
|
|
prev: nodes[i - 1],
|
|
next: nodes[i + 1]
|
|
};
|
|
const astResult = ast.visit(visitor, context);
|
|
if (astResult) {
|
|
result.push(astResult);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
var TokenType;
|
|
(function (TokenType) {
|
|
TokenType[TokenType["Character"] = 0] = "Character";
|
|
TokenType[TokenType["Identifier"] = 1] = "Identifier";
|
|
TokenType[TokenType["PrivateIdentifier"] = 2] = "PrivateIdentifier";
|
|
TokenType[TokenType["Keyword"] = 3] = "Keyword";
|
|
TokenType[TokenType["String"] = 4] = "String";
|
|
TokenType[TokenType["Operator"] = 5] = "Operator";
|
|
TokenType[TokenType["Number"] = 6] = "Number";
|
|
TokenType[TokenType["RegExpBody"] = 7] = "RegExpBody";
|
|
TokenType[TokenType["RegExpFlags"] = 8] = "RegExpFlags";
|
|
TokenType[TokenType["Error"] = 9] = "Error";
|
|
})(TokenType || (TokenType = {}));
|
|
var StringTokenKind;
|
|
(function (StringTokenKind) {
|
|
StringTokenKind[StringTokenKind["Plain"] = 0] = "Plain";
|
|
StringTokenKind[StringTokenKind["TemplateLiteralPart"] = 1] = "TemplateLiteralPart";
|
|
StringTokenKind[StringTokenKind["TemplateLiteralEnd"] = 2] = "TemplateLiteralEnd";
|
|
})(StringTokenKind || (StringTokenKind = {}));
|
|
const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this', 'typeof', 'void', 'in'];
|
|
class Lexer {
|
|
tokenize(text) {
|
|
return new _Scanner(text).scan();
|
|
}
|
|
}
|
|
class Token {
|
|
index;
|
|
end;
|
|
type;
|
|
numValue;
|
|
strValue;
|
|
constructor(index, end, type, numValue, strValue) {
|
|
this.index = index;
|
|
this.end = end;
|
|
this.type = type;
|
|
this.numValue = numValue;
|
|
this.strValue = strValue;
|
|
}
|
|
isCharacter(code) {
|
|
return this.type === TokenType.Character && this.numValue === code;
|
|
}
|
|
isNumber() {
|
|
return this.type === TokenType.Number;
|
|
}
|
|
isString() {
|
|
return this.type === TokenType.String;
|
|
}
|
|
isOperator(operator) {
|
|
return this.type === TokenType.Operator && this.strValue === operator;
|
|
}
|
|
isIdentifier() {
|
|
return this.type === TokenType.Identifier;
|
|
}
|
|
isPrivateIdentifier() {
|
|
return this.type === TokenType.PrivateIdentifier;
|
|
}
|
|
isKeyword() {
|
|
return this.type === TokenType.Keyword;
|
|
}
|
|
isKeywordLet() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'let';
|
|
}
|
|
isKeywordAs() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'as';
|
|
}
|
|
isKeywordNull() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'null';
|
|
}
|
|
isKeywordUndefined() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'undefined';
|
|
}
|
|
isKeywordTrue() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'true';
|
|
}
|
|
isKeywordFalse() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'false';
|
|
}
|
|
isKeywordThis() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'this';
|
|
}
|
|
isKeywordTypeof() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'typeof';
|
|
}
|
|
isKeywordVoid() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'void';
|
|
}
|
|
isKeywordIn() {
|
|
return this.type === TokenType.Keyword && this.strValue === 'in';
|
|
}
|
|
isError() {
|
|
return this.type === TokenType.Error;
|
|
}
|
|
isRegExpBody() {
|
|
return this.type === TokenType.RegExpBody;
|
|
}
|
|
isRegExpFlags() {
|
|
return this.type === TokenType.RegExpFlags;
|
|
}
|
|
toNumber() {
|
|
return this.type === TokenType.Number ? this.numValue : -1;
|
|
}
|
|
isTemplateLiteralPart() {
|
|
return this.isString() && this.kind === StringTokenKind.TemplateLiteralPart;
|
|
}
|
|
isTemplateLiteralEnd() {
|
|
return this.isString() && this.kind === StringTokenKind.TemplateLiteralEnd;
|
|
}
|
|
isTemplateLiteralInterpolationStart() {
|
|
return this.isOperator('${');
|
|
}
|
|
toString() {
|
|
switch (this.type) {
|
|
case TokenType.Character:
|
|
case TokenType.Identifier:
|
|
case TokenType.Keyword:
|
|
case TokenType.Operator:
|
|
case TokenType.PrivateIdentifier:
|
|
case TokenType.String:
|
|
case TokenType.Error:
|
|
case TokenType.RegExpBody:
|
|
case TokenType.RegExpFlags:
|
|
return this.strValue;
|
|
case TokenType.Number:
|
|
return this.numValue.toString();
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
class StringToken extends Token {
|
|
kind;
|
|
constructor(index, end, strValue, kind) {
|
|
super(index, end, TokenType.String, 0, strValue);
|
|
this.kind = kind;
|
|
}
|
|
}
|
|
function newCharacterToken(index, end, code) {
|
|
return new Token(index, end, TokenType.Character, code, String.fromCharCode(code));
|
|
}
|
|
function newIdentifierToken(index, end, text) {
|
|
return new Token(index, end, TokenType.Identifier, 0, text);
|
|
}
|
|
function newPrivateIdentifierToken(index, end, text) {
|
|
return new Token(index, end, TokenType.PrivateIdentifier, 0, text);
|
|
}
|
|
function newKeywordToken(index, end, text) {
|
|
return new Token(index, end, TokenType.Keyword, 0, text);
|
|
}
|
|
function newOperatorToken(index, end, text) {
|
|
return new Token(index, end, TokenType.Operator, 0, text);
|
|
}
|
|
function newNumberToken(index, end, n) {
|
|
return new Token(index, end, TokenType.Number, n, '');
|
|
}
|
|
function newErrorToken(index, end, message) {
|
|
return new Token(index, end, TokenType.Error, 0, message);
|
|
}
|
|
function newRegExpBodyToken(index, end, text) {
|
|
return new Token(index, end, TokenType.RegExpBody, 0, text);
|
|
}
|
|
function newRegExpFlagsToken(index, end, text) {
|
|
return new Token(index, end, TokenType.RegExpFlags, 0, text);
|
|
}
|
|
const EOF = new Token(-1, -1, TokenType.Character, 0, '');
|
|
class _Scanner {
|
|
input;
|
|
tokens = [];
|
|
length;
|
|
peek = 0;
|
|
index = -1;
|
|
braceStack = [];
|
|
constructor(input) {
|
|
this.input = input;
|
|
this.length = input.length;
|
|
this.advance();
|
|
}
|
|
scan() {
|
|
let token = this.scanToken();
|
|
while (token !== null) {
|
|
this.tokens.push(token);
|
|
token = this.scanToken();
|
|
}
|
|
return this.tokens;
|
|
}
|
|
advance() {
|
|
this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
|
|
}
|
|
scanToken() {
|
|
const input = this.input;
|
|
const length = this.length;
|
|
let peek = this.peek;
|
|
let index = this.index;
|
|
while (peek <= $SPACE) {
|
|
if (++index >= length) {
|
|
peek = $EOF;
|
|
break;
|
|
} else {
|
|
peek = input.charCodeAt(index);
|
|
}
|
|
}
|
|
this.peek = peek;
|
|
this.index = index;
|
|
if (index >= length) {
|
|
return null;
|
|
}
|
|
if (isIdentifierStart(peek)) {
|
|
return this.scanIdentifier();
|
|
}
|
|
if (isDigit(peek)) {
|
|
return this.scanNumber(index);
|
|
}
|
|
const start = index;
|
|
switch (peek) {
|
|
case $PERIOD:
|
|
this.advance();
|
|
if (isDigit(this.peek)) {
|
|
return this.scanNumber(start);
|
|
}
|
|
if (this.peek !== $PERIOD) {
|
|
return newCharacterToken(start, this.index, $PERIOD);
|
|
}
|
|
this.advance();
|
|
if (this.peek === $PERIOD) {
|
|
this.advance();
|
|
return newOperatorToken(start, this.index, '...');
|
|
}
|
|
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
|
case $LPAREN:
|
|
case $RPAREN:
|
|
case $LBRACKET:
|
|
case $RBRACKET:
|
|
case $COMMA:
|
|
case $COLON:
|
|
case $SEMICOLON:
|
|
return this.scanCharacter(start, peek);
|
|
case $LBRACE:
|
|
return this.scanOpenBrace(start, peek);
|
|
case $RBRACE:
|
|
return this.scanCloseBrace(start, peek);
|
|
case $SQ:
|
|
case $DQ:
|
|
return this.scanString();
|
|
case $BT:
|
|
this.advance();
|
|
return this.scanTemplateLiteralPart(start);
|
|
case $HASH:
|
|
return this.scanPrivateIdentifier();
|
|
case $PLUS:
|
|
return this.scanComplexOperator(start, '+', $EQ, '=');
|
|
case $MINUS:
|
|
return this.scanComplexOperator(start, '-', $EQ, '=');
|
|
case $SLASH:
|
|
return this.isStartOfRegex() ? this.scanRegex(index) : this.scanComplexOperator(start, '/', $EQ, '=');
|
|
case $PERCENT:
|
|
return this.scanComplexOperator(start, '%', $EQ, '=');
|
|
case $CARET:
|
|
return this.scanOperator(start, '^');
|
|
case $STAR:
|
|
return this.scanStar(start);
|
|
case $QUESTION:
|
|
return this.scanQuestion(start);
|
|
case $LT:
|
|
case $GT:
|
|
return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
|
|
case $BANG:
|
|
case $EQ:
|
|
return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
|
|
case $AMPERSAND:
|
|
return this.scanComplexOperator(start, '&', $AMPERSAND, '&', $EQ, '=');
|
|
case $BAR:
|
|
return this.scanComplexOperator(start, '|', $BAR, '|', $EQ, '=');
|
|
case $NBSP:
|
|
while (isWhitespace(this.peek)) this.advance();
|
|
return this.scanToken();
|
|
}
|
|
this.advance();
|
|
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
|
}
|
|
scanCharacter(start, code) {
|
|
this.advance();
|
|
return newCharacterToken(start, this.index, code);
|
|
}
|
|
scanOperator(start, str) {
|
|
this.advance();
|
|
return newOperatorToken(start, this.index, str);
|
|
}
|
|
scanOpenBrace(start, code) {
|
|
this.braceStack.push('expression');
|
|
this.advance();
|
|
return newCharacterToken(start, this.index, code);
|
|
}
|
|
scanCloseBrace(start, code) {
|
|
this.advance();
|
|
const currentBrace = this.braceStack.pop();
|
|
if (currentBrace === 'interpolation') {
|
|
this.tokens.push(newCharacterToken(start, this.index, $RBRACE));
|
|
return this.scanTemplateLiteralPart(this.index);
|
|
}
|
|
return newCharacterToken(start, this.index, code);
|
|
}
|
|
scanComplexOperator(start, one, twoCode, two, threeCode, three) {
|
|
this.advance();
|
|
let str = one;
|
|
if (this.peek == twoCode) {
|
|
this.advance();
|
|
str += two;
|
|
}
|
|
if (threeCode != null && this.peek == threeCode) {
|
|
this.advance();
|
|
str += three;
|
|
}
|
|
return newOperatorToken(start, this.index, str);
|
|
}
|
|
scanIdentifier() {
|
|
const start = this.index;
|
|
this.advance();
|
|
while (isIdentifierPart(this.peek)) this.advance();
|
|
const str = this.input.substring(start, this.index);
|
|
return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) : newIdentifierToken(start, this.index, str);
|
|
}
|
|
scanPrivateIdentifier() {
|
|
const start = this.index;
|
|
this.advance();
|
|
if (!isIdentifierStart(this.peek)) {
|
|
return this.error('Invalid character [#]', -1);
|
|
}
|
|
while (isIdentifierPart(this.peek)) this.advance();
|
|
const identifierName = this.input.substring(start, this.index);
|
|
return newPrivateIdentifierToken(start, this.index, identifierName);
|
|
}
|
|
scanNumber(start) {
|
|
let simple = this.index === start;
|
|
let hasSeparators = false;
|
|
this.advance();
|
|
while (true) {
|
|
if (isDigit(this.peek)) ; else if (this.peek === $_) {
|
|
if (!isDigit(this.input.charCodeAt(this.index - 1)) || !isDigit(this.input.charCodeAt(this.index + 1))) {
|
|
return this.error('Invalid numeric separator', 0);
|
|
}
|
|
hasSeparators = true;
|
|
} else if (this.peek === $PERIOD) {
|
|
simple = false;
|
|
} else if (isExponentStart(this.peek)) {
|
|
this.advance();
|
|
if (isExponentSign(this.peek)) this.advance();
|
|
if (!isDigit(this.peek)) return this.error('Invalid exponent', -1);
|
|
simple = false;
|
|
} else {
|
|
break;
|
|
}
|
|
this.advance();
|
|
}
|
|
let str = this.input.substring(start, this.index);
|
|
if (hasSeparators) {
|
|
str = str.replace(/_/g, '');
|
|
}
|
|
const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
|
|
return newNumberToken(start, this.index, value);
|
|
}
|
|
scanString() {
|
|
const start = this.index;
|
|
const quote = this.peek;
|
|
this.advance();
|
|
let buffer = '';
|
|
let marker = this.index;
|
|
const input = this.input;
|
|
while (this.peek != quote) {
|
|
if (this.peek == $BACKSLASH) {
|
|
const result = this.scanStringBackslash(buffer, marker);
|
|
if (typeof result !== 'string') {
|
|
return result;
|
|
}
|
|
buffer = result;
|
|
marker = this.index;
|
|
} else if (this.peek == $EOF) {
|
|
return this.error('Unterminated quote', 0);
|
|
} else {
|
|
this.advance();
|
|
}
|
|
}
|
|
const last = input.substring(marker, this.index);
|
|
this.advance();
|
|
return new StringToken(start, this.index, buffer + last, StringTokenKind.Plain);
|
|
}
|
|
scanQuestion(start) {
|
|
this.advance();
|
|
let operator = '?';
|
|
if (this.peek === $QUESTION) {
|
|
operator += '?';
|
|
this.advance();
|
|
if (this.peek === $EQ) {
|
|
operator += '=';
|
|
this.advance();
|
|
}
|
|
} else if (this.peek === $PERIOD) {
|
|
operator += '.';
|
|
this.advance();
|
|
}
|
|
return newOperatorToken(start, this.index, operator);
|
|
}
|
|
scanTemplateLiteralPart(start) {
|
|
let buffer = '';
|
|
let marker = this.index;
|
|
while (this.peek !== $BT) {
|
|
if (this.peek === $BACKSLASH) {
|
|
const result = this.scanStringBackslash(buffer, marker);
|
|
if (typeof result !== 'string') {
|
|
return result;
|
|
}
|
|
buffer = result;
|
|
marker = this.index;
|
|
} else if (this.peek === $$) {
|
|
const dollar = this.index;
|
|
this.advance();
|
|
if (this.peek === $LBRACE) {
|
|
this.braceStack.push('interpolation');
|
|
this.tokens.push(new StringToken(start, dollar, buffer + this.input.substring(marker, dollar), StringTokenKind.TemplateLiteralPart));
|
|
this.advance();
|
|
return newOperatorToken(dollar, this.index, this.input.substring(dollar, this.index));
|
|
}
|
|
} else if (this.peek === $EOF) {
|
|
return this.error('Unterminated template literal', 0);
|
|
} else {
|
|
this.advance();
|
|
}
|
|
}
|
|
const last = this.input.substring(marker, this.index);
|
|
this.advance();
|
|
return new StringToken(start, this.index, buffer + last, StringTokenKind.TemplateLiteralEnd);
|
|
}
|
|
error(message, offset) {
|
|
const position = this.index + offset;
|
|
return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
|
}
|
|
scanStringBackslash(buffer, marker) {
|
|
buffer += this.input.substring(marker, this.index);
|
|
let unescapedCode;
|
|
this.advance();
|
|
if (this.peek === $u) {
|
|
const hex = this.input.substring(this.index + 1, this.index + 5);
|
|
if (/^[0-9a-f]+$/i.test(hex)) {
|
|
unescapedCode = parseInt(hex, 16);
|
|
} else {
|
|
return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
|
}
|
|
for (let i = 0; i < 5; i++) {
|
|
this.advance();
|
|
}
|
|
} else {
|
|
unescapedCode = unescape(this.peek);
|
|
this.advance();
|
|
}
|
|
buffer += String.fromCharCode(unescapedCode);
|
|
return buffer;
|
|
}
|
|
scanStar(start) {
|
|
this.advance();
|
|
let operator = '*';
|
|
if (this.peek === $STAR) {
|
|
operator += '*';
|
|
this.advance();
|
|
if (this.peek === $EQ) {
|
|
operator += '=';
|
|
this.advance();
|
|
}
|
|
} else if (this.peek === $EQ) {
|
|
operator += '=';
|
|
this.advance();
|
|
}
|
|
return newOperatorToken(start, this.index, operator);
|
|
}
|
|
isStartOfRegex() {
|
|
if (this.tokens.length === 0) {
|
|
return true;
|
|
}
|
|
const prevToken = this.tokens[this.tokens.length - 1];
|
|
if (prevToken.isOperator('!')) {
|
|
const beforePrevToken = this.tokens.length > 1 ? this.tokens[this.tokens.length - 2] : null;
|
|
const isNegation = beforePrevToken === null || beforePrevToken.type !== TokenType.Identifier && !beforePrevToken.isCharacter($RPAREN) && !beforePrevToken.isCharacter($RBRACKET);
|
|
return isNegation;
|
|
}
|
|
return prevToken.type === TokenType.Operator || prevToken.isCharacter($LPAREN) || prevToken.isCharacter($LBRACKET) || prevToken.isCharacter($COMMA) || prevToken.isCharacter($COLON);
|
|
}
|
|
scanRegex(tokenStart) {
|
|
this.advance();
|
|
const textStart = this.index;
|
|
let inEscape = false;
|
|
let inCharacterClass = false;
|
|
while (true) {
|
|
const peek = this.peek;
|
|
if (peek === $EOF) {
|
|
return this.error('Unterminated regular expression', 0);
|
|
}
|
|
if (inEscape) {
|
|
inEscape = false;
|
|
} else if (peek === $BACKSLASH) {
|
|
inEscape = true;
|
|
} else if (peek === $LBRACKET) {
|
|
inCharacterClass = true;
|
|
} else if (peek === $RBRACKET) {
|
|
inCharacterClass = false;
|
|
} else if (peek === $SLASH && !inCharacterClass) {
|
|
break;
|
|
}
|
|
this.advance();
|
|
}
|
|
const value = this.input.substring(textStart, this.index);
|
|
this.advance();
|
|
const bodyToken = newRegExpBodyToken(tokenStart, this.index, value);
|
|
const flagsToken = this.scanRegexFlags(this.index);
|
|
if (flagsToken !== null) {
|
|
this.tokens.push(bodyToken);
|
|
return flagsToken;
|
|
}
|
|
return bodyToken;
|
|
}
|
|
scanRegexFlags(start) {
|
|
if (!isAsciiLetter(this.peek)) {
|
|
return null;
|
|
}
|
|
while (isAsciiLetter(this.peek)) {
|
|
this.advance();
|
|
}
|
|
return newRegExpFlagsToken(start, this.index, this.input.substring(start, this.index));
|
|
}
|
|
}
|
|
function isIdentifierStart(code) {
|
|
return $a <= code && code <= $z || $A <= code && code <= $Z || code == $_ || code == $$;
|
|
}
|
|
function isIdentifierPart(code) {
|
|
return isAsciiLetter(code) || isDigit(code) || code == $_ || code == $$;
|
|
}
|
|
function isExponentStart(code) {
|
|
return code == $e || code == $E;
|
|
}
|
|
function isExponentSign(code) {
|
|
return code == $MINUS || code == $PLUS;
|
|
}
|
|
function unescape(code) {
|
|
switch (code) {
|
|
case $n:
|
|
return $LF;
|
|
case $f:
|
|
return $FF;
|
|
case $r:
|
|
return $CR;
|
|
case $t:
|
|
return $TAB;
|
|
case $v:
|
|
return $VTAB;
|
|
default:
|
|
return code;
|
|
}
|
|
}
|
|
function parseIntAutoRadix(text) {
|
|
const result = parseInt(text);
|
|
if (isNaN(result)) {
|
|
throw new Error('Invalid integer literal when parsing ' + text);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class SplitInterpolation {
|
|
strings;
|
|
expressions;
|
|
offsets;
|
|
constructor(strings, expressions, offsets) {
|
|
this.strings = strings;
|
|
this.expressions = expressions;
|
|
this.offsets = offsets;
|
|
}
|
|
}
|
|
class TemplateBindingParseResult {
|
|
templateBindings;
|
|
warnings;
|
|
errors;
|
|
constructor(templateBindings, warnings, errors) {
|
|
this.templateBindings = templateBindings;
|
|
this.warnings = warnings;
|
|
this.errors = errors;
|
|
}
|
|
}
|
|
function getLocation(span) {
|
|
return span.start.toString() || '(unknown)';
|
|
}
|
|
class Parser {
|
|
_lexer;
|
|
_supportsDirectPipeReferences;
|
|
constructor(_lexer, _supportsDirectPipeReferences = false) {
|
|
this._lexer = _lexer;
|
|
this._supportsDirectPipeReferences = _supportsDirectPipeReferences;
|
|
}
|
|
parseAction(input, parseSourceSpan, absoluteOffset) {
|
|
const errors = [];
|
|
this._checkNoInterpolation(errors, input, parseSourceSpan);
|
|
const {
|
|
stripped: sourceToLex
|
|
} = this._stripComments(input);
|
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
const ast = new _ParseAST(input, parseSourceSpan, absoluteOffset, tokens, 1, errors, 0, this._supportsDirectPipeReferences).parseChain();
|
|
return new ASTWithSource(ast, input, getLocation(parseSourceSpan), absoluteOffset, errors);
|
|
}
|
|
parseBinding(input, parseSourceSpan, absoluteOffset) {
|
|
const errors = [];
|
|
const ast = this._parseBindingAst(input, parseSourceSpan, absoluteOffset, errors);
|
|
return new ASTWithSource(ast, input, getLocation(parseSourceSpan), absoluteOffset, errors);
|
|
}
|
|
checkSimpleExpression(ast) {
|
|
const checker = new SimpleExpressionChecker();
|
|
ast.visit(checker);
|
|
return checker.errors;
|
|
}
|
|
parseSimpleBinding(input, parseSourceSpan, absoluteOffset) {
|
|
const errors = [];
|
|
const ast = this._parseBindingAst(input, parseSourceSpan, absoluteOffset, errors);
|
|
const simplExpressionErrors = this.checkSimpleExpression(ast);
|
|
if (simplExpressionErrors.length > 0) {
|
|
errors.push(getParseError(`Host binding expression cannot contain ${simplExpressionErrors.join(' ')}`, input, '', parseSourceSpan));
|
|
}
|
|
return new ASTWithSource(ast, input, getLocation(parseSourceSpan), absoluteOffset, errors);
|
|
}
|
|
_parseBindingAst(input, parseSourceSpan, absoluteOffset, errors) {
|
|
this._checkNoInterpolation(errors, input, parseSourceSpan);
|
|
const {
|
|
stripped: sourceToLex
|
|
} = this._stripComments(input);
|
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
return new _ParseAST(input, parseSourceSpan, absoluteOffset, tokens, 0, errors, 0, this._supportsDirectPipeReferences).parseChain();
|
|
}
|
|
parseTemplateBindings(templateKey, templateValue, parseSourceSpan, absoluteKeyOffset, absoluteValueOffset) {
|
|
const tokens = this._lexer.tokenize(templateValue);
|
|
const errors = [];
|
|
const parser = new _ParseAST(templateValue, parseSourceSpan, absoluteValueOffset, tokens, 0, errors, 0, this._supportsDirectPipeReferences);
|
|
return parser.parseTemplateBindings({
|
|
source: templateKey,
|
|
span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length)
|
|
});
|
|
}
|
|
parseInterpolation(input, parseSourceSpan, absoluteOffset, interpolatedTokens) {
|
|
const errors = [];
|
|
const {
|
|
strings,
|
|
expressions,
|
|
offsets
|
|
} = this.splitInterpolation(input, parseSourceSpan, errors, interpolatedTokens);
|
|
if (expressions.length === 0) return null;
|
|
const expressionNodes = [];
|
|
for (let i = 0; i < expressions.length; ++i) {
|
|
const expressionSpan = interpolatedTokens?.[i * 2 + 1]?.sourceSpan;
|
|
const expressionText = expressions[i].text;
|
|
const {
|
|
stripped: sourceToLex,
|
|
hasComments
|
|
} = this._stripComments(expressionText);
|
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
if (hasComments && sourceToLex.trim().length === 0 && tokens.length === 0) {
|
|
errors.push(getParseError('Interpolation expression cannot only contain a comment', input, `at column ${expressions[i].start} in`, parseSourceSpan));
|
|
continue;
|
|
}
|
|
const ast = new _ParseAST(expressionSpan ? expressionText : input, expressionSpan || parseSourceSpan, absoluteOffset, tokens, 0, errors, offsets[i], this._supportsDirectPipeReferences).parseChain();
|
|
expressionNodes.push(ast);
|
|
}
|
|
return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, getLocation(parseSourceSpan), absoluteOffset, errors);
|
|
}
|
|
parseInterpolationExpression(expression, parseSourceSpan, absoluteOffset) {
|
|
const {
|
|
stripped: sourceToLex
|
|
} = this._stripComments(expression);
|
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
const errors = [];
|
|
const ast = new _ParseAST(expression, parseSourceSpan, absoluteOffset, tokens, 0, errors, 0, this._supportsDirectPipeReferences).parseChain();
|
|
const strings = ['', ''];
|
|
return this.createInterpolationAst(strings, [ast], expression, getLocation(parseSourceSpan), absoluteOffset, errors);
|
|
}
|
|
createInterpolationAst(strings, expressions, input, location, absoluteOffset, errors) {
|
|
const span = new ParseSpan(0, input.length);
|
|
const interpolation = new Interpolation$1(span, span.toAbsolute(absoluteOffset), strings, expressions);
|
|
return new ASTWithSource(interpolation, input, location, absoluteOffset, errors);
|
|
}
|
|
splitInterpolation(input, parseSourceSpan, errors, interpolatedTokens) {
|
|
const strings = [];
|
|
const expressions = [];
|
|
const offsets = [];
|
|
const inputToTemplateIndexMap = interpolatedTokens ? getIndexMapForOriginalTemplate(interpolatedTokens) : null;
|
|
let i = 0;
|
|
let atInterpolation = false;
|
|
let extendLastString = false;
|
|
const interpStart = '{{';
|
|
const interpEnd = '}}';
|
|
while (i < input.length) {
|
|
if (!atInterpolation) {
|
|
const start = i;
|
|
i = input.indexOf(interpStart, i);
|
|
if (i === -1) {
|
|
i = input.length;
|
|
}
|
|
const text = input.substring(start, i);
|
|
strings.push({
|
|
text,
|
|
start,
|
|
end: i
|
|
});
|
|
atInterpolation = true;
|
|
} else {
|
|
const fullStart = i;
|
|
const exprStart = fullStart + interpStart.length;
|
|
const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
|
|
if (exprEnd === -1) {
|
|
atInterpolation = false;
|
|
extendLastString = true;
|
|
break;
|
|
}
|
|
const fullEnd = exprEnd + interpEnd.length;
|
|
const text = input.substring(exprStart, exprEnd);
|
|
if (text.trim().length === 0) {
|
|
errors.push(getParseError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, parseSourceSpan));
|
|
}
|
|
expressions.push({
|
|
text,
|
|
start: fullStart,
|
|
end: fullEnd
|
|
});
|
|
const startInOriginalTemplate = inputToTemplateIndexMap?.get(fullStart) ?? fullStart;
|
|
const offset = startInOriginalTemplate + interpStart.length;
|
|
offsets.push(offset);
|
|
i = fullEnd;
|
|
atInterpolation = false;
|
|
}
|
|
}
|
|
if (!atInterpolation) {
|
|
if (extendLastString) {
|
|
const piece = strings[strings.length - 1];
|
|
piece.text += input.substring(i);
|
|
piece.end = input.length;
|
|
} else {
|
|
strings.push({
|
|
text: input.substring(i),
|
|
start: i,
|
|
end: input.length
|
|
});
|
|
}
|
|
}
|
|
return new SplitInterpolation(strings, expressions, offsets);
|
|
}
|
|
wrapLiteralPrimitive(input, sourceSpanOrLocation, absoluteOffset) {
|
|
const span = new ParseSpan(0, input == null ? 0 : input.length);
|
|
return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, typeof sourceSpanOrLocation === 'string' ? sourceSpanOrLocation : getLocation(sourceSpanOrLocation), absoluteOffset, []);
|
|
}
|
|
_stripComments(input) {
|
|
const i = this._commentStart(input);
|
|
return i != null ? {
|
|
stripped: input.substring(0, i),
|
|
hasComments: true
|
|
} : {
|
|
stripped: input,
|
|
hasComments: false
|
|
};
|
|
}
|
|
_commentStart(input) {
|
|
let outerQuote = null;
|
|
for (let i = 0; i < input.length - 1; i++) {
|
|
const char = input.charCodeAt(i);
|
|
const nextChar = input.charCodeAt(i + 1);
|
|
if (char === $SLASH && nextChar == $SLASH && outerQuote == null) return i;
|
|
if (outerQuote === char) {
|
|
outerQuote = null;
|
|
} else if (outerQuote == null && isQuote(char)) {
|
|
outerQuote = char;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
_checkNoInterpolation(errors, input, parseSourceSpan) {
|
|
let startIndex = -1;
|
|
let endIndex = -1;
|
|
for (const charIndex of this._forEachUnquotedChar(input, 0)) {
|
|
if (startIndex === -1) {
|
|
if (input.startsWith('{{')) {
|
|
startIndex = charIndex;
|
|
}
|
|
} else {
|
|
endIndex = this._getInterpolationEndIndex(input, '}}', charIndex);
|
|
if (endIndex > -1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (startIndex > -1 && endIndex > -1) {
|
|
errors.push(getParseError(`Got interpolation ({{}}) where expression was expected`, input, `at column ${startIndex} in`, parseSourceSpan));
|
|
}
|
|
}
|
|
_getInterpolationEndIndex(input, expressionEnd, start) {
|
|
for (const charIndex of this._forEachUnquotedChar(input, start)) {
|
|
if (input.startsWith(expressionEnd, charIndex)) {
|
|
return charIndex;
|
|
}
|
|
if (input.startsWith('//', charIndex)) {
|
|
return input.indexOf(expressionEnd, charIndex);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
*_forEachUnquotedChar(input, start) {
|
|
let currentQuote = null;
|
|
let escapeCount = 0;
|
|
for (let i = start; i < input.length; i++) {
|
|
const char = input[i];
|
|
if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) && escapeCount % 2 === 0) {
|
|
currentQuote = currentQuote === null ? char : null;
|
|
} else if (currentQuote === null) {
|
|
yield i;
|
|
}
|
|
escapeCount = char === '\\' ? escapeCount + 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
var ParseContextFlags;
|
|
(function (ParseContextFlags) {
|
|
ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
|
|
ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
|
|
})(ParseContextFlags || (ParseContextFlags = {}));
|
|
const SUPPORTED_REGEX_FLAGS = new Set(['d', 'g', 'i', 'm', 's', 'u', 'v', 'y']);
|
|
class _ParseAST {
|
|
input;
|
|
parseSourceSpan;
|
|
absoluteOffset;
|
|
tokens;
|
|
parseFlags;
|
|
errors;
|
|
offset;
|
|
supportsDirectPipeReferences;
|
|
rparensExpected = 0;
|
|
rbracketsExpected = 0;
|
|
rbracesExpected = 0;
|
|
context = ParseContextFlags.None;
|
|
sourceSpanCache = new Map();
|
|
index = 0;
|
|
constructor(input, parseSourceSpan, absoluteOffset, tokens, parseFlags, errors, offset, supportsDirectPipeReferences) {
|
|
this.input = input;
|
|
this.parseSourceSpan = parseSourceSpan;
|
|
this.absoluteOffset = absoluteOffset;
|
|
this.tokens = tokens;
|
|
this.parseFlags = parseFlags;
|
|
this.errors = errors;
|
|
this.offset = offset;
|
|
this.supportsDirectPipeReferences = supportsDirectPipeReferences;
|
|
}
|
|
peek(offset) {
|
|
const i = this.index + offset;
|
|
return i < this.tokens.length ? this.tokens[i] : EOF;
|
|
}
|
|
get next() {
|
|
return this.peek(0);
|
|
}
|
|
get atEOF() {
|
|
return this.index >= this.tokens.length;
|
|
}
|
|
get inputIndex() {
|
|
return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
|
|
}
|
|
get currentEndIndex() {
|
|
if (this.index > 0) {
|
|
const curToken = this.peek(-1);
|
|
return curToken.end + this.offset;
|
|
}
|
|
if (this.tokens.length === 0) {
|
|
return this.input.length + this.offset;
|
|
}
|
|
return this.next.index + this.offset;
|
|
}
|
|
get currentAbsoluteOffset() {
|
|
return this.absoluteOffset + this.inputIndex;
|
|
}
|
|
span(start, artificialEndIndex) {
|
|
let endIndex = this.currentEndIndex;
|
|
if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
|
|
endIndex = artificialEndIndex;
|
|
}
|
|
if (start > endIndex) {
|
|
const tmp = endIndex;
|
|
endIndex = start;
|
|
start = tmp;
|
|
}
|
|
return new ParseSpan(start, endIndex);
|
|
}
|
|
sourceSpan(start, artificialEndIndex) {
|
|
const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
|
|
if (!this.sourceSpanCache.has(serial)) {
|
|
this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
|
|
}
|
|
return this.sourceSpanCache.get(serial);
|
|
}
|
|
advance() {
|
|
this.index++;
|
|
}
|
|
withContext(context, cb) {
|
|
this.context |= context;
|
|
const ret = cb();
|
|
this.context ^= context;
|
|
return ret;
|
|
}
|
|
consumeOptionalCharacter(code) {
|
|
if (this.next.isCharacter(code)) {
|
|
this.advance();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
peekKeywordLet() {
|
|
return this.next.isKeywordLet();
|
|
}
|
|
peekKeywordAs() {
|
|
return this.next.isKeywordAs();
|
|
}
|
|
expectCharacter(code) {
|
|
if (this.consumeOptionalCharacter(code)) return;
|
|
this.error(`Missing expected ${String.fromCharCode(code)}`);
|
|
}
|
|
consumeOptionalOperator(op) {
|
|
if (this.next.isOperator(op)) {
|
|
this.advance();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
isAssignmentOperator(token) {
|
|
return token.type === TokenType.Operator && Binary.isAssignmentOperation(token.strValue);
|
|
}
|
|
expectOperator(operator) {
|
|
if (this.consumeOptionalOperator(operator)) return;
|
|
this.error(`Missing expected operator ${operator}`);
|
|
}
|
|
prettyPrintToken(tok) {
|
|
return tok === EOF ? 'end of input' : `token ${tok}`;
|
|
}
|
|
expectIdentifierOrKeyword() {
|
|
const n = this.next;
|
|
if (!n.isIdentifier() && !n.isKeyword()) {
|
|
if (n.isPrivateIdentifier()) {
|
|
this._reportErrorForPrivateIdentifier(n, 'expected identifier or keyword');
|
|
} else {
|
|
this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
|
|
}
|
|
return null;
|
|
}
|
|
this.advance();
|
|
return n.toString();
|
|
}
|
|
expectIdentifierOrKeywordOrString() {
|
|
const n = this.next;
|
|
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
|
if (n.isPrivateIdentifier()) {
|
|
this._reportErrorForPrivateIdentifier(n, 'expected identifier, keyword or string');
|
|
} else {
|
|
this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
|
|
}
|
|
return '';
|
|
}
|
|
this.advance();
|
|
return n.toString();
|
|
}
|
|
parseChain() {
|
|
const exprs = [];
|
|
const start = this.inputIndex;
|
|
while (this.index < this.tokens.length) {
|
|
const expr = this.parsePipe();
|
|
exprs.push(expr);
|
|
if (this.consumeOptionalCharacter($SEMICOLON)) {
|
|
if (!(this.parseFlags & 1)) {
|
|
this.error('Binding expression cannot contain chained expression');
|
|
}
|
|
while (this.consumeOptionalCharacter($SEMICOLON)) {}
|
|
} else if (this.index < this.tokens.length) {
|
|
const errorIndex = this.index;
|
|
this.error(`Unexpected token '${this.next}'`);
|
|
if (this.index === errorIndex) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (exprs.length === 0) {
|
|
const artificialStart = this.offset;
|
|
const artificialEnd = this.offset + this.input.length;
|
|
return new EmptyExpr$1(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
|
|
}
|
|
if (exprs.length == 1) return exprs[0];
|
|
return new Chain(this.span(start), this.sourceSpan(start), exprs);
|
|
}
|
|
parsePipe() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseExpression();
|
|
if (this.consumeOptionalOperator('|')) {
|
|
if (this.parseFlags & 1) {
|
|
this.error(`Cannot have a pipe in an action expression`);
|
|
}
|
|
do {
|
|
const nameStart = this.inputIndex;
|
|
let nameId = this.expectIdentifierOrKeyword();
|
|
let nameSpan;
|
|
let fullSpanEnd = undefined;
|
|
if (nameId !== null) {
|
|
nameSpan = this.sourceSpan(nameStart);
|
|
} else {
|
|
nameId = '';
|
|
fullSpanEnd = this.next.index !== -1 ? this.next.index : this.input.length + this.offset;
|
|
nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
|
|
}
|
|
const args = [];
|
|
while (this.consumeOptionalCharacter($COLON)) {
|
|
args.push(this.parseExpression());
|
|
}
|
|
let type;
|
|
if (this.supportsDirectPipeReferences) {
|
|
const charCode = nameId.charCodeAt(0);
|
|
type = charCode === $_ || charCode >= $A && charCode <= $Z ? BindingPipeType.ReferencedDirectly : BindingPipeType.ReferencedByName;
|
|
} else {
|
|
type = BindingPipeType.ReferencedByName;
|
|
}
|
|
result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, type, nameSpan);
|
|
} while (this.consumeOptionalOperator('|'));
|
|
}
|
|
return result;
|
|
}
|
|
parseExpression() {
|
|
return this.parseConditional();
|
|
}
|
|
parseConditional() {
|
|
const start = this.inputIndex;
|
|
const result = this.parseLogicalOr();
|
|
if (this.consumeOptionalOperator('?')) {
|
|
const yes = this.parsePipe();
|
|
let no;
|
|
if (!this.consumeOptionalCharacter($COLON)) {
|
|
const end = this.inputIndex;
|
|
const expression = this.input.substring(start, end);
|
|
this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
|
no = new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
} else {
|
|
no = this.parsePipe();
|
|
}
|
|
return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
parseLogicalOr() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseLogicalAnd();
|
|
while (this.consumeOptionalOperator('||')) {
|
|
const right = this.parseLogicalAnd();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
|
|
}
|
|
return result;
|
|
}
|
|
parseLogicalAnd() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseNullishCoalescing();
|
|
while (this.consumeOptionalOperator('&&')) {
|
|
const right = this.parseNullishCoalescing();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
|
|
}
|
|
return result;
|
|
}
|
|
parseNullishCoalescing() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseEquality();
|
|
while (this.consumeOptionalOperator('??')) {
|
|
const right = this.parseEquality();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), '??', result, right);
|
|
}
|
|
return result;
|
|
}
|
|
parseEquality() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseRelational();
|
|
while (this.next.type == TokenType.Operator) {
|
|
const operator = this.next.strValue;
|
|
switch (operator) {
|
|
case '==':
|
|
case '===':
|
|
case '!=':
|
|
case '!==':
|
|
this.advance();
|
|
const right = this.parseRelational();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
parseRelational() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseAdditive();
|
|
while (this.next.type == TokenType.Operator || this.next.isKeywordIn) {
|
|
const operator = this.next.strValue;
|
|
switch (operator) {
|
|
case '<':
|
|
case '>':
|
|
case '<=':
|
|
case '>=':
|
|
case 'in':
|
|
this.advance();
|
|
const right = this.parseAdditive();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
parseAdditive() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseMultiplicative();
|
|
while (this.next.type == TokenType.Operator) {
|
|
const operator = this.next.strValue;
|
|
switch (operator) {
|
|
case '+':
|
|
case '-':
|
|
this.advance();
|
|
let right = this.parseMultiplicative();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
parseMultiplicative() {
|
|
const start = this.inputIndex;
|
|
let result = this.parseExponentiation();
|
|
while (this.next.type == TokenType.Operator) {
|
|
const operator = this.next.strValue;
|
|
switch (operator) {
|
|
case '*':
|
|
case '%':
|
|
case '/':
|
|
this.advance();
|
|
const right = this.parseExponentiation();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
parseExponentiation() {
|
|
const start = this.inputIndex;
|
|
let result = this.parsePrefix();
|
|
while (this.next.type == TokenType.Operator && this.next.strValue === '**') {
|
|
if (result instanceof Unary || result instanceof PrefixNot || result instanceof TypeofExpression || result instanceof VoidExpression) {
|
|
this.error('Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence');
|
|
}
|
|
this.advance();
|
|
const right = this.parseExponentiation();
|
|
result = new Binary(this.span(start), this.sourceSpan(start), '**', result, right);
|
|
}
|
|
return result;
|
|
}
|
|
parsePrefix() {
|
|
if (this.next.type == TokenType.Operator) {
|
|
const start = this.inputIndex;
|
|
const operator = this.next.strValue;
|
|
let result;
|
|
switch (operator) {
|
|
case '+':
|
|
this.advance();
|
|
result = this.parsePrefix();
|
|
return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
|
|
case '-':
|
|
this.advance();
|
|
result = this.parsePrefix();
|
|
return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
|
|
case '!':
|
|
this.advance();
|
|
result = this.parsePrefix();
|
|
return new PrefixNot(this.span(start), this.sourceSpan(start), result);
|
|
}
|
|
} else if (this.next.isKeywordTypeof()) {
|
|
const start = this.inputIndex;
|
|
this.advance();
|
|
const result = this.parsePrefix();
|
|
return new TypeofExpression(this.span(start), this.sourceSpan(start), result);
|
|
} else if (this.next.isKeywordVoid()) {
|
|
const start = this.inputIndex;
|
|
this.advance();
|
|
const result = this.parsePrefix();
|
|
return new VoidExpression(this.span(start), this.sourceSpan(start), result);
|
|
}
|
|
return this.parseCallChain();
|
|
}
|
|
parseCallChain() {
|
|
const start = this.inputIndex;
|
|
let result = this.parsePrimary();
|
|
while (true) {
|
|
if (this.consumeOptionalCharacter($PERIOD)) {
|
|
result = this.parseAccessMember(result, start, false);
|
|
} else if (this.consumeOptionalOperator('?.')) {
|
|
if (this.consumeOptionalCharacter($LPAREN)) {
|
|
result = this.parseCall(result, start, true);
|
|
} else {
|
|
result = this.consumeOptionalCharacter($LBRACKET) ? this.parseKeyedReadOrWrite(result, start, true) : this.parseAccessMember(result, start, true);
|
|
}
|
|
} else if (this.consumeOptionalCharacter($LBRACKET)) {
|
|
result = this.parseKeyedReadOrWrite(result, start, false);
|
|
} else if (this.consumeOptionalCharacter($LPAREN)) {
|
|
result = this.parseCall(result, start, false);
|
|
} else if (this.consumeOptionalOperator('!')) {
|
|
result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
|
|
} else if (this.next.isTemplateLiteralEnd()) {
|
|
result = this.parseNoInterpolationTaggedTemplateLiteral(result, start);
|
|
} else if (this.next.isTemplateLiteralPart()) {
|
|
result = this.parseTaggedTemplateLiteral(result, start);
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
parsePrimary() {
|
|
const start = this.inputIndex;
|
|
if (this.consumeOptionalCharacter($LPAREN)) {
|
|
this.rparensExpected++;
|
|
const result = this.parsePipe();
|
|
if (!this.consumeOptionalCharacter($RPAREN)) {
|
|
this.error('Missing closing parentheses');
|
|
this.consumeOptionalCharacter($RPAREN);
|
|
}
|
|
this.rparensExpected--;
|
|
return new ParenthesizedExpression(this.span(start), this.sourceSpan(start), result);
|
|
} else if (this.next.isKeywordNull()) {
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
|
|
} else if (this.next.isKeywordUndefined()) {
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
|
|
} else if (this.next.isKeywordTrue()) {
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
|
|
} else if (this.next.isKeywordFalse()) {
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
|
|
} else if (this.next.isKeywordIn()) {
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), 'in');
|
|
} else if (this.next.isKeywordThis()) {
|
|
this.advance();
|
|
return new ThisReceiver(this.span(start), this.sourceSpan(start));
|
|
} else if (this.consumeOptionalCharacter($LBRACKET)) {
|
|
return this.parseLiteralArray(start);
|
|
} else if (this.next.isCharacter($LBRACE)) {
|
|
return this.parseLiteralMap();
|
|
} else if (this.next.isIdentifier()) {
|
|
return this.parseAccessMember(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
|
|
} else if (this.next.isNumber()) {
|
|
const value = this.next.toNumber();
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
|
|
} else if (this.next.isTemplateLiteralEnd()) {
|
|
return this.parseNoInterpolationTemplateLiteral();
|
|
} else if (this.next.isTemplateLiteralPart()) {
|
|
return this.parseTemplateLiteral();
|
|
} else if (this.next.isString() && this.next.kind === StringTokenKind.Plain) {
|
|
const literalValue = this.next.toString();
|
|
this.advance();
|
|
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
|
|
} else if (this.next.isPrivateIdentifier()) {
|
|
this._reportErrorForPrivateIdentifier(this.next, null);
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
} else if (this.next.isRegExpBody()) {
|
|
return this.parseRegularExpressionLiteral();
|
|
} else if (this.index >= this.tokens.length) {
|
|
this.error(`Unexpected end of expression: ${this.input}`);
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
} else {
|
|
this.error(`Unexpected token ${this.next}`);
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
}
|
|
}
|
|
parseLiteralArray(arrayStart) {
|
|
this.rbracketsExpected++;
|
|
const elements = [];
|
|
do {
|
|
if (this.next.isOperator('...')) {
|
|
elements.push(this.parseSpreadElement());
|
|
} else if (!this.next.isCharacter($RBRACKET)) {
|
|
elements.push(this.parsePipe());
|
|
} else {
|
|
break;
|
|
}
|
|
} while (this.consumeOptionalCharacter($COMMA));
|
|
this.rbracketsExpected--;
|
|
this.expectCharacter($RBRACKET);
|
|
return new LiteralArray(this.span(arrayStart), this.sourceSpan(arrayStart), elements);
|
|
}
|
|
parseLiteralMap() {
|
|
const keys = [];
|
|
const values = [];
|
|
const start = this.inputIndex;
|
|
this.expectCharacter($LBRACE);
|
|
if (!this.consumeOptionalCharacter($RBRACE)) {
|
|
this.rbracesExpected++;
|
|
do {
|
|
const keyStart = this.inputIndex;
|
|
if (this.next.isOperator('...')) {
|
|
this.advance();
|
|
keys.push({
|
|
kind: 'spread',
|
|
span: this.span(keyStart),
|
|
sourceSpan: this.sourceSpan(keyStart)
|
|
});
|
|
values.push(this.parsePipe());
|
|
continue;
|
|
}
|
|
const quoted = this.next.isString();
|
|
const key = this.expectIdentifierOrKeywordOrString();
|
|
const keySpan = this.span(keyStart);
|
|
const keySourceSpan = this.sourceSpan(keyStart);
|
|
const literalMapKey = {
|
|
kind: 'property',
|
|
key,
|
|
quoted,
|
|
span: keySpan,
|
|
sourceSpan: keySourceSpan
|
|
};
|
|
keys.push(literalMapKey);
|
|
if (quoted) {
|
|
this.expectCharacter($COLON);
|
|
values.push(this.parsePipe());
|
|
} else if (this.consumeOptionalCharacter($COLON)) {
|
|
values.push(this.parsePipe());
|
|
} else {
|
|
literalMapKey.isShorthandInitialized = true;
|
|
values.push(new PropertyRead(keySpan, keySourceSpan, keySourceSpan, new ImplicitReceiver(keySpan, keySourceSpan), key));
|
|
}
|
|
} while (this.consumeOptionalCharacter($COMMA) && !this.next.isCharacter($RBRACE));
|
|
this.rbracesExpected--;
|
|
this.expectCharacter($RBRACE);
|
|
}
|
|
return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
|
|
}
|
|
parseAccessMember(readReceiver, start, isSafe) {
|
|
const nameStart = this.inputIndex;
|
|
const id = this.withContext(ParseContextFlags.Writable, () => {
|
|
const id = this.expectIdentifierOrKeyword() ?? '';
|
|
if (id.length === 0) {
|
|
this.error(`Expected identifier for property access`, readReceiver.span.end);
|
|
}
|
|
return id;
|
|
});
|
|
const nameSpan = this.sourceSpan(nameStart);
|
|
if (isSafe) {
|
|
if (this.isAssignmentOperator(this.next)) {
|
|
this.advance();
|
|
this.error("The '?.' operator cannot be used in the assignment");
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
} else {
|
|
return new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);
|
|
}
|
|
} else {
|
|
if (this.isAssignmentOperator(this.next)) {
|
|
const operation = this.next.strValue;
|
|
if (!(this.parseFlags & 1)) {
|
|
this.advance();
|
|
this.error('Bindings cannot contain assignments');
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
}
|
|
const receiver = new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);
|
|
this.advance();
|
|
const value = this.parseConditional();
|
|
return new Binary(this.span(start), this.sourceSpan(start), operation, receiver, value);
|
|
} else {
|
|
return new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);
|
|
}
|
|
}
|
|
}
|
|
parseCall(receiver, start, isSafe) {
|
|
const argumentStart = this.inputIndex;
|
|
this.rparensExpected++;
|
|
const args = this.parseCallArguments();
|
|
const argumentSpan = this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset);
|
|
this.expectCharacter($RPAREN);
|
|
this.rparensExpected--;
|
|
const span = this.span(start);
|
|
const sourceSpan = this.sourceSpan(start);
|
|
return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) : new Call(span, sourceSpan, receiver, args, argumentSpan);
|
|
}
|
|
parseCallArguments() {
|
|
if (this.next.isCharacter($RPAREN)) {
|
|
return [];
|
|
}
|
|
const positionals = [];
|
|
do {
|
|
positionals.push(this.next.isOperator('...') ? this.parseSpreadElement() : this.parsePipe());
|
|
} while (this.consumeOptionalCharacter($COMMA));
|
|
return positionals;
|
|
}
|
|
parseSpreadElement() {
|
|
if (!this.next.isOperator('...')) {
|
|
this.error("Spread element must start with '...' operator");
|
|
}
|
|
const spreadStart = this.inputIndex;
|
|
this.advance();
|
|
const expression = this.parsePipe();
|
|
const span = this.span(spreadStart);
|
|
const sourceSpan = this.sourceSpan(spreadStart);
|
|
return new SpreadElement(span, sourceSpan, expression);
|
|
}
|
|
expectTemplateBindingKey() {
|
|
let result = '';
|
|
let operatorFound = false;
|
|
const start = this.currentAbsoluteOffset;
|
|
do {
|
|
result += this.expectIdentifierOrKeywordOrString();
|
|
operatorFound = this.consumeOptionalOperator('-');
|
|
if (operatorFound) {
|
|
result += '-';
|
|
}
|
|
} while (operatorFound);
|
|
return {
|
|
source: result,
|
|
span: new AbsoluteSourceSpan(start, start + result.length)
|
|
};
|
|
}
|
|
parseTemplateBindings(templateKey) {
|
|
const bindings = [];
|
|
bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
|
|
while (this.index < this.tokens.length) {
|
|
const letBinding = this.parseLetBinding();
|
|
if (letBinding) {
|
|
bindings.push(letBinding);
|
|
} else {
|
|
const key = this.expectTemplateBindingKey();
|
|
const binding = this.parseAsBinding(key);
|
|
if (binding) {
|
|
bindings.push(binding);
|
|
} else {
|
|
key.source = templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
|
|
bindings.push(...this.parseDirectiveKeywordBindings(key));
|
|
}
|
|
}
|
|
this.consumeStatementTerminator();
|
|
}
|
|
return new TemplateBindingParseResult(bindings, [], this.errors);
|
|
}
|
|
parseKeyedReadOrWrite(receiver, start, isSafe) {
|
|
return this.withContext(ParseContextFlags.Writable, () => {
|
|
this.rbracketsExpected++;
|
|
const key = this.parsePipe();
|
|
if (key instanceof EmptyExpr$1) {
|
|
this.error(`Key access cannot be empty`);
|
|
}
|
|
this.rbracketsExpected--;
|
|
this.expectCharacter($RBRACKET);
|
|
if (this.isAssignmentOperator(this.next)) {
|
|
const operation = this.next.strValue;
|
|
if (isSafe) {
|
|
this.advance();
|
|
this.error("The '?.' operator cannot be used in the assignment");
|
|
} else {
|
|
const binaryReceiver = new KeyedRead(this.span(start), this.sourceSpan(start), receiver, key);
|
|
this.advance();
|
|
const value = this.parseConditional();
|
|
return new Binary(this.span(start), this.sourceSpan(start), operation, binaryReceiver, value);
|
|
}
|
|
} else {
|
|
return isSafe ? new SafeKeyedRead(this.span(start), this.sourceSpan(start), receiver, key) : new KeyedRead(this.span(start), this.sourceSpan(start), receiver, key);
|
|
}
|
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
});
|
|
}
|
|
parseDirectiveKeywordBindings(key) {
|
|
const bindings = [];
|
|
this.consumeOptionalCharacter($COLON);
|
|
const value = this.getDirectiveBoundTarget();
|
|
let spanEnd = this.currentAbsoluteOffset;
|
|
const asBinding = this.parseAsBinding(key);
|
|
if (!asBinding) {
|
|
this.consumeStatementTerminator();
|
|
spanEnd = this.currentAbsoluteOffset;
|
|
}
|
|
const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);
|
|
bindings.push(new ExpressionBinding(sourceSpan, key, value));
|
|
if (asBinding) {
|
|
bindings.push(asBinding);
|
|
}
|
|
return bindings;
|
|
}
|
|
getDirectiveBoundTarget() {
|
|
if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
|
|
return null;
|
|
}
|
|
const ast = this.parsePipe();
|
|
const {
|
|
start,
|
|
end
|
|
} = ast.span;
|
|
const value = this.input.substring(start, end);
|
|
return new ASTWithSource(ast, value, getLocation(this.parseSourceSpan), this.absoluteOffset + start, this.errors);
|
|
}
|
|
parseAsBinding(value) {
|
|
if (!this.peekKeywordAs()) {
|
|
return null;
|
|
}
|
|
this.advance();
|
|
const key = this.expectTemplateBindingKey();
|
|
this.consumeStatementTerminator();
|
|
const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);
|
|
return new VariableBinding(sourceSpan, key, value);
|
|
}
|
|
parseLetBinding() {
|
|
if (!this.peekKeywordLet()) {
|
|
return null;
|
|
}
|
|
const spanStart = this.currentAbsoluteOffset;
|
|
this.advance();
|
|
const key = this.expectTemplateBindingKey();
|
|
let value = null;
|
|
if (this.consumeOptionalOperator('=')) {
|
|
value = this.expectTemplateBindingKey();
|
|
}
|
|
this.consumeStatementTerminator();
|
|
const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);
|
|
return new VariableBinding(sourceSpan, key, value);
|
|
}
|
|
parseNoInterpolationTaggedTemplateLiteral(tag, start) {
|
|
const template = this.parseNoInterpolationTemplateLiteral();
|
|
return new TaggedTemplateLiteral(this.span(start), this.sourceSpan(start), tag, template);
|
|
}
|
|
parseNoInterpolationTemplateLiteral() {
|
|
const text = this.next.strValue;
|
|
const start = this.inputIndex;
|
|
this.advance();
|
|
const span = this.span(start);
|
|
const sourceSpan = this.sourceSpan(start);
|
|
return new TemplateLiteral(span, sourceSpan, [new TemplateLiteralElement(span, sourceSpan, text)], []);
|
|
}
|
|
parseTaggedTemplateLiteral(tag, start) {
|
|
const template = this.parseTemplateLiteral();
|
|
return new TaggedTemplateLiteral(this.span(start), this.sourceSpan(start), tag, template);
|
|
}
|
|
parseTemplateLiteral() {
|
|
const elements = [];
|
|
const expressions = [];
|
|
const start = this.inputIndex;
|
|
while (this.next !== EOF) {
|
|
const token = this.next;
|
|
if (token.isTemplateLiteralPart() || token.isTemplateLiteralEnd()) {
|
|
const partStart = this.inputIndex;
|
|
this.advance();
|
|
elements.push(new TemplateLiteralElement(this.span(partStart), this.sourceSpan(partStart), token.strValue));
|
|
if (token.isTemplateLiteralEnd()) {
|
|
break;
|
|
}
|
|
} else if (token.isTemplateLiteralInterpolationStart()) {
|
|
this.advance();
|
|
this.rbracesExpected++;
|
|
const expression = this.parsePipe();
|
|
if (expression instanceof EmptyExpr$1) {
|
|
this.error('Template literal interpolation cannot be empty');
|
|
} else {
|
|
expressions.push(expression);
|
|
}
|
|
this.rbracesExpected--;
|
|
} else {
|
|
this.advance();
|
|
}
|
|
}
|
|
return new TemplateLiteral(this.span(start), this.sourceSpan(start), elements, expressions);
|
|
}
|
|
parseRegularExpressionLiteral() {
|
|
const bodyToken = this.next;
|
|
this.advance();
|
|
if (!bodyToken.isRegExpBody()) {
|
|
return new EmptyExpr$1(this.span(this.inputIndex), this.sourceSpan(this.inputIndex));
|
|
}
|
|
let flagsToken = null;
|
|
if (this.next.isRegExpFlags()) {
|
|
flagsToken = this.next;
|
|
this.advance();
|
|
const seenFlags = new Set();
|
|
for (let i = 0; i < flagsToken.strValue.length; i++) {
|
|
const char = flagsToken.strValue[i];
|
|
if (!SUPPORTED_REGEX_FLAGS.has(char)) {
|
|
this.error(`Unsupported regular expression flag "${char}". The supported flags are: ` + Array.from(SUPPORTED_REGEX_FLAGS, f => `"${f}"`).join(', '), flagsToken.index + i);
|
|
} else if (seenFlags.has(char)) {
|
|
this.error(`Duplicate regular expression flag "${char}"`, flagsToken.index + i);
|
|
} else {
|
|
seenFlags.add(char);
|
|
}
|
|
}
|
|
}
|
|
const start = bodyToken.index;
|
|
const end = flagsToken ? flagsToken.end : bodyToken.end;
|
|
return new RegularExpressionLiteral(this.span(start, end), this.sourceSpan(start, end), bodyToken.strValue, flagsToken ? flagsToken.strValue : null);
|
|
}
|
|
consumeStatementTerminator() {
|
|
this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
|
|
}
|
|
error(message, index = this.index) {
|
|
this.errors.push(getParseError(message, this.input, this.getErrorLocationText(index), this.parseSourceSpan));
|
|
this.skip();
|
|
}
|
|
getErrorLocationText(index) {
|
|
return index < this.tokens.length ? `at column ${this.tokens[index].index + 1} in` : `at the end of the expression`;
|
|
}
|
|
_reportErrorForPrivateIdentifier(token, extraMessage) {
|
|
let errorMessage = `Private identifiers are not supported. Unexpected private identifier: ${token}`;
|
|
if (extraMessage !== null) {
|
|
errorMessage += `, ${extraMessage}`;
|
|
}
|
|
this.error(errorMessage);
|
|
}
|
|
skip() {
|
|
let n = this.next;
|
|
while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) && !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) && (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) && (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) && (!(this.context & ParseContextFlags.Writable) || !this.isAssignmentOperator(n))) {
|
|
if (this.next.isError()) {
|
|
this.errors.push(getParseError(this.next.toString(), this.input, this.getErrorLocationText(this.next.index), this.parseSourceSpan));
|
|
}
|
|
this.advance();
|
|
n = this.next;
|
|
}
|
|
}
|
|
}
|
|
function getParseError(message, input, locationText, parseSourceSpan) {
|
|
if (locationText.length > 0) {
|
|
locationText = ` ${locationText} `;
|
|
}
|
|
const location = getLocation(parseSourceSpan);
|
|
const error = `Parser Error: ${message}${locationText}[${input}] in ${location}`;
|
|
return new ParseError(parseSourceSpan, error);
|
|
}
|
|
class SimpleExpressionChecker extends RecursiveAstVisitor {
|
|
errors = [];
|
|
visitPipe() {
|
|
this.errors.push('pipes');
|
|
}
|
|
}
|
|
function getIndexMapForOriginalTemplate(interpolatedTokens) {
|
|
let offsetMap = new Map();
|
|
let consumedInOriginalTemplate = 0;
|
|
let consumedInInput = 0;
|
|
let tokenIndex = 0;
|
|
while (tokenIndex < interpolatedTokens.length) {
|
|
const currentToken = interpolatedTokens[tokenIndex];
|
|
if (currentToken.type === 9) {
|
|
const [decoded, encoded] = currentToken.parts;
|
|
consumedInOriginalTemplate += encoded.length;
|
|
consumedInInput += decoded.length;
|
|
} else {
|
|
const lengthOfParts = currentToken.parts.reduce((sum, current) => sum + current.length, 0);
|
|
consumedInInput += lengthOfParts;
|
|
consumedInOriginalTemplate += lengthOfParts;
|
|
}
|
|
offsetMap.set(consumedInInput, consumedInOriginalTemplate);
|
|
tokenIndex++;
|
|
}
|
|
return offsetMap;
|
|
}
|
|
|
|
function serialize(expression) {
|
|
return expression.visit(new SerializeExpressionVisitor());
|
|
}
|
|
class SerializeExpressionVisitor {
|
|
visitUnary(ast, context) {
|
|
return `${ast.operator}${ast.expr.visit(this, context)}`;
|
|
}
|
|
visitBinary(ast, context) {
|
|
return `${ast.left.visit(this, context)} ${ast.operation} ${ast.right.visit(this, context)}`;
|
|
}
|
|
visitChain(ast, context) {
|
|
return ast.expressions.map(e => e.visit(this, context)).join('; ');
|
|
}
|
|
visitConditional(ast, context) {
|
|
return `${ast.condition.visit(this, context)} ? ${ast.trueExp.visit(this, context)} : ${ast.falseExp.visit(this, context)}`;
|
|
}
|
|
visitThisReceiver() {
|
|
return 'this';
|
|
}
|
|
visitImplicitReceiver() {
|
|
return '';
|
|
}
|
|
visitInterpolation(ast, context) {
|
|
return interleave(ast.strings, ast.expressions.map(e => e.visit(this, context))).join('');
|
|
}
|
|
visitKeyedRead(ast, context) {
|
|
return `${ast.receiver.visit(this, context)}[${ast.key.visit(this, context)}]`;
|
|
}
|
|
visitLiteralArray(ast, context) {
|
|
return `[${ast.expressions.map(e => e.visit(this, context)).join(', ')}]`;
|
|
}
|
|
visitLiteralMap(ast, context) {
|
|
return `{${zip(ast.keys.map(literal => {
|
|
if (literal.kind === 'spread') {
|
|
return '...';
|
|
}
|
|
return literal.quoted ? `'${literal.key}'` : literal.key;
|
|
}), ast.values.map(value => value.visit(this, context))).map(([key, value]) => `${key}: ${value}`).join(', ')}}`;
|
|
}
|
|
visitLiteralPrimitive(ast) {
|
|
if (ast.value === null) return 'null';
|
|
switch (typeof ast.value) {
|
|
case 'number':
|
|
case 'boolean':
|
|
return ast.value.toString();
|
|
case 'undefined':
|
|
return 'undefined';
|
|
case 'string':
|
|
return `'${ast.value.replace(/'/g, `\\'`)}'`;
|
|
default:
|
|
throw new Error(`Unsupported primitive type: ${ast.value}`);
|
|
}
|
|
}
|
|
visitPipe(ast, context) {
|
|
return `${ast.exp.visit(this, context)} | ${ast.name}`;
|
|
}
|
|
visitPrefixNot(ast, context) {
|
|
return `!${ast.expression.visit(this, context)}`;
|
|
}
|
|
visitNonNullAssert(ast, context) {
|
|
return `${ast.expression.visit(this, context)}!`;
|
|
}
|
|
visitPropertyRead(ast, context) {
|
|
if (ast.receiver instanceof ImplicitReceiver || ast.receiver instanceof ThisReceiver) {
|
|
return ast.name;
|
|
} else {
|
|
return `${ast.receiver.visit(this, context)}.${ast.name}`;
|
|
}
|
|
}
|
|
visitSafePropertyRead(ast, context) {
|
|
return `${ast.receiver.visit(this, context)}?.${ast.name}`;
|
|
}
|
|
visitSafeKeyedRead(ast, context) {
|
|
return `${ast.receiver.visit(this, context)}?.[${ast.key.visit(this, context)}]`;
|
|
}
|
|
visitCall(ast, context) {
|
|
return `${ast.receiver.visit(this, context)}(${ast.args.map(e => e.visit(this, context)).join(', ')})`;
|
|
}
|
|
visitSafeCall(ast, context) {
|
|
return `${ast.receiver.visit(this, context)}?.(${ast.args.map(e => e.visit(this, context)).join(', ')})`;
|
|
}
|
|
visitTypeofExpression(ast, context) {
|
|
return `typeof ${ast.expression.visit(this, context)}`;
|
|
}
|
|
visitVoidExpression(ast, context) {
|
|
return `void ${ast.expression.visit(this, context)}`;
|
|
}
|
|
visitRegularExpressionLiteral(ast, context) {
|
|
return `/${ast.body}/${ast.flags || ''}`;
|
|
}
|
|
visitASTWithSource(ast, context) {
|
|
return ast.ast.visit(this, context);
|
|
}
|
|
visitTemplateLiteral(ast, context) {
|
|
let result = '';
|
|
for (let i = 0; i < ast.elements.length; i++) {
|
|
result += ast.elements[i].visit(this, context);
|
|
const expression = i < ast.expressions.length ? ast.expressions[i] : null;
|
|
if (expression !== null) {
|
|
result += '${' + expression.visit(this, context) + '}';
|
|
}
|
|
}
|
|
return '`' + result + '`';
|
|
}
|
|
visitTemplateLiteralElement(ast, context) {
|
|
return ast.text;
|
|
}
|
|
visitTaggedTemplateLiteral(ast, context) {
|
|
return ast.tag.visit(this, context) + ast.template.visit(this, context);
|
|
}
|
|
visitSpreadElement(ast, context) {
|
|
return `...${ast.expression.visit(this, context)}`;
|
|
}
|
|
visitParenthesizedExpression(ast, context) {
|
|
return '(' + ast.expression.visit(this, context) + ')';
|
|
}
|
|
}
|
|
function zip(left, right) {
|
|
if (left.length !== right.length) throw new Error('Array lengths must match');
|
|
return left.map((l, i) => [l, right[i]]);
|
|
}
|
|
function interleave(left, right) {
|
|
const result = [];
|
|
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
if (index < left.length) result.push(left[index]);
|
|
if (index < right.length) result.push(right[index]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
let _SECURITY_SCHEMA;
|
|
function SECURITY_SCHEMA() {
|
|
if (!_SECURITY_SCHEMA) {
|
|
_SECURITY_SCHEMA = {};
|
|
registerContext(SecurityContext.HTML, ['iframe|srcdoc', '*|innerHTML', '*|outerHTML']);
|
|
registerContext(SecurityContext.STYLE, ['*|style']);
|
|
registerContext(SecurityContext.URL, ['*|formAction', 'area|href', 'a|href', 'a|xlink:href', 'form|action', 'annotation|href', 'annotation|xlink:href', 'annotation-xml|href', 'annotation-xml|xlink:href', 'maction|href', 'maction|xlink:href', 'malignmark|href', 'malignmark|xlink:href', 'math|href', 'math|xlink:href', 'mroot|href', 'mroot|xlink:href', 'msqrt|href', 'msqrt|xlink:href', 'merror|href', 'merror|xlink:href', 'mfrac|href', 'mfrac|xlink:href', 'mglyph|href', 'mglyph|xlink:href', 'msub|href', 'msub|xlink:href', 'msup|href', 'msup|xlink:href', 'msubsup|href', 'msubsup|xlink:href', 'mmultiscripts|href', 'mmultiscripts|xlink:href', 'mprescripts|href', 'mprescripts|xlink:href', 'mi|href', 'mi|xlink:href', 'mn|href', 'mn|xlink:href', 'mo|href', 'mo|xlink:href', 'mpadded|href', 'mpadded|xlink:href', 'mphantom|href', 'mphantom|xlink:href', 'mrow|href', 'mrow|xlink:href', 'ms|href', 'ms|xlink:href', 'mspace|href', 'mspace|xlink:href', 'mstyle|href', 'mstyle|xlink:href', 'mtable|href', 'mtable|xlink:href', 'mtd|href', 'mtd|xlink:href', 'mtr|href', 'mtr|xlink:href', 'mtext|href', 'mtext|xlink:href', 'mover|href', 'mover|xlink:href', 'munder|href', 'munder|xlink:href', 'munderover|href', 'munderover|xlink:href', 'semantics|href', 'semantics|xlink:href', 'none|href', 'none|xlink:href', 'img|src', 'video|src']);
|
|
registerContext(SecurityContext.RESOURCE_URL, ['base|href', 'embed|src', 'frame|src', 'iframe|src', 'link|href', 'object|codebase', 'object|data', 'script|src', 'script|href', 'script|xlink:href']);
|
|
registerContext(SecurityContext.ATTRIBUTE_NO_BINDING, ['animate|attributeName', 'set|attributeName', 'animateMotion|attributeName', 'animateTransform|attributeName', 'unknown|attributeName', 'iframe|sandbox', 'iframe|allow', 'iframe|allowFullscreen', 'iframe|referrerPolicy', 'iframe|csp', 'iframe|fetchPriority', 'unknown|sandbox', 'unknown|allow', 'unknown|allowFullscreen', 'unknown|referrerPolicy', 'unknown|csp', 'unknown|fetchPriority']);
|
|
}
|
|
return _SECURITY_SCHEMA;
|
|
}
|
|
function registerContext(ctx, specs) {
|
|
for (const spec of specs) _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
|
|
}
|
|
|
|
class ElementSchemaRegistry {}
|
|
|
|
const BOOLEAN = 'boolean';
|
|
const NUMBER = 'number';
|
|
const STRING = 'string';
|
|
const OBJECT = 'object';
|
|
const SCHEMA = ['[Element]|textContent,%ariaActiveDescendantElement,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColIndexText,%ariaColSpan,%ariaControlsElements,%ariaCurrent,%ariaDescribedByElements,%ariaDescription,%ariaDetailsElements,%ariaDisabled,%ariaErrorMessageElements,%ariaExpanded,%ariaFlowToElements,%ariaHasPopup,%ariaHidden,%ariaInvalid,%ariaKeyShortcuts,%ariaLabel,%ariaLabelledByElements,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaOwnsElements,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%ariaRowIndexText,%ariaRowSpan,%ariaSelected,%ariaSetSize,%ariaSort,%ariaValueMax,%ariaValueMin,%ariaValueNow,%ariaValueText,%classList,className,elementTiming,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*fullscreenchange,*fullscreenerror,*search,*webkitfullscreenchange,*webkitfullscreenerror,outerHTML,%part,#scrollLeft,#scrollTop,slot' + ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored', '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,!inert,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,search,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume', ':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex', ':svg:graphics^:svg:|', ':svg:animation^:svg:|*begin,*end,*repeat', ':svg:geometry^:svg:|', ':svg:componentTransferFunction^:svg:|', ':svg:gradient^:svg:|', ':svg:textContent^:svg:graphics|', ':svg:textPositioning^:svg:textContent|', 'a^[HTMLElement]|charset,coords,download,hash,host,hostname,href,hreflang,name,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,rev,search,shape,target,text,type,username', 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,search,shape,target,username', 'audio^media|', 'br^[HTMLElement]|clear', 'base^[HTMLElement]|href,target', 'body^[HTMLElement]|aLink,background,bgColor,link,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink', 'button^[HTMLElement]|!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value', 'canvas^[HTMLElement]|#height,#width', 'content^[HTMLElement]|select', 'dl^[HTMLElement]|!compact', 'data^[HTMLElement]|value', 'datalist^[HTMLElement]|', 'details^[HTMLElement]|!open', 'dialog^[HTMLElement]|!open,returnValue', 'dir^[HTMLElement]|!compact', 'div^[HTMLElement]|align', 'embed^[HTMLElement]|align,height,name,src,type,width', 'fieldset^[HTMLElement]|!disabled,name', 'font^[HTMLElement]|color,face,size', 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target', 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src', 'frameset^[HTMLElement]|cols,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows', 'hr^[HTMLElement]|align,color,!noShade,size,width', 'head^[HTMLElement]|', 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align', 'html^[HTMLElement]|version', 'iframe^[HTMLElement]|align,allow,!allowFullscreen,!allowPaymentRequest,csp,frameBorder,height,loading,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width', 'img^[HTMLElement]|align,alt,border,%crossOrigin,decoding,#height,#hspace,!isMap,loading,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width', 'input^[HTMLElement]|accept,align,alt,autocomplete,!checked,!defaultChecked,defaultValue,dirName,!disabled,%files,formAction,formEnctype,formMethod,!formNoValidate,formTarget,#height,!incremental,!indeterminate,max,#maxLength,min,#minLength,!multiple,name,pattern,placeholder,!readOnly,!required,selectionDirection,#selectionEnd,#selectionStart,#size,src,step,type,useMap,value,%valueAsDate,#valueAsNumber,#width', 'li^[HTMLElement]|type,#value', 'label^[HTMLElement]|htmlFor', 'legend^[HTMLElement]|align', 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,imageSizes,imageSrcset,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type', 'map^[HTMLElement]|name', 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width', 'menu^[HTMLElement]|!compact', 'meta^[HTMLElement]|content,httpEquiv,media,name,scheme', 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value', 'ins,del^[HTMLElement]|cite,dateTime', 'ol^[HTMLElement]|!compact,!reversed,#start,type', 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width', 'optgroup^[HTMLElement]|!disabled,label', 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value', 'output^[HTMLElement]|defaultValue,%htmlFor,name,value', 'p^[HTMLElement]|align', 'param^[HTMLElement]|name,type,value,valueType', 'picture^[HTMLElement]|', 'pre^[HTMLElement]|#width', 'progress^[HTMLElement]|#max,#value', 'q,blockquote,cite^[HTMLElement]|', 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,!noModule,%referrerPolicy,src,text,type', 'select^[HTMLElement]|autocomplete,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value', 'selectedcontent^[HTMLElement]|', 'slot^[HTMLElement]|name', 'source^[HTMLElement]|#height,media,sizes,src,srcset,type,#width', 'span^[HTMLElement]|', 'style^[HTMLElement]|!disabled,media,type', 'search^[HTMLELement]|', 'caption^[HTMLElement]|align', 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width', 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width', 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width', 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign', 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign', 'template^[HTMLElement]|', 'textarea^[HTMLElement]|autocomplete,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', 'time^[HTMLElement]|dateTime', 'title^[HTMLElement]|text', 'track^[HTMLElement]|!default,kind,label,src,srclang', 'ul^[HTMLElement]|!compact,type', 'unknown^[HTMLElement]|', 'video^media|!disablePictureInPicture,#height,*enterpictureinpicture,*leavepictureinpicture,!playsInline,poster,#width', ':svg:a^:svg:graphics|', ':svg:animate^:svg:animation|', ':svg:animateMotion^:svg:animation|', ':svg:animateTransform^:svg:animation|', ':svg:circle^:svg:geometry|', ':svg:clipPath^:svg:graphics|', ':svg:defs^:svg:graphics|', ':svg:desc^:svg:|', ':svg:discard^:svg:|', ':svg:ellipse^:svg:geometry|', ':svg:feBlend^:svg:|', ':svg:feColorMatrix^:svg:|', ':svg:feComponentTransfer^:svg:|', ':svg:feComposite^:svg:|', ':svg:feConvolveMatrix^:svg:|', ':svg:feDiffuseLighting^:svg:|', ':svg:feDisplacementMap^:svg:|', ':svg:feDistantLight^:svg:|', ':svg:feDropShadow^:svg:|', ':svg:feFlood^:svg:|', ':svg:feFuncA^:svg:componentTransferFunction|', ':svg:feFuncB^:svg:componentTransferFunction|', ':svg:feFuncG^:svg:componentTransferFunction|', ':svg:feFuncR^:svg:componentTransferFunction|', ':svg:feGaussianBlur^:svg:|', ':svg:feImage^:svg:|', ':svg:feMerge^:svg:|', ':svg:feMergeNode^:svg:|', ':svg:feMorphology^:svg:|', ':svg:feOffset^:svg:|', ':svg:fePointLight^:svg:|', ':svg:feSpecularLighting^:svg:|', ':svg:feSpotLight^:svg:|', ':svg:feTile^:svg:|', ':svg:feTurbulence^:svg:|', ':svg:filter^:svg:|', ':svg:foreignObject^:svg:graphics|', ':svg:g^:svg:graphics|', ':svg:image^:svg:graphics|decoding', ':svg:line^:svg:geometry|', ':svg:linearGradient^:svg:gradient|', ':svg:mpath^:svg:|', ':svg:marker^:svg:|', ':svg:mask^:svg:|', ':svg:metadata^:svg:|', ':svg:path^:svg:geometry|', ':svg:pattern^:svg:|', ':svg:polygon^:svg:geometry|', ':svg:polyline^:svg:geometry|', ':svg:radialGradient^:svg:gradient|', ':svg:rect^:svg:geometry|', ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan', ':svg:script^:svg:|type', ':svg:set^:svg:animation|', ':svg:stop^:svg:|', ':svg:style^:svg:|!disabled,media,title,type', ':svg:switch^:svg:graphics|', ':svg:symbol^:svg:|', ':svg:tspan^:svg:textPositioning|', ':svg:text^:svg:textPositioning|', ':svg:textPath^:svg:textContent|', ':svg:title^:svg:|', ':svg:use^:svg:graphics|', ':svg:view^:svg:|#zoomAndPan', 'data^[HTMLElement]|value', 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name', 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default', 'summary^[HTMLElement]|', 'time^[HTMLElement]|dateTime', ':svg:cursor^:svg:|', ':math:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforeinput,*beforematch,*beforetoggle,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contentvisibilityautostatechange,*contextlost,*contextmenu,*contextrestored,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*scrollend,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex', ':math:math^:math:|', ':math:maction^:math:|', ':math:menclose^:math:|', ':math:merror^:math:|', ':math:mfenced^:math:|', ':math:mfrac^:math:|', ':math:mi^:math:|', ':math:mmultiscripts^:math:|', ':math:mn^:math:|', ':math:mo^:math:|', ':math:mover^:math:|', ':math:mpadded^:math:|', ':math:mphantom^:math:|', ':math:mroot^:math:|', ':math:mrow^:math:|', ':math:ms^:math:|', ':math:mspace^:math:|', ':math:msqrt^:math:|', ':math:mstyle^:math:|', ':math:msub^:math:|', ':math:msubsup^:math:|', ':math:msup^:math:|', ':math:mtable^:math:|', ':math:mtd^:math:|', ':math:mtext^:math:|', ':math:mtr^:math:|', ':math:munder^:math:|', ':math:munderover^:math:|', ':math:semantics^:math:|'];
|
|
const _ATTR_TO_PROP = new Map(Object.entries({
|
|
'class': 'className',
|
|
'for': 'htmlFor',
|
|
'formaction': 'formAction',
|
|
'innerHtml': 'innerHTML',
|
|
'readonly': 'readOnly',
|
|
'tabindex': 'tabIndex',
|
|
'aria-activedescendant': 'ariaActiveDescendantElement',
|
|
'aria-atomic': 'ariaAtomic',
|
|
'aria-autocomplete': 'ariaAutoComplete',
|
|
'aria-busy': 'ariaBusy',
|
|
'aria-checked': 'ariaChecked',
|
|
'aria-colcount': 'ariaColCount',
|
|
'aria-colindex': 'ariaColIndex',
|
|
'aria-colindextext': 'ariaColIndexText',
|
|
'aria-colspan': 'ariaColSpan',
|
|
'aria-controls': 'ariaControlsElements',
|
|
'aria-current': 'ariaCurrent',
|
|
'aria-describedby': 'ariaDescribedByElements',
|
|
'aria-description': 'ariaDescription',
|
|
'aria-details': 'ariaDetailsElements',
|
|
'aria-disabled': 'ariaDisabled',
|
|
'aria-errormessage': 'ariaErrorMessageElements',
|
|
'aria-expanded': 'ariaExpanded',
|
|
'aria-flowto': 'ariaFlowToElements',
|
|
'aria-haspopup': 'ariaHasPopup',
|
|
'aria-hidden': 'ariaHidden',
|
|
'aria-invalid': 'ariaInvalid',
|
|
'aria-keyshortcuts': 'ariaKeyShortcuts',
|
|
'aria-label': 'ariaLabel',
|
|
'aria-labelledby': 'ariaLabelledByElements',
|
|
'aria-level': 'ariaLevel',
|
|
'aria-live': 'ariaLive',
|
|
'aria-modal': 'ariaModal',
|
|
'aria-multiline': 'ariaMultiLine',
|
|
'aria-multiselectable': 'ariaMultiSelectable',
|
|
'aria-orientation': 'ariaOrientation',
|
|
'aria-owns': 'ariaOwnsElements',
|
|
'aria-placeholder': 'ariaPlaceholder',
|
|
'aria-posinset': 'ariaPosInSet',
|
|
'aria-pressed': 'ariaPressed',
|
|
'aria-readonly': 'ariaReadOnly',
|
|
'aria-required': 'ariaRequired',
|
|
'aria-roledescription': 'ariaRoleDescription',
|
|
'aria-rowcount': 'ariaRowCount',
|
|
'aria-rowindex': 'ariaRowIndex',
|
|
'aria-rowindextext': 'ariaRowIndexText',
|
|
'aria-rowspan': 'ariaRowSpan',
|
|
'aria-selected': 'ariaSelected',
|
|
'aria-setsize': 'ariaSetSize',
|
|
'aria-sort': 'ariaSort',
|
|
'aria-valuemax': 'ariaValueMax',
|
|
'aria-valuemin': 'ariaValueMin',
|
|
'aria-valuenow': 'ariaValueNow',
|
|
'aria-valuetext': 'ariaValueText'
|
|
}));
|
|
const _PROP_TO_ATTR = Array.from(_ATTR_TO_PROP).reduce((inverted, [propertyName, attributeName]) => {
|
|
inverted.set(propertyName, attributeName);
|
|
return inverted;
|
|
}, new Map());
|
|
class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
|
_schema = new Map();
|
|
_eventSchema = new Map();
|
|
constructor() {
|
|
super();
|
|
SCHEMA.forEach(encodedType => {
|
|
const type = new Map();
|
|
const events = new Set();
|
|
const [strType, strProperties] = encodedType.split('|');
|
|
const properties = strProperties.split(',');
|
|
const [typeNames, superName] = strType.split('^');
|
|
typeNames.split(',').forEach(tag => {
|
|
this._schema.set(tag.toLowerCase(), type);
|
|
this._eventSchema.set(tag.toLowerCase(), events);
|
|
});
|
|
const superType = superName && this._schema.get(superName.toLowerCase());
|
|
if (superType) {
|
|
for (const [prop, value] of superType) {
|
|
type.set(prop, value);
|
|
}
|
|
for (const superEvent of this._eventSchema.get(superName.toLowerCase())) {
|
|
events.add(superEvent);
|
|
}
|
|
}
|
|
properties.forEach(property => {
|
|
if (property.length > 0) {
|
|
switch (property[0]) {
|
|
case '*':
|
|
events.add(property.substring(1));
|
|
break;
|
|
case '!':
|
|
type.set(property.substring(1), BOOLEAN);
|
|
break;
|
|
case '#':
|
|
type.set(property.substring(1), NUMBER);
|
|
break;
|
|
case '%':
|
|
type.set(property.substring(1), OBJECT);
|
|
break;
|
|
default:
|
|
type.set(property, STRING);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
hasProperty(tagName, propName, schemaMetas) {
|
|
if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) {
|
|
return true;
|
|
}
|
|
if (tagName.indexOf('-') > -1) {
|
|
if (isNgContainer(tagName) || isNgContent(tagName)) {
|
|
return false;
|
|
}
|
|
if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
|
return true;
|
|
}
|
|
}
|
|
const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown');
|
|
return elementProperties.has(propName);
|
|
}
|
|
hasElement(tagName, schemaMetas) {
|
|
if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) {
|
|
return true;
|
|
}
|
|
if (tagName.indexOf('-') > -1) {
|
|
if (isNgContainer(tagName) || isNgContent(tagName)) {
|
|
return true;
|
|
}
|
|
if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return this._schema.has(tagName.toLowerCase());
|
|
}
|
|
securityContext(tagName, propName, isAttribute) {
|
|
if (isAttribute) {
|
|
propName = this.getMappedPropName(propName);
|
|
}
|
|
tagName = tagName.toLowerCase();
|
|
propName = propName.toLowerCase();
|
|
let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
|
|
if (ctx) {
|
|
return ctx;
|
|
}
|
|
ctx = SECURITY_SCHEMA()['*|' + propName];
|
|
return ctx ? ctx : SecurityContext.NONE;
|
|
}
|
|
getMappedPropName(propName) {
|
|
return _ATTR_TO_PROP.get(propName) ?? propName;
|
|
}
|
|
getDefaultComponentElementName() {
|
|
return 'ng-component';
|
|
}
|
|
validateProperty(name) {
|
|
if (name.toLowerCase().startsWith('on')) {
|
|
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...` + `\nIf '${name}' is a directive input, make sure the directive is imported by the` + ` current module.`;
|
|
return {
|
|
error: true,
|
|
msg: msg
|
|
};
|
|
} else {
|
|
return {
|
|
error: false
|
|
};
|
|
}
|
|
}
|
|
validateAttribute(name) {
|
|
if (name.toLowerCase().startsWith('on')) {
|
|
const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...`;
|
|
return {
|
|
error: true,
|
|
msg: msg
|
|
};
|
|
} else {
|
|
return {
|
|
error: false
|
|
};
|
|
}
|
|
}
|
|
allKnownElementNames() {
|
|
return Array.from(this._schema.keys());
|
|
}
|
|
allKnownAttributesOfElement(tagName) {
|
|
const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown');
|
|
return Array.from(elementProperties.keys()).map(prop => _PROP_TO_ATTR.get(prop) ?? prop);
|
|
}
|
|
allKnownEventsOfElement(tagName) {
|
|
return Array.from(this._eventSchema.get(tagName.toLowerCase()) ?? []);
|
|
}
|
|
normalizeAnimationStyleProperty(propName) {
|
|
return dashCaseToCamelCase(propName);
|
|
}
|
|
normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
|
|
let unit = '';
|
|
const strVal = val.toString().trim();
|
|
let errorMsg = null;
|
|
if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
|
|
if (typeof val === 'number') {
|
|
unit = 'px';
|
|
} else {
|
|
const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
|
|
if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
|
|
errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
error: errorMsg,
|
|
value: strVal + unit
|
|
};
|
|
}
|
|
}
|
|
function _isPixelDimensionStyle(prop) {
|
|
switch (prop) {
|
|
case 'width':
|
|
case 'height':
|
|
case 'minWidth':
|
|
case 'minHeight':
|
|
case 'maxWidth':
|
|
case 'maxHeight':
|
|
case 'left':
|
|
case 'top':
|
|
case 'bottom':
|
|
case 'right':
|
|
case 'fontSize':
|
|
case 'outlineWidth':
|
|
case 'outlineOffset':
|
|
case 'paddingTop':
|
|
case 'paddingLeft':
|
|
case 'paddingBottom':
|
|
case 'paddingRight':
|
|
case 'marginTop':
|
|
case 'marginLeft':
|
|
case 'marginBottom':
|
|
case 'marginRight':
|
|
case 'borderRadius':
|
|
case 'borderWidth':
|
|
case 'borderTopWidth':
|
|
case 'borderLeftWidth':
|
|
case 'borderRightWidth':
|
|
case 'borderBottomWidth':
|
|
case 'textIndent':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class HtmlTagDefinition {
|
|
closedByChildren = {};
|
|
contentType;
|
|
closedByParent = false;
|
|
implicitNamespacePrefix;
|
|
isVoid;
|
|
ignoreFirstLf;
|
|
canSelfClose;
|
|
preventNamespaceInheritance;
|
|
constructor({
|
|
closedByChildren,
|
|
implicitNamespacePrefix,
|
|
contentType = TagContentType.PARSABLE_DATA,
|
|
closedByParent = false,
|
|
isVoid = false,
|
|
ignoreFirstLf = false,
|
|
preventNamespaceInheritance = false,
|
|
canSelfClose = false
|
|
} = {}) {
|
|
if (closedByChildren && closedByChildren.length > 0) {
|
|
closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
|
|
}
|
|
this.isVoid = isVoid;
|
|
this.closedByParent = closedByParent || isVoid;
|
|
this.implicitNamespacePrefix = implicitNamespacePrefix || null;
|
|
this.contentType = contentType;
|
|
this.ignoreFirstLf = ignoreFirstLf;
|
|
this.preventNamespaceInheritance = preventNamespaceInheritance;
|
|
this.canSelfClose = canSelfClose ?? isVoid;
|
|
}
|
|
isClosedByChild(name) {
|
|
return this.isVoid || name.toLowerCase() in this.closedByChildren;
|
|
}
|
|
getContentType(prefix) {
|
|
if (typeof this.contentType === 'object') {
|
|
const overrideType = prefix === undefined ? undefined : this.contentType[prefix];
|
|
return overrideType ?? this.contentType.default;
|
|
}
|
|
return this.contentType;
|
|
}
|
|
}
|
|
let DEFAULT_TAG_DEFINITION;
|
|
let TAG_DEFINITIONS;
|
|
function getHtmlTagDefinition(tagName) {
|
|
if (!TAG_DEFINITIONS) {
|
|
DEFAULT_TAG_DEFINITION = new HtmlTagDefinition({
|
|
canSelfClose: true
|
|
});
|
|
TAG_DEFINITIONS = Object.assign(Object.create(null), {
|
|
'base': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'meta': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'area': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'embed': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'link': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'img': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'input': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'param': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'hr': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'br': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'source': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'track': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'wbr': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'p': new HtmlTagDefinition({
|
|
closedByChildren: ['address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'],
|
|
closedByParent: true
|
|
}),
|
|
'thead': new HtmlTagDefinition({
|
|
closedByChildren: ['tbody', 'tfoot']
|
|
}),
|
|
'tbody': new HtmlTagDefinition({
|
|
closedByChildren: ['tbody', 'tfoot'],
|
|
closedByParent: true
|
|
}),
|
|
'tfoot': new HtmlTagDefinition({
|
|
closedByChildren: ['tbody'],
|
|
closedByParent: true
|
|
}),
|
|
'tr': new HtmlTagDefinition({
|
|
closedByChildren: ['tr'],
|
|
closedByParent: true
|
|
}),
|
|
'td': new HtmlTagDefinition({
|
|
closedByChildren: ['td', 'th'],
|
|
closedByParent: true
|
|
}),
|
|
'th': new HtmlTagDefinition({
|
|
closedByChildren: ['td', 'th'],
|
|
closedByParent: true
|
|
}),
|
|
'col': new HtmlTagDefinition({
|
|
isVoid: true
|
|
}),
|
|
'svg': new HtmlTagDefinition({
|
|
implicitNamespacePrefix: 'svg'
|
|
}),
|
|
'foreignObject': new HtmlTagDefinition({
|
|
implicitNamespacePrefix: 'svg',
|
|
preventNamespaceInheritance: true
|
|
}),
|
|
'math': new HtmlTagDefinition({
|
|
implicitNamespacePrefix: 'math'
|
|
}),
|
|
'li': new HtmlTagDefinition({
|
|
closedByChildren: ['li'],
|
|
closedByParent: true
|
|
}),
|
|
'dt': new HtmlTagDefinition({
|
|
closedByChildren: ['dt', 'dd']
|
|
}),
|
|
'dd': new HtmlTagDefinition({
|
|
closedByChildren: ['dt', 'dd'],
|
|
closedByParent: true
|
|
}),
|
|
'rb': new HtmlTagDefinition({
|
|
closedByChildren: ['rb', 'rt', 'rtc', 'rp'],
|
|
closedByParent: true
|
|
}),
|
|
'rt': new HtmlTagDefinition({
|
|
closedByChildren: ['rb', 'rt', 'rtc', 'rp'],
|
|
closedByParent: true
|
|
}),
|
|
'rtc': new HtmlTagDefinition({
|
|
closedByChildren: ['rb', 'rtc', 'rp'],
|
|
closedByParent: true
|
|
}),
|
|
'rp': new HtmlTagDefinition({
|
|
closedByChildren: ['rb', 'rt', 'rtc', 'rp'],
|
|
closedByParent: true
|
|
}),
|
|
'optgroup': new HtmlTagDefinition({
|
|
closedByChildren: ['optgroup'],
|
|
closedByParent: true
|
|
}),
|
|
'option': new HtmlTagDefinition({
|
|
closedByChildren: ['option', 'optgroup'],
|
|
closedByParent: true
|
|
}),
|
|
'pre': new HtmlTagDefinition({
|
|
ignoreFirstLf: true
|
|
}),
|
|
'listing': new HtmlTagDefinition({
|
|
ignoreFirstLf: true
|
|
}),
|
|
'style': new HtmlTagDefinition({
|
|
contentType: TagContentType.RAW_TEXT
|
|
}),
|
|
'script': new HtmlTagDefinition({
|
|
contentType: TagContentType.RAW_TEXT
|
|
}),
|
|
'title': new HtmlTagDefinition({
|
|
contentType: {
|
|
default: TagContentType.ESCAPABLE_RAW_TEXT,
|
|
svg: TagContentType.PARSABLE_DATA
|
|
}
|
|
}),
|
|
'textarea': new HtmlTagDefinition({
|
|
contentType: TagContentType.ESCAPABLE_RAW_TEXT,
|
|
ignoreFirstLf: true
|
|
})
|
|
});
|
|
new DomElementSchemaRegistry().allKnownElementNames().forEach(knownTagName => {
|
|
if (!TAG_DEFINITIONS[knownTagName] && getNsPrefix(knownTagName) === null) {
|
|
TAG_DEFINITIONS[knownTagName] = new HtmlTagDefinition({
|
|
canSelfClose: false
|
|
});
|
|
}
|
|
});
|
|
}
|
|
return TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ?? DEFAULT_TAG_DEFINITION;
|
|
}
|
|
|
|
const TAG_TO_PLACEHOLDER_NAMES = {
|
|
'A': 'LINK',
|
|
'B': 'BOLD_TEXT',
|
|
'BR': 'LINE_BREAK',
|
|
'EM': 'EMPHASISED_TEXT',
|
|
'H1': 'HEADING_LEVEL1',
|
|
'H2': 'HEADING_LEVEL2',
|
|
'H3': 'HEADING_LEVEL3',
|
|
'H4': 'HEADING_LEVEL4',
|
|
'H5': 'HEADING_LEVEL5',
|
|
'H6': 'HEADING_LEVEL6',
|
|
'HR': 'HORIZONTAL_RULE',
|
|
'I': 'ITALIC_TEXT',
|
|
'LI': 'LIST_ITEM',
|
|
'LINK': 'MEDIA_LINK',
|
|
'OL': 'ORDERED_LIST',
|
|
'P': 'PARAGRAPH',
|
|
'Q': 'QUOTATION',
|
|
'S': 'STRIKETHROUGH_TEXT',
|
|
'SMALL': 'SMALL_TEXT',
|
|
'SUB': 'SUBSTRIPT',
|
|
'SUP': 'SUPERSCRIPT',
|
|
'TBODY': 'TABLE_BODY',
|
|
'TD': 'TABLE_CELL',
|
|
'TFOOT': 'TABLE_FOOTER',
|
|
'TH': 'TABLE_HEADER_CELL',
|
|
'THEAD': 'TABLE_HEADER',
|
|
'TR': 'TABLE_ROW',
|
|
'TT': 'MONOSPACED_TEXT',
|
|
'U': 'UNDERLINED_TEXT',
|
|
'UL': 'UNORDERED_LIST'
|
|
};
|
|
class PlaceholderRegistry {
|
|
_placeHolderNameCounts = {};
|
|
_signatureToName = {};
|
|
getStartTagPlaceholderName(tag, attrs, isVoid) {
|
|
const signature = this._hashTag(tag, attrs, isVoid);
|
|
if (this._signatureToName[signature]) {
|
|
return this._signatureToName[signature];
|
|
}
|
|
const upperTag = tag.toUpperCase();
|
|
const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
|
const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
|
|
this._signatureToName[signature] = name;
|
|
return name;
|
|
}
|
|
getCloseTagPlaceholderName(tag) {
|
|
const signature = this._hashClosingTag(tag);
|
|
if (this._signatureToName[signature]) {
|
|
return this._signatureToName[signature];
|
|
}
|
|
const upperTag = tag.toUpperCase();
|
|
const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
|
const name = this._generateUniqueName(`CLOSE_${baseName}`);
|
|
this._signatureToName[signature] = name;
|
|
return name;
|
|
}
|
|
getPlaceholderName(name, content) {
|
|
const upperName = name.toUpperCase();
|
|
const signature = `PH: ${upperName}=${content}`;
|
|
if (this._signatureToName[signature]) {
|
|
return this._signatureToName[signature];
|
|
}
|
|
const uniqueName = this._generateUniqueName(upperName);
|
|
this._signatureToName[signature] = uniqueName;
|
|
return uniqueName;
|
|
}
|
|
getUniquePlaceholder(name) {
|
|
return this._generateUniqueName(name.toUpperCase());
|
|
}
|
|
getStartBlockPlaceholderName(name, parameters) {
|
|
const signature = this._hashBlock(name, parameters);
|
|
if (this._signatureToName[signature]) {
|
|
return this._signatureToName[signature];
|
|
}
|
|
const placeholder = this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(name)}`);
|
|
this._signatureToName[signature] = placeholder;
|
|
return placeholder;
|
|
}
|
|
getCloseBlockPlaceholderName(name) {
|
|
const signature = this._hashClosingBlock(name);
|
|
if (this._signatureToName[signature]) {
|
|
return this._signatureToName[signature];
|
|
}
|
|
const placeholder = this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(name)}`);
|
|
this._signatureToName[signature] = placeholder;
|
|
return placeholder;
|
|
}
|
|
_hashTag(tag, attrs, isVoid) {
|
|
const start = `<${tag}`;
|
|
const strAttrs = Object.keys(attrs).sort().map(name => ` ${name}=${attrs[name]}`).join('');
|
|
const end = isVoid ? '/>' : `></${tag}>`;
|
|
return start + strAttrs + end;
|
|
}
|
|
_hashClosingTag(tag) {
|
|
return this._hashTag(`/${tag}`, {}, false);
|
|
}
|
|
_hashBlock(name, parameters) {
|
|
const params = parameters.length === 0 ? '' : ` (${parameters.sort().join('; ')})`;
|
|
return `@${name}${params} {}`;
|
|
}
|
|
_hashClosingBlock(name) {
|
|
return this._hashBlock(`close_${name}`, []);
|
|
}
|
|
_toSnakeCase(name) {
|
|
return name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
}
|
|
_generateUniqueName(base) {
|
|
const seen = this._placeHolderNameCounts.hasOwnProperty(base);
|
|
if (!seen) {
|
|
this._placeHolderNameCounts[base] = 1;
|
|
return base;
|
|
}
|
|
const id = this._placeHolderNameCounts[base];
|
|
this._placeHolderNameCounts[base] = id + 1;
|
|
return `${base}_${id}`;
|
|
}
|
|
}
|
|
|
|
const _expParser = new Parser(new Lexer());
|
|
function createI18nMessageFactory(retainEmptyTokens, preserveExpressionWhitespace) {
|
|
const visitor = new _I18nVisitor(_expParser, retainEmptyTokens, preserveExpressionWhitespace);
|
|
return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
|
}
|
|
function noopVisitNodeFn(_html, i18n) {
|
|
return i18n;
|
|
}
|
|
class _I18nVisitor {
|
|
_expressionParser;
|
|
_retainEmptyTokens;
|
|
_preserveExpressionWhitespace;
|
|
constructor(_expressionParser, _retainEmptyTokens, _preserveExpressionWhitespace) {
|
|
this._expressionParser = _expressionParser;
|
|
this._retainEmptyTokens = _retainEmptyTokens;
|
|
this._preserveExpressionWhitespace = _preserveExpressionWhitespace;
|
|
}
|
|
toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
|
|
const context = {
|
|
isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
|
|
icuDepth: 0,
|
|
placeholderRegistry: new PlaceholderRegistry(),
|
|
placeholderToContent: {},
|
|
placeholderToMessage: {},
|
|
visitNodeFn: visitNodeFn || noopVisitNodeFn
|
|
};
|
|
const i18nodes = visitAll(this, nodes, context);
|
|
return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
|
|
}
|
|
visitElement(el, context) {
|
|
return this._visitElementLike(el, context);
|
|
}
|
|
visitComponent(component, context) {
|
|
return this._visitElementLike(component, context);
|
|
}
|
|
visitDirective(directive, context) {
|
|
throw new Error('Unreachable code');
|
|
}
|
|
visitAttribute(attribute, context) {
|
|
const node = attribute.valueTokens === undefined || attribute.valueTokens.length === 1 ? new Text$2(attribute.value, attribute.valueSpan || attribute.sourceSpan) : this._visitTextWithInterpolation(attribute.valueTokens, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
|
|
return context.visitNodeFn(attribute, node);
|
|
}
|
|
visitText(text, context) {
|
|
const node = text.tokens.length === 1 ? new Text$2(text.value, text.sourceSpan) : this._visitTextWithInterpolation(text.tokens, text.sourceSpan, context, text.i18n);
|
|
return context.visitNodeFn(text, node);
|
|
}
|
|
visitComment(comment, context) {
|
|
return null;
|
|
}
|
|
visitExpansion(icu, context) {
|
|
context.icuDepth++;
|
|
const i18nIcuCases = {};
|
|
const i18nIcu = new Icu(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
|
|
icu.cases.forEach(caze => {
|
|
i18nIcuCases[caze.value] = new Container(caze.expression.map(node => node.visit(this, context)), caze.expSourceSpan);
|
|
});
|
|
context.icuDepth--;
|
|
if (context.isIcu || context.icuDepth > 0) {
|
|
const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
|
|
i18nIcu.expressionPlaceholder = expPh;
|
|
context.placeholderToContent[expPh] = {
|
|
text: icu.switchValue,
|
|
sourceSpan: icu.switchValueSourceSpan
|
|
};
|
|
return context.visitNodeFn(icu, i18nIcu);
|
|
}
|
|
const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
|
context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
|
|
const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
|
|
return context.visitNodeFn(icu, node);
|
|
}
|
|
visitExpansionCase(_icuCase, _context) {
|
|
throw new Error('Unreachable code');
|
|
}
|
|
visitBlock(block, context) {
|
|
const children = visitAll(this, block.children, context);
|
|
if (block.name === 'switch') {
|
|
return new Container(children, block.sourceSpan);
|
|
}
|
|
const parameters = block.parameters.map(param => param.expression);
|
|
const startPhName = context.placeholderRegistry.getStartBlockPlaceholderName(block.name, parameters);
|
|
const closePhName = context.placeholderRegistry.getCloseBlockPlaceholderName(block.name);
|
|
context.placeholderToContent[startPhName] = {
|
|
text: block.startSourceSpan.toString(),
|
|
sourceSpan: block.startSourceSpan
|
|
};
|
|
context.placeholderToContent[closePhName] = {
|
|
text: block.endSourceSpan ? block.endSourceSpan.toString() : '}',
|
|
sourceSpan: block.endSourceSpan ?? block.sourceSpan
|
|
};
|
|
const node = new BlockPlaceholder(block.name, parameters, startPhName, closePhName, children, block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
|
|
return context.visitNodeFn(block, node);
|
|
}
|
|
visitBlockParameter(_parameter, _context) {
|
|
throw new Error('Unreachable code');
|
|
}
|
|
visitLetDeclaration(decl, context) {
|
|
return null;
|
|
}
|
|
_visitElementLike(node, context) {
|
|
const children = visitAll(this, node.children, context);
|
|
const attrs = {};
|
|
const visitAttribute = attr => {
|
|
attrs[attr.name] = attr.value;
|
|
};
|
|
let nodeName;
|
|
let isVoid;
|
|
if (node instanceof Element) {
|
|
nodeName = node.name;
|
|
isVoid = getHtmlTagDefinition(node.name).isVoid;
|
|
} else {
|
|
nodeName = node.fullName;
|
|
isVoid = node.tagName ? getHtmlTagDefinition(node.tagName).isVoid : false;
|
|
}
|
|
node.attrs.forEach(visitAttribute);
|
|
node.directives.forEach(dir => dir.attrs.forEach(visitAttribute));
|
|
const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(nodeName, attrs, isVoid);
|
|
context.placeholderToContent[startPhName] = {
|
|
text: node.startSourceSpan.toString(),
|
|
sourceSpan: node.startSourceSpan
|
|
};
|
|
let closePhName = '';
|
|
if (!isVoid) {
|
|
closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(nodeName);
|
|
context.placeholderToContent[closePhName] = {
|
|
text: `</${nodeName}>`,
|
|
sourceSpan: node.endSourceSpan ?? node.sourceSpan
|
|
};
|
|
}
|
|
const i18nNode = new TagPlaceholder(nodeName, attrs, startPhName, closePhName, children, isVoid, node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
|
|
return context.visitNodeFn(node, i18nNode);
|
|
}
|
|
_visitTextWithInterpolation(tokens, sourceSpan, context, previousI18n) {
|
|
const nodes = [];
|
|
let hasInterpolation = false;
|
|
for (const token of tokens) {
|
|
switch (token.type) {
|
|
case 8:
|
|
case 17:
|
|
hasInterpolation = true;
|
|
const [startMarker, expression, endMarker] = token.parts;
|
|
const baseName = extractPlaceholderName(expression) || 'INTERPOLATION';
|
|
const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression);
|
|
if (this._preserveExpressionWhitespace) {
|
|
context.placeholderToContent[phName] = {
|
|
text: token.parts.join(''),
|
|
sourceSpan: token.sourceSpan
|
|
};
|
|
nodes.push(new Placeholder(expression, phName, token.sourceSpan));
|
|
} else {
|
|
const normalized = this.normalizeExpression(token);
|
|
context.placeholderToContent[phName] = {
|
|
text: `${startMarker}${normalized}${endMarker}`,
|
|
sourceSpan: token.sourceSpan
|
|
};
|
|
nodes.push(new Placeholder(normalized, phName, token.sourceSpan));
|
|
}
|
|
break;
|
|
default:
|
|
if (token.parts[0].length > 0 || this._retainEmptyTokens) {
|
|
const previous = nodes[nodes.length - 1];
|
|
if (previous instanceof Text$2) {
|
|
previous.value += token.parts[0];
|
|
previous.sourceSpan = new ParseSourceSpan(previous.sourceSpan.start, token.sourceSpan.end, previous.sourceSpan.fullStart, previous.sourceSpan.details);
|
|
} else {
|
|
nodes.push(new Text$2(token.parts[0], token.sourceSpan));
|
|
}
|
|
} else {
|
|
if (this._retainEmptyTokens) {
|
|
nodes.push(new Text$2(token.parts[0], token.sourceSpan));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (hasInterpolation) {
|
|
reusePreviousSourceSpans(nodes, previousI18n);
|
|
return new Container(nodes, sourceSpan);
|
|
} else {
|
|
return nodes[0];
|
|
}
|
|
}
|
|
normalizeExpression(token) {
|
|
const expression = token.parts[1];
|
|
const expr = this._expressionParser.parseBinding(expression, token.sourceSpan, token.sourceSpan.start.offset);
|
|
return serialize(expr);
|
|
}
|
|
}
|
|
function reusePreviousSourceSpans(nodes, previousI18n) {
|
|
if (previousI18n instanceof Message) {
|
|
assertSingleContainerMessage(previousI18n);
|
|
previousI18n = previousI18n.nodes[0];
|
|
}
|
|
if (previousI18n instanceof Container) {
|
|
assertEquivalentNodes(previousI18n.children, nodes);
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
|
|
}
|
|
}
|
|
}
|
|
function assertSingleContainerMessage(message) {
|
|
const nodes = message.nodes;
|
|
if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
|
|
throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
|
|
}
|
|
}
|
|
function assertEquivalentNodes(previousNodes, nodes) {
|
|
if (previousNodes.length !== nodes.length) {
|
|
throw new Error(`
|
|
The number of i18n message children changed between first and second pass.
|
|
|
|
First pass (${previousNodes.length} tokens):
|
|
${previousNodes.map(node => `"${node.sourceSpan.toString()}"`).join('\n')}
|
|
|
|
Second pass (${nodes.length} tokens):
|
|
${nodes.map(node => `"${node.sourceSpan.toString()}"`).join('\n')}
|
|
`.trim());
|
|
}
|
|
if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
|
|
throw new Error('The types of the i18n message children changed between first and second pass.');
|
|
}
|
|
}
|
|
const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
|
|
function extractPlaceholderName(input) {
|
|
return input.split(_CUSTOM_PH_EXP)[2];
|
|
}
|
|
|
|
const TRUSTED_TYPES_SINKS = new Set(['iframe|srcdoc', '*|innerhtml', '*|outerhtml', 'embed|src', 'object|codebase', 'object|data']);
|
|
function isTrustedTypesSink(tagName, propName) {
|
|
tagName = tagName.toLowerCase();
|
|
propName = propName.toLowerCase();
|
|
return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) || TRUSTED_TYPES_SINKS.has('*|' + propName);
|
|
}
|
|
|
|
const setI18nRefs = originalNodeMap => {
|
|
return (trimmedNode, i18nNode) => {
|
|
const originalNode = originalNodeMap.get(trimmedNode) ?? trimmedNode;
|
|
if (originalNode instanceof NodeWithI18n) {
|
|
if (i18nNode instanceof IcuPlaceholder && originalNode.i18n instanceof Message) {
|
|
i18nNode.previousMessage = originalNode.i18n;
|
|
}
|
|
originalNode.i18n = i18nNode;
|
|
}
|
|
return i18nNode;
|
|
};
|
|
};
|
|
class I18nMetaVisitor {
|
|
keepI18nAttrs;
|
|
enableI18nLegacyMessageIdFormat;
|
|
preserveSignificantWhitespace;
|
|
retainEmptyTokens;
|
|
hasI18nMeta = false;
|
|
_errors = [];
|
|
constructor(keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false, preserveSignificantWhitespace = true, retainEmptyTokens = !preserveSignificantWhitespace) {
|
|
this.keepI18nAttrs = keepI18nAttrs;
|
|
this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
|
|
this.preserveSignificantWhitespace = preserveSignificantWhitespace;
|
|
this.retainEmptyTokens = retainEmptyTokens;
|
|
}
|
|
_generateI18nMessage(nodes, meta = '', visitNodeFn) {
|
|
const {
|
|
meaning,
|
|
description,
|
|
customId
|
|
} = this._parseMetadata(meta);
|
|
const createI18nMessage = createI18nMessageFactory(this.retainEmptyTokens, this.preserveSignificantWhitespace);
|
|
const message = createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
|
this._setMessageId(message, meta);
|
|
this._setLegacyIds(message, meta);
|
|
return message;
|
|
}
|
|
visitAllWithErrors(nodes) {
|
|
const result = nodes.map(node => node.visit(this, null));
|
|
return new ParseTreeResult(result, this._errors);
|
|
}
|
|
visitElement(element) {
|
|
this._visitElementLike(element);
|
|
return element;
|
|
}
|
|
visitComponent(component, context) {
|
|
this._visitElementLike(component);
|
|
return component;
|
|
}
|
|
visitExpansion(expansion, currentMessage) {
|
|
let message;
|
|
const meta = expansion.i18n;
|
|
this.hasI18nMeta = true;
|
|
if (meta instanceof IcuPlaceholder) {
|
|
const name = meta.name;
|
|
message = this._generateI18nMessage([expansion], meta);
|
|
const icu = icuFromI18nMessage(message);
|
|
icu.name = name;
|
|
if (currentMessage !== null) {
|
|
currentMessage.placeholderToMessage[name] = message;
|
|
}
|
|
} else {
|
|
message = this._generateI18nMessage([expansion], currentMessage || meta);
|
|
}
|
|
expansion.i18n = message;
|
|
return expansion;
|
|
}
|
|
visitText(text) {
|
|
return text;
|
|
}
|
|
visitAttribute(attribute) {
|
|
return attribute;
|
|
}
|
|
visitComment(comment) {
|
|
return comment;
|
|
}
|
|
visitExpansionCase(expansionCase) {
|
|
return expansionCase;
|
|
}
|
|
visitBlock(block, context) {
|
|
visitAll(this, block.children, context);
|
|
return block;
|
|
}
|
|
visitBlockParameter(parameter, context) {
|
|
return parameter;
|
|
}
|
|
visitLetDeclaration(decl, context) {
|
|
return decl;
|
|
}
|
|
visitDirective(directive, context) {
|
|
return directive;
|
|
}
|
|
_visitElementLike(node) {
|
|
let message = undefined;
|
|
if (hasI18nAttrs(node)) {
|
|
this.hasI18nMeta = true;
|
|
const attrs = [];
|
|
const attrsMeta = {};
|
|
for (const attr of node.attrs) {
|
|
if (attr.name === I18N_ATTR) {
|
|
const i18n = node.i18n || attr.value;
|
|
const originalNodeMap = new Map();
|
|
const trimmedNodes = this.preserveSignificantWhitespace ? node.children : visitAllWithSiblings(new WhitespaceVisitor(false, originalNodeMap), node.children);
|
|
message = this._generateI18nMessage(trimmedNodes, i18n, setI18nRefs(originalNodeMap));
|
|
if (message.nodes.length === 0) {
|
|
message = undefined;
|
|
}
|
|
node.i18n = message;
|
|
} else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
|
|
const name = attr.name.slice(I18N_ATTR_PREFIX.length);
|
|
let isTrustedType;
|
|
if (node instanceof Component) {
|
|
isTrustedType = node.tagName === null ? false : isTrustedTypesSink(node.tagName, name);
|
|
} else {
|
|
isTrustedType = isTrustedTypesSink(node.name, name);
|
|
}
|
|
if (isTrustedType) {
|
|
this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
|
|
} else {
|
|
attrsMeta[name] = attr.value;
|
|
}
|
|
} else {
|
|
attrs.push(attr);
|
|
}
|
|
}
|
|
if (Object.keys(attrsMeta).length) {
|
|
for (const attr of attrs) {
|
|
const meta = attrsMeta[attr.name];
|
|
if (meta !== undefined && attr.value) {
|
|
attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
|
|
}
|
|
}
|
|
}
|
|
if (!this.keepI18nAttrs) {
|
|
node.attrs = attrs;
|
|
}
|
|
}
|
|
visitAll(this, node.children, message);
|
|
}
|
|
_parseMetadata(meta) {
|
|
return typeof meta === 'string' ? parseI18nMeta(meta) : meta instanceof Message ? meta : {};
|
|
}
|
|
_setMessageId(message, meta) {
|
|
if (!message.id) {
|
|
message.id = meta instanceof Message && meta.id || decimalDigest(message);
|
|
}
|
|
}
|
|
_setLegacyIds(message, meta) {
|
|
if (this.enableI18nLegacyMessageIdFormat) {
|
|
message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
|
|
} else if (typeof meta !== 'string') {
|
|
const previousMessage = meta instanceof Message ? meta : meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
|
|
message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
|
|
}
|
|
}
|
|
_reportError(node, msg) {
|
|
this._errors.push(new ParseError(node.sourceSpan, msg));
|
|
}
|
|
}
|
|
const I18N_MEANING_SEPARATOR = '|';
|
|
const I18N_ID_SEPARATOR = '@@';
|
|
function parseI18nMeta(meta = '') {
|
|
let customId;
|
|
let meaning;
|
|
let description;
|
|
meta = meta.trim();
|
|
if (meta) {
|
|
const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
|
|
const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
|
|
let meaningAndDesc;
|
|
[meaningAndDesc, customId] = idIndex > -1 ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
|
|
[meaning, description] = descIndex > -1 ? [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : ['', meaningAndDesc];
|
|
}
|
|
return {
|
|
customId,
|
|
meaning,
|
|
description
|
|
};
|
|
}
|
|
function i18nMetaToJSDoc(meta) {
|
|
const tags = [];
|
|
if (meta.description) {
|
|
tags.push({
|
|
tagName: "desc",
|
|
text: meta.description
|
|
});
|
|
} else {
|
|
tags.push({
|
|
tagName: "suppress",
|
|
text: '{msgDescriptions}'
|
|
});
|
|
}
|
|
if (meta.meaning) {
|
|
tags.push({
|
|
tagName: "meaning",
|
|
text: meta.meaning
|
|
});
|
|
}
|
|
return jsDocComment(tags);
|
|
}
|
|
|
|
const GOOG_GET_MSG = 'goog.getMsg';
|
|
function createGoogleGetMsgStatements(variable$1, message, closureVar, placeholderValues) {
|
|
const messageString = serializeI18nMessageForGetMsg(message);
|
|
const args = [literal(messageString)];
|
|
if (Object.keys(placeholderValues).length) {
|
|
args.push(mapLiteral(formatI18nPlaceholderNamesInMap(placeholderValues, true), true));
|
|
args.push(mapLiteral({
|
|
original_code: literalMap(Object.keys(placeholderValues).map(param => ({
|
|
key: formatI18nPlaceholderName(param),
|
|
quoted: true,
|
|
value: message.placeholders[param] ? literal(message.placeholders[param].sourceSpan.toString()) : literal(message.placeholderToMessage[param].nodes.map(node => node.sourceSpan.toString()).join(''))
|
|
})))
|
|
}));
|
|
}
|
|
const googGetMsgStmt = new DeclareVarStmt(closureVar.name, variable(GOOG_GET_MSG).callFn(args), INFERRED_TYPE, StmtModifier.Final);
|
|
googGetMsgStmt.addLeadingComment(i18nMetaToJSDoc(message));
|
|
const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
|
|
return [googGetMsgStmt, i18nAssignmentStmt];
|
|
}
|
|
class GetMsgSerializerVisitor {
|
|
formatPh(value) {
|
|
return `{$${formatI18nPlaceholderName(value)}}`;
|
|
}
|
|
visitText(text) {
|
|
return text.value;
|
|
}
|
|
visitContainer(container) {
|
|
return container.children.map(child => child.visit(this)).join('');
|
|
}
|
|
visitIcu(icu) {
|
|
return serializeIcuNode(icu);
|
|
}
|
|
visitTagPlaceholder(ph) {
|
|
return ph.isVoid ? this.formatPh(ph.startName) : `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
|
}
|
|
visitPlaceholder(ph) {
|
|
return this.formatPh(ph.name);
|
|
}
|
|
visitBlockPlaceholder(ph) {
|
|
return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
return this.formatPh(ph.name);
|
|
}
|
|
}
|
|
const serializerVisitor = new GetMsgSerializerVisitor();
|
|
function serializeI18nMessageForGetMsg(message) {
|
|
return message.nodes.map(node => node.visit(serializerVisitor, null)).join('');
|
|
}
|
|
|
|
function createLocalizeStatements(variable, message, params) {
|
|
const {
|
|
messageParts,
|
|
placeHolders
|
|
} = serializeI18nMessageForLocalize(message);
|
|
const sourceSpan = getSourceSpan(message);
|
|
const expressions = placeHolders.map(ph => params[ph.text]);
|
|
const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
|
|
const variableInitialization = variable.set(localizedString$1);
|
|
return [new ExpressionStatement(variableInitialization)];
|
|
}
|
|
class LocalizeSerializerVisitor {
|
|
placeholderToMessage;
|
|
pieces;
|
|
constructor(placeholderToMessage, pieces) {
|
|
this.placeholderToMessage = placeholderToMessage;
|
|
this.pieces = pieces;
|
|
}
|
|
visitText(text) {
|
|
if (this.pieces[this.pieces.length - 1] instanceof LiteralPiece) {
|
|
this.pieces[this.pieces.length - 1].text += text.value;
|
|
} else {
|
|
const sourceSpan = new ParseSourceSpan(text.sourceSpan.fullStart, text.sourceSpan.end, text.sourceSpan.fullStart, text.sourceSpan.details);
|
|
this.pieces.push(new LiteralPiece(text.value, sourceSpan));
|
|
}
|
|
}
|
|
visitContainer(container) {
|
|
container.children.forEach(child => child.visit(this));
|
|
}
|
|
visitIcu(icu) {
|
|
this.pieces.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
|
|
}
|
|
visitTagPlaceholder(ph) {
|
|
this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
|
|
if (!ph.isVoid) {
|
|
ph.children.forEach(child => child.visit(this));
|
|
this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
|
|
}
|
|
}
|
|
visitPlaceholder(ph) {
|
|
this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
|
}
|
|
visitBlockPlaceholder(ph) {
|
|
this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
|
|
ph.children.forEach(child => child.visit(this));
|
|
this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
|
|
}
|
|
visitIcuPlaceholder(ph) {
|
|
this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan, this.placeholderToMessage[ph.name]));
|
|
}
|
|
createPlaceholderPiece(name, sourceSpan, associatedMessage) {
|
|
return new PlaceholderPiece(formatI18nPlaceholderName(name, false), sourceSpan, associatedMessage);
|
|
}
|
|
}
|
|
function serializeI18nMessageForLocalize(message) {
|
|
const pieces = [];
|
|
const serializerVisitor = new LocalizeSerializerVisitor(message.placeholderToMessage, pieces);
|
|
message.nodes.forEach(node => node.visit(serializerVisitor));
|
|
return processMessagePieces(pieces);
|
|
}
|
|
function getSourceSpan(message) {
|
|
const startNode = message.nodes[0];
|
|
const endNode = message.nodes[message.nodes.length - 1];
|
|
return new ParseSourceSpan(startNode.sourceSpan.fullStart, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
|
|
}
|
|
function processMessagePieces(pieces) {
|
|
const messageParts = [];
|
|
const placeHolders = [];
|
|
if (pieces[0] instanceof PlaceholderPiece) {
|
|
messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
|
|
}
|
|
for (let i = 0; i < pieces.length; i++) {
|
|
const part = pieces[i];
|
|
if (part instanceof LiteralPiece) {
|
|
messageParts.push(part);
|
|
} else {
|
|
placeHolders.push(part);
|
|
if (pieces[i - 1] instanceof PlaceholderPiece) {
|
|
messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
|
|
}
|
|
}
|
|
}
|
|
if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
|
|
messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
|
|
}
|
|
return {
|
|
messageParts,
|
|
placeHolders
|
|
};
|
|
}
|
|
function createEmptyMessagePart(location) {
|
|
return new LiteralPiece('', new ParseSourceSpan(location, location));
|
|
}
|
|
|
|
const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
|
|
const TRANSLATION_VAR_PREFIX = 'i18n_';
|
|
const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
|
const ESCAPE = '\uFFFD';
|
|
const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
|
|
function getTranslationConstPrefix(extra) {
|
|
return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
|
|
}
|
|
function declareI18nVariable(variable) {
|
|
return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
|
|
}
|
|
function collectI18nConsts(job) {
|
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
const extractedAttributesByI18nContext = new Map();
|
|
const i18nAttributesByElement = new Map();
|
|
const i18nExpressionsByElement = new Map();
|
|
const messages = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
|
|
const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
|
|
attributes.push(op);
|
|
extractedAttributesByI18nContext.set(op.i18nContext, attributes);
|
|
} else if (op.kind === OpKind.I18nAttributes) {
|
|
i18nAttributesByElement.set(op.target, op);
|
|
} else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
|
|
const expressions = i18nExpressionsByElement.get(op.target) ?? [];
|
|
expressions.push(op);
|
|
i18nExpressionsByElement.set(op.target, expressions);
|
|
} else if (op.kind === OpKind.I18nMessage) {
|
|
messages.set(op.xref, op);
|
|
}
|
|
}
|
|
}
|
|
const i18nValuesByContext = new Map();
|
|
const messageConstIndices = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.I18nMessage) {
|
|
if (op.messagePlaceholder === null) {
|
|
const {
|
|
mainVar,
|
|
statements
|
|
} = collectMessage(job, fileBasedI18nSuffix, messages, op);
|
|
if (op.i18nBlock !== null) {
|
|
const i18nConst = job.addConst(mainVar, statements);
|
|
messageConstIndices.set(op.i18nBlock, i18nConst);
|
|
} else {
|
|
job.constsInitializers.push(...statements);
|
|
i18nValuesByContext.set(op.i18nContext, mainVar);
|
|
const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
|
|
if (attributesForMessage !== undefined) {
|
|
for (const attr of attributesForMessage) {
|
|
attr.expression = mainVar.clone();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const elem of unit.create) {
|
|
if (isElementOrContainerOp(elem)) {
|
|
const i18nAttributes = i18nAttributesByElement.get(elem.xref);
|
|
if (i18nAttributes === undefined) {
|
|
continue;
|
|
}
|
|
let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
|
|
if (i18nExpressions === undefined) {
|
|
throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
|
|
}
|
|
const seenPropertyNames = new Set();
|
|
i18nExpressions = i18nExpressions.filter(i18nExpr => {
|
|
const seen = seenPropertyNames.has(i18nExpr.name);
|
|
seenPropertyNames.add(i18nExpr.name);
|
|
return !seen;
|
|
});
|
|
const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
|
|
const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
|
|
if (i18nExprValue === undefined) {
|
|
throw new Error("AssertionError: Could not find i18n expression's value");
|
|
}
|
|
return [literal(i18nExpr.name), i18nExprValue];
|
|
});
|
|
i18nAttributes.i18nAttributesConfig = job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
|
|
}
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.I18nStart) {
|
|
const msgIndex = messageConstIndices.get(op.root);
|
|
if (msgIndex === undefined) {
|
|
throw new Error('AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?');
|
|
}
|
|
op.messageIndex = msgIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
const statements = [];
|
|
const subMessagePlaceholders = new Map();
|
|
for (const subMessageId of messageOp.subMessages) {
|
|
const subMessage = messages.get(subMessageId);
|
|
const {
|
|
mainVar: subMessageVar,
|
|
statements: subMessageStatements
|
|
} = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
|
|
statements.push(...subMessageStatements);
|
|
const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
|
|
subMessages.push(subMessageVar);
|
|
subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
|
|
}
|
|
addSubMessageParams(messageOp, subMessagePlaceholders);
|
|
messageOp.params = new Map([...messageOp.params.entries()].sort());
|
|
const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
|
|
const closureVar = i18nGenerateClosureVar(job.pool, messageOp.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
|
|
let transformFn = undefined;
|
|
if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
|
|
const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
|
|
const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, false);
|
|
const extraTransformFnParams = [];
|
|
if (messageOp.postprocessingParams.size > 0) {
|
|
extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, true));
|
|
}
|
|
transformFn = expr => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
|
|
}
|
|
statements.push(...getTranslationDeclStmts(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
|
|
return {
|
|
mainVar,
|
|
statements
|
|
};
|
|
}
|
|
function addSubMessageParams(messageOp, subMessagePlaceholders) {
|
|
for (const [placeholder, subMessages] of subMessagePlaceholders) {
|
|
if (subMessages.length === 1) {
|
|
messageOp.params.set(placeholder, subMessages[0]);
|
|
} else {
|
|
messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
|
|
messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
|
|
}
|
|
}
|
|
}
|
|
function getTranslationDeclStmts(message, variable, closureVar, params, transformFn) {
|
|
const paramsObject = Object.fromEntries(params);
|
|
const statements = [declareI18nVariable(variable), ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, paramsObject), createLocalizeStatements(variable, message, formatI18nPlaceholderNamesInMap(paramsObject, false)))];
|
|
if (transformFn) {
|
|
statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
|
|
}
|
|
return statements;
|
|
}
|
|
function createClosureModeGuard() {
|
|
return typeofExpr(variable(NG_I18N_CLOSURE_MODE)).notIdentical(literal('undefined', STRING_TYPE)).and(variable(NG_I18N_CLOSURE_MODE));
|
|
}
|
|
function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExternalIds) {
|
|
let name;
|
|
const suffix = fileBasedI18nSuffix;
|
|
if (useExternalIds) {
|
|
const prefix = getTranslationConstPrefix(`EXTERNAL_`);
|
|
const uniqueSuffix = pool.uniqueName(suffix);
|
|
name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
|
|
} else {
|
|
const prefix = getTranslationConstPrefix(suffix);
|
|
name = pool.uniqueName(prefix);
|
|
}
|
|
return variable(name);
|
|
}
|
|
|
|
function convertI18nText(job) {
|
|
for (const unit of job.units) {
|
|
let currentI18n = null;
|
|
let currentIcu = null;
|
|
const textNodeI18nBlocks = new Map();
|
|
const textNodeIcus = new Map();
|
|
const icuPlaceholderByText = new Map();
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
if (op.context === null) {
|
|
throw Error('I18n op should have its context set.');
|
|
}
|
|
currentI18n = op;
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
currentI18n = null;
|
|
break;
|
|
case OpKind.IcuStart:
|
|
if (op.context === null) {
|
|
throw Error('Icu op should have its context set.');
|
|
}
|
|
currentIcu = op;
|
|
break;
|
|
case OpKind.IcuEnd:
|
|
currentIcu = null;
|
|
break;
|
|
case OpKind.Text:
|
|
if (currentI18n !== null) {
|
|
textNodeI18nBlocks.set(op.xref, currentI18n);
|
|
textNodeIcus.set(op.xref, currentIcu);
|
|
if (op.icuPlaceholder !== null) {
|
|
const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
|
|
OpList.replace(op, icuPlaceholderOp);
|
|
icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
|
|
} else {
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
for (const op of unit.update) {
|
|
switch (op.kind) {
|
|
case OpKind.InterpolateText:
|
|
if (!textNodeI18nBlocks.has(op.target)) {
|
|
continue;
|
|
}
|
|
const i18nOp = textNodeI18nBlocks.get(op.target);
|
|
const icuOp = textNodeIcus.get(op.target);
|
|
const icuPlaceholder = icuPlaceholderByText.get(op.target);
|
|
const contextId = icuOp ? icuOp.context : i18nOp.context;
|
|
const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing : I18nParamResolutionTime.Creation;
|
|
const ops = [];
|
|
for (let i = 0; i < op.interpolation.expressions.length; i++) {
|
|
const expr = op.interpolation.expressions[i];
|
|
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
|
|
}
|
|
OpList.replaceWithMany(op, ops);
|
|
if (icuPlaceholder !== undefined) {
|
|
icuPlaceholder.strings = op.interpolation.strings;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function liftLocalRefs(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.ElementStart:
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
if (!Array.isArray(op.localRefs)) {
|
|
throw new Error(`AssertionError: expected localRefs to be an array still`);
|
|
}
|
|
op.numSlotsUsed += op.localRefs.length;
|
|
if (op.localRefs.length > 0) {
|
|
const localRefs = serializeLocalRefs(op.localRefs);
|
|
op.localRefs = job.addConst(localRefs);
|
|
} else {
|
|
op.localRefs = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function serializeLocalRefs(refs) {
|
|
const constRefs = [];
|
|
for (const ref of refs) {
|
|
constRefs.push(literal(ref.name), literal(ref.target));
|
|
}
|
|
return literalArr(constRefs);
|
|
}
|
|
|
|
function emitNamespaceChanges(job) {
|
|
for (const unit of job.units) {
|
|
let activeNamespace = Namespace.HTML;
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.ElementStart) {
|
|
continue;
|
|
}
|
|
if (op.namespace !== activeNamespace) {
|
|
OpList.insertBefore(createNamespaceOp(op.namespace), op);
|
|
activeNamespace = op.namespace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function parse(value) {
|
|
const styles = [];
|
|
let i = 0;
|
|
let parenDepth = 0;
|
|
let quote = 0;
|
|
let valueStart = 0;
|
|
let propStart = 0;
|
|
let currentProp = null;
|
|
while (i < value.length) {
|
|
const token = value.charCodeAt(i++);
|
|
switch (token) {
|
|
case 40:
|
|
parenDepth++;
|
|
break;
|
|
case 41:
|
|
parenDepth--;
|
|
break;
|
|
case 39:
|
|
if (quote === 0) {
|
|
quote = 39;
|
|
} else if (quote === 39 && value.charCodeAt(i - 1) !== 92) {
|
|
quote = 0;
|
|
}
|
|
break;
|
|
case 34:
|
|
if (quote === 0) {
|
|
quote = 34;
|
|
} else if (quote === 34 && value.charCodeAt(i - 1) !== 92) {
|
|
quote = 0;
|
|
}
|
|
break;
|
|
case 58:
|
|
if (!currentProp && parenDepth === 0 && quote === 0) {
|
|
currentProp = hyphenate(value.substring(propStart, i - 1).trim());
|
|
valueStart = i;
|
|
}
|
|
break;
|
|
case 59:
|
|
if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0) {
|
|
const styleVal = value.substring(valueStart, i - 1).trim();
|
|
styles.push(currentProp, styleVal);
|
|
propStart = i;
|
|
valueStart = 0;
|
|
currentProp = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (currentProp && valueStart) {
|
|
const styleVal = value.slice(valueStart).trim();
|
|
styles.push(currentProp, styleVal);
|
|
}
|
|
return styles;
|
|
}
|
|
function hyphenate(value) {
|
|
return value.replace(/[a-z][A-Z]/g, v => {
|
|
return v.charAt(0) + '-' + v.charAt(1);
|
|
}).toLowerCase();
|
|
}
|
|
function parseExtractedStyles(job) {
|
|
const elements = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (isElementOrContainerOp(op)) {
|
|
elements.set(op.xref, op);
|
|
}
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute && isStringLiteral(op.expression)) {
|
|
const target = elements.get(op.target);
|
|
if (target !== undefined && (target.kind === OpKind.Template || target.kind === OpKind.ConditionalCreate || target.kind === OpKind.ConditionalBranchCreate) && target.templateKind === TemplateKind.Structural) {
|
|
continue;
|
|
}
|
|
if (op.name === 'style') {
|
|
const parsedStyles = parse(op.expression.value);
|
|
for (let i = 0; i < parsedStyles.length - 1; i += 2) {
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, null, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
|
|
}
|
|
OpList.remove(op);
|
|
} else if (op.name === 'class') {
|
|
const parsedClasses = op.expression.value.trim().split(/\s+/g);
|
|
for (const parsedClass of parsedClasses) {
|
|
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, null, parsedClass, null, null, null, SecurityContext.NONE), op);
|
|
}
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function nameFunctionsAndVariables(job) {
|
|
addNamesToView(job.root, job.componentName, {
|
|
index: 0
|
|
}, job.compatibility === CompatibilityMode.TemplateDefinitionBuilder);
|
|
}
|
|
function addNamesToView(unit, baseName, state, compatibility) {
|
|
if (unit.fnName === null) {
|
|
unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), false);
|
|
}
|
|
const varNames = new Map();
|
|
for (const op of unit.ops()) {
|
|
switch (op.kind) {
|
|
case OpKind.Property:
|
|
case OpKind.DomProperty:
|
|
if (op.bindingKind === BindingKind.LegacyAnimation) {
|
|
op.name = '@' + op.name;
|
|
}
|
|
break;
|
|
case OpKind.Animation:
|
|
if (op.handlerFnName === null) {
|
|
const animationKind = op.name.replace('.', '');
|
|
op.handlerFnName = `${unit.fnName}_${animationKind}_cb`;
|
|
op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
|
|
}
|
|
break;
|
|
case OpKind.AnimationListener:
|
|
if (op.handlerFnName !== null) {
|
|
break;
|
|
}
|
|
if (!op.hostListener && op.targetSlot.slot === null) {
|
|
throw new Error(`Expected a slot to be assigned`);
|
|
}
|
|
const animationKind = op.name.replace('.', '');
|
|
if (op.hostListener) {
|
|
op.handlerFnName = `${baseName}_${animationKind}_HostBindingHandler`;
|
|
} else {
|
|
op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animationKind}_${op.targetSlot.slot}_listener`;
|
|
}
|
|
op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
|
|
break;
|
|
case OpKind.Listener:
|
|
if (op.handlerFnName !== null) {
|
|
break;
|
|
}
|
|
if (!op.hostListener && op.targetSlot.slot === null) {
|
|
throw new Error(`Expected a slot to be assigned`);
|
|
}
|
|
let animation = '';
|
|
if (op.isLegacyAnimationListener) {
|
|
op.name = `@${op.name}.${op.legacyAnimationPhase}`;
|
|
animation = 'animation';
|
|
}
|
|
if (op.hostListener) {
|
|
op.handlerFnName = `${baseName}_${animation}${op.name}_HostBindingHandler`;
|
|
} else {
|
|
op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot.slot}_listener`;
|
|
}
|
|
op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
|
|
break;
|
|
case OpKind.TwoWayListener:
|
|
if (op.handlerFnName !== null) {
|
|
break;
|
|
}
|
|
if (op.targetSlot.slot === null) {
|
|
throw new Error(`Expected a slot to be assigned`);
|
|
}
|
|
op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`);
|
|
break;
|
|
case OpKind.Variable:
|
|
varNames.set(op.xref, getVariableName(unit, op.variable, state));
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
if (op.handle.slot === null) {
|
|
throw new Error(`Expected slot to be assigned`);
|
|
}
|
|
if (op.emptyView !== null) {
|
|
const emptyView = unit.job.views.get(op.emptyView);
|
|
addNamesToView(emptyView, `${baseName}_${op.functionNameSuffix}Empty_${op.handle.slot + 2}`, state, compatibility);
|
|
}
|
|
addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility);
|
|
break;
|
|
case OpKind.Projection:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
if (op.handle.slot === null) {
|
|
throw new Error(`Expected slot to be assigned`);
|
|
}
|
|
if (op.fallbackView !== null) {
|
|
const fallbackView = unit.job.views.get(op.fallbackView);
|
|
addNamesToView(fallbackView, `${baseName}_ProjectionFallback_${op.handle.slot}`, state, compatibility);
|
|
}
|
|
break;
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
const childView = unit.job.views.get(op.xref);
|
|
if (op.handle.slot === null) {
|
|
throw new Error(`Expected slot to be assigned`);
|
|
}
|
|
const suffix = op.functionNameSuffix.length === 0 ? '' : `_${op.functionNameSuffix}`;
|
|
addNamesToView(childView, `${baseName}${suffix}_${op.handle.slot}`, state, compatibility);
|
|
break;
|
|
case OpKind.StyleProp:
|
|
op.name = normalizeStylePropName(op.name);
|
|
if (compatibility) {
|
|
op.name = stripImportant(op.name);
|
|
}
|
|
break;
|
|
case OpKind.ClassProp:
|
|
if (compatibility) {
|
|
op.name = stripImportant(op.name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
for (const op of unit.ops()) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
return;
|
|
}
|
|
if (!varNames.has(expr.xref)) {
|
|
throw new Error(`Variable ${expr.xref} not yet named`);
|
|
}
|
|
expr.name = varNames.get(expr.xref);
|
|
});
|
|
}
|
|
}
|
|
function getVariableName(unit, variable, state) {
|
|
if (variable.name === null) {
|
|
switch (variable.kind) {
|
|
case SemanticVariableKind.Context:
|
|
variable.name = `ctx_r${state.index++}`;
|
|
break;
|
|
case SemanticVariableKind.Identifier:
|
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
const compatPrefix = variable.identifier === 'ctx' ? 'i' : '';
|
|
variable.name = `${variable.identifier}_${compatPrefix}r${++state.index}`;
|
|
} else {
|
|
variable.name = `${variable.identifier}_i${state.index++}`;
|
|
}
|
|
break;
|
|
default:
|
|
variable.name = `_r${++state.index}`;
|
|
break;
|
|
}
|
|
}
|
|
return variable.name;
|
|
}
|
|
function normalizeStylePropName(name) {
|
|
return name.startsWith('--') ? name : hyphenate(name);
|
|
}
|
|
function stripImportant(name) {
|
|
const importantIndex = name.indexOf('!important');
|
|
if (importantIndex > -1) {
|
|
return name.substring(0, importantIndex);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
function mergeNextContextExpressions(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener || op.kind === OpKind.TwoWayListener) {
|
|
mergeNextContextsInOps(op.handlerOps);
|
|
}
|
|
}
|
|
mergeNextContextsInOps(unit.update);
|
|
}
|
|
}
|
|
function mergeNextContextsInOps(ops) {
|
|
for (const op of ops) {
|
|
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement) || !(op.statement.expr instanceof NextContextExpr)) {
|
|
continue;
|
|
}
|
|
const mergeSteps = op.statement.expr.steps;
|
|
let tryToMerge = true;
|
|
for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) {
|
|
visitExpressionsInOp(candidate, (expr, flags) => {
|
|
if (!isIrExpression(expr)) {
|
|
return expr;
|
|
}
|
|
if (!tryToMerge) {
|
|
return;
|
|
}
|
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
return;
|
|
}
|
|
switch (expr.kind) {
|
|
case ExpressionKind.NextContext:
|
|
expr.steps += mergeSteps;
|
|
OpList.remove(op);
|
|
tryToMerge = false;
|
|
break;
|
|
case ExpressionKind.GetCurrentView:
|
|
case ExpressionKind.Reference:
|
|
case ExpressionKind.ContextLetReference:
|
|
tryToMerge = false;
|
|
break;
|
|
}
|
|
return;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const CONTAINER_TAG = 'ng-container';
|
|
function generateNgContainerOps(job) {
|
|
for (const unit of job.units) {
|
|
const updatedElementXrefs = new Set();
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) {
|
|
op.kind = OpKind.ContainerStart;
|
|
updatedElementXrefs.add(op.xref);
|
|
}
|
|
if (op.kind === OpKind.ElementEnd && updatedElementXrefs.has(op.xref)) {
|
|
op.kind = OpKind.ContainerEnd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function lookupElement(elements, xref) {
|
|
const el = elements.get(xref);
|
|
if (el === undefined) {
|
|
throw new Error('All attributes should have an element-like target.');
|
|
}
|
|
return el;
|
|
}
|
|
function disableBindings$1(job) {
|
|
const elements = new Map();
|
|
for (const view of job.units) {
|
|
for (const op of view.create) {
|
|
if (!isElementOrContainerOp(op)) {
|
|
continue;
|
|
}
|
|
elements.set(op.xref, op);
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if ((op.kind === OpKind.ElementStart || op.kind === OpKind.ContainerStart) && op.nonBindable) {
|
|
OpList.insertAfter(createDisableBindingsOp(op.xref), op);
|
|
}
|
|
if ((op.kind === OpKind.ElementEnd || op.kind === OpKind.ContainerEnd) && lookupElement(elements, op.xref).nonBindable) {
|
|
OpList.insertBefore(createEnableBindingsOp(op.xref), op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function kindTest(kind) {
|
|
return op => op.kind === kind;
|
|
}
|
|
function kindWithInterpolationTest(kind, interpolation) {
|
|
return op => {
|
|
return op.kind === kind && interpolation === op.expression instanceof Interpolation;
|
|
};
|
|
}
|
|
function basicListenerKindTest(op) {
|
|
return op.kind === OpKind.Listener && !(op.hostListener && op.isLegacyAnimationListener) || op.kind === OpKind.TwoWayListener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener;
|
|
}
|
|
function nonInterpolationPropertyKindTest(op) {
|
|
return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) && !(op.expression instanceof Interpolation);
|
|
}
|
|
const CREATE_ORDERING = [{
|
|
test: op => op.kind === OpKind.Listener && op.hostListener && op.isLegacyAnimationListener
|
|
}, {
|
|
test: basicListenerKindTest
|
|
}];
|
|
const UPDATE_ORDERING = [{
|
|
test: kindTest(OpKind.StyleMap),
|
|
transform: keepLast
|
|
}, {
|
|
test: kindTest(OpKind.ClassMap),
|
|
transform: keepLast
|
|
}, {
|
|
test: kindTest(OpKind.StyleProp)
|
|
}, {
|
|
test: kindTest(OpKind.ClassProp)
|
|
}, {
|
|
test: kindWithInterpolationTest(OpKind.Attribute, true)
|
|
}, {
|
|
test: kindWithInterpolationTest(OpKind.Property, true)
|
|
}, {
|
|
test: nonInterpolationPropertyKindTest
|
|
}, {
|
|
test: kindWithInterpolationTest(OpKind.Attribute, false)
|
|
}, {
|
|
test: kindTest(OpKind.Control)
|
|
}];
|
|
const UPDATE_HOST_ORDERING = [{
|
|
test: kindWithInterpolationTest(OpKind.DomProperty, true)
|
|
}, {
|
|
test: kindWithInterpolationTest(OpKind.DomProperty, false)
|
|
}, {
|
|
test: kindTest(OpKind.Attribute)
|
|
}, {
|
|
test: kindTest(OpKind.StyleMap),
|
|
transform: keepLast
|
|
}, {
|
|
test: kindTest(OpKind.ClassMap),
|
|
transform: keepLast
|
|
}, {
|
|
test: kindTest(OpKind.StyleProp)
|
|
}, {
|
|
test: kindTest(OpKind.ClassProp)
|
|
}];
|
|
const handledOpKinds = new Set([OpKind.Listener, OpKind.TwoWayListener, OpKind.AnimationListener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty, OpKind.DomProperty, OpKind.Attribute, OpKind.Animation, OpKind.Control]);
|
|
function orderOps(job) {
|
|
for (const unit of job.units) {
|
|
orderWithin(unit.create, CREATE_ORDERING);
|
|
const ordering = unit.job.kind === CompilationJobKind.Host ? UPDATE_HOST_ORDERING : UPDATE_ORDERING;
|
|
orderWithin(unit.update, ordering);
|
|
}
|
|
}
|
|
function orderWithin(opList, ordering) {
|
|
let opsToOrder = [];
|
|
let firstTargetInGroup = null;
|
|
for (const op of opList) {
|
|
const currentTarget = hasDependsOnSlotContextTrait(op) ? op.target : null;
|
|
if (!handledOpKinds.has(op.kind) || currentTarget !== firstTargetInGroup && firstTargetInGroup !== null && currentTarget !== null) {
|
|
OpList.insertBefore(reorder(opsToOrder, ordering), op);
|
|
opsToOrder = [];
|
|
firstTargetInGroup = null;
|
|
}
|
|
if (handledOpKinds.has(op.kind)) {
|
|
opsToOrder.push(op);
|
|
OpList.remove(op);
|
|
firstTargetInGroup = currentTarget ?? firstTargetInGroup;
|
|
}
|
|
}
|
|
opList.push(reorder(opsToOrder, ordering));
|
|
}
|
|
function reorder(ops, ordering) {
|
|
const groups = Array.from(ordering, () => new Array());
|
|
for (const op of ops) {
|
|
const groupIndex = ordering.findIndex(o => o.test(op));
|
|
groups[groupIndex].push(op);
|
|
}
|
|
return groups.flatMap((group, i) => {
|
|
const transform = ordering[i].transform;
|
|
return transform ? transform(group) : group;
|
|
});
|
|
}
|
|
function keepLast(ops) {
|
|
return ops.slice(ops.length - 1);
|
|
}
|
|
|
|
function removeContentSelectors(job) {
|
|
for (const unit of job.units) {
|
|
const elements = createOpXrefMap(unit);
|
|
for (const op of unit.ops()) {
|
|
switch (op.kind) {
|
|
case OpKind.Binding:
|
|
const target = lookupInXrefMap(elements, op.target);
|
|
if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
|
|
OpList.remove(op);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function isSelectAttribute(name) {
|
|
return name.toLowerCase() === 'select';
|
|
}
|
|
function lookupInXrefMap(map, xref) {
|
|
const el = map.get(xref);
|
|
if (el === undefined) {
|
|
throw new Error('All attributes should have an slottable target.');
|
|
}
|
|
return el;
|
|
}
|
|
|
|
function createPipes(job) {
|
|
for (const unit of job.units) {
|
|
processPipeBindingsInView(unit);
|
|
}
|
|
}
|
|
function processPipeBindingsInView(unit) {
|
|
for (const updateOp of unit.update) {
|
|
visitExpressionsInOp(updateOp, (expr, flags) => {
|
|
if (!isIrExpression(expr)) {
|
|
return;
|
|
}
|
|
if (expr.kind !== ExpressionKind.PipeBinding) {
|
|
return;
|
|
}
|
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
|
|
}
|
|
if (unit.job.compatibility) {
|
|
const slotHandle = updateOp.target;
|
|
if (slotHandle == undefined) {
|
|
throw new Error(`AssertionError: expected slot handle to be assigned for pipe creation`);
|
|
}
|
|
addPipeToCreationBlock(unit, updateOp.target, expr);
|
|
} else {
|
|
unit.create.push(createPipeOp(expr.target, expr.targetSlot, expr.name));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function addPipeToCreationBlock(unit, afterTargetXref, binding) {
|
|
for (let op = unit.create.head.next; op.kind !== OpKind.ListEnd; op = op.next) {
|
|
if (!hasConsumesSlotTrait(op)) {
|
|
continue;
|
|
}
|
|
if (op.xref !== afterTargetXref) {
|
|
continue;
|
|
}
|
|
while (op.next.kind === OpKind.Pipe) {
|
|
op = op.next;
|
|
}
|
|
const pipe = createPipeOp(binding.target, binding.targetSlot, binding.name);
|
|
OpList.insertBefore(pipe, op.next);
|
|
return;
|
|
}
|
|
throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
|
|
}
|
|
|
|
function createVariadicPipes(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (!(expr instanceof PipeBindingExpr)) {
|
|
return expr;
|
|
}
|
|
if (expr.args.length <= 4) {
|
|
return expr;
|
|
}
|
|
return new PipeBindingVariadicExpr(expr.target, expr.targetSlot, expr.name, literalArr(expr.args), expr.args.length);
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
|
|
function propagateI18nBlocks(job) {
|
|
propagateI18nBlocksToTemplates(job.root, 0);
|
|
}
|
|
function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
let i18nBlock = null;
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
op.subTemplateIndex = subTemplateIndex === 0 ? null : subTemplateIndex;
|
|
i18nBlock = op;
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
if (i18nBlock.subTemplateIndex === null) {
|
|
subTemplateIndex = 0;
|
|
}
|
|
i18nBlock = null;
|
|
break;
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
const forView = unit.job.views.get(op.xref);
|
|
subTemplateIndex = propagateI18nBlocksForView(forView, i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
if (op.emptyView !== null) {
|
|
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
|
|
}
|
|
break;
|
|
case OpKind.Projection:
|
|
if (op.fallbackView !== null) {
|
|
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.fallbackView), i18nBlock, op.fallbackViewI18nPlaceholder, subTemplateIndex);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return subTemplateIndex;
|
|
}
|
|
function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
|
|
if (i18nPlaceholder !== undefined) {
|
|
if (i18nBlock === null) {
|
|
throw Error('Expected template with i18n placeholder to be in an i18n block.');
|
|
}
|
|
subTemplateIndex++;
|
|
wrapTemplateWithI18n(view, i18nBlock);
|
|
}
|
|
return propagateI18nBlocksToTemplates(view, subTemplateIndex);
|
|
}
|
|
function wrapTemplateWithI18n(unit, parentI18n) {
|
|
if (unit.create.head.next?.kind !== OpKind.I18nStart) {
|
|
const id = unit.job.allocateXrefId();
|
|
OpList.insertAfter(createI18nStartOp(id, parentI18n.message, parentI18n.root, null), unit.create.head);
|
|
OpList.insertBefore(createI18nEndOp(id, null), unit.create.tail);
|
|
}
|
|
}
|
|
|
|
function extractPureFunctions(job) {
|
|
for (const view of job.units) {
|
|
for (const op of view.ops()) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!(expr instanceof PureFunctionExpr) || expr.body === null) {
|
|
return;
|
|
}
|
|
const constantDef = new PureFunctionConstant(expr.args.length);
|
|
expr.fn = job.pool.getSharedConstant(constantDef, expr.body);
|
|
expr.body = null;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
class PureFunctionConstant extends GenericKeyFn {
|
|
numArgs;
|
|
constructor(numArgs) {
|
|
super();
|
|
this.numArgs = numArgs;
|
|
}
|
|
keyOf(expr) {
|
|
if (expr instanceof PureFunctionParameterExpr) {
|
|
return `param(${expr.index})`;
|
|
} else {
|
|
return super.keyOf(expr);
|
|
}
|
|
}
|
|
toSharedConstantDeclaration(declName, keyExpr) {
|
|
const fnParams = [];
|
|
for (let idx = 0; idx < this.numArgs; idx++) {
|
|
fnParams.push(new FnParam('a' + idx));
|
|
}
|
|
const returnExpr = transformExpressionsInExpression(keyExpr, expr => {
|
|
if (!(expr instanceof PureFunctionParameterExpr)) {
|
|
return expr;
|
|
}
|
|
return variable('a' + expr.index);
|
|
}, VisitorContextFlag.None);
|
|
return new DeclareVarStmt(declName, new ArrowFunctionExpr(fnParams, returnExpr), undefined, StmtModifier.Final);
|
|
}
|
|
}
|
|
|
|
function generatePureLiteralStructures(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
transformExpressionsInOp(op, (expr, flags) => {
|
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
return expr;
|
|
}
|
|
if (expr instanceof LiteralArrayExpr) {
|
|
return transformLiteralArray(expr);
|
|
} else if (expr instanceof LiteralMapExpr) {
|
|
return transformLiteralMap(expr);
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
function transformLiteralArray(expr) {
|
|
const derivedEntries = [];
|
|
const nonConstantArgs = [];
|
|
for (const entry of expr.entries) {
|
|
if (entry instanceof SpreadElementExpr) {
|
|
if (entry.expression.isConstant()) {
|
|
derivedEntries.push(entry);
|
|
} else {
|
|
const idx = nonConstantArgs.length;
|
|
nonConstantArgs.push(entry.expression);
|
|
derivedEntries.push(new SpreadElementExpr(new PureFunctionParameterExpr(idx)));
|
|
}
|
|
continue;
|
|
}
|
|
if (entry.isConstant()) {
|
|
derivedEntries.push(entry);
|
|
} else {
|
|
const idx = nonConstantArgs.length;
|
|
nonConstantArgs.push(entry);
|
|
derivedEntries.push(new PureFunctionParameterExpr(idx));
|
|
}
|
|
}
|
|
return new PureFunctionExpr(literalArr(derivedEntries), nonConstantArgs);
|
|
}
|
|
function transformLiteralMap(expr) {
|
|
let derivedEntries = [];
|
|
const nonConstantArgs = [];
|
|
for (const entry of expr.entries) {
|
|
if (entry instanceof LiteralMapSpreadAssignment) {
|
|
if (entry.expression.isConstant()) {
|
|
derivedEntries.push(entry);
|
|
} else {
|
|
const idx = nonConstantArgs.length;
|
|
nonConstantArgs.push(entry.expression);
|
|
derivedEntries.push(new LiteralMapSpreadAssignment(new PureFunctionParameterExpr(idx)));
|
|
}
|
|
continue;
|
|
}
|
|
if (entry.value.isConstant()) {
|
|
derivedEntries.push(entry);
|
|
} else {
|
|
const idx = nonConstantArgs.length;
|
|
nonConstantArgs.push(entry.value);
|
|
derivedEntries.push(new LiteralMapPropertyAssignment(entry.key, new PureFunctionParameterExpr(idx), entry.quoted));
|
|
}
|
|
}
|
|
return new PureFunctionExpr(new LiteralMapExpr(derivedEntries), nonConstantArgs);
|
|
}
|
|
|
|
function optimizeRegularExpressions(job) {
|
|
for (const view of job.units) {
|
|
for (const op of view.ops()) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof RegularExpressionLiteralExpr && (expr.flags === null || !expr.flags.includes('g'))) {
|
|
return job.pool.getSharedConstant(new RegularExpressionConstant(), expr);
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
class RegularExpressionConstant extends GenericKeyFn {
|
|
toSharedConstantDeclaration(declName, keyExpr) {
|
|
return new DeclareVarStmt(declName, keyExpr, undefined, StmtModifier.Final);
|
|
}
|
|
}
|
|
|
|
function element(slot, tag, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.element, slot, tag, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function elementStart(slot, tag, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.elementStart, slot, tag, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function elementOrContainerBase(instruction, slot, tag, constIndex, localRefIndex, sourceSpan) {
|
|
const args = [literal(slot)];
|
|
if (tag !== null) {
|
|
args.push(literal(tag));
|
|
}
|
|
if (localRefIndex !== null) {
|
|
args.push(literal(constIndex), literal(localRefIndex));
|
|
} else if (constIndex !== null) {
|
|
args.push(literal(constIndex));
|
|
}
|
|
return call(instruction, args, sourceSpan);
|
|
}
|
|
function templateBase(instruction, slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
|
|
const args = [literal(slot), templateFnRef, literal(decls), literal(vars), literal(tag), literal(constIndex)];
|
|
if (localRefs !== null) {
|
|
args.push(literal(localRefs));
|
|
args.push(importExpr(Identifiers.templateRefExtractor));
|
|
}
|
|
while (args[args.length - 1].isEquivalent(NULL_EXPR)) {
|
|
args.pop();
|
|
}
|
|
return call(instruction, args, sourceSpan);
|
|
}
|
|
function propertyBase(instruction, name, expression, sanitizer, sourceSpan) {
|
|
const args = [literal(name)];
|
|
if (expression instanceof Interpolation) {
|
|
args.push(interpolationToExpression(expression, sourceSpan));
|
|
} else {
|
|
args.push(expression);
|
|
}
|
|
if (sanitizer !== null) {
|
|
args.push(sanitizer);
|
|
}
|
|
return call(instruction, args, sourceSpan);
|
|
}
|
|
function elementEnd(sourceSpan) {
|
|
return call(Identifiers.elementEnd, [], sourceSpan);
|
|
}
|
|
function elementContainerStart(slot, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.elementContainerStart, slot, null, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function elementContainer(slot, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.elementContainer, slot, null, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function elementContainerEnd() {
|
|
return call(Identifiers.elementContainerEnd, [], null);
|
|
}
|
|
function template(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
|
|
return templateBase(Identifiers.templateCreate, slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan);
|
|
}
|
|
function disableBindings() {
|
|
return call(Identifiers.disableBindings, [], null);
|
|
}
|
|
function enableBindings() {
|
|
return call(Identifiers.enableBindings, [], null);
|
|
}
|
|
function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
|
|
const args = [literal(name), handlerFn];
|
|
if (eventTargetResolver !== null) {
|
|
args.push(importExpr(eventTargetResolver));
|
|
}
|
|
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
}
|
|
function twoWayBindingSet(target, value) {
|
|
return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]);
|
|
}
|
|
function twoWayListener(name, handlerFn, sourceSpan) {
|
|
return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan);
|
|
}
|
|
function pipe(slot, name) {
|
|
return call(Identifiers.pipe, [literal(slot), literal(name)], null);
|
|
}
|
|
function namespaceHTML() {
|
|
return call(Identifiers.namespaceHTML, [], null);
|
|
}
|
|
function namespaceSVG() {
|
|
return call(Identifiers.namespaceSVG, [], null);
|
|
}
|
|
function namespaceMath() {
|
|
return call(Identifiers.namespaceMathML, [], null);
|
|
}
|
|
function advance(delta, sourceSpan) {
|
|
return call(Identifiers.advance, delta > 1 ? [literal(delta)] : [], sourceSpan);
|
|
}
|
|
function reference(slot) {
|
|
return importExpr(Identifiers.reference).callFn([literal(slot)]);
|
|
}
|
|
function nextContext(steps) {
|
|
return importExpr(Identifiers.nextContext).callFn(steps === 1 ? [] : [literal(steps)]);
|
|
}
|
|
function getCurrentView() {
|
|
return importExpr(Identifiers.getCurrentView).callFn([]);
|
|
}
|
|
function restoreView(savedView) {
|
|
return importExpr(Identifiers.restoreView).callFn([savedView]);
|
|
}
|
|
function resetView(returnValue) {
|
|
return importExpr(Identifiers.resetView).callFn([returnValue]);
|
|
}
|
|
function text(slot, initialValue, sourceSpan) {
|
|
const args = [literal(slot, null)];
|
|
if (initialValue !== '') {
|
|
args.push(literal(initialValue));
|
|
}
|
|
return call(Identifiers.text, args, sourceSpan);
|
|
}
|
|
function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan, flags) {
|
|
const args = [literal(selfSlot), literal(primarySlot), dependencyResolverFn ?? literal(null), literal(loadingSlot), literal(placeholderSlot), literal(errorSlot), loadingConfig ?? literal(null), placeholderConfig ?? literal(null), enableTimerScheduling ? importExpr(Identifiers.deferEnableTimerScheduling) : literal(null), literal(flags)];
|
|
let expr;
|
|
while ((expr = args[args.length - 1]) !== null && expr instanceof LiteralExpr && expr.value === null) {
|
|
args.pop();
|
|
}
|
|
return call(Identifiers.defer, args, sourceSpan);
|
|
}
|
|
const deferTriggerToR3TriggerInstructionsMap = new Map([[DeferTriggerKind.Idle, {
|
|
["none"]: Identifiers.deferOnIdle,
|
|
["prefetch"]: Identifiers.deferPrefetchOnIdle,
|
|
["hydrate"]: Identifiers.deferHydrateOnIdle
|
|
}], [DeferTriggerKind.Immediate, {
|
|
["none"]: Identifiers.deferOnImmediate,
|
|
["prefetch"]: Identifiers.deferPrefetchOnImmediate,
|
|
["hydrate"]: Identifiers.deferHydrateOnImmediate
|
|
}], [DeferTriggerKind.Timer, {
|
|
["none"]: Identifiers.deferOnTimer,
|
|
["prefetch"]: Identifiers.deferPrefetchOnTimer,
|
|
["hydrate"]: Identifiers.deferHydrateOnTimer
|
|
}], [DeferTriggerKind.Hover, {
|
|
["none"]: Identifiers.deferOnHover,
|
|
["prefetch"]: Identifiers.deferPrefetchOnHover,
|
|
["hydrate"]: Identifiers.deferHydrateOnHover
|
|
}], [DeferTriggerKind.Interaction, {
|
|
["none"]: Identifiers.deferOnInteraction,
|
|
["prefetch"]: Identifiers.deferPrefetchOnInteraction,
|
|
["hydrate"]: Identifiers.deferHydrateOnInteraction
|
|
}], [DeferTriggerKind.Viewport, {
|
|
["none"]: Identifiers.deferOnViewport,
|
|
["prefetch"]: Identifiers.deferPrefetchOnViewport,
|
|
["hydrate"]: Identifiers.deferHydrateOnViewport
|
|
}], [DeferTriggerKind.Never, {
|
|
["none"]: Identifiers.deferHydrateNever,
|
|
["prefetch"]: Identifiers.deferHydrateNever,
|
|
["hydrate"]: Identifiers.deferHydrateNever
|
|
}]]);
|
|
function deferOn(trigger, args, modifier, sourceSpan) {
|
|
const instructionToCall = deferTriggerToR3TriggerInstructionsMap.get(trigger)?.[modifier];
|
|
if (instructionToCall === undefined) {
|
|
throw new Error(`Unable to determine instruction for trigger ${trigger}`);
|
|
}
|
|
return call(instructionToCall, args, sourceSpan);
|
|
}
|
|
function projectionDef(def) {
|
|
return call(Identifiers.projectionDef, def ? [def] : [], null);
|
|
}
|
|
function projection(slot, projectionSlotIndex, attributes, fallbackFnName, fallbackDecls, fallbackVars, sourceSpan) {
|
|
const args = [literal(slot)];
|
|
if (projectionSlotIndex !== 0 || attributes !== null || fallbackFnName !== null) {
|
|
args.push(literal(projectionSlotIndex));
|
|
if (attributes !== null) {
|
|
args.push(attributes);
|
|
}
|
|
if (fallbackFnName !== null) {
|
|
if (attributes === null) {
|
|
args.push(literal(null));
|
|
}
|
|
args.push(variable(fallbackFnName), literal(fallbackDecls), literal(fallbackVars));
|
|
}
|
|
}
|
|
return call(Identifiers.projection, args, sourceSpan);
|
|
}
|
|
function i18nStart(slot, constIndex, subTemplateIndex, sourceSpan) {
|
|
const args = [literal(slot), literal(constIndex)];
|
|
if (subTemplateIndex !== null) {
|
|
args.push(literal(subTemplateIndex));
|
|
}
|
|
return call(Identifiers.i18nStart, args, sourceSpan);
|
|
}
|
|
function conditionalCreate(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
|
|
const args = [literal(slot), templateFnRef, literal(decls), literal(vars), literal(tag), literal(constIndex)];
|
|
if (localRefs !== null) {
|
|
args.push(literal(localRefs));
|
|
args.push(importExpr(Identifiers.templateRefExtractor));
|
|
}
|
|
while (args[args.length - 1].isEquivalent(NULL_EXPR)) {
|
|
args.pop();
|
|
}
|
|
return call(Identifiers.conditionalCreate, args, sourceSpan);
|
|
}
|
|
function conditionalBranchCreate(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
|
|
const args = [literal(slot), templateFnRef, literal(decls), literal(vars), literal(tag), literal(constIndex)];
|
|
if (localRefs !== null) {
|
|
args.push(literal(localRefs));
|
|
args.push(importExpr(Identifiers.templateRefExtractor));
|
|
}
|
|
while (args[args.length - 1].isEquivalent(NULL_EXPR)) {
|
|
args.pop();
|
|
}
|
|
return call(Identifiers.conditionalBranchCreate, args, sourceSpan);
|
|
}
|
|
function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, emptyTag, emptyConstIndex, sourceSpan) {
|
|
const args = [literal(slot), variable(viewFnName), literal(decls), literal(vars), literal(tag), literal(constIndex), trackByFn];
|
|
if (trackByUsesComponentInstance || emptyViewFnName !== null) {
|
|
args.push(literal(trackByUsesComponentInstance));
|
|
if (emptyViewFnName !== null) {
|
|
args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
|
|
if (emptyTag !== null || emptyConstIndex !== null) {
|
|
args.push(literal(emptyTag));
|
|
}
|
|
if (emptyConstIndex !== null) {
|
|
args.push(literal(emptyConstIndex));
|
|
}
|
|
}
|
|
}
|
|
return call(Identifiers.repeaterCreate, args, sourceSpan);
|
|
}
|
|
function repeater(collection, sourceSpan) {
|
|
return call(Identifiers.repeater, [collection], sourceSpan);
|
|
}
|
|
function deferWhen(modifier, expr, sourceSpan) {
|
|
if (modifier === "prefetch") {
|
|
return call(Identifiers.deferPrefetchWhen, [expr], sourceSpan);
|
|
} else if (modifier === "hydrate") {
|
|
return call(Identifiers.deferHydrateWhen, [expr], sourceSpan);
|
|
}
|
|
return call(Identifiers.deferWhen, [expr], sourceSpan);
|
|
}
|
|
function declareLet(slot, sourceSpan) {
|
|
return call(Identifiers.declareLet, [literal(slot)], sourceSpan);
|
|
}
|
|
function storeLet(value, sourceSpan) {
|
|
return importExpr(Identifiers.storeLet).callFn([value], sourceSpan);
|
|
}
|
|
function readContextLet(slot) {
|
|
return importExpr(Identifiers.readContextLet).callFn([literal(slot)]);
|
|
}
|
|
function i18n(slot, constIndex, subTemplateIndex, sourceSpan) {
|
|
const args = [literal(slot), literal(constIndex)];
|
|
if (subTemplateIndex) {
|
|
args.push(literal(subTemplateIndex));
|
|
}
|
|
return call(Identifiers.i18n, args, sourceSpan);
|
|
}
|
|
function i18nEnd(endSourceSpan) {
|
|
return call(Identifiers.i18nEnd, [], endSourceSpan);
|
|
}
|
|
function i18nAttributes(slot, i18nAttributesConfig) {
|
|
const args = [literal(slot), literal(i18nAttributesConfig)];
|
|
return call(Identifiers.i18nAttributes, args, null);
|
|
}
|
|
function ariaProperty(name, expression, sourceSpan) {
|
|
return propertyBase(Identifiers.ariaProperty, name, expression, null, sourceSpan);
|
|
}
|
|
function property(name, expression, sanitizer, sourceSpan) {
|
|
return propertyBase(Identifiers.property, name, expression, sanitizer, sourceSpan);
|
|
}
|
|
function control(name, expression, sanitizer, sourceSpan) {
|
|
const args = [];
|
|
if (expression instanceof Interpolation) {
|
|
args.push(interpolationToExpression(expression, sourceSpan));
|
|
} else {
|
|
args.push(expression);
|
|
}
|
|
args.push(literal(name));
|
|
if (sanitizer !== null) {
|
|
args.push(sanitizer);
|
|
}
|
|
return call(Identifiers.control, args, sourceSpan);
|
|
}
|
|
function controlCreate(sourceSpan) {
|
|
return call(Identifiers.controlCreate, [], sourceSpan);
|
|
}
|
|
function twoWayProperty(name, expression, sanitizer, sourceSpan) {
|
|
const args = [literal(name), expression];
|
|
if (sanitizer !== null) {
|
|
args.push(sanitizer);
|
|
}
|
|
return call(Identifiers.twoWayProperty, args, sourceSpan);
|
|
}
|
|
function attribute(name, expression, sanitizer, namespace, sourceSpan) {
|
|
const args = [literal(name)];
|
|
if (expression instanceof Interpolation) {
|
|
args.push(interpolationToExpression(expression, sourceSpan));
|
|
} else {
|
|
args.push(expression);
|
|
}
|
|
if (sanitizer !== null || namespace !== null) {
|
|
args.push(sanitizer ?? literal(null));
|
|
}
|
|
if (namespace !== null) {
|
|
args.push(literal(namespace));
|
|
}
|
|
return call(Identifiers.attribute, args, null);
|
|
}
|
|
function styleProp(name, expression, unit, sourceSpan) {
|
|
const args = [literal(name)];
|
|
if (expression instanceof Interpolation) {
|
|
args.push(interpolationToExpression(expression, sourceSpan));
|
|
} else {
|
|
args.push(expression);
|
|
}
|
|
if (unit !== null) {
|
|
args.push(literal(unit));
|
|
}
|
|
return call(Identifiers.styleProp, args, sourceSpan);
|
|
}
|
|
function classProp(name, expression, sourceSpan) {
|
|
return call(Identifiers.classProp, [literal(name), expression], sourceSpan);
|
|
}
|
|
function styleMap(expression, sourceSpan) {
|
|
const value = expression instanceof Interpolation ? interpolationToExpression(expression, sourceSpan) : expression;
|
|
return call(Identifiers.styleMap, [value], sourceSpan);
|
|
}
|
|
function classMap(expression, sourceSpan) {
|
|
const value = expression instanceof Interpolation ? interpolationToExpression(expression, sourceSpan) : expression;
|
|
return call(Identifiers.classMap, [value], sourceSpan);
|
|
}
|
|
function domElement(slot, tag, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.domElement, slot, tag, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function domElementStart(slot, tag, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.domElementStart, slot, tag, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function domElementEnd(sourceSpan) {
|
|
return call(Identifiers.domElementEnd, [], sourceSpan);
|
|
}
|
|
function domElementContainerStart(slot, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.domElementContainerStart, slot, null, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function domElementContainer(slot, constIndex, localRefIndex, sourceSpan) {
|
|
return elementOrContainerBase(Identifiers.domElementContainer, slot, null, constIndex, localRefIndex, sourceSpan);
|
|
}
|
|
function domElementContainerEnd() {
|
|
return call(Identifiers.domElementContainerEnd, [], null);
|
|
}
|
|
function domListener(name, handlerFn, eventTargetResolver, sourceSpan) {
|
|
const args = [literal(name), handlerFn];
|
|
if (eventTargetResolver !== null) {
|
|
args.push(importExpr(eventTargetResolver));
|
|
}
|
|
return call(Identifiers.domListener, args, sourceSpan);
|
|
}
|
|
function domTemplate(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
|
|
return templateBase(Identifiers.domTemplate, slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan);
|
|
}
|
|
const PIPE_BINDINGS = [Identifiers.pipeBind1, Identifiers.pipeBind2, Identifiers.pipeBind3, Identifiers.pipeBind4];
|
|
function pipeBind(slot, varOffset, args) {
|
|
if (args.length < 1 || args.length > PIPE_BINDINGS.length) {
|
|
throw new Error(`pipeBind() argument count out of bounds`);
|
|
}
|
|
const instruction = PIPE_BINDINGS[args.length - 1];
|
|
return importExpr(instruction).callFn([literal(slot), literal(varOffset), ...args]);
|
|
}
|
|
function pipeBindV(slot, varOffset, args) {
|
|
return importExpr(Identifiers.pipeBindV).callFn([literal(slot), literal(varOffset), args]);
|
|
}
|
|
function textInterpolate(strings, expressions, sourceSpan) {
|
|
const interpolationArgs = collateInterpolationArgs(strings, expressions);
|
|
return callVariadicInstruction(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs, sourceSpan);
|
|
}
|
|
function i18nExp(expr, sourceSpan) {
|
|
return call(Identifiers.i18nExp, [expr], sourceSpan);
|
|
}
|
|
function i18nApply(slot, sourceSpan) {
|
|
return call(Identifiers.i18nApply, [literal(slot)], sourceSpan);
|
|
}
|
|
function domProperty(name, expression, sanitizer, sourceSpan) {
|
|
return propertyBase(Identifiers.domProperty, name, expression, sanitizer, sourceSpan);
|
|
}
|
|
function animation(animationKind, handlerFn, sanitizer, sourceSpan) {
|
|
const args = [handlerFn];
|
|
if (sanitizer !== null) {
|
|
args.push(sanitizer);
|
|
}
|
|
const identifier = animationKind === "enter" ? Identifiers.animationEnter : Identifiers.animationLeave;
|
|
return call(identifier, args, sourceSpan);
|
|
}
|
|
function animationString(animationKind, expression, sanitizer, sourceSpan) {
|
|
const value = expression instanceof Interpolation ? interpolationToExpression(expression, sourceSpan) : expression;
|
|
const args = [value];
|
|
if (sanitizer !== null) {
|
|
args.push(sanitizer);
|
|
}
|
|
const identifier = animationKind === "enter" ? Identifiers.animationEnter : Identifiers.animationLeave;
|
|
return call(identifier, args, sourceSpan);
|
|
}
|
|
function animationListener(animationKind, handlerFn, eventTargetResolver, sourceSpan) {
|
|
const args = [handlerFn];
|
|
const identifier = animationKind === "enter" ? Identifiers.animationEnterListener : Identifiers.animationLeaveListener;
|
|
return call(identifier, args, sourceSpan);
|
|
}
|
|
function syntheticHostProperty(name, expression, sourceSpan) {
|
|
return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
|
|
}
|
|
function pureFunction(varOffset, fn, args) {
|
|
return callVariadicInstructionExpr(PURE_FUNCTION_CONFIG, [literal(varOffset), fn], args, null);
|
|
}
|
|
function attachSourceLocation(templatePath, locations) {
|
|
return call(Identifiers.attachSourceLocations, [literal(templatePath), locations], null);
|
|
}
|
|
function collateInterpolationArgs(strings, expressions) {
|
|
if (strings.length < 1 || expressions.length !== strings.length - 1) {
|
|
throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
|
|
}
|
|
const interpolationArgs = [];
|
|
if (expressions.length === 1 && strings[0] === '' && strings[1] === '') {
|
|
interpolationArgs.push(expressions[0]);
|
|
} else {
|
|
let idx;
|
|
for (idx = 0; idx < expressions.length; idx++) {
|
|
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
}
|
|
interpolationArgs.push(literal(strings[idx]));
|
|
}
|
|
return interpolationArgs;
|
|
}
|
|
function interpolationToExpression(interpolation, sourceSpan) {
|
|
const interpolationArgs = collateInterpolationArgs(interpolation.strings, interpolation.expressions);
|
|
return callVariadicInstructionExpr(VALUE_INTERPOLATE_CONFIG, [], interpolationArgs, sourceSpan);
|
|
}
|
|
function call(instruction, args, sourceSpan) {
|
|
const expr = importExpr(instruction).callFn(args, sourceSpan);
|
|
return createStatementOp(new ExpressionStatement(expr, sourceSpan));
|
|
}
|
|
function conditional(condition, contextValue, sourceSpan) {
|
|
const args = [condition];
|
|
if (contextValue !== null) {
|
|
args.push(contextValue);
|
|
}
|
|
return call(Identifiers.conditional, args, sourceSpan);
|
|
}
|
|
const TEXT_INTERPOLATE_CONFIG = {
|
|
constant: [Identifiers.textInterpolate, Identifiers.textInterpolate1, Identifiers.textInterpolate2, Identifiers.textInterpolate3, Identifiers.textInterpolate4, Identifiers.textInterpolate5, Identifiers.textInterpolate6, Identifiers.textInterpolate7, Identifiers.textInterpolate8],
|
|
variable: Identifiers.textInterpolateV,
|
|
mapping: n => {
|
|
if (n % 2 === 0) {
|
|
throw new Error(`Expected odd number of arguments`);
|
|
}
|
|
return (n - 1) / 2;
|
|
}
|
|
};
|
|
const VALUE_INTERPOLATE_CONFIG = {
|
|
constant: [Identifiers.interpolate, Identifiers.interpolate1, Identifiers.interpolate2, Identifiers.interpolate3, Identifiers.interpolate4, Identifiers.interpolate5, Identifiers.interpolate6, Identifiers.interpolate7, Identifiers.interpolate8],
|
|
variable: Identifiers.interpolateV,
|
|
mapping: n => {
|
|
if (n % 2 === 0) {
|
|
throw new Error(`Expected odd number of arguments`);
|
|
}
|
|
return (n - 1) / 2;
|
|
}
|
|
};
|
|
const PURE_FUNCTION_CONFIG = {
|
|
constant: [Identifiers.pureFunction0, Identifiers.pureFunction1, Identifiers.pureFunction2, Identifiers.pureFunction3, Identifiers.pureFunction4, Identifiers.pureFunction5, Identifiers.pureFunction6, Identifiers.pureFunction7, Identifiers.pureFunction8],
|
|
variable: Identifiers.pureFunctionV,
|
|
mapping: n => n
|
|
};
|
|
function callVariadicInstructionExpr(config, baseArgs, interpolationArgs, sourceSpan) {
|
|
const n = config.mapping(interpolationArgs.length);
|
|
const lastInterpolationArg = interpolationArgs.at(-1);
|
|
if (interpolationArgs.length > 1 && lastInterpolationArg instanceof LiteralExpr && lastInterpolationArg.value === '') {
|
|
interpolationArgs.pop();
|
|
}
|
|
if (n < config.constant.length) {
|
|
return importExpr(config.constant[n]).callFn([...baseArgs, ...interpolationArgs], sourceSpan);
|
|
} else if (config.variable !== null) {
|
|
return importExpr(config.variable).callFn([...baseArgs, literalArr(interpolationArgs)], sourceSpan);
|
|
} else {
|
|
throw new Error(`AssertionError: unable to call variadic function`);
|
|
}
|
|
}
|
|
function callVariadicInstruction(config, baseArgs, interpolationArgs, sourceSpan) {
|
|
return createStatementOp(callVariadicInstructionExpr(config, baseArgs, interpolationArgs, sourceSpan).toStmt());
|
|
}
|
|
|
|
const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers.resolveWindow], ['document', Identifiers.resolveDocument], ['body', Identifiers.resolveBody]]);
|
|
const DOM_PROPERTY_REMAPPING = new Map([['class', 'className'], ['for', 'htmlFor'], ['formaction', 'formAction'], ['innerHtml', 'innerHTML'], ['readonly', 'readOnly'], ['tabindex', 'tabIndex']]);
|
|
function reify(job) {
|
|
for (const unit of job.units) {
|
|
reifyCreateOperations(unit, unit.create);
|
|
reifyUpdateOperations(unit, unit.update);
|
|
}
|
|
}
|
|
function reifyCreateOperations(unit, ops) {
|
|
for (const op of ops) {
|
|
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
switch (op.kind) {
|
|
case OpKind.Text:
|
|
OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
|
|
break;
|
|
case OpKind.ElementStart:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan) : elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
break;
|
|
case OpKind.Element:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElement(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan) : element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
|
|
break;
|
|
case OpKind.ElementEnd:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElementEnd(op.sourceSpan) : elementEnd(op.sourceSpan));
|
|
break;
|
|
case OpKind.ContainerStart:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan) : elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
|
|
break;
|
|
case OpKind.Container:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan) : elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
|
|
break;
|
|
case OpKind.ContainerEnd:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly ? domElementContainerEnd() : elementContainerEnd());
|
|
break;
|
|
case OpKind.I18nStart:
|
|
OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
OpList.replace(op, i18nEnd(op.sourceSpan));
|
|
break;
|
|
case OpKind.I18n:
|
|
OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
|
|
break;
|
|
case OpKind.I18nAttributes:
|
|
if (op.i18nAttributesConfig === null) {
|
|
throw new Error(`AssertionError: i18nAttributesConfig was not set`);
|
|
}
|
|
OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
|
|
break;
|
|
case OpKind.Template:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
if (Array.isArray(op.localRefs)) {
|
|
throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
|
|
}
|
|
const childView = unit.job.views.get(op.xref);
|
|
OpList.replace(op, op.templateKind === TemplateKind.Block || unit.job.mode === TemplateCompilationMode.DomOnly ? domTemplate(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan) : template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
break;
|
|
case OpKind.DisableBindings:
|
|
OpList.replace(op, disableBindings());
|
|
break;
|
|
case OpKind.EnableBindings:
|
|
OpList.replace(op, enableBindings());
|
|
break;
|
|
case OpKind.Pipe:
|
|
OpList.replace(op, pipe(op.handle.slot, op.name));
|
|
break;
|
|
case OpKind.DeclareLet:
|
|
OpList.replace(op, declareLet(op.handle.slot, op.sourceSpan));
|
|
break;
|
|
case OpKind.AnimationString:
|
|
OpList.replace(op, animationString(op.animationKind, op.expression, op.sanitizer, op.sourceSpan));
|
|
break;
|
|
case OpKind.Animation:
|
|
const animationCallbackFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, false);
|
|
OpList.replace(op, animation(op.animationKind, animationCallbackFn, op.sanitizer, op.sourceSpan));
|
|
break;
|
|
case OpKind.AnimationListener:
|
|
const animationListenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
|
|
OpList.replace(op, animationListener(op.animationKind, animationListenerFn, null, op.sourceSpan));
|
|
break;
|
|
case OpKind.Listener:
|
|
const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
|
|
const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS.get(op.eventTarget) : null;
|
|
if (eventTargetResolver === undefined) {
|
|
throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
|
|
}
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly && !op.hostListener && !op.isLegacyAnimationListener ? domListener(op.name, listenerFn, eventTargetResolver, op.sourceSpan) : listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isLegacyAnimationListener, op.sourceSpan));
|
|
break;
|
|
case OpKind.TwoWayListener:
|
|
OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan));
|
|
break;
|
|
case OpKind.Variable:
|
|
if (op.variable.name === null) {
|
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
}
|
|
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
break;
|
|
case OpKind.Namespace:
|
|
switch (op.active) {
|
|
case Namespace.HTML:
|
|
OpList.replace(op, namespaceHTML());
|
|
break;
|
|
case Namespace.SVG:
|
|
OpList.replace(op, namespaceSVG());
|
|
break;
|
|
case Namespace.Math:
|
|
OpList.replace(op, namespaceMath());
|
|
break;
|
|
}
|
|
break;
|
|
case OpKind.Defer:
|
|
const timerScheduling = !!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime;
|
|
OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, op.resolverFn, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan, op.flags));
|
|
break;
|
|
case OpKind.DeferOn:
|
|
let args = [];
|
|
switch (op.trigger.kind) {
|
|
case DeferTriggerKind.Never:
|
|
case DeferTriggerKind.Idle:
|
|
case DeferTriggerKind.Immediate:
|
|
break;
|
|
case DeferTriggerKind.Timer:
|
|
args = [literal(op.trigger.delay)];
|
|
break;
|
|
case DeferTriggerKind.Viewport:
|
|
if (op.modifier === "hydrate") {
|
|
args = op.trigger.options ? [op.trigger.options] : [];
|
|
} else {
|
|
args = [literal(op.trigger.targetSlot?.slot ?? null)];
|
|
if (op.trigger.targetSlotViewSteps !== 0) {
|
|
args.push(literal(op.trigger.targetSlotViewSteps));
|
|
} else if (op.trigger.options) {
|
|
args.push(literal(null));
|
|
}
|
|
if (op.trigger.options) {
|
|
args.push(op.trigger.options);
|
|
}
|
|
}
|
|
break;
|
|
case DeferTriggerKind.Interaction:
|
|
case DeferTriggerKind.Hover:
|
|
if (op.modifier === "hydrate") {
|
|
args = [];
|
|
} else {
|
|
args = [literal(op.trigger.targetSlot?.slot ?? null)];
|
|
if (op.trigger.targetSlotViewSteps !== 0) {
|
|
args.push(literal(op.trigger.targetSlotViewSteps));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`AssertionError: Unsupported reification of defer trigger kind ${op.trigger.kind}`);
|
|
}
|
|
OpList.replace(op, deferOn(op.trigger.kind, args, op.modifier, op.sourceSpan));
|
|
break;
|
|
case OpKind.ProjectionDef:
|
|
OpList.replace(op, projectionDef(op.def));
|
|
break;
|
|
case OpKind.Projection:
|
|
if (op.handle.slot === null) {
|
|
throw new Error('No slot was assigned for project instruction');
|
|
}
|
|
let fallbackViewFnName = null;
|
|
let fallbackDecls = null;
|
|
let fallbackVars = null;
|
|
if (op.fallbackView !== null) {
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
const fallbackView = unit.job.views.get(op.fallbackView);
|
|
if (fallbackView === undefined) {
|
|
throw new Error('AssertionError: projection had fallback view xref, but fallback view was not found');
|
|
}
|
|
if (fallbackView.fnName === null || fallbackView.decls === null || fallbackView.vars === null) {
|
|
throw new Error(`AssertionError: expected projection fallback view to have been named and counted`);
|
|
}
|
|
fallbackViewFnName = fallbackView.fnName;
|
|
fallbackDecls = fallbackView.decls;
|
|
fallbackVars = fallbackView.vars;
|
|
}
|
|
OpList.replace(op, projection(op.handle.slot, op.projectionSlotIndex, op.attributes, fallbackViewFnName, fallbackDecls, fallbackVars, op.sourceSpan));
|
|
break;
|
|
case OpKind.ConditionalCreate:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
if (Array.isArray(op.localRefs)) {
|
|
throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
|
|
}
|
|
const conditionalCreateChildView = unit.job.views.get(op.xref);
|
|
OpList.replace(op, conditionalCreate(op.handle.slot, variable(conditionalCreateChildView.fnName), conditionalCreateChildView.decls, conditionalCreateChildView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
break;
|
|
case OpKind.ConditionalBranchCreate:
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
if (Array.isArray(op.localRefs)) {
|
|
throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
|
|
}
|
|
const conditionalBranchCreateChildView = unit.job.views.get(op.xref);
|
|
OpList.replace(op, conditionalBranchCreate(op.handle.slot, variable(conditionalBranchCreateChildView.fnName), conditionalBranchCreateChildView.decls, conditionalBranchCreateChildView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (op.handle.slot === null) {
|
|
throw new Error('No slot was assigned for repeater instruction');
|
|
}
|
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
}
|
|
const repeaterView = unit.job.views.get(op.xref);
|
|
if (repeaterView.fnName === null) {
|
|
throw new Error(`AssertionError: expected repeater primary view to have been named`);
|
|
}
|
|
let emptyViewFnName = null;
|
|
let emptyDecls = null;
|
|
let emptyVars = null;
|
|
if (op.emptyView !== null) {
|
|
const emptyView = unit.job.views.get(op.emptyView);
|
|
if (emptyView === undefined) {
|
|
throw new Error('AssertionError: repeater had empty view xref, but empty view was not found');
|
|
}
|
|
if (emptyView.fnName === null || emptyView.decls === null || emptyView.vars === null) {
|
|
throw new Error(`AssertionError: expected repeater empty view to have been named and counted`);
|
|
}
|
|
emptyViewFnName = emptyView.fnName;
|
|
emptyDecls = emptyView.decls;
|
|
emptyVars = emptyView.vars;
|
|
}
|
|
OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, reifyTrackBy(unit, op), op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.emptyTag, op.emptyAttributes, op.wholeSourceSpan));
|
|
break;
|
|
case OpKind.SourceLocation:
|
|
const locationsLiteral = literalArr(op.locations.map(({
|
|
targetSlot,
|
|
offset,
|
|
line,
|
|
column
|
|
}) => {
|
|
if (targetSlot.slot === null) {
|
|
throw new Error('No slot was assigned for source location');
|
|
}
|
|
return literalArr([literal(targetSlot.slot), literal(offset), literal(line), literal(column)]);
|
|
}));
|
|
OpList.replace(op, attachSourceLocation(op.templatePath, locationsLiteral));
|
|
break;
|
|
case OpKind.ControlCreate:
|
|
OpList.replace(op, controlCreate(op.sourceSpan));
|
|
break;
|
|
case OpKind.Statement:
|
|
break;
|
|
default:
|
|
throw new Error(`AssertionError: Unsupported reification of create op ${OpKind[op.kind]}`);
|
|
}
|
|
}
|
|
}
|
|
function reifyUpdateOperations(unit, ops) {
|
|
for (const op of ops) {
|
|
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
switch (op.kind) {
|
|
case OpKind.Advance:
|
|
OpList.replace(op, advance(op.delta, op.sourceSpan));
|
|
break;
|
|
case OpKind.Property:
|
|
OpList.replace(op, unit.job.mode === TemplateCompilationMode.DomOnly && op.bindingKind !== BindingKind.LegacyAnimation && op.bindingKind !== BindingKind.Animation ? reifyDomProperty(op) : reifyProperty(op));
|
|
break;
|
|
case OpKind.Control:
|
|
OpList.replace(op, reifyControl(op));
|
|
break;
|
|
case OpKind.TwoWayProperty:
|
|
OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
break;
|
|
case OpKind.StyleProp:
|
|
OpList.replace(op, styleProp(op.name, op.expression, op.unit, op.sourceSpan));
|
|
break;
|
|
case OpKind.ClassProp:
|
|
OpList.replace(op, classProp(op.name, op.expression, op.sourceSpan));
|
|
break;
|
|
case OpKind.StyleMap:
|
|
OpList.replace(op, styleMap(op.expression, op.sourceSpan));
|
|
break;
|
|
case OpKind.ClassMap:
|
|
OpList.replace(op, classMap(op.expression, op.sourceSpan));
|
|
break;
|
|
case OpKind.I18nExpression:
|
|
OpList.replace(op, i18nExp(op.expression, op.sourceSpan));
|
|
break;
|
|
case OpKind.I18nApply:
|
|
OpList.replace(op, i18nApply(op.handle.slot, op.sourceSpan));
|
|
break;
|
|
case OpKind.InterpolateText:
|
|
OpList.replace(op, textInterpolate(op.interpolation.strings, op.interpolation.expressions, op.sourceSpan));
|
|
break;
|
|
case OpKind.Attribute:
|
|
OpList.replace(op, attribute(op.name, op.expression, op.sanitizer, op.namespace, op.sourceSpan));
|
|
break;
|
|
case OpKind.DomProperty:
|
|
if (op.expression instanceof Interpolation) {
|
|
throw new Error('not yet handled');
|
|
} else {
|
|
if (op.bindingKind === BindingKind.LegacyAnimation || op.bindingKind === BindingKind.Animation) {
|
|
OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
|
|
} else {
|
|
OpList.replace(op, reifyDomProperty(op));
|
|
}
|
|
}
|
|
break;
|
|
case OpKind.Variable:
|
|
if (op.variable.name === null) {
|
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
}
|
|
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
break;
|
|
case OpKind.Conditional:
|
|
if (op.processed === null) {
|
|
throw new Error(`Conditional test was not set.`);
|
|
}
|
|
OpList.replace(op, conditional(op.processed, op.contextValue, op.sourceSpan));
|
|
break;
|
|
case OpKind.Repeater:
|
|
OpList.replace(op, repeater(op.collection, op.sourceSpan));
|
|
break;
|
|
case OpKind.DeferWhen:
|
|
OpList.replace(op, deferWhen(op.modifier, op.expr, op.sourceSpan));
|
|
break;
|
|
case OpKind.StoreLet:
|
|
throw new Error(`AssertionError: unexpected storeLet ${op.declaredName}`);
|
|
case OpKind.Statement:
|
|
break;
|
|
default:
|
|
throw new Error(`AssertionError: Unsupported reification of update op ${OpKind[op.kind]}`);
|
|
}
|
|
}
|
|
}
|
|
function reifyDomProperty(op) {
|
|
return domProperty(DOM_PROPERTY_REMAPPING.get(op.name) ?? op.name, op.expression, op.sanitizer, op.sourceSpan);
|
|
}
|
|
function reifyProperty(op) {
|
|
return isAriaAttribute(op.name) ? ariaProperty(op.name, op.expression, op.sourceSpan) : property(op.name, op.expression, op.sanitizer, op.sourceSpan);
|
|
}
|
|
function reifyControl(op) {
|
|
return control(op.name, op.expression, op.sanitizer, op.sourceSpan);
|
|
}
|
|
function reifyIrExpression(expr) {
|
|
if (!isIrExpression(expr)) {
|
|
return expr;
|
|
}
|
|
switch (expr.kind) {
|
|
case ExpressionKind.NextContext:
|
|
return nextContext(expr.steps);
|
|
case ExpressionKind.Reference:
|
|
return reference(expr.targetSlot.slot + 1 + expr.offset);
|
|
case ExpressionKind.LexicalRead:
|
|
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
case ExpressionKind.TwoWayBindingSet:
|
|
throw new Error(`AssertionError: unresolved TwoWayBindingSet`);
|
|
case ExpressionKind.RestoreView:
|
|
if (typeof expr.view === 'number') {
|
|
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
}
|
|
return restoreView(expr.view);
|
|
case ExpressionKind.ResetView:
|
|
return resetView(expr.expr);
|
|
case ExpressionKind.GetCurrentView:
|
|
return getCurrentView();
|
|
case ExpressionKind.ReadVariable:
|
|
if (expr.name === null) {
|
|
throw new Error(`Read of unnamed variable ${expr.xref}`);
|
|
}
|
|
return variable(expr.name);
|
|
case ExpressionKind.ReadTemporaryExpr:
|
|
if (expr.name === null) {
|
|
throw new Error(`Read of unnamed temporary ${expr.xref}`);
|
|
}
|
|
return variable(expr.name);
|
|
case ExpressionKind.AssignTemporaryExpr:
|
|
if (expr.name === null) {
|
|
throw new Error(`Assign of unnamed temporary ${expr.xref}`);
|
|
}
|
|
return variable(expr.name).set(expr.expr);
|
|
case ExpressionKind.PureFunctionExpr:
|
|
if (expr.fn === null) {
|
|
throw new Error(`AssertionError: expected PureFunctions to have been extracted`);
|
|
}
|
|
return pureFunction(expr.varOffset, expr.fn, expr.args);
|
|
case ExpressionKind.PureFunctionParameterExpr:
|
|
throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
|
|
case ExpressionKind.PipeBinding:
|
|
return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
case ExpressionKind.PipeBindingVariadic:
|
|
return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
case ExpressionKind.SlotLiteralExpr:
|
|
return literal(expr.slot.slot);
|
|
case ExpressionKind.ContextLetReference:
|
|
return readContextLet(expr.targetSlot.slot);
|
|
case ExpressionKind.StoreLet:
|
|
return storeLet(expr.value, expr.sourceSpan);
|
|
case ExpressionKind.TrackContext:
|
|
return variable('this');
|
|
default:
|
|
throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
|
|
}
|
|
}
|
|
function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) {
|
|
reifyUpdateOperations(unit, handlerOps);
|
|
const handlerStmts = [];
|
|
for (const op of handlerOps) {
|
|
if (op.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected reified statements, but found op ${OpKind[op.kind]}`);
|
|
}
|
|
handlerStmts.push(op.statement);
|
|
}
|
|
const params = [];
|
|
if (consumesDollarEvent) {
|
|
params.push(new FnParam('$event'));
|
|
}
|
|
return fn(params, handlerStmts, undefined, undefined, name);
|
|
}
|
|
function reifyTrackBy(unit, op) {
|
|
if (op.trackByFn !== null) {
|
|
return op.trackByFn;
|
|
}
|
|
const params = [new FnParam('$index'), new FnParam('$item')];
|
|
let fn$1;
|
|
if (op.trackByOps === null) {
|
|
fn$1 = op.usesComponentInstance ? fn(params, [new ReturnStatement(op.track)]) : arrowFn(params, op.track);
|
|
} else {
|
|
reifyUpdateOperations(unit, op.trackByOps);
|
|
const statements = [];
|
|
for (const trackOp of op.trackByOps) {
|
|
if (trackOp.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected reified statements, but found op ${OpKind[trackOp.kind]}`);
|
|
}
|
|
statements.push(trackOp.statement);
|
|
}
|
|
fn$1 = op.usesComponentInstance || statements.length !== 1 || !(statements[0] instanceof ReturnStatement) ? fn(params, statements) : arrowFn(params, statements[0].value);
|
|
}
|
|
op.trackByFn = unit.job.pool.getSharedFunctionReference(fn$1, '_forTrack');
|
|
return op.trackByFn;
|
|
}
|
|
|
|
function removeEmptyBindings(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
switch (op.kind) {
|
|
case OpKind.Attribute:
|
|
case OpKind.Binding:
|
|
case OpKind.ClassProp:
|
|
case OpKind.ClassMap:
|
|
case OpKind.Property:
|
|
case OpKind.StyleProp:
|
|
case OpKind.StyleMap:
|
|
if (op.expression instanceof EmptyExpr) {
|
|
OpList.remove(op);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function removeI18nContexts(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nContext:
|
|
OpList.remove(op);
|
|
break;
|
|
case OpKind.I18nStart:
|
|
op.context = null;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function removeIllegalLetReferences(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
if (op.kind !== OpKind.Variable || op.variable.kind !== SemanticVariableKind.Identifier || !(op.initializer instanceof StoreLetExpr)) {
|
|
continue;
|
|
}
|
|
const name = op.variable.identifier;
|
|
let current = op;
|
|
while (current && current.kind !== OpKind.ListEnd) {
|
|
transformExpressionsInOp(current, expr => expr instanceof LexicalReadExpr && expr.name === name ? literal(undefined) : expr, VisitorContextFlag.None);
|
|
current = current.prev;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function removeUnusedI18nAttributesOps(job) {
|
|
for (const unit of job.units) {
|
|
const ownersWithI18nExpressions = new Set();
|
|
for (const op of unit.update) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nExpression:
|
|
ownersWithI18nExpressions.add(op.i18nOwner);
|
|
}
|
|
}
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nAttributes:
|
|
if (ownersWithI18nExpressions.has(op.xref)) {
|
|
continue;
|
|
}
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveContexts(job) {
|
|
for (const unit of job.units) {
|
|
processLexicalScope$1(unit, unit.create);
|
|
processLexicalScope$1(unit, unit.update);
|
|
}
|
|
}
|
|
function processLexicalScope$1(view, ops) {
|
|
const scope = new Map();
|
|
scope.set(view.xref, variable('ctx'));
|
|
for (const op of ops) {
|
|
switch (op.kind) {
|
|
case OpKind.Variable:
|
|
switch (op.variable.kind) {
|
|
case SemanticVariableKind.Context:
|
|
scope.set(op.variable.view, new ReadVariableExpr(op.xref));
|
|
break;
|
|
}
|
|
break;
|
|
case OpKind.Animation:
|
|
case OpKind.AnimationListener:
|
|
case OpKind.Listener:
|
|
case OpKind.TwoWayListener:
|
|
processLexicalScope$1(view, op.handlerOps);
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (op.trackByOps !== null) {
|
|
processLexicalScope$1(view, op.trackByOps);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (view === view.job.root) {
|
|
scope.set(view.xref, variable('ctx'));
|
|
}
|
|
for (const op of ops) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof ContextExpr) {
|
|
if (!scope.has(expr.view)) {
|
|
throw new Error(`No context found for reference to view ${expr.view} from view ${view.xref}`);
|
|
}
|
|
return scope.get(expr.view);
|
|
} else {
|
|
return expr;
|
|
}
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
|
|
function resolveDeferDepsFns(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.Defer) {
|
|
if (op.resolverFn !== null) {
|
|
continue;
|
|
}
|
|
if (op.ownResolverFn !== null) {
|
|
if (op.handle.slot === null) {
|
|
throw new Error('AssertionError: slot must be assigned before extracting defer deps functions');
|
|
}
|
|
const fullPathName = unit.fnName?.replace('_Template', '');
|
|
op.resolverFn = job.pool.getSharedFunctionReference(op.ownResolverFn, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveDollarEvent(job) {
|
|
for (const unit of job.units) {
|
|
transformDollarEvent(unit.create);
|
|
transformDollarEvent(unit.update);
|
|
}
|
|
}
|
|
function transformDollarEvent(ops) {
|
|
for (const op of ops) {
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener || op.kind === OpKind.AnimationListener) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof LexicalReadExpr && expr.name === '$event') {
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.AnimationListener) {
|
|
op.consumesDollarEvent = true;
|
|
}
|
|
return new ReadVarExpr(expr.name);
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.InChildOperation);
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveI18nElementPlaceholders(job) {
|
|
const i18nContexts = new Map();
|
|
const elements = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nContext:
|
|
i18nContexts.set(op.xref, op);
|
|
break;
|
|
case OpKind.ElementStart:
|
|
elements.set(op.xref, op);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
|
|
}
|
|
function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
|
|
let currentOps = null;
|
|
let pendingStructuralDirectiveCloses = new Map();
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
if (!op.context) {
|
|
throw Error('Could not find i18n context for i18n op');
|
|
}
|
|
currentOps = {
|
|
i18nBlock: op,
|
|
i18nContext: i18nContexts.get(op.context)
|
|
};
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
currentOps = null;
|
|
break;
|
|
case OpKind.ElementStart:
|
|
if (op.i18nPlaceholder !== undefined) {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
|
|
pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
|
|
}
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
break;
|
|
case OpKind.ElementEnd:
|
|
const startOp = elements.get(op.xref);
|
|
if (startOp && startOp.i18nPlaceholder !== undefined) {
|
|
if (currentOps === null) {
|
|
throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
|
|
pendingStructuralDirectiveCloses.delete(op.xref);
|
|
}
|
|
break;
|
|
case OpKind.Projection:
|
|
if (op.i18nPlaceholder !== undefined) {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
if (op.fallbackView !== null) {
|
|
const view = job.views.get(op.fallbackView);
|
|
if (op.fallbackViewI18nPlaceholder === undefined) {
|
|
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
} else {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordTemplateStart(job, view, op.handle.slot, op.fallbackViewI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
recordTemplateClose(job, view, op.handle.slot, op.fallbackViewI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
}
|
|
break;
|
|
case OpKind.ConditionalCreate:
|
|
case OpKind.ConditionalBranchCreate:
|
|
case OpKind.Template:
|
|
const view = job.views.get(op.xref);
|
|
if (op.i18nPlaceholder === undefined) {
|
|
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
} else {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
if (op.templateKind === TemplateKind.Structural) {
|
|
resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
|
|
} else {
|
|
recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
}
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (pendingStructuralDirective !== undefined) {
|
|
throw Error('AssertionError: Unexpected structural directive associated with @for block');
|
|
}
|
|
const forSlot = op.handle.slot + 1;
|
|
const forView = job.views.get(op.xref);
|
|
if (op.i18nPlaceholder === undefined) {
|
|
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
} else {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
if (op.emptyView !== null) {
|
|
const emptySlot = op.handle.slot + 2;
|
|
const emptyView = job.views.get(op.emptyView);
|
|
if (op.emptyI18nPlaceholder === undefined) {
|
|
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
} else {
|
|
if (currentOps === null) {
|
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
}
|
|
recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
pendingStructuralDirective = undefined;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
|
|
const {
|
|
startName,
|
|
closeName
|
|
} = op.i18nPlaceholder;
|
|
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
|
|
let value = op.handle.slot;
|
|
if (structuralDirective !== undefined) {
|
|
flags |= I18nParamValueFlags.TemplateTag;
|
|
value = {
|
|
element: value,
|
|
template: structuralDirective.handle.slot
|
|
};
|
|
}
|
|
if (!closeName) {
|
|
flags |= I18nParamValueFlags.CloseTag;
|
|
}
|
|
addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
|
|
}
|
|
function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
|
|
const {
|
|
closeName
|
|
} = op.i18nPlaceholder;
|
|
if (closeName) {
|
|
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
|
|
let value = op.handle.slot;
|
|
if (structuralDirective !== undefined) {
|
|
flags |= I18nParamValueFlags.TemplateTag;
|
|
value = {
|
|
element: value,
|
|
template: structuralDirective.handle.slot
|
|
};
|
|
}
|
|
addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
|
|
}
|
|
}
|
|
function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
let {
|
|
startName,
|
|
closeName
|
|
} = i18nPlaceholder;
|
|
let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
|
|
if (!closeName) {
|
|
flags |= I18nParamValueFlags.CloseTag;
|
|
}
|
|
if (structuralDirective !== undefined) {
|
|
addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
}
|
|
addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
}
|
|
function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
const {
|
|
closeName
|
|
} = i18nPlaceholder;
|
|
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
if (closeName) {
|
|
addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
if (structuralDirective !== undefined) {
|
|
addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
}
|
|
}
|
|
}
|
|
function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
|
|
for (const childOp of view.create) {
|
|
if (childOp.kind === OpKind.I18nStart) {
|
|
return childOp.subTemplateIndex;
|
|
}
|
|
}
|
|
return i18nOp.subTemplateIndex;
|
|
}
|
|
function addParam(params, placeholder, value, subTemplateIndex, flags) {
|
|
const values = params.get(placeholder) ?? [];
|
|
values.push({
|
|
value,
|
|
subTemplateIndex,
|
|
flags
|
|
});
|
|
params.set(placeholder, values);
|
|
}
|
|
|
|
function resolveI18nExpressionPlaceholders(job) {
|
|
const subTemplateIndices = new Map();
|
|
const i18nContexts = new Map();
|
|
const icuPlaceholders = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
subTemplateIndices.set(op.xref, op.subTemplateIndex);
|
|
break;
|
|
case OpKind.I18nContext:
|
|
i18nContexts.set(op.xref, op);
|
|
break;
|
|
case OpKind.IcuPlaceholder:
|
|
icuPlaceholders.set(op.xref, op);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const expressionIndices = new Map();
|
|
const referenceIndex = op => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
if (op.kind === OpKind.I18nExpression) {
|
|
const index = expressionIndices.get(referenceIndex(op)) || 0;
|
|
const subTemplateIndex = subTemplateIndices.get(op.i18nOwner) ?? null;
|
|
const value = {
|
|
value: index,
|
|
subTemplateIndex: subTemplateIndex,
|
|
flags: I18nParamValueFlags.ExpressionIndex
|
|
};
|
|
updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
|
|
expressionIndices.set(referenceIndex(op), index + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
|
|
if (op.i18nPlaceholder !== null) {
|
|
const i18nContext = i18nContexts.get(op.context);
|
|
const params = op.resolutionTime === I18nParamResolutionTime.Creation ? i18nContext.params : i18nContext.postprocessingParams;
|
|
const values = params.get(op.i18nPlaceholder) || [];
|
|
values.push(value);
|
|
params.set(op.i18nPlaceholder, values);
|
|
}
|
|
if (op.icuPlaceholder !== null) {
|
|
const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
|
|
icuPlaceholderOp?.expressionPlaceholders.push(value);
|
|
}
|
|
}
|
|
|
|
function resolveNames(job) {
|
|
for (const unit of job.units) {
|
|
processLexicalScope(unit, unit.create, null);
|
|
processLexicalScope(unit, unit.update, null);
|
|
}
|
|
}
|
|
function processLexicalScope(unit, ops, savedView) {
|
|
const scope = new Map();
|
|
const localDefinitions = new Map();
|
|
for (const op of ops) {
|
|
switch (op.kind) {
|
|
case OpKind.Variable:
|
|
switch (op.variable.kind) {
|
|
case SemanticVariableKind.Identifier:
|
|
if (op.variable.local) {
|
|
if (localDefinitions.has(op.variable.identifier)) {
|
|
continue;
|
|
}
|
|
localDefinitions.set(op.variable.identifier, op.xref);
|
|
} else if (scope.has(op.variable.identifier)) {
|
|
continue;
|
|
}
|
|
scope.set(op.variable.identifier, op.xref);
|
|
break;
|
|
case SemanticVariableKind.Alias:
|
|
if (scope.has(op.variable.identifier)) {
|
|
continue;
|
|
}
|
|
scope.set(op.variable.identifier, op.xref);
|
|
break;
|
|
case SemanticVariableKind.SavedView:
|
|
savedView = {
|
|
view: op.variable.view,
|
|
variable: op.xref
|
|
};
|
|
break;
|
|
}
|
|
break;
|
|
case OpKind.Animation:
|
|
case OpKind.AnimationListener:
|
|
case OpKind.Listener:
|
|
case OpKind.TwoWayListener:
|
|
processLexicalScope(unit, op.handlerOps, savedView);
|
|
break;
|
|
case OpKind.RepeaterCreate:
|
|
if (op.trackByOps !== null) {
|
|
processLexicalScope(unit, op.trackByOps, savedView);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
for (const op of ops) {
|
|
if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener) {
|
|
continue;
|
|
}
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof LexicalReadExpr) {
|
|
if (localDefinitions.has(expr.name)) {
|
|
return new ReadVariableExpr(localDefinitions.get(expr.name));
|
|
} else if (scope.has(expr.name)) {
|
|
return new ReadVariableExpr(scope.get(expr.name));
|
|
} else {
|
|
return new ReadPropExpr(new ContextExpr(unit.job.root.xref), expr.name);
|
|
}
|
|
} else if (expr instanceof RestoreViewExpr && typeof expr.view === 'number') {
|
|
if (savedView === null || savedView.view !== expr.view) {
|
|
throw new Error(`AssertionError: no saved view ${expr.view} from view ${unit.xref}`);
|
|
}
|
|
expr.view = new ReadVariableExpr(savedView.variable);
|
|
return expr;
|
|
} else {
|
|
return expr;
|
|
}
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
for (const op of ops) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (expr instanceof LexicalReadExpr) {
|
|
throw new Error(`AssertionError: no lexical reads should remain, but found read of ${expr.name}`);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
const sanitizerFns = new Map([[SecurityContext.HTML, Identifiers.sanitizeHtml], [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl], [SecurityContext.SCRIPT, Identifiers.sanitizeScript], [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl], [SecurityContext.ATTRIBUTE_NO_BINDING, Identifiers.validateAttribute]]);
|
|
const trustedValueFns = new Map([[SecurityContext.HTML, Identifiers.trustConstantHtml], [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl]]);
|
|
function resolveSanitizers(job) {
|
|
for (const unit of job.units) {
|
|
if (job.kind !== CompilationJobKind.Host) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.ExtractedAttribute) {
|
|
const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
|
|
}
|
|
}
|
|
}
|
|
for (const op of unit.update) {
|
|
switch (op.kind) {
|
|
case OpKind.Property:
|
|
case OpKind.Attribute:
|
|
case OpKind.DomProperty:
|
|
let sanitizerFn = null;
|
|
if (Array.isArray(op.securityContext) && op.securityContext.length === 2 && op.securityContext.includes(SecurityContext.URL) && op.securityContext.includes(SecurityContext.RESOURCE_URL)) {
|
|
sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
|
|
} else {
|
|
sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
}
|
|
op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function getOnlySecurityContext(securityContext) {
|
|
if (Array.isArray(securityContext)) {
|
|
if (securityContext.length > 1) {
|
|
throw Error(`AssertionError: Ambiguous security context`);
|
|
}
|
|
return securityContext[0] || SecurityContext.NONE;
|
|
}
|
|
return securityContext;
|
|
}
|
|
|
|
function saveAndRestoreView(job) {
|
|
for (const unit of job.units) {
|
|
unit.create.prepend([createVariableOp(unit.job.allocateXrefId(), {
|
|
kind: SemanticVariableKind.SavedView,
|
|
name: null,
|
|
view: unit.xref
|
|
}, new GetCurrentViewExpr(), VariableFlags.None)]);
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener && op.kind !== OpKind.Animation && op.kind !== OpKind.AnimationListener) {
|
|
continue;
|
|
}
|
|
let needsRestoreView = unit !== job.root;
|
|
if (!needsRestoreView) {
|
|
for (const handlerOp of op.handlerOps) {
|
|
visitExpressionsInOp(handlerOp, expr => {
|
|
if (expr instanceof ReferenceExpr || expr instanceof ContextLetReferenceExpr) {
|
|
needsRestoreView = true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
if (needsRestoreView) {
|
|
addSaveRestoreViewOperationToListener(unit, op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function addSaveRestoreViewOperationToListener(unit, op) {
|
|
op.handlerOps.prepend([createVariableOp(unit.job.allocateXrefId(), {
|
|
kind: SemanticVariableKind.Context,
|
|
name: null,
|
|
view: unit.xref
|
|
}, new RestoreViewExpr(unit.xref), VariableFlags.None)]);
|
|
for (const handlerOp of op.handlerOps) {
|
|
if (handlerOp.kind === OpKind.Statement && handlerOp.statement instanceof ReturnStatement) {
|
|
handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
function allocateSlots(job) {
|
|
const slotMap = new Map();
|
|
for (const unit of job.units) {
|
|
let slotCount = 0;
|
|
for (const op of unit.create) {
|
|
if (!hasConsumesSlotTrait(op)) {
|
|
continue;
|
|
}
|
|
op.handle.slot = slotCount;
|
|
slotMap.set(op.xref, op.handle.slot);
|
|
slotCount += op.numSlotsUsed;
|
|
}
|
|
unit.decls = slotCount;
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind === OpKind.Template || op.kind === OpKind.ConditionalCreate || op.kind === OpKind.ConditionalBranchCreate || op.kind === OpKind.RepeaterCreate) {
|
|
const childView = job.views.get(op.xref);
|
|
op.decls = childView.decls;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function optimizeStoreLet(job) {
|
|
const letUsedExternally = new Set();
|
|
const declareLetOps = new Map();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
if (op.kind === OpKind.DeclareLet) {
|
|
declareLetOps.set(op.xref, op);
|
|
}
|
|
visitExpressionsInOp(op, expr => {
|
|
if (expr instanceof ContextLetReferenceExpr) {
|
|
letUsedExternally.add(expr.target);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof StoreLetExpr && !letUsedExternally.has(expr.target)) {
|
|
if (!hasPipe(expr)) {
|
|
OpList.remove(declareLetOps.get(expr.target));
|
|
}
|
|
return expr.value;
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
function hasPipe(root) {
|
|
let result = false;
|
|
transformExpressionsInExpression(root, expr => {
|
|
if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) {
|
|
result = true;
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
return result;
|
|
}
|
|
|
|
function stripNonrequiredParentheses(job) {
|
|
const requiredParens = new Set();
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (expr instanceof BinaryOperatorExpr) {
|
|
switch (expr.operator) {
|
|
case BinaryOperator.Exponentiation:
|
|
checkExponentiationParens(expr, requiredParens);
|
|
break;
|
|
case BinaryOperator.NullishCoalesce:
|
|
checkNullishCoalescingParens(expr, requiredParens);
|
|
break;
|
|
case BinaryOperator.And:
|
|
case BinaryOperator.Or:
|
|
checkAndOrParens(expr, requiredParens);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
for (const unit of job.units) {
|
|
for (const op of unit.ops()) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof ParenthesizedExpr) {
|
|
return requiredParens.has(expr) ? expr : expr.expr;
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
function checkExponentiationParens(expr, requiredParens) {
|
|
if (expr.lhs instanceof ParenthesizedExpr && expr.lhs.expr instanceof UnaryOperatorExpr) {
|
|
requiredParens.add(expr.lhs);
|
|
}
|
|
}
|
|
function checkNullishCoalescingParens(expr, requiredParens) {
|
|
if (expr.lhs instanceof ParenthesizedExpr && (isLogicalAndOr(expr.lhs.expr) || expr.lhs.expr instanceof ConditionalExpr)) {
|
|
requiredParens.add(expr.lhs);
|
|
}
|
|
if (expr.rhs instanceof ParenthesizedExpr && (isLogicalAndOr(expr.rhs.expr) || expr.rhs.expr instanceof ConditionalExpr)) {
|
|
requiredParens.add(expr.rhs);
|
|
}
|
|
}
|
|
function checkAndOrParens(expr, requiredParens) {
|
|
if (expr.lhs instanceof ParenthesizedExpr && expr.lhs.expr instanceof BinaryOperatorExpr && expr.lhs.expr.operator === BinaryOperator.NullishCoalesce) {
|
|
requiredParens.add(expr.lhs);
|
|
}
|
|
}
|
|
function isLogicalAndOr(expr) {
|
|
return expr instanceof BinaryOperatorExpr && (expr.operator === BinaryOperator.And || expr.operator === BinaryOperator.Or);
|
|
}
|
|
|
|
function specializeStyleBindings(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.update) {
|
|
if (op.kind !== OpKind.Binding) {
|
|
continue;
|
|
}
|
|
switch (op.bindingKind) {
|
|
case BindingKind.ClassName:
|
|
if (op.expression instanceof Interpolation) {
|
|
throw new Error(`Unexpected interpolation in ClassName binding`);
|
|
}
|
|
OpList.replace(op, createClassPropOp(op.target, op.name, op.expression, op.sourceSpan));
|
|
break;
|
|
case BindingKind.StyleProperty:
|
|
OpList.replace(op, createStylePropOp(op.target, op.name, op.expression, op.unit, op.sourceSpan));
|
|
break;
|
|
case BindingKind.Property:
|
|
case BindingKind.Template:
|
|
if (op.name === 'style') {
|
|
OpList.replace(op, createStyleMapOp(op.target, op.expression, op.sourceSpan));
|
|
} else if (op.name === 'class') {
|
|
OpList.replace(op, createClassMapOp(op.target, op.expression, op.sourceSpan));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateTemporaryVariables(job) {
|
|
for (const unit of job.units) {
|
|
unit.create.prepend(generateTemporaries(unit.create));
|
|
unit.update.prepend(generateTemporaries(unit.update));
|
|
}
|
|
}
|
|
function generateTemporaries(ops) {
|
|
let opCount = 0;
|
|
let generatedStatements = [];
|
|
for (const op of ops) {
|
|
const finalReads = new Map();
|
|
visitExpressionsInOp(op, (expr, flag) => {
|
|
if (flag & VisitorContextFlag.InChildOperation) {
|
|
return;
|
|
}
|
|
if (expr instanceof ReadTemporaryExpr) {
|
|
finalReads.set(expr.xref, expr);
|
|
}
|
|
});
|
|
let count = 0;
|
|
const assigned = new Set();
|
|
const released = new Set();
|
|
const defs = new Map();
|
|
visitExpressionsInOp(op, (expr, flag) => {
|
|
if (flag & VisitorContextFlag.InChildOperation) {
|
|
return;
|
|
}
|
|
if (expr instanceof AssignTemporaryExpr) {
|
|
if (!assigned.has(expr.xref)) {
|
|
assigned.add(expr.xref);
|
|
defs.set(expr.xref, `tmp_${opCount}_${count++}`);
|
|
}
|
|
assignName(defs, expr);
|
|
} else if (expr instanceof ReadTemporaryExpr) {
|
|
if (finalReads.get(expr.xref) === expr) {
|
|
released.add(expr.xref);
|
|
count--;
|
|
}
|
|
assignName(defs, expr);
|
|
}
|
|
});
|
|
generatedStatements.push(...Array.from(new Set(defs.values())).map(name => createStatementOp(new DeclareVarStmt(name))));
|
|
opCount++;
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener || op.kind === OpKind.TwoWayListener) {
|
|
op.handlerOps.prepend(generateTemporaries(op.handlerOps));
|
|
} else if (op.kind === OpKind.RepeaterCreate && op.trackByOps !== null) {
|
|
op.trackByOps.prepend(generateTemporaries(op.trackByOps));
|
|
}
|
|
}
|
|
return generatedStatements;
|
|
}
|
|
function assignName(names, expr) {
|
|
const name = names.get(expr.xref);
|
|
if (name === undefined) {
|
|
throw new Error(`Found xref with unassigned name: ${expr.xref}`);
|
|
}
|
|
expr.name = name;
|
|
}
|
|
|
|
function optimizeTrackFns(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.RepeaterCreate) {
|
|
continue;
|
|
}
|
|
if (op.track instanceof ReadVarExpr && op.track.name === '$index') {
|
|
op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex);
|
|
} else if (op.track instanceof ReadVarExpr && op.track.name === '$item') {
|
|
op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity);
|
|
} else if (isTrackByFunctionCall(job.root.xref, op.track)) {
|
|
op.usesComponentInstance = true;
|
|
if (op.track.receiver.receiver.view === unit.xref) {
|
|
op.trackByFn = op.track.receiver;
|
|
} else {
|
|
op.trackByFn = importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name);
|
|
op.track = op.trackByFn;
|
|
}
|
|
} else {
|
|
op.track = transformExpressionsInExpression(op.track, expr => {
|
|
if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) {
|
|
throw new Error(`Illegal State: Pipes are not allowed in this context`);
|
|
} else if (expr instanceof ContextExpr) {
|
|
op.usesComponentInstance = true;
|
|
return new TrackContextExpr(expr.view);
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
const trackOpList = new OpList();
|
|
trackOpList.push(createStatementOp(new ReturnStatement(op.track, op.track.sourceSpan)));
|
|
op.trackByOps = trackOpList;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function isTrackByFunctionCall(rootView, expr) {
|
|
if (!(expr instanceof InvokeFunctionExpr) || expr.args.length === 0 || expr.args.length > 2) {
|
|
return false;
|
|
}
|
|
if (!(expr.receiver instanceof ReadPropExpr && expr.receiver.receiver instanceof ContextExpr) || expr.receiver.receiver.view !== rootView) {
|
|
return false;
|
|
}
|
|
const [arg0, arg1] = expr.args;
|
|
if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') {
|
|
return false;
|
|
} else if (expr.args.length === 1) {
|
|
return true;
|
|
}
|
|
if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function generateTrackVariables(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.RepeaterCreate) {
|
|
continue;
|
|
}
|
|
op.track = transformExpressionsInExpression(op.track, expr => {
|
|
if (expr instanceof LexicalReadExpr) {
|
|
if (op.varNames.$index.has(expr.name)) {
|
|
return variable('$index');
|
|
} else if (expr.name === op.varNames.$implicit) {
|
|
return variable('$item');
|
|
}
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
}
|
|
}
|
|
|
|
function transformTwoWayBindingSet(job) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.TwoWayListener) {
|
|
transformExpressionsInOp(op, expr => {
|
|
if (!(expr instanceof TwoWayBindingSetExpr)) {
|
|
return expr;
|
|
}
|
|
const {
|
|
target,
|
|
value
|
|
} = expr;
|
|
if (target instanceof ReadPropExpr || target instanceof ReadKeyExpr) {
|
|
return twoWayBindingSet(target, value).or(target.set(value));
|
|
}
|
|
if (target instanceof ReadVariableExpr) {
|
|
return twoWayBindingSet(target, value);
|
|
}
|
|
throw new Error(`Unsupported expression in two-way action binding.`);
|
|
}, VisitorContextFlag.InChildOperation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function countVariables(job) {
|
|
for (const unit of job.units) {
|
|
let varCount = 0;
|
|
for (const op of unit.ops()) {
|
|
if (hasConsumesVarsTrait(op)) {
|
|
varCount += varsUsedByOp(op);
|
|
}
|
|
}
|
|
for (const op of unit.ops()) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!isIrExpression(expr)) {
|
|
return;
|
|
}
|
|
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder && expr instanceof PureFunctionExpr) {
|
|
return;
|
|
}
|
|
if (hasUsesVarOffsetTrait(expr)) {
|
|
expr.varOffset = varCount;
|
|
}
|
|
if (hasConsumesVarsTrait(expr)) {
|
|
varCount += varsUsedByIrExpression(expr);
|
|
}
|
|
});
|
|
}
|
|
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
for (const op of unit.ops()) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!isIrExpression(expr) || !(expr instanceof PureFunctionExpr)) {
|
|
return;
|
|
}
|
|
if (hasUsesVarOffsetTrait(expr)) {
|
|
expr.varOffset = varCount;
|
|
}
|
|
if (hasConsumesVarsTrait(expr)) {
|
|
varCount += varsUsedByIrExpression(expr);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
unit.vars = varCount;
|
|
}
|
|
if (job instanceof ComponentCompilationJob) {
|
|
for (const unit of job.units) {
|
|
for (const op of unit.create) {
|
|
if (op.kind !== OpKind.Template && op.kind !== OpKind.RepeaterCreate && op.kind !== OpKind.ConditionalCreate && op.kind !== OpKind.ConditionalBranchCreate) {
|
|
continue;
|
|
}
|
|
const childView = job.views.get(op.xref);
|
|
op.vars = childView.vars;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function varsUsedByOp(op) {
|
|
let slots;
|
|
switch (op.kind) {
|
|
case OpKind.Attribute:
|
|
slots = 1;
|
|
if (op.expression instanceof Interpolation && !isSingletonInterpolation(op.expression)) {
|
|
slots += op.expression.expressions.length;
|
|
}
|
|
return slots;
|
|
case OpKind.Property:
|
|
case OpKind.DomProperty:
|
|
slots = 1;
|
|
if (op.expression instanceof Interpolation) {
|
|
slots += op.expression.expressions.length;
|
|
}
|
|
return slots;
|
|
case OpKind.Control:
|
|
return 2;
|
|
case OpKind.TwoWayProperty:
|
|
return 1;
|
|
case OpKind.StyleProp:
|
|
case OpKind.ClassProp:
|
|
case OpKind.StyleMap:
|
|
case OpKind.ClassMap:
|
|
slots = 2;
|
|
if (op.expression instanceof Interpolation) {
|
|
slots += op.expression.expressions.length;
|
|
}
|
|
return slots;
|
|
case OpKind.InterpolateText:
|
|
return op.interpolation.expressions.length;
|
|
case OpKind.I18nExpression:
|
|
case OpKind.Conditional:
|
|
case OpKind.DeferWhen:
|
|
case OpKind.StoreLet:
|
|
return 1;
|
|
case OpKind.RepeaterCreate:
|
|
return op.emptyView ? 1 : 0;
|
|
default:
|
|
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
}
|
|
}
|
|
function varsUsedByIrExpression(expr) {
|
|
switch (expr.kind) {
|
|
case ExpressionKind.PureFunctionExpr:
|
|
return 1 + expr.args.length;
|
|
case ExpressionKind.PipeBinding:
|
|
return 1 + expr.args.length;
|
|
case ExpressionKind.PipeBindingVariadic:
|
|
return 1 + expr.numArgs;
|
|
case ExpressionKind.StoreLet:
|
|
return 1;
|
|
default:
|
|
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
}
|
|
}
|
|
function isSingletonInterpolation(expr) {
|
|
if (expr.expressions.length !== 1 || expr.strings.length !== 2) {
|
|
return false;
|
|
}
|
|
if (expr.strings[0] !== '' || expr.strings[1] !== '') {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function optimizeVariables(job) {
|
|
for (const unit of job.units) {
|
|
inlineAlwaysInlineVariables(unit.create);
|
|
inlineAlwaysInlineVariables(unit.update);
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener || op.kind === OpKind.TwoWayListener) {
|
|
inlineAlwaysInlineVariables(op.handlerOps);
|
|
} else if (op.kind === OpKind.RepeaterCreate && op.trackByOps !== null) {
|
|
inlineAlwaysInlineVariables(op.trackByOps);
|
|
}
|
|
}
|
|
optimizeVariablesInOpList(unit.create, job.compatibility);
|
|
optimizeVariablesInOpList(unit.update, job.compatibility);
|
|
for (const op of unit.create) {
|
|
if (op.kind === OpKind.Listener || op.kind === OpKind.Animation || op.kind === OpKind.AnimationListener || op.kind === OpKind.TwoWayListener) {
|
|
optimizeVariablesInOpList(op.handlerOps, job.compatibility);
|
|
} else if (op.kind === OpKind.RepeaterCreate && op.trackByOps !== null) {
|
|
optimizeVariablesInOpList(op.trackByOps, job.compatibility);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var Fence;
|
|
(function (Fence) {
|
|
Fence[Fence["None"] = 0] = "None";
|
|
Fence[Fence["ViewContextRead"] = 1] = "ViewContextRead";
|
|
Fence[Fence["ViewContextWrite"] = 2] = "ViewContextWrite";
|
|
Fence[Fence["SideEffectful"] = 4] = "SideEffectful";
|
|
})(Fence || (Fence = {}));
|
|
function inlineAlwaysInlineVariables(ops) {
|
|
const vars = new Map();
|
|
for (const op of ops) {
|
|
if (op.kind === OpKind.Variable && op.flags & VariableFlags.AlwaysInline) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (isIrExpression(expr) && fencesForIrExpression(expr) !== Fence.None) {
|
|
throw new Error(`AssertionError: A context-sensitive variable was marked AlwaysInline`);
|
|
}
|
|
});
|
|
vars.set(op.xref, op);
|
|
}
|
|
transformExpressionsInOp(op, expr => {
|
|
if (expr instanceof ReadVariableExpr && vars.has(expr.xref)) {
|
|
const varOp = vars.get(expr.xref);
|
|
return varOp.initializer.clone();
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
}
|
|
for (const op of vars.values()) {
|
|
OpList.remove(op);
|
|
}
|
|
}
|
|
function optimizeVariablesInOpList(ops, compatibility) {
|
|
const varDecls = new Map();
|
|
const varUsages = new Map();
|
|
const varRemoteUsages = new Set();
|
|
const opMap = new Map();
|
|
for (const op of ops) {
|
|
if (op.kind === OpKind.Variable) {
|
|
if (varDecls.has(op.xref) || varUsages.has(op.xref)) {
|
|
throw new Error(`Should not see two declarations of the same variable: ${op.xref}`);
|
|
}
|
|
varDecls.set(op.xref, op);
|
|
varUsages.set(op.xref, 0);
|
|
}
|
|
opMap.set(op, collectOpInfo(op));
|
|
countVariableUsages(op, varUsages, varRemoteUsages);
|
|
}
|
|
let contextIsUsed = false;
|
|
for (const op of ops.reversed()) {
|
|
const opInfo = opMap.get(op);
|
|
if (op.kind === OpKind.Variable && varUsages.get(op.xref) === 0) {
|
|
if (contextIsUsed && opInfo.fences & Fence.ViewContextWrite || opInfo.fences & Fence.SideEffectful) {
|
|
const stmtOp = createStatementOp(op.initializer.toStmt());
|
|
opMap.set(stmtOp, opInfo);
|
|
OpList.replace(op, stmtOp);
|
|
} else {
|
|
uncountVariableUsages(op, varUsages);
|
|
OpList.remove(op);
|
|
}
|
|
opMap.delete(op);
|
|
varDecls.delete(op.xref);
|
|
varUsages.delete(op.xref);
|
|
continue;
|
|
}
|
|
if (opInfo.fences & Fence.ViewContextRead) {
|
|
contextIsUsed = true;
|
|
}
|
|
}
|
|
const toInline = [];
|
|
for (const [id, count] of varUsages) {
|
|
const decl = varDecls.get(id);
|
|
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
if (count !== 1 || isAlwaysInline) {
|
|
continue;
|
|
}
|
|
if (varRemoteUsages.has(id)) {
|
|
continue;
|
|
}
|
|
toInline.push(id);
|
|
}
|
|
let candidate;
|
|
while (candidate = toInline.pop()) {
|
|
const decl = varDecls.get(candidate);
|
|
const varInfo = opMap.get(decl);
|
|
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
if (isAlwaysInline) {
|
|
throw new Error(`AssertionError: Found an 'AlwaysInline' variable after the always inlining pass.`);
|
|
}
|
|
for (let targetOp = decl.next; targetOp.kind !== OpKind.ListEnd; targetOp = targetOp.next) {
|
|
const opInfo = opMap.get(targetOp);
|
|
if (opInfo.variablesUsed.has(candidate)) {
|
|
if (compatibility === CompatibilityMode.TemplateDefinitionBuilder && !allowConservativeInlining(decl, targetOp)) {
|
|
break;
|
|
}
|
|
if (tryInlineVariableInitializer(candidate, decl.initializer, targetOp, varInfo.fences)) {
|
|
opInfo.variablesUsed.delete(candidate);
|
|
for (const id of varInfo.variablesUsed) {
|
|
opInfo.variablesUsed.add(id);
|
|
}
|
|
opInfo.fences |= varInfo.fences;
|
|
varDecls.delete(candidate);
|
|
varUsages.delete(candidate);
|
|
opMap.delete(decl);
|
|
OpList.remove(decl);
|
|
}
|
|
break;
|
|
}
|
|
if (!safeToInlinePastFences(opInfo.fences, varInfo.fences)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function fencesForIrExpression(expr) {
|
|
switch (expr.kind) {
|
|
case ExpressionKind.NextContext:
|
|
return Fence.ViewContextRead | Fence.ViewContextWrite;
|
|
case ExpressionKind.RestoreView:
|
|
return Fence.ViewContextRead | Fence.ViewContextWrite | Fence.SideEffectful;
|
|
case ExpressionKind.StoreLet:
|
|
return Fence.SideEffectful;
|
|
case ExpressionKind.Reference:
|
|
case ExpressionKind.ContextLetReference:
|
|
return Fence.ViewContextRead;
|
|
default:
|
|
return Fence.None;
|
|
}
|
|
}
|
|
function collectOpInfo(op) {
|
|
let fences = Fence.None;
|
|
const variablesUsed = new Set();
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!isIrExpression(expr)) {
|
|
return;
|
|
}
|
|
switch (expr.kind) {
|
|
case ExpressionKind.ReadVariable:
|
|
variablesUsed.add(expr.xref);
|
|
break;
|
|
default:
|
|
fences |= fencesForIrExpression(expr);
|
|
}
|
|
});
|
|
return {
|
|
fences,
|
|
variablesUsed
|
|
};
|
|
}
|
|
function countVariableUsages(op, varUsages, varRemoteUsage) {
|
|
visitExpressionsInOp(op, (expr, flags) => {
|
|
if (!isIrExpression(expr)) {
|
|
return;
|
|
}
|
|
if (expr.kind !== ExpressionKind.ReadVariable) {
|
|
return;
|
|
}
|
|
const count = varUsages.get(expr.xref);
|
|
if (count === undefined) {
|
|
return;
|
|
}
|
|
varUsages.set(expr.xref, count + 1);
|
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
varRemoteUsage.add(expr.xref);
|
|
}
|
|
});
|
|
}
|
|
function uncountVariableUsages(op, varUsages) {
|
|
visitExpressionsInOp(op, expr => {
|
|
if (!isIrExpression(expr)) {
|
|
return;
|
|
}
|
|
if (expr.kind !== ExpressionKind.ReadVariable) {
|
|
return;
|
|
}
|
|
const count = varUsages.get(expr.xref);
|
|
if (count === undefined) {
|
|
return;
|
|
} else if (count === 0) {
|
|
throw new Error(`Inaccurate variable count: ${expr.xref} - found another read but count is already 0`);
|
|
}
|
|
varUsages.set(expr.xref, count - 1);
|
|
});
|
|
}
|
|
function safeToInlinePastFences(fences, declFences) {
|
|
if (fences & Fence.ViewContextWrite) {
|
|
if (declFences & Fence.ViewContextRead) {
|
|
return false;
|
|
}
|
|
} else if (fences & Fence.ViewContextRead) {
|
|
if (declFences & Fence.ViewContextWrite) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function tryInlineVariableInitializer(id, initializer, target, declFences) {
|
|
let inlined = false;
|
|
let inliningAllowed = true;
|
|
transformExpressionsInOp(target, (expr, flags) => {
|
|
if (!isIrExpression(expr)) {
|
|
return expr;
|
|
}
|
|
if (inlined || !inliningAllowed) {
|
|
return expr;
|
|
} else if (flags & VisitorContextFlag.InChildOperation && declFences & Fence.ViewContextRead) {
|
|
return expr;
|
|
}
|
|
switch (expr.kind) {
|
|
case ExpressionKind.ReadVariable:
|
|
if (expr.xref === id) {
|
|
inlined = true;
|
|
return initializer;
|
|
}
|
|
break;
|
|
default:
|
|
const exprFences = fencesForIrExpression(expr);
|
|
inliningAllowed = inliningAllowed && safeToInlinePastFences(exprFences, declFences);
|
|
break;
|
|
}
|
|
return expr;
|
|
}, VisitorContextFlag.None);
|
|
return inlined;
|
|
}
|
|
function allowConservativeInlining(decl, target) {
|
|
switch (decl.variable.kind) {
|
|
case SemanticVariableKind.Identifier:
|
|
if (decl.initializer instanceof ReadVarExpr && decl.initializer.name === 'ctx') {
|
|
return true;
|
|
}
|
|
return false;
|
|
case SemanticVariableKind.Context:
|
|
return target.kind === OpKind.Variable;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function wrapI18nIcus(job) {
|
|
for (const unit of job.units) {
|
|
let currentI18nOp = null;
|
|
let addedI18nId = null;
|
|
for (const op of unit.create) {
|
|
switch (op.kind) {
|
|
case OpKind.I18nStart:
|
|
currentI18nOp = op;
|
|
break;
|
|
case OpKind.I18nEnd:
|
|
currentI18nOp = null;
|
|
break;
|
|
case OpKind.IcuStart:
|
|
if (currentI18nOp === null) {
|
|
addedI18nId = job.allocateXrefId();
|
|
OpList.insertBefore(createI18nStartOp(addedI18nId, op.message, undefined, null), op);
|
|
}
|
|
break;
|
|
case OpKind.IcuEnd:
|
|
if (addedI18nId !== null) {
|
|
OpList.insertAfter(createI18nEndOp(addedI18nId, null), op);
|
|
addedI18nId = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @license
|
|
* Copyright Google LLC 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.dev/license
|
|
*/
|
|
const phases = [{
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: removeContentSelectors
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: optimizeRegularExpressions
|
|
}, {
|
|
kind: CompilationJobKind.Host,
|
|
fn: parseHostStyleProperties
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: emitNamespaceChanges
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: propagateI18nBlocks
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: wrapI18nIcus
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: deduplicateTextBindings
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: specializeStyleBindings
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: specializeBindings
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: convertAnimations
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: extractAttributes
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: createI18nContexts
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: parseExtractedStyles
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: removeEmptyBindings
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: collapseSingletonInterpolations
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: orderOps
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateConditionalExpressions
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: createPipes
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: configureDeferInstructions
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: createVariadicPipes
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: generatePureLiteralStructures
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateProjectionDefs
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateLocalLetReferences
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateVariables
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: saveAndRestoreView
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: deleteAnyCasts
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: resolveDollarEvent
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateTrackVariables
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: removeIllegalLetReferences
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: resolveNames
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: resolveDeferTargetNames
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: transformTwoWayBindingSet
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: optimizeTrackFns
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: resolveContexts
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: resolveSanitizers
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: liftLocalRefs
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: expandSafeReads
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: stripNonrequiredParentheses
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: generateTemporaryVariables
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: optimizeVariables
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: optimizeStoreLet
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: convertI18nText
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: convertI18nBindings
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: removeUnusedI18nAttributesOps
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: assignI18nSlotDependencies
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: applyI18nExpressions
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: allocateSlots
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: resolveI18nElementPlaceholders
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: resolveI18nExpressionPlaceholders
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: extractI18nMessages
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: collectI18nConsts
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: collectConstExpressions
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: collectElementConsts
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: removeI18nContexts
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: countVariables
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateAdvance
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: nameFunctionsAndVariables
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: resolveDeferDepsFns
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: mergeNextContextExpressions
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: generateNgContainerOps
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: collapseEmptyInstructions
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: attachSourceLocations
|
|
}, {
|
|
kind: CompilationJobKind.Tmpl,
|
|
fn: disableBindings$1
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: extractPureFunctions
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: reify
|
|
}, {
|
|
kind: CompilationJobKind.Both,
|
|
fn: chain
|
|
}];
|
|
function transform(job, kind) {
|
|
for (const phase of phases) {
|
|
if (phase.kind === kind || phase.kind === CompilationJobKind.Both) {
|
|
phase.fn(job);
|
|
}
|
|
}
|
|
}
|
|
function emitTemplateFn(tpl, pool) {
|
|
const rootFn = emitView(tpl.root);
|
|
emitChildViews(tpl.root, pool);
|
|
return rootFn;
|
|
}
|
|
function emitChildViews(parent, pool) {
|
|
for (const unit of parent.job.units) {
|
|
if (unit.parent !== parent.xref) {
|
|
continue;
|
|
}
|
|
emitChildViews(unit, pool);
|
|
const viewFn = emitView(unit);
|
|
pool.statements.push(viewFn.toDeclStmt(viewFn.name));
|
|
}
|
|
}
|
|
function emitView(view) {
|
|
if (view.fnName === null) {
|
|
throw new Error(`AssertionError: view ${view.xref} is unnamed`);
|
|
}
|
|
const createStatements = [];
|
|
for (const op of view.create) {
|
|
if (op.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
}
|
|
createStatements.push(op.statement);
|
|
}
|
|
const updateStatements = [];
|
|
for (const op of view.update) {
|
|
if (op.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
}
|
|
updateStatements.push(op.statement);
|
|
}
|
|
const createCond = maybeGenerateRfBlock(1, createStatements);
|
|
const updateCond = maybeGenerateRfBlock(2, updateStatements);
|
|
return fn([new FnParam('rf'), new FnParam('ctx')], [...createCond, ...updateCond], undefined, undefined, view.fnName);
|
|
}
|
|
function maybeGenerateRfBlock(flag, statements) {
|
|
if (statements.length === 0) {
|
|
return [];
|
|
}
|
|
return [ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, variable('rf'), literal(flag)), statements)];
|
|
}
|
|
function emitHostBindingFunction(job) {
|
|
if (job.root.fnName === null) {
|
|
throw new Error(`AssertionError: host binding function is unnamed`);
|
|
}
|
|
const createStatements = [];
|
|
for (const op of job.root.create) {
|
|
if (op.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
}
|
|
createStatements.push(op.statement);
|
|
}
|
|
const updateStatements = [];
|
|
for (const op of job.root.update) {
|
|
if (op.kind !== OpKind.Statement) {
|
|
throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
}
|
|
updateStatements.push(op.statement);
|
|
}
|
|
if (createStatements.length === 0 && updateStatements.length === 0) {
|
|
return null;
|
|
}
|
|
const createCond = maybeGenerateRfBlock(1, createStatements);
|
|
const updateCond = maybeGenerateRfBlock(2, updateStatements);
|
|
return fn([new FnParam('rf'), new FnParam('ctx')], [...createCond, ...updateCond], undefined, undefined, job.root.fnName);
|
|
}
|
|
|
|
const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
|
|
const domSchema = new DomElementSchemaRegistry();
|
|
const NG_TEMPLATE_TAG_NAME = 'ng-template';
|
|
const ANIMATE_PREFIX$1 = 'animate.';
|
|
function isI18nRootNode(meta) {
|
|
return meta instanceof Message;
|
|
}
|
|
function isSingleI18nIcu(meta) {
|
|
return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu;
|
|
}
|
|
function ingestComponent(componentName, template, constantPool, compilationMode, relativeContextFilePath, i18nUseExternalIds, deferMeta, allDeferrableDepsFn, relativeTemplatePath, enableDebugLocations) {
|
|
const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, compilationMode, relativeContextFilePath, i18nUseExternalIds, deferMeta, allDeferrableDepsFn, relativeTemplatePath, enableDebugLocations);
|
|
ingestNodes(job.root, template);
|
|
return job;
|
|
}
|
|
function ingestHostBinding(input, bindingParser, constantPool) {
|
|
const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode, TemplateCompilationMode.DomOnly);
|
|
for (const property of input.properties ?? []) {
|
|
let bindingKind = BindingKind.Property;
|
|
if (property.name.startsWith('attr.')) {
|
|
property.name = property.name.substring('attr.'.length);
|
|
bindingKind = BindingKind.Attribute;
|
|
}
|
|
if (property.isLegacyAnimation) {
|
|
bindingKind = BindingKind.LegacyAnimation;
|
|
}
|
|
if (property.isAnimation) {
|
|
bindingKind = BindingKind.Animation;
|
|
}
|
|
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute).filter(context => context !== SecurityContext.NONE);
|
|
ingestDomProperty(job, property, bindingKind, securityContexts);
|
|
}
|
|
for (const [name, expr] of Object.entries(input.attributes) ?? []) {
|
|
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true).filter(context => context !== SecurityContext.NONE);
|
|
ingestHostAttribute(job, name, expr, securityContexts);
|
|
}
|
|
for (const event of input.events ?? []) {
|
|
ingestHostEvent(job, event);
|
|
}
|
|
return job;
|
|
}
|
|
function ingestDomProperty(job, property, bindingKind, securityContexts) {
|
|
let expression;
|
|
const ast = property.expression.ast;
|
|
if (ast instanceof Interpolation$1) {
|
|
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
|
|
} else {
|
|
expression = convertAst(ast, job, property.sourceSpan);
|
|
}
|
|
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, null, null, property.sourceSpan));
|
|
}
|
|
function ingestHostAttribute(job, name, value, securityContexts) {
|
|
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null, null, value.sourceSpan);
|
|
job.root.update.push(attrBinding);
|
|
}
|
|
function ingestHostEvent(job, event) {
|
|
let eventBinding;
|
|
if (event.type === ParsedEventType.Animation) {
|
|
eventBinding = createAnimationListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), event.name.endsWith('enter') ? "enter" : "leave", event.targetOrPhase, true, event.sourceSpan);
|
|
} else {
|
|
const [phase, target] = event.type !== ParsedEventType.LegacyAnimation ? [null, event.targetOrPhase] : [event.targetOrPhase, null];
|
|
eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
|
|
}
|
|
job.root.create.push(eventBinding);
|
|
}
|
|
function ingestNodes(unit, template) {
|
|
for (const node of template) {
|
|
if (node instanceof Element$1) {
|
|
ingestElement(unit, node);
|
|
} else if (node instanceof Template) {
|
|
ingestTemplate(unit, node);
|
|
} else if (node instanceof Content) {
|
|
ingestContent(unit, node);
|
|
} else if (node instanceof Text$3) {
|
|
ingestText(unit, node, null);
|
|
} else if (node instanceof BoundText) {
|
|
ingestBoundText(unit, node, null);
|
|
} else if (node instanceof IfBlock) {
|
|
ingestIfBlock(unit, node);
|
|
} else if (node instanceof SwitchBlock) {
|
|
ingestSwitchBlock(unit, node);
|
|
} else if (node instanceof DeferredBlock) {
|
|
ingestDeferBlock(unit, node);
|
|
} else if (node instanceof Icu$1) {
|
|
ingestIcu(unit, node);
|
|
} else if (node instanceof ForLoopBlock) {
|
|
ingestForBlock(unit, node);
|
|
} else if (node instanceof LetDeclaration$1) {
|
|
ingestLetDeclaration(unit, node);
|
|
} else if (node instanceof Component$1) ; else {
|
|
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
|
}
|
|
}
|
|
}
|
|
function ingestElement(unit, element) {
|
|
if (element.i18n !== undefined && !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
|
|
throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
|
|
}
|
|
const id = unit.job.allocateXrefId();
|
|
const [namespaceKey, elementName] = splitNsName(element.name);
|
|
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
|
|
unit.create.push(startOp);
|
|
ingestElementBindings(unit, startOp, element);
|
|
ingestReferences(startOp, element);
|
|
let i18nBlockId = null;
|
|
if (element.i18n instanceof Message) {
|
|
i18nBlockId = unit.job.allocateXrefId();
|
|
unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
|
|
}
|
|
ingestNodes(unit, element.children);
|
|
const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
|
|
unit.create.push(endOp);
|
|
const fieldInput = element.inputs.find(input => input.name === 'formField' && input.type === BindingType.Property);
|
|
if (fieldInput) {
|
|
unit.create.push(createControlCreateOp(fieldInput.sourceSpan));
|
|
}
|
|
if (i18nBlockId !== null) {
|
|
OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
|
|
}
|
|
}
|
|
function ingestTemplate(unit, tmpl) {
|
|
if (tmpl.i18n !== undefined && !(tmpl.i18n instanceof Message || tmpl.i18n instanceof TagPlaceholder)) {
|
|
throw Error(`Unhandled i18n metadata type for template: ${tmpl.i18n.constructor.name}`);
|
|
}
|
|
const childView = unit.job.allocateView(unit.xref);
|
|
let tagNameWithoutNamespace = tmpl.tagName;
|
|
let namespacePrefix = '';
|
|
if (tmpl.tagName) {
|
|
[namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
|
|
}
|
|
const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
|
|
const namespace = namespaceForKey(namespacePrefix);
|
|
const functionNameSuffix = tagNameWithoutNamespace === null ? '' : prefixWithNamespace(tagNameWithoutNamespace, namespace);
|
|
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
|
|
unit.create.push(templateOp);
|
|
ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
|
|
ingestReferences(templateOp, tmpl);
|
|
ingestNodes(childView, tmpl.children);
|
|
for (const {
|
|
name,
|
|
value
|
|
} of tmpl.variables) {
|
|
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
}
|
|
if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
|
|
const id = unit.job.allocateXrefId();
|
|
OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
|
|
OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
|
|
}
|
|
}
|
|
function ingestContent(unit, content) {
|
|
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
|
|
}
|
|
let fallbackView = null;
|
|
if (content.children.some(child => !(child instanceof Comment$1) && (!(child instanceof Text$3) || child.value.trim().length > 0))) {
|
|
fallbackView = unit.job.allocateView(unit.xref);
|
|
ingestNodes(fallbackView, content.children);
|
|
}
|
|
const id = unit.job.allocateXrefId();
|
|
const op = createProjectionOp(id, content.selector, content.i18n, fallbackView?.xref ?? null, content.sourceSpan);
|
|
for (const attr of content.attributes) {
|
|
const securityContext = domSchema.securityContext(content.name, attr.name, true);
|
|
unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
}
|
|
unit.create.push(op);
|
|
}
|
|
function ingestText(unit, text, icuPlaceholder) {
|
|
unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
|
|
}
|
|
function ingestBoundText(unit, text, icuPlaceholder) {
|
|
let value = text.value;
|
|
if (value instanceof ASTWithSource) {
|
|
value = value.ast;
|
|
}
|
|
if (!(value instanceof Interpolation$1)) {
|
|
throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`);
|
|
}
|
|
if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
|
|
throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
|
|
}
|
|
const i18nPlaceholders = text.i18n instanceof Container ? text.i18n.children.filter(node => node instanceof Placeholder).map(placeholder => placeholder.name) : [];
|
|
if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
|
|
throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
|
|
}
|
|
const textXref = unit.job.allocateXrefId();
|
|
unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
|
|
const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
|
|
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
|
|
}
|
|
function ingestIfBlock(unit, ifBlock) {
|
|
let firstXref = null;
|
|
let conditions = [];
|
|
for (let i = 0; i < ifBlock.branches.length; i++) {
|
|
const ifCase = ifBlock.branches[i];
|
|
const cView = unit.job.allocateView(unit.xref);
|
|
const tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase);
|
|
if (ifCase.expressionAlias !== null) {
|
|
cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
|
|
}
|
|
let ifCaseI18nMeta = undefined;
|
|
if (ifCase.i18n !== undefined) {
|
|
if (!(ifCase.i18n instanceof BlockPlaceholder)) {
|
|
throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
|
|
}
|
|
ifCaseI18nMeta = ifCase.i18n;
|
|
}
|
|
const createOp = i === 0 ? createConditionalCreateOp : createConditionalBranchCreateOp;
|
|
const conditionalCreateOp = createOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
|
|
unit.create.push(conditionalCreateOp);
|
|
if (firstXref === null) {
|
|
firstXref = cView.xref;
|
|
}
|
|
const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
|
|
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, conditionalCreateOp.xref, conditionalCreateOp.handle, ifCase.expressionAlias);
|
|
conditions.push(conditionalCaseExpr);
|
|
ingestNodes(cView, ifCase.children);
|
|
}
|
|
unit.update.push(createConditionalOp(firstXref, null, conditions, ifBlock.sourceSpan));
|
|
}
|
|
function ingestSwitchBlock(unit, switchBlock) {
|
|
if (switchBlock.groups.length === 0) {
|
|
return;
|
|
}
|
|
let firstXref = null;
|
|
let conditions = [];
|
|
for (let i = 0; i < switchBlock.groups.length; i++) {
|
|
const switchCaseGroup = switchBlock.groups[i];
|
|
const cView = unit.job.allocateView(unit.xref);
|
|
const tagName = ingestControlFlowInsertionPoint(unit, cView.xref, switchCaseGroup);
|
|
let switchCaseI18nMeta = undefined;
|
|
if (switchCaseGroup.i18n !== undefined) {
|
|
if (!(switchCaseGroup.i18n instanceof BlockPlaceholder)) {
|
|
throw Error(`Unhandled i18n metadata type for switch block: ${switchCaseGroup.i18n?.constructor.name}`);
|
|
}
|
|
switchCaseI18nMeta = switchCaseGroup.i18n;
|
|
}
|
|
const createOp = i === 0 ? createConditionalCreateOp : createConditionalBranchCreateOp;
|
|
const conditionalCreateOp = createOp(cView.xref, TemplateKind.Block, tagName, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCaseGroup.startSourceSpan, switchCaseGroup.sourceSpan);
|
|
unit.create.push(conditionalCreateOp);
|
|
if (firstXref === null) {
|
|
firstXref = cView.xref;
|
|
}
|
|
for (const switchCase of switchCaseGroup.cases) {
|
|
const caseExpr = switchCase.expression ? convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) : null;
|
|
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, conditionalCreateOp.xref, conditionalCreateOp.handle);
|
|
conditions.push(conditionalCaseExpr);
|
|
}
|
|
ingestNodes(cView, switchCaseGroup.children);
|
|
}
|
|
unit.update.push(createConditionalOp(firstXref, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan));
|
|
}
|
|
function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
|
|
if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
|
|
throw Error('Unhandled i18n metadata type for defer block');
|
|
}
|
|
if (children === undefined) {
|
|
return null;
|
|
}
|
|
const secondaryView = unit.job.allocateView(unit.xref);
|
|
ingestNodes(secondaryView, children);
|
|
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
|
|
unit.create.push(templateOp);
|
|
return templateOp;
|
|
}
|
|
function ingestDeferBlock(unit, deferBlock) {
|
|
let ownResolverFn = null;
|
|
if (unit.job.deferMeta.mode === 0) {
|
|
if (!unit.job.deferMeta.blocks.has(deferBlock)) {
|
|
throw new Error(`AssertionError: unable to find a dependency function for this deferred block`);
|
|
}
|
|
ownResolverFn = unit.job.deferMeta.blocks.get(deferBlock) ?? null;
|
|
}
|
|
const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
|
|
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
const deferXref = unit.job.allocateXrefId();
|
|
const deferOp = createDeferOp(deferXref, main.xref, main.handle, ownResolverFn, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan);
|
|
deferOp.placeholderView = placeholder?.xref ?? null;
|
|
deferOp.placeholderSlot = placeholder?.handle ?? null;
|
|
deferOp.loadingSlot = loading?.handle ?? null;
|
|
deferOp.errorSlot = error?.handle ?? null;
|
|
deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
|
|
deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
|
|
deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
|
|
deferOp.flags = calcDeferBlockFlags(deferBlock);
|
|
unit.create.push(deferOp);
|
|
const deferOnOps = [];
|
|
const deferWhenOps = [];
|
|
ingestDeferTriggers("hydrate", deferBlock.hydrateTriggers, deferOnOps, deferWhenOps, unit, deferXref);
|
|
ingestDeferTriggers("none", deferBlock.triggers, deferOnOps, deferWhenOps, unit, deferXref);
|
|
ingestDeferTriggers("prefetch", deferBlock.prefetchTriggers, deferOnOps, deferWhenOps, unit, deferXref);
|
|
const hasConcreteTrigger = deferOnOps.some(op => op.modifier === "none") || deferWhenOps.some(op => op.modifier === "none");
|
|
if (!hasConcreteTrigger) {
|
|
deferOnOps.push(createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Idle
|
|
}, "none", null));
|
|
}
|
|
unit.create.push(deferOnOps);
|
|
unit.update.push(deferWhenOps);
|
|
}
|
|
function calcDeferBlockFlags(deferBlockDetails) {
|
|
if (Object.keys(deferBlockDetails.hydrateTriggers).length > 0) {
|
|
return 1;
|
|
}
|
|
return null;
|
|
}
|
|
function ingestDeferTriggers(modifier, triggers, onOps, whenOps, unit, deferXref) {
|
|
if (triggers.idle !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Idle
|
|
}, modifier, triggers.idle.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.immediate !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Immediate
|
|
}, modifier, triggers.immediate.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.timer !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Timer,
|
|
delay: triggers.timer.delay
|
|
}, modifier, triggers.timer.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.hover !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Hover,
|
|
targetName: triggers.hover.reference,
|
|
targetXref: null,
|
|
targetSlot: null,
|
|
targetView: null,
|
|
targetSlotViewSteps: null
|
|
}, modifier, triggers.hover.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.interaction !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Interaction,
|
|
targetName: triggers.interaction.reference,
|
|
targetXref: null,
|
|
targetSlot: null,
|
|
targetView: null,
|
|
targetSlotViewSteps: null
|
|
}, modifier, triggers.interaction.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.viewport !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Viewport,
|
|
targetName: triggers.viewport.reference,
|
|
targetXref: null,
|
|
targetSlot: null,
|
|
targetView: null,
|
|
targetSlotViewSteps: null,
|
|
options: triggers.viewport.options ? convertAst(triggers.viewport.options, unit.job, triggers.viewport.sourceSpan) : null
|
|
}, modifier, triggers.viewport.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.never !== undefined) {
|
|
const deferOnOp = createDeferOnOp(deferXref, {
|
|
kind: DeferTriggerKind.Never
|
|
}, modifier, triggers.never.sourceSpan);
|
|
onOps.push(deferOnOp);
|
|
}
|
|
if (triggers.when !== undefined) {
|
|
if (triggers.when.value instanceof Interpolation$1) {
|
|
throw new Error(`Unexpected interpolation in defer block when trigger`);
|
|
}
|
|
const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), modifier, triggers.when.sourceSpan);
|
|
whenOps.push(deferOnOp);
|
|
}
|
|
}
|
|
function ingestIcu(unit, icu) {
|
|
if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) {
|
|
const xref = unit.job.allocateXrefId();
|
|
unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
|
|
for (const [placeholder, text] of Object.entries({
|
|
...icu.vars,
|
|
...icu.placeholders
|
|
})) {
|
|
if (text instanceof BoundText) {
|
|
ingestBoundText(unit, text, placeholder);
|
|
} else {
|
|
ingestText(unit, text, placeholder);
|
|
}
|
|
}
|
|
unit.create.push(createIcuEndOp(xref));
|
|
} else {
|
|
throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
|
|
}
|
|
}
|
|
function ingestForBlock(unit, forBlock) {
|
|
const repeaterView = unit.job.allocateView(unit.xref);
|
|
const indexName = `ɵ$index_${repeaterView.xref}`;
|
|
const countName = `ɵ$count_${repeaterView.xref}`;
|
|
const indexVarNames = new Set();
|
|
repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
|
|
for (const variable of forBlock.contextVariables) {
|
|
if (variable.value === '$index') {
|
|
indexVarNames.add(variable.name);
|
|
}
|
|
if (variable.name === '$index') {
|
|
repeaterView.contextVariables.set('$index', variable.value).set(indexName, variable.value);
|
|
} else if (variable.name === '$count') {
|
|
repeaterView.contextVariables.set('$count', variable.value).set(countName, variable.value);
|
|
} else {
|
|
repeaterView.aliases.add({
|
|
kind: SemanticVariableKind.Alias,
|
|
name: null,
|
|
identifier: variable.name,
|
|
expression: getComputedForLoopVariableExpression(variable, indexName, countName)
|
|
});
|
|
}
|
|
}
|
|
const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
|
|
const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
|
|
ingestNodes(repeaterView, forBlock.children);
|
|
let emptyView = null;
|
|
let emptyTagName = null;
|
|
if (forBlock.empty !== null) {
|
|
emptyView = unit.job.allocateView(unit.xref);
|
|
ingestNodes(emptyView, forBlock.empty.children);
|
|
emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
|
|
}
|
|
const varNames = {
|
|
$index: indexVarNames,
|
|
$implicit: forBlock.item.name
|
|
};
|
|
if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
|
|
throw Error('AssertionError: Unhandled i18n metadata type or @for');
|
|
}
|
|
if (forBlock.empty?.i18n !== undefined && !(forBlock.empty.i18n instanceof BlockPlaceholder)) {
|
|
throw Error('AssertionError: Unhandled i18n metadata type or @empty');
|
|
}
|
|
const i18nPlaceholder = forBlock.i18n;
|
|
const emptyI18nPlaceholder = forBlock.empty?.i18n;
|
|
const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
|
|
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
|
|
unit.create.push(repeaterCreate);
|
|
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
|
|
unit.update.push(repeater);
|
|
}
|
|
function getComputedForLoopVariableExpression(variable, indexName, countName) {
|
|
switch (variable.value) {
|
|
case '$index':
|
|
return new LexicalReadExpr(indexName);
|
|
case '$count':
|
|
return new LexicalReadExpr(countName);
|
|
case '$first':
|
|
return new LexicalReadExpr(indexName).identical(literal(0));
|
|
case '$last':
|
|
return new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1)));
|
|
case '$even':
|
|
return new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0));
|
|
case '$odd':
|
|
return new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0));
|
|
default:
|
|
throw new Error(`AssertionError: unknown @for loop variable ${variable.value}`);
|
|
}
|
|
}
|
|
function ingestLetDeclaration(unit, node) {
|
|
const target = unit.job.allocateXrefId();
|
|
unit.create.push(createDeclareLetOp(target, node.name, node.sourceSpan));
|
|
unit.update.push(createStoreLetOp(target, node.name, convertAst(node.value, unit.job, node.valueSpan), node.sourceSpan));
|
|
}
|
|
function convertAst(ast, job, baseSourceSpan) {
|
|
if (ast instanceof ASTWithSource) {
|
|
return convertAst(ast.ast, job, baseSourceSpan);
|
|
} else if (ast instanceof PropertyRead) {
|
|
if (ast.receiver instanceof ImplicitReceiver) {
|
|
return new LexicalReadExpr(ast.name);
|
|
} else {
|
|
return new ReadPropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, null, convertSourceSpan(ast.span, baseSourceSpan));
|
|
}
|
|
} else if (ast instanceof Call) {
|
|
if (ast.receiver instanceof ImplicitReceiver) {
|
|
throw new Error(`Unexpected ImplicitReceiver`);
|
|
} else {
|
|
return new InvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(arg => convertAst(arg, job, baseSourceSpan)), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
}
|
|
} else if (ast instanceof LiteralPrimitive) {
|
|
return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof Unary) {
|
|
switch (ast.operator) {
|
|
case '+':
|
|
return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
case '-':
|
|
return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
default:
|
|
throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
|
|
}
|
|
} else if (ast instanceof Binary) {
|
|
const operator = BINARY_OPERATORS.get(ast.operation);
|
|
if (operator === undefined) {
|
|
throw new Error(`AssertionError: unknown binary operator ${ast.operation}`);
|
|
}
|
|
return new BinaryOperatorExpr(operator, convertAst(ast.left, job, baseSourceSpan), convertAst(ast.right, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof ThisReceiver) {
|
|
return new ContextExpr(job.root.xref);
|
|
} else if (ast instanceof KeyedRead) {
|
|
return new ReadKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof Chain) {
|
|
throw new Error(`AssertionError: Chain in unknown context`);
|
|
} else if (ast instanceof LiteralMap) {
|
|
const entries = ast.keys.map((key, idx) => {
|
|
const value = convertAst(ast.values[idx], job, baseSourceSpan);
|
|
return key.kind === 'spread' ? new LiteralMapSpreadAssignment(value) : new LiteralMapPropertyAssignment(key.key, value, key.quoted);
|
|
});
|
|
return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof LiteralArray) {
|
|
return new LiteralArrayExpr(ast.expressions.map(expr => convertAst(expr, job, baseSourceSpan)));
|
|
} else if (ast instanceof Conditional) {
|
|
return new ConditionalExpr(convertAst(ast.condition, job, baseSourceSpan), convertAst(ast.trueExp, job, baseSourceSpan), convertAst(ast.falseExp, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof NonNullAssert) {
|
|
return convertAst(ast.expression, job, baseSourceSpan);
|
|
} else if (ast instanceof BindingPipe) {
|
|
return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [convertAst(ast.exp, job, baseSourceSpan), ...ast.args.map(arg => convertAst(arg, job, baseSourceSpan))]);
|
|
} else if (ast instanceof SafeKeyedRead) {
|
|
return new SafeKeyedReadExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof SafePropertyRead) {
|
|
return new SafePropertyReadExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name);
|
|
} else if (ast instanceof SafeCall) {
|
|
return new SafeInvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(a => convertAst(a, job, baseSourceSpan)));
|
|
} else if (ast instanceof EmptyExpr$1) {
|
|
return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof PrefixNot) {
|
|
return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof TypeofExpression) {
|
|
return typeofExpr(convertAst(ast.expression, job, baseSourceSpan));
|
|
} else if (ast instanceof VoidExpression) {
|
|
return new VoidExpr(convertAst(ast.expression, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof TemplateLiteral) {
|
|
return convertTemplateLiteral(ast, job, baseSourceSpan);
|
|
} else if (ast instanceof TaggedTemplateLiteral) {
|
|
return new TaggedTemplateLiteralExpr(convertAst(ast.tag, job, baseSourceSpan), convertTemplateLiteral(ast.template, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof ParenthesizedExpression) {
|
|
return new ParenthesizedExpr(convertAst(ast.expression, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
} else if (ast instanceof RegularExpressionLiteral) {
|
|
return new RegularExpressionLiteralExpr(ast.body, ast.flags, baseSourceSpan);
|
|
} else if (ast instanceof SpreadElement) {
|
|
return new SpreadElementExpr(convertAst(ast.expression, job, baseSourceSpan));
|
|
} else {
|
|
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
}
|
|
}
|
|
function convertTemplateLiteral(ast, job, baseSourceSpan) {
|
|
return new TemplateLiteralExpr(ast.elements.map(el => {
|
|
return new TemplateLiteralElementExpr(el.text, convertSourceSpan(el.span, baseSourceSpan));
|
|
}), ast.expressions.map(expr => convertAst(expr, job, baseSourceSpan)), convertSourceSpan(ast.span, baseSourceSpan));
|
|
}
|
|
function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
|
|
let expression;
|
|
if (value instanceof Interpolation$1) {
|
|
expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
|
|
} else if (value instanceof AST) {
|
|
expression = convertAst(value, job, null);
|
|
} else {
|
|
expression = literal(value);
|
|
}
|
|
return expression;
|
|
}
|
|
const BINDING_KINDS = new Map([[BindingType.Property, BindingKind.Property], [BindingType.TwoWay, BindingKind.TwoWayProperty], [BindingType.Attribute, BindingKind.Attribute], [BindingType.Class, BindingKind.ClassName], [BindingType.Style, BindingKind.StyleProperty], [BindingType.LegacyAnimation, BindingKind.LegacyAnimation], [BindingType.Animation, BindingKind.Animation]]);
|
|
function isPlainTemplate(tmpl) {
|
|
return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME;
|
|
}
|
|
function asMessage(i18nMeta) {
|
|
if (i18nMeta == null) {
|
|
return null;
|
|
}
|
|
if (!(i18nMeta instanceof Message)) {
|
|
throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
|
|
}
|
|
return i18nMeta;
|
|
}
|
|
function ingestElementBindings(unit, op, element) {
|
|
let bindings = new Array();
|
|
let i18nAttributeBindingNames = new Set();
|
|
for (const attr of element.attributes) {
|
|
const securityContext = domSchema.securityContext(element.name, attr.name, true);
|
|
bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
if (attr.i18n) {
|
|
i18nAttributeBindingNames.add(attr.name);
|
|
}
|
|
}
|
|
for (const input of element.inputs) {
|
|
if (i18nAttributeBindingNames.has(input.name)) {
|
|
console.error(`On component ${unit.job.componentName}, the binding ${input.name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`);
|
|
}
|
|
bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
|
|
}
|
|
unit.create.push(bindings.filter(b => b?.kind === OpKind.ExtractedAttribute));
|
|
unit.update.push(bindings.filter(b => b?.kind === OpKind.Binding));
|
|
for (const output of element.outputs) {
|
|
if (output.type === ParsedEventType.LegacyAnimation && output.phase === null) {
|
|
throw Error('Animation listener should have a phase');
|
|
}
|
|
if (output.type === ParsedEventType.TwoWay) {
|
|
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
} else if (output.type === ParsedEventType.Animation) {
|
|
unit.create.push(createAnimationListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.name.endsWith('enter') ? "enter" : "leave", output.target, false, output.sourceSpan));
|
|
} else {
|
|
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
}
|
|
}
|
|
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
}
|
|
}
|
|
function ingestTemplateBindings(unit, op, template, templateKind) {
|
|
let bindings = new Array();
|
|
for (const attr of template.templateAttrs) {
|
|
if (attr instanceof TextAttribute) {
|
|
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME, attr.name, true);
|
|
bindings.push(createTemplateBinding(unit, op.xref, BindingType.Attribute, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
} else {
|
|
bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
}
|
|
}
|
|
for (const attr of template.attributes) {
|
|
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME, attr.name, true);
|
|
bindings.push(createTemplateBinding(unit, op.xref, BindingType.Attribute, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
}
|
|
for (const input of template.inputs) {
|
|
bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
|
|
}
|
|
unit.create.push(bindings.filter(b => b?.kind === OpKind.ExtractedAttribute));
|
|
unit.update.push(bindings.filter(b => b?.kind === OpKind.Binding));
|
|
for (const output of template.outputs) {
|
|
if (output.type === ParsedEventType.LegacyAnimation && output.phase === null) {
|
|
throw Error('Animation listener should have a phase');
|
|
}
|
|
if (templateKind === TemplateKind.NgTemplate) {
|
|
if (output.type === ParsedEventType.TwoWay) {
|
|
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
} else {
|
|
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
}
|
|
}
|
|
if (templateKind === TemplateKind.Structural && output.type !== ParsedEventType.LegacyAnimation) {
|
|
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME, output.name, false);
|
|
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext));
|
|
}
|
|
}
|
|
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
}
|
|
}
|
|
function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
const isTextBinding = typeof value === 'string';
|
|
if (templateKind === TemplateKind.Structural) {
|
|
if (!isStructuralTemplateAttribute) {
|
|
switch (type) {
|
|
case BindingType.Property:
|
|
case BindingType.Class:
|
|
case BindingType.Style:
|
|
return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
|
|
case BindingType.TwoWay:
|
|
return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext);
|
|
}
|
|
}
|
|
if (!isTextBinding && (type === BindingType.Attribute || type === BindingType.LegacyAnimation || type === BindingType.Animation)) {
|
|
return null;
|
|
}
|
|
}
|
|
let bindingType = BINDING_KINDS.get(type);
|
|
if (templateKind === TemplateKind.NgTemplate) {
|
|
if (type === BindingType.Class || type === BindingType.Style || type === BindingType.Attribute && !isTextBinding) {
|
|
bindingType = BindingKind.Property;
|
|
}
|
|
}
|
|
return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
|
|
}
|
|
function makeListenerHandlerOps(unit, handler, handlerSpan) {
|
|
handler = astOf(handler);
|
|
const handlerOps = new Array();
|
|
let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
|
|
if (handlerExprs.length === 0) {
|
|
throw new Error('Expected listener to have non-empty expression list.');
|
|
}
|
|
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
|
|
const returnExpr = expressions.pop();
|
|
handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
|
|
handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
return handlerOps;
|
|
}
|
|
function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) {
|
|
handler = astOf(handler);
|
|
const handlerOps = new Array();
|
|
if (handler instanceof Chain) {
|
|
if (handler.expressions.length === 1) {
|
|
handler = handler.expressions[0];
|
|
} else {
|
|
throw new Error('Expected two-way listener to have a single expression.');
|
|
}
|
|
}
|
|
const handlerExpr = convertAst(handler, unit.job, handlerSpan);
|
|
const eventReference = new LexicalReadExpr('$event');
|
|
const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference);
|
|
handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
|
|
handlerOps.push(createStatementOp(new ReturnStatement(eventReference)));
|
|
return handlerOps;
|
|
}
|
|
function astOf(ast) {
|
|
return ast instanceof ASTWithSource ? ast.ast : ast;
|
|
}
|
|
function ingestReferences(op, element) {
|
|
assertIsArray(op.localRefs);
|
|
for (const {
|
|
name,
|
|
value
|
|
} of element.references) {
|
|
op.localRefs.push({
|
|
name,
|
|
target: value
|
|
});
|
|
}
|
|
}
|
|
function assertIsArray(value) {
|
|
if (!Array.isArray(value)) {
|
|
throw new Error(`AssertionError: expected an array`);
|
|
}
|
|
}
|
|
function convertSourceSpan(span, baseSourceSpan) {
|
|
if (baseSourceSpan === null) {
|
|
return null;
|
|
}
|
|
const start = baseSourceSpan.start.moveBy(span.start);
|
|
const end = baseSourceSpan.start.moveBy(span.end);
|
|
const fullStart = baseSourceSpan.fullStart.moveBy(span.start);
|
|
return new ParseSourceSpan(start, end, fullStart);
|
|
}
|
|
function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
let root = null;
|
|
for (const child of node.children) {
|
|
if (child instanceof Comment$1 || child instanceof LetDeclaration$1) {
|
|
continue;
|
|
}
|
|
if (root !== null) {
|
|
return null;
|
|
}
|
|
if (child instanceof Element$1 || child instanceof Template && child.tagName !== null) {
|
|
root = child;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
if (root !== null) {
|
|
for (const attr of root.attributes) {
|
|
if (!attr.name.startsWith(ANIMATE_PREFIX$1)) {
|
|
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME, attr.name, true);
|
|
unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
}
|
|
}
|
|
for (const attr of root.inputs) {
|
|
if (attr.type !== BindingType.LegacyAnimation && attr.type !== BindingType.Animation && attr.type !== BindingType.Attribute) {
|
|
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME, attr.name, true);
|
|
unit.create.push(createExtractedAttributeOp(xref, BindingKind.Property, null, attr.name, null, null, null, securityContext));
|
|
}
|
|
}
|
|
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
return tagName === NG_TEMPLATE_TAG_NAME ? null : tagName;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
let ENABLE_TEMPLATE_SOURCE_LOCATIONS = false;
|
|
function setEnableTemplateSourceLocations(value) {
|
|
ENABLE_TEMPLATE_SOURCE_LOCATIONS = value;
|
|
}
|
|
function getTemplateSourceLocationsEnabled() {
|
|
return ENABLE_TEMPLATE_SOURCE_LOCATIONS;
|
|
}
|
|
|
|
function renderFlagCheckIfStmt(flags, statements) {
|
|
return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null), statements);
|
|
}
|
|
function toQueryFlags(query) {
|
|
return (query.descendants ? 1 : 0) | (query.static ? 2 : 0) | (query.emitDistinctChangesOnly ? 4 : 0);
|
|
}
|
|
function getQueryPredicate(query, constantPool) {
|
|
if (Array.isArray(query.predicate)) {
|
|
let predicate = [];
|
|
query.predicate.forEach(selector => {
|
|
const selectors = selector.split(',').map(token => literal(token.trim()));
|
|
predicate.push(...selectors);
|
|
});
|
|
return constantPool.getConstLiteral(literalArr(predicate), true);
|
|
} else {
|
|
switch (query.predicate.forwardRef) {
|
|
case 0:
|
|
case 2:
|
|
return query.predicate.expression;
|
|
case 1:
|
|
return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]);
|
|
}
|
|
}
|
|
}
|
|
function getQueryCreateParameters(query, constantPool, prependParams) {
|
|
const parameters = [];
|
|
if (prependParams !== undefined) {
|
|
parameters.push(...prependParams);
|
|
}
|
|
if (query.isSignal) {
|
|
parameters.push(new ReadPropExpr(variable(CONTEXT_NAME), query.propertyName));
|
|
}
|
|
parameters.push(getQueryPredicate(query, constantPool), literal(toQueryFlags(query)));
|
|
if (query.read) {
|
|
parameters.push(query.read);
|
|
}
|
|
return parameters;
|
|
}
|
|
const queryAdvancePlaceholder = Symbol('queryAdvancePlaceholder');
|
|
function collapseAdvanceStatements(statements) {
|
|
const result = [];
|
|
let advanceCollapseCount = 0;
|
|
const flushAdvanceCount = () => {
|
|
if (advanceCollapseCount > 0) {
|
|
result.unshift(importExpr(Identifiers.queryAdvance).callFn(advanceCollapseCount === 1 ? [] : [literal(advanceCollapseCount)]).toStmt());
|
|
advanceCollapseCount = 0;
|
|
}
|
|
};
|
|
for (let i = statements.length - 1; i >= 0; i--) {
|
|
const st = statements[i];
|
|
if (st === queryAdvancePlaceholder) {
|
|
advanceCollapseCount++;
|
|
} else {
|
|
flushAdvanceCount();
|
|
result.unshift(st);
|
|
}
|
|
}
|
|
flushAdvanceCount();
|
|
return result;
|
|
}
|
|
function createViewQueriesFunction(viewQueries, constantPool, name) {
|
|
const createStatements = [];
|
|
const updateStatements = [];
|
|
const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
|
|
let viewQuerySignalCall = null;
|
|
let viewQueryCall = null;
|
|
viewQueries.forEach(query => {
|
|
const params = getQueryCreateParameters(query, constantPool);
|
|
if (query.isSignal) {
|
|
viewQuerySignalCall ??= importExpr(Identifiers.viewQuerySignal);
|
|
viewQuerySignalCall = viewQuerySignalCall.callFn(params);
|
|
} else {
|
|
viewQueryCall ??= importExpr(Identifiers.viewQuery);
|
|
viewQueryCall = viewQueryCall.callFn(params);
|
|
}
|
|
if (query.isSignal) {
|
|
updateStatements.push(queryAdvancePlaceholder);
|
|
return;
|
|
}
|
|
const temporary = tempAllocator();
|
|
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
const updateDirective = variable(CONTEXT_NAME).prop(query.propertyName).set(query.first ? temporary.prop('first') : temporary);
|
|
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
});
|
|
if (viewQuerySignalCall !== null) {
|
|
createStatements.push(new ExpressionStatement(viewQuerySignalCall));
|
|
}
|
|
if (viewQueryCall !== null) {
|
|
createStatements.push(new ExpressionStatement(viewQueryCall));
|
|
}
|
|
const viewQueryFnName = name ? `${name}_Query` : null;
|
|
return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [renderFlagCheckIfStmt(1, createStatements), renderFlagCheckIfStmt(2, collapseAdvanceStatements(updateStatements))], INFERRED_TYPE, null, viewQueryFnName);
|
|
}
|
|
function createContentQueriesFunction(queries, constantPool, name) {
|
|
const createStatements = [];
|
|
const updateStatements = [];
|
|
const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
|
|
let contentQuerySignalCall = null;
|
|
let contentQueryCall = null;
|
|
for (const query of queries) {
|
|
const params = getQueryCreateParameters(query, constantPool, [variable('dirIndex')]);
|
|
if (query.isSignal) {
|
|
contentQuerySignalCall ??= importExpr(Identifiers.contentQuerySignal);
|
|
contentQuerySignalCall = contentQuerySignalCall.callFn(params);
|
|
} else {
|
|
contentQueryCall ??= importExpr(Identifiers.contentQuery);
|
|
contentQueryCall = contentQueryCall.callFn(params);
|
|
}
|
|
if (query.isSignal) {
|
|
updateStatements.push(queryAdvancePlaceholder);
|
|
continue;
|
|
}
|
|
const temporary = tempAllocator();
|
|
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
const updateDirective = variable(CONTEXT_NAME).prop(query.propertyName).set(query.first ? temporary.prop('first') : temporary);
|
|
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
}
|
|
if (contentQuerySignalCall !== null) {
|
|
createStatements.push(new ExpressionStatement(contentQuerySignalCall));
|
|
}
|
|
if (contentQueryCall !== null) {
|
|
createStatements.push(new ExpressionStatement(contentQueryCall));
|
|
}
|
|
const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
|
|
return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null), new FnParam('dirIndex', null)], [renderFlagCheckIfStmt(1, createStatements), renderFlagCheckIfStmt(2, collapseAdvanceStatements(updateStatements))], INFERRED_TYPE, null, contentQueriesFnName);
|
|
}
|
|
|
|
class HtmlParser extends Parser$1 {
|
|
constructor() {
|
|
super(getHtmlTagDefinition);
|
|
}
|
|
parse(source, url, options) {
|
|
return super.parse(source, url, options);
|
|
}
|
|
}
|
|
|
|
const PROPERTY_PARTS_SEPARATOR = '.';
|
|
const ATTRIBUTE_PREFIX = 'attr';
|
|
const ANIMATE_PREFIX = 'animate';
|
|
const CLASS_PREFIX = 'class';
|
|
const STYLE_PREFIX = 'style';
|
|
const TEMPLATE_ATTR_PREFIX$1 = '*';
|
|
const LEGACY_ANIMATE_PROP_PREFIX = 'animate-';
|
|
class BindingParser {
|
|
_exprParser;
|
|
_schemaRegistry;
|
|
errors;
|
|
constructor(_exprParser, _schemaRegistry, errors) {
|
|
this._exprParser = _exprParser;
|
|
this._schemaRegistry = _schemaRegistry;
|
|
this.errors = errors;
|
|
}
|
|
createBoundHostProperties(properties, sourceSpan) {
|
|
const boundProps = [];
|
|
for (const propName of Object.keys(properties)) {
|
|
const expression = properties[propName];
|
|
if (typeof expression === 'string') {
|
|
this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [], boundProps, 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);
|
|
}
|
|
}
|
|
return boundProps;
|
|
}
|
|
createDirectiveHostEventAsts(hostListeners, sourceSpan) {
|
|
const targetEvents = [];
|
|
for (const propName of Object.keys(hostListeners)) {
|
|
const expression = hostListeners[propName];
|
|
if (typeof expression === 'string') {
|
|
this.parseEvent(propName, expression, false, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
|
|
} else {
|
|
this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
|
|
}
|
|
}
|
|
return targetEvents;
|
|
}
|
|
parseInterpolation(value, sourceSpan, interpolatedTokens) {
|
|
const absoluteOffset = sourceSpan.fullStart.offset;
|
|
try {
|
|
const ast = this._exprParser.parseInterpolation(value, sourceSpan, absoluteOffset, interpolatedTokens);
|
|
if (ast) {
|
|
this.errors.push(...ast.errors);
|
|
}
|
|
return ast;
|
|
} catch (e) {
|
|
this._reportError(`${e}`, sourceSpan);
|
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceSpan, absoluteOffset);
|
|
}
|
|
}
|
|
parseInterpolationExpression(expression, sourceSpan) {
|
|
const absoluteOffset = sourceSpan.start.offset;
|
|
try {
|
|
const ast = this._exprParser.parseInterpolationExpression(expression, sourceSpan, absoluteOffset);
|
|
if (ast) {
|
|
this.errors.push(...ast.errors);
|
|
}
|
|
return ast;
|
|
} catch (e) {
|
|
this._reportError(`${e}`, sourceSpan);
|
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceSpan, absoluteOffset);
|
|
}
|
|
}
|
|
parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
|
|
const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX$1.length;
|
|
const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
|
|
for (const binding of bindings) {
|
|
const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
|
|
const key = binding.key.source;
|
|
const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
|
|
if (binding instanceof VariableBinding) {
|
|
const value = binding.value ? binding.value.source : '$implicit';
|
|
const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
|
|
targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
|
|
} else if (binding.value) {
|
|
const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
|
|
const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
|
|
this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
} else {
|
|
targetMatchableAttrs.push([key, '']);
|
|
this.parseLiteralAttr(key, null, keySpan, absoluteValueOffset, undefined, targetMatchableAttrs, targetProps, keySpan);
|
|
}
|
|
}
|
|
}
|
|
_parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
|
|
try {
|
|
const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
|
|
bindingsResult.errors.forEach(e => this.errors.push(e));
|
|
bindingsResult.warnings.forEach(warning => {
|
|
this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
|
|
});
|
|
return bindingsResult.templateBindings;
|
|
} catch (e) {
|
|
this._reportError(`${e}`, sourceSpan);
|
|
return [];
|
|
}
|
|
}
|
|
parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
if (isLegacyAnimationLabel(name)) {
|
|
name = name.substring(1);
|
|
if (keySpan !== undefined) {
|
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
|
}
|
|
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.ERROR);
|
|
}
|
|
this._parseLegacyAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
} else {
|
|
targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
}
|
|
parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
if (name.length === 0) {
|
|
this._reportError(`Property name is missing in binding`, sourceSpan);
|
|
}
|
|
let isLegacyAnimationProp = false;
|
|
if (name.startsWith(LEGACY_ANIMATE_PROP_PREFIX)) {
|
|
isLegacyAnimationProp = true;
|
|
name = name.substring(LEGACY_ANIMATE_PROP_PREFIX.length);
|
|
if (keySpan !== undefined) {
|
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + LEGACY_ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
|
|
}
|
|
} else if (isLegacyAnimationLabel(name)) {
|
|
isLegacyAnimationProp = true;
|
|
name = name.substring(1);
|
|
if (keySpan !== undefined) {
|
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
|
}
|
|
}
|
|
if (isLegacyAnimationProp) {
|
|
this._parseLegacyAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
} else if (name.startsWith(`${ANIMATE_PREFIX}${PROPERTY_PARTS_SEPARATOR}`)) {
|
|
this._parseAnimation(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
} else {
|
|
this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
}
|
|
}
|
|
parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {
|
|
const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);
|
|
if (expr) {
|
|
this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
_parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
targetMatchableAttrs.push([name, ast.source]);
|
|
targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? ParsedPropertyType.TWO_WAY : ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
_parseAnimation(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
targetMatchableAttrs.push([name, ast.source]);
|
|
targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
_parseLegacyAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
if (name.length === 0) {
|
|
this._reportError('Animation trigger is missing', sourceSpan);
|
|
}
|
|
const ast = this.parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
|
|
targetMatchableAttrs.push([name, ast.source]);
|
|
targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.LEGACY_ANIMATION, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
|
|
try {
|
|
const ast = isHostBinding ? this._exprParser.parseSimpleBinding(value, sourceSpan, absoluteOffset) : this._exprParser.parseBinding(value, sourceSpan, absoluteOffset);
|
|
if (ast) {
|
|
this.errors.push(...ast.errors);
|
|
}
|
|
return ast;
|
|
} catch (e) {
|
|
this._reportError(`${e}`, sourceSpan);
|
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceSpan, absoluteOffset);
|
|
}
|
|
}
|
|
createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
|
|
if (boundProp.isLegacyAnimation) {
|
|
return new BoundElementProperty(boundProp.name, BindingType.LegacyAnimation, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
|
}
|
|
let unit = null;
|
|
let bindingType = undefined;
|
|
let boundPropertyName = null;
|
|
const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
|
let securityContexts = undefined;
|
|
if (parts.length > 1) {
|
|
if (parts[0] == ATTRIBUTE_PREFIX) {
|
|
boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
|
|
if (!skipValidation) {
|
|
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 = BindingType.Attribute;
|
|
} else if (parts[0] == CLASS_PREFIX) {
|
|
boundPropertyName = parts[1];
|
|
bindingType = BindingType.Class;
|
|
securityContexts = [SecurityContext.NONE];
|
|
} else if (parts[0] == STYLE_PREFIX) {
|
|
unit = parts.length > 2 ? parts[2] : null;
|
|
boundPropertyName = parts[1];
|
|
bindingType = BindingType.Style;
|
|
securityContexts = [SecurityContext.STYLE];
|
|
} else if (parts[0] == ANIMATE_PREFIX) {
|
|
boundPropertyName = boundProp.name;
|
|
bindingType = BindingType.Animation;
|
|
securityContexts = [SecurityContext.NONE];
|
|
}
|
|
}
|
|
if (boundPropertyName === null) {
|
|
const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
|
boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
|
|
securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
|
|
bindingType = boundProp.type === ParsedPropertyType.TWO_WAY ? BindingType.TwoWay : BindingType.Property;
|
|
if (!skipValidation) {
|
|
this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
|
|
}
|
|
}
|
|
return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
|
}
|
|
parseEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
|
if (name.length === 0) {
|
|
this._reportError(`Event name is missing in binding`, sourceSpan);
|
|
}
|
|
if (isLegacyAnimationLabel(name)) {
|
|
name = name.slice(1);
|
|
if (keySpan !== undefined) {
|
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
|
}
|
|
this._parseLegacyAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
|
|
} else {
|
|
this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
|
|
}
|
|
}
|
|
calcPossibleSecurityContexts(selector, propName, isAttribute) {
|
|
const prop = this._schemaRegistry.getMappedPropName(propName);
|
|
return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
|
|
}
|
|
parseEventListenerName(rawName) {
|
|
const [target, eventName] = splitAtColon(rawName, [null, rawName]);
|
|
return {
|
|
eventName: eventName,
|
|
target
|
|
};
|
|
}
|
|
parseLegacyAnimationEventName(rawName) {
|
|
const matches = splitAtPeriod(rawName, [rawName, null]);
|
|
return {
|
|
eventName: matches[0],
|
|
phase: matches[1] === null ? null : matches[1].toLowerCase()
|
|
};
|
|
}
|
|
_parseLegacyAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
|
|
const {
|
|
eventName,
|
|
phase
|
|
} = this.parseLegacyAnimationEventName(name);
|
|
const ast = this._parseAction(expression, handlerSpan);
|
|
targetEvents.push(new ParsedEvent(eventName, phase, ParsedEventType.LegacyAnimation, ast, sourceSpan, handlerSpan, keySpan));
|
|
if (eventName.length === 0) {
|
|
this._reportError(`Animation event name is missing in binding`, sourceSpan);
|
|
}
|
|
if (phase) {
|
|
if (phase !== 'start' && phase !== 'done') {
|
|
this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
|
|
}
|
|
} else {
|
|
this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
|
|
}
|
|
}
|
|
_parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
|
const {
|
|
eventName,
|
|
target
|
|
} = this.parseEventListenerName(name);
|
|
const prevErrorCount = this.errors.length;
|
|
const ast = this._parseAction(expression, handlerSpan);
|
|
const isValid = this.errors.length === prevErrorCount;
|
|
targetMatchableAttrs.push([name, ast.source]);
|
|
if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) {
|
|
this._reportError('Unsupported expression in a two-way binding', sourceSpan);
|
|
}
|
|
let eventType = ParsedEventType.Regular;
|
|
if (isAssignmentEvent) {
|
|
eventType = ParsedEventType.TwoWay;
|
|
}
|
|
if (name.startsWith(`${ANIMATE_PREFIX}${PROPERTY_PARTS_SEPARATOR}`)) {
|
|
eventType = ParsedEventType.Animation;
|
|
}
|
|
targetEvents.push(new ParsedEvent(eventName, target, eventType, ast, sourceSpan, handlerSpan, keySpan));
|
|
}
|
|
_parseAction(value, sourceSpan) {
|
|
const absoluteOffset = sourceSpan && sourceSpan.start ? sourceSpan.start.offset : 0;
|
|
try {
|
|
const ast = this._exprParser.parseAction(value, sourceSpan, absoluteOffset);
|
|
if (ast) {
|
|
this.errors.push(...ast.errors);
|
|
}
|
|
if (!ast || ast.ast instanceof EmptyExpr$1) {
|
|
this._reportError(`Empty expressions are not allowed`, sourceSpan);
|
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceSpan, absoluteOffset);
|
|
}
|
|
return ast;
|
|
} catch (e) {
|
|
this._reportError(`${e}`, sourceSpan);
|
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceSpan, absoluteOffset);
|
|
}
|
|
}
|
|
_reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
|
this.errors.push(new ParseError(sourceSpan, message, level));
|
|
}
|
|
_validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
|
|
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : this._schemaRegistry.validateProperty(propName);
|
|
if (report.error) {
|
|
this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
|
|
}
|
|
}
|
|
_isAllowedAssignmentEvent(ast) {
|
|
if (ast instanceof ASTWithSource) {
|
|
return this._isAllowedAssignmentEvent(ast.ast);
|
|
}
|
|
if (ast instanceof NonNullAssert) {
|
|
return this._isAllowedAssignmentEvent(ast.expression);
|
|
}
|
|
if (ast instanceof Call && ast.args.length === 1 && ast.receiver instanceof PropertyRead && ast.receiver.name === '$any' && ast.receiver.receiver instanceof ImplicitReceiver) {
|
|
return this._isAllowedAssignmentEvent(ast.args[0]);
|
|
}
|
|
if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
|
|
if (!hasRecursiveSafeReceiver(ast)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
function hasRecursiveSafeReceiver(ast) {
|
|
if (ast instanceof SafePropertyRead || ast instanceof SafeKeyedRead) {
|
|
return true;
|
|
}
|
|
if (ast instanceof ParenthesizedExpression) {
|
|
return hasRecursiveSafeReceiver(ast.expression);
|
|
}
|
|
if (ast instanceof PropertyRead || ast instanceof KeyedRead || ast instanceof Call) {
|
|
return hasRecursiveSafeReceiver(ast.receiver);
|
|
}
|
|
return false;
|
|
}
|
|
function isLegacyAnimationLabel(name) {
|
|
return name[0] == '@';
|
|
}
|
|
function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
|
|
let ctxs;
|
|
const nameToContext = elName => registry.securityContext(elName, propName, isAttribute);
|
|
if (selector === null) {
|
|
ctxs = registry.allKnownElementNames().map(nameToContext);
|
|
} else {
|
|
ctxs = [];
|
|
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(elName => !notElementNames.has(elName));
|
|
ctxs.push(...possibleElementNames.map(nameToContext));
|
|
});
|
|
}
|
|
return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
|
|
}
|
|
function moveParseSourceSpan(sourceSpan, absoluteSpan) {
|
|
const startDiff = absoluteSpan.start - sourceSpan.start.offset;
|
|
const endDiff = absoluteSpan.end - sourceSpan.end.offset;
|
|
return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
|
|
}
|
|
|
|
function isStyleUrlResolvable(url) {
|
|
if (url == null || url.length === 0 || url[0] == '/') return false;
|
|
const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
|
|
return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
|
|
}
|
|
const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
|
|
|
|
const NG_CONTENT_SELECT_ATTR = 'select';
|
|
const LINK_ELEMENT = 'link';
|
|
const LINK_STYLE_REL_ATTR = 'rel';
|
|
const LINK_STYLE_HREF_ATTR = 'href';
|
|
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
|
const STYLE_ELEMENT = 'style';
|
|
const SCRIPT_ELEMENT = 'script';
|
|
const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
|
|
const NG_PROJECT_AS = 'ngProjectAs';
|
|
function preparseElement(ast) {
|
|
let selectAttr = null;
|
|
let hrefAttr = null;
|
|
let relAttr = null;
|
|
let nonBindable = false;
|
|
let projectAs = '';
|
|
ast.attrs.forEach(attr => {
|
|
const lcAttrName = attr.name.toLowerCase();
|
|
if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
|
|
selectAttr = attr.value;
|
|
} else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
|
|
hrefAttr = attr.value;
|
|
} else if (lcAttrName == LINK_STYLE_REL_ATTR) {
|
|
relAttr = attr.value;
|
|
} else if (attr.name == NG_NON_BINDABLE_ATTR) {
|
|
nonBindable = true;
|
|
} else if (attr.name == NG_PROJECT_AS) {
|
|
if (attr.value.length > 0) {
|
|
projectAs = attr.value;
|
|
}
|
|
}
|
|
});
|
|
selectAttr = normalizeNgContentSelect(selectAttr);
|
|
const nodeName = ast.name.toLowerCase();
|
|
let type = PreparsedElementType.OTHER;
|
|
if (isNgContent(nodeName)) {
|
|
type = PreparsedElementType.NG_CONTENT;
|
|
} else if (nodeName == STYLE_ELEMENT) {
|
|
type = PreparsedElementType.STYLE;
|
|
} else if (nodeName == SCRIPT_ELEMENT) {
|
|
type = PreparsedElementType.SCRIPT;
|
|
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
|
type = PreparsedElementType.STYLESHEET;
|
|
}
|
|
return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
|
|
}
|
|
var PreparsedElementType;
|
|
(function (PreparsedElementType) {
|
|
PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
|
|
PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
|
|
PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
|
|
PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
|
|
PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
|
|
})(PreparsedElementType || (PreparsedElementType = {}));
|
|
class PreparsedElement {
|
|
type;
|
|
selectAttr;
|
|
hrefAttr;
|
|
nonBindable;
|
|
projectAs;
|
|
constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
|
|
this.type = type;
|
|
this.selectAttr = selectAttr;
|
|
this.hrefAttr = hrefAttr;
|
|
this.nonBindable = nonBindable;
|
|
this.projectAs = projectAs;
|
|
}
|
|
}
|
|
function normalizeNgContentSelect(selectAttr) {
|
|
if (selectAttr === null || selectAttr.length === 0) {
|
|
return '*';
|
|
}
|
|
return selectAttr;
|
|
}
|
|
|
|
const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
|
|
const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
|
|
const CONDITIONAL_ALIAS_PATTERN = /^(as\s+)(.*)/;
|
|
const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/;
|
|
const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/;
|
|
const IDENTIFIER_PATTERN = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/;
|
|
const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
|
|
function isConnectedForLoopBlock(name) {
|
|
return name === 'empty';
|
|
}
|
|
function isConnectedIfLoopBlock(name) {
|
|
return name === 'else' || ELSE_IF_PATTERN.test(name);
|
|
}
|
|
function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
|
|
const errors = validateIfConnectedBlocks(connectedBlocks);
|
|
const branches = [];
|
|
const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
|
|
if (mainBlockParams !== null) {
|
|
branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan, ast.i18n));
|
|
}
|
|
for (const block of connectedBlocks) {
|
|
if (ELSE_IF_PATTERN.test(block.name)) {
|
|
const params = parseConditionalBlockParameters(block, errors, bindingParser);
|
|
if (params !== null) {
|
|
const children = visitAll(visitor, block.children, block.children);
|
|
branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
|
|
}
|
|
} else if (block.name === 'else') {
|
|
const children = visitAll(visitor, block.children, block.children);
|
|
branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
|
|
}
|
|
}
|
|
const ifBlockStartSourceSpan = branches.length > 0 ? branches[0].startSourceSpan : ast.startSourceSpan;
|
|
const ifBlockEndSourceSpan = branches.length > 0 ? branches[branches.length - 1].endSourceSpan : ast.endSourceSpan;
|
|
let wholeSourceSpan = ast.sourceSpan;
|
|
const lastBranch = branches[branches.length - 1];
|
|
if (lastBranch !== undefined) {
|
|
wholeSourceSpan = new ParseSourceSpan(ifBlockStartSourceSpan.start, lastBranch.sourceSpan.end);
|
|
}
|
|
return {
|
|
node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan, ast.nameSpan),
|
|
errors
|
|
};
|
|
}
|
|
function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
|
|
const errors = [];
|
|
const params = parseForLoopParameters(ast, errors, bindingParser);
|
|
let node = null;
|
|
let empty = null;
|
|
for (const block of connectedBlocks) {
|
|
if (block.name === 'empty') {
|
|
if (empty !== null) {
|
|
errors.push(new ParseError(block.sourceSpan, '@for loop can only have one @empty block'));
|
|
} else if (block.parameters.length > 0) {
|
|
errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
|
|
} else {
|
|
empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n);
|
|
}
|
|
} else {
|
|
errors.push(new ParseError(block.sourceSpan, `Unrecognized @for loop block "${block.name}"`));
|
|
}
|
|
}
|
|
if (params !== null) {
|
|
if (params.trackBy === null) {
|
|
errors.push(new ParseError(ast.startSourceSpan, '@for loop must have a "track" expression'));
|
|
} else {
|
|
const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
|
|
const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end);
|
|
validateTrackByExpression(params.trackBy.expression, params.trackBy.keywordSpan, errors);
|
|
node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan, ast.i18n);
|
|
}
|
|
}
|
|
return {
|
|
node,
|
|
errors
|
|
};
|
|
}
|
|
function createSwitchBlock(ast, visitor, bindingParser) {
|
|
const errors = validateSwitchBlock(ast);
|
|
const primaryExpression = ast.parameters.length > 0 ? parseBlockParameterToBinding(ast.parameters[0], bindingParser) : bindingParser.parseBinding('', false, ast.sourceSpan, 0);
|
|
const groups = [];
|
|
const unknownBlocks = [];
|
|
let collectedCases = [];
|
|
let firstCaseStart = null;
|
|
for (const node of ast.children) {
|
|
if (!(node instanceof Block)) {
|
|
continue;
|
|
}
|
|
if ((node.name !== 'case' || node.parameters.length === 0) && node.name !== 'default') {
|
|
unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan, node.nameSpan));
|
|
continue;
|
|
}
|
|
const isCase = node.name === 'case';
|
|
let expression = null;
|
|
if (isCase) {
|
|
expression = parseBlockParameterToBinding(node.parameters[0], bindingParser);
|
|
}
|
|
const switchCase = new SwitchBlockCase(expression, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
|
|
collectedCases.push(switchCase);
|
|
const caseWithoutBody = node.children.length === 0 && node.endSourceSpan !== null && node.endSourceSpan.start.offset === node.endSourceSpan.end.offset;
|
|
if (caseWithoutBody) {
|
|
if (firstCaseStart === null) {
|
|
firstCaseStart = node.sourceSpan;
|
|
}
|
|
continue;
|
|
}
|
|
let sourceSpan = node.sourceSpan;
|
|
let startSourceSpan = node.startSourceSpan;
|
|
if (firstCaseStart !== null) {
|
|
sourceSpan = new ParseSourceSpan(firstCaseStart.start, node.sourceSpan.end);
|
|
startSourceSpan = new ParseSourceSpan(firstCaseStart.start, node.startSourceSpan.end);
|
|
firstCaseStart = null;
|
|
}
|
|
const group = new SwitchBlockCaseGroup(collectedCases, visitAll(visitor, node.children, node.children), sourceSpan, startSourceSpan, node.endSourceSpan, node.nameSpan, node.i18n);
|
|
groups.push(group);
|
|
collectedCases = [];
|
|
}
|
|
const node = new SwitchBlock(primaryExpression, groups, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan);
|
|
return {
|
|
node,
|
|
errors
|
|
};
|
|
}
|
|
function parseForLoopParameters(block, errors, bindingParser) {
|
|
if (block.parameters.length === 0) {
|
|
errors.push(new ParseError(block.startSourceSpan, '@for loop does not have an expression'));
|
|
return null;
|
|
}
|
|
const [expressionParam, ...secondaryParams] = block.parameters;
|
|
const match = stripOptionalParentheses(expressionParam, errors)?.match(FOR_LOOP_EXPRESSION_PATTERN);
|
|
if (!match || match[2].trim().length === 0) {
|
|
errors.push(new ParseError(expressionParam.sourceSpan, 'Cannot parse expression. @for loop expression must match the pattern "<identifier> of <expression>"'));
|
|
return null;
|
|
}
|
|
const [, itemName, rawExpression] = match;
|
|
if (ALLOWED_FOR_LOOP_LET_VARIABLES.has(itemName)) {
|
|
errors.push(new ParseError(expressionParam.sourceSpan, `@for loop item name cannot be one of ${Array.from(ALLOWED_FOR_LOOP_LET_VARIABLES).join(', ')}.`));
|
|
}
|
|
const variableName = expressionParam.expression.split(' ')[0];
|
|
const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length));
|
|
const result = {
|
|
itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan),
|
|
trackBy: null,
|
|
expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
|
|
context: Array.from(ALLOWED_FOR_LOOP_LET_VARIABLES, variableName => {
|
|
const emptySpanAfterForBlockStart = new ParseSourceSpan(block.startSourceSpan.end, block.startSourceSpan.end);
|
|
return new Variable(variableName, variableName, emptySpanAfterForBlockStart, emptySpanAfterForBlockStart);
|
|
})
|
|
};
|
|
for (const param of secondaryParams) {
|
|
const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
|
|
if (letMatch !== null) {
|
|
const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end);
|
|
parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, itemName, result.context, errors);
|
|
continue;
|
|
}
|
|
const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
|
|
if (trackMatch !== null) {
|
|
if (result.trackBy !== null) {
|
|
errors.push(new ParseError(param.sourceSpan, '@for loop can only have one "track" expression'));
|
|
} else {
|
|
const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
|
|
if (expression.ast instanceof EmptyExpr$1) {
|
|
errors.push(new ParseError(block.startSourceSpan, '@for loop must have a "track" expression'));
|
|
}
|
|
const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length));
|
|
result.trackBy = {
|
|
expression,
|
|
keywordSpan
|
|
};
|
|
}
|
|
continue;
|
|
}
|
|
errors.push(new ParseError(param.sourceSpan, `Unrecognized @for loop parameter "${param.expression}"`));
|
|
}
|
|
return result;
|
|
}
|
|
function validateTrackByExpression(expression, parseSourceSpan, errors) {
|
|
const visitor = new PipeVisitor();
|
|
expression.ast.visit(visitor);
|
|
if (visitor.hasPipe) {
|
|
errors.push(new ParseError(parseSourceSpan, 'Cannot use pipes in track expressions'));
|
|
}
|
|
}
|
|
function parseLetParameter(sourceSpan, expression, span, loopItemName, context, errors) {
|
|
const parts = expression.split(',');
|
|
let startSpan = span.start;
|
|
for (const part of parts) {
|
|
const expressionParts = part.split('=');
|
|
const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
|
|
const variableName = expressionParts.length === 2 ? expressionParts[1].trim() : '';
|
|
if (name.length === 0 || variableName.length === 0) {
|
|
errors.push(new ParseError(sourceSpan, `Invalid @for loop "let" parameter. Parameter should match the pattern "<name> = <variable name>"`));
|
|
} else if (!ALLOWED_FOR_LOOP_LET_VARIABLES.has(variableName)) {
|
|
errors.push(new ParseError(sourceSpan, `Unknown "let" parameter variable "${variableName}". The allowed variables are: ${Array.from(ALLOWED_FOR_LOOP_LET_VARIABLES).join(', ')}`));
|
|
} else if (name === loopItemName) {
|
|
errors.push(new ParseError(sourceSpan, `Invalid @for loop "let" parameter. Variable cannot be called "${loopItemName}"`));
|
|
} else if (context.some(v => v.name === name)) {
|
|
errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
|
|
} else {
|
|
const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ? new ParseSourceSpan(startSpan.moveBy(keyLeadingWhitespace.length), startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) : span;
|
|
let valueSpan = undefined;
|
|
if (expressionParts.length === 2) {
|
|
const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
valueSpan = valueLeadingWhitespace !== undefined ? new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length + implicit.length)) : undefined;
|
|
}
|
|
const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end);
|
|
context.push(new Variable(name, variableName, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
startSpan = startSpan.moveBy(part.length + 1);
|
|
}
|
|
}
|
|
function validateIfConnectedBlocks(connectedBlocks) {
|
|
const errors = [];
|
|
let hasElse = false;
|
|
for (let i = 0; i < connectedBlocks.length; i++) {
|
|
const block = connectedBlocks[i];
|
|
if (block.name === 'else') {
|
|
if (hasElse) {
|
|
errors.push(new ParseError(block.startSourceSpan, 'Conditional can only have one @else block'));
|
|
} else if (connectedBlocks.length > 1 && i < connectedBlocks.length - 1) {
|
|
errors.push(new ParseError(block.startSourceSpan, '@else block must be last inside the conditional'));
|
|
} else if (block.parameters.length > 0) {
|
|
errors.push(new ParseError(block.startSourceSpan, '@else block cannot have parameters'));
|
|
}
|
|
hasElse = true;
|
|
} else if (!ELSE_IF_PATTERN.test(block.name)) {
|
|
errors.push(new ParseError(block.startSourceSpan, `Unrecognized conditional block @${block.name}`));
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
function validateSwitchBlock(ast) {
|
|
const errors = [];
|
|
let hasDefault = false;
|
|
if (ast.parameters.length !== 1) {
|
|
errors.push(new ParseError(ast.startSourceSpan, '@switch block must have exactly one parameter'));
|
|
return errors;
|
|
}
|
|
for (const node of ast.children) {
|
|
if (node instanceof Comment || node instanceof Text && node.value.trim().length === 0) {
|
|
continue;
|
|
}
|
|
if (!(node instanceof Block) || node.name !== 'case' && node.name !== 'default') {
|
|
errors.push(new ParseError(node.sourceSpan, '@switch block can only contain @case and @default blocks'));
|
|
continue;
|
|
}
|
|
if (node.name === 'default') {
|
|
if (hasDefault) {
|
|
errors.push(new ParseError(node.startSourceSpan, '@switch block can only have one @default block'));
|
|
} else if (node.parameters.length > 0) {
|
|
errors.push(new ParseError(node.startSourceSpan, '@default block cannot have parameters'));
|
|
}
|
|
hasDefault = true;
|
|
} else if (node.name === 'case' && node.parameters.length !== 1) {
|
|
errors.push(new ParseError(node.startSourceSpan, '@case block must have exactly one parameter'));
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
function parseBlockParameterToBinding(ast, bindingParser, part) {
|
|
let start;
|
|
let end;
|
|
if (typeof part === 'string') {
|
|
start = Math.max(0, ast.expression.lastIndexOf(part));
|
|
end = start + part.length;
|
|
} else {
|
|
start = 0;
|
|
end = ast.expression.length;
|
|
}
|
|
return bindingParser.parseBinding(ast.expression.slice(start, end), false, ast.sourceSpan, ast.sourceSpan.start.offset + start);
|
|
}
|
|
function parseConditionalBlockParameters(block, errors, bindingParser) {
|
|
if (block.parameters.length === 0) {
|
|
errors.push(new ParseError(block.startSourceSpan, 'Conditional block does not have an expression'));
|
|
return null;
|
|
}
|
|
const expression = parseBlockParameterToBinding(block.parameters[0], bindingParser);
|
|
let expressionAlias = null;
|
|
for (let i = 1; i < block.parameters.length; i++) {
|
|
const param = block.parameters[i];
|
|
const aliasMatch = param.expression.match(CONDITIONAL_ALIAS_PATTERN);
|
|
if (aliasMatch === null) {
|
|
errors.push(new ParseError(param.sourceSpan, `Unrecognized conditional parameter "${param.expression}"`));
|
|
} else if (block.name !== 'if' && !ELSE_IF_PATTERN.test(block.name)) {
|
|
errors.push(new ParseError(param.sourceSpan, '"as" expression is only allowed on `@if` and `@else if` blocks'));
|
|
} else if (expressionAlias !== null) {
|
|
errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
|
|
} else {
|
|
const name = aliasMatch[2].trim();
|
|
if (IDENTIFIER_PATTERN.test(name)) {
|
|
const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length);
|
|
const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length));
|
|
expressionAlias = new Variable(name, name, variableSpan, variableSpan);
|
|
} else {
|
|
errors.push(new ParseError(param.sourceSpan, '"as" expression must be a valid JavaScript identifier'));
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
expression,
|
|
expressionAlias
|
|
};
|
|
}
|
|
function stripOptionalParentheses(param, errors) {
|
|
const expression = param.expression;
|
|
const spaceRegex = /^\s$/;
|
|
let openParens = 0;
|
|
let start = 0;
|
|
let end = expression.length - 1;
|
|
for (let i = 0; i < expression.length; i++) {
|
|
const char = expression[i];
|
|
if (char === '(') {
|
|
start = i + 1;
|
|
openParens++;
|
|
} else if (spaceRegex.test(char)) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (openParens === 0) {
|
|
return expression;
|
|
}
|
|
for (let i = expression.length - 1; i > -1; i--) {
|
|
const char = expression[i];
|
|
if (char === ')') {
|
|
end = i;
|
|
openParens--;
|
|
if (openParens === 0) {
|
|
break;
|
|
}
|
|
} else if (spaceRegex.test(char)) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (openParens !== 0) {
|
|
errors.push(new ParseError(param.sourceSpan, 'Unclosed parentheses in expression'));
|
|
return null;
|
|
}
|
|
return expression.slice(start, end);
|
|
}
|
|
class PipeVisitor extends RecursiveAstVisitor {
|
|
hasPipe = false;
|
|
visitPipe() {
|
|
this.hasPipe = true;
|
|
}
|
|
}
|
|
|
|
const TIME_PATTERN = /^\d+\.?\d*(ms|s)?$/;
|
|
const SEPARATOR_PATTERN = /^\s$/;
|
|
const COMMA_DELIMITED_SYNTAX = new Map([[$LBRACE, $RBRACE], [$LBRACKET, $RBRACKET], [$LPAREN, $RPAREN]]);
|
|
var OnTriggerType;
|
|
(function (OnTriggerType) {
|
|
OnTriggerType["IDLE"] = "idle";
|
|
OnTriggerType["TIMER"] = "timer";
|
|
OnTriggerType["INTERACTION"] = "interaction";
|
|
OnTriggerType["IMMEDIATE"] = "immediate";
|
|
OnTriggerType["HOVER"] = "hover";
|
|
OnTriggerType["VIEWPORT"] = "viewport";
|
|
OnTriggerType["NEVER"] = "never";
|
|
})(OnTriggerType || (OnTriggerType = {}));
|
|
function parseNeverTrigger({
|
|
expression,
|
|
sourceSpan
|
|
}, triggers, errors) {
|
|
const neverIndex = expression.indexOf('never');
|
|
const neverSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(neverIndex), sourceSpan.start.moveBy(neverIndex + 'never'.length));
|
|
const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
|
|
const hydrateSpan = getHydrateSpan(expression, sourceSpan);
|
|
if (neverIndex === -1) {
|
|
errors.push(new ParseError(sourceSpan, `Could not find "never" keyword in expression`));
|
|
} else {
|
|
trackTrigger('never', triggers, errors, new NeverDeferredTrigger(neverSourceSpan, sourceSpan, prefetchSpan, null, hydrateSpan));
|
|
}
|
|
}
|
|
function parseWhenTrigger({
|
|
expression,
|
|
sourceSpan
|
|
}, bindingParser, triggers, errors) {
|
|
const whenIndex = expression.indexOf('when');
|
|
const whenSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(whenIndex), sourceSpan.start.moveBy(whenIndex + 'when'.length));
|
|
const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
|
|
const hydrateSpan = getHydrateSpan(expression, sourceSpan);
|
|
if (whenIndex === -1) {
|
|
errors.push(new ParseError(sourceSpan, `Could not find "when" keyword in expression`));
|
|
} else {
|
|
const start = getTriggerParametersStart(expression, whenIndex + 1);
|
|
const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);
|
|
trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan, prefetchSpan, whenSourceSpan, hydrateSpan));
|
|
}
|
|
}
|
|
function parseOnTrigger({
|
|
expression,
|
|
sourceSpan
|
|
}, bindingParser, triggers, errors, placeholder) {
|
|
const onIndex = expression.indexOf('on');
|
|
const onSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(onIndex), sourceSpan.start.moveBy(onIndex + 'on'.length));
|
|
const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
|
|
const hydrateSpan = getHydrateSpan(expression, sourceSpan);
|
|
if (onIndex === -1) {
|
|
errors.push(new ParseError(sourceSpan, `Could not find "on" keyword in expression`));
|
|
} else {
|
|
const start = getTriggerParametersStart(expression, onIndex + 1);
|
|
const isHydrationTrigger = expression.startsWith('hydrate');
|
|
const parser = new OnTriggerParser(expression, bindingParser, start, sourceSpan, triggers, errors, isHydrationTrigger ? validateHydrateReferenceBasedTrigger : validatePlainReferenceBasedTrigger, isHydrationTrigger, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
parser.parse();
|
|
}
|
|
}
|
|
function getPrefetchSpan(expression, sourceSpan) {
|
|
if (!expression.startsWith('prefetch')) {
|
|
return null;
|
|
}
|
|
return new ParseSourceSpan(sourceSpan.start, sourceSpan.start.moveBy('prefetch'.length));
|
|
}
|
|
function getHydrateSpan(expression, sourceSpan) {
|
|
if (!expression.startsWith('hydrate')) {
|
|
return null;
|
|
}
|
|
return new ParseSourceSpan(sourceSpan.start, sourceSpan.start.moveBy('hydrate'.length));
|
|
}
|
|
class OnTriggerParser {
|
|
expression;
|
|
bindingParser;
|
|
start;
|
|
span;
|
|
triggers;
|
|
errors;
|
|
validator;
|
|
isHydrationTrigger;
|
|
prefetchSpan;
|
|
onSourceSpan;
|
|
hydrateSpan;
|
|
index = 0;
|
|
tokens;
|
|
constructor(expression, bindingParser, start, span, triggers, errors, validator, isHydrationTrigger, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
this.expression = expression;
|
|
this.bindingParser = bindingParser;
|
|
this.start = start;
|
|
this.span = span;
|
|
this.triggers = triggers;
|
|
this.errors = errors;
|
|
this.validator = validator;
|
|
this.isHydrationTrigger = isHydrationTrigger;
|
|
this.prefetchSpan = prefetchSpan;
|
|
this.onSourceSpan = onSourceSpan;
|
|
this.hydrateSpan = hydrateSpan;
|
|
this.tokens = new Lexer().tokenize(expression.slice(start));
|
|
}
|
|
parse() {
|
|
while (this.tokens.length > 0 && this.index < this.tokens.length) {
|
|
const token = this.token();
|
|
if (!token.isIdentifier()) {
|
|
this.unexpectedToken(token);
|
|
break;
|
|
}
|
|
if (this.isFollowedByOrLast($COMMA)) {
|
|
this.consumeTrigger(token, []);
|
|
this.advance();
|
|
} else if (this.isFollowedByOrLast($LPAREN)) {
|
|
this.advance();
|
|
const prevErrors = this.errors.length;
|
|
const parameters = this.consumeParameters();
|
|
if (this.errors.length !== prevErrors) {
|
|
break;
|
|
}
|
|
this.consumeTrigger(token, parameters);
|
|
this.advance();
|
|
} else if (this.index < this.tokens.length - 1) {
|
|
this.unexpectedToken(this.tokens[this.index + 1]);
|
|
}
|
|
this.advance();
|
|
}
|
|
}
|
|
advance() {
|
|
this.index++;
|
|
}
|
|
isFollowedByOrLast(char) {
|
|
if (this.index === this.tokens.length - 1) {
|
|
return true;
|
|
}
|
|
return this.tokens[this.index + 1].isCharacter(char);
|
|
}
|
|
token() {
|
|
return this.tokens[Math.min(this.index, this.tokens.length - 1)];
|
|
}
|
|
consumeTrigger(identifier, parameters) {
|
|
const triggerNameStartSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index);
|
|
const nameSpan = new ParseSourceSpan(triggerNameStartSpan, triggerNameStartSpan.moveBy(identifier.strValue.length));
|
|
const endSpan = triggerNameStartSpan.moveBy(this.token().end - identifier.index);
|
|
const isFirstTrigger = identifier.index === 0;
|
|
const onSourceSpan = isFirstTrigger ? this.onSourceSpan : null;
|
|
const prefetchSourceSpan = isFirstTrigger ? this.prefetchSpan : null;
|
|
const hydrateSourceSpan = isFirstTrigger ? this.hydrateSpan : null;
|
|
const sourceSpan = new ParseSourceSpan(isFirstTrigger ? this.span.start : triggerNameStartSpan, endSpan);
|
|
try {
|
|
switch (identifier.toString()) {
|
|
case OnTriggerType.IDLE:
|
|
this.trackTrigger('idle', createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSourceSpan, onSourceSpan, hydrateSourceSpan));
|
|
break;
|
|
case OnTriggerType.TIMER:
|
|
this.trackTrigger('timer', createTimerTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.hydrateSpan));
|
|
break;
|
|
case OnTriggerType.INTERACTION:
|
|
this.trackTrigger('interaction', createInteractionTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.hydrateSpan, this.validator));
|
|
break;
|
|
case OnTriggerType.IMMEDIATE:
|
|
this.trackTrigger('immediate', createImmediateTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.hydrateSpan));
|
|
break;
|
|
case OnTriggerType.HOVER:
|
|
this.trackTrigger('hover', createHoverTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.hydrateSpan, this.validator));
|
|
break;
|
|
case OnTriggerType.VIEWPORT:
|
|
this.trackTrigger('viewport', createViewportTrigger(this.start, this.isHydrationTrigger, this.bindingParser, parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.hydrateSpan, this.validator));
|
|
break;
|
|
default:
|
|
throw new Error(`Unrecognized trigger type "${identifier}"`);
|
|
}
|
|
} catch (e) {
|
|
this.error(identifier, e.message);
|
|
}
|
|
}
|
|
consumeParameters() {
|
|
const parameters = [];
|
|
if (!this.token().isCharacter($LPAREN)) {
|
|
this.unexpectedToken(this.token());
|
|
return parameters;
|
|
}
|
|
this.advance();
|
|
const commaDelimStack = [];
|
|
let tokens = [];
|
|
while (this.index < this.tokens.length) {
|
|
const token = this.token();
|
|
if (token.isCharacter($RPAREN) && commaDelimStack.length === 0) {
|
|
if (tokens.length) {
|
|
parameters.push({
|
|
expression: this.tokenRangeText(tokens),
|
|
start: tokens[0].index
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
if (token.type === TokenType.Character && COMMA_DELIMITED_SYNTAX.has(token.numValue)) {
|
|
commaDelimStack.push(COMMA_DELIMITED_SYNTAX.get(token.numValue));
|
|
}
|
|
if (commaDelimStack.length > 0 && token.isCharacter(commaDelimStack[commaDelimStack.length - 1])) {
|
|
commaDelimStack.pop();
|
|
}
|
|
if (commaDelimStack.length === 0 && token.isCharacter($COMMA) && tokens.length > 0) {
|
|
parameters.push({
|
|
expression: this.tokenRangeText(tokens),
|
|
start: tokens[0].index
|
|
});
|
|
this.advance();
|
|
tokens = [];
|
|
continue;
|
|
}
|
|
tokens.push(token);
|
|
this.advance();
|
|
}
|
|
if (!this.token().isCharacter($RPAREN) || commaDelimStack.length > 0) {
|
|
this.error(this.token(), 'Unexpected end of expression');
|
|
}
|
|
if (this.index < this.tokens.length - 1 && !this.tokens[this.index + 1].isCharacter($COMMA)) {
|
|
this.unexpectedToken(this.tokens[this.index + 1]);
|
|
}
|
|
return parameters;
|
|
}
|
|
tokenRangeText(tokens) {
|
|
if (tokens.length === 0) {
|
|
return '';
|
|
}
|
|
return this.expression.slice(this.start + tokens[0].index, this.start + tokens[tokens.length - 1].end);
|
|
}
|
|
trackTrigger(name, trigger) {
|
|
trackTrigger(name, this.triggers, this.errors, trigger);
|
|
}
|
|
error(token, message) {
|
|
const newStart = this.span.start.moveBy(this.start + token.index);
|
|
const newEnd = newStart.moveBy(token.end - token.index);
|
|
this.errors.push(new ParseError(new ParseSourceSpan(newStart, newEnd), message));
|
|
}
|
|
unexpectedToken(token) {
|
|
this.error(token, `Unexpected token "${token}"`);
|
|
}
|
|
}
|
|
function trackTrigger(name, allTriggers, errors, trigger) {
|
|
if (allTriggers[name]) {
|
|
errors.push(new ParseError(trigger.sourceSpan, `Duplicate "${name}" trigger is not allowed`));
|
|
} else {
|
|
allTriggers[name] = trigger;
|
|
}
|
|
}
|
|
function createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
if (parameters.length > 0) {
|
|
throw new Error(`"${OnTriggerType.IDLE}" trigger cannot have parameters`);
|
|
}
|
|
return new IdleDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function createTimerTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
if (parameters.length !== 1) {
|
|
throw new Error(`"${OnTriggerType.TIMER}" trigger must have exactly one parameter`);
|
|
}
|
|
const delay = parseDeferredTime(parameters[0].expression);
|
|
if (delay === null) {
|
|
throw new Error(`Could not parse time value of trigger "${OnTriggerType.TIMER}"`);
|
|
}
|
|
return new TimerDeferredTrigger(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function createImmediateTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan) {
|
|
if (parameters.length > 0) {
|
|
throw new Error(`"${OnTriggerType.IMMEDIATE}" trigger cannot have parameters`);
|
|
}
|
|
return new ImmediateDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function createHoverTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan, validator) {
|
|
validator(OnTriggerType.HOVER, parameters);
|
|
return new HoverDeferredTrigger(parameters[0]?.expression ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function createInteractionTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan, validator) {
|
|
validator(OnTriggerType.INTERACTION, parameters);
|
|
return new InteractionDeferredTrigger(parameters[0]?.expression ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function createViewportTrigger(start, isHydrationTrigger, bindingParser, parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan, validator) {
|
|
validator(OnTriggerType.VIEWPORT, parameters);
|
|
let reference;
|
|
let options;
|
|
if (parameters.length === 0) {
|
|
reference = options = null;
|
|
} else if (!parameters[0].expression.startsWith('{')) {
|
|
reference = parameters[0].expression;
|
|
options = null;
|
|
} else {
|
|
const parsed = bindingParser.parseBinding(parameters[0].expression, false, sourceSpan, sourceSpan.start.offset + start + parameters[0].start);
|
|
if (!(parsed.ast instanceof LiteralMap)) {
|
|
throw new Error('Options parameter of the "viewport" trigger must be an object literal');
|
|
} else if (parsed.ast.keys.some(key => key.kind === 'spread')) {
|
|
throw new Error('Spread operator are not allowed in this context');
|
|
} else if (parsed.ast.keys.some(key => key.kind === 'property' && key.key === 'root')) {
|
|
throw new Error('The "root" option is not supported in the options parameter of the "viewport" trigger');
|
|
}
|
|
const triggerIndex = parsed.ast.keys.findIndex(key => key.kind === 'property' && key.key === 'trigger');
|
|
if (triggerIndex === -1) {
|
|
reference = null;
|
|
options = parsed.ast;
|
|
} else {
|
|
const value = parsed.ast.values[triggerIndex];
|
|
const triggerFilter = (_, index) => index !== triggerIndex;
|
|
if (!(value instanceof PropertyRead) || !(value.receiver instanceof ImplicitReceiver)) {
|
|
throw new Error(`"trigger" option of the "viewport" trigger must be an identifier`);
|
|
}
|
|
reference = value.name;
|
|
options = new LiteralMap(parsed.ast.span, parsed.ast.sourceSpan, parsed.ast.keys.filter(triggerFilter), parsed.ast.values.filter(triggerFilter));
|
|
}
|
|
}
|
|
if (isHydrationTrigger && reference !== null) {
|
|
throw new Error(`"viewport" hydration trigger cannot have a "trigger"`);
|
|
} else if (options) {
|
|
const dynamicNode = DynamicAstValidator.findDynamicNode(options);
|
|
if (dynamicNode !== null) {
|
|
throw new Error(`Options of the "viewport" trigger must be an object ` + `literal containing only literal values, but "${dynamicNode.constructor.name}" was found`);
|
|
}
|
|
}
|
|
return new ViewportDeferredTrigger(reference, options, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, hydrateSpan);
|
|
}
|
|
function validatePlainReferenceBasedTrigger(type, parameters) {
|
|
if (parameters.length > 1) {
|
|
throw new Error(`"${type}" trigger can only have zero or one parameters`);
|
|
}
|
|
}
|
|
function validateHydrateReferenceBasedTrigger(type, parameters) {
|
|
if (type === OnTriggerType.VIEWPORT) {
|
|
if (parameters.length > 1) {
|
|
throw new Error(`Hydration trigger "${type}" cannot have more than one parameter`);
|
|
}
|
|
return;
|
|
}
|
|
if (parameters.length > 0) {
|
|
throw new Error(`Hydration trigger "${type}" cannot have parameters`);
|
|
}
|
|
}
|
|
function getTriggerParametersStart(value, startPosition = 0) {
|
|
let hasFoundSeparator = false;
|
|
for (let i = startPosition; i < value.length; i++) {
|
|
if (SEPARATOR_PATTERN.test(value[i])) {
|
|
hasFoundSeparator = true;
|
|
} else if (hasFoundSeparator) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function parseDeferredTime(value) {
|
|
const match = value.match(TIME_PATTERN);
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
const [time, units] = match;
|
|
return parseFloat(time) * (units === 's' ? 1000 : 1);
|
|
}
|
|
class DynamicAstValidator extends RecursiveAstVisitor {
|
|
dynamicNode = null;
|
|
static findDynamicNode(ast) {
|
|
const visitor = new DynamicAstValidator();
|
|
visitor.visit(ast);
|
|
return visitor.dynamicNode;
|
|
}
|
|
visit(ast) {
|
|
if (!(ast instanceof ASTWithSource) && !(ast instanceof LiteralPrimitive) && !(ast instanceof LiteralArray) && !(ast instanceof LiteralMap)) {
|
|
this.dynamicNode = ast;
|
|
} else {
|
|
super.visit(ast);
|
|
}
|
|
}
|
|
}
|
|
|
|
const PREFETCH_WHEN_PATTERN = /^prefetch\s+when\s/;
|
|
const PREFETCH_ON_PATTERN = /^prefetch\s+on\s/;
|
|
const HYDRATE_WHEN_PATTERN = /^hydrate\s+when\s/;
|
|
const HYDRATE_ON_PATTERN = /^hydrate\s+on\s/;
|
|
const HYDRATE_NEVER_PATTERN = /^hydrate\s+never(\s*)$/;
|
|
const MINIMUM_PARAMETER_PATTERN = /^minimum\s/;
|
|
const AFTER_PARAMETER_PATTERN = /^after\s/;
|
|
const WHEN_PARAMETER_PATTERN = /^when\s/;
|
|
const ON_PARAMETER_PATTERN = /^on\s/;
|
|
function isConnectedDeferLoopBlock(name) {
|
|
return name === 'placeholder' || name === 'loading' || name === 'error';
|
|
}
|
|
function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
|
|
const errors = [];
|
|
const {
|
|
placeholder,
|
|
loading,
|
|
error
|
|
} = parseConnectedBlocks(connectedBlocks, errors, visitor);
|
|
const {
|
|
triggers,
|
|
prefetchTriggers,
|
|
hydrateTriggers
|
|
} = parsePrimaryTriggers(ast, bindingParser, errors);
|
|
let lastEndSourceSpan = ast.endSourceSpan;
|
|
let endOfLastSourceSpan = ast.sourceSpan.end;
|
|
if (connectedBlocks.length > 0) {
|
|
const lastConnectedBlock = connectedBlocks[connectedBlocks.length - 1];
|
|
lastEndSourceSpan = lastConnectedBlock.endSourceSpan;
|
|
endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
|
|
}
|
|
const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
|
|
const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, hydrateTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan, ast.i18n);
|
|
return {
|
|
node,
|
|
errors
|
|
};
|
|
}
|
|
function parseConnectedBlocks(connectedBlocks, errors, visitor) {
|
|
let placeholder = null;
|
|
let loading = null;
|
|
let error = null;
|
|
for (const block of connectedBlocks) {
|
|
try {
|
|
if (!isConnectedDeferLoopBlock(block.name)) {
|
|
errors.push(new ParseError(block.startSourceSpan, `Unrecognized block "@${block.name}"`));
|
|
break;
|
|
}
|
|
switch (block.name) {
|
|
case 'placeholder':
|
|
if (placeholder !== null) {
|
|
errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @placeholder block`));
|
|
} else {
|
|
placeholder = parsePlaceholderBlock(block, visitor);
|
|
}
|
|
break;
|
|
case 'loading':
|
|
if (loading !== null) {
|
|
errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @loading block`));
|
|
} else {
|
|
loading = parseLoadingBlock(block, visitor);
|
|
}
|
|
break;
|
|
case 'error':
|
|
if (error !== null) {
|
|
errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @error block`));
|
|
} else {
|
|
error = parseErrorBlock(block, visitor);
|
|
}
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
errors.push(new ParseError(block.startSourceSpan, e.message));
|
|
}
|
|
}
|
|
return {
|
|
placeholder,
|
|
loading,
|
|
error
|
|
};
|
|
}
|
|
function parsePlaceholderBlock(ast, visitor) {
|
|
let minimumTime = null;
|
|
for (const param of ast.parameters) {
|
|
if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {
|
|
if (minimumTime != null) {
|
|
throw new Error(`@placeholder block can only have one "minimum" parameter`);
|
|
}
|
|
const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
|
|
if (parsedTime === null) {
|
|
throw new Error(`Could not parse time value of parameter "minimum"`);
|
|
}
|
|
minimumTime = parsedTime;
|
|
} else {
|
|
throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
|
|
}
|
|
}
|
|
return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
|
|
}
|
|
function parseLoadingBlock(ast, visitor) {
|
|
let afterTime = null;
|
|
let minimumTime = null;
|
|
for (const param of ast.parameters) {
|
|
if (AFTER_PARAMETER_PATTERN.test(param.expression)) {
|
|
if (afterTime != null) {
|
|
throw new Error(`@loading block can only have one "after" parameter`);
|
|
}
|
|
const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
|
|
if (parsedTime === null) {
|
|
throw new Error(`Could not parse time value of parameter "after"`);
|
|
}
|
|
afterTime = parsedTime;
|
|
} else if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {
|
|
if (minimumTime != null) {
|
|
throw new Error(`@loading block can only have one "minimum" parameter`);
|
|
}
|
|
const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
|
|
if (parsedTime === null) {
|
|
throw new Error(`Could not parse time value of parameter "minimum"`);
|
|
}
|
|
minimumTime = parsedTime;
|
|
} else {
|
|
throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
|
|
}
|
|
}
|
|
return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
|
|
}
|
|
function parseErrorBlock(ast, visitor) {
|
|
if (ast.parameters.length > 0) {
|
|
throw new Error(`@error block cannot have parameters`);
|
|
}
|
|
return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
|
|
}
|
|
function parsePrimaryTriggers(ast, bindingParser, errors, placeholder) {
|
|
const triggers = {};
|
|
const prefetchTriggers = {};
|
|
const hydrateTriggers = {};
|
|
for (const param of ast.parameters) {
|
|
if (WHEN_PARAMETER_PATTERN.test(param.expression)) {
|
|
parseWhenTrigger(param, bindingParser, triggers, errors);
|
|
} else if (ON_PARAMETER_PATTERN.test(param.expression)) {
|
|
parseOnTrigger(param, bindingParser, triggers, errors);
|
|
} else if (PREFETCH_WHEN_PATTERN.test(param.expression)) {
|
|
parseWhenTrigger(param, bindingParser, prefetchTriggers, errors);
|
|
} else if (PREFETCH_ON_PATTERN.test(param.expression)) {
|
|
parseOnTrigger(param, bindingParser, prefetchTriggers, errors);
|
|
} else if (HYDRATE_WHEN_PATTERN.test(param.expression)) {
|
|
parseWhenTrigger(param, bindingParser, hydrateTriggers, errors);
|
|
} else if (HYDRATE_ON_PATTERN.test(param.expression)) {
|
|
parseOnTrigger(param, bindingParser, hydrateTriggers, errors);
|
|
} else if (HYDRATE_NEVER_PATTERN.test(param.expression)) {
|
|
parseNeverTrigger(param, hydrateTriggers, errors);
|
|
} else {
|
|
errors.push(new ParseError(param.sourceSpan, 'Unrecognized trigger'));
|
|
}
|
|
}
|
|
if (hydrateTriggers.never && Object.keys(hydrateTriggers).length > 1) {
|
|
errors.push(new ParseError(ast.startSourceSpan, 'Cannot specify additional `hydrate` triggers if `hydrate never` is present'));
|
|
}
|
|
return {
|
|
triggers,
|
|
prefetchTriggers,
|
|
hydrateTriggers
|
|
};
|
|
}
|
|
|
|
const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
|
|
const KW_BIND_IDX = 1;
|
|
const KW_LET_IDX = 2;
|
|
const KW_REF_IDX = 3;
|
|
const KW_ON_IDX = 4;
|
|
const KW_BINDON_IDX = 5;
|
|
const KW_AT_IDX = 6;
|
|
const IDENT_KW_IDX = 7;
|
|
const BINDING_DELIMS = {
|
|
BANANA_BOX: {
|
|
start: '[(',
|
|
end: ')]'
|
|
},
|
|
PROPERTY: {
|
|
start: '[',
|
|
end: ']'
|
|
},
|
|
EVENT: {
|
|
start: '(',
|
|
end: ')'
|
|
}
|
|
};
|
|
const TEMPLATE_ATTR_PREFIX = '*';
|
|
const UNSUPPORTED_SELECTORLESS_TAGS = new Set(['link', 'style', 'script', 'ng-template', 'ng-container', 'ng-content']);
|
|
const UNSUPPORTED_SELECTORLESS_DIRECTIVE_ATTRS = new Set(['ngProjectAs', 'ngNonBindable']);
|
|
function htmlAstToRender3Ast(htmlNodes, bindingParser, options) {
|
|
const transformer = new HtmlAstToIvyAst(bindingParser, options);
|
|
const ivyNodes = visitAll(transformer, htmlNodes, htmlNodes);
|
|
const allErrors = bindingParser.errors.concat(transformer.errors);
|
|
const result = {
|
|
nodes: ivyNodes,
|
|
errors: allErrors,
|
|
styleUrls: transformer.styleUrls,
|
|
styles: transformer.styles,
|
|
ngContentSelectors: transformer.ngContentSelectors
|
|
};
|
|
if (options.collectCommentNodes) {
|
|
result.commentNodes = transformer.commentNodes;
|
|
}
|
|
return result;
|
|
}
|
|
class HtmlAstToIvyAst {
|
|
bindingParser;
|
|
options;
|
|
errors = [];
|
|
styles = [];
|
|
styleUrls = [];
|
|
ngContentSelectors = [];
|
|
commentNodes = [];
|
|
inI18nBlock = false;
|
|
processedNodes = new Set();
|
|
constructor(bindingParser, options) {
|
|
this.bindingParser = bindingParser;
|
|
this.options = options;
|
|
}
|
|
visitElement(element) {
|
|
const isI18nRootElement = isI18nRootNode(element.i18n);
|
|
if (isI18nRootElement) {
|
|
if (this.inI18nBlock) {
|
|
this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
|
|
}
|
|
this.inI18nBlock = true;
|
|
}
|
|
const preparsedElement = preparseElement(element);
|
|
if (preparsedElement.type === PreparsedElementType.SCRIPT) {
|
|
return null;
|
|
} else if (preparsedElement.type === PreparsedElementType.STYLE) {
|
|
const contents = textContents(element);
|
|
if (contents !== null) {
|
|
this.styles.push(contents);
|
|
}
|
|
return null;
|
|
} else if (preparsedElement.type === PreparsedElementType.STYLESHEET && isStyleUrlResolvable(preparsedElement.hrefAttr)) {
|
|
this.styleUrls.push(preparsedElement.hrefAttr);
|
|
return null;
|
|
}
|
|
const isTemplateElement = isNgTemplate(element.name);
|
|
const {
|
|
attributes,
|
|
boundEvents,
|
|
references,
|
|
variables,
|
|
templateVariables,
|
|
elementHasInlineTemplate,
|
|
parsedProperties,
|
|
templateParsedProperties,
|
|
i18nAttrsMeta
|
|
} = this.prepareAttributes(element.attrs, isTemplateElement);
|
|
const directives = this.extractDirectives(element);
|
|
let children;
|
|
if (preparsedElement.nonBindable) {
|
|
children = visitAll(NON_BINDABLE_VISITOR, element.children).flat(Infinity);
|
|
} else {
|
|
children = visitAll(this, element.children, element.children);
|
|
}
|
|
let parsedElement;
|
|
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
|
const selector = preparsedElement.selectAttr;
|
|
const attrs = element.attrs.map(attr => this.visitAttribute(attr));
|
|
parsedElement = new Content(selector, attrs, children, element.isSelfClosing, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
|
this.ngContentSelectors.push(selector);
|
|
} else if (isTemplateElement) {
|
|
const attrs = this.categorizePropertyAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
|
parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, directives, [], children, references, variables, element.isSelfClosing, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
|
} else {
|
|
const attrs = this.categorizePropertyAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
|
if (element.name === 'ng-container') {
|
|
for (const bound of attrs.bound) {
|
|
if (bound.type === BindingType.Attribute) {
|
|
this.reportError(`Attribute bindings are not supported on ng-container. Use property bindings instead.`, bound.sourceSpan);
|
|
}
|
|
}
|
|
}
|
|
parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, directives, children, references, element.isSelfClosing, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.isVoid, element.i18n);
|
|
}
|
|
if (elementHasInlineTemplate) {
|
|
parsedElement = this.wrapInTemplate(parsedElement, templateParsedProperties, templateVariables, i18nAttrsMeta, isTemplateElement, isI18nRootElement);
|
|
}
|
|
if (isI18nRootElement) {
|
|
this.inI18nBlock = false;
|
|
}
|
|
return parsedElement;
|
|
}
|
|
visitAttribute(attribute) {
|
|
return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
|
}
|
|
visitText(text) {
|
|
return this.processedNodes.has(text) ? null : this._visitTextWithInterpolation(text.value, text.sourceSpan, text.tokens, text.i18n);
|
|
}
|
|
visitExpansion(expansion) {
|
|
if (!expansion.i18n) {
|
|
return null;
|
|
}
|
|
if (!isI18nRootNode(expansion.i18n)) {
|
|
throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
|
|
}
|
|
const message = expansion.i18n;
|
|
const vars = {};
|
|
const placeholders = {};
|
|
Object.keys(message.placeholders).forEach(key => {
|
|
const value = message.placeholders[key];
|
|
if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
|
|
const formattedKey = key.trim();
|
|
const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
|
|
vars[formattedKey] = new BoundText(ast, value.sourceSpan);
|
|
} else {
|
|
placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan, null);
|
|
}
|
|
});
|
|
return new Icu$1(vars, placeholders, expansion.sourceSpan, message);
|
|
}
|
|
visitExpansionCase(expansionCase) {
|
|
return null;
|
|
}
|
|
visitComment(comment) {
|
|
if (this.options.collectCommentNodes) {
|
|
this.commentNodes.push(new Comment$1(comment.value || '', comment.sourceSpan));
|
|
}
|
|
return null;
|
|
}
|
|
visitLetDeclaration(decl, context) {
|
|
const value = this.bindingParser.parseBinding(decl.value, false, decl.valueSpan, decl.valueSpan.start.offset);
|
|
if (value.errors.length === 0 && value.ast instanceof EmptyExpr$1) {
|
|
this.reportError('@let declaration value cannot be empty', decl.valueSpan);
|
|
}
|
|
return new LetDeclaration$1(decl.name, value, decl.sourceSpan, decl.nameSpan, decl.valueSpan);
|
|
}
|
|
visitComponent(component) {
|
|
const isI18nRootElement = isI18nRootNode(component.i18n);
|
|
if (isI18nRootElement) {
|
|
if (this.inI18nBlock) {
|
|
this.reportError('Cannot mark a component as translatable inside of a translatable section. Please remove the nested i18n marker.', component.sourceSpan);
|
|
}
|
|
this.inI18nBlock = true;
|
|
}
|
|
if (component.tagName !== null && UNSUPPORTED_SELECTORLESS_TAGS.has(component.tagName)) {
|
|
this.reportError(`Tag name "${component.tagName}" cannot be used as a component tag`, component.startSourceSpan);
|
|
return null;
|
|
}
|
|
const {
|
|
attributes,
|
|
boundEvents,
|
|
references,
|
|
templateVariables,
|
|
elementHasInlineTemplate,
|
|
parsedProperties,
|
|
templateParsedProperties,
|
|
i18nAttrsMeta
|
|
} = this.prepareAttributes(component.attrs, false);
|
|
this.validateSelectorlessReferences(references);
|
|
const directives = this.extractDirectives(component);
|
|
let children;
|
|
if (component.attrs.find(attr => attr.name === 'ngNonBindable')) {
|
|
children = visitAll(NON_BINDABLE_VISITOR, component.children).flat(Infinity);
|
|
} else {
|
|
children = visitAll(this, component.children, component.children);
|
|
}
|
|
const attrs = this.categorizePropertyAttributes(component.tagName, parsedProperties, i18nAttrsMeta);
|
|
let node = new Component$1(component.componentName, component.tagName, component.fullName, attributes, attrs.bound, boundEvents, directives, children, references, component.isSelfClosing, component.sourceSpan, component.startSourceSpan, component.endSourceSpan, component.i18n);
|
|
if (elementHasInlineTemplate) {
|
|
node = this.wrapInTemplate(node, templateParsedProperties, templateVariables, i18nAttrsMeta, false, isI18nRootElement);
|
|
}
|
|
if (isI18nRootElement) {
|
|
this.inI18nBlock = false;
|
|
}
|
|
return node;
|
|
}
|
|
visitDirective() {
|
|
return null;
|
|
}
|
|
visitBlockParameter() {
|
|
return null;
|
|
}
|
|
visitBlock(block, context) {
|
|
const index = Array.isArray(context) ? context.indexOf(block) : -1;
|
|
if (index === -1) {
|
|
throw new Error('Visitor invoked incorrectly. Expecting visitBlock to be invoked siblings array as its context');
|
|
}
|
|
if (this.processedNodes.has(block)) {
|
|
return null;
|
|
}
|
|
let result = null;
|
|
switch (block.name) {
|
|
case 'defer':
|
|
result = createDeferredBlock(block, this.findConnectedBlocks(index, context, isConnectedDeferLoopBlock), this, this.bindingParser);
|
|
break;
|
|
case 'switch':
|
|
result = createSwitchBlock(block, this, this.bindingParser);
|
|
break;
|
|
case 'for':
|
|
result = createForLoop(block, this.findConnectedBlocks(index, context, isConnectedForLoopBlock), this, this.bindingParser);
|
|
break;
|
|
case 'if':
|
|
result = createIfBlock(block, this.findConnectedBlocks(index, context, isConnectedIfLoopBlock), this, this.bindingParser);
|
|
break;
|
|
default:
|
|
let errorMessage;
|
|
if (isConnectedDeferLoopBlock(block.name)) {
|
|
errorMessage = `@${block.name} block can only be used after an @defer block.`;
|
|
this.processedNodes.add(block);
|
|
} else if (isConnectedForLoopBlock(block.name)) {
|
|
errorMessage = `@${block.name} block can only be used after an @for block.`;
|
|
this.processedNodes.add(block);
|
|
} else if (isConnectedIfLoopBlock(block.name)) {
|
|
errorMessage = `@${block.name} block can only be used after an @if or @else if block.`;
|
|
this.processedNodes.add(block);
|
|
} else {
|
|
errorMessage = `Unrecognized block @${block.name}.`;
|
|
}
|
|
result = {
|
|
node: new UnknownBlock(block.name, block.sourceSpan, block.nameSpan),
|
|
errors: [new ParseError(block.sourceSpan, errorMessage)]
|
|
};
|
|
break;
|
|
}
|
|
this.errors.push(...result.errors);
|
|
return result.node;
|
|
}
|
|
findConnectedBlocks(primaryBlockIndex, siblings, predicate) {
|
|
const relatedBlocks = [];
|
|
for (let i = primaryBlockIndex + 1; i < siblings.length; i++) {
|
|
const node = siblings[i];
|
|
if (node instanceof Comment) {
|
|
continue;
|
|
}
|
|
if (node instanceof Text && node.value.trim().length === 0) {
|
|
this.processedNodes.add(node);
|
|
continue;
|
|
}
|
|
if (!(node instanceof Block) || !predicate(node.name)) {
|
|
break;
|
|
}
|
|
relatedBlocks.push(node);
|
|
this.processedNodes.add(node);
|
|
}
|
|
return relatedBlocks;
|
|
}
|
|
categorizePropertyAttributes(elementName, properties, i18nPropsMeta) {
|
|
const bound = [];
|
|
const literal = [];
|
|
properties.forEach(prop => {
|
|
const i18n = i18nPropsMeta[prop.name];
|
|
if (prop.isLiteral) {
|
|
literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
|
|
} else {
|
|
const bep = this.bindingParser.createBoundElementProperty(elementName, prop, true, false);
|
|
bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
|
|
}
|
|
});
|
|
return {
|
|
bound,
|
|
literal
|
|
};
|
|
}
|
|
prepareAttributes(attrs, isTemplateElement) {
|
|
const parsedProperties = [];
|
|
const boundEvents = [];
|
|
const variables = [];
|
|
const references = [];
|
|
const attributes = [];
|
|
const i18nAttrsMeta = {};
|
|
const templateParsedProperties = [];
|
|
const templateVariables = [];
|
|
let elementHasInlineTemplate = false;
|
|
for (const attribute of attrs) {
|
|
let hasBinding = false;
|
|
const normalizedName = normalizeAttributeName(attribute.name);
|
|
let isTemplateBinding = false;
|
|
if (attribute.i18n) {
|
|
i18nAttrsMeta[attribute.name] = attribute.i18n;
|
|
}
|
|
if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
|
if (elementHasInlineTemplate) {
|
|
this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
|
|
}
|
|
isTemplateBinding = true;
|
|
elementHasInlineTemplate = true;
|
|
const templateValue = attribute.value;
|
|
const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
|
|
const parsedVariables = [];
|
|
const absoluteValueOffset = attribute.valueSpan ? attribute.valueSpan.fullStart.offset : attribute.sourceSpan.fullStart.offset + attribute.name.length;
|
|
this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true);
|
|
templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
|
|
} else {
|
|
hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
|
|
}
|
|
if (!hasBinding && !isTemplateBinding) {
|
|
attributes.push(this.visitAttribute(attribute));
|
|
}
|
|
}
|
|
return {
|
|
attributes,
|
|
boundEvents,
|
|
references,
|
|
variables,
|
|
templateVariables,
|
|
elementHasInlineTemplate,
|
|
parsedProperties,
|
|
templateParsedProperties,
|
|
i18nAttrsMeta
|
|
};
|
|
}
|
|
parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
|
|
const name = normalizeAttributeName(attribute.name);
|
|
const value = attribute.value;
|
|
const srcSpan = attribute.sourceSpan;
|
|
const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.fullStart.offset : srcSpan.fullStart.offset;
|
|
function createKeySpan(srcSpan, prefix, identifier) {
|
|
const normalizationAdjustment = attribute.name.length - name.length;
|
|
const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
|
|
const keySpanEnd = keySpanStart.moveBy(identifier.length);
|
|
return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
|
|
}
|
|
const bindParts = name.match(BIND_NAME_REGEXP);
|
|
if (bindParts) {
|
|
if (bindParts[KW_BIND_IDX] != null) {
|
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
|
|
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
} else if (bindParts[KW_LET_IDX]) {
|
|
if (isTemplateElement) {
|
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier);
|
|
this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
|
|
} else {
|
|
this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
|
|
}
|
|
} else if (bindParts[KW_REF_IDX]) {
|
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier);
|
|
this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
|
|
} else if (bindParts[KW_ON_IDX]) {
|
|
const events = [];
|
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier);
|
|
this.bindingParser.parseEvent(identifier, value, false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
|
addEvents(events, boundEvents);
|
|
} else if (bindParts[KW_BINDON_IDX]) {
|
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
|
|
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan, absoluteOffset);
|
|
} else if (bindParts[KW_AT_IDX]) {
|
|
const keySpan = createKeySpan(srcSpan, '', name);
|
|
this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
}
|
|
return true;
|
|
}
|
|
let delims = null;
|
|
if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
|
|
delims = BINDING_DELIMS.BANANA_BOX;
|
|
} else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
|
|
delims = BINDING_DELIMS.PROPERTY;
|
|
} else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
|
|
delims = BINDING_DELIMS.EVENT;
|
|
}
|
|
if (delims !== null && name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
|
|
const identifier = name.substring(delims.start.length, name.length - delims.end.length);
|
|
const keySpan = createKeySpan(srcSpan, delims.start, identifier);
|
|
if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
|
|
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan, absoluteOffset);
|
|
} else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
|
|
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
} else {
|
|
const events = [];
|
|
this.bindingParser.parseEvent(identifier, value, false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
|
addEvents(events, boundEvents);
|
|
}
|
|
return true;
|
|
}
|
|
const keySpan = createKeySpan(srcSpan, '', name);
|
|
const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan, attribute.valueTokens ?? null);
|
|
return hasBinding;
|
|
}
|
|
extractDirectives(node) {
|
|
const elementName = node instanceof Component ? node.tagName : node.name;
|
|
const directives = [];
|
|
const seenDirectives = new Set();
|
|
for (const directive of node.directives) {
|
|
let invalid = false;
|
|
for (const attr of directive.attrs) {
|
|
if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
|
invalid = true;
|
|
this.reportError(`Shorthand template syntax "${attr.name}" is not supported inside a directive context`, attr.sourceSpan);
|
|
} else if (UNSUPPORTED_SELECTORLESS_DIRECTIVE_ATTRS.has(attr.name)) {
|
|
invalid = true;
|
|
this.reportError(`Attribute "${attr.name}" is not supported in a directive context`, attr.sourceSpan);
|
|
}
|
|
}
|
|
if (!invalid && seenDirectives.has(directive.name)) {
|
|
invalid = true;
|
|
this.reportError(`Cannot apply directive "${directive.name}" multiple times on the same element`, directive.sourceSpan);
|
|
}
|
|
if (invalid) {
|
|
continue;
|
|
}
|
|
const {
|
|
attributes,
|
|
parsedProperties,
|
|
boundEvents,
|
|
references,
|
|
i18nAttrsMeta
|
|
} = this.prepareAttributes(directive.attrs, false);
|
|
this.validateSelectorlessReferences(references);
|
|
const {
|
|
bound: inputs
|
|
} = this.categorizePropertyAttributes(elementName, parsedProperties, i18nAttrsMeta);
|
|
for (const input of inputs) {
|
|
if (input.type !== BindingType.Property && input.type !== BindingType.TwoWay) {
|
|
invalid = true;
|
|
this.reportError('Binding is not supported in a directive context', input.sourceSpan);
|
|
}
|
|
}
|
|
if (invalid) {
|
|
continue;
|
|
}
|
|
seenDirectives.add(directive.name);
|
|
directives.push(new Directive$1(directive.name, attributes, inputs, boundEvents, references, directive.sourceSpan, directive.startSourceSpan, directive.endSourceSpan, undefined));
|
|
}
|
|
return directives;
|
|
}
|
|
filterAnimationAttributes(attributes) {
|
|
return attributes.filter(a => !a.name.startsWith('animate.'));
|
|
}
|
|
filterAnimationInputs(attributes) {
|
|
return attributes.filter(a => a.type !== BindingType.Animation);
|
|
}
|
|
wrapInTemplate(node, templateProperties, templateVariables, i18nAttrsMeta, isTemplateElement, isI18nRootElement) {
|
|
const attrs = this.categorizePropertyAttributes('ng-template', templateProperties, i18nAttrsMeta);
|
|
const templateAttrs = [];
|
|
attrs.literal.forEach(attr => templateAttrs.push(attr));
|
|
attrs.bound.forEach(attr => templateAttrs.push(attr));
|
|
const hoistedAttrs = {
|
|
attributes: [],
|
|
inputs: [],
|
|
outputs: []
|
|
};
|
|
if (node instanceof Element$1 || node instanceof Component$1) {
|
|
hoistedAttrs.attributes.push(...this.filterAnimationAttributes(node.attributes));
|
|
hoistedAttrs.inputs.push(...this.filterAnimationInputs(node.inputs));
|
|
hoistedAttrs.outputs.push(...node.outputs);
|
|
}
|
|
const i18n = isTemplateElement && isI18nRootElement ? undefined : node.i18n;
|
|
let name;
|
|
if (node instanceof Component$1) {
|
|
name = node.tagName;
|
|
} else if (node instanceof Template) {
|
|
name = null;
|
|
} else {
|
|
name = node.name;
|
|
}
|
|
return new Template(name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, [], templateAttrs, [node], [], templateVariables, false, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, i18n);
|
|
}
|
|
_visitTextWithInterpolation(value, sourceSpan, interpolatedTokens, i18n) {
|
|
const valueNoNgsp = replaceNgsp(value);
|
|
const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan, interpolatedTokens);
|
|
return expr ? new BoundText(expr, sourceSpan, i18n) : new Text$3(valueNoNgsp, sourceSpan);
|
|
}
|
|
parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
|
|
if (identifier.indexOf('-') > -1) {
|
|
this.reportError(`"-" is not allowed in variable names`, sourceSpan);
|
|
} else if (identifier.length === 0) {
|
|
this.reportError(`Variable does not have a name`, sourceSpan);
|
|
}
|
|
variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
|
|
if (identifier.indexOf('-') > -1) {
|
|
this.reportError(`"-" is not allowed in reference names`, sourceSpan);
|
|
} else if (identifier.length === 0) {
|
|
this.reportError(`Reference does not have a name`, sourceSpan);
|
|
} else if (references.some(reference => reference.name === identifier)) {
|
|
this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
|
|
}
|
|
references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));
|
|
}
|
|
parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan, absoluteOffset) {
|
|
const events = [];
|
|
this.bindingParser.parseEvent(`${name}Change`, expression, true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
|
|
addEvents(events, boundEvents);
|
|
}
|
|
validateSelectorlessReferences(references) {
|
|
if (references.length === 0) {
|
|
return;
|
|
}
|
|
const seenNames = new Set();
|
|
for (const ref of references) {
|
|
if (ref.value.length > 0) {
|
|
this.reportError('Cannot specify a value for a local reference in this context', ref.valueSpan || ref.sourceSpan);
|
|
} else if (seenNames.has(ref.name)) {
|
|
this.reportError('Duplicate reference names are not allowed', ref.sourceSpan);
|
|
} else {
|
|
seenNames.add(ref.name);
|
|
}
|
|
}
|
|
}
|
|
reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
|
this.errors.push(new ParseError(sourceSpan, message, level));
|
|
}
|
|
}
|
|
class NonBindableVisitor {
|
|
visitElement(ast) {
|
|
const preparsedElement = preparseElement(ast);
|
|
if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE || preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
|
return null;
|
|
}
|
|
const children = visitAll(this, ast.children, null);
|
|
return new Element$1(ast.name, visitAll(this, ast.attrs), [], [], [], children, [], ast.isSelfClosing, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.isVoid);
|
|
}
|
|
visitComment(comment) {
|
|
return null;
|
|
}
|
|
visitAttribute(attribute) {
|
|
return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
|
}
|
|
visitText(text) {
|
|
return new Text$3(text.value, text.sourceSpan);
|
|
}
|
|
visitExpansion(expansion) {
|
|
return null;
|
|
}
|
|
visitExpansionCase(expansionCase) {
|
|
return null;
|
|
}
|
|
visitBlock(block, context) {
|
|
const nodes = [new Text$3(block.startSourceSpan.toString(), block.startSourceSpan), ...visitAll(this, block.children)];
|
|
if (block.endSourceSpan !== null) {
|
|
nodes.push(new Text$3(block.endSourceSpan.toString(), block.endSourceSpan));
|
|
}
|
|
return nodes;
|
|
}
|
|
visitBlockParameter(parameter, context) {
|
|
return null;
|
|
}
|
|
visitLetDeclaration(decl, context) {
|
|
return new Text$3(`@let ${decl.name} = ${decl.value};`, decl.sourceSpan);
|
|
}
|
|
visitComponent(ast, context) {
|
|
const children = visitAll(this, ast.children, null);
|
|
return new Element$1(ast.fullName, visitAll(this, ast.attrs), [], [], [], children, [], ast.isSelfClosing, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, false);
|
|
}
|
|
visitDirective(directive, context) {
|
|
return null;
|
|
}
|
|
}
|
|
const NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
|
function normalizeAttributeName(attrName) {
|
|
return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
|
}
|
|
function addEvents(events, boundEvents) {
|
|
boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
|
|
}
|
|
function textContents(node) {
|
|
if (node.children.length !== 1 || !(node.children[0] instanceof Text)) {
|
|
return null;
|
|
} else {
|
|
return node.children[0].value;
|
|
}
|
|
}
|
|
|
|
const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
|
|
function parseTemplate(template, templateUrl, options = {}) {
|
|
const {
|
|
preserveWhitespaces,
|
|
enableI18nLegacyMessageIdFormat
|
|
} = options;
|
|
const selectorlessEnabled = options.enableSelectorless ?? false;
|
|
const bindingParser = makeBindingParser(selectorlessEnabled);
|
|
const htmlParser = new HtmlParser();
|
|
const parseResult = htmlParser.parse(template, templateUrl, {
|
|
leadingTriviaChars: LEADING_TRIVIA_CHARS,
|
|
...options,
|
|
tokenizeExpansionForms: true,
|
|
tokenizeBlocks: options.enableBlockSyntax ?? true,
|
|
tokenizeLet: options.enableLetSyntax ?? true,
|
|
selectorlessEnabled
|
|
});
|
|
if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors && parseResult.errors.length > 0) {
|
|
const parsedTemplate = {
|
|
preserveWhitespaces,
|
|
errors: parseResult.errors,
|
|
nodes: [],
|
|
styleUrls: [],
|
|
styles: [],
|
|
ngContentSelectors: []
|
|
};
|
|
if (options.collectCommentNodes) {
|
|
parsedTemplate.commentNodes = [];
|
|
}
|
|
return parsedTemplate;
|
|
}
|
|
let rootNodes = parseResult.rootNodes;
|
|
const retainEmptyTokens = !(options.preserveSignificantWhitespace ?? true);
|
|
const i18nMetaVisitor = new I18nMetaVisitor(!preserveWhitespaces, enableI18nLegacyMessageIdFormat, options.preserveSignificantWhitespace, retainEmptyTokens);
|
|
const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
|
|
if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors && i18nMetaResult.errors.length > 0) {
|
|
const parsedTemplate = {
|
|
preserveWhitespaces,
|
|
errors: i18nMetaResult.errors,
|
|
nodes: [],
|
|
styleUrls: [],
|
|
styles: [],
|
|
ngContentSelectors: []
|
|
};
|
|
if (options.collectCommentNodes) {
|
|
parsedTemplate.commentNodes = [];
|
|
}
|
|
return parsedTemplate;
|
|
}
|
|
rootNodes = i18nMetaResult.rootNodes;
|
|
if (!preserveWhitespaces) {
|
|
rootNodes = visitAll(new WhitespaceVisitor(true, undefined, false), rootNodes);
|
|
if (i18nMetaVisitor.hasI18nMeta) {
|
|
rootNodes = visitAll(new I18nMetaVisitor(false, undefined, true, retainEmptyTokens), rootNodes);
|
|
}
|
|
}
|
|
const {
|
|
nodes,
|
|
errors,
|
|
styleUrls,
|
|
styles,
|
|
ngContentSelectors,
|
|
commentNodes
|
|
} = htmlAstToRender3Ast(rootNodes, bindingParser, {
|
|
collectCommentNodes: !!options.collectCommentNodes
|
|
});
|
|
errors.push(...parseResult.errors, ...i18nMetaResult.errors);
|
|
const parsedTemplate = {
|
|
preserveWhitespaces,
|
|
errors: errors.length > 0 ? errors : null,
|
|
nodes,
|
|
styleUrls,
|
|
styles,
|
|
ngContentSelectors
|
|
};
|
|
if (options.collectCommentNodes) {
|
|
parsedTemplate.commentNodes = commentNodes;
|
|
}
|
|
return parsedTemplate;
|
|
}
|
|
const elementRegistry = new DomElementSchemaRegistry();
|
|
function makeBindingParser(selectorlessEnabled = false) {
|
|
return new BindingParser(new Parser(new Lexer(), selectorlessEnabled), elementRegistry, []);
|
|
}
|
|
|
|
const COMPONENT_VARIABLE = '%COMP%';
|
|
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
|
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
|
function baseDirectiveFields(meta, constantPool, bindingParser) {
|
|
const definitionMap = new DefinitionMap();
|
|
const selectors = parseSelectorToR3Selector(meta.selector);
|
|
definitionMap.set('type', meta.type.value);
|
|
if (selectors.length > 0) {
|
|
definitionMap.set('selectors', asLiteral(selectors));
|
|
}
|
|
if (meta.queries.length > 0) {
|
|
definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
|
|
}
|
|
if (meta.viewQueries.length) {
|
|
definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
|
|
}
|
|
definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
|
|
definitionMap.set('inputs', conditionallyCreateDirectiveBindingLiteral(meta.inputs, true));
|
|
definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs));
|
|
if (meta.exportAs !== null) {
|
|
definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e))));
|
|
}
|
|
if (meta.isStandalone === false) {
|
|
definitionMap.set('standalone', literal(false));
|
|
}
|
|
if (meta.isSignal) {
|
|
definitionMap.set('signals', literal(true));
|
|
}
|
|
return definitionMap;
|
|
}
|
|
function addFeatures(definitionMap, meta) {
|
|
const features = [];
|
|
const providers = meta.providers;
|
|
const viewProviders = meta.viewProviders;
|
|
if (providers || viewProviders) {
|
|
const args = [providers || new LiteralArrayExpr([])];
|
|
if (viewProviders) {
|
|
args.push(viewProviders);
|
|
}
|
|
features.push(importExpr(Identifiers.ProvidersFeature).callFn(args));
|
|
}
|
|
if (meta.hostDirectives?.length) {
|
|
features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
|
|
}
|
|
if (meta.usesInheritance) {
|
|
features.push(importExpr(Identifiers.InheritDefinitionFeature));
|
|
}
|
|
if (meta.lifecycle.usesOnChanges) {
|
|
features.push(importExpr(Identifiers.NgOnChangesFeature));
|
|
}
|
|
if ('externalStyles' in meta && meta.externalStyles?.length) {
|
|
const externalStyleNodes = meta.externalStyles.map(externalStyle => literal(externalStyle));
|
|
features.push(importExpr(Identifiers.ExternalStylesFeature).callFn([literalArr(externalStyleNodes)]));
|
|
}
|
|
if (features.length) {
|
|
definitionMap.set('features', literalArr(features));
|
|
}
|
|
}
|
|
function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
|
|
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
|
addFeatures(definitionMap, meta);
|
|
const expression = importExpr(Identifiers.defineDirective).callFn([definitionMap.toLiteralMap()], undefined, true);
|
|
const type = createDirectiveType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
|
addFeatures(definitionMap, meta);
|
|
const selector = meta.selector && CssSelector.parse(meta.selector);
|
|
const firstSelector = selector && selector[0];
|
|
if (firstSelector) {
|
|
const selectorAttributes = firstSelector.getAttrs();
|
|
if (selectorAttributes.length) {
|
|
definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))), true));
|
|
}
|
|
}
|
|
const templateTypeName = meta.name;
|
|
let allDeferrableDepsFn = null;
|
|
if (meta.defer.mode === 1 && meta.defer.dependenciesFn !== null) {
|
|
const fnName = `${templateTypeName}_DeferFn`;
|
|
constantPool.statements.push(new DeclareVarStmt(fnName, meta.defer.dependenciesFn, undefined, StmtModifier.Final));
|
|
allDeferrableDepsFn = variable(fnName);
|
|
}
|
|
const compilationMode = meta.isStandalone && !meta.hasDirectiveDependencies ? TemplateCompilationMode.DomOnly : TemplateCompilationMode.Full;
|
|
const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, compilationMode, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.defer, allDeferrableDepsFn, meta.relativeTemplatePath, getTemplateSourceLocationsEnabled());
|
|
transform(tpl, CompilationJobKind.Tmpl);
|
|
const templateFn = emitTemplateFn(tpl, constantPool);
|
|
if (tpl.contentSelectors !== null) {
|
|
definitionMap.set('ngContentSelectors', tpl.contentSelectors);
|
|
}
|
|
definitionMap.set('decls', literal(tpl.root.decls));
|
|
definitionMap.set('vars', literal(tpl.root.vars));
|
|
if (tpl.consts.length > 0) {
|
|
if (tpl.constsInitializers.length > 0) {
|
|
definitionMap.set('consts', arrowFn([], [...tpl.constsInitializers, new ReturnStatement(literalArr(tpl.consts))]));
|
|
} else {
|
|
definitionMap.set('consts', literalArr(tpl.consts));
|
|
}
|
|
}
|
|
definitionMap.set('template', templateFn);
|
|
if (meta.declarationListEmitMode !== 3 && meta.declarations.length > 0) {
|
|
definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
|
|
} else if (meta.declarationListEmitMode === 3) {
|
|
const args = [meta.type.value];
|
|
if (meta.rawImports) {
|
|
args.push(meta.rawImports);
|
|
}
|
|
definitionMap.set('dependencies', importExpr(Identifiers.getComponentDepsFactory).callFn(args));
|
|
}
|
|
if (meta.encapsulation === null) {
|
|
meta.encapsulation = ViewEncapsulation$1.Emulated;
|
|
}
|
|
let hasStyles = !!meta.externalStyles?.length;
|
|
if (meta.styles && meta.styles.length) {
|
|
const styleValues = meta.encapsulation == ViewEncapsulation$1.Emulated ? compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) : meta.styles;
|
|
const styleNodes = styleValues.reduce((result, style) => {
|
|
if (style.trim().length > 0) {
|
|
result.push(constantPool.getConstLiteral(literal(style)));
|
|
}
|
|
return result;
|
|
}, []);
|
|
if (styleNodes.length > 0) {
|
|
hasStyles = true;
|
|
definitionMap.set('styles', literalArr(styleNodes));
|
|
}
|
|
}
|
|
if (!hasStyles && meta.encapsulation === ViewEncapsulation$1.Emulated) {
|
|
meta.encapsulation = ViewEncapsulation$1.None;
|
|
}
|
|
if (meta.encapsulation !== ViewEncapsulation$1.Emulated) {
|
|
definitionMap.set('encapsulation', literal(meta.encapsulation));
|
|
}
|
|
if (meta.animations !== null) {
|
|
definitionMap.set('data', literalMap([{
|
|
key: 'animation',
|
|
value: meta.animations,
|
|
quoted: false
|
|
}]));
|
|
}
|
|
if (meta.changeDetection !== null) {
|
|
if (typeof meta.changeDetection === 'number' && meta.changeDetection !== ChangeDetectionStrategy.Default) {
|
|
definitionMap.set('changeDetection', literal(meta.changeDetection));
|
|
} else if (typeof meta.changeDetection === 'object') {
|
|
definitionMap.set('changeDetection', meta.changeDetection);
|
|
}
|
|
}
|
|
const expression = importExpr(Identifiers.defineComponent).callFn([definitionMap.toLiteralMap()], undefined, true);
|
|
const type = createComponentType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createComponentType(meta) {
|
|
const typeParams = createBaseDirectiveTypeParams(meta);
|
|
typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
|
|
typeParams.push(expressionType(literal(meta.isStandalone)));
|
|
typeParams.push(createHostDirectivesType(meta));
|
|
if (meta.isSignal) {
|
|
typeParams.push(expressionType(literal(meta.isSignal)));
|
|
}
|
|
return expressionType(importExpr(Identifiers.ComponentDeclaration, typeParams));
|
|
}
|
|
function compileDeclarationList(list, mode) {
|
|
switch (mode) {
|
|
case 0:
|
|
return list;
|
|
case 1:
|
|
return arrowFn([], list);
|
|
case 2:
|
|
const resolvedList = list.prop('map').callFn([importExpr(Identifiers.resolveForwardRef)]);
|
|
return arrowFn([], resolvedList);
|
|
case 3:
|
|
throw new Error(`Unsupported with an array of pre-resolved dependencies`);
|
|
}
|
|
}
|
|
function stringAsType(str) {
|
|
return expressionType(literal(str));
|
|
}
|
|
function stringMapAsLiteralExpression(map) {
|
|
const mapValues = Object.keys(map).map(key => {
|
|
const value = Array.isArray(map[key]) ? map[key][0] : map[key];
|
|
return {
|
|
key,
|
|
value: literal(value),
|
|
quoted: true
|
|
};
|
|
});
|
|
return literalMap(mapValues);
|
|
}
|
|
function stringArrayAsType(arr) {
|
|
return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) : NONE_TYPE;
|
|
}
|
|
function createBaseDirectiveTypeParams(meta) {
|
|
const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
|
|
return [typeWithParameters(meta.type.type, meta.typeArgumentCount), selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE, meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE, expressionType(getInputsTypeExpression(meta)), expressionType(stringMapAsLiteralExpression(meta.outputs)), stringArrayAsType(meta.queries.map(q => q.propertyName))];
|
|
}
|
|
function getInputsTypeExpression(meta) {
|
|
return literalMap(Object.keys(meta.inputs).map(key => {
|
|
const value = meta.inputs[key];
|
|
const values = [{
|
|
key: 'alias',
|
|
value: literal(value.bindingPropertyName),
|
|
quoted: true
|
|
}, {
|
|
key: 'required',
|
|
value: literal(value.required),
|
|
quoted: true
|
|
}];
|
|
if (value.isSignal) {
|
|
values.push({
|
|
key: 'isSignal',
|
|
value: literal(value.isSignal),
|
|
quoted: true
|
|
});
|
|
}
|
|
return {
|
|
key,
|
|
value: literalMap(values),
|
|
quoted: true
|
|
};
|
|
}));
|
|
}
|
|
function createDirectiveType(meta) {
|
|
const typeParams = createBaseDirectiveTypeParams(meta);
|
|
typeParams.push(NONE_TYPE);
|
|
typeParams.push(expressionType(literal(meta.isStandalone)));
|
|
typeParams.push(createHostDirectivesType(meta));
|
|
if (meta.isSignal) {
|
|
typeParams.push(expressionType(literal(meta.isSignal)));
|
|
}
|
|
return expressionType(importExpr(Identifiers.DirectiveDeclaration, typeParams));
|
|
}
|
|
function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
|
|
const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
|
|
const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
|
|
if (hostBindingsMetadata.specialAttributes.styleAttr) {
|
|
hostBindingsMetadata.attributes['style'] = literal(hostBindingsMetadata.specialAttributes.styleAttr);
|
|
}
|
|
if (hostBindingsMetadata.specialAttributes.classAttr) {
|
|
hostBindingsMetadata.attributes['class'] = literal(hostBindingsMetadata.specialAttributes.classAttr);
|
|
}
|
|
const hostJob = ingestHostBinding({
|
|
componentName: name,
|
|
componentSelector: selector,
|
|
properties: bindings,
|
|
events: eventBindings,
|
|
attributes: hostBindingsMetadata.attributes
|
|
}, bindingParser, constantPool);
|
|
transform(hostJob, CompilationJobKind.Host);
|
|
definitionMap.set('hostAttrs', hostJob.root.attributes);
|
|
const varCount = hostJob.root.vars;
|
|
if (varCount !== null && varCount > 0) {
|
|
definitionMap.set('hostVars', literal(varCount));
|
|
}
|
|
return emitHostBindingFunction(hostJob);
|
|
}
|
|
const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
|
|
function parseHostBindings(host) {
|
|
const attributes = {};
|
|
const listeners = {};
|
|
const properties = {};
|
|
const specialAttributes = {};
|
|
for (const key of Object.keys(host)) {
|
|
const value = host[key];
|
|
const matches = key.match(HOST_REG_EXP);
|
|
if (matches === null) {
|
|
switch (key) {
|
|
case 'class':
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Class binding must be string`);
|
|
}
|
|
specialAttributes.classAttr = value;
|
|
break;
|
|
case 'style':
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Style binding must be string`);
|
|
}
|
|
specialAttributes.styleAttr = value;
|
|
break;
|
|
default:
|
|
if (typeof value === 'string') {
|
|
attributes[key] = literal(value);
|
|
} else {
|
|
attributes[key] = value;
|
|
}
|
|
}
|
|
} else if (matches[1] != null) {
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Property binding must be string`);
|
|
}
|
|
properties[matches[1]] = value;
|
|
} else if (matches[2] != null) {
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Event binding must be string`);
|
|
}
|
|
listeners[matches[2]] = value;
|
|
}
|
|
}
|
|
return {
|
|
attributes,
|
|
listeners,
|
|
properties,
|
|
specialAttributes
|
|
};
|
|
}
|
|
function verifyHostBindings(bindings, sourceSpan) {
|
|
const bindingParser = makeBindingParser();
|
|
bindingParser.createDirectiveHostEventAsts(bindings.listeners, sourceSpan);
|
|
bindingParser.createBoundHostProperties(bindings.properties, sourceSpan);
|
|
return bindingParser.errors;
|
|
}
|
|
function compileStyles(styles, selector, hostSelector) {
|
|
const shadowCss = new ShadowCss();
|
|
return styles.map(style => {
|
|
return shadowCss.shimCssText(style, selector, hostSelector);
|
|
});
|
|
}
|
|
function encapsulateStyle(style, componentIdentifier) {
|
|
const shadowCss = new ShadowCss();
|
|
const selector = componentIdentifier ? CONTENT_ATTR.replace(COMPONENT_VARIABLE, componentIdentifier) : CONTENT_ATTR;
|
|
const hostSelector = componentIdentifier ? HOST_ATTR.replace(COMPONENT_VARIABLE, componentIdentifier) : HOST_ATTR;
|
|
return shadowCss.shimCssText(style, selector, hostSelector);
|
|
}
|
|
function createHostDirectivesType(meta) {
|
|
if (!meta.hostDirectives?.length) {
|
|
return NONE_TYPE;
|
|
}
|
|
return expressionType(literalArr(meta.hostDirectives.map(hostMeta => literalMap([{
|
|
key: 'directive',
|
|
value: typeofExpr(hostMeta.directive.type),
|
|
quoted: false
|
|
}, {
|
|
key: 'inputs',
|
|
value: stringMapAsLiteralExpression(hostMeta.inputs || {}),
|
|
quoted: false
|
|
}, {
|
|
key: 'outputs',
|
|
value: stringMapAsLiteralExpression(hostMeta.outputs || {}),
|
|
quoted: false
|
|
}]))));
|
|
}
|
|
function createHostDirectivesFeatureArg(hostDirectives) {
|
|
const expressions = [];
|
|
let hasForwardRef = false;
|
|
for (const current of hostDirectives) {
|
|
if (!current.inputs && !current.outputs) {
|
|
expressions.push(current.directive.type);
|
|
} else {
|
|
const keys = [{
|
|
key: 'directive',
|
|
value: current.directive.type,
|
|
quoted: false
|
|
}];
|
|
if (current.inputs) {
|
|
const inputsLiteral = createHostDirectivesMappingArray(current.inputs);
|
|
if (inputsLiteral) {
|
|
keys.push({
|
|
key: 'inputs',
|
|
value: inputsLiteral,
|
|
quoted: false
|
|
});
|
|
}
|
|
}
|
|
if (current.outputs) {
|
|
const outputsLiteral = createHostDirectivesMappingArray(current.outputs);
|
|
if (outputsLiteral) {
|
|
keys.push({
|
|
key: 'outputs',
|
|
value: outputsLiteral,
|
|
quoted: false
|
|
});
|
|
}
|
|
}
|
|
expressions.push(literalMap(keys));
|
|
}
|
|
if (current.isForwardReference) {
|
|
hasForwardRef = true;
|
|
}
|
|
}
|
|
return hasForwardRef ? new FunctionExpr([], [new ReturnStatement(literalArr(expressions))]) : literalArr(expressions);
|
|
}
|
|
function createHostDirectivesMappingArray(mapping) {
|
|
const elements = [];
|
|
for (const publicName in mapping) {
|
|
if (mapping.hasOwnProperty(publicName)) {
|
|
elements.push(literal(publicName), literal(mapping[publicName]));
|
|
}
|
|
}
|
|
return elements.length > 0 ? literalArr(elements) : null;
|
|
}
|
|
function compileDeferResolverFunction(meta) {
|
|
const depExpressions = [];
|
|
if (meta.mode === 0) {
|
|
for (const dep of meta.dependencies) {
|
|
if (dep.isDeferrable) {
|
|
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
|
|
const importExpr = new DynamicImportExpr(dep.importPath).prop('then').callFn([innerFn]);
|
|
depExpressions.push(importExpr);
|
|
} else {
|
|
depExpressions.push(dep.typeReference);
|
|
}
|
|
}
|
|
} else {
|
|
for (const {
|
|
symbolName,
|
|
importPath,
|
|
isDefaultImport
|
|
} of meta.dependencies) {
|
|
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
const importExpr = new DynamicImportExpr(importPath).prop('then').callFn([innerFn]);
|
|
depExpressions.push(importExpr);
|
|
}
|
|
}
|
|
return arrowFn([], literalArr(depExpressions));
|
|
}
|
|
|
|
class CombinedRecursiveAstVisitor extends RecursiveAstVisitor {
|
|
visit(node) {
|
|
if (node instanceof ASTWithSource) {
|
|
this.visit(node.ast);
|
|
} else {
|
|
node.visit(this);
|
|
}
|
|
}
|
|
visitElement(element) {
|
|
this.visitAllTemplateNodes(element.attributes);
|
|
this.visitAllTemplateNodes(element.inputs);
|
|
this.visitAllTemplateNodes(element.outputs);
|
|
this.visitAllTemplateNodes(element.directives);
|
|
this.visitAllTemplateNodes(element.references);
|
|
this.visitAllTemplateNodes(element.children);
|
|
}
|
|
visitTemplate(template) {
|
|
this.visitAllTemplateNodes(template.attributes);
|
|
this.visitAllTemplateNodes(template.inputs);
|
|
this.visitAllTemplateNodes(template.outputs);
|
|
this.visitAllTemplateNodes(template.directives);
|
|
this.visitAllTemplateNodes(template.templateAttrs);
|
|
this.visitAllTemplateNodes(template.variables);
|
|
this.visitAllTemplateNodes(template.references);
|
|
this.visitAllTemplateNodes(template.children);
|
|
}
|
|
visitContent(content) {
|
|
this.visitAllTemplateNodes(content.children);
|
|
}
|
|
visitBoundAttribute(attribute) {
|
|
this.visit(attribute.value);
|
|
}
|
|
visitBoundEvent(attribute) {
|
|
this.visit(attribute.handler);
|
|
}
|
|
visitBoundText(text) {
|
|
this.visit(text.value);
|
|
}
|
|
visitIcu(icu) {
|
|
Object.keys(icu.vars).forEach(key => this.visit(icu.vars[key]));
|
|
Object.keys(icu.placeholders).forEach(key => this.visit(icu.placeholders[key]));
|
|
}
|
|
visitDeferredBlock(deferred) {
|
|
deferred.visitAll(this);
|
|
}
|
|
visitDeferredTrigger(trigger) {
|
|
if (trigger instanceof BoundDeferredTrigger) {
|
|
this.visit(trigger.value);
|
|
} else if (trigger instanceof ViewportDeferredTrigger && trigger.options !== null) {
|
|
this.visit(trigger.options);
|
|
}
|
|
}
|
|
visitDeferredBlockPlaceholder(block) {
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitDeferredBlockError(block) {
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitDeferredBlockLoading(block) {
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitSwitchBlock(block) {
|
|
this.visit(block.expression);
|
|
this.visitAllTemplateNodes(block.groups);
|
|
}
|
|
visitSwitchBlockCase(block) {
|
|
block.expression && this.visit(block.expression);
|
|
}
|
|
visitSwitchBlockCaseGroup(block) {
|
|
this.visitAllTemplateNodes(block.cases);
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitForLoopBlock(block) {
|
|
block.item.visit(this);
|
|
this.visitAllTemplateNodes(block.contextVariables);
|
|
this.visit(block.expression);
|
|
this.visitAllTemplateNodes(block.children);
|
|
block.empty?.visit(this);
|
|
}
|
|
visitForLoopBlockEmpty(block) {
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitIfBlock(block) {
|
|
this.visitAllTemplateNodes(block.branches);
|
|
}
|
|
visitIfBlockBranch(block) {
|
|
block.expression && this.visit(block.expression);
|
|
block.expressionAlias?.visit(this);
|
|
this.visitAllTemplateNodes(block.children);
|
|
}
|
|
visitLetDeclaration(decl) {
|
|
this.visit(decl.value);
|
|
}
|
|
visitComponent(component) {
|
|
this.visitAllTemplateNodes(component.attributes);
|
|
this.visitAllTemplateNodes(component.inputs);
|
|
this.visitAllTemplateNodes(component.outputs);
|
|
this.visitAllTemplateNodes(component.directives);
|
|
this.visitAllTemplateNodes(component.references);
|
|
this.visitAllTemplateNodes(component.children);
|
|
}
|
|
visitDirective(directive) {
|
|
this.visitAllTemplateNodes(directive.attributes);
|
|
this.visitAllTemplateNodes(directive.inputs);
|
|
this.visitAllTemplateNodes(directive.outputs);
|
|
this.visitAllTemplateNodes(directive.references);
|
|
}
|
|
visitVariable(variable) {}
|
|
visitReference(reference) {}
|
|
visitTextAttribute(attribute) {}
|
|
visitText(text) {}
|
|
visitUnknownBlock(block) {}
|
|
visitAllTemplateNodes(nodes) {
|
|
for (const node of nodes) {
|
|
this.visit(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
function diff(fullList, itemsToExclude) {
|
|
const exclude = new Set(itemsToExclude);
|
|
return fullList.filter(item => !exclude.has(item));
|
|
}
|
|
function findMatchingDirectivesAndPipes(template, directiveSelectors) {
|
|
const matcher = new SelectorMatcher();
|
|
for (const selector of directiveSelectors) {
|
|
const fakeDirective = {
|
|
selector,
|
|
exportAs: null,
|
|
inputs: {
|
|
hasBindingPropertyName() {
|
|
return false;
|
|
}
|
|
},
|
|
outputs: {
|
|
hasBindingPropertyName() {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
matcher.addSelectables(CssSelector.parse(selector), [fakeDirective]);
|
|
}
|
|
const parsedTemplate = parseTemplate(template, '');
|
|
const binder = new R3TargetBinder(matcher);
|
|
const bound = binder.bind({
|
|
template: parsedTemplate.nodes
|
|
});
|
|
const eagerDirectiveSelectors = bound.getEagerlyUsedDirectives().map(dir => dir.selector);
|
|
const allMatchedDirectiveSelectors = bound.getUsedDirectives().map(dir => dir.selector);
|
|
const eagerPipes = bound.getEagerlyUsedPipes();
|
|
return {
|
|
directives: {
|
|
regular: eagerDirectiveSelectors,
|
|
deferCandidates: diff(allMatchedDirectiveSelectors, eagerDirectiveSelectors)
|
|
},
|
|
pipes: {
|
|
regular: eagerPipes,
|
|
deferCandidates: diff(bound.getUsedPipes(), eagerPipes)
|
|
}
|
|
};
|
|
}
|
|
class R3TargetBinder {
|
|
directiveMatcher;
|
|
constructor(directiveMatcher) {
|
|
this.directiveMatcher = directiveMatcher;
|
|
}
|
|
bind(target) {
|
|
if (!target.template && !target.host) {
|
|
throw new Error('Empty bound targets are not supported');
|
|
}
|
|
const directives = new Map();
|
|
const eagerDirectives = [];
|
|
const missingDirectives = new Set();
|
|
const bindings = new Map();
|
|
const references = new Map();
|
|
const scopedNodeEntities = new Map();
|
|
const expressions = new Map();
|
|
const symbols = new Map();
|
|
const nestingLevel = new Map();
|
|
const usedPipes = new Set();
|
|
const eagerPipes = new Set();
|
|
const deferBlocks = [];
|
|
if (target.template) {
|
|
const scope = Scope.apply(target.template);
|
|
extractScopedNodeEntities(scope, scopedNodeEntities);
|
|
DirectiveBinder.apply(target.template, this.directiveMatcher, directives, eagerDirectives, missingDirectives, bindings, references);
|
|
TemplateBinder.applyWithScope(target.template, scope, expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks);
|
|
}
|
|
if (target.host) {
|
|
directives.set(target.host.node, target.host.directives);
|
|
TemplateBinder.applyWithScope(target.host.node, Scope.apply(target.host.node), expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks);
|
|
}
|
|
return new R3BoundTarget(target, directives, eagerDirectives, missingDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks);
|
|
}
|
|
}
|
|
class Scope {
|
|
parentScope;
|
|
rootNode;
|
|
namedEntities = new Map();
|
|
elementLikeInScope = new Set();
|
|
childScopes = new Map();
|
|
isDeferred;
|
|
constructor(parentScope, rootNode) {
|
|
this.parentScope = parentScope;
|
|
this.rootNode = rootNode;
|
|
this.isDeferred = parentScope !== null && parentScope.isDeferred ? true : rootNode instanceof DeferredBlock;
|
|
}
|
|
static newRootScope() {
|
|
return new Scope(null, null);
|
|
}
|
|
static apply(template) {
|
|
const scope = Scope.newRootScope();
|
|
scope.ingest(template);
|
|
return scope;
|
|
}
|
|
ingest(nodeOrNodes) {
|
|
if (nodeOrNodes instanceof Template) {
|
|
nodeOrNodes.variables.forEach(node => this.visitVariable(node));
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
} else if (nodeOrNodes instanceof IfBlockBranch) {
|
|
if (nodeOrNodes.expressionAlias !== null) {
|
|
this.visitVariable(nodeOrNodes.expressionAlias);
|
|
}
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
} else if (nodeOrNodes instanceof ForLoopBlock) {
|
|
this.visitVariable(nodeOrNodes.item);
|
|
nodeOrNodes.contextVariables.forEach(v => this.visitVariable(v));
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
} else if (nodeOrNodes instanceof SwitchBlockCaseGroup || nodeOrNodes instanceof ForLoopBlockEmpty || nodeOrNodes instanceof DeferredBlock || nodeOrNodes instanceof DeferredBlockError || nodeOrNodes instanceof DeferredBlockPlaceholder || nodeOrNodes instanceof DeferredBlockLoading || nodeOrNodes instanceof Content) {
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
} else if (!(nodeOrNodes instanceof HostElement)) {
|
|
nodeOrNodes.forEach(node => node.visit(this));
|
|
}
|
|
}
|
|
visitElement(element) {
|
|
this.visitElementLike(element);
|
|
}
|
|
visitTemplate(template) {
|
|
template.directives.forEach(node => node.visit(this));
|
|
template.references.forEach(node => this.visitReference(node));
|
|
this.ingestScopedNode(template);
|
|
}
|
|
visitVariable(variable) {
|
|
this.maybeDeclare(variable);
|
|
}
|
|
visitReference(reference) {
|
|
this.maybeDeclare(reference);
|
|
}
|
|
visitDeferredBlock(deferred) {
|
|
this.ingestScopedNode(deferred);
|
|
deferred.placeholder?.visit(this);
|
|
deferred.loading?.visit(this);
|
|
deferred.error?.visit(this);
|
|
}
|
|
visitDeferredBlockPlaceholder(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitDeferredBlockError(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitDeferredBlockLoading(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitSwitchBlock(block) {
|
|
block.groups.forEach(node => node.visit(this));
|
|
}
|
|
visitSwitchBlockCase(block) {}
|
|
visitSwitchBlockCaseGroup(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitForLoopBlock(block) {
|
|
this.ingestScopedNode(block);
|
|
block.empty?.visit(this);
|
|
}
|
|
visitForLoopBlockEmpty(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitIfBlock(block) {
|
|
block.branches.forEach(node => node.visit(this));
|
|
}
|
|
visitIfBlockBranch(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitContent(content) {
|
|
this.ingestScopedNode(content);
|
|
}
|
|
visitLetDeclaration(decl) {
|
|
this.maybeDeclare(decl);
|
|
}
|
|
visitComponent(component) {
|
|
this.visitElementLike(component);
|
|
}
|
|
visitDirective(directive) {
|
|
directive.references.forEach(current => this.visitReference(current));
|
|
}
|
|
visitBoundAttribute(attr) {}
|
|
visitBoundEvent(event) {}
|
|
visitBoundText(text) {}
|
|
visitText(text) {}
|
|
visitTextAttribute(attr) {}
|
|
visitIcu(icu) {}
|
|
visitDeferredTrigger(trigger) {}
|
|
visitUnknownBlock(block) {}
|
|
visitElementLike(node) {
|
|
node.directives.forEach(current => current.visit(this));
|
|
node.references.forEach(current => this.visitReference(current));
|
|
node.children.forEach(current => current.visit(this));
|
|
this.elementLikeInScope.add(node);
|
|
}
|
|
maybeDeclare(thing) {
|
|
if (!this.namedEntities.has(thing.name)) {
|
|
this.namedEntities.set(thing.name, thing);
|
|
}
|
|
}
|
|
lookup(name) {
|
|
if (this.namedEntities.has(name)) {
|
|
return this.namedEntities.get(name);
|
|
} else if (this.parentScope !== null) {
|
|
return this.parentScope.lookup(name);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
getChildScope(node) {
|
|
const res = this.childScopes.get(node);
|
|
if (res === undefined) {
|
|
throw new Error(`Assertion error: child scope for ${node} not found`);
|
|
}
|
|
return res;
|
|
}
|
|
ingestScopedNode(node) {
|
|
const scope = new Scope(this, node);
|
|
scope.ingest(node);
|
|
this.childScopes.set(node, scope);
|
|
}
|
|
}
|
|
class DirectiveBinder {
|
|
directiveMatcher;
|
|
directives;
|
|
eagerDirectives;
|
|
missingDirectives;
|
|
bindings;
|
|
references;
|
|
isInDeferBlock = false;
|
|
constructor(directiveMatcher, directives, eagerDirectives, missingDirectives, bindings, references) {
|
|
this.directiveMatcher = directiveMatcher;
|
|
this.directives = directives;
|
|
this.eagerDirectives = eagerDirectives;
|
|
this.missingDirectives = missingDirectives;
|
|
this.bindings = bindings;
|
|
this.references = references;
|
|
}
|
|
static apply(template, directiveMatcher, directives, eagerDirectives, missingDirectives, bindings, references) {
|
|
const matcher = new DirectiveBinder(directiveMatcher, directives, eagerDirectives, missingDirectives, bindings, references);
|
|
matcher.ingest(template);
|
|
}
|
|
ingest(template) {
|
|
template.forEach(node => node.visit(this));
|
|
}
|
|
visitElement(element) {
|
|
this.visitElementOrTemplate(element);
|
|
}
|
|
visitTemplate(template) {
|
|
this.visitElementOrTemplate(template);
|
|
}
|
|
visitDeferredBlock(deferred) {
|
|
const wasInDeferBlock = this.isInDeferBlock;
|
|
this.isInDeferBlock = true;
|
|
deferred.children.forEach(child => child.visit(this));
|
|
this.isInDeferBlock = wasInDeferBlock;
|
|
deferred.placeholder?.visit(this);
|
|
deferred.loading?.visit(this);
|
|
deferred.error?.visit(this);
|
|
}
|
|
visitDeferredBlockPlaceholder(block) {
|
|
block.children.forEach(child => child.visit(this));
|
|
}
|
|
visitDeferredBlockError(block) {
|
|
block.children.forEach(child => child.visit(this));
|
|
}
|
|
visitDeferredBlockLoading(block) {
|
|
block.children.forEach(child => child.visit(this));
|
|
}
|
|
visitSwitchBlock(block) {
|
|
block.groups.forEach(node => node.visit(this));
|
|
}
|
|
visitSwitchBlockCase(block) {}
|
|
visitSwitchBlockCaseGroup(block) {
|
|
block.children.forEach(node => node.visit(this));
|
|
}
|
|
visitForLoopBlock(block) {
|
|
block.item.visit(this);
|
|
block.contextVariables.forEach(v => v.visit(this));
|
|
block.children.forEach(node => node.visit(this));
|
|
block.empty?.visit(this);
|
|
}
|
|
visitForLoopBlockEmpty(block) {
|
|
block.children.forEach(node => node.visit(this));
|
|
}
|
|
visitIfBlock(block) {
|
|
block.branches.forEach(node => node.visit(this));
|
|
}
|
|
visitIfBlockBranch(block) {
|
|
block.expressionAlias?.visit(this);
|
|
block.children.forEach(node => node.visit(this));
|
|
}
|
|
visitContent(content) {
|
|
content.children.forEach(child => child.visit(this));
|
|
}
|
|
visitComponent(node) {
|
|
if (this.directiveMatcher instanceof SelectorlessMatcher) {
|
|
const componentMatches = this.directiveMatcher.match(node.componentName);
|
|
if (componentMatches.length > 0) {
|
|
this.trackSelectorlessMatchesAndDirectives(node, componentMatches);
|
|
} else {
|
|
this.missingDirectives.add(node.componentName);
|
|
}
|
|
}
|
|
node.directives.forEach(directive => directive.visit(this));
|
|
node.children.forEach(child => child.visit(this));
|
|
}
|
|
visitDirective(node) {
|
|
if (this.directiveMatcher instanceof SelectorlessMatcher) {
|
|
const directives = this.directiveMatcher.match(node.name);
|
|
if (directives.length > 0) {
|
|
this.trackSelectorlessMatchesAndDirectives(node, directives);
|
|
} else {
|
|
this.missingDirectives.add(node.name);
|
|
}
|
|
}
|
|
}
|
|
visitElementOrTemplate(node) {
|
|
if (this.directiveMatcher instanceof SelectorMatcher) {
|
|
const directives = [];
|
|
const cssSelector = createCssSelectorFromNode(node);
|
|
this.directiveMatcher.match(cssSelector, (_, results) => directives.push(...results));
|
|
this.trackSelectorBasedBindingsAndDirectives(node, directives);
|
|
} else {
|
|
node.references.forEach(ref => {
|
|
if (ref.value.trim() === '') {
|
|
this.references.set(ref, node);
|
|
}
|
|
});
|
|
}
|
|
node.directives.forEach(directive => directive.visit(this));
|
|
node.children.forEach(child => child.visit(this));
|
|
}
|
|
trackMatchedDirectives(node, directives) {
|
|
if (directives.length > 0) {
|
|
this.directives.set(node, directives);
|
|
if (!this.isInDeferBlock) {
|
|
this.eagerDirectives.push(...directives);
|
|
}
|
|
}
|
|
}
|
|
trackSelectorlessMatchesAndDirectives(node, directives) {
|
|
if (directives.length === 0) {
|
|
return;
|
|
}
|
|
this.trackMatchedDirectives(node, directives);
|
|
const setBinding = (meta, attribute, ioType) => {
|
|
if (meta[ioType].hasBindingPropertyName(attribute.name)) {
|
|
this.bindings.set(attribute, meta);
|
|
}
|
|
};
|
|
for (const directive of directives) {
|
|
node.inputs.forEach(input => setBinding(directive, input, 'inputs'));
|
|
node.attributes.forEach(attr => setBinding(directive, attr, 'inputs'));
|
|
node.outputs.forEach(output => setBinding(directive, output, 'outputs'));
|
|
}
|
|
node.references.forEach(ref => this.references.set(ref, {
|
|
directive: directives[0],
|
|
node: node
|
|
}));
|
|
}
|
|
trackSelectorBasedBindingsAndDirectives(node, directives) {
|
|
this.trackMatchedDirectives(node, directives);
|
|
node.references.forEach(ref => {
|
|
let dirTarget = null;
|
|
if (ref.value.trim() === '') {
|
|
dirTarget = directives.find(dir => dir.isComponent) || null;
|
|
} else {
|
|
dirTarget = directives.find(dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) || null;
|
|
if (dirTarget === null) {
|
|
return;
|
|
}
|
|
}
|
|
if (dirTarget !== null) {
|
|
this.references.set(ref, {
|
|
directive: dirTarget,
|
|
node
|
|
});
|
|
} else {
|
|
this.references.set(ref, node);
|
|
}
|
|
});
|
|
const setAttributeBinding = (attribute, ioType) => {
|
|
const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name));
|
|
const binding = dir !== undefined ? dir : node;
|
|
this.bindings.set(attribute, binding);
|
|
};
|
|
node.inputs.forEach(input => setAttributeBinding(input, 'inputs'));
|
|
node.attributes.forEach(attr => setAttributeBinding(attr, 'inputs'));
|
|
if (node instanceof Template) {
|
|
node.templateAttrs.forEach(attr => setAttributeBinding(attr, 'inputs'));
|
|
}
|
|
node.outputs.forEach(output => setAttributeBinding(output, 'outputs'));
|
|
}
|
|
visitVariable(variable) {}
|
|
visitReference(reference) {}
|
|
visitTextAttribute(attribute) {}
|
|
visitBoundAttribute(attribute) {}
|
|
visitBoundEvent(attribute) {}
|
|
visitBoundAttributeOrEvent(node) {}
|
|
visitText(text) {}
|
|
visitBoundText(text) {}
|
|
visitIcu(icu) {}
|
|
visitDeferredTrigger(trigger) {}
|
|
visitUnknownBlock(block) {}
|
|
visitLetDeclaration(decl) {}
|
|
}
|
|
class TemplateBinder extends CombinedRecursiveAstVisitor {
|
|
bindings;
|
|
symbols;
|
|
usedPipes;
|
|
eagerPipes;
|
|
deferBlocks;
|
|
nestingLevel;
|
|
scope;
|
|
rootNode;
|
|
level;
|
|
visitNode = node => node.visit(this);
|
|
constructor(bindings, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, rootNode, level) {
|
|
super();
|
|
this.bindings = bindings;
|
|
this.symbols = symbols;
|
|
this.usedPipes = usedPipes;
|
|
this.eagerPipes = eagerPipes;
|
|
this.deferBlocks = deferBlocks;
|
|
this.nestingLevel = nestingLevel;
|
|
this.scope = scope;
|
|
this.rootNode = rootNode;
|
|
this.level = level;
|
|
}
|
|
static applyWithScope(nodeOrNodes, scope, expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks) {
|
|
const template = nodeOrNodes instanceof Template ? nodeOrNodes : null;
|
|
const binder = new TemplateBinder(expressions, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, template, 0);
|
|
binder.ingest(nodeOrNodes);
|
|
}
|
|
ingest(nodeOrNodes) {
|
|
if (nodeOrNodes instanceof Template) {
|
|
nodeOrNodes.variables.forEach(this.visitNode);
|
|
nodeOrNodes.children.forEach(this.visitNode);
|
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
} else if (nodeOrNodes instanceof IfBlockBranch) {
|
|
if (nodeOrNodes.expressionAlias !== null) {
|
|
this.visitNode(nodeOrNodes.expressionAlias);
|
|
}
|
|
nodeOrNodes.children.forEach(this.visitNode);
|
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
} else if (nodeOrNodes instanceof ForLoopBlock) {
|
|
this.visitNode(nodeOrNodes.item);
|
|
nodeOrNodes.contextVariables.forEach(v => this.visitNode(v));
|
|
nodeOrNodes.trackBy.visit(this);
|
|
nodeOrNodes.children.forEach(this.visitNode);
|
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
} else if (nodeOrNodes instanceof DeferredBlock) {
|
|
if (this.scope.rootNode !== nodeOrNodes) {
|
|
throw new Error(`Assertion error: resolved incorrect scope for deferred block ${nodeOrNodes}`);
|
|
}
|
|
this.deferBlocks.push([nodeOrNodes, this.scope]);
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
} else if (nodeOrNodes instanceof SwitchBlockCaseGroup || nodeOrNodes instanceof ForLoopBlockEmpty || nodeOrNodes instanceof DeferredBlockError || nodeOrNodes instanceof DeferredBlockPlaceholder || nodeOrNodes instanceof DeferredBlockLoading || nodeOrNodes instanceof Content) {
|
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
} else if (nodeOrNodes instanceof HostElement) {
|
|
this.nestingLevel.set(nodeOrNodes, 0);
|
|
} else {
|
|
nodeOrNodes.forEach(this.visitNode);
|
|
}
|
|
}
|
|
visitTemplate(template) {
|
|
template.inputs.forEach(this.visitNode);
|
|
template.outputs.forEach(this.visitNode);
|
|
template.directives.forEach(this.visitNode);
|
|
template.templateAttrs.forEach(this.visitNode);
|
|
template.references.forEach(this.visitNode);
|
|
this.ingestScopedNode(template);
|
|
}
|
|
visitVariable(variable) {
|
|
if (this.rootNode !== null) {
|
|
this.symbols.set(variable, this.rootNode);
|
|
}
|
|
}
|
|
visitReference(reference) {
|
|
if (this.rootNode !== null) {
|
|
this.symbols.set(reference, this.rootNode);
|
|
}
|
|
}
|
|
visitDeferredBlock(deferred) {
|
|
this.ingestScopedNode(deferred);
|
|
deferred.triggers.when?.value.visit(this);
|
|
deferred.prefetchTriggers.when?.value.visit(this);
|
|
deferred.hydrateTriggers.when?.value.visit(this);
|
|
deferred.hydrateTriggers.never?.visit(this);
|
|
deferred.placeholder && this.visitNode(deferred.placeholder);
|
|
deferred.loading && this.visitNode(deferred.loading);
|
|
deferred.error && this.visitNode(deferred.error);
|
|
}
|
|
visitDeferredBlockPlaceholder(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitDeferredBlockError(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitDeferredBlockLoading(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitSwitchBlockCase(block) {
|
|
block.expression?.visit(this);
|
|
}
|
|
visitSwitchBlockCaseGroup(block) {
|
|
block.cases.forEach(caseNode => caseNode.visit(this));
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitForLoopBlock(block) {
|
|
block.expression.visit(this);
|
|
this.ingestScopedNode(block);
|
|
block.empty?.visit(this);
|
|
}
|
|
visitForLoopBlockEmpty(block) {
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitIfBlockBranch(block) {
|
|
block.expression?.visit(this);
|
|
this.ingestScopedNode(block);
|
|
}
|
|
visitContent(content) {
|
|
this.ingestScopedNode(content);
|
|
}
|
|
visitLetDeclaration(decl) {
|
|
super.visitLetDeclaration(decl);
|
|
if (this.rootNode !== null) {
|
|
this.symbols.set(decl, this.rootNode);
|
|
}
|
|
}
|
|
visitPipe(ast, context) {
|
|
this.usedPipes.add(ast.name);
|
|
if (!this.scope.isDeferred) {
|
|
this.eagerPipes.add(ast.name);
|
|
}
|
|
return super.visitPipe(ast, context);
|
|
}
|
|
visitPropertyRead(ast, context) {
|
|
this.maybeMap(ast, ast.name);
|
|
return super.visitPropertyRead(ast, context);
|
|
}
|
|
visitSafePropertyRead(ast, context) {
|
|
this.maybeMap(ast, ast.name);
|
|
return super.visitSafePropertyRead(ast, context);
|
|
}
|
|
ingestScopedNode(node) {
|
|
const childScope = this.scope.getChildScope(node);
|
|
const binder = new TemplateBinder(this.bindings, this.symbols, this.usedPipes, this.eagerPipes, this.deferBlocks, this.nestingLevel, childScope, node, this.level + 1);
|
|
binder.ingest(node);
|
|
}
|
|
maybeMap(ast, name) {
|
|
if (!(ast.receiver instanceof ImplicitReceiver)) {
|
|
return;
|
|
}
|
|
const target = this.scope.lookup(name);
|
|
if (target !== null) {
|
|
this.bindings.set(ast, target);
|
|
}
|
|
}
|
|
}
|
|
class R3BoundTarget {
|
|
target;
|
|
directives;
|
|
eagerDirectives;
|
|
missingDirectives;
|
|
bindings;
|
|
references;
|
|
exprTargets;
|
|
symbols;
|
|
nestingLevel;
|
|
scopedNodeEntities;
|
|
usedPipes;
|
|
eagerPipes;
|
|
deferredBlocks;
|
|
deferredScopes;
|
|
constructor(target, directives, eagerDirectives, missingDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, rawDeferred) {
|
|
this.target = target;
|
|
this.directives = directives;
|
|
this.eagerDirectives = eagerDirectives;
|
|
this.missingDirectives = missingDirectives;
|
|
this.bindings = bindings;
|
|
this.references = references;
|
|
this.exprTargets = exprTargets;
|
|
this.symbols = symbols;
|
|
this.nestingLevel = nestingLevel;
|
|
this.scopedNodeEntities = scopedNodeEntities;
|
|
this.usedPipes = usedPipes;
|
|
this.eagerPipes = eagerPipes;
|
|
this.deferredBlocks = rawDeferred.map(current => current[0]);
|
|
this.deferredScopes = new Map(rawDeferred);
|
|
}
|
|
getEntitiesInScope(node) {
|
|
return this.scopedNodeEntities.get(node) ?? new Set();
|
|
}
|
|
getDirectivesOfNode(node) {
|
|
return this.directives.get(node) || null;
|
|
}
|
|
getReferenceTarget(ref) {
|
|
return this.references.get(ref) || null;
|
|
}
|
|
getConsumerOfBinding(binding) {
|
|
return this.bindings.get(binding) || null;
|
|
}
|
|
getExpressionTarget(expr) {
|
|
return this.exprTargets.get(expr) || null;
|
|
}
|
|
getDefinitionNodeOfSymbol(symbol) {
|
|
return this.symbols.get(symbol) || null;
|
|
}
|
|
getNestingLevel(node) {
|
|
return this.nestingLevel.get(node) || 0;
|
|
}
|
|
getUsedDirectives() {
|
|
const set = new Set();
|
|
this.directives.forEach(dirs => dirs.forEach(dir => set.add(dir)));
|
|
return Array.from(set.values());
|
|
}
|
|
getEagerlyUsedDirectives() {
|
|
const set = new Set(this.eagerDirectives);
|
|
return Array.from(set.values());
|
|
}
|
|
getUsedPipes() {
|
|
return Array.from(this.usedPipes);
|
|
}
|
|
getEagerlyUsedPipes() {
|
|
return Array.from(this.eagerPipes);
|
|
}
|
|
getDeferBlocks() {
|
|
return this.deferredBlocks;
|
|
}
|
|
getDeferredTriggerTarget(block, trigger) {
|
|
if (!(trigger instanceof InteractionDeferredTrigger) && !(trigger instanceof ViewportDeferredTrigger) && !(trigger instanceof HoverDeferredTrigger)) {
|
|
return null;
|
|
}
|
|
const name = trigger.reference;
|
|
if (name === null) {
|
|
let target = null;
|
|
if (block.placeholder !== null) {
|
|
for (const child of block.placeholder.children) {
|
|
if (child instanceof Comment$1) {
|
|
continue;
|
|
}
|
|
if (target !== null) {
|
|
return null;
|
|
}
|
|
if (child instanceof Element$1) {
|
|
target = child;
|
|
}
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
const outsideRef = this.findEntityInScope(block, name);
|
|
if (outsideRef instanceof Reference && this.getDefinitionNodeOfSymbol(outsideRef) !== block) {
|
|
const target = this.getReferenceTarget(outsideRef);
|
|
if (target !== null) {
|
|
return this.referenceTargetToElement(target);
|
|
}
|
|
}
|
|
if (block.placeholder !== null) {
|
|
const refInPlaceholder = this.findEntityInScope(block.placeholder, name);
|
|
const targetInPlaceholder = refInPlaceholder instanceof Reference ? this.getReferenceTarget(refInPlaceholder) : null;
|
|
if (targetInPlaceholder !== null) {
|
|
return this.referenceTargetToElement(targetInPlaceholder);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
isDeferred(element) {
|
|
for (const block of this.deferredBlocks) {
|
|
if (!this.deferredScopes.has(block)) {
|
|
continue;
|
|
}
|
|
const stack = [this.deferredScopes.get(block)];
|
|
while (stack.length > 0) {
|
|
const current = stack.pop();
|
|
if (current.elementLikeInScope.has(element)) {
|
|
return true;
|
|
}
|
|
stack.push(...current.childScopes.values());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
referencedDirectiveExists(name) {
|
|
return !this.missingDirectives.has(name);
|
|
}
|
|
findEntityInScope(rootNode, name) {
|
|
const entities = this.getEntitiesInScope(rootNode);
|
|
for (const entity of entities) {
|
|
if (entity.name === name) {
|
|
return entity;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
referenceTargetToElement(target) {
|
|
if (target instanceof Element$1) {
|
|
return target;
|
|
}
|
|
if (target instanceof Template || target.node instanceof Component$1 || target.node instanceof Directive$1 || target.node instanceof HostElement) {
|
|
return null;
|
|
}
|
|
return this.referenceTargetToElement(target.node);
|
|
}
|
|
}
|
|
function extractScopedNodeEntities(rootScope, templateEntities) {
|
|
const entityMap = new Map();
|
|
function extractScopeEntities(scope) {
|
|
if (entityMap.has(scope.rootNode)) {
|
|
return entityMap.get(scope.rootNode);
|
|
}
|
|
const currentEntities = scope.namedEntities;
|
|
let entities;
|
|
if (scope.parentScope !== null) {
|
|
entities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]);
|
|
} else {
|
|
entities = new Map(currentEntities);
|
|
}
|
|
entityMap.set(scope.rootNode, entities);
|
|
return entities;
|
|
}
|
|
const scopesToProcess = [rootScope];
|
|
while (scopesToProcess.length > 0) {
|
|
const scope = scopesToProcess.pop();
|
|
for (const childScope of scope.childScopes.values()) {
|
|
scopesToProcess.push(childScope);
|
|
}
|
|
extractScopeEntities(scope);
|
|
}
|
|
for (const [template, entities] of entityMap) {
|
|
templateEntities.set(template, new Set(entities.values()));
|
|
}
|
|
}
|
|
|
|
class ResourceLoader {}
|
|
|
|
class CompilerFacadeImpl {
|
|
jitEvaluator;
|
|
FactoryTarget = FactoryTarget;
|
|
ResourceLoader = ResourceLoader;
|
|
elementSchemaRegistry = new DomElementSchemaRegistry();
|
|
constructor(jitEvaluator = new JitEvaluator()) {
|
|
this.jitEvaluator = jitEvaluator;
|
|
}
|
|
compilePipe(angularCoreEnv, sourceMapUrl, facade) {
|
|
const metadata = {
|
|
name: facade.name,
|
|
type: wrapReference(facade.type),
|
|
typeArgumentCount: 0,
|
|
deps: null,
|
|
pipeName: facade.pipeName,
|
|
pure: facade.pure,
|
|
isStandalone: facade.isStandalone
|
|
};
|
|
const res = compilePipeFromMetadata(metadata);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compilePipeDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
|
const meta = convertDeclarePipeFacadeToMetadata(declaration);
|
|
const res = compilePipeFromMetadata(meta);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
|
|
const {
|
|
expression,
|
|
statements
|
|
} = compileInjectable({
|
|
name: facade.name,
|
|
type: wrapReference(facade.type),
|
|
typeArgumentCount: facade.typeArgumentCount,
|
|
providedIn: computeProvidedIn(facade.providedIn),
|
|
useClass: convertToProviderExpression(facade, 'useClass'),
|
|
useFactory: wrapExpression(facade, 'useFactory'),
|
|
useValue: convertToProviderExpression(facade, 'useValue'),
|
|
useExisting: convertToProviderExpression(facade, 'useExisting'),
|
|
deps: facade.deps?.map(convertR3DependencyMetadata)
|
|
}, true);
|
|
return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
|
|
}
|
|
compileInjectableDeclaration(angularCoreEnv, sourceMapUrl, facade) {
|
|
const {
|
|
expression,
|
|
statements
|
|
} = compileInjectable({
|
|
name: facade.type.name,
|
|
type: wrapReference(facade.type),
|
|
typeArgumentCount: 0,
|
|
providedIn: computeProvidedIn(facade.providedIn),
|
|
useClass: convertToProviderExpression(facade, 'useClass'),
|
|
useFactory: wrapExpression(facade, 'useFactory'),
|
|
useValue: convertToProviderExpression(facade, 'useValue'),
|
|
useExisting: convertToProviderExpression(facade, 'useExisting'),
|
|
deps: facade.deps?.map(convertR3DeclareDependencyMetadata)
|
|
}, true);
|
|
return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
|
|
}
|
|
compileInjector(angularCoreEnv, sourceMapUrl, facade) {
|
|
const meta = {
|
|
name: facade.name,
|
|
type: wrapReference(facade.type),
|
|
providers: facade.providers && facade.providers.length > 0 ? new WrappedNodeExpr(facade.providers) : null,
|
|
imports: facade.imports.map(i => new WrappedNodeExpr(i))
|
|
};
|
|
const res = compileInjector(meta);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compileInjectorDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
|
const meta = convertDeclareInjectorFacadeToMetadata(declaration);
|
|
const res = compileInjector(meta);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
|
|
const meta = {
|
|
kind: R3NgModuleMetadataKind.Global,
|
|
type: wrapReference(facade.type),
|
|
bootstrap: facade.bootstrap.map(wrapReference),
|
|
declarations: facade.declarations.map(wrapReference),
|
|
publicDeclarationTypes: null,
|
|
imports: facade.imports.map(wrapReference),
|
|
includeImportTypes: true,
|
|
exports: facade.exports.map(wrapReference),
|
|
selectorScopeMode: R3SelectorScopeMode.Inline,
|
|
containsForwardDecls: false,
|
|
schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
|
|
id: facade.id ? new WrappedNodeExpr(facade.id) : null
|
|
};
|
|
const res = compileNgModule(meta);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compileNgModuleDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
|
const expression = compileNgModuleDeclarationExpression(declaration);
|
|
return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, []);
|
|
}
|
|
compileDirective(angularCoreEnv, sourceMapUrl, facade) {
|
|
const meta = convertDirectiveFacadeToMetadata(facade);
|
|
return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
|
}
|
|
compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
|
const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
|
|
const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
|
|
return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
|
}
|
|
compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
|
const constantPool = new ConstantPool();
|
|
const bindingParser = makeBindingParser();
|
|
const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
|
}
|
|
compileComponent(angularCoreEnv, sourceMapUrl, facade) {
|
|
const {
|
|
template,
|
|
defer
|
|
} = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, undefined);
|
|
const meta = {
|
|
...facade,
|
|
...convertDirectiveFacadeToMetadata(facade),
|
|
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
|
template,
|
|
declarations: facade.declarations.map(convertDeclarationFacadeToMetadata),
|
|
declarationListEmitMode: 0,
|
|
defer,
|
|
styles: [...facade.styles, ...template.styles],
|
|
encapsulation: facade.encapsulation,
|
|
changeDetection: facade.changeDetection ?? null,
|
|
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
|
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) : null,
|
|
relativeContextFilePath: '',
|
|
i18nUseExternalIds: true,
|
|
relativeTemplatePath: null
|
|
};
|
|
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
|
return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
|
|
}
|
|
compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
|
const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
|
|
const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
|
|
return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
|
}
|
|
compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
|
const constantPool = new ConstantPool();
|
|
const bindingParser = makeBindingParser();
|
|
const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
|
}
|
|
compileFactory(angularCoreEnv, sourceMapUrl, meta) {
|
|
const factoryRes = compileFactoryFunction({
|
|
name: meta.name,
|
|
type: wrapReference(meta.type),
|
|
typeArgumentCount: meta.typeArgumentCount,
|
|
deps: convertR3DependencyMetadataArray(meta.deps),
|
|
target: meta.target
|
|
});
|
|
return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
|
}
|
|
compileFactoryDeclaration(angularCoreEnv, sourceMapUrl, meta) {
|
|
const factoryRes = compileFactoryFunction({
|
|
name: meta.type.name,
|
|
type: wrapReference(meta.type),
|
|
typeArgumentCount: 0,
|
|
deps: Array.isArray(meta.deps) ? meta.deps.map(convertR3DeclareDependencyMetadata) : meta.deps,
|
|
target: meta.target
|
|
});
|
|
return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
|
}
|
|
createParseSourceSpan(kind, typeName, sourceUrl) {
|
|
return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
|
|
}
|
|
jitExpression(def, context, sourceUrl, preStatements) {
|
|
const statements = [...preStatements, new DeclareVarStmt('$def', def, undefined, StmtModifier.Exported)];
|
|
const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), true);
|
|
return res['$def'];
|
|
}
|
|
}
|
|
function convertToR3QueryMetadata(facade) {
|
|
return {
|
|
...facade,
|
|
isSignal: facade.isSignal,
|
|
predicate: convertQueryPredicate(facade.predicate),
|
|
read: facade.read ? new WrappedNodeExpr(facade.read) : null,
|
|
static: facade.static,
|
|
emitDistinctChangesOnly: facade.emitDistinctChangesOnly
|
|
};
|
|
}
|
|
function convertQueryDeclarationToMetadata(declaration) {
|
|
return {
|
|
propertyName: declaration.propertyName,
|
|
first: declaration.first ?? false,
|
|
predicate: convertQueryPredicate(declaration.predicate),
|
|
descendants: declaration.descendants ?? false,
|
|
read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
|
static: declaration.static ?? false,
|
|
emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true,
|
|
isSignal: !!declaration.isSignal
|
|
};
|
|
}
|
|
function convertQueryPredicate(predicate) {
|
|
return Array.isArray(predicate) ? predicate : createMayBeForwardRefExpression(new WrappedNodeExpr(predicate), 1);
|
|
}
|
|
function convertDirectiveFacadeToMetadata(facade) {
|
|
const inputsFromMetadata = parseInputsArray(facade.inputs || []);
|
|
const outputsFromMetadata = parseMappingStringArray(facade.outputs || []);
|
|
const propMetadata = facade.propMetadata;
|
|
const inputsFromType = {};
|
|
const outputsFromType = {};
|
|
for (const field in propMetadata) {
|
|
if (propMetadata.hasOwnProperty(field)) {
|
|
propMetadata[field].forEach(ann => {
|
|
if (isInput(ann)) {
|
|
inputsFromType[field] = {
|
|
bindingPropertyName: ann.alias || field,
|
|
classPropertyName: field,
|
|
required: ann.required || false,
|
|
isSignal: !!ann.isSignal,
|
|
transformFunction: ann.transform != null ? new WrappedNodeExpr(ann.transform) : null
|
|
};
|
|
} else if (isOutput(ann)) {
|
|
outputsFromType[field] = ann.alias || field;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
const hostDirectives = facade.hostDirectives?.length ? facade.hostDirectives.map(hostDirective => {
|
|
return typeof hostDirective === 'function' ? {
|
|
directive: wrapReference(hostDirective),
|
|
inputs: null,
|
|
outputs: null,
|
|
isForwardReference: false
|
|
} : {
|
|
directive: wrapReference(hostDirective.directive),
|
|
isForwardReference: false,
|
|
inputs: hostDirective.inputs ? parseMappingStringArray(hostDirective.inputs) : null,
|
|
outputs: hostDirective.outputs ? parseMappingStringArray(hostDirective.outputs) : null
|
|
};
|
|
}) : null;
|
|
return {
|
|
...facade,
|
|
typeArgumentCount: 0,
|
|
typeSourceSpan: facade.typeSourceSpan,
|
|
type: wrapReference(facade.type),
|
|
deps: null,
|
|
host: {
|
|
...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host)
|
|
},
|
|
inputs: {
|
|
...inputsFromMetadata,
|
|
...inputsFromType
|
|
},
|
|
outputs: {
|
|
...outputsFromMetadata,
|
|
...outputsFromType
|
|
},
|
|
queries: facade.queries.map(convertToR3QueryMetadata),
|
|
providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null,
|
|
viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
|
|
hostDirectives
|
|
};
|
|
}
|
|
function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
|
|
const hostDirectives = declaration.hostDirectives?.length ? declaration.hostDirectives.map(dir => ({
|
|
directive: wrapReference(dir.directive),
|
|
isForwardReference: false,
|
|
inputs: dir.inputs ? getHostDirectiveBindingMapping(dir.inputs) : null,
|
|
outputs: dir.outputs ? getHostDirectiveBindingMapping(dir.outputs) : null
|
|
})) : null;
|
|
return {
|
|
name: declaration.type.name,
|
|
type: wrapReference(declaration.type),
|
|
typeSourceSpan,
|
|
selector: declaration.selector ?? null,
|
|
inputs: declaration.inputs ? inputsPartialMetadataToInputMetadata(declaration.inputs) : {},
|
|
outputs: declaration.outputs ?? {},
|
|
host: convertHostDeclarationToMetadata(declaration.host),
|
|
queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata),
|
|
viewQueries: (declaration.viewQueries ?? []).map(convertQueryDeclarationToMetadata),
|
|
providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) : null,
|
|
exportAs: declaration.exportAs ?? null,
|
|
usesInheritance: declaration.usesInheritance ?? false,
|
|
lifecycle: {
|
|
usesOnChanges: declaration.usesOnChanges ?? false
|
|
},
|
|
deps: null,
|
|
typeArgumentCount: 0,
|
|
isStandalone: declaration.isStandalone ?? getJitStandaloneDefaultForVersion(declaration.version),
|
|
isSignal: declaration.isSignal ?? false,
|
|
hostDirectives
|
|
};
|
|
}
|
|
function convertHostDeclarationToMetadata(host = {}) {
|
|
return {
|
|
attributes: convertOpaqueValuesToExpressions(host.attributes ?? {}),
|
|
listeners: host.listeners ?? {},
|
|
properties: host.properties ?? {},
|
|
specialAttributes: {
|
|
classAttr: host.classAttribute,
|
|
styleAttr: host.styleAttribute
|
|
}
|
|
};
|
|
}
|
|
function getHostDirectiveBindingMapping(array) {
|
|
let result = null;
|
|
for (let i = 1; i < array.length; i += 2) {
|
|
result = result || {};
|
|
result[array[i - 1]] = array[i];
|
|
}
|
|
return result;
|
|
}
|
|
function convertOpaqueValuesToExpressions(obj) {
|
|
const result = {};
|
|
for (const key of Object.keys(obj)) {
|
|
result[key] = new WrappedNodeExpr(obj[key]);
|
|
}
|
|
return result;
|
|
}
|
|
function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMapUrl) {
|
|
const {
|
|
template,
|
|
defer
|
|
} = parseJitTemplate(decl.template, decl.type.name, sourceMapUrl, decl.preserveWhitespaces ?? false, decl.deferBlockDependencies);
|
|
const declarations = [];
|
|
if (decl.dependencies) {
|
|
for (const innerDep of decl.dependencies) {
|
|
switch (innerDep.kind) {
|
|
case 'directive':
|
|
case 'component':
|
|
declarations.push(convertDirectiveDeclarationToMetadata(innerDep));
|
|
break;
|
|
case 'pipe':
|
|
declarations.push(convertPipeDeclarationToMetadata(innerDep));
|
|
break;
|
|
}
|
|
}
|
|
} else if (decl.components || decl.directives || decl.pipes) {
|
|
decl.components && declarations.push(...decl.components.map(dir => convertDirectiveDeclarationToMetadata(dir, true)));
|
|
decl.directives && declarations.push(...decl.directives.map(dir => convertDirectiveDeclarationToMetadata(dir)));
|
|
decl.pipes && declarations.push(...convertPipeMapToMetadata(decl.pipes));
|
|
}
|
|
const hasDirectiveDependencies = declarations.some(({
|
|
kind
|
|
}) => kind === R3TemplateDependencyKind.Directive || kind === R3TemplateDependencyKind.NgModule);
|
|
return {
|
|
...convertDeclareDirectiveFacadeToMetadata(decl, typeSourceSpan),
|
|
template,
|
|
styles: decl.styles ?? [],
|
|
declarations,
|
|
viewProviders: decl.viewProviders !== undefined ? new WrappedNodeExpr(decl.viewProviders) : null,
|
|
animations: decl.animations !== undefined ? new WrappedNodeExpr(decl.animations) : null,
|
|
defer,
|
|
changeDetection: decl.changeDetection ?? ChangeDetectionStrategy.Default,
|
|
encapsulation: decl.encapsulation ?? ViewEncapsulation$1.Emulated,
|
|
declarationListEmitMode: 2,
|
|
relativeContextFilePath: '',
|
|
i18nUseExternalIds: true,
|
|
relativeTemplatePath: null,
|
|
hasDirectiveDependencies
|
|
};
|
|
}
|
|
function convertDeclarationFacadeToMetadata(declaration) {
|
|
return {
|
|
...declaration,
|
|
type: new WrappedNodeExpr(declaration.type)
|
|
};
|
|
}
|
|
function convertDirectiveDeclarationToMetadata(declaration, isComponent = null) {
|
|
return {
|
|
kind: R3TemplateDependencyKind.Directive,
|
|
isComponent: isComponent || declaration.kind === 'component',
|
|
selector: declaration.selector,
|
|
type: new WrappedNodeExpr(declaration.type),
|
|
inputs: declaration.inputs ?? [],
|
|
outputs: declaration.outputs ?? [],
|
|
exportAs: declaration.exportAs ?? null
|
|
};
|
|
}
|
|
function convertPipeMapToMetadata(pipes) {
|
|
if (!pipes) {
|
|
return [];
|
|
}
|
|
return Object.keys(pipes).map(name => {
|
|
return {
|
|
kind: R3TemplateDependencyKind.Pipe,
|
|
name,
|
|
type: new WrappedNodeExpr(pipes[name])
|
|
};
|
|
});
|
|
}
|
|
function convertPipeDeclarationToMetadata(pipe) {
|
|
return {
|
|
kind: R3TemplateDependencyKind.Pipe,
|
|
name: pipe.name,
|
|
type: new WrappedNodeExpr(pipe.type)
|
|
};
|
|
}
|
|
function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, deferBlockDependencies) {
|
|
const parsed = parseTemplate(template, sourceMapUrl, {
|
|
preserveWhitespaces
|
|
});
|
|
if (parsed.errors !== null) {
|
|
const errors = parsed.errors.map(err => err.toString()).join(', ');
|
|
throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
|
|
}
|
|
const binder = new R3TargetBinder(null);
|
|
const boundTarget = binder.bind({
|
|
template: parsed.nodes
|
|
});
|
|
return {
|
|
template: parsed,
|
|
defer: createR3ComponentDeferMetadata(boundTarget, deferBlockDependencies)
|
|
};
|
|
}
|
|
function convertToProviderExpression(obj, property) {
|
|
if (obj.hasOwnProperty(property)) {
|
|
return createMayBeForwardRefExpression(new WrappedNodeExpr(obj[property]), 0);
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
function wrapExpression(obj, property) {
|
|
if (obj.hasOwnProperty(property)) {
|
|
return new WrappedNodeExpr(obj[property]);
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
function computeProvidedIn(providedIn) {
|
|
const expression = typeof providedIn === 'function' ? new WrappedNodeExpr(providedIn) : new LiteralExpr(providedIn ?? null);
|
|
return createMayBeForwardRefExpression(expression, 0);
|
|
}
|
|
function convertR3DependencyMetadataArray(facades) {
|
|
return facades == null ? null : facades.map(convertR3DependencyMetadata);
|
|
}
|
|
function convertR3DependencyMetadata(facade) {
|
|
const isAttributeDep = facade.attribute != null;
|
|
const rawToken = facade.token === null ? null : new WrappedNodeExpr(facade.token);
|
|
const token = isAttributeDep ? new WrappedNodeExpr(facade.attribute) : rawToken;
|
|
return createR3DependencyMetadata(token, isAttributeDep, facade.host, facade.optional, facade.self, facade.skipSelf);
|
|
}
|
|
function convertR3DeclareDependencyMetadata(facade) {
|
|
const isAttributeDep = facade.attribute ?? false;
|
|
const token = facade.token === null ? null : new WrappedNodeExpr(facade.token);
|
|
return createR3DependencyMetadata(token, isAttributeDep, facade.host ?? false, facade.optional ?? false, facade.self ?? false, facade.skipSelf ?? false);
|
|
}
|
|
function createR3DependencyMetadata(token, isAttributeDep, host, optional, self, skipSelf) {
|
|
const attributeNameType = isAttributeDep ? literal('unknown') : null;
|
|
return {
|
|
token,
|
|
attributeNameType,
|
|
host,
|
|
optional,
|
|
self,
|
|
skipSelf
|
|
};
|
|
}
|
|
function createR3ComponentDeferMetadata(boundTarget, deferBlockDependencies) {
|
|
const deferredBlocks = boundTarget.getDeferBlocks();
|
|
const blocks = new Map();
|
|
for (let i = 0; i < deferredBlocks.length; i++) {
|
|
const dependencyFn = deferBlockDependencies?.[i];
|
|
blocks.set(deferredBlocks[i], dependencyFn ? new WrappedNodeExpr(dependencyFn) : null);
|
|
}
|
|
return {
|
|
mode: 0,
|
|
blocks
|
|
};
|
|
}
|
|
function extractHostBindings(propMetadata, sourceSpan, host) {
|
|
const bindings = parseHostBindings(host || {});
|
|
const errors = verifyHostBindings(bindings, sourceSpan);
|
|
if (errors.length) {
|
|
throw new Error(errors.map(error => error.msg).join('\n'));
|
|
}
|
|
for (const field in propMetadata) {
|
|
if (propMetadata.hasOwnProperty(field)) {
|
|
propMetadata[field].forEach(ann => {
|
|
if (isHostBinding(ann)) {
|
|
bindings.properties[ann.hostPropertyName || field] = getSafePropertyAccessString('this', field);
|
|
} else if (isHostListener(ann)) {
|
|
bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return bindings;
|
|
}
|
|
function isHostBinding(value) {
|
|
return value.ngMetadataName === 'HostBinding';
|
|
}
|
|
function isHostListener(value) {
|
|
return value.ngMetadataName === 'HostListener';
|
|
}
|
|
function isInput(value) {
|
|
return value.ngMetadataName === 'Input';
|
|
}
|
|
function isOutput(value) {
|
|
return value.ngMetadataName === 'Output';
|
|
}
|
|
function inputsPartialMetadataToInputMetadata(inputs) {
|
|
return Object.keys(inputs).reduce((result, minifiedClassName) => {
|
|
const value = inputs[minifiedClassName];
|
|
if (typeof value === 'string' || Array.isArray(value)) {
|
|
result[minifiedClassName] = parseLegacyInputPartialOutput(value);
|
|
} else {
|
|
result[minifiedClassName] = {
|
|
bindingPropertyName: value.publicName,
|
|
classPropertyName: minifiedClassName,
|
|
transformFunction: value.transformFunction !== null ? new WrappedNodeExpr(value.transformFunction) : null,
|
|
required: value.isRequired,
|
|
isSignal: value.isSignal
|
|
};
|
|
}
|
|
return result;
|
|
}, {});
|
|
}
|
|
function parseLegacyInputPartialOutput(value) {
|
|
if (typeof value === 'string') {
|
|
return {
|
|
bindingPropertyName: value,
|
|
classPropertyName: value,
|
|
transformFunction: null,
|
|
required: false,
|
|
isSignal: false
|
|
};
|
|
}
|
|
return {
|
|
bindingPropertyName: value[0],
|
|
classPropertyName: value[1],
|
|
transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
|
|
required: false,
|
|
isSignal: false
|
|
};
|
|
}
|
|
function parseInputsArray(values) {
|
|
return values.reduce((results, value) => {
|
|
if (typeof value === 'string') {
|
|
const [bindingPropertyName, classPropertyName] = parseMappingString(value);
|
|
results[classPropertyName] = {
|
|
bindingPropertyName,
|
|
classPropertyName,
|
|
required: false,
|
|
isSignal: false,
|
|
transformFunction: null
|
|
};
|
|
} else {
|
|
results[value.name] = {
|
|
bindingPropertyName: value.alias || value.name,
|
|
classPropertyName: value.name,
|
|
required: value.required || false,
|
|
isSignal: false,
|
|
transformFunction: value.transform != null ? new WrappedNodeExpr(value.transform) : null
|
|
};
|
|
}
|
|
return results;
|
|
}, {});
|
|
}
|
|
function parseMappingStringArray(values) {
|
|
return values.reduce((results, value) => {
|
|
const [alias, fieldName] = parseMappingString(value);
|
|
results[fieldName] = alias;
|
|
return results;
|
|
}, {});
|
|
}
|
|
function parseMappingString(value) {
|
|
const [fieldName, bindingPropertyName] = value.split(':', 2).map(str => str.trim());
|
|
return [bindingPropertyName ?? fieldName, fieldName];
|
|
}
|
|
function convertDeclarePipeFacadeToMetadata(declaration) {
|
|
return {
|
|
name: declaration.type.name,
|
|
type: wrapReference(declaration.type),
|
|
typeArgumentCount: 0,
|
|
pipeName: declaration.name,
|
|
deps: null,
|
|
pure: declaration.pure ?? true,
|
|
isStandalone: declaration.isStandalone ?? getJitStandaloneDefaultForVersion(declaration.version)
|
|
};
|
|
}
|
|
function convertDeclareInjectorFacadeToMetadata(declaration) {
|
|
return {
|
|
name: declaration.type.name,
|
|
type: wrapReference(declaration.type),
|
|
providers: declaration.providers !== undefined && declaration.providers.length > 0 ? new WrappedNodeExpr(declaration.providers) : null,
|
|
imports: declaration.imports !== undefined ? declaration.imports.map(i => new WrappedNodeExpr(i)) : []
|
|
};
|
|
}
|
|
function publishFacade(global) {
|
|
const ng = global.ng || (global.ng = {});
|
|
ng.ɵcompilerFacade = new CompilerFacadeImpl();
|
|
}
|
|
|
|
class CompilerConfig {
|
|
defaultEncapsulation;
|
|
preserveWhitespaces;
|
|
strictInjectionParameters;
|
|
constructor({
|
|
defaultEncapsulation = ViewEncapsulation$1.Emulated,
|
|
preserveWhitespaces,
|
|
strictInjectionParameters
|
|
} = {}) {
|
|
this.defaultEncapsulation = defaultEncapsulation;
|
|
this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
|
|
this.strictInjectionParameters = strictInjectionParameters === true;
|
|
}
|
|
}
|
|
function preserveWhitespacesDefault(preserveWhitespacesOption, defaultSetting = false) {
|
|
return preserveWhitespacesOption === null ? defaultSetting : preserveWhitespacesOption;
|
|
}
|
|
|
|
const _I18N_ATTR = 'i18n';
|
|
const _I18N_ATTR_PREFIX = 'i18n-';
|
|
const _I18N_COMMENT_PREFIX_REGEXP = /^i18n:?/;
|
|
const MEANING_SEPARATOR = '|';
|
|
const ID_SEPARATOR = '@@';
|
|
let i18nCommentsWarned = false;
|
|
function extractMessages(nodes, implicitTags, implicitAttrs, preserveSignificantWhitespace) {
|
|
const visitor = new _Visitor(implicitTags, implicitAttrs, preserveSignificantWhitespace);
|
|
return visitor.extract(nodes);
|
|
}
|
|
function mergeTranslations(nodes, translations, implicitTags, implicitAttrs) {
|
|
const visitor = new _Visitor(implicitTags, implicitAttrs);
|
|
return visitor.merge(nodes, translations);
|
|
}
|
|
class ExtractionResult {
|
|
messages;
|
|
errors;
|
|
constructor(messages, errors) {
|
|
this.messages = messages;
|
|
this.errors = errors;
|
|
}
|
|
}
|
|
var _VisitorMode;
|
|
(function (_VisitorMode) {
|
|
_VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
|
|
_VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
|
|
})(_VisitorMode || (_VisitorMode = {}));
|
|
class _Visitor {
|
|
_implicitTags;
|
|
_implicitAttrs;
|
|
_preserveSignificantWhitespace;
|
|
_depth;
|
|
_inI18nNode;
|
|
_inImplicitNode;
|
|
_inI18nBlock;
|
|
_blockMeaningAndDesc;
|
|
_blockChildren;
|
|
_blockStartDepth;
|
|
_inIcu;
|
|
_msgCountAtSectionStart;
|
|
_errors;
|
|
_mode;
|
|
_messages;
|
|
_translations;
|
|
_createI18nMessage;
|
|
constructor(_implicitTags, _implicitAttrs, _preserveSignificantWhitespace = true) {
|
|
this._implicitTags = _implicitTags;
|
|
this._implicitAttrs = _implicitAttrs;
|
|
this._preserveSignificantWhitespace = _preserveSignificantWhitespace;
|
|
}
|
|
extract(nodes) {
|
|
this._init(_VisitorMode.Extract);
|
|
nodes.forEach(node => node.visit(this, null));
|
|
if (this._inI18nBlock) {
|
|
this._reportError(nodes[nodes.length - 1], 'Unclosed block');
|
|
}
|
|
return new ExtractionResult(this._messages, this._errors);
|
|
}
|
|
merge(nodes, translations) {
|
|
this._init(_VisitorMode.Merge);
|
|
this._translations = translations;
|
|
const wrapper = new Element('wrapper', [], [], nodes, false, undefined, undefined, undefined, false);
|
|
const translatedNode = wrapper.visit(this, null);
|
|
if (this._inI18nBlock) {
|
|
this._reportError(nodes[nodes.length - 1], 'Unclosed block');
|
|
}
|
|
return new ParseTreeResult(translatedNode.children, this._errors);
|
|
}
|
|
visitExpansionCase(icuCase, context) {
|
|
const expression = visitAll(this, icuCase.expression, context);
|
|
if (this._mode === _VisitorMode.Merge) {
|
|
return new ExpansionCase(icuCase.value, expression, icuCase.sourceSpan, icuCase.valueSourceSpan, icuCase.expSourceSpan);
|
|
}
|
|
}
|
|
visitExpansion(icu, context) {
|
|
this._mayBeAddBlockChildren(icu);
|
|
const wasInIcu = this._inIcu;
|
|
if (!this._inIcu) {
|
|
if (this._isInTranslatableSection) {
|
|
this._addMessage([icu]);
|
|
}
|
|
this._inIcu = true;
|
|
}
|
|
const cases = visitAll(this, icu.cases, context);
|
|
if (this._mode === _VisitorMode.Merge) {
|
|
icu = new Expansion(icu.switchValue, icu.type, cases, icu.sourceSpan, icu.switchValueSourceSpan);
|
|
}
|
|
this._inIcu = wasInIcu;
|
|
return icu;
|
|
}
|
|
visitComment(comment, context) {
|
|
const isOpening = _isOpeningComment(comment);
|
|
if (isOpening && this._isInTranslatableSection) {
|
|
this._reportError(comment, 'Could not start a block inside a translatable section');
|
|
return;
|
|
}
|
|
const isClosing = _isClosingComment(comment);
|
|
if (isClosing && !this._inI18nBlock) {
|
|
this._reportError(comment, 'Trying to close an unopened block');
|
|
return;
|
|
}
|
|
if (!this._inI18nNode && !this._inIcu) {
|
|
if (!this._inI18nBlock) {
|
|
if (isOpening) {
|
|
if (!i18nCommentsWarned && console && console.warn) {
|
|
i18nCommentsWarned = true;
|
|
const details = comment.sourceSpan.details ? `, ${comment.sourceSpan.details}` : '';
|
|
console.warn(`I18n comments are deprecated, use an <ng-container> element instead (${comment.sourceSpan.start}${details})`);
|
|
}
|
|
this._inI18nBlock = true;
|
|
this._blockStartDepth = this._depth;
|
|
this._blockChildren = [];
|
|
this._blockMeaningAndDesc = comment.value.replace(_I18N_COMMENT_PREFIX_REGEXP, '').trim();
|
|
this._openTranslatableSection(comment);
|
|
}
|
|
} else {
|
|
if (isClosing) {
|
|
if (this._depth == this._blockStartDepth) {
|
|
this._closeTranslatableSection(comment, this._blockChildren);
|
|
this._inI18nBlock = false;
|
|
const message = this._addMessage(this._blockChildren, this._blockMeaningAndDesc);
|
|
const nodes = this._translateMessage(comment, message);
|
|
return visitAll(this, nodes);
|
|
} else {
|
|
this._reportError(comment, 'I18N blocks should not cross element boundaries');
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
visitText(text, context) {
|
|
if (this._isInTranslatableSection) {
|
|
this._mayBeAddBlockChildren(text);
|
|
}
|
|
return text;
|
|
}
|
|
visitElement(el, context) {
|
|
return this._visitElementLike(el, context);
|
|
}
|
|
visitAttribute(attribute, context) {
|
|
throw new Error('unreachable code');
|
|
}
|
|
visitBlock(block, context) {
|
|
visitAll(this, block.children, context);
|
|
}
|
|
visitBlockParameter(parameter, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
return this._visitElementLike(component, context);
|
|
}
|
|
visitDirective(directive, context) {
|
|
throw new Error('unreachable code');
|
|
}
|
|
_init(mode) {
|
|
this._mode = mode;
|
|
this._inI18nBlock = false;
|
|
this._inI18nNode = false;
|
|
this._depth = 0;
|
|
this._inIcu = false;
|
|
this._msgCountAtSectionStart = undefined;
|
|
this._errors = [];
|
|
this._messages = [];
|
|
this._inImplicitNode = false;
|
|
this._createI18nMessage = createI18nMessageFactory(!this._preserveSignificantWhitespace, this._preserveSignificantWhitespace);
|
|
}
|
|
_visitElementLike(node, context) {
|
|
this._mayBeAddBlockChildren(node);
|
|
this._depth++;
|
|
const wasInI18nNode = this._inI18nNode;
|
|
const wasInImplicitNode = this._inImplicitNode;
|
|
let childNodes = [];
|
|
let translatedChildNodes = undefined;
|
|
const nodeName = node instanceof Component ? node.tagName : node.name;
|
|
const i18nAttr = _getI18nAttr(node);
|
|
const i18nMeta = i18nAttr ? i18nAttr.value : '';
|
|
const isImplicit = this._implicitTags.some(tag => nodeName === tag) && !this._inIcu && !this._isInTranslatableSection;
|
|
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
|
this._inImplicitNode = wasInImplicitNode || isImplicit;
|
|
if (!this._isInTranslatableSection && !this._inIcu) {
|
|
if (i18nAttr || isTopLevelImplicit) {
|
|
this._inI18nNode = true;
|
|
const message = this._addMessage(node.children, i18nMeta);
|
|
translatedChildNodes = this._translateMessage(node, message);
|
|
}
|
|
if (this._mode == _VisitorMode.Extract) {
|
|
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
|
if (isTranslatable) this._openTranslatableSection(node);
|
|
visitAll(this, node.children);
|
|
if (isTranslatable) this._closeTranslatableSection(node, node.children);
|
|
}
|
|
} else {
|
|
if (i18nAttr || isTopLevelImplicit) {
|
|
this._reportError(node, 'Could not mark an element as translatable inside a translatable section');
|
|
}
|
|
if (this._mode == _VisitorMode.Extract) {
|
|
visitAll(this, node.children);
|
|
}
|
|
}
|
|
if (this._mode === _VisitorMode.Merge) {
|
|
const visitNodes = translatedChildNodes || node.children;
|
|
visitNodes.forEach(child => {
|
|
const visited = child.visit(this, context);
|
|
if (visited && !this._isInTranslatableSection) {
|
|
childNodes = childNodes.concat(visited);
|
|
}
|
|
});
|
|
}
|
|
this._visitAttributesOf(node);
|
|
this._depth--;
|
|
this._inI18nNode = wasInI18nNode;
|
|
this._inImplicitNode = wasInImplicitNode;
|
|
if (this._mode === _VisitorMode.Merge) {
|
|
if (node instanceof Element) {
|
|
return new Element(node.name, this._translateAttributes(node), this._translateDirectives(node), childNodes, node.isSelfClosing, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.isVoid);
|
|
} else {
|
|
return new Component(node.componentName, node.tagName, node.fullName, this._translateAttributes(node), this._translateDirectives(node), childNodes, node.isSelfClosing, node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
_visitAttributesOf(el) {
|
|
const explicitAttrNameToValue = {};
|
|
const implicitAttrNames = this._implicitAttrs[el instanceof Component ? el.tagName || '' : el.name] || [];
|
|
el.attrs.filter(attr => attr instanceof Attribute && attr.name.startsWith(_I18N_ATTR_PREFIX)).forEach(attr => {
|
|
explicitAttrNameToValue[attr.name.slice(_I18N_ATTR_PREFIX.length)] = attr.value;
|
|
});
|
|
el.attrs.forEach(attr => {
|
|
if (attr.name in explicitAttrNameToValue) {
|
|
this._addMessage([attr], explicitAttrNameToValue[attr.name]);
|
|
} else if (implicitAttrNames.some(name => attr.name === name)) {
|
|
this._addMessage([attr]);
|
|
}
|
|
});
|
|
}
|
|
_addMessage(ast, msgMeta) {
|
|
if (ast.length == 0 || this._isEmptyAttributeValue(ast) || this._isPlaceholderOnlyAttributeValue(ast) || this._isPlaceholderOnlyMessage(ast)) {
|
|
return null;
|
|
}
|
|
const {
|
|
meaning,
|
|
description,
|
|
id
|
|
} = _parseMessageMeta(msgMeta);
|
|
const message = this._createI18nMessage(ast, meaning, description, id);
|
|
this._messages.push(message);
|
|
return message;
|
|
}
|
|
_isEmptyAttributeValue(ast) {
|
|
if (!isAttrNode(ast)) return false;
|
|
const node = ast[0];
|
|
return node.value.trim() === '';
|
|
}
|
|
_isPlaceholderOnlyAttributeValue(ast) {
|
|
if (!isAttrNode(ast)) return false;
|
|
const tokens = ast[0].valueTokens ?? [];
|
|
const interpolations = tokens.filter(token => token.type === 17);
|
|
const plainText = tokens.filter(token => token.type === 16).map(token => token.parts[0].trim()).join('');
|
|
return interpolations.length === 1 && plainText === '';
|
|
}
|
|
_isPlaceholderOnlyMessage(ast) {
|
|
if (!isTextNode(ast)) return false;
|
|
const tokens = ast[0].tokens;
|
|
const interpolations = tokens.filter(token => token.type === 8);
|
|
const plainText = tokens.filter(token => token.type === 5).map(token => token.parts[0].trim()).join('');
|
|
return interpolations.length === 1 && plainText === '';
|
|
}
|
|
_translateMessage(el, message) {
|
|
if (message && this._mode === _VisitorMode.Merge) {
|
|
const nodes = this._translations.get(message);
|
|
if (nodes) {
|
|
return nodes;
|
|
}
|
|
this._reportError(el, `Translation unavailable for message id="${this._translations.digest(message)}"`);
|
|
}
|
|
return [];
|
|
}
|
|
_translateAttributes(node) {
|
|
const i18nParsedMessageMeta = {};
|
|
const translatedAttributes = [];
|
|
node.attrs.forEach(attr => {
|
|
if (attr.name.startsWith(_I18N_ATTR_PREFIX)) {
|
|
i18nParsedMessageMeta[attr.name.slice(_I18N_ATTR_PREFIX.length)] = _parseMessageMeta(attr.value);
|
|
}
|
|
});
|
|
node.attrs.forEach(attr => {
|
|
if (attr.name === _I18N_ATTR || attr.name.startsWith(_I18N_ATTR_PREFIX)) {
|
|
return;
|
|
}
|
|
if (attr.value && attr.value != '' && i18nParsedMessageMeta.hasOwnProperty(attr.name)) {
|
|
const {
|
|
meaning,
|
|
description,
|
|
id
|
|
} = i18nParsedMessageMeta[attr.name];
|
|
const message = this._createI18nMessage([attr], meaning, description, id);
|
|
const nodes = this._translations.get(message);
|
|
if (nodes) {
|
|
if (nodes.length == 0) {
|
|
translatedAttributes.push(new Attribute(attr.name, '', attr.sourceSpan, undefined, undefined, undefined, undefined));
|
|
} else if (nodes[0] instanceof Text) {
|
|
const value = nodes[0].value;
|
|
translatedAttributes.push(new Attribute(attr.name, value, attr.sourceSpan, undefined, undefined, undefined, undefined));
|
|
} else {
|
|
this._reportError(node, `Unexpected translation for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
|
|
}
|
|
} else {
|
|
this._reportError(node, `Translation unavailable for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
|
|
}
|
|
} else {
|
|
translatedAttributes.push(attr);
|
|
}
|
|
});
|
|
return translatedAttributes;
|
|
}
|
|
_translateDirectives(node) {
|
|
return node.directives.map(dir => new Directive(dir.name, this._translateAttributes(dir), dir.sourceSpan, dir.startSourceSpan, dir.endSourceSpan));
|
|
}
|
|
_mayBeAddBlockChildren(node) {
|
|
if (this._inI18nBlock && !this._inIcu && this._depth == this._blockStartDepth) {
|
|
this._blockChildren.push(node);
|
|
}
|
|
}
|
|
_openTranslatableSection(node) {
|
|
if (this._isInTranslatableSection) {
|
|
this._reportError(node, 'Unexpected section start');
|
|
} else {
|
|
this._msgCountAtSectionStart = this._messages.length;
|
|
}
|
|
}
|
|
get _isInTranslatableSection() {
|
|
return this._msgCountAtSectionStart !== void 0;
|
|
}
|
|
_closeTranslatableSection(node, directChildren) {
|
|
if (!this._isInTranslatableSection) {
|
|
this._reportError(node, 'Unexpected section end');
|
|
return;
|
|
}
|
|
const startIndex = this._msgCountAtSectionStart;
|
|
const significantChildren = directChildren.reduce((count, node) => count + (node instanceof Comment ? 0 : 1), 0);
|
|
if (significantChildren == 1) {
|
|
for (let i = this._messages.length - 1; i >= startIndex; i--) {
|
|
const ast = this._messages[i].nodes;
|
|
if (!(ast.length == 1 && ast[0] instanceof Text$2)) {
|
|
this._messages.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this._msgCountAtSectionStart = undefined;
|
|
}
|
|
_reportError(node, msg) {
|
|
this._errors.push(new ParseError(node.sourceSpan, msg));
|
|
}
|
|
}
|
|
function _isOpeningComment(n) {
|
|
return !!(n instanceof Comment && n.value && n.value.startsWith('i18n'));
|
|
}
|
|
function _isClosingComment(n) {
|
|
return !!(n instanceof Comment && n.value && n.value === '/i18n');
|
|
}
|
|
function _getI18nAttr(p) {
|
|
return p.attrs.find(attr => attr instanceof Attribute && attr.name === _I18N_ATTR) || null;
|
|
}
|
|
function _parseMessageMeta(i18n) {
|
|
if (!i18n) return {
|
|
meaning: '',
|
|
description: '',
|
|
id: ''
|
|
};
|
|
const idIndex = i18n.indexOf(ID_SEPARATOR);
|
|
const descIndex = i18n.indexOf(MEANING_SEPARATOR);
|
|
const [meaningAndDesc, id] = idIndex > -1 ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, ''];
|
|
const [meaning, description] = descIndex > -1 ? [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : ['', meaningAndDesc];
|
|
return {
|
|
meaning,
|
|
description,
|
|
id: id.trim()
|
|
};
|
|
}
|
|
function isTextNode(ast) {
|
|
return ast.length === 1 && ast[0] instanceof Text;
|
|
}
|
|
function isAttrNode(ast) {
|
|
return ast.length === 1 && ast[0] instanceof Attribute;
|
|
}
|
|
|
|
class XmlTagDefinition {
|
|
closedByParent = false;
|
|
implicitNamespacePrefix = null;
|
|
isVoid = false;
|
|
ignoreFirstLf = false;
|
|
canSelfClose = true;
|
|
preventNamespaceInheritance = false;
|
|
requireExtraParent(currentParent) {
|
|
return false;
|
|
}
|
|
isClosedByChild(name) {
|
|
return false;
|
|
}
|
|
getContentType() {
|
|
return TagContentType.PARSABLE_DATA;
|
|
}
|
|
}
|
|
const _TAG_DEFINITION = new XmlTagDefinition();
|
|
function getXmlTagDefinition(tagName) {
|
|
return _TAG_DEFINITION;
|
|
}
|
|
|
|
class XmlParser extends Parser$1 {
|
|
constructor() {
|
|
super(getXmlTagDefinition);
|
|
}
|
|
parse(source, url, options = {}) {
|
|
return super.parse(source, url, {
|
|
...options,
|
|
tokenizeBlocks: false,
|
|
tokenizeLet: false,
|
|
selectorlessEnabled: false
|
|
});
|
|
}
|
|
}
|
|
|
|
const _VERSION$1 = '1.2';
|
|
const _XMLNS$1 = 'urn:oasis:names:tc:xliff:document:1.2';
|
|
const _DEFAULT_SOURCE_LANG$1 = 'en';
|
|
const _PLACEHOLDER_TAG$2 = 'x';
|
|
const _MARKER_TAG$1 = 'mrk';
|
|
const _FILE_TAG = 'file';
|
|
const _SOURCE_TAG$1 = 'source';
|
|
const _SEGMENT_SOURCE_TAG = 'seg-source';
|
|
const _ALT_TRANS_TAG = 'alt-trans';
|
|
const _TARGET_TAG$1 = 'target';
|
|
const _UNIT_TAG$1 = 'trans-unit';
|
|
const _CONTEXT_GROUP_TAG = 'context-group';
|
|
const _CONTEXT_TAG = 'context';
|
|
class Xliff extends Serializer {
|
|
write(messages, locale) {
|
|
const visitor = new _WriteVisitor$1();
|
|
const transUnits = [];
|
|
messages.forEach(message => {
|
|
let contextTags = [];
|
|
message.sources.forEach(source => {
|
|
let contextGroupTag = new Tag(_CONTEXT_GROUP_TAG, {
|
|
purpose: 'location'
|
|
});
|
|
contextGroupTag.children.push(new CR(10), new Tag(_CONTEXT_TAG, {
|
|
'context-type': 'sourcefile'
|
|
}, [new Text$1(source.filePath)]), new CR(10), new Tag(_CONTEXT_TAG, {
|
|
'context-type': 'linenumber'
|
|
}, [new Text$1(`${source.startLine}`)]), new CR(8));
|
|
contextTags.push(new CR(8), contextGroupTag);
|
|
});
|
|
const transUnit = new Tag(_UNIT_TAG$1, {
|
|
id: message.id,
|
|
datatype: 'html'
|
|
});
|
|
transUnit.children.push(new CR(8), new Tag(_SOURCE_TAG$1, {}, visitor.serialize(message.nodes)), ...contextTags);
|
|
if (message.description) {
|
|
transUnit.children.push(new CR(8), new Tag('note', {
|
|
priority: '1',
|
|
from: 'description'
|
|
}, [new Text$1(message.description)]));
|
|
}
|
|
if (message.meaning) {
|
|
transUnit.children.push(new CR(8), new Tag('note', {
|
|
priority: '1',
|
|
from: 'meaning'
|
|
}, [new Text$1(message.meaning)]));
|
|
}
|
|
transUnit.children.push(new CR(6));
|
|
transUnits.push(new CR(6), transUnit);
|
|
});
|
|
const body = new Tag('body', {}, [...transUnits, new CR(4)]);
|
|
const file = new Tag('file', {
|
|
'source-language': locale || _DEFAULT_SOURCE_LANG$1,
|
|
datatype: 'plaintext',
|
|
original: 'ng2.template'
|
|
}, [new CR(4), body, new CR(2)]);
|
|
const xliff = new Tag('xliff', {
|
|
version: _VERSION$1,
|
|
xmlns: _XMLNS$1
|
|
}, [new CR(2), file, new CR()]);
|
|
return serialize$1([new Declaration({
|
|
version: '1.0',
|
|
encoding: 'UTF-8'
|
|
}), new CR(), xliff, new CR()]);
|
|
}
|
|
load(content, url) {
|
|
const xliffParser = new XliffParser();
|
|
const {
|
|
locale,
|
|
msgIdToHtml,
|
|
errors
|
|
} = xliffParser.parse(content, url);
|
|
const i18nNodesByMsgId = {};
|
|
const converter = new XmlToI18n$2();
|
|
Object.keys(msgIdToHtml).forEach(msgId => {
|
|
const {
|
|
i18nNodes,
|
|
errors: e
|
|
} = converter.convert(msgIdToHtml[msgId], url);
|
|
errors.push(...e);
|
|
i18nNodesByMsgId[msgId] = i18nNodes;
|
|
});
|
|
if (errors.length) {
|
|
throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
|
|
}
|
|
return {
|
|
locale: locale,
|
|
i18nNodesByMsgId
|
|
};
|
|
}
|
|
digest(message) {
|
|
return digest$1(message);
|
|
}
|
|
}
|
|
let _WriteVisitor$1 = class _WriteVisitor {
|
|
visitText(text, context) {
|
|
return [new Text$1(text.value)];
|
|
}
|
|
visitContainer(container, context) {
|
|
const nodes = [];
|
|
container.children.forEach(node => nodes.push(...node.visit(this)));
|
|
return nodes;
|
|
}
|
|
visitIcu(icu, context) {
|
|
const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
|
Object.keys(icu.cases).forEach(c => {
|
|
nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `));
|
|
});
|
|
nodes.push(new Text$1(`}`));
|
|
return nodes;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
const ctype = getCtypeForTag(ph.tag);
|
|
if (ph.isVoid) {
|
|
return [new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.startName,
|
|
ctype,
|
|
'equiv-text': `<${ph.tag}/>`
|
|
})];
|
|
}
|
|
const startTagPh = new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.startName,
|
|
ctype,
|
|
'equiv-text': `<${ph.tag}>`
|
|
});
|
|
const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.closeName,
|
|
ctype,
|
|
'equiv-text': `</${ph.tag}>`
|
|
});
|
|
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
return [new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.name,
|
|
'equiv-text': `{{${ph.value}}}`
|
|
})];
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
const startTagPh = new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.startName,
|
|
ctype,
|
|
'equiv-text': `@${ph.name}`
|
|
});
|
|
const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.closeName,
|
|
ctype,
|
|
'equiv-text': `}`
|
|
});
|
|
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map(value => value + ' {...}').join(' ')}}`;
|
|
return [new Tag(_PLACEHOLDER_TAG$2, {
|
|
id: ph.name,
|
|
'equiv-text': equivText
|
|
})];
|
|
}
|
|
serialize(nodes) {
|
|
return [].concat(...nodes.map(node => node.visit(this)));
|
|
}
|
|
};
|
|
class XliffParser {
|
|
_unitMlString;
|
|
_errors;
|
|
_msgIdToHtml;
|
|
_locale = null;
|
|
parse(xliff, url) {
|
|
this._unitMlString = null;
|
|
this._msgIdToHtml = {};
|
|
const xml = new XmlParser().parse(xliff, url);
|
|
this._errors = xml.errors;
|
|
visitAll(this, xml.rootNodes, null);
|
|
return {
|
|
msgIdToHtml: this._msgIdToHtml,
|
|
errors: this._errors,
|
|
locale: this._locale
|
|
};
|
|
}
|
|
visitElement(element, context) {
|
|
switch (element.name) {
|
|
case _UNIT_TAG$1:
|
|
this._unitMlString = null;
|
|
const idAttr = element.attrs.find(attr => attr.name === 'id');
|
|
if (!idAttr) {
|
|
this._addError(element, `<${_UNIT_TAG$1}> misses the "id" attribute`);
|
|
} else {
|
|
const id = idAttr.value;
|
|
if (this._msgIdToHtml.hasOwnProperty(id)) {
|
|
this._addError(element, `Duplicated translations for msg ${id}`);
|
|
} else {
|
|
visitAll(this, element.children, null);
|
|
if (typeof this._unitMlString === 'string') {
|
|
this._msgIdToHtml[id] = this._unitMlString;
|
|
} else {
|
|
this._addError(element, `Message ${id} misses a translation`);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case _SOURCE_TAG$1:
|
|
case _SEGMENT_SOURCE_TAG:
|
|
case _ALT_TRANS_TAG:
|
|
break;
|
|
case _TARGET_TAG$1:
|
|
const innerTextStart = element.startSourceSpan.end.offset;
|
|
const innerTextEnd = element.endSourceSpan.start.offset;
|
|
const content = element.startSourceSpan.start.file.content;
|
|
const innerText = content.slice(innerTextStart, innerTextEnd);
|
|
this._unitMlString = innerText;
|
|
break;
|
|
case _FILE_TAG:
|
|
const localeAttr = element.attrs.find(attr => attr.name === 'target-language');
|
|
if (localeAttr) {
|
|
this._locale = localeAttr.value;
|
|
}
|
|
visitAll(this, element.children, null);
|
|
break;
|
|
default:
|
|
visitAll(this, element.children, null);
|
|
}
|
|
}
|
|
visitAttribute(attribute, context) {}
|
|
visitText(text, context) {}
|
|
visitComment(comment, context) {}
|
|
visitExpansion(expansion, context) {}
|
|
visitExpansionCase(expansionCase, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(parameter, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {}
|
|
visitDirective(directive, context) {}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
}
|
|
let XmlToI18n$2 = class XmlToI18n {
|
|
_errors;
|
|
convert(message, url) {
|
|
const xmlIcu = new XmlParser().parse(message, url, {
|
|
tokenizeExpansionForms: true
|
|
});
|
|
this._errors = xmlIcu.errors;
|
|
const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : [].concat(...visitAll(this, xmlIcu.rootNodes));
|
|
return {
|
|
i18nNodes: i18nNodes,
|
|
errors: this._errors
|
|
};
|
|
}
|
|
visitText(text, context) {
|
|
return new Text$2(text.value, text.sourceSpan);
|
|
}
|
|
visitElement(el, context) {
|
|
if (el.name === _PLACEHOLDER_TAG$2) {
|
|
const nameAttr = el.attrs.find(attr => attr.name === 'id');
|
|
if (nameAttr) {
|
|
return new Placeholder('', nameAttr.value, el.sourceSpan);
|
|
}
|
|
this._addError(el, `<${_PLACEHOLDER_TAG$2}> misses the "id" attribute`);
|
|
return null;
|
|
}
|
|
if (el.name === _MARKER_TAG$1) {
|
|
return [].concat(...visitAll(this, el.children));
|
|
}
|
|
this._addError(el, `Unexpected tag`);
|
|
return null;
|
|
}
|
|
visitExpansion(icu, context) {
|
|
const caseMap = {};
|
|
visitAll(this, icu.cases).forEach(c => {
|
|
caseMap[c.value] = new Container(c.nodes, icu.sourceSpan);
|
|
});
|
|
return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
|
|
}
|
|
visitExpansionCase(icuCase, context) {
|
|
return {
|
|
value: icuCase.value,
|
|
nodes: visitAll(this, icuCase.expression)
|
|
};
|
|
}
|
|
visitComment(comment, context) {}
|
|
visitAttribute(attribute, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(parameter, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
this._addError(component, 'Unexpected node');
|
|
}
|
|
visitDirective(directive, context) {
|
|
this._addError(directive, 'Unexpected node');
|
|
}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
};
|
|
function getCtypeForTag(tag) {
|
|
switch (tag.toLowerCase()) {
|
|
case 'br':
|
|
return 'lb';
|
|
case 'img':
|
|
return 'image';
|
|
default:
|
|
return `x-${tag}`;
|
|
}
|
|
}
|
|
|
|
const _VERSION = '2.0';
|
|
const _XMLNS = 'urn:oasis:names:tc:xliff:document:2.0';
|
|
const _DEFAULT_SOURCE_LANG = 'en';
|
|
const _PLACEHOLDER_TAG$1 = 'ph';
|
|
const _PLACEHOLDER_SPANNING_TAG = 'pc';
|
|
const _MARKER_TAG = 'mrk';
|
|
const _XLIFF_TAG = 'xliff';
|
|
const _SOURCE_TAG = 'source';
|
|
const _TARGET_TAG = 'target';
|
|
const _UNIT_TAG = 'unit';
|
|
class Xliff2 extends Serializer {
|
|
write(messages, locale) {
|
|
const visitor = new _WriteVisitor();
|
|
const units = [];
|
|
messages.forEach(message => {
|
|
const unit = new Tag(_UNIT_TAG, {
|
|
id: message.id
|
|
});
|
|
const notes = new Tag('notes');
|
|
if (message.description || message.meaning) {
|
|
if (message.description) {
|
|
notes.children.push(new CR(8), new Tag('note', {
|
|
category: 'description'
|
|
}, [new Text$1(message.description)]));
|
|
}
|
|
if (message.meaning) {
|
|
notes.children.push(new CR(8), new Tag('note', {
|
|
category: 'meaning'
|
|
}, [new Text$1(message.meaning)]));
|
|
}
|
|
}
|
|
message.sources.forEach(source => {
|
|
notes.children.push(new CR(8), new Tag('note', {
|
|
category: 'location'
|
|
}, [new Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)]));
|
|
});
|
|
notes.children.push(new CR(6));
|
|
unit.children.push(new CR(6), notes);
|
|
const segment = new Tag('segment');
|
|
segment.children.push(new CR(8), new Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), new CR(6));
|
|
unit.children.push(new CR(6), segment, new CR(4));
|
|
units.push(new CR(4), unit);
|
|
});
|
|
const file = new Tag('file', {
|
|
'original': 'ng.template',
|
|
id: 'ngi18n'
|
|
}, [...units, new CR(2)]);
|
|
const xliff = new Tag(_XLIFF_TAG, {
|
|
version: _VERSION,
|
|
xmlns: _XMLNS,
|
|
srcLang: locale || _DEFAULT_SOURCE_LANG
|
|
}, [new CR(2), file, new CR()]);
|
|
return serialize$1([new Declaration({
|
|
version: '1.0',
|
|
encoding: 'UTF-8'
|
|
}), new CR(), xliff, new CR()]);
|
|
}
|
|
load(content, url) {
|
|
const xliff2Parser = new Xliff2Parser();
|
|
const {
|
|
locale,
|
|
msgIdToHtml,
|
|
errors
|
|
} = xliff2Parser.parse(content, url);
|
|
const i18nNodesByMsgId = {};
|
|
const converter = new XmlToI18n$1();
|
|
Object.keys(msgIdToHtml).forEach(msgId => {
|
|
const {
|
|
i18nNodes,
|
|
errors: e
|
|
} = converter.convert(msgIdToHtml[msgId], url);
|
|
errors.push(...e);
|
|
i18nNodesByMsgId[msgId] = i18nNodes;
|
|
});
|
|
if (errors.length) {
|
|
throw new Error(`xliff2 parse errors:\n${errors.join('\n')}`);
|
|
}
|
|
return {
|
|
locale: locale,
|
|
i18nNodesByMsgId
|
|
};
|
|
}
|
|
digest(message) {
|
|
return decimalDigest(message);
|
|
}
|
|
}
|
|
class _WriteVisitor {
|
|
_nextPlaceholderId = 0;
|
|
visitText(text, context) {
|
|
return [new Text$1(text.value)];
|
|
}
|
|
visitContainer(container, context) {
|
|
const nodes = [];
|
|
container.children.forEach(node => nodes.push(...node.visit(this)));
|
|
return nodes;
|
|
}
|
|
visitIcu(icu, context) {
|
|
const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
|
Object.keys(icu.cases).forEach(c => {
|
|
nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `));
|
|
});
|
|
nodes.push(new Text$1(`}`));
|
|
return nodes;
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
const type = getTypeForTag(ph.tag);
|
|
if (ph.isVoid) {
|
|
const tagPh = new Tag(_PLACEHOLDER_TAG$1, {
|
|
id: (this._nextPlaceholderId++).toString(),
|
|
equiv: ph.startName,
|
|
type: type,
|
|
disp: `<${ph.tag}/>`
|
|
});
|
|
return [tagPh];
|
|
}
|
|
const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, {
|
|
id: (this._nextPlaceholderId++).toString(),
|
|
equivStart: ph.startName,
|
|
equivEnd: ph.closeName,
|
|
type: type,
|
|
dispStart: `<${ph.tag}>`,
|
|
dispEnd: `</${ph.tag}>`
|
|
});
|
|
const nodes = [].concat(...ph.children.map(node => node.visit(this)));
|
|
if (nodes.length) {
|
|
nodes.forEach(node => tagPc.children.push(node));
|
|
} else {
|
|
tagPc.children.push(new Text$1(''));
|
|
}
|
|
return [tagPc];
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
const idStr = (this._nextPlaceholderId++).toString();
|
|
return [new Tag(_PLACEHOLDER_TAG$1, {
|
|
id: idStr,
|
|
equiv: ph.name,
|
|
disp: `{{${ph.value}}}`
|
|
})];
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, {
|
|
id: (this._nextPlaceholderId++).toString(),
|
|
equivStart: ph.startName,
|
|
equivEnd: ph.closeName,
|
|
type: 'other',
|
|
dispStart: `@${ph.name}`,
|
|
dispEnd: `}`
|
|
});
|
|
const nodes = [].concat(...ph.children.map(node => node.visit(this)));
|
|
if (nodes.length) {
|
|
nodes.forEach(node => tagPc.children.push(node));
|
|
} else {
|
|
tagPc.children.push(new Text$1(''));
|
|
}
|
|
return [tagPc];
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
const cases = Object.keys(ph.value.cases).map(value => value + ' {...}').join(' ');
|
|
const idStr = (this._nextPlaceholderId++).toString();
|
|
return [new Tag(_PLACEHOLDER_TAG$1, {
|
|
id: idStr,
|
|
equiv: ph.name,
|
|
disp: `{${ph.value.expression}, ${ph.value.type}, ${cases}}`
|
|
})];
|
|
}
|
|
serialize(nodes) {
|
|
this._nextPlaceholderId = 0;
|
|
return [].concat(...nodes.map(node => node.visit(this)));
|
|
}
|
|
}
|
|
class Xliff2Parser {
|
|
_unitMlString;
|
|
_errors;
|
|
_msgIdToHtml;
|
|
_locale = null;
|
|
parse(xliff, url) {
|
|
this._unitMlString = null;
|
|
this._msgIdToHtml = {};
|
|
const xml = new XmlParser().parse(xliff, url);
|
|
this._errors = xml.errors;
|
|
visitAll(this, xml.rootNodes, null);
|
|
return {
|
|
msgIdToHtml: this._msgIdToHtml,
|
|
errors: this._errors,
|
|
locale: this._locale
|
|
};
|
|
}
|
|
visitElement(element, context) {
|
|
switch (element.name) {
|
|
case _UNIT_TAG:
|
|
this._unitMlString = null;
|
|
const idAttr = element.attrs.find(attr => attr.name === 'id');
|
|
if (!idAttr) {
|
|
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
|
} else {
|
|
const id = idAttr.value;
|
|
if (this._msgIdToHtml.hasOwnProperty(id)) {
|
|
this._addError(element, `Duplicated translations for msg ${id}`);
|
|
} else {
|
|
visitAll(this, element.children, null);
|
|
if (typeof this._unitMlString === 'string') {
|
|
this._msgIdToHtml[id] = this._unitMlString;
|
|
} else {
|
|
this._addError(element, `Message ${id} misses a translation`);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case _SOURCE_TAG:
|
|
break;
|
|
case _TARGET_TAG:
|
|
const innerTextStart = element.startSourceSpan.end.offset;
|
|
const innerTextEnd = element.endSourceSpan.start.offset;
|
|
const content = element.startSourceSpan.start.file.content;
|
|
const innerText = content.slice(innerTextStart, innerTextEnd);
|
|
this._unitMlString = innerText;
|
|
break;
|
|
case _XLIFF_TAG:
|
|
const localeAttr = element.attrs.find(attr => attr.name === 'trgLang');
|
|
if (localeAttr) {
|
|
this._locale = localeAttr.value;
|
|
}
|
|
const versionAttr = element.attrs.find(attr => attr.name === 'version');
|
|
if (versionAttr) {
|
|
const version = versionAttr.value;
|
|
if (version !== '2.0') {
|
|
this._addError(element, `The XLIFF file version ${version} is not compatible with XLIFF 2.0 serializer`);
|
|
} else {
|
|
visitAll(this, element.children, null);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
visitAll(this, element.children, null);
|
|
}
|
|
}
|
|
visitAttribute(attribute, context) {}
|
|
visitText(text, context) {}
|
|
visitComment(comment, context) {}
|
|
visitExpansion(expansion, context) {}
|
|
visitExpansionCase(expansionCase, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(parameter, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {}
|
|
visitDirective(directive, context) {}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
}
|
|
let XmlToI18n$1 = class XmlToI18n {
|
|
_errors;
|
|
convert(message, url) {
|
|
const xmlIcu = new XmlParser().parse(message, url, {
|
|
tokenizeExpansionForms: true
|
|
});
|
|
this._errors = xmlIcu.errors;
|
|
const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : [].concat(...visitAll(this, xmlIcu.rootNodes));
|
|
return {
|
|
i18nNodes,
|
|
errors: this._errors
|
|
};
|
|
}
|
|
visitText(text, context) {
|
|
return new Text$2(text.value, text.sourceSpan);
|
|
}
|
|
visitElement(el, context) {
|
|
switch (el.name) {
|
|
case _PLACEHOLDER_TAG$1:
|
|
const nameAttr = el.attrs.find(attr => attr.name === 'equiv');
|
|
if (nameAttr) {
|
|
return [new Placeholder('', nameAttr.value, el.sourceSpan)];
|
|
}
|
|
this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equiv" attribute`);
|
|
break;
|
|
case _PLACEHOLDER_SPANNING_TAG:
|
|
const startAttr = el.attrs.find(attr => attr.name === 'equivStart');
|
|
const endAttr = el.attrs.find(attr => attr.name === 'equivEnd');
|
|
if (!startAttr) {
|
|
this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equivStart" attribute`);
|
|
} else if (!endAttr) {
|
|
this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equivEnd" attribute`);
|
|
} else {
|
|
const startId = startAttr.value;
|
|
const endId = endAttr.value;
|
|
const nodes = [];
|
|
return nodes.concat(new Placeholder('', startId, el.sourceSpan), ...el.children.map(node => node.visit(this, null)), new Placeholder('', endId, el.sourceSpan));
|
|
}
|
|
break;
|
|
case _MARKER_TAG:
|
|
return [].concat(...visitAll(this, el.children));
|
|
default:
|
|
this._addError(el, `Unexpected tag`);
|
|
}
|
|
return null;
|
|
}
|
|
visitExpansion(icu, context) {
|
|
const caseMap = {};
|
|
visitAll(this, icu.cases).forEach(c => {
|
|
caseMap[c.value] = new Container(c.nodes, icu.sourceSpan);
|
|
});
|
|
return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
|
|
}
|
|
visitExpansionCase(icuCase, context) {
|
|
return {
|
|
value: icuCase.value,
|
|
nodes: [].concat(...visitAll(this, icuCase.expression))
|
|
};
|
|
}
|
|
visitComment(comment, context) {}
|
|
visitAttribute(attribute, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(parameter, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
this._addError(component, 'Unexpected node');
|
|
}
|
|
visitDirective(directive, context) {
|
|
this._addError(directive, 'Unexpected node');
|
|
}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
};
|
|
function getTypeForTag(tag) {
|
|
switch (tag.toLowerCase()) {
|
|
case 'br':
|
|
case 'b':
|
|
case 'i':
|
|
case 'u':
|
|
return 'fmt';
|
|
case 'img':
|
|
return 'image';
|
|
case 'a':
|
|
return 'link';
|
|
default:
|
|
return 'other';
|
|
}
|
|
}
|
|
|
|
const _TRANSLATIONS_TAG = 'translationbundle';
|
|
const _TRANSLATION_TAG = 'translation';
|
|
const _PLACEHOLDER_TAG = 'ph';
|
|
class Xtb extends Serializer {
|
|
write(messages, locale) {
|
|
throw new Error('Unsupported');
|
|
}
|
|
load(content, url) {
|
|
const xtbParser = new XtbParser();
|
|
const {
|
|
locale,
|
|
msgIdToHtml,
|
|
errors
|
|
} = xtbParser.parse(content, url);
|
|
const i18nNodesByMsgId = {};
|
|
const converter = new XmlToI18n();
|
|
Object.keys(msgIdToHtml).forEach(msgId => {
|
|
const valueFn = function () {
|
|
const {
|
|
i18nNodes,
|
|
errors
|
|
} = converter.convert(msgIdToHtml[msgId], url);
|
|
if (errors.length) {
|
|
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
|
}
|
|
return i18nNodes;
|
|
};
|
|
createLazyProperty(i18nNodesByMsgId, msgId, valueFn);
|
|
});
|
|
if (errors.length) {
|
|
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
|
}
|
|
return {
|
|
locale: locale,
|
|
i18nNodesByMsgId
|
|
};
|
|
}
|
|
digest(message) {
|
|
return digest(message);
|
|
}
|
|
createNameMapper(message) {
|
|
return new SimplePlaceholderMapper(message, toPublicName);
|
|
}
|
|
}
|
|
function createLazyProperty(messages, id, valueFn) {
|
|
Object.defineProperty(messages, id, {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get: function () {
|
|
const value = valueFn();
|
|
Object.defineProperty(messages, id, {
|
|
enumerable: true,
|
|
value
|
|
});
|
|
return value;
|
|
},
|
|
set: _ => {
|
|
throw new Error('Could not overwrite an XTB translation');
|
|
}
|
|
});
|
|
}
|
|
class XtbParser {
|
|
_bundleDepth;
|
|
_errors;
|
|
_msgIdToHtml;
|
|
_locale = null;
|
|
parse(xtb, url) {
|
|
this._bundleDepth = 0;
|
|
this._msgIdToHtml = {};
|
|
const xml = new XmlParser().parse(xtb, url);
|
|
this._errors = xml.errors;
|
|
visitAll(this, xml.rootNodes);
|
|
return {
|
|
msgIdToHtml: this._msgIdToHtml,
|
|
errors: this._errors,
|
|
locale: this._locale
|
|
};
|
|
}
|
|
visitElement(element, context) {
|
|
switch (element.name) {
|
|
case _TRANSLATIONS_TAG:
|
|
this._bundleDepth++;
|
|
if (this._bundleDepth > 1) {
|
|
this._addError(element, `<${_TRANSLATIONS_TAG}> elements can not be nested`);
|
|
}
|
|
const langAttr = element.attrs.find(attr => attr.name === 'lang');
|
|
if (langAttr) {
|
|
this._locale = langAttr.value;
|
|
}
|
|
visitAll(this, element.children, null);
|
|
this._bundleDepth--;
|
|
break;
|
|
case _TRANSLATION_TAG:
|
|
const idAttr = element.attrs.find(attr => attr.name === 'id');
|
|
if (!idAttr) {
|
|
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
|
|
} else {
|
|
const id = idAttr.value;
|
|
if (this._msgIdToHtml.hasOwnProperty(id)) {
|
|
this._addError(element, `Duplicated translations for msg ${id}`);
|
|
} else {
|
|
const innerTextStart = element.startSourceSpan.end.offset;
|
|
const innerTextEnd = element.endSourceSpan.start.offset;
|
|
const content = element.startSourceSpan.start.file.content;
|
|
const innerText = content.slice(innerTextStart, innerTextEnd);
|
|
this._msgIdToHtml[id] = innerText;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
this._addError(element, 'Unexpected tag');
|
|
}
|
|
}
|
|
visitAttribute(attribute, context) {}
|
|
visitText(text, context) {}
|
|
visitComment(comment, context) {}
|
|
visitExpansion(expansion, context) {}
|
|
visitExpansionCase(expansionCase, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(block, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
this._addError(component, 'Unexpected node');
|
|
}
|
|
visitDirective(directive, context) {
|
|
this._addError(directive, 'Unexpected node');
|
|
}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
}
|
|
class XmlToI18n {
|
|
_errors;
|
|
convert(message, url) {
|
|
const xmlIcu = new XmlParser().parse(message, url, {
|
|
tokenizeExpansionForms: true
|
|
});
|
|
this._errors = xmlIcu.errors;
|
|
const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : visitAll(this, xmlIcu.rootNodes);
|
|
return {
|
|
i18nNodes,
|
|
errors: this._errors
|
|
};
|
|
}
|
|
visitText(text, context) {
|
|
return new Text$2(text.value, text.sourceSpan);
|
|
}
|
|
visitExpansion(icu, context) {
|
|
const caseMap = {};
|
|
visitAll(this, icu.cases).forEach(c => {
|
|
caseMap[c.value] = new Container(c.nodes, icu.sourceSpan);
|
|
});
|
|
return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
|
|
}
|
|
visitExpansionCase(icuCase, context) {
|
|
return {
|
|
value: icuCase.value,
|
|
nodes: visitAll(this, icuCase.expression)
|
|
};
|
|
}
|
|
visitElement(el, context) {
|
|
if (el.name === _PLACEHOLDER_TAG) {
|
|
const nameAttr = el.attrs.find(attr => attr.name === 'name');
|
|
if (nameAttr) {
|
|
return new Placeholder('', nameAttr.value, el.sourceSpan);
|
|
}
|
|
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`);
|
|
} else {
|
|
this._addError(el, `Unexpected tag`);
|
|
}
|
|
return null;
|
|
}
|
|
visitComment(comment, context) {}
|
|
visitAttribute(attribute, context) {}
|
|
visitBlock(block, context) {}
|
|
visitBlockParameter(block, context) {}
|
|
visitLetDeclaration(decl, context) {}
|
|
visitComponent(component, context) {
|
|
this._addError(component, 'Unexpected node');
|
|
}
|
|
visitDirective(directive, context) {
|
|
this._addError(directive, 'Unexpected node');
|
|
}
|
|
_addError(node, message) {
|
|
this._errors.push(new ParseError(node.sourceSpan, message));
|
|
}
|
|
}
|
|
|
|
class TranslationBundle {
|
|
_i18nNodesByMsgId;
|
|
digest;
|
|
mapperFactory;
|
|
_i18nToHtml;
|
|
constructor(_i18nNodesByMsgId = {}, locale, digest, mapperFactory, missingTranslationStrategy = MissingTranslationStrategy.Warning, console) {
|
|
this._i18nNodesByMsgId = _i18nNodesByMsgId;
|
|
this.digest = digest;
|
|
this.mapperFactory = mapperFactory;
|
|
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, locale, digest, mapperFactory, missingTranslationStrategy, console);
|
|
}
|
|
static load(content, url, serializer, missingTranslationStrategy, console) {
|
|
const {
|
|
locale,
|
|
i18nNodesByMsgId
|
|
} = serializer.load(content, url);
|
|
const digestFn = m => serializer.digest(m);
|
|
const mapperFactory = m => serializer.createNameMapper(m);
|
|
return new TranslationBundle(i18nNodesByMsgId, locale, digestFn, mapperFactory, missingTranslationStrategy, console);
|
|
}
|
|
get(srcMsg) {
|
|
const html = this._i18nToHtml.convert(srcMsg);
|
|
if (html.errors.length) {
|
|
throw new Error(html.errors.join('\n'));
|
|
}
|
|
return html.nodes;
|
|
}
|
|
has(srcMsg) {
|
|
return this.digest(srcMsg) in this._i18nNodesByMsgId;
|
|
}
|
|
}
|
|
class I18nToHtmlVisitor {
|
|
_i18nNodesByMsgId;
|
|
_locale;
|
|
_digest;
|
|
_mapperFactory;
|
|
_missingTranslationStrategy;
|
|
_console;
|
|
_srcMsg;
|
|
_errors = [];
|
|
_contextStack = [];
|
|
_mapper;
|
|
constructor(_i18nNodesByMsgId = {}, _locale, _digest, _mapperFactory, _missingTranslationStrategy, _console) {
|
|
this._i18nNodesByMsgId = _i18nNodesByMsgId;
|
|
this._locale = _locale;
|
|
this._digest = _digest;
|
|
this._mapperFactory = _mapperFactory;
|
|
this._missingTranslationStrategy = _missingTranslationStrategy;
|
|
this._console = _console;
|
|
}
|
|
convert(srcMsg) {
|
|
this._contextStack.length = 0;
|
|
this._errors.length = 0;
|
|
const text = this._convertToText(srcMsg);
|
|
const url = srcMsg.nodes[0].sourceSpan.start.file.url;
|
|
const html = new HtmlParser().parse(text, url, {
|
|
tokenizeExpansionForms: true
|
|
});
|
|
return {
|
|
nodes: html.rootNodes,
|
|
errors: [...this._errors, ...html.errors]
|
|
};
|
|
}
|
|
visitText(text, context) {
|
|
return escapeXml(text.value);
|
|
}
|
|
visitContainer(container, context) {
|
|
return container.children.map(n => n.visit(this)).join('');
|
|
}
|
|
visitIcu(icu, context) {
|
|
const cases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
|
const exp = this._srcMsg.placeholders.hasOwnProperty(icu.expression) ? this._srcMsg.placeholders[icu.expression].text : icu.expression;
|
|
return `{${exp}, ${icu.type}, ${cases.join(' ')}}`;
|
|
}
|
|
visitPlaceholder(ph, context) {
|
|
const phName = this._mapper(ph.name);
|
|
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
|
|
return this._srcMsg.placeholders[phName].text;
|
|
}
|
|
if (this._srcMsg.placeholderToMessage.hasOwnProperty(phName)) {
|
|
return this._convertToText(this._srcMsg.placeholderToMessage[phName]);
|
|
}
|
|
this._addError(ph, `Unknown placeholder "${ph.name}"`);
|
|
return '';
|
|
}
|
|
visitTagPlaceholder(ph, context) {
|
|
const tag = `${ph.tag}`;
|
|
const attrs = Object.keys(ph.attrs).map(name => `${name}="${ph.attrs[name]}"`).join(' ');
|
|
if (ph.isVoid) {
|
|
return `<${tag} ${attrs}/>`;
|
|
}
|
|
const children = ph.children.map(c => c.visit(this)).join('');
|
|
return `<${tag} ${attrs}>${children}</${tag}>`;
|
|
}
|
|
visitIcuPlaceholder(ph, context) {
|
|
return this._convertToText(this._srcMsg.placeholderToMessage[ph.name]);
|
|
}
|
|
visitBlockPlaceholder(ph, context) {
|
|
const params = ph.parameters.length === 0 ? '' : ` (${ph.parameters.join('; ')})`;
|
|
const children = ph.children.map(c => c.visit(this)).join('');
|
|
return `@${ph.name}${params} {${children}}`;
|
|
}
|
|
_convertToText(srcMsg) {
|
|
const id = this._digest(srcMsg);
|
|
const mapper = this._mapperFactory ? this._mapperFactory(srcMsg) : null;
|
|
let nodes;
|
|
this._contextStack.push({
|
|
msg: this._srcMsg,
|
|
mapper: this._mapper
|
|
});
|
|
this._srcMsg = srcMsg;
|
|
if (this._i18nNodesByMsgId.hasOwnProperty(id)) {
|
|
nodes = this._i18nNodesByMsgId[id];
|
|
this._mapper = name => mapper ? mapper.toInternalName(name) : name;
|
|
} else {
|
|
if (this._missingTranslationStrategy === MissingTranslationStrategy.Error) {
|
|
const ctx = this._locale ? ` for locale "${this._locale}"` : '';
|
|
this._addError(srcMsg.nodes[0], `Missing translation for message "${id}"${ctx}`);
|
|
} else if (this._console && this._missingTranslationStrategy === MissingTranslationStrategy.Warning) {
|
|
const ctx = this._locale ? ` for locale "${this._locale}"` : '';
|
|
this._console.warn(`Missing translation for message "${id}"${ctx}`);
|
|
}
|
|
nodes = srcMsg.nodes;
|
|
this._mapper = name => name;
|
|
}
|
|
const text = nodes.map(node => node.visit(this)).join('');
|
|
const context = this._contextStack.pop();
|
|
this._srcMsg = context.msg;
|
|
this._mapper = context.mapper;
|
|
return text;
|
|
}
|
|
_addError(el, msg) {
|
|
this._errors.push(new ParseError(el.sourceSpan, msg));
|
|
}
|
|
}
|
|
|
|
class I18NHtmlParser {
|
|
_htmlParser;
|
|
getTagDefinition;
|
|
_translationBundle;
|
|
constructor(_htmlParser, translations, translationsFormat, missingTranslation = MissingTranslationStrategy.Warning, console) {
|
|
this._htmlParser = _htmlParser;
|
|
if (translations) {
|
|
const serializer = createSerializer(translationsFormat);
|
|
this._translationBundle = TranslationBundle.load(translations, 'i18n', serializer, missingTranslation, console);
|
|
} else {
|
|
this._translationBundle = new TranslationBundle({}, null, digest$1, undefined, missingTranslation, console);
|
|
}
|
|
}
|
|
parse(source, url, options = {}) {
|
|
const parseResult = this._htmlParser.parse(source, url, {
|
|
...options
|
|
});
|
|
if (parseResult.errors.length) {
|
|
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors);
|
|
}
|
|
return mergeTranslations(parseResult.rootNodes, this._translationBundle, [], {});
|
|
}
|
|
}
|
|
function createSerializer(format) {
|
|
format = (format || 'xlf').toLowerCase();
|
|
switch (format) {
|
|
case 'xmb':
|
|
return new Xmb();
|
|
case 'xtb':
|
|
return new Xtb();
|
|
case 'xliff2':
|
|
case 'xlf2':
|
|
return new Xliff2();
|
|
case 'xliff':
|
|
case 'xlf':
|
|
default:
|
|
return new Xliff();
|
|
}
|
|
}
|
|
|
|
class MessageBundle {
|
|
_htmlParser;
|
|
_implicitTags;
|
|
_implicitAttrs;
|
|
_locale;
|
|
_preserveWhitespace;
|
|
_messages = [];
|
|
constructor(_htmlParser, _implicitTags, _implicitAttrs, _locale = null, _preserveWhitespace = true) {
|
|
this._htmlParser = _htmlParser;
|
|
this._implicitTags = _implicitTags;
|
|
this._implicitAttrs = _implicitAttrs;
|
|
this._locale = _locale;
|
|
this._preserveWhitespace = _preserveWhitespace;
|
|
}
|
|
updateFromTemplate(source, url) {
|
|
const htmlParserResult = this._htmlParser.parse(source, url, {
|
|
tokenizeExpansionForms: true
|
|
});
|
|
if (htmlParserResult.errors.length) {
|
|
return htmlParserResult.errors;
|
|
}
|
|
const rootNodes = this._preserveWhitespace ? htmlParserResult.rootNodes : visitAllWithSiblings(new WhitespaceVisitor(false), htmlParserResult.rootNodes);
|
|
const i18nParserResult = extractMessages(rootNodes, this._implicitTags, this._implicitAttrs, this._preserveWhitespace);
|
|
if (i18nParserResult.errors.length) {
|
|
return i18nParserResult.errors;
|
|
}
|
|
this._messages.push(...i18nParserResult.messages);
|
|
return [];
|
|
}
|
|
getMessages() {
|
|
return this._messages;
|
|
}
|
|
write(serializer, filterSources) {
|
|
const messages = {};
|
|
const mapperVisitor = new MapPlaceholderNames();
|
|
this._messages.forEach(message => {
|
|
const id = serializer.digest(message);
|
|
if (!messages.hasOwnProperty(id)) {
|
|
messages[id] = message;
|
|
} else {
|
|
messages[id].sources.push(...message.sources);
|
|
}
|
|
});
|
|
const msgList = Object.keys(messages).map(id => {
|
|
const mapper = serializer.createNameMapper(messages[id]);
|
|
const src = messages[id];
|
|
const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
|
|
let transformedMessage = new Message(nodes, {}, {}, src.meaning, src.description, id);
|
|
transformedMessage.sources = src.sources;
|
|
if (filterSources) {
|
|
transformedMessage.sources.forEach(source => source.filePath = filterSources(source.filePath));
|
|
}
|
|
return transformedMessage;
|
|
});
|
|
return serializer.write(msgList, this._locale);
|
|
}
|
|
}
|
|
class MapPlaceholderNames extends CloneVisitor {
|
|
convert(nodes, mapper) {
|
|
return mapper ? nodes.map(n => n.visit(this, mapper)) : nodes;
|
|
}
|
|
visitTagPlaceholder(ph, mapper) {
|
|
const startName = mapper.toPublicName(ph.startName);
|
|
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
|
|
const children = ph.children.map(n => n.visit(this, mapper));
|
|
return new TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
|
|
}
|
|
visitBlockPlaceholder(ph, mapper) {
|
|
const startName = mapper.toPublicName(ph.startName);
|
|
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
|
|
const children = ph.children.map(n => n.visit(this, mapper));
|
|
return new BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
|
|
}
|
|
visitPlaceholder(ph, mapper) {
|
|
return new Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
|
|
}
|
|
visitIcuPlaceholder(ph, mapper) {
|
|
return new IcuPlaceholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
|
|
}
|
|
}
|
|
|
|
function compileClassMetadata(metadata) {
|
|
const fnCall = internalCompileClassMetadata(metadata);
|
|
return arrowFn([], [devOnlyGuardedExpression(fnCall).toStmt()]).callFn([]);
|
|
}
|
|
function internalCompileClassMetadata(metadata) {
|
|
return importExpr(Identifiers.setClassMetadata).callFn([metadata.type, metadata.decorators, metadata.ctorParameters ?? literal(null), metadata.propDecorators ?? literal(null)]);
|
|
}
|
|
function compileComponentClassMetadata(metadata, dependencies) {
|
|
if (dependencies === null || dependencies.length === 0) {
|
|
return compileClassMetadata(metadata);
|
|
}
|
|
return internalCompileSetClassMetadataAsync(metadata, dependencies.map(dep => new FnParam(dep.symbolName, DYNAMIC_TYPE)), compileComponentMetadataAsyncResolver(dependencies));
|
|
}
|
|
function compileOpaqueAsyncClassMetadata(metadata, deferResolver, deferredDependencyNames) {
|
|
return internalCompileSetClassMetadataAsync(metadata, deferredDependencyNames.map(name => new FnParam(name, DYNAMIC_TYPE)), deferResolver);
|
|
}
|
|
function internalCompileSetClassMetadataAsync(metadata, wrapperParams, dependencyResolverFn) {
|
|
const setClassMetadataCall = internalCompileClassMetadata(metadata);
|
|
const setClassMetaWrapper = arrowFn(wrapperParams, [setClassMetadataCall.toStmt()]);
|
|
const setClassMetaAsync = importExpr(Identifiers.setClassMetadataAsync).callFn([metadata.type, dependencyResolverFn, setClassMetaWrapper]);
|
|
return arrowFn([], [devOnlyGuardedExpression(setClassMetaAsync).toStmt()]).callFn([]);
|
|
}
|
|
function compileComponentMetadataAsyncResolver(dependencies) {
|
|
const dynamicImports = dependencies.map(({
|
|
symbolName,
|
|
importPath,
|
|
isDefaultImport
|
|
}) => {
|
|
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
return new DynamicImportExpr(importPath).prop('then').callFn([innerFn]);
|
|
});
|
|
return arrowFn([], literalArr(dynamicImports));
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
|
|
const MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION = '18.0.0';
|
|
function compileDeclareClassMetadata(metadata) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', metadata.type);
|
|
definitionMap.set('decorators', metadata.decorators);
|
|
definitionMap.set('ctorParameters', metadata.ctorParameters);
|
|
definitionMap.set('propDecorators', metadata.propDecorators);
|
|
return importExpr(Identifiers.declareClassMetadata).callFn([definitionMap.toLiteralMap()]);
|
|
}
|
|
function compileComponentDeclareClassMetadata(metadata, dependencies) {
|
|
if (dependencies === null || dependencies.length === 0) {
|
|
return compileDeclareClassMetadata(metadata);
|
|
}
|
|
const definitionMap = new DefinitionMap();
|
|
const callbackReturnDefinitionMap = new DefinitionMap();
|
|
callbackReturnDefinitionMap.set('decorators', metadata.decorators);
|
|
callbackReturnDefinitionMap.set('ctorParameters', metadata.ctorParameters ?? literal(null));
|
|
callbackReturnDefinitionMap.set('propDecorators', metadata.propDecorators ?? literal(null));
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', metadata.type);
|
|
definitionMap.set('resolveDeferredDeps', compileComponentMetadataAsyncResolver(dependencies));
|
|
definitionMap.set('resolveMetadata', arrowFn(dependencies.map(dep => new FnParam(dep.symbolName, DYNAMIC_TYPE)), callbackReturnDefinitionMap.toLiteralMap()));
|
|
return importExpr(Identifiers.declareClassMetadataAsync).callFn([definitionMap.toLiteralMap()]);
|
|
}
|
|
|
|
function toOptionalLiteralArray(values, mapper) {
|
|
if (values === null || values.length === 0) {
|
|
return null;
|
|
}
|
|
return literalArr(values.map(value => mapper(value)));
|
|
}
|
|
function toOptionalLiteralMap(object, mapper) {
|
|
const entries = Object.keys(object).map(key => {
|
|
const value = object[key];
|
|
return {
|
|
key,
|
|
value: mapper(value),
|
|
quoted: true
|
|
};
|
|
});
|
|
if (entries.length > 0) {
|
|
return literalMap(entries);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
function compileDependencies(deps) {
|
|
if (deps === 'invalid') {
|
|
return literal('invalid');
|
|
} else if (deps === null) {
|
|
return literal(null);
|
|
} else {
|
|
return literalArr(deps.map(compileDependency));
|
|
}
|
|
}
|
|
function compileDependency(dep) {
|
|
const depMeta = new DefinitionMap();
|
|
depMeta.set('token', dep.token);
|
|
if (dep.attributeNameType !== null) {
|
|
depMeta.set('attribute', literal(true));
|
|
}
|
|
if (dep.host) {
|
|
depMeta.set('host', literal(true));
|
|
}
|
|
if (dep.optional) {
|
|
depMeta.set('optional', literal(true));
|
|
}
|
|
if (dep.self) {
|
|
depMeta.set('self', literal(true));
|
|
}
|
|
if (dep.skipSelf) {
|
|
depMeta.set('skipSelf', literal(true));
|
|
}
|
|
return depMeta.toLiteralMap();
|
|
}
|
|
|
|
function compileDeclareDirectiveFromMetadata(meta) {
|
|
const definitionMap = createDirectiveDefinitionMap(meta);
|
|
const expression = importExpr(Identifiers.declareDirective).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createDirectiveType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createDirectiveDefinitionMap(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
const minVersion = getMinimumVersionForPartialOutput(meta);
|
|
definitionMap.set('minVersion', literal(minVersion));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('type', meta.type.value);
|
|
if (meta.isStandalone !== undefined) {
|
|
definitionMap.set('isStandalone', literal(meta.isStandalone));
|
|
}
|
|
if (meta.isSignal) {
|
|
definitionMap.set('isSignal', literal(meta.isSignal));
|
|
}
|
|
if (meta.selector !== null) {
|
|
definitionMap.set('selector', literal(meta.selector));
|
|
}
|
|
definitionMap.set('inputs', needsNewInputPartialOutput(meta) ? createInputsPartialMetadata(meta.inputs) : legacyInputsPartialMetadata(meta.inputs));
|
|
definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs));
|
|
definitionMap.set('host', compileHostMetadata(meta.host));
|
|
definitionMap.set('providers', meta.providers);
|
|
if (meta.queries.length > 0) {
|
|
definitionMap.set('queries', literalArr(meta.queries.map(compileQuery)));
|
|
}
|
|
if (meta.viewQueries.length > 0) {
|
|
definitionMap.set('viewQueries', literalArr(meta.viewQueries.map(compileQuery)));
|
|
}
|
|
if (meta.exportAs !== null) {
|
|
definitionMap.set('exportAs', asLiteral(meta.exportAs));
|
|
}
|
|
if (meta.usesInheritance) {
|
|
definitionMap.set('usesInheritance', literal(true));
|
|
}
|
|
if (meta.lifecycle.usesOnChanges) {
|
|
definitionMap.set('usesOnChanges', literal(true));
|
|
}
|
|
if (meta.hostDirectives?.length) {
|
|
definitionMap.set('hostDirectives', createHostDirectives(meta.hostDirectives));
|
|
}
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
return definitionMap;
|
|
}
|
|
function getMinimumVersionForPartialOutput(meta) {
|
|
let minVersion = '14.0.0';
|
|
const hasDecoratorTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
|
|
if (hasDecoratorTransformFunctions) {
|
|
minVersion = '16.1.0';
|
|
}
|
|
if (needsNewInputPartialOutput(meta)) {
|
|
minVersion = '17.1.0';
|
|
}
|
|
if (meta.queries.some(q => q.isSignal) || meta.viewQueries.some(q => q.isSignal)) {
|
|
minVersion = '17.2.0';
|
|
}
|
|
return minVersion;
|
|
}
|
|
function needsNewInputPartialOutput(meta) {
|
|
return Object.values(meta.inputs).some(input => input.isSignal);
|
|
}
|
|
function compileQuery(query) {
|
|
const meta = new DefinitionMap();
|
|
meta.set('propertyName', literal(query.propertyName));
|
|
if (query.first) {
|
|
meta.set('first', literal(true));
|
|
}
|
|
meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : convertFromMaybeForwardRefExpression(query.predicate));
|
|
if (!query.emitDistinctChangesOnly) {
|
|
meta.set('emitDistinctChangesOnly', literal(false));
|
|
}
|
|
if (query.descendants) {
|
|
meta.set('descendants', literal(true));
|
|
}
|
|
meta.set('read', query.read);
|
|
if (query.static) {
|
|
meta.set('static', literal(true));
|
|
}
|
|
if (query.isSignal) {
|
|
meta.set('isSignal', literal(true));
|
|
}
|
|
return meta.toLiteralMap();
|
|
}
|
|
function compileHostMetadata(meta) {
|
|
const hostMetadata = new DefinitionMap();
|
|
hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression));
|
|
hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, literal));
|
|
hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, literal));
|
|
if (meta.specialAttributes.styleAttr) {
|
|
hostMetadata.set('styleAttribute', literal(meta.specialAttributes.styleAttr));
|
|
}
|
|
if (meta.specialAttributes.classAttr) {
|
|
hostMetadata.set('classAttribute', literal(meta.specialAttributes.classAttr));
|
|
}
|
|
if (hostMetadata.values.length > 0) {
|
|
return hostMetadata.toLiteralMap();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
function createHostDirectives(hostDirectives) {
|
|
const expressions = hostDirectives.map(current => {
|
|
const keys = [{
|
|
key: 'directive',
|
|
value: current.isForwardReference ? generateForwardRef(current.directive.type) : current.directive.type,
|
|
quoted: false
|
|
}];
|
|
const inputsLiteral = current.inputs ? createHostDirectivesMappingArray(current.inputs) : null;
|
|
const outputsLiteral = current.outputs ? createHostDirectivesMappingArray(current.outputs) : null;
|
|
if (inputsLiteral) {
|
|
keys.push({
|
|
key: 'inputs',
|
|
value: inputsLiteral,
|
|
quoted: false
|
|
});
|
|
}
|
|
if (outputsLiteral) {
|
|
keys.push({
|
|
key: 'outputs',
|
|
value: outputsLiteral,
|
|
quoted: false
|
|
});
|
|
}
|
|
return literalMap(keys);
|
|
});
|
|
return literalArr(expressions);
|
|
}
|
|
function createInputsPartialMetadata(inputs) {
|
|
const keys = Object.getOwnPropertyNames(inputs);
|
|
if (keys.length === 0) {
|
|
return null;
|
|
}
|
|
return literalMap(keys.map(declaredName => {
|
|
const value = inputs[declaredName];
|
|
return {
|
|
key: declaredName,
|
|
quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
|
|
value: literalMap([{
|
|
key: 'classPropertyName',
|
|
quoted: false,
|
|
value: asLiteral(value.classPropertyName)
|
|
}, {
|
|
key: 'publicName',
|
|
quoted: false,
|
|
value: asLiteral(value.bindingPropertyName)
|
|
}, {
|
|
key: 'isSignal',
|
|
quoted: false,
|
|
value: asLiteral(value.isSignal)
|
|
}, {
|
|
key: 'isRequired',
|
|
quoted: false,
|
|
value: asLiteral(value.required)
|
|
}, {
|
|
key: 'transformFunction',
|
|
quoted: false,
|
|
value: value.transformFunction ?? NULL_EXPR
|
|
}])
|
|
};
|
|
}));
|
|
}
|
|
function legacyInputsPartialMetadata(inputs) {
|
|
const keys = Object.getOwnPropertyNames(inputs);
|
|
if (keys.length === 0) {
|
|
return null;
|
|
}
|
|
return literalMap(keys.map(declaredName => {
|
|
const value = inputs[declaredName];
|
|
const publicName = value.bindingPropertyName;
|
|
const differentDeclaringName = publicName !== declaredName;
|
|
let result;
|
|
if (differentDeclaringName || value.transformFunction !== null) {
|
|
const values = [asLiteral(publicName), asLiteral(declaredName)];
|
|
if (value.transformFunction !== null) {
|
|
values.push(value.transformFunction);
|
|
}
|
|
result = literalArr(values);
|
|
} else {
|
|
result = asLiteral(publicName);
|
|
}
|
|
return {
|
|
key: declaredName,
|
|
quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
|
|
value: result
|
|
};
|
|
}));
|
|
}
|
|
|
|
function compileDeclareComponentFromMetadata(meta, template, additionalTemplateInfo) {
|
|
const definitionMap = createComponentDefinitionMap(meta, template, additionalTemplateInfo);
|
|
const expression = importExpr(Identifiers.declareComponent).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createComponentType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createComponentDefinitionMap(meta, template, templateInfo) {
|
|
const definitionMap = createDirectiveDefinitionMap(meta);
|
|
const blockVisitor = new BlockPresenceVisitor();
|
|
visitAll$1(blockVisitor, template.nodes);
|
|
definitionMap.set('template', getTemplateExpression(template, templateInfo));
|
|
if (templateInfo.isInline) {
|
|
definitionMap.set('isInline', literal(true));
|
|
}
|
|
if (blockVisitor.hasBlocks) {
|
|
definitionMap.set('minVersion', literal('17.0.0'));
|
|
}
|
|
definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal));
|
|
definitionMap.set('dependencies', compileUsedDependenciesMetadata(meta));
|
|
definitionMap.set('viewProviders', meta.viewProviders);
|
|
definitionMap.set('animations', meta.animations);
|
|
if (meta.changeDetection !== null) {
|
|
if (typeof meta.changeDetection === 'object') {
|
|
throw new Error('Impossible state! Change detection flag is not resolved!');
|
|
}
|
|
definitionMap.set('changeDetection', importExpr(Identifiers.ChangeDetectionStrategy).prop(ChangeDetectionStrategy[meta.changeDetection]));
|
|
}
|
|
if (meta.encapsulation !== ViewEncapsulation$1.Emulated) {
|
|
definitionMap.set('encapsulation', importExpr(Identifiers.ViewEncapsulation).prop(ViewEncapsulation$1[meta.encapsulation]));
|
|
}
|
|
if (template.preserveWhitespaces === true) {
|
|
definitionMap.set('preserveWhitespaces', literal(true));
|
|
}
|
|
if (meta.defer.mode === 0) {
|
|
const resolvers = [];
|
|
let hasResolvers = false;
|
|
for (const deps of meta.defer.blocks.values()) {
|
|
if (deps === null) {
|
|
resolvers.push(literal(null));
|
|
} else {
|
|
resolvers.push(deps);
|
|
hasResolvers = true;
|
|
}
|
|
}
|
|
if (hasResolvers) {
|
|
definitionMap.set('deferBlockDependencies', literalArr(resolvers));
|
|
}
|
|
} else {
|
|
throw new Error('Unsupported defer function emit mode in partial compilation');
|
|
}
|
|
return definitionMap;
|
|
}
|
|
function getTemplateExpression(template, templateInfo) {
|
|
if (templateInfo.inlineTemplateLiteralExpression !== null) {
|
|
return templateInfo.inlineTemplateLiteralExpression;
|
|
}
|
|
if (templateInfo.isInline) {
|
|
return literal(templateInfo.content, null, null);
|
|
}
|
|
const contents = templateInfo.content;
|
|
const file = new ParseSourceFile(contents, templateInfo.sourceUrl);
|
|
const start = new ParseLocation(file, 0, 0, 0);
|
|
const end = computeEndLocation(file, contents);
|
|
const span = new ParseSourceSpan(start, end);
|
|
return literal(contents, null, span);
|
|
}
|
|
function computeEndLocation(file, contents) {
|
|
const length = contents.length;
|
|
let lineStart = 0;
|
|
let lastLineStart = 0;
|
|
let line = 0;
|
|
do {
|
|
lineStart = contents.indexOf('\n', lastLineStart);
|
|
if (lineStart !== -1) {
|
|
lastLineStart = lineStart + 1;
|
|
line++;
|
|
}
|
|
} while (lineStart !== -1);
|
|
return new ParseLocation(file, length, line, length - lastLineStart);
|
|
}
|
|
function compileUsedDependenciesMetadata(meta) {
|
|
const wrapType = meta.declarationListEmitMode !== 0 ? generateForwardRef : expr => expr;
|
|
if (meta.declarationListEmitMode === 3) {
|
|
throw new Error(`Unsupported emit mode`);
|
|
}
|
|
return toOptionalLiteralArray(meta.declarations, decl => {
|
|
switch (decl.kind) {
|
|
case R3TemplateDependencyKind.Directive:
|
|
const dirMeta = new DefinitionMap();
|
|
dirMeta.set('kind', literal(decl.isComponent ? 'component' : 'directive'));
|
|
dirMeta.set('type', wrapType(decl.type));
|
|
dirMeta.set('selector', literal(decl.selector));
|
|
dirMeta.set('inputs', toOptionalLiteralArray(decl.inputs, literal));
|
|
dirMeta.set('outputs', toOptionalLiteralArray(decl.outputs, literal));
|
|
dirMeta.set('exportAs', toOptionalLiteralArray(decl.exportAs, literal));
|
|
return dirMeta.toLiteralMap();
|
|
case R3TemplateDependencyKind.Pipe:
|
|
const pipeMeta = new DefinitionMap();
|
|
pipeMeta.set('kind', literal('pipe'));
|
|
pipeMeta.set('type', wrapType(decl.type));
|
|
pipeMeta.set('name', literal(decl.name));
|
|
return pipeMeta.toLiteralMap();
|
|
case R3TemplateDependencyKind.NgModule:
|
|
const ngModuleMeta = new DefinitionMap();
|
|
ngModuleMeta.set('kind', literal('ngmodule'));
|
|
ngModuleMeta.set('type', wrapType(decl.type));
|
|
return ngModuleMeta.toLiteralMap();
|
|
}
|
|
});
|
|
}
|
|
class BlockPresenceVisitor extends RecursiveVisitor$1 {
|
|
hasBlocks = false;
|
|
visitDeferredBlock() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitDeferredBlockPlaceholder() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitDeferredBlockLoading() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitDeferredBlockError() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitIfBlock() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitIfBlockBranch() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitForLoopBlock() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitForLoopBlockEmpty() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitSwitchBlock() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitSwitchBlockCase() {
|
|
this.hasBlocks = true;
|
|
}
|
|
visitSwitchBlockCaseGroup() {
|
|
this.hasBlocks = true;
|
|
}
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
function compileDeclareFactoryFunction(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', meta.type.value);
|
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
definitionMap.set('target', importExpr(Identifiers.FactoryTarget).prop(FactoryTarget[meta.target]));
|
|
return {
|
|
expression: importExpr(Identifiers.declareFactory).callFn([definitionMap.toLiteralMap()]),
|
|
statements: [],
|
|
type: createFactoryType(meta)
|
|
};
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION$3 = '12.0.0';
|
|
function compileDeclareInjectableFromMetadata(meta) {
|
|
const definitionMap = createInjectableDefinitionMap(meta);
|
|
const expression = importExpr(Identifiers.declareInjectable).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createInjectableType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createInjectableDefinitionMap(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', meta.type.value);
|
|
if (meta.providedIn !== undefined) {
|
|
const providedIn = convertFromMaybeForwardRefExpression(meta.providedIn);
|
|
if (providedIn.value !== null) {
|
|
definitionMap.set('providedIn', providedIn);
|
|
}
|
|
}
|
|
if (meta.useClass !== undefined) {
|
|
definitionMap.set('useClass', convertFromMaybeForwardRefExpression(meta.useClass));
|
|
}
|
|
if (meta.useExisting !== undefined) {
|
|
definitionMap.set('useExisting', convertFromMaybeForwardRefExpression(meta.useExisting));
|
|
}
|
|
if (meta.useValue !== undefined) {
|
|
definitionMap.set('useValue', convertFromMaybeForwardRefExpression(meta.useValue));
|
|
}
|
|
if (meta.useFactory !== undefined) {
|
|
definitionMap.set('useFactory', meta.useFactory);
|
|
}
|
|
if (meta.deps !== undefined) {
|
|
definitionMap.set('deps', literalArr(meta.deps.map(compileDependency)));
|
|
}
|
|
return definitionMap;
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION$2 = '12.0.0';
|
|
function compileDeclareInjectorFromMetadata(meta) {
|
|
const definitionMap = createInjectorDefinitionMap(meta);
|
|
const expression = importExpr(Identifiers.declareInjector).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createInjectorType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createInjectorDefinitionMap(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', meta.type.value);
|
|
definitionMap.set('providers', meta.providers);
|
|
if (meta.imports.length > 0) {
|
|
definitionMap.set('imports', literalArr(meta.imports));
|
|
}
|
|
return definitionMap;
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION$1 = '14.0.0';
|
|
function compileDeclareNgModuleFromMetadata(meta) {
|
|
const definitionMap = createNgModuleDefinitionMap(meta);
|
|
const expression = importExpr(Identifiers.declareNgModule).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createNgModuleType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createNgModuleDefinitionMap(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
if (meta.kind === R3NgModuleMetadataKind.Local) {
|
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
}
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', meta.type.value);
|
|
if (meta.bootstrap.length > 0) {
|
|
definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls));
|
|
}
|
|
if (meta.declarations.length > 0) {
|
|
definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
|
|
}
|
|
if (meta.imports.length > 0) {
|
|
definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
|
|
}
|
|
if (meta.exports.length > 0) {
|
|
definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
|
|
}
|
|
if (meta.schemas !== null && meta.schemas.length > 0) {
|
|
definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value)));
|
|
}
|
|
if (meta.id !== null) {
|
|
definitionMap.set('id', meta.id);
|
|
}
|
|
return definitionMap;
|
|
}
|
|
|
|
const MINIMUM_PARTIAL_LINKER_VERSION = '14.0.0';
|
|
function compileDeclarePipeFromMetadata(meta) {
|
|
const definitionMap = createPipeDefinitionMap(meta);
|
|
const expression = importExpr(Identifiers.declarePipe).callFn([definitionMap.toLiteralMap()]);
|
|
const type = createPipeType(meta);
|
|
return {
|
|
expression,
|
|
type,
|
|
statements: []
|
|
};
|
|
}
|
|
function createPipeDefinitionMap(meta) {
|
|
const definitionMap = new DefinitionMap();
|
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
definitionMap.set('version', literal('21.1.1'));
|
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
definitionMap.set('type', meta.type.value);
|
|
if (meta.isStandalone !== undefined) {
|
|
definitionMap.set('isStandalone', literal(meta.isStandalone));
|
|
}
|
|
definitionMap.set('name', literal(meta.pipeName ?? meta.name));
|
|
if (meta.pure === false) {
|
|
definitionMap.set('pure', literal(meta.pure));
|
|
}
|
|
return definitionMap;
|
|
}
|
|
|
|
function compileClassDebugInfo(debugInfo) {
|
|
const debugInfoObject = {
|
|
className: debugInfo.className
|
|
};
|
|
if (debugInfo.filePath) {
|
|
debugInfoObject.filePath = debugInfo.filePath;
|
|
debugInfoObject.lineNumber = debugInfo.lineNumber;
|
|
}
|
|
if (debugInfo.forbidOrphanRendering) {
|
|
debugInfoObject.forbidOrphanRendering = literal(true);
|
|
}
|
|
const fnCall = importExpr(Identifiers.setClassDebugInfo).callFn([debugInfo.type, mapLiteral(debugInfoObject)]);
|
|
const iife = arrowFn([], [devOnlyGuardedExpression(fnCall).toStmt()]);
|
|
return iife.callFn([]);
|
|
}
|
|
|
|
function compileHmrInitializer(meta) {
|
|
const moduleName = 'm';
|
|
const dataName = 'd';
|
|
const timestampName = 't';
|
|
const idName = 'id';
|
|
const importCallbackName = `${meta.className}_HmrLoad`;
|
|
const namespaces = meta.namespaceDependencies.map(dep => {
|
|
return new ExternalExpr({
|
|
moduleName: dep.moduleName,
|
|
name: null
|
|
});
|
|
});
|
|
const defaultRead = variable(moduleName).prop('default');
|
|
const replaceCall = importExpr(Identifiers.replaceMetadata).callFn([meta.type, defaultRead, literalArr(namespaces), literalArr(meta.localDependencies.map(l => l.runtimeRepresentation)), variable('import').prop('meta'), variable(idName)]);
|
|
const replaceCallback = arrowFn([new FnParam(moduleName)], defaultRead.and(replaceCall));
|
|
const url = importExpr(Identifiers.getReplaceMetadataURL).callFn([variable(idName), variable(timestampName), variable('import').prop('meta').prop('url')]);
|
|
const importCallback = new DeclareFunctionStmt(importCallbackName, [new FnParam(timestampName)], [new DynamicImportExpr(url, null, '@vite-ignore').prop('then').callFn([replaceCallback]).toStmt()], null, StmtModifier.Final);
|
|
const updateCallback = arrowFn([new FnParam(dataName)], variable(dataName).prop('id').identical(variable(idName)).and(variable(importCallbackName).callFn([variable(dataName).prop('timestamp')])));
|
|
const initialCall = variable(importCallbackName).callFn([variable('Date').prop('now').callFn([])]);
|
|
const hotRead = variable('import').prop('meta').prop('hot');
|
|
const hotListener = hotRead.clone().prop('on').callFn([literal('angular:component-update'), updateCallback]);
|
|
return arrowFn([], [new DeclareVarStmt(idName, literal(encodeURIComponent(`${meta.filePath}@${meta.className}`)), null, StmtModifier.Final), importCallback, devOnlyGuardedExpression(initialCall).toStmt(), devOnlyGuardedExpression(hotRead.and(hotListener)).toStmt()]).callFn([]);
|
|
}
|
|
function compileHmrUpdateCallback(definitions, constantStatements, meta) {
|
|
const namespaces = 'ɵɵnamespaces';
|
|
const params = [meta.className, namespaces].map(name => new FnParam(name, DYNAMIC_TYPE));
|
|
const body = [];
|
|
for (const local of meta.localDependencies) {
|
|
params.push(new FnParam(local.name));
|
|
}
|
|
for (let i = 0; i < meta.namespaceDependencies.length; i++) {
|
|
body.push(new DeclareVarStmt(meta.namespaceDependencies[i].assignedName, variable(namespaces).key(literal(i)), DYNAMIC_TYPE, StmtModifier.Final));
|
|
}
|
|
body.push(...constantStatements);
|
|
for (const field of definitions) {
|
|
if (field.initializer !== null) {
|
|
body.push(variable(meta.className).prop(field.name).set(field.initializer).toStmt());
|
|
for (const stmt of field.statements) {
|
|
body.push(stmt);
|
|
}
|
|
}
|
|
}
|
|
return new DeclareFunctionStmt(`${meta.className}_UpdateMetadata`, params, body, null, StmtModifier.Final);
|
|
}
|
|
|
|
const VERSION = new Version('21.1.1');
|
|
|
|
publishFacade(_global);
|
|
|
|
export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, BindingPipeType, BindingType, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CombinedRecursiveAstVisitor, CommaExpr, Comment, CompilerConfig, CompilerFacadeImpl, Component, Conditional, ConditionalExpr, ConstantPool, CssSelector, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, Directive, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, LeadingComment, LetDeclaration, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralMapPropertyAssignment, LiteralMapSpreadAssignment, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParenthesizedExpr, ParenthesizedExpression, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedEventType, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser, PrefixNot, PropertyRead, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, RegularExpressionLiteral, RegularExpressionLiteralExpr, ResourceLoader, ReturnStatement, SCHEMA, SECURITY_SCHEMA, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, SelectorlessMatcher, Serializer, SplitInterpolation, SpreadElement, SpreadElementExpr, Statement, StmtModifier, StringToken, StringTokenKind, TagContentType, TaggedTemplateLiteral, TaggedTemplateLiteralExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, TemplateLiteralElementExpr, TemplateLiteralExpr, Text, ThisReceiver, BlockNode as TmplAstBlockNode, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Component$1 as TmplAstComponent, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Directive$1 as TmplAstDirective, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HostElement as TmplAstHostElement, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, LetDeclaration$1 as TmplAstLetDeclaration, NeverDeferredTrigger as TmplAstNeverDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, SwitchBlockCaseGroup as TmplAstSwitchBlockCaseGroup, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, TypeofExpression, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation$1 as ViewEncapsulation, VoidExpr, VoidExpression, WrappedNodeExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ATTR_TO_PROP, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentDeclareClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDeferResolverFunction, compileDirectiveFromMetadata, compileFactoryFunction, compileHmrInitializer, compileHmrUpdateCallback, compileInjectable, compileInjector, compileNgModule, compileOpaqueAsyncClassMetadata, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, encapsulateStyle, escapeRegExp, findMatchingDirectivesAndPipes, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, setEnableTemplateSourceLocations, splitNsName, visitAll$1 as tmplAstVisitAll, verifyHostBindings, visitAll };
|
|
//# sourceMappingURL=compiler.mjs.map
|