Socket
Socket
Sign inDemoInstall

angular-html-parser

Package Overview
Dependencies
1
Maintainers
4
Versions
20
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.0.1 to 5.0.0

4

lib/angular-html-parser/src/index.d.ts

@@ -32,2 +32,6 @@ import { TagContentType } from '../../compiler/src/ml_parser/tags.js';

}>) => void | TagContentType;
/**
* tokenize blocks (Angular Control Flow Syntax)
*/
tokenizeBlocks?: boolean;
}

@@ -34,0 +38,0 @@ export declare function parse(input: string, options?: ParseOptions): ParseTreeResult;

3

lib/angular-html-parser/src/index.js

@@ -11,3 +11,3 @@ import { HtmlParser } from "../../compiler/src/ml_parser/html_parser.js";

export function parse(input, options = {}) {
const { canSelfClose = false, allowHtmComponentClosingTags = false, isTagNameCaseSensitive = false, getTagContentType, } = options;
const { canSelfClose = false, allowHtmComponentClosingTags = false, isTagNameCaseSensitive = false, getTagContentType, tokenizeBlocks = false, } = options;
return getParser().parse(input, "angular-html-parser", {

@@ -18,2 +18,3 @@ tokenizeExpansionForms: false,

allowHtmComponentClosingTags,
tokenizeBlocks,
}, isTagNameCaseSensitive, getTagContentType);

@@ -20,0 +21,0 @@ }

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -19,6 +19,8 @@ /**

export interface Input {
bindingPropertyName?: string;
alias?: string;
required?: boolean;
transform?: (value: any) => any;
}
export interface Output {
bindingPropertyName?: string;
alias?: string;
}

@@ -25,0 +27,0 @@ export interface HostBinding {

@@ -0,0 +0,0 @@ /**

@@ -79,6 +79,6 @@ /**

sourceSpan: ParseSourceSpan;
expressionPlaceholder: string;
expressionPlaceholder?: string;
constructor(expression: string, type: string, cases: {
[k: string]: Node;
}, sourceSpan: ParseSourceSpan);
}, sourceSpan: ParseSourceSpan, expressionPlaceholder?: string);
visit(visitor: Visitor, context?: any): any;

@@ -85,0 +85,0 @@ }

@@ -24,5 +24,5 @@ /**

this.customId = customId;
this.id = this.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);

@@ -63,3 +63,3 @@ if (nodes.length) {

export class Icu {
constructor(expression, type, cases, sourceSpan) {
constructor(expression, type, cases, sourceSpan, expressionPlaceholder) {
this.expression = expression;

@@ -69,2 +69,3 @@ this.type = type;

this.sourceSpan = sourceSpan;
this.expressionPlaceholder = expressionPlaceholder;
}

@@ -125,4 +126,3 @@ visit(visitor, context) {

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);
msg.expressionPlaceholder = icu.expressionPlaceholder;
const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan, icu.expressionPlaceholder);
return msg;

@@ -129,0 +129,0 @@ }

@@ -15,3 +15,3 @@ /**

}
export type Node = Attribute | CDATA | Comment | DocType | Element | Text;
export type Node = Attribute | CDATA | Comment | DocType | Element | Text | Block | BlockParameter;
export declare abstract class NodeWithI18n implements BaseNode {

@@ -90,2 +90,20 @@ sourceSpan: ParseSourceSpan;

}
export declare class Block implements BaseNode {
name: string;
parameters: BlockParameter[];
children: Node[];
sourceSpan: ParseSourceSpan;
startSourceSpan: ParseSourceSpan;
endSourceSpan: ParseSourceSpan | null;
constructor(name: string, parameters: BlockParameter[], children: Node[], sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null);
visit(visitor: Visitor, context: any): any;
readonly type = "block";
}
export declare class BlockParameter implements BaseNode {
expression: string;
sourceSpan: ParseSourceSpan;
constructor(expression: string, sourceSpan: ParseSourceSpan);
visit(visitor: Visitor, context: any): any;
readonly type = "blockParameter";
}
export interface Visitor {

@@ -101,2 +119,4 @@ visit?(node: Node, context: any): any;

visitExpansionCase(expansionCase: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(parameter: BlockParameter, context: any): any;
}

@@ -114,4 +134,6 @@ export declare function visitAll(visitor: Visitor, nodes: Node[], context?: any): any[];

visitExpansionCase(ast: ExpansionCase, context: any): any;
visitBlock(block: Block, context: any): any;
visitBlockParameter(ast: BlockParameter, context: any): any;
private visitChildren;
}
export {};

@@ -114,2 +114,26 @@ /**

}
export class Block {
constructor(name, parameters, children, sourceSpan, startSourceSpan, endSourceSpan = null) {
this.name = name;
this.parameters = parameters;
this.children = children;
this.sourceSpan = sourceSpan;
this.startSourceSpan = startSourceSpan;
this.endSourceSpan = endSourceSpan;
this.type = 'block';
}
visit(visitor, context) {
return visitor.visitBlock(this, context);
}
}
export class BlockParameter {
constructor(expression, sourceSpan) {
this.expression = expression;
this.sourceSpan = sourceSpan;
this.type = 'blockParameter';
}
visit(visitor, context) {
return visitor.visitBlockParameter(this, context);
}
}
export function visitAll(visitor, nodes, context = null) {

@@ -147,2 +171,9 @@ const result = [];

visitExpansionCase(ast, context) { }
visitBlock(block, context) {
this.visitChildren(context, visit => {
visit(block.parameters);
visit(block.children);
});
}
visitBlockParameter(ast, context) { }
visitChildren(context, cb) {

@@ -149,0 +180,0 @@ let results = [];

@@ -2139,5 +2139,6 @@ /**

};
// The &ngsp; pseudo-entity is denoting a space. see:
// https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart
// 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;

@@ -43,3 +43,3 @@ /**

DEFAULT_TAG_DEFINITION = new HtmlTagDefinition({ canSelfClose: true });
TAG_DEFINITIONS = {
TAG_DEFINITIONS = Object.assign(Object.create(null), {
'base': new HtmlTagDefinition({ isVoid: true }),

@@ -106,5 +106,5 @@ 'meta': new HtmlTagDefinition({ isVoid: true }),

'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
};
});
new DomElementSchemaRegistry().allKnownElementNames().forEach(knownTagName => {
if (!TAG_DEFINITIONS.hasOwnProperty(knownTagName) && getNsPrefix(knownTagName) === null) {
if (!TAG_DEFINITIONS[knownTagName] && getNsPrefix(knownTagName) === null) {
TAG_DEFINITIONS[knownTagName] = new HtmlTagDefinition({ canSelfClose: false });

@@ -116,4 +116,5 @@ }

// HTML tag names are case insensitive, whereas some SVG tags are case sensitive.
return TAG_DEFINITIONS[tagName] ?? // TAG_DEFINITIONS[tagName.toLowerCase()] ?? -- angular-html-parser modification
return TAG_DEFINITIONS[tagName] ?? // TAG_DEFINITIONS[tagName.toLowerCase()] ?? --
// angular-html-parser modification
DEFAULT_TAG_DEFINITION;
}

@@ -84,2 +84,7 @@ /**

preserveLineEndings?: boolean;
/**
* Whether to tokenize @ block syntax. Otherwise considered text,
* or ICU tokens if `tokenizeExpansionForms` is enabled.
*/
tokenizeBlocks?: boolean;
canSelfClose?: boolean;

@@ -86,0 +91,0 @@ allowHtmComponentClosingTags?: boolean;

@@ -12,3 +12,3 @@ /**

import { DEFAULT_INTERPOLATION_CONFIG } from "./interpolation_config.js";
import { TagContentType, mergeNsAndName } from "./tags.js";
import { mergeNsAndName, TagContentType } from "./tags.js";
export class TokenError extends ParseError {

@@ -57,3 +57,4 @@ constructor(errorMsg, tokenType, span) {

* @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 _getTagContentType A function that will retrieve a tag content type for a given tag
* name.
* @param options Configuration of the tokenization.

@@ -81,4 +82,4 @@ */

this._preserveLineEndings = options.preserveLineEndings || false;
this._escapedString = options.escapedString || false;
this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
this._tokenizeBlocks = options.tokenizeBlocks ?? true;
try {

@@ -134,2 +135,9 @@ this._cursor.init();

}
else if (this._tokenizeBlocks && this._attemptCharCode(chars.$AT)) {
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())) {

@@ -145,5 +153,93 @@ // In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while

}
this._beginToken(25 /* TokenType.EOF */);
this._beginToken(30 /* 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._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);
}
}
/**

@@ -460,3 +556,4 @@ * @returns whether an ICU token has been created

}
if (this._canSelfClose && this.tokens[this.tokens.length - 1].type === 2 /* TokenType.TAG_OPEN_END_VOID */) {
if (this._canSelfClose &&
this.tokens[this.tokens.length - 1].type === 2 /* TokenType.TAG_OPEN_END_VOID */) {
return;

@@ -721,2 +818,6 @@ }

}
if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansion() &&
(this._isBlockStart() || this._cursor.peek() === chars.$RBRACE)) {
return true;
}
return false;

@@ -742,2 +843,13 @@ }

}
_isBlockStart() {
if (this._tokenizeBlocks && this._cursor.peek() === chars.$AT) {
const tmp = this._cursor.clone();
// If it is, also verify that the next character is a valid block identifier.
tmp.advance();
if (isBlockNameChar(tmp.peek())) {
return true;
}
}
return false;
}
_readUntil(char) {

@@ -748,2 +860,5 @@ const start = this._cursor.clone();

}
_isInExpansion() {
return this._isInExpansionCase() || this._isInExpansionForm();
}
_isInExpansionCase() {

@@ -773,3 +888,4 @@ return this._expansionCaseStack.length > 0 &&

const fullName = mergeNsAndName(prefix, tagName);
if (this._fullNameStack.length === 0 || this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
if (this._fullNameStack.length === 0 ||
this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
this._fullNameStack.push(fullName);

@@ -780,3 +896,4 @@ }

const fullName = mergeNsAndName(prefix, tagName);
if (this._fullNameStack.length !== 0 && this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
if (this._fullNameStack.length !== 0 &&
this._fullNameStack[this._fullNameStack.length - 1] === fullName) {
this._fullNameStack.pop();

@@ -813,2 +930,8 @@ }

}
function isBlockNameChar(code) {
return chars.isAsciiLetter(code) || chars.isDigit(code) || code === chars.$_;
}
function isBlockParameterChar(code) {
return code !== chars.$SEMICOLON && isNotWhitespace(code);
}
function mergeTextTokens(srcTokens) {

@@ -815,0 +938,0 @@ const dstTokens = [];

@@ -37,8 +37,8 @@ /**

const getTagContentTypeWithProcessedTagName = isTagNameCaseSensitive ? getTagContentType : lowercasify(getTagContentType);
const _getTagContentType = getTagContentType
? (tagName, prefix, hasParent, attrs) => {
const _getTagContentType = getTagContentType ?
(tagName, prefix, hasParent, attrs) => {
const contentType = getTagContentTypeWithProcessedTagName(tagName, prefix, hasParent, attrs);
return contentType !== undefined ? contentType : getDefaultTagContentType(tagName);
}
: getDefaultTagContentType;
} :
getDefaultTagContentType;
const tokenizeResult = tokenize(source, url, _getTagContentType, options);

@@ -60,3 +60,3 @@ const canSelfClose = (options && options.canSelfClose) || false;

this._index = -1;
this._elementStack = [];
this._containerStack = [];
this.rootNodes = [];

@@ -67,3 +67,3 @@ this.errors = [];

build() {
while (this._peek.type !== 25 /* TokenType.EOF */) {
while (this._peek.type !== 30 /* TokenType.EOF */) {
if (this._peek.type === 0 /* TokenType.TAG_OPEN_START */ ||

@@ -93,2 +93,14 @@ this._peek.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {

}
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 === 18 /* TokenType.DOC_TYPE_START */) {

@@ -102,2 +114,8 @@ this._consumeDocType(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}"`));
}
}
}

@@ -172,3 +190,3 @@ _advance() {

const end = this._advance();
exp.push({ type: 25 /* TokenType.EOF */, parts: [], sourceSpan: end.sourceSpan });
exp.push({ type: 30 /* TokenType.EOF */, parts: [], sourceSpan: end.sourceSpan });
// parse everything in between { and }

@@ -213,3 +231,3 @@ const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition, this.canSelfClose, this.allowHtmComponentClosingTags, this.isTagNameCaseSensitive);

}
if (this._peek.type === 25 /* TokenType.EOF */) {
if (this._peek.type === 30 /* TokenType.EOF */) {
this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));

@@ -224,3 +242,3 @@ return null;

if (text.length > 0 && text[0] == '\n') {
const parent = this._getParentElement();
const parent = this._getClosestParentElement();
if (parent != null && parent.children.length == 0 &&

@@ -238,3 +256,3 @@ this.getTagDefinition(parent.name).ignoreFirstLf) {

if (text.length > 0 && text[0] === '\n') {
const parent = this._getParentElement();
const parent = this._getContainer();
if (parent != null && parent.children.length === 0 &&

@@ -270,5 +288,5 @@ this.getTagDefinition(parent.name).ignoreFirstLf) {

_closeVoidElement() {
const el = this._getParentElement();
if (el && this.getTagDefinition(el.name).isVoid) {
this._elementStack.pop();
const el = this._getContainer();
if (el instanceof html.Element && this.getTagDefinition(el.name).isVoid) {
this._containerStack.pop();
}

@@ -282,3 +300,3 @@ }

}
const fullName = this._getElementFullName(prefix, name, this._getParentElement());
const fullName = this._getElementFullName(prefix, name, this._getClosestParentElement());
let selfClosing = false;

@@ -291,3 +309,4 @@ // Note: There could have been a tokenizer error

const tagDef = this.getTagDefinition(fullName);
if (!(this.canSelfClose || tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
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]}"`));

@@ -306,7 +325,9 @@ }

const el = new html.Element(fullName, attrs, [], span, startSpan, undefined, nameSpan);
this._pushElement(el);
const parentEl = this._getContainer();
this._pushContainer(el, parentEl instanceof html.Element &&
this.getTagDefinition(parentEl.name).isClosedByChild(el.name));
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._popElement(fullName, span);
this._popContainer(fullName, html.Element, span);
}

@@ -316,23 +337,23 @@ else if (startTagToken.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {

// close tag. Let's optimistically parse it as a full element and emit an error.
this._popElement(fullName, null);
this._popContainer(fullName, html.Element, null);
this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
}
}
_pushElement(el) {
const parentEl = this._getParentElement();
if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
this._elementStack.pop();
_pushContainer(node, isClosedByChild) {
if (isClosedByChild) {
this._containerStack.pop();
}
this._addToParent(el);
this._elementStack.push(el);
this._addToParent(node);
this._containerStack.push(node);
}
_consumeEndTag(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.parts[0], endTagToken.parts[1], this._getParentElement());
// @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.parts[0], endTagToken.parts[1], this._getClosestParentElement());
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._popElement(fullName, endTagToken.sourceSpan)) {
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`;

@@ -348,17 +369,19 @@ this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));

*/
_popElement(fullName, endSourceSpan) {
_popContainer(expectedName, expectedType, endSourceSpan) {
let unexpectedCloseTagDetected = false;
for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
const el = this._elementStack[stackIndex];
if (!fullName ||
( /* isForeignElement */(getNsPrefix(el.name) ? el.name == fullName : el.name.toLowerCase() == fullName.toLowerCase()))) {
for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) {
const node = this._containerStack[stackIndex];
if (( /* isForeignElement */(getNsPrefix(node.name) ? node.name === expectedName : (expectedName == null || node.name.toLowerCase() === expectedName.toLowerCase()) &&
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).
el.endSourceSpan = endSourceSpan;
el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
node.endSourceSpan = endSourceSpan;
node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end;
this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex);
return !unexpectedCloseTagDetected;
}
if (!this.getTagDefinition(el.name).closedByParent) {
// Blocks and most elements are not self closing.
if (node instanceof html.Block ||
node instanceof html.Element && !this.getTagDefinition(node.name).closedByParent) {
// Note that we encountered an unexpected close tag but continue processing the element

@@ -423,12 +446,61 @@ // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this

}
_getParentElement() {
return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
_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, 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 "}" ` +
`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, 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 "@" HTML entity instead.`));
}
_getContainer() {
return this._containerStack.length > 0 ? this._containerStack[this._containerStack.length - 1] :
null;
}
_getClosestParentElement() {
for (let i = this._containerStack.length - 1; i > -1; i--) {
if (this._containerStack[i] instanceof html.Element) {
return this._containerStack[i];
}
}
return null;
}
_addToParent(node) {
const parent = this._getParentElement();
if (parent != null) {
parent.children.push(node);
const parent = this._getContainer();
if (parent === null) {
this.rootNodes.push(node);
}
else {
this.rootNodes.push(node);
parent.children.push(node);
}

@@ -435,0 +507,0 @@ }

@@ -35,5 +35,10 @@ /**

EXPANSION_FORM_END = 24,
EOF = 25
BLOCK_OPEN_START = 25,
BLOCK_OPEN_END = 26,
BLOCK_CLOSE = 27,
BLOCK_PARAMETER = 28,
INCOMPLETE_BLOCK_OPEN = 29,
EOF = 30
}
export type Token = TagOpenStartToken | TagOpenEndToken | TagOpenEndVoidToken | TagCloseToken | IncompleteTagOpenToken | TextToken | InterpolationToken | EncodedEntityToken | CommentStartToken | CommentEndToken | CdataStartToken | CdataEndToken | AttributeNameToken | AttributeQuoteToken | AttributeValueTextToken | AttributeValueInterpolationToken | DocTypeStartToken | DocTypeEndToken | ExpansionFormStartToken | ExpansionCaseValueToken | ExpansionCaseExpressionStartToken | ExpansionCaseExpressionEndToken | ExpansionFormEndToken | EndOfFileToken;
export type Token = TagOpenStartToken | TagOpenEndToken | TagOpenEndVoidToken | TagCloseToken | IncompleteTagOpenToken | TextToken | InterpolationToken | EncodedEntityToken | CommentStartToken | CommentEndToken | CdataStartToken | CdataEndToken | AttributeNameToken | AttributeQuoteToken | AttributeValueTextToken | AttributeValueInterpolationToken | DocTypeStartToken | DocTypeEndToken | ExpansionFormStartToken | ExpansionCaseValueToken | ExpansionCaseExpressionStartToken | ExpansionCaseExpressionEndToken | ExpansionFormEndToken | EndOfFileToken | BlockParameterToken | BlockOpenStartToken | BlockOpenEndToken | BlockCloseToken | IncompleteBlockOpenToken;
export type InterpolatedTextToken = TextToken | InterpolationToken | EncodedEntityToken;

@@ -148,1 +153,21 @@ export type InterpolatedAttributeToken = AttributeValueTextToken | AttributeValueInterpolationToken | EncodedEntityToken;

}
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];
}

@@ -0,0 +0,0 @@ export declare class ParseLocation {

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -99,9 +99,3 @@ /**

}
// Check `global` first, because in Node tests both `global` and `window` may be defined and our
// `_global` variable should point to the NodeJS `global` in that case. Note: Typeof/Instanceof
// checks are considered side-effects in Terser. We explicitly mark this as side-effect free:
// https://github.com/terser/terser/issues/250.
const _global = ( /* @__PURE__ */((() => (typeof global !== 'undefined' && global) || (typeof window !== 'undefined' && window) ||
(typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
self instanceof WorkerGlobalScope && self))()));
const _global = globalThis;
export { _global as global };

@@ -108,0 +102,0 @@ export function newArray(size, value) {

{
"name": "angular-html-parser",
"version": "4.0.1",
"version": "5.0.0",
"description": "A HTML parser extracted from Angular with some modifications",

@@ -19,3 +19,3 @@ "main": "./lib/angular-html-parser/src/index.js",

"build": "tsc -p tsconfig.build.json",
"postbuild": "jscodeshift -t postbuild-codemod.ts lib --extensions=js,ts --parser=ts",
"postbuild": "node ./node_modules/.bin/jscodeshift -t postbuild-codemod.ts lib --extensions=js,ts --parser=ts",
"test": "ts-node --project tsconfig.test.json -r tsconfig-paths/register node_modules/jasmine/bin/jasmine.js ../compiler/test/ml_parser/*_spec.ts ./test/*_spec.ts",

@@ -22,0 +22,0 @@ "release": "standard-version"

@@ -72,2 +72,6 @@ # angular-html-parser

) => void | ng.TagContentType;
/**
* tokenize angular control flow block syntax
*/
tokenizeAngularBlocks?: boolean,
}

@@ -74,0 +78,0 @@ ```

@@ -0,0 +0,0 @@ This project incorporates third party material from the projects listed below.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc