fix(core): size regression with closure compiler (#25531)

By pulling in `compiler` into `core` the `compiler` was not
100% tree-shakable and about  8KB of code was retained
when tree-shaken with closure.

PR Close #25531
This commit is contained in:
Miško Hevery 2018-08-16 14:37:21 -07:00 committed by Misko Hevery
parent 371df35624
commit 1f59f2f04d
6 changed files with 123 additions and 99 deletions

View File

@ -69,63 +69,72 @@ export class HtmlTagDefinition implements TagDefinition {
} }
} }
let _DEFAULT_TAG_DEFINITION !: HtmlTagDefinition;
// see http://www.w3.org/TR/html51/syntax.html#optional-tags // see http://www.w3.org/TR/html51/syntax.html#optional-tags
// This implementation does not fully conform to the HTML5 spec. // This implementation does not fully conform to the HTML5 spec.
const TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = { let TAG_DEFINITIONS !: {[key: string]: HtmlTagDefinition};
'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'],
requiredParents: ['tbody', 'tfoot', 'thead'],
closedByParent: true
}),
'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
'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: TagContentType.ESCAPABLE_RAW_TEXT}),
'textarea':
new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}),
};
const _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition {
if (!TAG_DEFINITIONS) {
_DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
TAG_DEFINITIONS = {
'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'],
requiredParents: ['tbody', 'tfoot', 'thead'],
closedByParent: true
}),
'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
'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: TagContentType.ESCAPABLE_RAW_TEXT}),
'textarea': new HtmlTagDefinition(
{contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}),
};
}
return TAG_DEFINITIONS[tagName.toLowerCase()] || _DEFAULT_TAG_DEFINITION; return TAG_DEFINITIONS[tagName.toLowerCase()] || _DEFAULT_TAG_DEFINITION;
} }

View File

@ -1078,8 +1078,14 @@ export class BindingScope implements LocalResolver {
private map = new Map<string, BindingData>(); private map = new Map<string, BindingData>();
private referenceNameIndex = 0; private referenceNameIndex = 0;
private restoreViewVariable: o.ReadVarExpr|null = null; private restoreViewVariable: o.ReadVarExpr|null = null;
private static _ROOT_SCOPE: BindingScope;
static ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event')); static get ROOT_SCOPE(): BindingScope {
if (!BindingScope._ROOT_SCOPE) {
BindingScope._ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event'));
}
return BindingScope._ROOT_SCOPE;
}
private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {} private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {}

View File

@ -342,11 +342,11 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
// property names do not have a security impact. // property names do not have a security impact.
tagName = tagName.toLowerCase(); tagName = tagName.toLowerCase();
propName = propName.toLowerCase(); propName = propName.toLowerCase();
let ctx = SECURITY_SCHEMA[tagName + '|' + propName]; let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
if (ctx) { if (ctx) {
return ctx; return ctx;
} }
ctx = SECURITY_SCHEMA['*|' + propName]; ctx = SECURITY_SCHEMA()['*|' + propName];
return ctx ? ctx : SecurityContext.NONE; return ctx ? ctx : SecurityContext.NONE;
} }

View File

@ -20,39 +20,45 @@ import {SecurityContext} from '../core';
// ================================================================================================= // =================================================================================================
/** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */ /** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */
export const SECURITY_SCHEMA: {[k: string]: SecurityContext} = {}; let _SECURITY_SCHEMA !: {[k: string]: SecurityContext};
function registerContext(ctx: SecurityContext, specs: string[]) { export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} {
for (const spec of specs) SECURITY_SCHEMA[spec.toLowerCase()] = ctx; if (!_SECURITY_SCHEMA) {
_SECURITY_SCHEMA = {};
// Case is insignificant below, all element and attribute names are lower-cased for lookup.
registerContext(SecurityContext.HTML, [
'iframe|srcdoc',
'*|innerHTML',
'*|outerHTML',
]);
registerContext(SecurityContext.STYLE, ['*|style']);
// NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
registerContext(SecurityContext.URL, [
'*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
]);
registerContext(SecurityContext.RESOURCE_URL, [
'applet|code',
'applet|codebase',
'base|href',
'embed|src',
'frame|src',
'head|profile',
'html|manifest',
'iframe|src',
'link|href',
'media|src',
'object|codebase',
'object|data',
'script|src',
]);
}
return _SECURITY_SCHEMA;
} }
// Case is insignificant below, all element and attribute names are lower-cased for lookup. function registerContext(ctx: SecurityContext, specs: string[]) {
for (const spec of specs) _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
registerContext(SecurityContext.HTML, [ }
'iframe|srcdoc',
'*|innerHTML',
'*|outerHTML',
]);
registerContext(SecurityContext.STYLE, ['*|style']);
// NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
registerContext(SecurityContext.URL, [
'*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
]);
registerContext(SecurityContext.RESOURCE_URL, [
'applet|code',
'applet|codebase',
'base|href',
'embed|src',
'frame|src',
'head|profile',
'html|manifest',
'iframe|src',
'link|href',
'media|src',
'object|codebase',
'object|data',
'script|src',
]);

View File

@ -57,7 +57,13 @@ const IDENT_EVENT_IDX = 10;
const TEMPLATE_ATTR_PREFIX = '*'; const TEMPLATE_ATTR_PREFIX = '*';
const CLASS_ATTR = 'class'; const CLASS_ATTR = 'class';
const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0]; let _TEXT_CSS_SELECTOR !: CssSelector;
function TEXT_CSS_SELECTOR(): CssSelector {
if (!_TEXT_CSS_SELECTOR) {
_TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
}
return _TEXT_CSS_SELECTOR;
}
export class TemplateParseError extends ParseError { export class TemplateParseError extends ParseError {
constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) { constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) {
@ -227,7 +233,7 @@ class TemplateParseVisitor implements html.Visitor {
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return null; } visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return null; }
visitText(text: html.Text, parent: ElementContext): any { visitText(text: html.Text, parent: ElementContext): any {
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !; const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !;
const valueNoNgsp = replaceNgsp(text.value); const valueNoNgsp = replaceNgsp(text.value);
const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !); const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !);
return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan !) : return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan !) :
@ -775,7 +781,7 @@ class NonBindableVisitor implements html.Visitor {
} }
visitText(text: html.Text, parent: ElementContext): t.TextAst { visitText(text: html.Text, parent: ElementContext): t.TextAst {
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !; const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !;
return new t.TextAst(text.value, ngContentIndex, text.sourceSpan !); return new t.TextAst(text.value, ngContentIndex, text.sourceSpan !);
} }

View File

@ -1439,9 +1439,6 @@
{ {
"name": "SyncAsync" "name": "SyncAsync"
}, },
{
"name": "TAG_DEFINITIONS"
},
{ {
"name": "TAG_TO_PLACEHOLDER_NAMES" "name": "TAG_TO_PLACEHOLDER_NAMES"
}, },
@ -1730,9 +1727,6 @@
{ {
"name": "_DEFAULT_SOURCE_LANG$1" "name": "_DEFAULT_SOURCE_LANG$1"
}, },
{
"name": "_DEFAULT_TAG_DEFINITION"
},
{ {
"name": "_DOCTYPE" "name": "_DOCTYPE"
}, },
@ -3509,6 +3503,9 @@
{ {
"name": "refCount" "name": "refCount"
}, },
{
"name": "registerContext"
},
{ {
"name": "registerModuleFactory" "name": "registerModuleFactory"
}, },