🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

angular-html-parser

Package Overview
Dependencies
Maintainers
4
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

angular-html-parser - npm Package Compare versions

Comparing version
9.3.0
to
10.0.0
+583
dist/index.d.ts
//#endregion
//#region ../compiler/src/ml_parser/tags.d.ts
/**
* @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
*/
declare enum TagContentType {
RAW_TEXT = 0,
ESCAPABLE_RAW_TEXT = 1,
PARSABLE_DATA = 2,
}
interface TagDefinition {
closedByParent: boolean;
implicitNamespacePrefix: string | null;
isVoid: boolean;
ignoreFirstLf: boolean;
canSelfClose: boolean;
preventNamespaceInheritance: boolean;
isClosedByChild(name: string): boolean;
getContentType(prefix?: string): TagContentType;
}
//#endregion
//#region ../compiler/src/parse_util.d.ts
declare class ParseLocation {
file: ParseSourceFile;
offset: number;
line: number;
col: number;
constructor(file: ParseSourceFile, offset: number, line: number, col: number);
toString(): string;
moveBy(delta: number): ParseLocation;
getContext(maxChars: number, maxLines: number): {
before: string;
after: string;
} | null;
}
declare class ParseSourceFile {
content: string;
url: string;
constructor(content: string, url: string);
}
declare class ParseSourceSpan {
start: ParseLocation;
end: ParseLocation;
fullStart: ParseLocation;
details: string | null;
/**
* Create an object that holds information about spans of tokens/nodes captured during
* lexing/parsing of text.
*
* @param start
* The location of the start of the span (having skipped leading trivia).
* Skipping leading trivia makes source-spans more "user friendly", since things like HTML
* elements will appear to begin at the start of the opening tag, rather than at the start of any
* leading trivia, which could include newlines.
*
* @param end
* The location of the end of the span.
*
* @param fullStart
* The start of the token without skipping the leading trivia.
* This is used by tooling that splits tokens further, such as extracting Angular interpolations
* from text tokens. Such tooling creates new source-spans relative to the original token's
* source-span. If leading trivia characters have been skipped then the new source-spans may be
* incorrectly offset.
*
* @param details
* Additional information (such as identifier names) that should be associated with the span.
*/
constructor(start: ParseLocation, end: ParseLocation, fullStart?: ParseLocation, details?: string | null);
toString(): string;
}
declare enum ParseErrorLevel {
WARNING = 0,
ERROR = 1,
}
declare class ParseError extends Error {
/** Location of the error. */
readonly span: ParseSourceSpan;
/** Error message. */
readonly msg: string;
/** Severity level of the error. */
readonly level: ParseErrorLevel;
/**
* Error that caused the error to be surfaced. For example, an error in a sub-expression that
* couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
*/
readonly relatedError?: unknown;
constructor(/** Location of the error. */
span: ParseSourceSpan, /** Error message. */
msg: string, /** Severity level of the error. */
level?: ParseErrorLevel,
/**
* Error that caused the error to be surfaced. For example, an error in a sub-expression that
* couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
*/
relatedError?: unknown);
contextualMessage(): string;
toString(): string;
}
//#endregion
//#region ../compiler/src/i18n/i18n_ast.d.ts
/**
* Describes the text contents of a placeholder as it appears in an ICU expression, including its
* source span information.
*/
interface MessagePlaceholder {
/** The text contents of the placeholder */
text: string;
/** The source span of the placeholder */
sourceSpan: ParseSourceSpan;
}
declare class Message {
nodes: Node$1[];
placeholders: {
[phName: string]: MessagePlaceholder;
};
placeholderToMessage: {
[phName: string]: Message;
};
meaning: string;
description: string;
customId: string;
sources: MessageSpan[];
id: string;
/** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
legacyIds: string[];
messageString: string;
/**
* @param nodes message AST
* @param placeholders maps placeholder names to static content and their source spans
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
* @param meaning
* @param description
* @param customId
*/
constructor(nodes: Node$1[], placeholders: {
[phName: string]: MessagePlaceholder;
}, placeholderToMessage: {
[phName: string]: Message;
}, meaning: string, description: string, customId: string);
}
interface MessageSpan {
filePath: string;
startLine: number;
startCol: number;
endLine: number;
endCol: number;
}
interface Node$1 {
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor$1, context?: any): any;
}
declare class Text$1 implements Node$1 {
value: string;
sourceSpan: ParseSourceSpan;
constructor(value: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor$1, context?: any): any;
}
declare class Container implements Node$1 {
children: Node$1[];
sourceSpan: ParseSourceSpan;
constructor(children: Node$1[], sourceSpan: ParseSourceSpan);
visit(visitor: Visitor$1, context?: any): any;
}
declare class Icu implements Node$1 {
expression: string;
type: string;
cases: {
[k: string]: Node$1;
};
sourceSpan: ParseSourceSpan;
expressionPlaceholder?: string;
constructor(expression: string, type: string, cases: {
[k: string]: Node$1;
}, sourceSpan: ParseSourceSpan, expressionPlaceholder?: string);
visit(visitor: Visitor$1, context?: any): any;
}
declare class TagPlaceholder implements Node$1 {
tag: string;
attrs: {
[k: string]: string;
};
startName: string;
closeName: string;
children: Node$1[];
isVoid: boolean;
sourceSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan | null;
endSourceSpan: ParseSourceSpan | null;
constructor(tag: string, attrs: {
[k: string]: string;
}, startName: string, closeName: string, children: Node$1[], isVoid: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan | null, endSourceSpan: ParseSourceSpan | null);
visit(visitor: Visitor$1, context?: any): any;
}
declare class Placeholder implements Node$1 {
value: string;
name: string;
sourceSpan: ParseSourceSpan;
constructor(value: string, name: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor$1, context?: any): any;
}
declare class IcuPlaceholder implements Node$1 {
value: Icu;
name: string;
sourceSpan: ParseSourceSpan;
/** Used to capture a message computed from a previous processing pass (see `setI18nRefs()`). */
previousMessage?: Message;
constructor(value: Icu, name: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor$1, context?: any): any;
}
declare class BlockPlaceholder implements Node$1 {
name: string;
parameters: string[];
startName: string;
closeName: string;
children: Node$1[];
sourceSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan | null;
endSourceSpan: ParseSourceSpan | null;
constructor(name: string, parameters: string[], startName: string, closeName: string, children: Node$1[], sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan | null, endSourceSpan: ParseSourceSpan | null);
visit(visitor: Visitor$1, context?: any): any;
}
/**
* Each HTML node that is affect by an i18n tag will also have an `i18n` property that is of type
* `I18nMeta`.
* This information is either a `Message`, which indicates it is the root of an i18n message, or a
* `Node`, which indicates is it part of a containing `Message`.
*/
type I18nMeta = Message | Node$1;
interface Visitor$1 {
visitText(text: Text$1, context?: any): any;
visitContainer(container: Container, context?: any): any;
visitIcu(icu: Icu, context?: any): any;
visitTagPlaceholder(ph: TagPlaceholder, context?: any): any;
visitPlaceholder(ph: Placeholder, context?: any): any;
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any;
visitBlockPlaceholder(ph: BlockPlaceholder, context?: any): any;
}
//#endregion
//#region ../compiler/src/ml_parser/tokens.d.ts
declare const enum TokenType {
TAG_OPEN_START = 0,
TAG_OPEN_END = 1,
TAG_OPEN_END_VOID = 2,
TAG_CLOSE = 3,
INCOMPLETE_TAG_OPEN = 4,
TEXT = 5,
ESCAPABLE_RAW_TEXT = 6,
RAW_TEXT = 7,
INTERPOLATION = 8,
ENCODED_ENTITY = 9,
COMMENT_START = 10,
COMMENT_END = 11,
CDATA_START = 12,
CDATA_END = 13,
ATTR_NAME = 14,
ATTR_QUOTE = 15,
ATTR_VALUE_TEXT = 16,
ATTR_VALUE_INTERPOLATION = 17,
DOC_TYPE_START = 18,
DOC_TYPE_END = 19,
EXPANSION_FORM_START = 20,
EXPANSION_CASE_VALUE = 21,
EXPANSION_CASE_EXP_START = 22,
EXPANSION_CASE_EXP_END = 23,
EXPANSION_FORM_END = 24,
BLOCK_OPEN_START = 25,
BLOCK_OPEN_END = 26,
BLOCK_CLOSE = 27,
BLOCK_PARAMETER = 28,
INCOMPLETE_BLOCK_OPEN = 29,
LET_START = 30,
LET_VALUE = 31,
LET_END = 32,
INCOMPLETE_LET = 33,
COMPONENT_OPEN_START = 34,
COMPONENT_OPEN_END = 35,
COMPONENT_OPEN_END_VOID = 36,
COMPONENT_CLOSE = 37,
INCOMPLETE_COMPONENT_OPEN = 38,
DIRECTIVE_NAME = 39,
DIRECTIVE_OPEN = 40,
DIRECTIVE_CLOSE = 41,
EOF = 42,
}
type InterpolatedTextToken = TextToken | InterpolationToken | EncodedEntityToken;
type InterpolatedAttributeToken = AttributeValueTextToken | AttributeValueInterpolationToken | EncodedEntityToken;
interface TokenBase {
type: TokenType;
parts: string[];
sourceSpan: ParseSourceSpan;
}
interface TextToken extends TokenBase {
type: TokenType.TEXT | TokenType.ESCAPABLE_RAW_TEXT | TokenType.RAW_TEXT;
parts: [text: string];
}
interface InterpolationToken extends TokenBase {
type: TokenType.INTERPOLATION;
parts: [startMarker: string, expression: string, endMarker: string] | [startMarker: string, expression: string];
}
interface EncodedEntityToken extends TokenBase {
type: TokenType.ENCODED_ENTITY;
parts: [decoded: string, encoded: string];
}
interface AttributeValueTextToken extends TokenBase {
type: TokenType.ATTR_VALUE_TEXT;
parts: [value: string];
}
interface AttributeValueInterpolationToken extends TokenBase {
type: TokenType.ATTR_VALUE_INTERPOLATION;
parts: [startMarker: string, expression: string, endMarker: string] | [startMarker: string, expression: string];
}
declare namespace ast_d_exports {
export { Attribute, Block, BlockParameter, CDATA, Comment, Component, Directive, DocType, Element, Expansion, ExpansionCase, LetDeclaration, Node, NodeWithI18n, RecursiveVisitor, Text, Visitor, visitAll };
}
interface BaseNode {
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context: any): any;
}
type Node = Attribute | CDATA | Comment | DocType | Element | Expansion | ExpansionCase | Text | Block | BlockParameter | LetDeclaration | Component | Directive;
declare abstract class NodeWithI18n implements BaseNode {
sourceSpan: ParseSourceSpan;
i18n?: I18nMeta;
constructor(sourceSpan: ParseSourceSpan, i18n?: I18nMeta);
abstract visit(visitor: Visitor, context: any): any;
}
declare class Text extends NodeWithI18n {
value: string;
tokens: InterpolatedTextToken[];
constructor(value: string, sourceSpan: ParseSourceSpan, tokens: InterpolatedTextToken[], i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "text";
}
declare class CDATA extends NodeWithI18n {
value: string;
tokens: InterpolatedTextToken[];
constructor(value: string, sourceSpan: ParseSourceSpan, tokens: InterpolatedTextToken[], i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "cdata";
}
declare class Expansion extends NodeWithI18n {
switchValue: string;
type: string;
cases: ExpansionCase[];
switchValueSourceSpan: ParseSourceSpan;
constructor(switchValue: string, type: string, cases: ExpansionCase[], sourceSpan: ParseSourceSpan, switchValueSourceSpan: ParseSourceSpan, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "expansion";
}
declare class ExpansionCase implements BaseNode {
value: string;
expression: Node[];
sourceSpan: ParseSourceSpan;
valueSourceSpan: ParseSourceSpan;
expSourceSpan: ParseSourceSpan;
constructor(value: string, expression: Node[], sourceSpan: ParseSourceSpan, valueSourceSpan: ParseSourceSpan, expSourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly kind = "expansionCase";
}
declare class Attribute extends NodeWithI18n {
name: string;
value: string;
readonly keySpan: ParseSourceSpan | undefined;
valueSpan: ParseSourceSpan | undefined;
valueTokens: InterpolatedAttributeToken[] | undefined;
constructor(name: string, value: string, sourceSpan: ParseSourceSpan, keySpan: ParseSourceSpan | undefined, valueSpan: ParseSourceSpan | undefined, valueTokens: InterpolatedAttributeToken[] | undefined, i18n: I18nMeta | undefined);
visit(visitor: Visitor, context: any): any;
readonly kind = "attribute";
get nameSpan(): ParseSourceSpan;
}
declare class Element extends NodeWithI18n {
name: string;
attrs: Attribute[];
readonly directives: Directive[];
children: Node[];
readonly isSelfClosing: boolean;
startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
nameSpan: ParseSourceSpan | null;
readonly isVoid: boolean;
constructor(name: string, attrs: Attribute[], directives: Directive[], children: Node[], isSelfClosing: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan: ParseSourceSpan | null, nameSpan: ParseSourceSpan | null, isVoid: boolean, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "element";
}
declare class Comment implements BaseNode {
value: string | null;
sourceSpan: ParseSourceSpan;
constructor(value: string | null, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly kind = "comment";
}
declare class DocType implements BaseNode {
value: string | null;
sourceSpan: ParseSourceSpan;
constructor(value: string | null, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly kind = "docType";
}
declare class Block extends NodeWithI18n {
name: string;
parameters: BlockParameter[];
children: Node[];
nameSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
constructor(name: string, parameters: BlockParameter[], children: Node[], sourceSpan: ParseSourceSpan, nameSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "block";
}
declare class Component extends NodeWithI18n {
readonly componentName: string;
readonly tagName: string | null;
readonly fullName: string;
attrs: Attribute[];
readonly directives: Directive[];
readonly children: Node[];
readonly isSelfClosing: boolean;
readonly startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
constructor(componentName: string, tagName: string | null, fullName: string, attrs: Attribute[], directives: Directive[], children: Node[], isSelfClosing: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly kind = "component";
}
declare class Directive implements BaseNode {
readonly name: string;
readonly attrs: Attribute[];
readonly sourceSpan: ParseSourceSpan;
readonly startSourceSpan: ParseSourceSpan;
readonly endSourceSpan: ParseSourceSpan | null;
constructor(name: string, attrs: Attribute[], sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null);
visit(visitor: Visitor, context: any): any;
readonly kind = "directive";
}
declare class BlockParameter implements BaseNode {
expression: string;
sourceSpan: ParseSourceSpan;
constructor(expression: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly kind = "blockParameter";
readonly startSourceSpan: null;
readonly endSourceSpan: null;
}
declare class LetDeclaration implements BaseNode {
name: string;
value: string;
sourceSpan: ParseSourceSpan;
readonly nameSpan: ParseSourceSpan;
valueSpan: ParseSourceSpan;
constructor(name: string, value: string, sourceSpan: ParseSourceSpan, nameSpan: ParseSourceSpan, valueSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly kind = "letDeclaration";
readonly startSourceSpan: null;
readonly endSourceSpan: null;
}
interface Visitor {
visit?(node: Node, context: any): any;
visitElement(element: Element, context: any): any;
visitAttribute(attribute: Attribute, context: any): any;
visitText(text: Text, context: any): any;
visitCdata(text: CDATA, context: any): any;
visitComment(comment: Comment, context: any): any;
visitDocType(docType: DocType, context: any): any;
visitExpansion(expansion: Expansion, context: any): any;
visitExpansionCase(expansionCase: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(parameter: BlockParameter, context: any): any;
visitLetDeclaration(decl: LetDeclaration, context: any): any;
visitComponent(component: Component, context: any): any;
visitDirective(directive: Directive, context: any): any;
}
declare function visitAll(visitor: Visitor, nodes: Node[], context?: any): any[];
declare class RecursiveVisitor implements Visitor {
constructor();
visitElement(ast: Element, context: any): any;
visitAttribute(ast: Attribute, context: any): any;
visitText(ast: Text, context: any): any;
visitCdata(ast: CDATA, context: any): any;
visitComment(ast: Comment, context: any): any;
visitDocType(ast: DocType, context: any): any;
visitExpansion(ast: Expansion, context: any): any;
visitExpansionCase(ast: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(ast: BlockParameter, context: any): any;
visitLetDeclaration(decl: LetDeclaration, context: any): void;
visitComponent(component: Component, context: any): void;
visitDirective(directive: Directive, context: any): void;
private visitChildren;
}
//#endregion
//#region ../compiler/src/ml_parser/parser.d.ts
declare class ParseTreeResult {
rootNodes: Node[];
errors: ParseError[];
constructor(rootNodes: Node[], errors: ParseError[]);
}
//#endregion
//#region ../compiler/src/ml_parser/html_tags.d.ts
declare class HtmlTagDefinition implements TagDefinition {
private closedByChildren;
private contentType;
closedByParent: boolean;
implicitNamespacePrefix: string | null;
isVoid: boolean;
ignoreFirstLf: boolean;
canSelfClose: boolean;
preventNamespaceInheritance: boolean;
constructor({
closedByChildren,
implicitNamespacePrefix,
contentType,
closedByParent,
isVoid,
ignoreFirstLf,
preventNamespaceInheritance,
canSelfClose
}?: {
closedByChildren?: string[];
closedByParent?: boolean;
implicitNamespacePrefix?: string;
contentType?: TagContentType | {
default: TagContentType;
[namespace: string]: TagContentType;
};
isVoid?: boolean;
ignoreFirstLf?: boolean;
preventNamespaceInheritance?: boolean;
canSelfClose?: boolean;
});
isClosedByChild(name: string): boolean;
getContentType(prefix?: string): TagContentType;
}
declare function getHtmlTagDefinition(tagName: string): HtmlTagDefinition;
//#endregion
//#region src/index.d.ts
interface ParseOptions {
/**
* any element can self close
*
* defaults to false
*/
canSelfClose?: boolean;
/**
* support [`htm`](https://github.com/developit/htm) component closing tags (`<//>`)
*
* defaults to false
*/
allowHtmComponentClosingTags?: boolean;
/**
* do not lowercase tag names before querying their tag definitions
*
* defaults to false
*/
isTagNameCaseSensitive?: boolean;
/**
* customize tag content type
*
* defaults to the content type defined in the HTML spec
*/
getTagContentType?: (tagName: string, prefix: string, hasParent: boolean, attrs: Array<{
prefix: string;
name: string;
value?: string;
}>) => void | TagContentType;
/**
* tokenize angular control flow block syntax
*/
tokenizeAngularBlocks?: boolean;
/**
* tokenize angular let declaration syntax
*/
tokenizeAngularLetDeclaration?: boolean;
/**
* enable angular selectorless syntax
*/
enableAngularSelectorlessSyntax?: boolean;
}
declare function parse(input: string, options?: ParseOptions): ParseTreeResult;
//#endregion
export { type ast_d_exports as Ast, ParseLocation, ParseOptions, ParseSourceFile, ParseSourceSpan, type ParseTreeResult, RecursiveVisitor, TagContentType, getHtmlTagDefinition, parse, visitAll };

Sorry, the diff of this file is too big to display

+9
-16
{
"name": "angular-html-parser",
"version": "9.3.0",
"version": "10.0.0",
"description": "A HTML parser extracted from Angular with some modifications",

@@ -14,4 +14,4 @@ "repository": "https://github.com/prettier/angular-html-parser/blob/HEAD/packages/angular-html-parser",

".": {
"types": "./lib/angular-html-parser/src/index.d.ts",
"default": "./lib/angular-html-parser/src/index.js"
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},

@@ -23,6 +23,3 @@ "./*": "./*"

"prepublish": "yarn run build",
"build": "yarn clean && yarn build-lib && yarn codemod",
"clean": "del-cli ./lib",
"build-lib": "tsc -p tsconfig.build.json",
"codemod": "node ./node_modules/jscodeshift/bin/jscodeshift.js -t postbuild-codemod.ts lib --extensions=js,ts --parser=ts",
"build": "tsdown",
"test": "vitest",

@@ -34,13 +31,9 @@ "release": "release-it",

"devDependencies": {
"@types/node": "24.3.0",
"@types/node": "24.7.1",
"@vitest/coverage-v8": "3.2.4",
"del-cli": "6.0.0",
"jasmine": "5.10.0",
"jscodeshift": "17.3.0",
"prettier": "3.6.2",
"release-it": "19.0.4",
"standard-version": "9.5.0",
"ts-node": "10.9.2",
"release-it": "19.0.5",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.2",
"tsdown": "0.15.6",
"typescript": "5.9.3",
"vitest": "3.2.4"

@@ -52,3 +45,3 @@ },

"files": [
"lib",
"dist",
"ThirdPartyNoticeText.txt"

@@ -55,0 +48,0 @@ ],

import { TagContentType } from "../../compiler/src/ml_parser/tags.js";
import { ParseTreeResult } from "../../compiler/src/ml_parser/parser.js";
export interface ParseOptions {
/**
* any element can self close
*
* defaults to false
*/
canSelfClose?: boolean;
/**
* support [`htm`](https://github.com/developit/htm) component closing tags (`<//>`)
*
* defaults to false
*/
allowHtmComponentClosingTags?: boolean;
/**
* do not lowercase tag names before querying their tag definitions
*
* defaults to false
*/
isTagNameCaseSensitive?: boolean;
/**
* customize tag content type
*
* defaults to the content type defined in the HTML spec
*/
getTagContentType?: (tagName: string, prefix: string, hasParent: boolean, attrs: Array<{
prefix: string;
name: string;
value?: string;
}>) => void | TagContentType;
/**
* tokenize angular control flow block syntax
*/
tokenizeAngularBlocks?: boolean;
/**
* tokenize angular let declaration syntax
*/
tokenizeAngularLetDeclaration?: boolean;
/**
* enable angular selectorless syntax
*/
enableAngularSelectorlessSyntax?: boolean;
}
export declare function parse(input: string, options?: ParseOptions): ParseTreeResult;
export { TagContentType };
export { RecursiveVisitor, visitAll, } from "../../compiler/src/ml_parser/ast.js";
export { ParseSourceSpan, ParseLocation, ParseSourceFile, } from "../../compiler/src/parse_util.js";
export { getHtmlTagDefinition } from "../../compiler/src/ml_parser/html_tags.js";
import { HtmlParser } from "../../compiler/src/ml_parser/html_parser.js";
import { TagContentType } from "../../compiler/src/ml_parser/tags.js";
let parser = null;
const getParser = () => {
if (!parser) {
parser = new HtmlParser();
}
return parser;
};
export function parse(input, options = {}) {
const { canSelfClose = false, allowHtmComponentClosingTags = false, isTagNameCaseSensitive = false, getTagContentType, tokenizeAngularBlocks = false, tokenizeAngularLetDeclaration = false, enableAngularSelectorlessSyntax = false, } = options;
return getParser().parse(input, "angular-html-parser", {
tokenizeExpansionForms: tokenizeAngularBlocks,
interpolationConfig: undefined,
canSelfClose,
allowHtmComponentClosingTags,
tokenizeBlocks: tokenizeAngularBlocks,
tokenizeLet: tokenizeAngularLetDeclaration,
selectorlessEnabled: enableAngularSelectorlessSyntax,
}, isTagNameCaseSensitive, getTagContentType);
}
// For prettier
export { TagContentType };
export { RecursiveVisitor, visitAll, } from "../../compiler/src/ml_parser/ast.js";
export { ParseSourceSpan, ParseLocation, ParseSourceFile, } from "../../compiler/src/parse_util.js";
export { getHtmlTagDefinition } from "../../compiler/src/ml_parser/html_tags.js";
/**
* @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
*/
export declare function assertInterpolationSymbols(identifier: string, value: any): void;
/**
* @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 UNUSABLE_INTERPOLATION_REGEXPS = [
/@/, // control flow reserved symbol
/^\s*$/, // empty
/[<>]/, // html tag
/^[{}]$/, // i18n expansion
/&(#|[a-z])/i, // character reference,
/^\/\//, // comment
];
export function assertInterpolationSymbols(identifier, value) {
if (value != null && !(Array.isArray(value) && value.length == 2)) {
throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
}
else if (value != null) {
const start = value[0];
const end = value[1];
// Check for unusable interpolation symbols
UNUSABLE_INTERPOLATION_REGEXPS.forEach((regexp) => {
if (regexp.test(start) || regexp.test(end)) {
throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
}
});
}
}
/**
* @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
*/
export declare const $EOF = 0;
export declare const $BSPACE = 8;
export declare const $TAB = 9;
export declare const $LF = 10;
export declare const $VTAB = 11;
export declare const $FF = 12;
export declare const $CR = 13;
export declare const $SPACE = 32;
export declare const $BANG = 33;
export declare const $DQ = 34;
export declare const $HASH = 35;
export declare const $$ = 36;
export declare const $PERCENT = 37;
export declare const $AMPERSAND = 38;
export declare const $SQ = 39;
export declare const $LPAREN = 40;
export declare const $RPAREN = 41;
export declare const $STAR = 42;
export declare const $PLUS = 43;
export declare const $COMMA = 44;
export declare const $MINUS = 45;
export declare const $PERIOD = 46;
export declare const $SLASH = 47;
export declare const $COLON = 58;
export declare const $SEMICOLON = 59;
export declare const $LT = 60;
export declare const $EQ = 61;
export declare const $GT = 62;
export declare const $QUESTION = 63;
export declare const $0 = 48;
export declare const $7 = 55;
export declare const $9 = 57;
export declare const $A = 65;
export declare const $E = 69;
export declare const $F = 70;
export declare const $X = 88;
export declare const $Z = 90;
export declare const $LBRACKET = 91;
export declare const $BACKSLASH = 92;
export declare const $RBRACKET = 93;
export declare const $CARET = 94;
export declare const $_ = 95;
export declare const $a = 97;
export declare const $b = 98;
export declare const $e = 101;
export declare const $f = 102;
export declare const $n = 110;
export declare const $r = 114;
export declare const $t = 116;
export declare const $u = 117;
export declare const $v = 118;
export declare const $x = 120;
export declare const $z = 122;
export declare const $LBRACE = 123;
export declare const $BAR = 124;
export declare const $RBRACE = 125;
export declare const $NBSP = 160;
export declare const $PIPE = 124;
export declare const $TILDA = 126;
export declare const $AT = 64;
export declare const $BT = 96;
export declare function isWhitespace(code: number): boolean;
export declare function isDigit(code: number): boolean;
export declare function isAsciiLetter(code: number): boolean;
export declare function isAsciiHexDigit(code: number): boolean;
export declare function isNewLine(code: number): boolean;
export declare function isOctalDigit(code: number): boolean;
export declare function isQuote(code: number): boolean;
/**
* @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
*/
export const $EOF = 0;
export const $BSPACE = 8;
export const $TAB = 9;
export const $LF = 10;
export const $VTAB = 11;
export const $FF = 12;
export const $CR = 13;
export const $SPACE = 32;
export const $BANG = 33;
export const $DQ = 34;
export const $HASH = 35;
export const $$ = 36;
export const $PERCENT = 37;
export const $AMPERSAND = 38;
export const $SQ = 39;
export const $LPAREN = 40;
export const $RPAREN = 41;
export const $STAR = 42;
export const $PLUS = 43;
export const $COMMA = 44;
export const $MINUS = 45;
export const $PERIOD = 46;
export const $SLASH = 47;
export const $COLON = 58;
export const $SEMICOLON = 59;
export const $LT = 60;
export const $EQ = 61;
export const $GT = 62;
export const $QUESTION = 63;
export const $0 = 48;
export const $7 = 55;
export const $9 = 57;
export const $A = 65;
export const $E = 69;
export const $F = 70;
export const $X = 88;
export const $Z = 90;
export const $LBRACKET = 91;
export const $BACKSLASH = 92;
export const $RBRACKET = 93;
export const $CARET = 94;
export const $_ = 95;
export const $a = 97;
export const $b = 98;
export const $e = 101;
export const $f = 102;
export const $n = 110;
export const $r = 114;
export const $t = 116;
export const $u = 117;
export const $v = 118;
export const $x = 120;
export const $z = 122;
export const $LBRACE = 123;
export const $BAR = 124;
export const $RBRACE = 125;
export const $NBSP = 160;
export const $PIPE = 124;
export const $TILDA = 126;
export const $AT = 64;
export const $BT = 96;
export function isWhitespace(code) {
return (code >= $TAB && code <= $SPACE) || code == $NBSP;
}
export function isDigit(code) {
return $0 <= code && code <= $9;
}
export function isAsciiLetter(code) {
return (code >= $a && code <= $z) || (code >= $A && code <= $Z);
}
export function isAsciiHexDigit(code) {
return (code >= $a && code <= $f) || (code >= $A && code <= $F) || isDigit(code);
}
export function isNewLine(code) {
return code === $LF || code === $CR;
}
export function isOctalDigit(code) {
return $0 <= code && code <= $7;
}
export function isQuote(code) {
return code === $SQ || code === $DQ || code === $BT;
}
/**
* @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
*/
export declare const emitDistinctChangesOnlyDefaultValue = true;
export declare enum ViewEncapsulation {
Emulated = 0,
None = 2,
ShadowDom = 3,
IsolatedShadowDom = 4
}
export declare enum ChangeDetectionStrategy {
OnPush = 0,
Default = 1
}
export interface Input {
alias?: string;
required?: boolean;
transform?: (value: any) => any;
isSignal: boolean;
}
/** Flags describing an input for a directive. */
export declare enum InputFlags {
None = 0,
SignalBased = 1,
HasDecoratorInputTransform = 2
}
export interface Output {
alias?: string;
}
export interface HostBinding {
hostPropertyName?: string;
}
export interface HostListener {
eventName?: string;
args?: string[];
}
export interface SchemaMetadata {
name: string;
}
export declare const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata;
export declare const NO_ERRORS_SCHEMA: SchemaMetadata;
export interface Type extends Function {
new (...args: any[]): any;
}
export declare const Type: FunctionConstructor;
export declare enum SecurityContext {
NONE = 0,
HTML = 1,
STYLE = 2,
SCRIPT = 3,
URL = 4,
RESOURCE_URL = 5
}
/**
* Injection flags for DI.
*/
export declare const enum InjectFlags {
Default = 0,
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
Host = 1,
/** Don't descend into ancestors of the node requesting injection. */
Self = 2,
/** Skip the node that is requesting injection. */
SkipSelf = 4,
/** Inject `defaultValue` instead if token not found. */
Optional = 8,
/**
* This token is being injected into a pipe.
* @internal
*/
ForPipe = 16
}
export declare enum MissingTranslationStrategy {
Error = 0,
Warning = 1,
Ignore = 2
}
/**
* Flags used to generate R3-style CSS Selectors. They are pasted from
* core/src/render3/projection.ts because they cannot be referenced directly.
*/
export declare const enum SelectorFlags {
/** Indicates this is the beginning of a new negative selector */
NOT = 1,
/** Mode for matching attributes */
ATTRIBUTE = 2,
/** Mode for matching tag names */
ELEMENT = 4,
/** Mode for matching class names */
CLASS = 8
}
export type R3CssSelector = (string | SelectorFlags)[];
export type R3CssSelectorList = R3CssSelector[];
export declare function parseSelectorToR3Selector(selector: string | null): R3CssSelectorList;
/**
* Flags passed into template functions to determine which blocks (i.e. creation, update)
* should be executed.
*
* Typically, a template runs both the creation block and the update block on initialization and
* subsequent runs only execute the update block. However, dynamically created views require that
* the creation block be executed separately from the update block (for backwards compat).
*/
export declare const enum RenderFlags {
Create = 1,
Update = 2
}
/**
* A set of marker values to be used in the attributes arrays. These markers indicate that some
* items are not regular attributes and the processing should be adapted accordingly.
*/
export declare const enum AttributeMarker {
/**
* Marker indicates that the following 3 values in the attributes array are:
* namespaceUri, attributeName, attributeValue
* in that order.
*/
NamespaceURI = 0,
/**
* Signals class declaration.
*
* Each value following `Classes` designates a class name to include on the element.
* ## Example:
*
* Given:
* ```html
* <div class="foo bar baz">...</div>
* ```
*
* the generated code is:
* ```ts
* var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz'];
* ```
*/
Classes = 1,
/**
* Signals style declaration.
*
* Each pair of values following `Styles` designates a style name and value to include on the
* element.
* ## Example:
*
* Given:
* ```html
* <div style="width:100px; height:200px; color:red">...</div>
* ```
*
* the generated code is:
* ```ts
* var _c1 = [AttributeMarker.Styles, 'width', '100px', 'height'. '200px', 'color', 'red'];
* ```
*/
Styles = 2,
/**
* Signals that the following attribute names were extracted from input or output bindings.
*
* For example, given the following HTML:
*
* ```html
* <div moo="car" [foo]="exp" (bar)="doSth()">
* ```
*
* the generated code is:
*
* ```ts
* var _c1 = ['moo', 'car', AttributeMarker.Bindings, 'foo', 'bar'];
* ```
*/
Bindings = 3,
/**
* Signals that the following attribute names were hoisted from an inline-template declaration.
*
* For example, given the following HTML:
*
* ```html
* <div *ngFor="let value of values; trackBy:trackBy" dirA [dirB]="value">
* ```
*
* the generated code for the `template()` instruction would include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB', AttributeMarker.Template, 'ngFor', 'ngForOf',
* 'ngForTrackBy', 'let-value']
* ```
*
* while the generated code for the `element()` instruction inside the template function would
* include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB']
* ```
*/
Template = 4,
/**
* Signals that the following attribute is `ngProjectAs` and its value is a parsed `CssSelector`.
*
* For example, given the following HTML:
*
* ```html
* <h1 attr="value" ngProjectAs="[title]">
* ```
*
* the generated code for the `element()` instruction would include:
*
* ```
* ['attr', 'value', AttributeMarker.ProjectAs, ['', 'title', '']]
* ```
*/
ProjectAs = 5,
/**
* Signals that the following attribute will be translated by runtime i18n
*
* For example, given the following HTML:
*
* ```html
* <div moo="car" foo="value" i18n-foo [bar]="binding" i18n-bar>
* ```
*
* the generated code is:
*
* ```ts
* var _c1 = ['moo', 'car', AttributeMarker.I18n, 'foo', 'bar'];
* ```
*/
I18n = 6
}
/**
* @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
*/
// Attention:
// This file duplicates types and values from @angular/core
// so that we are able to make @angular/compiler independent of @angular/core.
// This is important to prevent a build cycle, as @angular/core needs to
// be compiled with the compiler.
import { CssSelector } from "./directive_matching.js";
// Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
// explicitly set.
export const emitDistinctChangesOnlyDefaultValue = true;
export var ViewEncapsulation;
(function (ViewEncapsulation) {
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
ViewEncapsulation[ViewEncapsulation["IsolatedShadowDom"] = 4] = "IsolatedShadowDom";
})(ViewEncapsulation || (ViewEncapsulation = {}));
export var ChangeDetectionStrategy;
(function (ChangeDetectionStrategy) {
ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
/** Flags describing an input for a directive. */
export var InputFlags;
(function (InputFlags) {
InputFlags[InputFlags["None"] = 0] = "None";
InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
})(InputFlags || (InputFlags = {}));
export const CUSTOM_ELEMENTS_SCHEMA = {
name: 'custom-elements',
};
export const NO_ERRORS_SCHEMA = {
name: 'no-errors-schema',
};
export const Type = Function;
export 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 = {}));
export 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 /* SelectorFlags.CLASS */, ...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 /* SelectorFlags.CLASS */, ...selector.classNames]
: [];
if (selector.element) {
return [
1 /* SelectorFlags.NOT */ | 4 /* SelectorFlags.ELEMENT */,
selector.element,
...selector.attrs,
...classes,
];
}
else if (selector.attrs.length) {
return [1 /* SelectorFlags.NOT */ | 2 /* SelectorFlags.ATTRIBUTE */, ...selector.attrs, ...classes];
}
else {
return selector.classNames && selector.classNames.length
? [1 /* SelectorFlags.NOT */ | 8 /* SelectorFlags.CLASS */, ...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);
}
export function parseSelectorToR3Selector(selector) {
return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
}
/**
* @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
*/
/**
* A css selector contains an element name,
* css classes and attribute/value pairs with the purpose
* of selecting subsets out of them.
*/
export declare class CssSelector {
element: string | null;
classNames: string[];
/**
* The selectors are encoded in pairs where:
* - even locations are attribute names
* - odd locations are attribute values.
*
* Example:
* Selector: `[key1=value1][key2]` would parse to:
* ```
* ['key1', 'value1', 'key2', '']
* ```
*/
attrs: string[];
notSelectors: CssSelector[];
static parse(selector: string): CssSelector[];
/**
* Unescape `\$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* but we might want to match an attribute that contains `$`.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to unescape.
* @returns the unescaped string.
*/
unescapeAttribute(attr: string): string;
/**
* Escape `$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* with this method we are escaping `$` with `\$'.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to escape.
* @returns the escaped string.
*/
escapeAttribute(attr: string): string;
isElementSelector(): boolean;
hasElementSelector(): boolean;
setElement(element?: string | null): void;
getAttrs(): string[];
addAttribute(name: string, value?: string): void;
addClassName(name: string): void;
toString(): string;
}
/**
* Reads a list of CssSelectors and allows to calculate which ones
* are contained in a given CssSelector.
*/
export declare class SelectorMatcher<T = any> {
static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher<null>;
private _elementMap;
private _elementPartialMap;
private _classMap;
private _classPartialMap;
private _attrValueMap;
private _attrValuePartialMap;
private _listContexts;
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: T): void;
/**
* Add an object that can be found later on by calling `match`.
* @param cssSelector A css selector
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
*/
private _addSelectable;
private _addTerminal;
private _addPartial;
/**
* Find the objects that have been added via `addSelectable`
* whose css selector is contained in the given css selector.
* @param cssSelector A css selector
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
* @return boolean true if a match was found
*/
match(cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: T) => void) | null): boolean;
/** @internal */
_matchTerminal(map: Map<string, SelectorContext<T>[]>, name: string, cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: any) => void) | null): boolean;
/** @internal */
_matchPartial(map: Map<string, SelectorMatcher<T>>, name: string, cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: any) => void) | null): boolean;
}
export declare class SelectorListContext {
selectors: CssSelector[];
alreadyMatched: boolean;
constructor(selectors: CssSelector[]);
}
export declare class SelectorContext<T = any> {
selector: CssSelector;
cbContext: T;
listContext: SelectorListContext;
notSelectors: CssSelector[];
constructor(selector: CssSelector, cbContext: T, listContext: SelectorListContext);
finalize(cssSelector: CssSelector, callback: ((c: CssSelector, a: T) => void) | null): boolean;
}
export declare class SelectorlessMatcher<T = unknown> {
private registry;
constructor(registry: Map<string, T[]>);
match(name: string): T[];
}
/**
* @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 _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not("
'(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#";
// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
// 4: attribute; 5: attribute_string; 6: attribute_value
'(?:\\[([-.\\w*\\\\$]+)(?:=(["\']?)([^\\]"\']*)\\5)?\\])|' + // "[name]", "[name=value]",
// "[name="value"]",
// "[name='value']"
'(\\))|' + // 7: ")"
'(\\s*,\\s*)', // 8: ","
'g');
/**
* A css selector contains an element name,
* css classes and attribute/value pairs with the purpose
* of selecting subsets out of them.
*/
export class CssSelector {
constructor() {
this.element = null;
this.classNames = [];
/**
* The selectors are encoded in pairs where:
* - even locations are attribute names
* - odd locations are attribute values.
*
* Example:
* Selector: `[key1=value1][key2]` would parse to:
* ```
* ['key1', 'value1', 'key2', '']
* ```
*/
this.attrs = [];
this.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 /* SelectorRegexp.NOT */]) {
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 /* SelectorRegexp.TAG */];
if (tag) {
const prefix = match[3 /* SelectorRegexp.PREFIX */];
if (prefix === '#') {
// #hash
current.addAttribute('id', tag.slice(1));
}
else if (prefix === '.') {
// Class
current.addClassName(tag.slice(1));
}
else {
// Element
current.setElement(tag);
}
}
const attribute = match[4 /* SelectorRegexp.ATTRIBUTE */];
if (attribute) {
current.addAttribute(current.unescapeAttribute(attribute), match[6 /* SelectorRegexp.ATTRIBUTE_VALUE */]);
}
if (match[7 /* SelectorRegexp.NOT_END */]) {
inNot = false;
current = cssSelector;
}
if (match[8 /* SelectorRegexp.SEPARATOR */]) {
if (inNot) {
throw new Error('Multiple selectors in :not are not supported');
}
_addResult(results, cssSelector);
cssSelector = current = new CssSelector();
}
}
_addResult(results, cssSelector);
return results;
}
/**
* Unescape `\$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* but we might want to match an attribute that contains `$`.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to unescape.
* @returns the unescaped string.
*/
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;
}
/**
* Escape `$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* with this method we are escaping `$` with `\$'.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to escape.
* @returns the escaped string.
*/
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;
}
}
/**
* Reads a list of CssSelectors and allows to calculate which ones
* are contained in a given CssSelector.
*/
export class SelectorMatcher {
constructor() {
this._elementMap = new Map();
this._elementPartialMap = new Map();
this._classMap = new Map();
this._classPartialMap = new Map();
this._attrValueMap = new Map();
this._attrValuePartialMap = new Map();
this._listContexts = [];
}
static createNotMatcher(notSelectors) {
const notMatcher = new SelectorMatcher();
notMatcher.addSelectables(notSelectors, null);
return notMatcher;
}
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);
}
}
/**
* Add an object that can be found later on by calling `match`.
* @param cssSelector A css selector
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
*/
_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;
}
/**
* Find the objects that have been added via `addSelectable`
* whose css selector is contained in the given css selector.
* @param cssSelector A css selector
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
* @return boolean true if a match was found
*/
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;
}
/** @internal */
_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;
}
/** @internal */
_matchPartial(map, name, cssSelector, matchedCallback) {
if (!map || typeof name !== 'string') {
return false;
}
const nestedSelector = map.get(name);
if (!nestedSelector) {
return false;
}
// TODO(perf): get rid of recursion and measure again
// TODO(perf): don't pass the whole selector into the recursion,
// but only the not processed parts
return nestedSelector.match(cssSelector, matchedCallback);
}
}
export class SelectorListContext {
constructor(selectors) {
this.selectors = selectors;
this.alreadyMatched = false;
}
}
// Store context to pass back selector and context when a selector is matched
export class SelectorContext {
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;
}
}
export class SelectorlessMatcher {
constructor(registry) {
this.registry = registry;
}
match(name) {
return this.registry.has(name) ? this.registry.get(name) : [];
}
}
/**
* @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
*/
import { ParseSourceSpan } from '../parse_util';
/**
* Describes the text contents of a placeholder as it appears in an ICU expression, including its
* source span information.
*/
export interface MessagePlaceholder {
/** The text contents of the placeholder */
text: string;
/** The source span of the placeholder */
sourceSpan: ParseSourceSpan;
}
export declare class Message {
nodes: Node[];
placeholders: {
[phName: string]: MessagePlaceholder;
};
placeholderToMessage: {
[phName: string]: Message;
};
meaning: string;
description: string;
customId: string;
sources: MessageSpan[];
id: string;
/** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
legacyIds: string[];
messageString: string;
/**
* @param nodes message AST
* @param placeholders maps placeholder names to static content and their source spans
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
* @param meaning
* @param description
* @param customId
*/
constructor(nodes: Node[], placeholders: {
[phName: string]: MessagePlaceholder;
}, placeholderToMessage: {
[phName: string]: Message;
}, meaning: string, description: string, customId: string);
}
export interface MessageSpan {
filePath: string;
startLine: number;
startCol: number;
endLine: number;
endCol: number;
}
export interface Node {
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context?: any): any;
}
export declare class Text implements Node {
value: string;
sourceSpan: ParseSourceSpan;
constructor(value: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context?: any): any;
}
export declare class Container implements Node {
children: Node[];
sourceSpan: ParseSourceSpan;
constructor(children: Node[], sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context?: any): any;
}
export declare class Icu implements Node {
expression: string;
type: string;
cases: {
[k: string]: Node;
};
sourceSpan: ParseSourceSpan;
expressionPlaceholder?: string;
constructor(expression: string, type: string, cases: {
[k: string]: Node;
}, sourceSpan: ParseSourceSpan, expressionPlaceholder?: string);
visit(visitor: Visitor, context?: any): any;
}
export declare class TagPlaceholder implements Node {
tag: string;
attrs: {
[k: string]: string;
};
startName: string;
closeName: string;
children: Node[];
isVoid: boolean;
sourceSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan | null;
endSourceSpan: ParseSourceSpan | null;
constructor(tag: string, attrs: {
[k: string]: string;
}, startName: string, closeName: string, children: Node[], isVoid: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan | null, endSourceSpan: ParseSourceSpan | null);
visit(visitor: Visitor, context?: any): any;
}
export declare class Placeholder implements Node {
value: string;
name: string;
sourceSpan: ParseSourceSpan;
constructor(value: string, name: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context?: any): any;
}
export declare class IcuPlaceholder implements Node {
value: Icu;
name: string;
sourceSpan: ParseSourceSpan;
/** Used to capture a message computed from a previous processing pass (see `setI18nRefs()`). */
previousMessage?: Message;
constructor(value: Icu, name: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context?: any): any;
}
export declare class BlockPlaceholder implements Node {
name: string;
parameters: string[];
startName: string;
closeName: string;
children: Node[];
sourceSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan | null;
endSourceSpan: ParseSourceSpan | null;
constructor(name: string, parameters: string[], startName: string, closeName: string, children: Node[], sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan | null, endSourceSpan: ParseSourceSpan | null);
visit(visitor: Visitor, context?: any): any;
}
/**
* Each HTML node that is affect by an i18n tag will also have an `i18n` property that is of type
* `I18nMeta`.
* This information is either a `Message`, which indicates it is the root of an i18n message, or a
* `Node`, which indicates is it part of a containing `Message`.
*/
export type I18nMeta = Message | Node;
export interface Visitor {
visitText(text: Text, context?: any): any;
visitContainer(container: Container, context?: any): any;
visitIcu(icu: Icu, context?: any): any;
visitTagPlaceholder(ph: TagPlaceholder, context?: any): any;
visitPlaceholder(ph: Placeholder, context?: any): any;
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any;
visitBlockPlaceholder(ph: BlockPlaceholder, context?: any): any;
}
export declare class CloneVisitor implements Visitor {
visitText(text: Text, context?: any): Text;
visitContainer(container: Container, context?: any): Container;
visitIcu(icu: Icu, context?: any): Icu;
visitTagPlaceholder(ph: TagPlaceholder, context?: any): TagPlaceholder;
visitPlaceholder(ph: Placeholder, context?: any): Placeholder;
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): IcuPlaceholder;
visitBlockPlaceholder(ph: BlockPlaceholder, context?: any): BlockPlaceholder;
}
export declare class RecurseVisitor implements Visitor {
visitText(text: Text, context?: any): any;
visitContainer(container: Container, context?: any): any;
visitIcu(icu: Icu, context?: any): any;
visitTagPlaceholder(ph: TagPlaceholder, context?: any): any;
visitPlaceholder(ph: Placeholder, context?: any): any;
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any;
visitBlockPlaceholder(ph: BlockPlaceholder, context?: any): any;
}
/**
* @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
*/
export class Message {
/**
* @param nodes message AST
* @param placeholders maps placeholder names to static content and their source spans
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
* @param meaning
* @param description
* @param customId
*/
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;
/** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
this.legacyIds = [];
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 = [];
}
}
}
export class Text {
constructor(value, sourceSpan) {
this.value = value;
this.sourceSpan = sourceSpan;
}
visit(visitor, context) {
return visitor.visitText(this, context);
}
}
// TODO(vicb): do we really need this node (vs an array) ?
export class Container {
constructor(children, sourceSpan) {
this.children = children;
this.sourceSpan = sourceSpan;
}
visit(visitor, context) {
return visitor.visitContainer(this, context);
}
}
export class Icu {
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);
}
}
export class TagPlaceholder {
constructor(tag, attrs, startName, closeName, children, isVoid,
// TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
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);
}
}
export class Placeholder {
constructor(value, name, sourceSpan) {
this.value = value;
this.name = name;
this.sourceSpan = sourceSpan;
}
visit(visitor, context) {
return visitor.visitPlaceholder(this, context);
}
}
export class IcuPlaceholder {
constructor(value, name, sourceSpan) {
this.value = value;
this.name = name;
this.sourceSpan = sourceSpan;
}
visit(visitor, context) {
return visitor.visitIcuPlaceholder(this, context);
}
}
export class BlockPlaceholder {
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);
}
}
// Clone the AST
export class CloneVisitor {
visitText(text, context) {
return new Text(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);
}
}
// Visit all the nodes recursively
export 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));
}
}
/**
* Serialize the message to the Localize backtick string format that would appear in compiled code.
*/
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}}`;
}
}
/**
* @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
*/
import { I18nMeta } from '../i18n/i18n_ast';
import { ParseSourceSpan } from '../parse_util';
import { InterpolatedAttributeToken, InterpolatedTextToken } from './tokens';
interface BaseNode {
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context: any): any;
}
export type Node = Attribute | CDATA | Comment | DocType | Element | Expansion | ExpansionCase | Text | Block | BlockParameter | LetDeclaration | Component | Directive;
export declare abstract class NodeWithI18n implements BaseNode {
sourceSpan: ParseSourceSpan;
i18n?: I18nMeta;
constructor(sourceSpan: ParseSourceSpan, i18n?: I18nMeta);
abstract visit(visitor: Visitor, context: any): any;
}
export declare class Text extends NodeWithI18n {
value: string;
tokens: InterpolatedTextToken[];
constructor(value: string, sourceSpan: ParseSourceSpan, tokens: InterpolatedTextToken[], i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly type = "text";
}
export declare class CDATA extends NodeWithI18n {
value: string;
tokens: InterpolatedTextToken[];
constructor(value: string, sourceSpan: ParseSourceSpan, tokens: InterpolatedTextToken[], i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly type = "cdata";
}
export declare class Expansion extends NodeWithI18n {
switchValue: string;
type: string;
cases: ExpansionCase[];
switchValueSourceSpan: ParseSourceSpan;
constructor(switchValue: string, type: string, cases: ExpansionCase[], sourceSpan: ParseSourceSpan, switchValueSourceSpan: ParseSourceSpan, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
}
export declare class ExpansionCase implements BaseNode {
value: string;
expression: Node[];
sourceSpan: ParseSourceSpan;
valueSourceSpan: ParseSourceSpan;
expSourceSpan: ParseSourceSpan;
constructor(value: string, expression: Node[], sourceSpan: ParseSourceSpan, valueSourceSpan: ParseSourceSpan, expSourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "expansionCase";
}
export declare class Attribute extends NodeWithI18n {
name: string;
value: string;
readonly keySpan: ParseSourceSpan | undefined;
valueSpan: ParseSourceSpan | undefined;
valueTokens: InterpolatedAttributeToken[] | undefined;
constructor(name: string, value: string, sourceSpan: ParseSourceSpan, keySpan: ParseSourceSpan | undefined, valueSpan: ParseSourceSpan | undefined, valueTokens: InterpolatedAttributeToken[] | undefined, i18n: I18nMeta | undefined);
visit(visitor: Visitor, context: any): any;
readonly type = "attribute";
get nameSpan(): ParseSourceSpan;
}
export declare class Element extends NodeWithI18n {
name: string;
attrs: Attribute[];
readonly directives: Directive[];
children: Node[];
readonly isSelfClosing: boolean;
startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
nameSpan: ParseSourceSpan | null;
readonly isVoid: boolean;
constructor(name: string, attrs: Attribute[], directives: Directive[], children: Node[], isSelfClosing: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan: ParseSourceSpan | null, nameSpan: ParseSourceSpan | null, isVoid: boolean, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly type = "element";
}
export declare class Comment implements BaseNode {
value: string | null;
sourceSpan: ParseSourceSpan;
constructor(value: string | null, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "comment";
}
export declare class DocType implements BaseNode {
value: string | null;
sourceSpan: ParseSourceSpan;
constructor(value: string | null, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "docType";
}
export declare class Block extends NodeWithI18n {
name: string;
parameters: BlockParameter[];
children: Node[];
nameSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
constructor(name: string, parameters: BlockParameter[], children: Node[], sourceSpan: ParseSourceSpan, nameSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly type = "block";
}
export declare class Component extends NodeWithI18n {
readonly componentName: string;
readonly tagName: string | null;
readonly fullName: string;
attrs: Attribute[];
readonly directives: Directive[];
readonly children: Node[];
readonly isSelfClosing: boolean;
readonly startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
constructor(componentName: string, tagName: string | null, fullName: string, attrs: Attribute[], directives: Directive[], children: Node[], isSelfClosing: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null, i18n?: I18nMeta);
visit(visitor: Visitor, context: any): any;
readonly type = "component";
}
export declare class Directive implements BaseNode {
readonly name: string;
readonly attrs: Attribute[];
readonly sourceSpan: ParseSourceSpan;
readonly startSourceSpan: ParseSourceSpan;
readonly endSourceSpan: ParseSourceSpan | null;
constructor(name: string, attrs: Attribute[], sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null);
visit(visitor: Visitor, context: any): any;
readonly type = "directive";
}
export declare class BlockParameter implements BaseNode {
expression: string;
sourceSpan: ParseSourceSpan;
constructor(expression: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "blockParameter";
readonly startSourceSpan: null;
readonly endSourceSpan: null;
}
export declare class LetDeclaration implements BaseNode {
name: string;
value: string;
sourceSpan: ParseSourceSpan;
readonly nameSpan: ParseSourceSpan;
valueSpan: ParseSourceSpan;
constructor(name: string, value: string, sourceSpan: ParseSourceSpan, nameSpan: ParseSourceSpan, valueSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "letDeclaration";
readonly startSourceSpan: null;
readonly endSourceSpan: null;
}
export interface Visitor {
visit?(node: Node, context: any): any;
visitElement(element: Element, context: any): any;
visitAttribute(attribute: Attribute, context: any): any;
visitText(text: Text, context: any): any;
visitCdata(text: CDATA, context: any): any;
visitComment(comment: Comment, context: any): any;
visitDocType(docType: DocType, context: any): any;
visitExpansion(expansion: Expansion, context: any): any;
visitExpansionCase(expansionCase: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(parameter: BlockParameter, context: any): any;
visitLetDeclaration(decl: LetDeclaration, context: any): any;
visitComponent(component: Component, context: any): any;
visitDirective(directive: Directive, context: any): any;
}
export declare function visitAll(visitor: Visitor, nodes: Node[], context?: any): any[];
export declare class RecursiveVisitor implements Visitor {
constructor();
visitElement(ast: Element, context: any): any;
visitAttribute(ast: Attribute, context: any): any;
visitText(ast: Text, context: any): any;
visitCdata(ast: CDATA, context: any): any;
visitComment(ast: Comment, context: any): any;
visitDocType(ast: DocType, context: any): any;
visitExpansion(ast: Expansion, context: any): any;
visitExpansionCase(ast: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(ast: BlockParameter, context: any): any;
visitLetDeclaration(decl: LetDeclaration, context: any): void;
visitComponent(component: Component, context: any): void;
visitDirective(directive: Directive, context: any): void;
private visitChildren;
}
export {};
/**
* @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
*/
export class NodeWithI18n {
constructor(sourceSpan, i18n) {
this.sourceSpan = sourceSpan;
this.i18n = i18n;
}
}
export class Text extends NodeWithI18n {
constructor(value, sourceSpan, tokens, i18n) {
super(sourceSpan, i18n);
this.value = value;
this.tokens = tokens;
this.type = 'text';
}
visit(visitor, context) {
return visitor.visitText(this, context);
}
}
export class CDATA extends NodeWithI18n {
constructor(value, sourceSpan, tokens, i18n) {
super(sourceSpan, i18n);
this.value = value;
this.tokens = tokens;
this.type = 'cdata';
}
visit(visitor, context) {
return visitor.visitCdata(this, context);
}
}
export class Expansion extends NodeWithI18n {
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);
}
}
export class ExpansionCase {
constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
this.value = value;
this.expression = expression;
this.sourceSpan = sourceSpan;
this.valueSourceSpan = valueSourceSpan;
this.expSourceSpan = expSourceSpan;
this.type = 'expansionCase';
}
visit(visitor, context) {
return visitor.visitExpansionCase(this, context);
}
}
export class Attribute extends NodeWithI18n {
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;
this.type = 'attribute';
}
visit(visitor, context) {
return visitor.visitAttribute(this, context);
}
// angular-html-parser: backwards compatibility for Prettier
get nameSpan() {
return this.keySpan;
}
}
export class Element extends NodeWithI18n {
constructor(name, attrs, directives, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan = null, nameSpan = 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.nameSpan = nameSpan;
this.isVoid = isVoid;
this.type = 'element';
}
visit(visitor, context) {
return visitor.visitElement(this, context);
}
}
export class Comment {
constructor(value, sourceSpan) {
this.value = value;
this.sourceSpan = sourceSpan;
this.type = 'comment';
}
visit(visitor, context) {
return visitor.visitComment(this, context);
}
}
export class DocType {
constructor(value, sourceSpan) {
this.value = value;
this.sourceSpan = sourceSpan;
this.type = 'docType';
}
visit(visitor, context) {
return visitor.visitDocType(this, context);
}
}
export class Block extends NodeWithI18n {
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;
this.type = 'block';
}
visit(visitor, context) {
return visitor.visitBlock(this, context);
}
}
export class Component extends NodeWithI18n {
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;
this.type = 'component';
}
visit(visitor, context) {
return visitor.visitComponent(this, context);
}
}
export class Directive {
constructor(name, attrs, sourceSpan, startSourceSpan, endSourceSpan = null) {
this.name = name;
this.attrs = attrs;
this.sourceSpan = sourceSpan;
this.startSourceSpan = startSourceSpan;
this.endSourceSpan = endSourceSpan;
this.type = 'directive';
}
visit(visitor, context) {
return visitor.visitDirective(this, context);
}
}
export class BlockParameter {
constructor(expression, sourceSpan) {
this.expression = expression;
this.sourceSpan = sourceSpan;
this.type = 'blockParameter';
this.startSourceSpan = null;
this.endSourceSpan = null;
}
visit(visitor, context) {
return visitor.visitBlockParameter(this, context);
}
}
export class LetDeclaration {
constructor(name, value, sourceSpan, nameSpan, valueSpan) {
this.name = name;
this.value = value;
this.sourceSpan = sourceSpan;
this.nameSpan = nameSpan;
this.valueSpan = valueSpan;
this.type = 'letDeclaration';
this.startSourceSpan = null;
this.endSourceSpan = null;
}
visit(visitor, context) {
return visitor.visitLetDeclaration(this, context);
}
}
export 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;
}
export 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) { }
visitCdata(ast, context) { }
visitComment(ast, context) { }
visitDocType(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);
}
}
/**
* @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
*/
export declare class InterpolationConfig {
start: string;
end: string;
static fromArray(markers: [string, string] | null): InterpolationConfig;
constructor(start: string, end: string);
}
export declare const DEFAULT_INTERPOLATION_CONFIG: InterpolationConfig;
export declare const DEFAULT_CONTAINER_BLOCKS: Set<string>;
/**
* @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
*/
import { assertInterpolationSymbols } from "../assertions.js";
export class InterpolationConfig {
static fromArray(markers) {
if (!markers) {
return DEFAULT_INTERPOLATION_CONFIG;
}
assertInterpolationSymbols('interpolation', markers);
return new InterpolationConfig(markers[0], markers[1]);
}
constructor(start, end) {
this.start = start;
this.end = end;
}
}
export const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
export const DEFAULT_CONTAINER_BLOCKS = new Set(['switch']);
/**
* @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
*/
export declare const NAMED_ENTITIES: Record<string, string>;
export declare const NGSP_UNICODE = "\uE500";
/**
* @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
*/
// Mapping between all HTML entity names and their unicode representation.
// Generated from https://html.spec.whatwg.org/multipage/entities.json by stripping
// the `&` and `;` from the keys and removing the duplicates.
// see https://www.w3.org/TR/html51/syntax.html#named-character-references
export 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',
};
// The &ngsp; pseudo-entity is denoting a space.
// 0xE500 is a PUA (Private Use Areas) unicode character
// This is inspired by the Angular Dart implementation.
export const NGSP_UNICODE = '\uE500';
NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
/**
* @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
*/
import { TokenizeOptions } from './lexer';
import { Parser, ParseTreeResult } from './parser';
import { TagContentType } from './tags';
export declare class HtmlParser extends Parser {
constructor();
parse(source: string, url: string, options?: TokenizeOptions, isTagNameCaseSensitive?: boolean, getTagContentType?: (tagName: string, prefix: string, hasParent: boolean, attrs: Array<{
prefix: string;
name: string;
value?: string;
}>) => void | TagContentType): ParseTreeResult;
}
/**
* @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
*/
import { getHtmlTagDefinition } from "./html_tags.js";
import { Parser } from "./parser.js";
export class HtmlParser extends Parser {
constructor() {
super(getHtmlTagDefinition);
}
// angular-html-parser: More options
parse(source, url, options, isTagNameCaseSensitive = false, getTagContentType) {
return super.parse(source, url, options, isTagNameCaseSensitive, getTagContentType);
}
}
/**
* @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
*/
import { TagContentType, TagDefinition } from './tags';
export declare class HtmlTagDefinition implements TagDefinition {
private closedByChildren;
private contentType;
closedByParent: boolean;
implicitNamespacePrefix: string | null;
isVoid: boolean;
ignoreFirstLf: boolean;
canSelfClose: boolean;
preventNamespaceInheritance: boolean;
constructor({ closedByChildren, implicitNamespacePrefix, contentType, closedByParent, isVoid, ignoreFirstLf, preventNamespaceInheritance, canSelfClose, }?: {
closedByChildren?: string[];
closedByParent?: boolean;
implicitNamespacePrefix?: string;
contentType?: TagContentType | {
default: TagContentType;
[namespace: string]: TagContentType;
};
isVoid?: boolean;
ignoreFirstLf?: boolean;
preventNamespaceInheritance?: boolean;
canSelfClose?: boolean;
});
isClosedByChild(name: string): boolean;
getContentType(prefix?: string): TagContentType;
}
export declare function getHtmlTagDefinition(tagName: string): HtmlTagDefinition;
/**
* @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
*/
import { DomElementSchemaRegistry } from "../schema/dom_element_schema_registry.js";
import { getNsPrefix, TagContentType } from "./tags.js";
export class HtmlTagDefinition {
constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false, canSelfClose = false, } = {}) {
this.closedByChildren = {};
this.closedByParent = 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;
// see https://www.w3.org/TR/html51/syntax.html#optional-tags
// This implementation does not fully conform to the HTML5 spec.
let TAG_DEFINITIONS;
export 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({
// Usually the implicit namespace here would be redundant since it will be inherited from
// the parent `svg`, but we have to do it for `foreignObject`, because the way the parser
// works is that the parent node of an end tag is its own start tag which means that
// the `preventNamespaceInheritance` on `foreignObject` would have it default to the
// implicit namespace which is `html`, unless specified otherwise.
implicitNamespacePrefix: 'svg',
// We want to prevent children of foreignObject from inheriting its namespace, because
// the point of the element is to allow nodes from other namespaces to be inserted.
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({
// The browser supports two separate `title` tags which have to use
// a different content type: `HTMLTitleElement` and `SVGTitleElement`
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 });
}
});
}
// We have to make both a case-sensitive and a case-insensitive lookup, because
// HTML tag names are case insensitive, whereas some SVG tags are case sensitive.
// angular-html-parser: modification
return (TAG_DEFINITIONS[tagName] ?? DEFAULT_TAG_DEFINITION);
// return (
// TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ?? DEFAULT_TAG_DEFINITION
// );
}
/**
* @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
*/
import { ParseError, ParseSourceSpan } from '../parse_util';
import { InterpolationConfig } from './defaults';
import { TagContentType } from './tags';
import { Token } from './tokens';
export declare class TokenizeResult {
tokens: Token[];
errors: ParseError[];
nonNormalizedIcuExpressions: Token[];
constructor(tokens: Token[], errors: ParseError[], nonNormalizedIcuExpressions: Token[]);
}
export interface LexerRange {
startPos: number;
startLine: number;
startCol: number;
endPos: number;
}
/**
* Options that modify how the text is tokenized.
*/
export interface TokenizeOptions {
/** Whether to tokenize ICU messages (considered as text nodes when false). */
tokenizeExpansionForms?: boolean;
/** How to tokenize interpolation markers. */
interpolationConfig?: InterpolationConfig;
/**
* The start and end point of the text to parse within the `source` string.
* The entire `source` string is parsed if this is not provided.
* */
range?: LexerRange;
/**
* If this text is stored in a JavaScript string, then we have to deal with escape sequences.
*
* **Example 1:**
*
* ```
* "abc\"def\nghi"
* ```
*
* - The `\"` must be converted to `"`.
* - The `\n` must be converted to a new line character in a token,
* but it should not increment the current line for source mapping.
*
* **Example 2:**
*
* ```
* "abc\
* def"
* ```
*
* The line continuation (`\` followed by a newline) should be removed from a token
* but the new line should increment the current line for source mapping.
*/
escapedString?: boolean;
/**
* If this text is stored in an external template (e.g. via `templateUrl`) then we need to decide
* whether or not to normalize the line-endings (from `\r\n` to `\n`) when processing ICU
* expressions.
*
* If `true` then we will normalize ICU expression line endings.
* The default is `false`, but this will be switched in a future major release.
*/
i18nNormalizeLineEndingsInICUs?: boolean;
/**
* An array of characters that should be considered as leading trivia.
* Leading trivia are characters that are not important to the developer, and so should not be
* included in source-map segments. A common example is whitespace.
*/
leadingTriviaChars?: string[];
/**
* If true, do not convert CRLF to LF.
*/
preserveLineEndings?: boolean;
/**
* Whether to tokenize @ block syntax. Otherwise considered text,
* or ICU tokens if `tokenizeExpansionForms` is enabled.
*/
tokenizeBlocks?: boolean;
/**
* Whether to tokenize the `@let` syntax. Otherwise will be considered either
* text or an incomplete block, depending on whether `tokenizeBlocks` is enabled.
*/
tokenizeLet?: boolean;
/** Whether the selectorless syntax is enabled. */
selectorlessEnabled?: boolean;
canSelfClose?: boolean;
allowHtmComponentClosingTags?: boolean;
}
export declare function tokenize(source: string, url: string, getTagContentType: (tagName: string, prefix: string, hasParent: boolean, attrs: Array<{
prefix: string;
name: string;
value?: string;
}>) => TagContentType, options?: TokenizeOptions): TokenizeResult;
/**
* The _Tokenizer uses objects of this type to move through the input text,
* extracting "parsed characters". These could be more than one actual character
* if the text contains escape sequences.
*/
interface CharacterCursor {
/** Initialize the cursor. */
init(): void;
/** The parsed character at the current cursor position. */
peek(): number;
/** Advance the cursor by one parsed character. */
advance(): void;
/** Get a span from the marked start point to the current point. */
getSpan(start?: this, leadingTriviaCodePoints?: number[]): ParseSourceSpan;
/** Get the parsed characters from the marked start point to the current point. */
getChars(start: this): string;
/** The number of characters left before the end of the cursor. */
charsLeft(): number;
/** The number of characters between `this` cursor and `other` cursor. */
diff(other: this): number;
/** Make a copy of this cursor */
clone(): CharacterCursor;
}
export declare class CursorError extends Error {
msg: string;
cursor: CharacterCursor;
constructor(msg: string, cursor: CharacterCursor);
}
export {};
/**
* @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
*/
import * as chars from "../chars.js";
import { ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan } from "../parse_util.js";
import { DEFAULT_INTERPOLATION_CONFIG } from "./defaults.js";
import { NAMED_ENTITIES } from "./entities.js";
import { mergeNsAndName, TagContentType } from "./tags.js";
export class TokenizeResult {
constructor(tokens, errors, nonNormalizedIcuExpressions) {
this.tokens = tokens;
this.errors = errors;
this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
}
}
export function tokenize(source, url, getTagContentType, options = {}) {
const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagContentType, 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 === chars.$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', // Covers `@else if` as well
'@for',
'@switch',
'@case',
'@default',
'@empty',
'@defer',
'@placeholder',
'@loading',
'@error',
];
// See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
class _Tokenizer {
/**
* @param _file The html source file being tokenized.
* @param _getTagContentType A function that will retrieve a tag content type for a given tag
* name.
* @param options Configuration of the tokenization.
*/
constructor(_file, _getTagContentType, options) {
this._getTagContentType = _getTagContentType;
this._currentTokenStart = null;
this._currentTokenType = null;
this._expansionCaseStack = [];
this._openDirectiveCount = 0;
this._inInterpolation = false;
this._fullNameStack = [];
this.tokens = [];
this.errors = [];
this.nonNormalizedIcuExpressions = [];
this._tokenizeIcu = options.tokenizeExpansionForms || false;
this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
this._leadingTriviaCodePoints =
options.leadingTriviaChars && options.leadingTriviaChars.map((c) => c.codePointAt(0) || 0);
this._canSelfClose = options.canSelfClose || false;
this._allowHtmComponentClosingTags = options.allowHtmComponentClosingTags || false;
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;
}
// https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
// In order to keep the original position in the source, we can not
// pre-process it.
// Instead CRs are processed right before instantiating the tokens.
return content.replace(_CR_OR_CRLF_REGEXP, '\n');
}
tokenize() {
while (this._cursor.peek() !== chars.$EOF) {
const start = this._cursor.clone();
try {
if (this._attemptCharCode(chars.$LT)) {
if (this._attemptCharCode(chars.$BANG)) {
if (this._attemptStr('[CDATA[')) {
this._consumeCdata(start);
}
else if (this._attemptStr('--')) {
this._consumeComment(start);
}
else if (this._attemptStrCaseInsensitive('doctype')) {
this._consumeDocType(start);
}
else {
this._consumeBogusComment(start);
}
}
else if (this._attemptCharCode(chars.$SLASH)) {
this._consumeTagClose(start);
}
else {
const savedPos = this._cursor.clone();
if (this._attemptCharCode(chars.$QUESTION)) {
this._cursor = savedPos;
this._consumeBogusComment(start);
}
else {
this._consumeTagOpen(start);
}
}
}
else if (this._tokenizeLet &&
// Use `peek` instead of `attempCharCode` since we
// don't want to advance in case it's not `@let`.
this._cursor.peek() === chars.$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(chars.$RBRACE)) {
this._consumeBlockEnd(start);
}
else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
// In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while
// the premature end of an interpolation is given by the start of a new HTML element.
this._consumeWithInterpolation(5 /* TokenType.TEXT */, 8 /* TokenType.INTERPOLATION */, () => this._isTextEnd(), () => this._isTagStart());
}
}
catch (e) {
this.handleError(e);
}
}
this._beginToken(42 /* TokenType.EOF */);
this._endToken([]);
}
_getBlockName() {
// This allows us to capture up something like `@else if`, but not `@ if`.
let spacesInNameAllowed = false;
const nameCursor = this._cursor.clone();
this._attemptCharCodeUntilFn((code) => {
if (chars.isWhitespace(code)) {
return !spacesInNameAllowed;
}
if (isBlockNameChar(code)) {
spacesInNameAllowed = true;
return false;
}
return true;
});
return this._cursor.getChars(nameCursor).trim();
}
_consumeBlockStart(start) {
this._requireCharCode(chars.$AT);
this._beginToken(25 /* TokenType.BLOCK_OPEN_START */, start);
const startToken = this._endToken([this._getBlockName()]);
if (this._cursor.peek() === chars.$LPAREN) {
// Advance past the opening paren.
this._cursor.advance();
// Capture the parameters.
this._consumeBlockParameters();
// Allow spaces before the closing paren.
this._attemptCharCodeUntilFn(isNotWhitespace);
if (this._attemptCharCode(chars.$RPAREN)) {
// Allow spaces after the paren.
this._attemptCharCodeUntilFn(isNotWhitespace);
}
else {
startToken.type = 29 /* TokenType.INCOMPLETE_BLOCK_OPEN */;
return;
}
}
if (this._attemptCharCode(chars.$LBRACE)) {
this._beginToken(26 /* TokenType.BLOCK_OPEN_END */);
this._endToken([]);
}
else {
startToken.type = 29 /* TokenType.INCOMPLETE_BLOCK_OPEN */;
}
}
_consumeBlockEnd(start) {
this._beginToken(27 /* TokenType.BLOCK_CLOSE */, start);
this._endToken([]);
}
_consumeBlockParameters() {
// Trim the whitespace until the first parameter.
this._attemptCharCodeUntilFn(isBlockParameterChar);
while (this._cursor.peek() !== chars.$RPAREN && this._cursor.peek() !== chars.$EOF) {
this._beginToken(28 /* TokenType.BLOCK_PARAMETER */);
const start = this._cursor.clone();
let inQuote = null;
let openParens = 0;
// Consume the parameter until the next semicolon or brace.
// Note that we skip over semicolons/braces inside of strings.
while ((this._cursor.peek() !== chars.$SEMICOLON && this._cursor.peek() !== chars.$EOF) ||
inQuote !== null) {
const char = this._cursor.peek();
// Skip to the next character if it was escaped.
if (char === chars.$BACKSLASH) {
this._cursor.advance();
}
else if (char === inQuote) {
inQuote = null;
}
else if (inQuote === null && chars.isQuote(char)) {
inQuote = char;
}
else if (char === chars.$LPAREN && inQuote === null) {
openParens++;
}
else if (char === chars.$RPAREN && inQuote === null) {
if (openParens === 0) {
break;
}
else if (openParens > 0) {
openParens--;
}
}
this._cursor.advance();
}
this._endToken([this._cursor.getChars(start)]);
// Skip to the next parameter.
this._attemptCharCodeUntilFn(isBlockParameterChar);
}
}
_consumeLetDeclaration(start) {
this._requireStr('@let');
this._beginToken(30 /* TokenType.LET_START */, start);
// Require at least one white space after the `@let`.
if (chars.isWhitespace(this._cursor.peek())) {
this._attemptCharCodeUntilFn(isNotWhitespace);
}
else {
const token = this._endToken([this._cursor.getChars(start)]);
token.type = 33 /* TokenType.INCOMPLETE_LET */;
return;
}
const startToken = this._endToken([this._getLetDeclarationName()]);
// Skip over white space before the equals character.
this._attemptCharCodeUntilFn(isNotWhitespace);
// Expect an equals sign.
if (!this._attemptCharCode(chars.$EQ)) {
startToken.type = 33 /* TokenType.INCOMPLETE_LET */;
return;
}
// Skip spaces after the equals.
this._attemptCharCodeUntilFn((code) => isNotWhitespace(code) && !chars.isNewLine(code));
this._consumeLetDeclarationValue();
// Terminate the `@let` with a semicolon.
const endChar = this._cursor.peek();
if (endChar === chars.$SEMICOLON) {
this._beginToken(32 /* TokenType.LET_END */);
this._endToken([]);
this._cursor.advance();
}
else {
startToken.type = 33 /* TokenType.INCOMPLETE_LET */;
startToken.sourceSpan = this._cursor.getSpan(start);
}
}
_getLetDeclarationName() {
const nameCursor = this._cursor.clone();
let allowDigit = false;
this._attemptCharCodeUntilFn((code) => {
if (chars.isAsciiLetter(code) ||
code === chars.$$ ||
code === chars.$_ ||
// `@let` names can't start with a digit, but digits are valid anywhere else in the name.
(allowDigit && chars.isDigit(code))) {
allowDigit = true;
return false;
}
return true;
});
return this._cursor.getChars(nameCursor).trim();
}
_consumeLetDeclarationValue() {
const start = this._cursor.clone();
this._beginToken(31 /* TokenType.LET_VALUE */, start);
while (this._cursor.peek() !== chars.$EOF) {
const char = this._cursor.peek();
// `@let` declarations terminate with a semicolon.
if (char === chars.$SEMICOLON) {
break;
}
// If we hit a quote, skip over its content since we don't care what's inside.
if (chars.isQuote(char)) {
this._cursor.advance();
this._attemptCharCodeUntilFn((inner) => {
if (inner === chars.$BACKSLASH) {
this._cursor.advance();
return false;
}
return inner === char;
});
}
this._cursor.advance();
}
this._endToken([this._cursor.getChars(start)]);
}
/**
* @returns whether an ICU token has been created
* @internal
*/
_tokenizeExpansionForm() {
if (this.isExpansionFormStart()) {
this._consumeExpansionFormStart();
return true;
}
if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
this._consumeExpansionCaseStart();
return true;
}
if (this._cursor.peek() === chars.$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))) {
// If attempting to parse the string fails, we want to reset the parser
// to where it was before the attempt
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));
}
}
_requireStrCaseInsensitive(chars) {
const location = this._cursor.clone();
if (!this._attemptStrCaseInsensitive(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() {
// Don't rely upon reading directly from `_input` as the actual char value
// may have been generated from an escape sequence.
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() === chars.$AT &&
SUPPORTED_BLOCKS.some((blockName) => this._peekStr(blockName)));
}
_isLetStart() {
return this._cursor.peek() === chars.$AT && this._peekStr('@let');
}
_consumeEntity(textTokenType) {
this._beginToken(9 /* TokenType.ENCODED_ENTITY */);
const start = this._cursor.clone();
this._cursor.advance();
if (this._attemptCharCode(chars.$HASH)) {
const isHex = this._attemptCharCode(chars.$x) || this._attemptCharCode(chars.$X);
const codeStart = this._cursor.clone();
this._attemptCharCodeUntilFn(isDigitEntityEnd);
if (this._cursor.peek() != chars.$SEMICOLON) {
// Advance cursor to include the peeked character in the string provided to the error
// message.
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.fromCharCode(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() != chars.$SEMICOLON) {
// No semicolon was found so abort the encoded entity token that was in progress, and treat
// this as a text token
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 /* TokenType.ESCAPABLE_RAW_TEXT */ : 7 /* TokenType.RAW_TEXT */);
const parts = [];
while (true) {
const tagCloseStart = this._cursor.clone();
const foundEndMarker = endMarkerPredicate();
this._cursor = tagCloseStart;
if (foundEndMarker) {
break;
}
if (consumeEntities && this._cursor.peek() === chars.$AMPERSAND) {
this._endToken([this._processCarriageReturns(parts.join(''))]);
parts.length = 0;
this._consumeEntity(6 /* TokenType.ESCAPABLE_RAW_TEXT */);
this._beginToken(6 /* TokenType.ESCAPABLE_RAW_TEXT */);
}
else {
parts.push(this._readChar());
}
}
this._endToken([this._processCarriageReturns(parts.join(''))]);
}
_consumeComment(start) {
this._beginToken(10 /* TokenType.COMMENT_START */, start);
this._endToken([]);
this._consumeRawText(false, () => this._attemptStr('-->'));
this._beginToken(11 /* TokenType.COMMENT_END */);
this._requireStr('-->');
this._endToken([]);
}
// https://www.w3.org/TR/html5/syntax.html#bogus-comment-state
_consumeBogusComment(start) {
this._beginToken(10 /* TokenType.COMMENT_START */, start);
this._endToken([]);
this._consumeRawText(false, () => this._cursor.peek() === chars.$GT);
this._beginToken(11 /* TokenType.COMMENT_END */);
this._cursor.advance();
this._endToken([]);
}
_consumeCdata(start) {
this._beginToken(12 /* TokenType.CDATA_START */, start);
this._endToken([]);
this._consumeRawText(false, () => this._attemptStr(']]>'));
this._beginToken(13 /* TokenType.CDATA_END */);
this._requireStr(']]>');
this._endToken([]);
}
_consumeDocType(start) {
this._beginToken(18 /* TokenType.DOC_TYPE_START */, start);
this._endToken([]);
this._consumeRawText(false, () => this._cursor.peek() === chars.$GT);
this._beginToken(19 /* TokenType.DOC_TYPE_END */);
this._cursor.advance();
this._endToken([]);
}
_consumePrefixAndName(endPredicate) {
const nameOrPrefixStart = this._cursor.clone();
let prefix = '';
while (this._cursor.peek() !== chars.$COLON && !isPrefixEnd(this._cursor.peek())) {
this._cursor.advance();
}
let nameStart;
if (this._cursor.peek() === chars.$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;
const attrs = [];
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 (!chars.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() === chars.$AT) {
const start = this._cursor.clone();
const nameStart = start.clone();
nameStart.advance();
if (isSelectorlessNameStart(nameStart.peek())) {
this._consumeDirective(start, nameStart);
}
}
else {
const attr = this._consumeAttribute();
attrs.push(attr);
}
}
if (openToken.type === 34 /* TokenType.COMPONENT_OPEN_START */) {
this._consumeComponentOpenEnd();
}
else {
this._consumeTagOpenEnd();
}
}
catch (e) {
if (e instanceof ParseError) {
if (openToken) {
// We errored before we could close the opening tag, so it is incomplete.
openToken.type =
openToken.type === 34 /* TokenType.COMPONENT_OPEN_START */
? 38 /* TokenType.INCOMPLETE_COMPONENT_OPEN */
: 4 /* TokenType.INCOMPLETE_TAG_OPEN */;
}
else {
// When the start tag is invalid, assume we want a "<" as text.
// Back to back text tokens are merged at the end.
this._beginToken(5 /* TokenType.TEXT */, start);
this._endToken(['<']);
}
return;
}
throw e;
}
if (this._canSelfClose &&
this.tokens[this.tokens.length - 1].type === 2 /* TokenType.TAG_OPEN_END_VOID */) {
return;
}
const contentTokenType = this._getTagContentType(tagName, prefix, this._fullNameStack.length > 0, attrs);
this._handleFullNameStackForTagOpen(prefix, tagName);
if (contentTokenType === TagContentType.RAW_TEXT) {
this._consumeRawTextWithTagClose(prefix, openToken, closingTagName, false);
}
else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
this._consumeRawTextWithTagClose(prefix, openToken, closingTagName, true);
}
}
_consumeRawTextWithTagClose(prefix, openToken, tagName, consumeEntities) {
this._consumeRawText(consumeEntities, () => {
if (!this._attemptCharCode(chars.$LT))
return false;
if (!this._attemptCharCode(chars.$SLASH))
return false;
this._attemptCharCodeUntilFn(isNotWhitespace);
if (!this._attemptStrCaseInsensitive(prefix && openToken.type !== 34 /* TokenType.COMPONENT_OPEN_START */ ?
`${prefix}:${tagName}`
: tagName))
return false;
this._attemptCharCodeUntilFn(isNotWhitespace);
return this._attemptCharCode(chars.$GT);
});
this._beginToken(openToken.type === 34 /* TokenType.COMPONENT_OPEN_START */
? 37 /* TokenType.COMPONENT_CLOSE */
: 3 /* TokenType.TAG_CLOSE */);
this._requireCharCodeUntilFn((code) => code === chars.$GT, 3);
this._cursor.advance(); // Consume the `>`
this._endToken(openToken.parts);
this._handleFullNameStackForTagClose(prefix, tagName);
}
_consumeTagOpenStart(start) {
this._beginToken(0 /* TokenType.TAG_OPEN_START */, start);
const parts = this._consumePrefixAndName(isNameEnd);
return this._endToken(parts);
}
_consumeComponentOpenStart(start) {
this._beginToken(34 /* TokenType.COMPONENT_OPEN_START */, 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() === chars.$COLON) {
this._cursor.advance();
[prefix, tagName] = this._consumePrefixAndName(isNameEnd);
}
return [name, prefix, tagName];
}
_consumeAttribute() {
const [prefix, name] = this._consumeAttributeName();
let value;
this._attemptCharCodeUntilFn(isNotWhitespace);
if (this._attemptCharCode(chars.$EQ)) {
this._attemptCharCodeUntilFn(isNotWhitespace);
value = this._consumeAttributeValue();
}
this._attemptCharCodeUntilFn(isNotWhitespace);
return { prefix, name, value };
}
_consumeAttributeName() {
const attrNameStart = this._cursor.peek();
if (attrNameStart === chars.$SQ || attrNameStart === chars.$DQ) {
throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
}
this._beginToken(14 /* TokenType.ATTR_NAME */);
let nameEndPredicate;
if (this._openDirectiveCount > 0) {
// If we're parsing attributes inside of directive syntax, we have to terminate the name
// on the first non-matching closing paren. For example, if we have `@Dir(someAttr)`,
// `@Dir` and `(` will have already been captured as `DIRECTIVE_NAME` and `DIRECTIVE_OPEN`
// respectively, but the `)` will get captured as a part of the name for `someAttr`
// because normally that would be an event binding.
let openParens = 0;
nameEndPredicate = (code) => {
if (this._openDirectiveCount > 0) {
if (code === chars.$LPAREN) {
openParens++;
}
else if (code === chars.$RPAREN) {
if (openParens === 0) {
return true;
}
openParens--;
}
}
return isNameEnd(code);
};
}
else if (attrNameStart === chars.$LBRACKET) {
let openBrackets = 0;
// Be more permissive for which characters are allowed inside square-bracketed attributes,
// because they usually end up being bound as attribute values. Some third-party packages
// like Tailwind take advantage of this.
nameEndPredicate = (code) => {
if (code === chars.$LBRACKET) {
openBrackets++;
}
else if (code === chars.$RBRACKET) {
openBrackets--;
}
// Only check for name-ending characters if the brackets are balanced or mismatched.
// Also interrupt the matching on new lines.
return openBrackets <= 0 ? isNameEnd(code) : chars.isNewLine(code);
};
}
else {
nameEndPredicate = isNameEnd;
}
const prefixAndName = this._consumePrefixAndName(nameEndPredicate);
this._endToken(prefixAndName);
return prefixAndName;
}
_consumeAttributeValue() {
let value;
if (this._cursor.peek() === chars.$SQ || this._cursor.peek() === chars.$DQ) {
const quoteChar = this._cursor.peek();
this._consumeQuote(quoteChar);
// In an attribute then end of the attribute value and the premature end to an interpolation
// are both triggered by the `quoteChar`.
const endPredicate = () => this._cursor.peek() === quoteChar;
value = this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);
this._consumeQuote(quoteChar);
}
else {
const endPredicate = () => isNameEnd(this._cursor.peek());
value = this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);
}
return value;
}
_consumeQuote(quoteChar) {
this._beginToken(15 /* TokenType.ATTR_QUOTE */);
this._requireCharCode(quoteChar);
this._endToken([String.fromCodePoint(quoteChar)]);
}
_consumeTagOpenEnd() {
const tokenType = this._attemptCharCode(chars.$SLASH)
? 2 /* TokenType.TAG_OPEN_END_VOID */
: 1 /* TokenType.TAG_OPEN_END */;
this._beginToken(tokenType);
this._requireCharCode(chars.$GT);
this._endToken([]);
}
_consumeComponentOpenEnd() {
const tokenType = this._attemptCharCode(chars.$SLASH)
? 36 /* TokenType.COMPONENT_OPEN_END_VOID */
: 35 /* TokenType.COMPONENT_OPEN_END */;
this._beginToken(tokenType);
this._requireCharCode(chars.$GT);
this._endToken([]);
}
_consumeTagClose(start) {
if (this._selectorlessEnabled) {
const clone = start.clone();
while (clone.peek() !== chars.$GT && !isSelectorlessNameStart(clone.peek())) {
clone.advance();
}
if (isSelectorlessNameStart(clone.peek())) {
this._beginToken(37 /* TokenType.COMPONENT_CLOSE */, start);
const parts = this._consumeComponentName();
this._attemptCharCodeUntilFn(isNotWhitespace);
this._requireCharCode(chars.$GT);
this._endToken(parts);
return;
}
}
this._beginToken(3 /* TokenType.TAG_CLOSE */, start);
this._attemptCharCodeUntilFn(isNotWhitespace);
// https://github.com/developit/htm
if (this._allowHtmComponentClosingTags && this._attemptCharCode(chars.$SLASH)) {
this._attemptCharCodeUntilFn(isNotWhitespace);
this._requireCharCode(chars.$GT);
this._endToken([]);
}
else {
const [prefix, name] = this._consumePrefixAndName(isNameEnd);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._requireCharCode(chars.$GT);
this._endToken([prefix, name]);
this._handleFullNameStackForTagClose(prefix, name);
}
}
_consumeExpansionFormStart() {
this._beginToken(20 /* TokenType.EXPANSION_FORM_START */);
this._requireCharCode(chars.$LBRACE);
this._endToken([]);
this._expansionCaseStack.push(20 /* TokenType.EXPANSION_FORM_START */);
this._beginToken(7 /* TokenType.RAW_TEXT */);
const condition = this._readUntil(chars.$COMMA);
const normalizedCondition = this._processCarriageReturns(condition);
if (this._i18nNormalizeLineEndingsInICUs) {
// We explicitly want to normalize line endings for this text.
this._endToken([normalizedCondition]);
}
else {
// We are not normalizing line endings.
const conditionToken = this._endToken([condition]);
if (normalizedCondition !== condition) {
this.nonNormalizedIcuExpressions.push(conditionToken);
}
}
this._requireCharCode(chars.$COMMA);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._beginToken(7 /* TokenType.RAW_TEXT */);
const type = this._readUntil(chars.$COMMA);
this._endToken([type]);
this._requireCharCode(chars.$COMMA);
this._attemptCharCodeUntilFn(isNotWhitespace);
}
_consumeExpansionCaseStart() {
this._beginToken(21 /* TokenType.EXPANSION_CASE_VALUE */);
const value = this._readUntil(chars.$LBRACE).trim();
this._endToken([value]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._beginToken(22 /* TokenType.EXPANSION_CASE_EXP_START */);
this._requireCharCode(chars.$LBRACE);
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._expansionCaseStack.push(22 /* TokenType.EXPANSION_CASE_EXP_START */);
}
_consumeExpansionCaseEnd() {
this._beginToken(23 /* TokenType.EXPANSION_CASE_EXP_END */);
this._requireCharCode(chars.$RBRACE);
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._expansionCaseStack.pop();
}
_consumeExpansionFormEnd() {
this._beginToken(24 /* TokenType.EXPANSION_FORM_END */);
this._requireCharCode(chars.$RBRACE);
this._endToken([]);
this._expansionCaseStack.pop();
}
/**
* Consume a string that may contain interpolation expressions.
*
* The first token consumed will be of `tokenType` and then there will be alternating
* `interpolationTokenType` and `tokenType` tokens until the `endPredicate()` returns true.
*
* If an interpolation token ends prematurely it will have no end marker in its `parts` array.
*
* @param textTokenType the kind of tokens to interleave around interpolation tokens.
* @param interpolationTokenType the kind of tokens that contain interpolation.
* @param endPredicate a function that should return true when we should stop consuming.
* @param endInterpolation a function that should return true if there is a premature end to an
* interpolation expression - i.e. before we get to the normal interpolation closing marker.
*/
_consumeWithInterpolation(textTokenType, interpolationTokenType, endPredicate, endInterpolation) {
this._beginToken(textTokenType);
const parts = [];
while (!endPredicate()) {
const current = this._cursor.clone();
if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
this._endToken([this._processCarriageReturns(parts.join(''))], current);
parts.length = 0;
this._consumeInterpolation(interpolationTokenType, current, endInterpolation);
this._beginToken(textTokenType);
}
else if (this._cursor.peek() === chars.$AMPERSAND) {
this._endToken([this._processCarriageReturns(parts.join(''))]);
parts.length = 0;
this._consumeEntity(textTokenType);
this._beginToken(textTokenType);
}
else {
parts.push(this._readChar());
}
}
// It is possible that an interpolation was started but not ended inside this text token.
// Make sure that we reset the state of the lexer correctly.
this._inInterpolation = false;
const value = this._processCarriageReturns(parts.join(''));
this._endToken([value]);
return value;
}
/**
* Consume a block of text that has been interpreted as an Angular interpolation.
*
* @param interpolationTokenType the type of the interpolation token to generate.
* @param interpolationStart a cursor that points to the start of this interpolation.
* @param prematureEndPredicate a function that should return true if the next characters indicate
* an end to the interpolation before its normal closing marker.
*/
_consumeInterpolation(interpolationTokenType, interpolationStart, prematureEndPredicate) {
const parts = [];
this._beginToken(interpolationTokenType, interpolationStart);
parts.push(this._interpolationConfig.start);
// Find the end of the interpolation, ignoring content inside quotes.
const expressionStart = this._cursor.clone();
let inQuote = null;
let inComment = false;
while (this._cursor.peek() !== chars.$EOF &&
(prematureEndPredicate === null || !prematureEndPredicate())) {
const current = this._cursor.clone();
if (this._isTagStart()) {
// We are starting what looks like an HTML element in the middle of this interpolation.
// Reset the cursor to before the `<` character and end the interpolation token.
// (This is actually wrong but here for backward compatibility).
this._cursor = current;
parts.push(this._getProcessedChars(expressionStart, current));
this._endToken(parts);
return;
}
if (inQuote === null) {
if (this._attemptStr(this._interpolationConfig.end)) {
// We are not in a string, and we hit the end interpolation marker
parts.push(this._getProcessedChars(expressionStart, current));
parts.push(this._interpolationConfig.end);
this._endToken(parts);
return;
}
else if (this._attemptStr('//')) {
// Once we are in a comment we ignore any quotes
inComment = true;
}
}
const char = this._cursor.peek();
this._cursor.advance();
if (char === chars.$BACKSLASH) {
// Skip the next character because it was escaped.
this._cursor.advance();
}
else if (char === inQuote) {
// Exiting the current quoted string
inQuote = null;
}
else if (!inComment && inQuote === null && chars.isQuote(char)) {
// Entering a new quoted string
inQuote = char;
}
}
// We hit EOF without finding a closing interpolation marker
parts.push(this._getProcessedChars(expressionStart, this._cursor));
this._endToken(parts);
}
_consumeDirective(start, nameStart) {
this._requireCharCode(chars.$AT);
// Skip over the @ since it's not part of the name.
this._cursor.advance();
// Capture the rest of the name.
while (isSelectorlessNameChar(this._cursor.peek())) {
this._cursor.advance();
}
// Capture the opening token.
this._beginToken(39 /* TokenType.DIRECTIVE_NAME */, start);
const name = this._cursor.getChars(nameStart);
this._endToken([name]);
this._attemptCharCodeUntilFn(isNotWhitespace);
// Optionally there might be attributes bound to the specific directive.
// Stop parsing if there's no opening character for them.
if (this._cursor.peek() !== chars.$LPAREN) {
return;
}
this._openDirectiveCount++;
this._beginToken(40 /* TokenType.DIRECTIVE_OPEN */);
this._cursor.advance();
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
// Capture all the attributes until we hit a closing paren.
while (!isAttributeTerminator(this._cursor.peek()) && this._cursor.peek() !== chars.$RPAREN) {
this._consumeAttribute();
}
// Trim any trailing whitespace.
this._attemptCharCodeUntilFn(isNotWhitespace);
this._openDirectiveCount--;
if (this._cursor.peek() !== chars.$RPAREN) {
// Stop parsing, instead of throwing, if we've hit the end of the tag.
// This can be handled better later when turning the tokens into AST.
if (this._cursor.peek() === chars.$GT || this._cursor.peek() === chars.$SLASH) {
return;
}
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
}
// Capture the closing token.
this._beginToken(41 /* TokenType.DIRECTIVE_CLOSE */);
this._cursor.advance();
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
}
_getProcessedChars(start, end) {
return this._processCarriageReturns(end.getChars(start));
}
_isTextEnd() {
if (this._isTagStart() || this._cursor.peek() === chars.$EOF) {
return true;
}
if (this._tokenizeIcu && !this._inInterpolation) {
if (this.isExpansionFormStart()) {
// start of an expansion form
return true;
}
if (this._cursor.peek() === chars.$RBRACE && this._isInExpansionCase()) {
// end of and expansion case
return true;
}
}
if (this._tokenizeBlocks &&
!this._inInterpolation &&
!this._isInExpansion() &&
(this._isBlockStart() || this._isLetStart() || this._cursor.peek() === chars.$RBRACE)) {
return true;
}
return false;
}
/**
* Returns true if the current cursor is pointing to the start of a tag
* (opening/closing/comments/cdata/etc).
*/
_isTagStart() {
if (this._cursor.peek() === chars.$LT) {
// We assume that `<` followed by whitespace is not the start of an HTML element.
const tmp = this._cursor.clone();
tmp.advance();
// If the next character is alphabetic, ! nor / then it is a tag start
const code = tmp.peek();
if ((chars.$a <= code && code <= chars.$z) ||
(chars.$A <= code && code <= chars.$Z) ||
code === chars.$SLASH ||
code === chars.$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] ===
22) /* TokenType.EXPANSION_CASE_EXP_START */;
}
_isInExpansionForm() {
return (this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
20) /* TokenType.EXPANSION_FORM_START */;
}
isExpansionFormStart() {
if (this._cursor.peek() !== chars.$LBRACE) {
return false;
}
if (this._interpolationConfig) {
const start = this._cursor.clone();
const isInterpolation = this._attemptStr(this._interpolationConfig.start);
this._cursor = start;
return !isInterpolation;
}
return true;
}
_handleFullNameStackForTagOpen(prefix, tagName) {
const fullName = mergeNsAndName(prefix, tagName);
if (this._fullNameStack.length === 0 ||
this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
this._fullNameStack.push(fullName);
}
}
_handleFullNameStackForTagClose(prefix, tagName) {
const fullName = mergeNsAndName(prefix, tagName);
if (this._fullNameStack.length !== 0 &&
this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
this._fullNameStack.pop();
}
}
}
function isNotWhitespace(code) {
return !chars.isWhitespace(code) || code === chars.$EOF;
}
function isNameEnd(code) {
return (chars.isWhitespace(code) ||
code === chars.$GT ||
code === chars.$LT ||
code === chars.$SLASH ||
code === chars.$SQ ||
code === chars.$DQ ||
code === chars.$EQ ||
code === chars.$EOF);
}
function isPrefixEnd(code) {
return ((code < chars.$a || chars.$z < code) &&
(code < chars.$A || chars.$Z < code) &&
(code < chars.$0 || code > chars.$9));
}
function isDigitEntityEnd(code) {
return code === chars.$SEMICOLON || code === chars.$EOF || !chars.isAsciiHexDigit(code);
}
function isNamedEntityEnd(code) {
return code === chars.$SEMICOLON || code === chars.$EOF || !chars.isAsciiLetter(code);
}
function isExpansionCaseStart(peek) {
return peek !== chars.$RBRACE;
}
function compareCharCodeCaseInsensitive(code1, code2) {
return toUpperCaseCharCode(code1) === toUpperCaseCharCode(code2);
}
function toUpperCaseCharCode(code) {
return code >= chars.$a && code <= chars.$z ? code - chars.$a + chars.$A : code;
}
function isBlockNameChar(code) {
return chars.isAsciiLetter(code) || chars.isDigit(code) || code === chars.$_;
}
function isBlockParameterChar(code) {
return code !== chars.$SEMICOLON && isNotWhitespace(code);
}
function isSelectorlessNameStart(code) {
return code === chars.$_ || (code >= chars.$A && code <= chars.$Z);
}
function isSelectorlessNameChar(code) {
return chars.isAsciiLetter(code) || chars.isDigit(code) || code === chars.$_;
}
function isAttributeTerminator(code) {
return code === chars.$SLASH || code === chars.$GT || code === chars.$LT || code === chars.$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 /* TokenType.TEXT */ && token.type === 5) /* TokenType.TEXT */) ||
((lastDstToken &&
lastDstToken.type === 16 /* TokenType.ATTR_VALUE_TEXT */ && token.type === 16) /* TokenType.ATTR_VALUE_TEXT */)) {
lastDstToken.parts[0] += token.parts[0];
lastDstToken.sourceSpan.end = token.sourceSpan.end;
}
else {
lastDstToken = token;
dstTokens.push(lastDstToken);
}
}
return dstTokens;
}
class PlainCharacterCursor {
constructor(fileOrCursor, range) {
if (fileOrCursor instanceof PlainCharacterCursor) {
this.file = fileOrCursor.file;
this.input = fileOrCursor.input;
this.end = fileOrCursor.end;
const state = fileOrCursor.state;
// Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
// In ES5 bundles the object spread operator is translated into the `__assign` helper, which
// is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
// called in tight loops, this difference matters.
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 === chars.$LF) {
state.line++;
state.column = 0;
}
else if (!chars.isNewLine(currentChar)) {
state.column++;
}
state.offset++;
this.updatePeek(state);
}
updatePeek(state) {
state.peek = state.offset >= this.end ? chars.$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 {
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;
}
/**
* Process the escape sequence that starts at the current position in the text.
*
* This method is called to ensure that `peek` has the unescaped value of escape sequences.
*/
processEscapeSequence() {
const peek = () => this.internalState.peek;
if (peek() === chars.$BACKSLASH) {
// We have hit an escape sequence so we need the internal state to become independent
// of the external state.
this.internalState = { ...this.state };
// Move past the backslash
this.advanceState(this.internalState);
// First check for standard control char sequences
if (peek() === chars.$n) {
this.state.peek = chars.$LF;
}
else if (peek() === chars.$r) {
this.state.peek = chars.$CR;
}
else if (peek() === chars.$v) {
this.state.peek = chars.$VTAB;
}
else if (peek() === chars.$t) {
this.state.peek = chars.$TAB;
}
else if (peek() === chars.$b) {
this.state.peek = chars.$BSPACE;
}
else if (peek() === chars.$f) {
this.state.peek = chars.$FF;
}
// Now consider more complex sequences
else if (peek() === chars.$u) {
// Unicode code-point sequence
this.advanceState(this.internalState); // advance past the `u` char
if (peek() === chars.$LBRACE) {
// Variable length Unicode, e.g. `\x{123}`
this.advanceState(this.internalState); // advance past the `{` char
// Advance past the variable number of hex digits until we hit a `}` char
const digitStart = this.clone();
let length = 0;
while (peek() !== chars.$RBRACE) {
this.advanceState(this.internalState);
length++;
}
this.state.peek = this.decodeHexDigits(digitStart, length);
}
else {
// Fixed length Unicode, e.g. `\u1234`
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() === chars.$x) {
// Hex char code, e.g. `\x2F`
this.advanceState(this.internalState); // advance past the `x` char
const digitStart = this.clone();
this.advanceState(this.internalState);
this.state.peek = this.decodeHexDigits(digitStart, 2);
}
else if (chars.isOctalDigit(peek())) {
// Octal char code, e.g. `\012`,
let octal = '';
let length = 0;
let previous = this.clone();
while (chars.isOctalDigit(peek()) && length < 3) {
previous = this.clone();
octal += String.fromCodePoint(peek());
this.advanceState(this.internalState);
length++;
}
this.state.peek = parseInt(octal, 8);
// Backup one char
this.internalState = previous.internalState;
}
else if (chars.isNewLine(this.internalState.peek)) {
// Line continuation `\` followed by a new line
this.advanceState(this.internalState); // advance over the newline
this.state = this.internalState;
}
else {
// If none of the `if` blocks were executed then we just have an escaped normal character.
// In that case we just, effectively, skip the backslash from the character.
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);
}
}
}
export class CursorError extends Error {
constructor(msg, cursor) {
super(msg);
this.msg = msg;
this.cursor = cursor;
// Extending `Error` does not always work when code is transpiled. See:
// https://stackoverflow.com/questions/41102060/typescript-extending-error-class
Object.setPrototypeOf(this, new.target.prototype);
}
}
/**
* @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
*/
import { ParseError, ParseSourceSpan } from '../parse_util';
import * as html from './ast';
import { TokenizeOptions } from './lexer';
import { TagContentType, TagDefinition } from './tags';
export declare class TreeError extends ParseError {
elementName: string | null;
static create(elementName: string | null, span: ParseSourceSpan, msg: string): TreeError;
constructor(elementName: string | null, span: ParseSourceSpan, msg: string);
}
export declare class ParseTreeResult {
rootNodes: html.Node[];
errors: ParseError[];
constructor(rootNodes: html.Node[], errors: ParseError[]);
}
export declare class Parser {
getTagDefinition: (tagName: string) => TagDefinition;
constructor(getTagDefinition: (tagName: string) => TagDefinition);
parse(source: string, url: string, options?: TokenizeOptions, isTagNameCaseSensitive?: boolean, getTagContentType?: (tagName: string, prefix: string, hasParent: boolean, attrs: Array<{
prefix: string;
name: string;
value?: string;
}>) => void | TagContentType): ParseTreeResult;
}
/**
* @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
*/
import { ParseError, ParseSourceSpan } from "../parse_util.js";
import * as html from "./ast.js";
import { NAMED_ENTITIES } from "./entities.js";
import { tokenize } from "./lexer.js";
import { getNsPrefix, mergeNsAndName, splitNsName } from "./tags.js";
export class TreeError extends ParseError {
static create(elementName, span, msg) {
return new TreeError(elementName, span, msg);
}
constructor(elementName, span, msg) {
super(span, msg);
this.elementName = elementName;
}
}
export class ParseTreeResult {
constructor(rootNodes, errors) {
this.rootNodes = rootNodes;
this.errors = errors;
}
}
export class Parser {
constructor(getTagDefinition) {
this.getTagDefinition = getTagDefinition;
}
parse(source, url, options, isTagNameCaseSensitive = false, getTagContentType) {
const lowercasify = (fn) => (x, ...args) => fn(x.toLowerCase(), ...args);
const getTagDefinition = isTagNameCaseSensitive ? this.getTagDefinition : lowercasify(this.getTagDefinition);
const getDefaultTagContentType = (tagName) => getTagDefinition(tagName).getContentType();
const getTagContentTypeWithProcessedTagName = isTagNameCaseSensitive ? getTagContentType : lowercasify(getTagContentType);
const _getTagContentType = getTagContentType ?
(tagName, prefix, hasParent, attrs) => {
const contentType = getTagContentTypeWithProcessedTagName(tagName, prefix, hasParent, attrs);
return contentType !== undefined ? contentType : getDefaultTagContentType(tagName);
} :
getDefaultTagContentType;
const tokenizeResult = tokenize(source, url, _getTagContentType, options);
const canSelfClose = (options && options.canSelfClose) || false;
const allowHtmComponentClosingTags = (options && options.allowHtmComponentClosingTags) || false;
const parser = new _TreeBuilder(tokenizeResult.tokens, getTagDefinition, canSelfClose, allowHtmComponentClosingTags, isTagNameCaseSensitive);
parser.build();
return new ParseTreeResult(parser.rootNodes, [...tokenizeResult.errors, ...parser.errors]);
}
}
class _TreeBuilder {
constructor(tokens, tagDefinitionResolver, canSelfClose, allowHtmComponentClosingTags, isTagNameCaseSensitive) {
this.tokens = tokens;
this.tagDefinitionResolver = tagDefinitionResolver;
this.canSelfClose = canSelfClose;
this.allowHtmComponentClosingTags = allowHtmComponentClosingTags;
this.isTagNameCaseSensitive = isTagNameCaseSensitive;
this._index = -1;
this._containerStack = [];
this.rootNodes = [];
this.errors = [];
this._advance();
}
build() {
while (this._peek.type !== 42 /* TokenType.EOF */) {
if (this._peek.type === 0 /* TokenType.TAG_OPEN_START */ ||
this._peek.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {
this._consumeElementStartTag(this._advance());
}
else if (this._peek.type === 3 /* TokenType.TAG_CLOSE */) {
this._closeVoidElement();
this._consumeElementEndTag(this._advance());
}
else if (this._peek.type === 12 /* TokenType.CDATA_START */) {
this._closeVoidElement();
this._consumeCdata(this._advance());
}
else if (this._peek.type === 10 /* TokenType.COMMENT_START */) {
this._closeVoidElement();
this._consumeComment(this._advance());
}
else if (this._peek.type === 5 /* TokenType.TEXT */ ||
this._peek.type === 7 /* TokenType.RAW_TEXT */ ||
this._peek.type === 6 /* TokenType.ESCAPABLE_RAW_TEXT */) {
this._closeVoidElement();
this._consumeText(this._advance());
}
else if (this._peek.type === 20 /* TokenType.EXPANSION_FORM_START */) {
this._consumeExpansion(this._advance());
}
else if (this._peek.type === 25 /* TokenType.BLOCK_OPEN_START */) {
this._closeVoidElement();
this._consumeBlockOpen(this._advance());
}
else if (this._peek.type === 27 /* TokenType.BLOCK_CLOSE */) {
this._closeVoidElement();
this._consumeBlockClose(this._advance());
}
else if (this._peek.type === 29 /* TokenType.INCOMPLETE_BLOCK_OPEN */) {
this._closeVoidElement();
this._consumeIncompleteBlock(this._advance());
}
else if (this._peek.type === 30 /* TokenType.LET_START */) {
this._closeVoidElement();
this._consumeLet(this._advance());
}
else if (this._peek.type === 18 /* TokenType.DOC_TYPE_START */) {
this._consumeDocType(this._advance());
}
else if (this._peek.type === 33 /* TokenType.INCOMPLETE_LET */) {
this._closeVoidElement();
this._consumeIncompleteLet(this._advance());
}
else if (this._peek.type === 34 /* TokenType.COMPONENT_OPEN_START */ ||
this._peek.type === 38 /* TokenType.INCOMPLETE_COMPONENT_OPEN */) {
this._consumeComponentStartTag(this._advance());
}
else if (this._peek.type === 37 /* TokenType.COMPONENT_CLOSE */) {
this._consumeComponentEndTag(this._advance());
}
else {
// Skip all other tokens...
this._advance();
}
}
for (const leftoverContainer of this._containerStack) {
// Unlike HTML elements, blocks aren't closed implicitly by the end of the file.
if (leftoverContainer instanceof html.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) {
// Note: there is always an EOF token at the end
this._index++;
}
this._peek = this.tokens[this._index];
return prev;
}
_advanceIf(type) {
if (this._peek.type === type) {
return this._advance();
}
return null;
}
_consumeCdata(startToken) {
const text = this._advance();
const value = this._getText(text);
const endToken = this._advanceIf(13 /* TokenType.CDATA_END */);
this._addToParent(new html.CDATA(value, new ParseSourceSpan(startToken.sourceSpan.start, (endToken || text).sourceSpan.end), [text]));
}
_consumeComment(token) {
const text = this._advanceIf(7 /* TokenType.RAW_TEXT */);
const endToken = this._advanceIf(11 /* TokenType.COMMENT_END */);
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 html.Comment(value, sourceSpan));
}
_consumeDocType(startToken) {
const text = this._advanceIf(7 /* TokenType.RAW_TEXT */);
const endToken = this._advanceIf(19 /* TokenType.DOC_TYPE_END */);
const value = text != null ? text.parts[0].trim() : null;
const sourceSpan = new ParseSourceSpan(startToken.sourceSpan.start, (endToken || text || startToken).sourceSpan.end);
this._addToParent(new html.DocType(value, sourceSpan));
}
_consumeExpansion(token) {
const switchValue = this._advance();
const type = this._advance();
const cases = [];
// read =
while (this._peek.type === 21 /* TokenType.EXPANSION_CASE_VALUE */) {
const expCase = this._parseExpansionCase();
if (!expCase)
return; // error
cases.push(expCase);
}
// read the final }
if (this._peek.type !== 24 /* TokenType.EXPANSION_FORM_END */) {
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 html.Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
this._advance();
}
_parseExpansionCase() {
const value = this._advance();
// read {
if (this._peek.type !== 22 /* TokenType.EXPANSION_CASE_EXP_START */) {
this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
return null;
}
// read until }
const start = this._advance();
const exp = this._collectExpansionExpTokens(start);
if (!exp)
return null;
const end = this._advance();
exp.push({ type: 42 /* TokenType.EOF */, parts: [], sourceSpan: end.sourceSpan });
// parse everything in between { and }
const expansionCaseParser = new _TreeBuilder(exp, this.tagDefinitionResolver, this.canSelfClose, this.allowHtmComponentClosingTags, this.isTagNameCaseSensitive);
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 html.ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
}
_collectExpansionExpTokens(start) {
const exp = [];
const expansionFormStack = [22 /* TokenType.EXPANSION_CASE_EXP_START */];
while (true) {
if (this._peek.type === 20 /* TokenType.EXPANSION_FORM_START */ ||
this._peek.type === 22 /* TokenType.EXPANSION_CASE_EXP_START */) {
expansionFormStack.push(this._peek.type);
}
if (this._peek.type === 23 /* TokenType.EXPANSION_CASE_EXP_END */) {
if (lastOnStack(expansionFormStack, 22 /* TokenType.EXPANSION_CASE_EXP_START */)) {
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 === 24 /* TokenType.EXPANSION_FORM_END */) {
if (lastOnStack(expansionFormStack, 20 /* TokenType.EXPANSION_FORM_START */)) {
expansionFormStack.pop();
}
else {
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
return null;
}
}
if (this._peek.type === 42 /* TokenType.EOF */) {
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
return null;
}
exp.push(this._advance());
}
}
_getText(token) {
let text = token.parts[0];
if (text.length > 0 && text[0] == '\n') {
const parent = this._getClosestElementLikeParent();
if (parent != null && parent.children.length == 0 &&
this._getTagDefinition(parent)?.ignoreFirstLf) {
text = text.substring(1);
}
}
return text;
}
_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 /* TokenType.INTERPOLATION */ ||
this._peek.type === 5 /* TokenType.TEXT */ ||
this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) {
token = this._advance();
tokens.push(token);
if (token.type === 8 /* TokenType.INTERPOLATION */) {
// For backward compatibility we decode HTML entities that appear in interpolation
// expressions. This is arguably a bug, but it could be a considerable breaking change to
// fix it. It should be addressed in a larger project to refactor the entire parser/lexer
// chain after View Engine has been removed.
text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity);
}
else if (token.type === 9 /* TokenType.ENCODED_ENTITY */) {
text += token.parts[0];
}
else {
text += token.parts.join('');
}
}
if (text.length > 0) {
const endSpan = token.sourceSpan;
this._addToParent(new html.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;
// Note: There could have been a tokenizer error
// so that we don't get a token for the end tag...
if (this._peek.type === 2 /* TokenType.TAG_OPEN_END_VOID */) {
this._advance();
selfClosing = true;
const tagDef = this._getTagDefinition(fullName);
if (!(this.canSelfClose || 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 /* TokenType.TAG_OPEN_END */) {
this._advance();
selfClosing = false;
}
const end = this._peek.sourceSpan.fullStart;
const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
const nameSpan = new ParseSourceSpan(startTagToken.sourceSpan.start.moveBy(1), startTagToken.sourceSpan.end);
const el = new html.Element(fullName, attrs, directives, [], selfClosing, span, startSpan, undefined, nameSpan, tagDef?.isVoid ?? false);
const parent = this._getContainer();
const isClosedByChild = parent !== null && !!this._getTagDefinition(parent)?.isClosedByChild(el.name);
this._pushContainer(el, isClosedByChild);
if (selfClosing) {
// Elements that are self-closed have their `endSourceSpan` set to the full span, as the
// element start tag also represents the end tag.
this._popContainer(fullName, html.Element, span);
}
else if (startTagToken.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {
// We already know the opening tag is not complete, so it is unlikely it has a corresponding
// close tag. Let's optimistically parse it as a full element and emit an error.
this._popContainer(fullName, html.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 === 36 /* TokenType.COMPONENT_OPEN_END_VOID */;
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 html.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, html.Component, span);
}
else if (startToken.type === 38 /* TokenType.INCOMPLETE_COMPONENT_OPEN */) {
this._popContainer(fullName, html.Component, null);
this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
}
}
_consumeAttributesAndDirectives(attributesResult, directivesResult) {
while (this._peek.type === 14 /* TokenType.ATTR_NAME */ ||
this._peek.type === 39 /* TokenType.DIRECTIVE_NAME */) {
if (this._peek.type === 39 /* TokenType.DIRECTIVE_NAME */) {
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, html.Component, endToken.sourceSpan)) {
const container = this._containerStack[this._containerStack.length - 1];
let suffix;
if (container instanceof html.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 html.Element) {
return this.tagDefinitionResolver(nodeOrName.name);
}
else if (nodeOrName instanceof html.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) {
// @ts-expect-error -- in angular-html-parser endTagToken.parts.length can be 0 (HTM component end-tags)
const fullName = this.allowHtmComponentClosingTags && endTagToken.parts.length === 0 ?
null :
this._getElementFullName(endTagToken, this._getClosestElementLikeParent());
;
if (fullName && 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, html.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));
}
}
/**
* Closes the nearest element with the tag name `fullName` in the parse tree.
* `endSourceSpan` is the span of the closing tag, or null if the element does
* not have a closing tag (for example, this happens when an incomplete
* opening tag is recovered).
*/
_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 html.Component ? node.fullName : node.name;
if (
/* isForeignElement */ getNsPrefix(nodeName) ?
nodeName === expectedName :
(nodeName === expectedName || expectedName === null) && node instanceof expectedType) {
// Record the parse span with the element that is being closed. Any elements that are
// removed from the element stack at this point are closed implicitly, so they won't get
// an end source span (as there is no explicit closing element).
node.endSourceSpan = endSourceSpan;
node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end;
this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex);
return !unexpectedCloseTagDetected;
}
// Blocks and most elements are not self closing.
if (node instanceof html.Block || !this._getTagDefinition(node)?.closedByParent) {
// Note that we encountered an unexpected close tag but continue processing the element
// stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this
// end tag in the stack.
unexpectedCloseTagDetected = true;
}
}
return false;
}
_consumeAttr(attrName) {
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
let attrEnd = attrName.sourceSpan.end;
let startQuoteToken;
// Consume any quote
if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) {
startQuoteToken = this._advance();
}
// Consume the attribute value
let value = '';
const valueTokens = [];
let valueStartSpan = undefined;
let valueEnd = undefined;
// NOTE: We need to use a new variable `nextTokenType` here to hide the actual type of
// `_peek.type` from TS. Otherwise TS will narrow the type of `_peek.type` preventing it from
// being able to consider `ATTR_VALUE_INTERPOLATION` as an option. This is because TS is not
// able to see that `_advance()` will actually mutate `_peek`.
const nextTokenType = this._peek.type;
if (nextTokenType === 16 /* TokenType.ATTR_VALUE_TEXT */) {
valueStartSpan = this._peek.sourceSpan;
valueEnd = this._peek.sourceSpan.end;
while (this._peek.type === 16 /* TokenType.ATTR_VALUE_TEXT */ ||
this._peek.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */ ||
this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) {
const valueToken = this._advance();
valueTokens.push(valueToken);
if (valueToken.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */) {
// For backward compatibility we decode HTML entities that appear in interpolation
// expressions. This is arguably a bug, but it could be a considerable breaking change to
// fix it. It should be addressed in a larger project to refactor the entire parser/lexer
// chain after View Engine has been removed.
value += valueToken.parts.join('').replace(/&([^;]+);/g, decodeEntity);
}
else if (valueToken.type === 9 /* TokenType.ENCODED_ENTITY */) {
value += valueToken.parts[0];
}
else {
value += valueToken.parts.join('');
}
valueEnd = attrEnd = valueToken.sourceSpan.end;
}
}
// Consume any quote
if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) {
const quoteToken = this._advance();
valueEnd = attrEnd = quoteToken.sourceSpan.end;
}
const valueSpan = valueStartSpan &&
valueEnd &&
new ParseSourceSpan(startQuoteToken?.sourceSpan.start ?? valueStartSpan.start, valueEnd, startQuoteToken?.sourceSpan.fullStart ?? valueStartSpan.fullStart);
return new html.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 === 40 /* TokenType.DIRECTIVE_OPEN */) {
// Capture the opening token in the start span.
startSourceSpanEnd = this._peek.sourceSpan.end;
this._advance();
// Cast here is necessary, because TS doesn't know that `_advance` changed `_peek`.
while (this._peek.type === 14 /* TokenType.ATTR_NAME */) {
attributes.push(this._consumeAttr(this._advance()));
}
if (this._peek.type === 41 /* TokenType.DIRECTIVE_CLOSE */) {
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 html.Directive(nameToken.parts[0], attributes, sourceSpan, startSourceSpan, endSourceSpan);
}
_consumeBlockOpen(token) {
const parameters = [];
while (this._peek.type === 28 /* TokenType.BLOCK_PARAMETER */) {
const paramToken = this._advance();
parameters.push(new html.BlockParameter(paramToken.parts[0], paramToken.sourceSpan));
}
if (this._peek.type === 26 /* TokenType.BLOCK_OPEN_END */) {
this._advance();
}
const end = this._peek.sourceSpan.fullStart;
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
const block = new html.Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
this._pushContainer(block, false);
}
_consumeBlockClose(token) {
if (!this._popContainer(null, html.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 "&#125;" ` +
`HTML entity instead.`));
}
}
_consumeIncompleteBlock(token) {
const parameters = [];
while (this._peek.type === 28 /* TokenType.BLOCK_PARAMETER */) {
const paramToken = this._advance();
parameters.push(new html.BlockParameter(paramToken.parts[0], paramToken.sourceSpan));
}
const end = this._peek.sourceSpan.fullStart;
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
const block = new html.Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
this._pushContainer(block, false);
// Incomplete blocks don't have children so we close them immediately and report an error.
this._popContainer(null, html.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 "&#64;" HTML entity instead.`));
}
_consumeLet(startToken) {
const name = startToken.parts[0];
let valueToken;
let endToken;
if (this._peek.type !== 31 /* TokenType.LET_VALUE */) {
this.errors.push(TreeError.create(startToken.parts[0], startToken.sourceSpan, `Invalid @let declaration "${name}". Declaration must have a value.`));
return;
}
else {
valueToken = this._advance();
}
// Type cast is necessary here since TS narrowed the type of `peek` above.
if (this._peek.type !== 32 /* TokenType.LET_END */) {
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);
// The start token usually captures the `@let`. Construct a name span by
// offsetting the start by the length of any text before the name.
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 html.LetDeclaration(name, valueToken.parts[0], span, nameSpan, valueToken.sourceSpan);
this._addToParent(node);
}
_consumeIncompleteLet(token) {
// Incomplete `@let` declaration may end up with an empty name.
const name = token.parts[0] ?? '';
const nameString = name ? ` "${name}"` : '';
// If there's at least a name, we can salvage an AST node that can be used for completions.
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 html.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 html.Element || current instanceof html.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 {
// TODO(crisbeto): re-evaluate this fallback. Maybe base it off the class name?
return mergeNsAndName(prefix, tagName || 'ng-component');
}
}
_getPrefix(token, parent) {
let prefix;
let tagName;
if (token.type === 34 /* TokenType.COMPONENT_OPEN_START */ ||
token.type === 38 /* TokenType.INCOMPLETE_COMPONENT_OPEN */ ||
token.type === 37 /* TokenType.COMPONENT_CLOSE */) {
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 html.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;
}
/**
* Decode the `entity` string, which we believe is the contents of an HTML entity.
*
* If the string is not actually a valid/known entity then just return the original `match` string.
*/
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;
}
/**
* @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
*/
export declare enum TagContentType {
RAW_TEXT = 0,
ESCAPABLE_RAW_TEXT = 1,
PARSABLE_DATA = 2
}
export interface TagDefinition {
closedByParent: boolean;
implicitNamespacePrefix: string | null;
isVoid: boolean;
ignoreFirstLf: boolean;
canSelfClose: boolean;
preventNamespaceInheritance: boolean;
isClosedByChild(name: string): boolean;
getContentType(prefix?: string): TagContentType;
}
export declare function splitNsName(elementName: string, fatal?: boolean): [string | null, string];
export declare function isNgContainer(tagName: string): boolean;
export declare function isNgContent(tagName: string): boolean;
export declare function isNgTemplate(tagName: string): boolean;
export declare function getNsPrefix(fullName: string): string;
export declare function getNsPrefix(fullName: null): null;
export declare function mergeNsAndName(prefix: string, localName: string): string;
/**
* @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
*/
export 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 = {}));
export 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)];
}
// `<ng-container>` tags work the same regardless the namespace
export function isNgContainer(tagName) {
return splitNsName(tagName)[1] === 'ng-container';
}
// `<ng-content>` tags work the same regardless the namespace
export function isNgContent(tagName) {
return splitNsName(tagName)[1] === 'ng-content';
}
// `<ng-template>` tags work the same regardless the namespace
export function isNgTemplate(tagName) {
return splitNsName(tagName)[1] === 'ng-template';
}
export function getNsPrefix(fullName) {
return fullName === null ? null : splitNsName(fullName)[0];
}
export function mergeNsAndName(prefix, localName) {
return prefix ? `:${prefix}:${localName}` : localName;
}
/**
* @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
*/
import { ParseSourceSpan } from '../parse_util';
export declare const enum TokenType {
TAG_OPEN_START = 0,
TAG_OPEN_END = 1,
TAG_OPEN_END_VOID = 2,
TAG_CLOSE = 3,
INCOMPLETE_TAG_OPEN = 4,
TEXT = 5,
ESCAPABLE_RAW_TEXT = 6,
RAW_TEXT = 7,
INTERPOLATION = 8,
ENCODED_ENTITY = 9,
COMMENT_START = 10,
COMMENT_END = 11,
CDATA_START = 12,
CDATA_END = 13,
ATTR_NAME = 14,
ATTR_QUOTE = 15,
ATTR_VALUE_TEXT = 16,
ATTR_VALUE_INTERPOLATION = 17,
DOC_TYPE_START = 18,
DOC_TYPE_END = 19,
EXPANSION_FORM_START = 20,
EXPANSION_CASE_VALUE = 21,
EXPANSION_CASE_EXP_START = 22,
EXPANSION_CASE_EXP_END = 23,
EXPANSION_FORM_END = 24,
BLOCK_OPEN_START = 25,
BLOCK_OPEN_END = 26,
BLOCK_CLOSE = 27,
BLOCK_PARAMETER = 28,
INCOMPLETE_BLOCK_OPEN = 29,
LET_START = 30,
LET_VALUE = 31,
LET_END = 32,
INCOMPLETE_LET = 33,
COMPONENT_OPEN_START = 34,
COMPONENT_OPEN_END = 35,
COMPONENT_OPEN_END_VOID = 36,
COMPONENT_CLOSE = 37,
INCOMPLETE_COMPONENT_OPEN = 38,
DIRECTIVE_NAME = 39,
DIRECTIVE_OPEN = 40,
DIRECTIVE_CLOSE = 41,
EOF = 42
}
export type Token = TagOpenStartToken | TagOpenEndToken | TagOpenEndVoidToken | TagCloseToken | IncompleteTagOpenToken | TextToken | InterpolationToken | EncodedEntityToken | CommentStartToken | CommentEndToken | CdataStartToken | CdataEndToken | AttributeNameToken | AttributeQuoteToken | AttributeValueTextToken | AttributeValueInterpolationToken | DocTypeStartToken | ExpansionFormStartToken | ExpansionCaseValueToken | ExpansionCaseExpressionStartToken | ExpansionCaseExpressionEndToken | ExpansionFormEndToken | EndOfFileToken | BlockParameterToken | BlockOpenStartToken | BlockOpenEndToken | BlockCloseToken | IncompleteBlockOpenToken | LetStartToken | LetValueToken | LetEndToken | IncompleteLetToken | ComponentOpenStartToken | ComponentOpenEndToken | ComponentOpenEndVoidToken | ComponentCloseToken | IncompleteComponentOpenToken | DirectiveNameToken | DirectiveOpenToken | DirectiveCloseToken;
export type InterpolatedTextToken = TextToken | InterpolationToken | EncodedEntityToken;
export type InterpolatedAttributeToken = AttributeValueTextToken | AttributeValueInterpolationToken | EncodedEntityToken;
export interface TokenBase {
type: TokenType;
parts: string[];
sourceSpan: ParseSourceSpan;
}
export interface TagOpenStartToken extends TokenBase {
type: TokenType.TAG_OPEN_START;
parts: [prefix: string, name: string];
}
export interface TagOpenEndToken extends TokenBase {
type: TokenType.TAG_OPEN_END;
parts: [];
}
export interface TagOpenEndVoidToken extends TokenBase {
type: TokenType.TAG_OPEN_END_VOID;
parts: [];
}
export interface TagCloseToken extends TokenBase {
type: TokenType.TAG_CLOSE;
parts: [prefix: string, name: string];
}
export interface IncompleteTagOpenToken extends TokenBase {
type: TokenType.INCOMPLETE_TAG_OPEN;
parts: [prefix: string, name: string];
}
export interface TextToken extends TokenBase {
type: TokenType.TEXT | TokenType.ESCAPABLE_RAW_TEXT | TokenType.RAW_TEXT;
parts: [text: string];
}
export interface InterpolationToken extends TokenBase {
type: TokenType.INTERPOLATION;
parts: [startMarker: string, expression: string, endMarker: string] | [startMarker: string, expression: string];
}
export interface EncodedEntityToken extends TokenBase {
type: TokenType.ENCODED_ENTITY;
parts: [decoded: string, encoded: string];
}
export interface CommentStartToken extends TokenBase {
type: TokenType.COMMENT_START;
parts: [];
}
export interface CommentEndToken extends TokenBase {
type: TokenType.COMMENT_END;
parts: [];
}
export interface CdataStartToken extends TokenBase {
type: TokenType.CDATA_START;
parts: [];
}
export interface CdataEndToken extends TokenBase {
type: TokenType.CDATA_END;
parts: [];
}
export interface AttributeNameToken extends TokenBase {
type: TokenType.ATTR_NAME;
parts: [prefix: string, name: string];
}
export interface AttributeQuoteToken extends TokenBase {
type: TokenType.ATTR_QUOTE;
parts: [quote: "'" | '"'];
}
export interface AttributeValueTextToken extends TokenBase {
type: TokenType.ATTR_VALUE_TEXT;
parts: [value: string];
}
export interface AttributeValueInterpolationToken extends TokenBase {
type: TokenType.ATTR_VALUE_INTERPOLATION;
parts: [startMarker: string, expression: string, endMarker: string] | [startMarker: string, expression: string];
}
export interface DocTypeStartToken extends TokenBase {
type: TokenType.DOC_TYPE_START;
parts: [];
}
export interface DocTypeEndToken extends TokenBase {
type: TokenType.DOC_TYPE_END;
parts: [];
}
export interface ExpansionFormStartToken extends TokenBase {
type: TokenType.EXPANSION_FORM_START;
parts: [];
}
export interface ExpansionCaseValueToken extends TokenBase {
type: TokenType.EXPANSION_CASE_VALUE;
parts: [value: string];
}
export interface ExpansionCaseExpressionStartToken extends TokenBase {
type: TokenType.EXPANSION_CASE_EXP_START;
parts: [];
}
export interface ExpansionCaseExpressionEndToken extends TokenBase {
type: TokenType.EXPANSION_CASE_EXP_END;
parts: [];
}
export interface ExpansionFormEndToken extends TokenBase {
type: TokenType.EXPANSION_FORM_END;
parts: [];
}
export interface EndOfFileToken extends TokenBase {
type: TokenType.EOF;
parts: [];
}
export interface BlockParameterToken extends TokenBase {
type: TokenType.BLOCK_PARAMETER;
parts: [expression: string];
}
export interface BlockOpenStartToken extends TokenBase {
type: TokenType.BLOCK_OPEN_START;
parts: [name: string];
}
export interface BlockOpenEndToken extends TokenBase {
type: TokenType.BLOCK_OPEN_END;
parts: [];
}
export interface BlockCloseToken extends TokenBase {
type: TokenType.BLOCK_CLOSE;
parts: [];
}
export interface IncompleteBlockOpenToken extends TokenBase {
type: TokenType.INCOMPLETE_BLOCK_OPEN;
parts: [name: string];
}
export interface LetStartToken extends TokenBase {
type: TokenType.LET_START;
parts: [name: string];
}
export interface LetValueToken extends TokenBase {
type: TokenType.LET_VALUE;
parts: [value: string];
}
export interface LetEndToken extends TokenBase {
type: TokenType.LET_END;
parts: [];
}
export interface IncompleteLetToken extends TokenBase {
type: TokenType.INCOMPLETE_LET;
parts: [name: string];
}
export interface ComponentOpenStartToken extends TokenBase {
type: TokenType.COMPONENT_OPEN_START;
parts: [componentName: string, prefix: string, tagName: string];
}
export interface ComponentOpenEndToken extends TokenBase {
type: TokenType.COMPONENT_OPEN_END;
parts: [];
}
export interface ComponentOpenEndVoidToken extends TokenBase {
type: TokenType.COMPONENT_OPEN_END_VOID;
parts: [];
}
export interface ComponentCloseToken extends TokenBase {
type: TokenType.COMPONENT_CLOSE;
parts: [componentName: string, prefix: string, tagName: string];
}
export interface IncompleteComponentOpenToken extends TokenBase {
type: TokenType.INCOMPLETE_COMPONENT_OPEN;
parts: [componentName: string, prefix: string, tagName: string];
}
export interface DirectiveNameToken extends TokenBase {
type: TokenType.DIRECTIVE_NAME;
parts: [name: string];
}
export interface DirectiveOpenToken extends TokenBase {
type: TokenType.DIRECTIVE_OPEN;
parts: [];
}
export interface DirectiveCloseToken extends TokenBase {
type: TokenType.DIRECTIVE_CLOSE;
parts: [];
}
/**
* @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
*/
export {};
export declare class ParseLocation {
file: ParseSourceFile;
offset: number;
line: number;
col: number;
constructor(file: ParseSourceFile, offset: number, line: number, col: number);
toString(): string;
moveBy(delta: number): ParseLocation;
getContext(maxChars: number, maxLines: number): {
before: string;
after: string;
} | null;
}
export declare class ParseSourceFile {
content: string;
url: string;
constructor(content: string, url: string);
}
export declare class ParseSourceSpan {
start: ParseLocation;
end: ParseLocation;
fullStart: ParseLocation;
details: string | null;
/**
* Create an object that holds information about spans of tokens/nodes captured during
* lexing/parsing of text.
*
* @param start
* The location of the start of the span (having skipped leading trivia).
* Skipping leading trivia makes source-spans more "user friendly", since things like HTML
* elements will appear to begin at the start of the opening tag, rather than at the start of any
* leading trivia, which could include newlines.
*
* @param end
* The location of the end of the span.
*
* @param fullStart
* The start of the token without skipping the leading trivia.
* This is used by tooling that splits tokens further, such as extracting Angular interpolations
* from text tokens. Such tooling creates new source-spans relative to the original token's
* source-span. If leading trivia characters have been skipped then the new source-spans may be
* incorrectly offset.
*
* @param details
* Additional information (such as identifier names) that should be associated with the span.
*/
constructor(start: ParseLocation, end: ParseLocation, fullStart?: ParseLocation, details?: string | null);
toString(): string;
}
export declare enum ParseErrorLevel {
WARNING = 0,
ERROR = 1
}
export declare class ParseError extends Error {
/** Location of the error. */
readonly span: ParseSourceSpan;
/** Error message. */
readonly msg: string;
/** Severity level of the error. */
readonly level: ParseErrorLevel;
/**
* Error that caused the error to be surfaced. For example, an error in a sub-expression that
* couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
*/
readonly relatedError?: unknown;
constructor(
/** Location of the error. */
span: ParseSourceSpan,
/** Error message. */
msg: string,
/** Severity level of the error. */
level?: ParseErrorLevel,
/**
* Error that caused the error to be surfaced. For example, an error in a sub-expression that
* couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
*/
relatedError?: unknown);
contextualMessage(): string;
toString(): string;
}
/**
* Generates Source Span object for a given R3 Type for JIT mode.
*
* @param kind Component or Directive.
* @param typeName name of the Component or Directive.
* @param sourceUrl reference to Component or Directive source.
* @returns instance of ParseSourceSpan that represent a given Component or Directive.
*/
export declare function r3JitTypeSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
export declare function identifierName(compileIdentifier: CompileIdentifierMetadata | null | undefined): string | null;
export interface CompileIdentifierMetadata {
reference: any;
}
export declare function sanitizeIdentifier(name: string): string;
/**
* @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
*/
import * as chars from "./chars.js";
import { stringify } from "./util.js";
export class ParseLocation {
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 == chars.$LF) {
line--;
const priorLine = source
.substring(0, offset - 1)
.lastIndexOf(String.fromCharCode(chars.$LF));
col = priorLine > 0 ? offset - priorLine : offset;
}
else {
col--;
}
}
while (offset < len && delta > 0) {
const ch = source.charCodeAt(offset);
offset++;
delta--;
if (ch == chars.$LF) {
line++;
col = 0;
}
else {
col++;
}
}
return new ParseLocation(this.file, offset, line, col);
}
// Return the source around the location
// Up to `maxChars` or `maxLines` on each side of the location
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;
}
}
export class ParseSourceFile {
constructor(content, url) {
this.content = content;
this.url = url;
}
}
export class ParseSourceSpan {
/**
* Create an object that holds information about spans of tokens/nodes captured during
* lexing/parsing of text.
*
* @param start
* The location of the start of the span (having skipped leading trivia).
* Skipping leading trivia makes source-spans more "user friendly", since things like HTML
* elements will appear to begin at the start of the opening tag, rather than at the start of any
* leading trivia, which could include newlines.
*
* @param end
* The location of the end of the span.
*
* @param fullStart
* The start of the token without skipping the leading trivia.
* This is used by tooling that splits tokens further, such as extracting Angular interpolations
* from text tokens. Such tooling creates new source-spans relative to the original token's
* source-span. If leading trivia characters have been skipped then the new source-spans may be
* incorrectly offset.
*
* @param details
* Additional information (such as identifier names) that should be associated with the span.
*/
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);
}
}
export var ParseErrorLevel;
(function (ParseErrorLevel) {
ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
})(ParseErrorLevel || (ParseErrorLevel = {}));
export class ParseError extends Error {
constructor(
/** Location of the error. */
span,
/** Error message. */
msg,
/** Severity level of the error. */
level = ParseErrorLevel.ERROR,
/**
* Error that caused the error to be surfaced. For example, an error in a sub-expression that
* couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
*/
relatedError) {
super(msg);
this.span = span;
this.msg = msg;
this.level = level;
this.relatedError = relatedError;
// Extending `Error` ends up breaking some internal tests. This appears to be a known issue
// when extending errors in TS and the workaround is to explicitly set the prototype.
// https://stackoverflow.com/questions/41102060/typescript-extending-error-class
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}`;
}
}
/**
* Generates Source Span object for a given R3 Type for JIT mode.
*
* @param kind Component or Directive.
* @param typeName name of the Component or Directive.
* @param sourceUrl reference to Component or Directive source.
* @returns instance of ParseSourceSpan that represent a given Component or Directive.
*/
export 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;
export function identifierName(compileIdentifier) {
if (!compileIdentifier || !compileIdentifier.reference) {
return null;
}
const ref = compileIdentifier.reference;
if (ref['__anonymousType']) {
return ref['__anonymousType'];
}
if (ref['__forward_ref__']) {
// We do not want to try to stringify a `forwardRef()` function because that would cause the
// inner function to be evaluated too early, defeating the whole point of the `forwardRef`.
return '__forward_ref__';
}
let identifier = stringify(ref);
if (identifier.indexOf('(') >= 0) {
// case: anonymous functions!
identifier = `anonymous_${_anonymousTypeIndex++}`;
ref['__anonymousType'] = identifier;
}
else {
identifier = sanitizeIdentifier(identifier);
}
return identifier;
}
export function sanitizeIdentifier(name) {
return name.replace(/\W/g, '_');
}
/**
* @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
*/
import { SchemaMetadata, SecurityContext } from '../core';
import { ElementSchemaRegistry } from './element_schema_registry';
export declare class DomElementSchemaRegistry extends ElementSchemaRegistry {
private _schema;
private _eventSchema;
constructor();
hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;
hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean;
/**
* securityContext returns the security context for the given property on the given DOM tag.
*
* Tag and property name are statically known and cannot change at runtime, i.e. it is not
* possible to bind a value into a changing attribute or tag name.
*
* The filtering is based on a list of allowed tags|attributes. All attributes in the schema
* above are assumed to have the 'NONE' security context, i.e. that they are safe inert
* string values. Only specific well known attack vectors are assigned their appropriate context.
*/
securityContext(tagName: string, propName: string, isAttribute: boolean): SecurityContext;
getMappedPropName(propName: string): string;
getDefaultComponentElementName(): string;
validateProperty(name: string): {
error: boolean;
msg?: string;
};
validateAttribute(name: string): {
error: boolean;
msg?: string;
};
allKnownElementNames(): string[];
allKnownAttributesOfElement(tagName: string): string[];
allKnownEventsOfElement(tagName: string): string[];
normalizeAnimationStyleProperty(propName: string): string;
normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string | number): {
error: string;
value: string;
};
}
/**
* @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
*/
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SecurityContext } from "../core.js";
import { isNgContainer, isNgContent } from "../ml_parser/tags.js";
import { dashCaseToCamelCase } from "../util.js";
import { SECURITY_SCHEMA } from "./dom_security_schema.js";
import { ElementSchemaRegistry } from "./element_schema_registry.js";
const BOOLEAN = 'boolean';
const NUMBER = 'number';
const STRING = 'string';
const OBJECT = 'object';
/**
* This array represents the DOM schema. It encodes inheritance, properties, and events.
*
* ## Overview
*
* Each line represents one kind of element. The `element_inheritance` and properties are joined
* using `element_inheritance|properties` syntax.
*
* ## Element Inheritance
*
* The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
* Here the individual elements are separated by `,` (commas). Every element in the list
* has identical properties.
*
* An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
* specified then `""` (blank) element is assumed.
*
* NOTE: The blank element inherits from root `[Element]` element, the super element of all
* elements.
*
* NOTE an element prefix such as `:svg:` has no special meaning to the schema.
*
* ## Properties
*
* Each element has a set of properties separated by `,` (commas). Each property can be prefixed
* by a special character designating its type:
*
* - (no prefix): property is a string.
* - `*`: property represents an event.
* - `!`: property is a boolean.
* - `#`: property is a number.
* - `%`: property is an object.
*
* ## Query
*
* The class creates an internal squas representation which allows to easily answer the query of
* if a given property exist on a given element.
*
* NOTE: We don't yet support querying for types or events.
* NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
* see dom_element_schema_registry_spec.ts
*/
// =================================================================================================
// =================================================================================================
// =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
// =================================================================================================
// =================================================================================================
//
// DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
//
// Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
// dom_security_schema.ts. Reach out to mprobst & rjamet for details.
//
// =================================================================================================
const SCHEMA = [
'[Element]|textContent,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColSpan,%ariaCurrent,%ariaDescription,%ariaDisabled,%ariaExpanded,%ariaHasPopup,%ariaHidden,%ariaKeyShortcuts,%ariaLabel,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%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' +
/* added manually to avoid breaking changes */
',*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',
// https://www.w3.org/TR/wai-aria-1.2/#accessibilityroleandproperties-correspondence
'aria-atomic': 'ariaAtomic',
'aria-autocomplete': 'ariaAutoComplete',
'aria-busy': 'ariaBusy',
'aria-checked': 'ariaChecked',
'aria-colcount': 'ariaColCount',
'aria-colindex': 'ariaColIndex',
'aria-colspan': 'ariaColSpan',
'aria-current': 'ariaCurrent',
'aria-disabled': 'ariaDisabled',
'aria-expanded': 'ariaExpanded',
'aria-haspopup': 'ariaHasPopup',
'aria-hidden': 'ariaHidden',
'aria-invalid': 'ariaInvalid',
'aria-keyshortcuts': 'ariaKeyShortcuts',
'aria-label': 'ariaLabel',
'aria-level': 'ariaLevel',
'aria-live': 'ariaLive',
'aria-modal': 'ariaModal',
'aria-multiline': 'ariaMultiLine',
'aria-multiselectable': 'ariaMultiSelectable',
'aria-orientation': 'ariaOrientation',
'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-rowspan': 'ariaRowSpan',
'aria-selected': 'ariaSelected',
'aria-setsize': 'ariaSetSize',
'aria-sort': 'ariaSort',
'aria-valuemax': 'ariaValueMax',
'aria-valuemin': 'ariaValueMin',
'aria-valuenow': 'ariaValueNow',
'aria-valuetext': 'ariaValueText',
}));
// Invert _ATTR_TO_PROP.
const _PROP_TO_ATTR = Array.from(_ATTR_TO_PROP).reduce((inverted, [propertyName, attributeName]) => {
inverted.set(propertyName, attributeName);
return inverted;
}, new Map());
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
constructor() {
super();
this._schema = new Map();
// We don't allow binding to events for security reasons. Allowing event bindings would almost
// certainly introduce bad XSS vulnerabilities. Instead, we store events in a separate schema.
this._eventSchema = new Map();
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)) {
// Can't tell now as we don't know which properties a custom element will get
// once it is instantiated
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)) {
// Allow any custom elements
return true;
}
}
return this._schema.has(tagName.toLowerCase());
}
/**
* securityContext returns the security context for the given property on the given DOM tag.
*
* Tag and property name are statically known and cannot change at runtime, i.e. it is not
* possible to bind a value into a changing attribute or tag name.
*
* The filtering is based on a list of allowed tags|attributes. All attributes in the schema
* above are assumed to have the 'NONE' security context, i.e. that they are safe inert
* string values. Only specific well known attack vectors are assigned their appropriate context.
*/
securityContext(tagName, propName, isAttribute) {
if (isAttribute) {
// NB: For security purposes, use the mapped property name, not the attribute name.
propName = this.getMappedPropName(propName);
}
// Make sure comparisons are case insensitive, so that case differences between attribute and
// property names do not have a security impact.
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');
// Convert properties to attributes.
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;
}
}
/**
* @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
*/
import { SecurityContext } from '../core';
export declare function SECURITY_SCHEMA(): {
[k: string]: SecurityContext;
};
/**
* The set of security-sensitive attributes of an `<iframe>` that *must* be
* applied as a static attribute only. This ensures that all security-sensitive
* attributes are taken into account while creating an instance of an `<iframe>`
* at runtime.
*
* Note: avoid using this set directly, use the `isIframeSecuritySensitiveAttr` function
* in the code instead.
*/
export declare const IFRAME_SECURITY_SENSITIVE_ATTRS: Set<string>;
/**
* Checks whether a given attribute name might represent a security-sensitive
* attribute of an <iframe>.
*/
export declare function isIframeSecuritySensitiveAttr(attrName: string): boolean;
/**
* @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
*/
import { SecurityContext } from "../core.js";
// =================================================================================================
// =================================================================================================
// =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
// =================================================================================================
// =================================================================================================
//
// DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
// Reach out to mprobst for details.
//
// =================================================================================================
/** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
let _SECURITY_SCHEMA;
export function SECURITY_SCHEMA() {
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',
'input|src',
'ins|cite',
'q|cite',
'source|src',
'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;
}
function registerContext(ctx, specs) {
for (const spec of specs)
_SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
}
/**
* The set of security-sensitive attributes of an `<iframe>` that *must* be
* applied as a static attribute only. This ensures that all security-sensitive
* attributes are taken into account while creating an instance of an `<iframe>`
* at runtime.
*
* Note: avoid using this set directly, use the `isIframeSecuritySensitiveAttr` function
* in the code instead.
*/
export const IFRAME_SECURITY_SENSITIVE_ATTRS = new Set([
'sandbox',
'allow',
'allowfullscreen',
'referrerpolicy',
'csp',
'fetchpriority',
]);
/**
* Checks whether a given attribute name might represent a security-sensitive
* attribute of an <iframe>.
*/
export function isIframeSecuritySensitiveAttr(attrName) {
// The `setAttribute` DOM API is case-insensitive, so we lowercase the value
// before checking it against a known security-sensitive attributes.
return IFRAME_SECURITY_SENSITIVE_ATTRS.has(attrName.toLowerCase());
}
/**
* @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
*/
import { SchemaMetadata, SecurityContext } from '../core';
export declare abstract class ElementSchemaRegistry {
abstract hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;
abstract hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean;
abstract securityContext(elementName: string, propName: string, isAttribute: boolean): SecurityContext;
abstract allKnownElementNames(): string[];
abstract getMappedPropName(propName: string): string;
abstract getDefaultComponentElementName(): string;
abstract validateProperty(name: string): {
error: boolean;
msg?: string;
};
abstract validateAttribute(name: string): {
error: boolean;
msg?: string;
};
abstract normalizeAnimationStyleProperty(propName: string): string;
abstract normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string | number): {
error: string;
value: string;
};
}
/**
* @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
*/
export class ElementSchemaRegistry {
}
/**
* @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
*/
export declare function dashCaseToCamelCase(input: string): string;
export declare function splitAtColon(input: string, defaultValues: (string | null)[]): (string | null)[];
export declare function splitAtPeriod(input: string, defaultValues: (string | null)[]): (string | null)[];
export declare function noUndefined<T>(val: T | undefined): T;
export declare function error(msg: string): never;
export declare function escapeRegExp(s: string): string;
export type Byte = number;
export declare function utf8Encode(str: string): Byte[];
export declare function stringify(token: any): string;
export declare class Version {
full: string;
readonly major: string;
readonly minor: string;
readonly patch: string;
constructor(full: string);
}
export interface Console {
log(message: string): void;
warn(message: string): void;
}
declare const _global: {
[name: string]: any;
};
export { _global as global };
export declare function getJitStandaloneDefaultForVersion(version: string): boolean;
/**
* @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 DASH_CASE_REGEXP = /-+([a-z0-9])/g;
export function dashCaseToCamelCase(input) {
return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
}
export function splitAtColon(input, defaultValues) {
return _splitAt(input, ':', defaultValues);
}
export 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()];
}
export function noUndefined(val) {
return val === undefined ? null : val;
}
export function error(msg) {
throw new Error(`Internal Error: ${msg}`);
}
// Escape characters that have a special meaning in Regular Expressions
export function escapeRegExp(s) {
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
export function utf8Encode(str) {
let encoded = [];
for (let index = 0; index < str.length; index++) {
let codePoint = str.charCodeAt(index);
// decode surrogate
// see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
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;
}
export 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';
}
// WARNING: do not try to `JSON.stringify(token)` here
// see https://github.com/angular/angular/issues/23440
const result = token.toString();
if (result == null) {
return '' + result;
}
const newLineIndex = result.indexOf('\n');
return newLineIndex >= 0 ? result.slice(0, newLineIndex) : result;
}
export class Version {
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;
export { _global as global };
const V1_TO_18 = /^([1-9]|1[0-8])\./;
export function getJitStandaloneDefaultForVersion(version) {
if (version.startsWith('0.')) {
// 0.0.0 is always "latest", default is true.
return true;
}
if (V1_TO_18.test(version)) {
// Angular v2 - v18 default is false.
return false;
}
// All other Angular versions (v19+) default to true.
return true;
}