angular-html-parser
Advanced tools
@@ -12,3 +12,4 @@ /** | ||
| None = 2, | ||
| ShadowDom = 3 | ||
| ShadowDom = 3, | ||
| IsolatedShadowDom = 4 | ||
| } | ||
@@ -15,0 +16,0 @@ export declare enum ChangeDetectionStrategy { |
@@ -23,2 +23,3 @@ /** | ||
| ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; | ||
| ViewEncapsulation[ViewEncapsulation["IsolatedShadowDom"] = 4] = "IsolatedShadowDom"; | ||
| })(ViewEncapsulation || (ViewEncapsulation = {})); | ||
@@ -25,0 +26,0 @@ export var ChangeDetectionStrategy; |
@@ -8,3 +8,3 @@ /** | ||
| */ | ||
| import { ParseSourceSpan } from "../parse_util.js"; | ||
| import { ParseSourceSpan } from '../parse_util'; | ||
| /** | ||
@@ -11,0 +11,0 @@ * Describes the text contents of a placeholder as it appears in an ICU expression, including its |
@@ -8,5 +8,5 @@ /** | ||
| */ | ||
| import { I18nMeta } from "../i18n/i18n_ast.js"; | ||
| import { ParseSourceSpan } from "../parse_util.js"; | ||
| import { InterpolatedAttributeToken, InterpolatedTextToken } from "./tokens.js"; | ||
| import { I18nMeta } from '../i18n/i18n_ast'; | ||
| import { ParseSourceSpan } from '../parse_util'; | ||
| import { InterpolatedAttributeToken, InterpolatedTextToken } from './tokens'; | ||
| interface BaseNode { | ||
@@ -16,3 +16,3 @@ sourceSpan: ParseSourceSpan; | ||
| } | ||
| export type Node = Attribute | CDATA | Comment | DocType | Element | Text | Block | BlockParameter | LetDeclaration | Component | Directive; | ||
| export type Node = Attribute | CDATA | Comment | DocType | Element | Expansion | ExpansionCase | Text | Block | BlockParameter | LetDeclaration | Component | Directive; | ||
| export declare abstract class NodeWithI18n implements BaseNode { | ||
@@ -76,3 +76,4 @@ sourceSpan: ParseSourceSpan; | ||
| nameSpan: ParseSourceSpan | null; | ||
| constructor(name: string, attrs: Attribute[], directives: Directive[], children: Node[], isSelfClosing: boolean, sourceSpan: ParseSourceSpan, startSourceSpan: ParseSourceSpan, endSourceSpan?: ParseSourceSpan | null, nameSpan?: ParseSourceSpan | null, i18n?: I18nMeta); | ||
| 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; | ||
@@ -79,0 +80,0 @@ readonly type = "element"; |
@@ -80,3 +80,3 @@ /** | ||
| export class Element extends NodeWithI18n { | ||
| constructor(name, attrs, directives, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan = null, nameSpan = null, i18n) { | ||
| constructor(name, attrs, directives, children, isSelfClosing, sourceSpan, startSourceSpan, endSourceSpan = null, nameSpan = null, isVoid, i18n) { | ||
| super(sourceSpan, i18n); | ||
@@ -91,2 +91,3 @@ this.name = name; | ||
| this.nameSpan = nameSpan; | ||
| this.isVoid = isVoid; | ||
| this.type = 'element'; | ||
@@ -93,0 +94,0 @@ } |
@@ -8,5 +8,5 @@ /** | ||
| */ | ||
| import { TokenizeOptions } from "./lexer.js"; | ||
| import { Parser, ParseTreeResult } from "./parser.js"; | ||
| import { TagContentType } from "./tags.js"; | ||
| import { TokenizeOptions } from './lexer'; | ||
| import { Parser, ParseTreeResult } from './parser'; | ||
| import { TagContentType } from './tags'; | ||
| export declare class HtmlParser extends Parser { | ||
@@ -13,0 +13,0 @@ constructor(); |
@@ -8,3 +8,3 @@ /** | ||
| */ | ||
| import { TagContentType, TagDefinition } from "./tags.js"; | ||
| import { TagContentType, TagDefinition } from './tags'; | ||
| export declare class HtmlTagDefinition implements TagDefinition { | ||
@@ -11,0 +11,0 @@ private closedByChildren; |
@@ -8,15 +8,11 @@ /** | ||
| */ | ||
| import { ParseError, ParseSourceSpan } from "../parse_util.js"; | ||
| import { InterpolationConfig } from "./defaults.js"; | ||
| import { TagContentType } from "./tags.js"; | ||
| import { Token, TokenType } from "./tokens.js"; | ||
| export declare class TokenError extends ParseError { | ||
| tokenType: TokenType | null; | ||
| constructor(errorMsg: string, tokenType: TokenType | null, span: ParseSourceSpan); | ||
| } | ||
| 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: TokenError[]; | ||
| errors: ParseError[]; | ||
| nonNormalizedIcuExpressions: Token[]; | ||
| constructor(tokens: Token[], errors: TokenError[], nonNormalizedIcuExpressions: Token[]); | ||
| constructor(tokens: Token[], errors: ParseError[], nonNormalizedIcuExpressions: Token[]); | ||
| } | ||
@@ -128,3 +124,3 @@ export interface LexerRange { | ||
| } | ||
| export declare class CursorError { | ||
| export declare class CursorError extends Error { | ||
| msg: string; | ||
@@ -131,0 +127,0 @@ cursor: CharacterCursor; |
@@ -13,8 +13,2 @@ /** | ||
| import { mergeNsAndName, TagContentType } from "./tags.js"; | ||
| export class TokenError extends ParseError { | ||
| constructor(errorMsg, tokenType, span) { | ||
| super(span, errorMsg); | ||
| this.tokenType = tokenType; | ||
| } | ||
| } | ||
| export class TokenizeResult { | ||
@@ -48,7 +42,15 @@ constructor(tokens, errors, nonNormalizedIcuExpressions) { | ||
| })(CharacterReferenceType || (CharacterReferenceType = {})); | ||
| class _ControlFlowError { | ||
| constructor(error) { | ||
| this.error = error; | ||
| } | ||
| } | ||
| 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 | ||
@@ -148,6 +150,6 @@ class _Tokenizer { | ||
| !this._inInterpolation && | ||
| this._attemptStr('@let')) { | ||
| this._isLetStart()) { | ||
| this._consumeLetDeclaration(start); | ||
| } | ||
| else if (this._tokenizeBlocks && this._attemptCharCode(chars.$AT)) { | ||
| else if (this._tokenizeBlocks && this._isBlockStart()) { | ||
| this._consumeBlockStart(start); | ||
@@ -192,2 +194,3 @@ } | ||
| _consumeBlockStart(start) { | ||
| this._requireCharCode(chars.$AT); | ||
| this._beginToken(25 /* TokenType.BLOCK_OPEN_START */, start); | ||
@@ -265,2 +268,3 @@ const startToken = this._endToken([this._getBlockName()]); | ||
| _consumeLetDeclaration(start) { | ||
| this._requireStr('@let'); | ||
| this._beginToken(30 /* TokenType.LET_START */, start); | ||
@@ -370,6 +374,6 @@ // Require at least one white space after the `@let`. | ||
| if (this._currentTokenStart === null) { | ||
| throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end)); | ||
| 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 TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart)); | ||
| throw new ParseError(this._cursor.getSpan(this._currentTokenStart), 'Programming error - attempted to end a token which has no token type'); | ||
| } | ||
@@ -390,6 +394,6 @@ const token = { | ||
| } | ||
| const error = new TokenError(msg, this._currentTokenType, span); | ||
| const error = new ParseError(span, msg); | ||
| this._currentTokenStart = null; | ||
| this._currentTokenType = null; | ||
| return new _ControlFlowError(error); | ||
| return error; | ||
| } | ||
@@ -400,4 +404,4 @@ handleError(e) { | ||
| } | ||
| if (e instanceof _ControlFlowError) { | ||
| this.errors.push(e.error); | ||
| if (e instanceof ParseError) { | ||
| this.errors.push(e); | ||
| } | ||
@@ -488,2 +492,23 @@ else { | ||
| } | ||
| _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) { | ||
@@ -658,3 +683,3 @@ this._beginToken(9 /* TokenType.ENCODED_ENTITY */); | ||
| catch (e) { | ||
| if (e instanceof _ControlFlowError) { | ||
| if (e instanceof ParseError) { | ||
| if (openToken) { | ||
@@ -776,2 +801,19 @@ // We errored before we could close the opening tag, so it is incomplete. | ||
| } | ||
| 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 { | ||
@@ -1070,3 +1112,3 @@ nameEndPredicate = isNameEnd; | ||
| !this._isInExpansion() && | ||
| (this._isBlockStart() || this._cursor.peek() === chars.$AT || this._cursor.peek() === chars.$RBRACE)) { | ||
| (this._isBlockStart() || this._isLetStart() || this._cursor.peek() === chars.$RBRACE)) { | ||
| return true; | ||
@@ -1096,13 +1138,2 @@ } | ||
| } | ||
| _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) { | ||
@@ -1448,7 +1479,11 @@ const start = this._cursor.clone(); | ||
| } | ||
| export class CursorError { | ||
| 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); | ||
| } | ||
| } |
@@ -8,6 +8,6 @@ /** | ||
| */ | ||
| import { ParseError, ParseSourceSpan } from "../parse_util.js"; | ||
| import * as html from "./ast.js"; | ||
| import { TokenizeOptions } from "./lexer.js"; | ||
| import { TagContentType, TagDefinition } from "./tags.js"; | ||
| 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 { | ||
@@ -14,0 +14,0 @@ elementName: string | null; |
@@ -48,3 +48,3 @@ /** | ||
| parser.build(); | ||
| return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors)); | ||
| return new ParseTreeResult(parser.rootNodes, [...tokenizeResult.errors, ...parser.errors]); | ||
| } | ||
@@ -310,2 +310,3 @@ } | ||
| const fullName = this._getElementFullName(startTagToken, this._getClosestElementLikeParent()); | ||
| const tagDef = this._getTagDefinition(fullName); | ||
| let selfClosing = false; | ||
@@ -331,3 +332,3 @@ // Note: There could have been a tokenizer error | ||
| 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); | ||
| const el = new html.Element(fullName, attrs, directives, [], selfClosing, span, startSpan, undefined, nameSpan, tagDef?.isVoid ?? false); | ||
| const parent = this._getContainer(); | ||
@@ -334,0 +335,0 @@ const isClosedByChild = parent !== null && !!this._getTagDefinition(parent)?.isClosedByChild(el.name); |
@@ -8,3 +8,3 @@ /** | ||
| */ | ||
| import { ParseSourceSpan } from "../parse_util.js"; | ||
| import { ParseSourceSpan } from '../parse_util'; | ||
| export declare const enum TokenType { | ||
@@ -11,0 +11,0 @@ TAG_OPEN_START = 0, |
@@ -54,3 +54,3 @@ export declare class ParseLocation { | ||
| } | ||
| export declare class ParseError { | ||
| export declare class ParseError extends Error { | ||
| /** Location of the error. */ | ||
@@ -57,0 +57,0 @@ readonly span: ParseSourceSpan; |
@@ -140,3 +140,3 @@ /** | ||
| })(ParseErrorLevel || (ParseErrorLevel = {})); | ||
| export class ParseError { | ||
| export class ParseError extends Error { | ||
| constructor( | ||
@@ -154,2 +154,3 @@ /** Location of the error. */ | ||
| relatedError) { | ||
| super(msg); | ||
| this.span = span; | ||
@@ -159,2 +160,6 @@ this.msg = msg; | ||
| 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); | ||
| } | ||
@@ -161,0 +166,0 @@ contextualMessage() { |
@@ -8,4 +8,4 @@ /** | ||
| */ | ||
| import { SchemaMetadata, SecurityContext } from "../core.js"; | ||
| import { ElementSchemaRegistry } from "./element_schema_registry.js"; | ||
| import { SchemaMetadata, SecurityContext } from '../core'; | ||
| import { ElementSchemaRegistry } from './element_schema_registry'; | ||
| export declare class DomElementSchemaRegistry extends ElementSchemaRegistry { | ||
@@ -12,0 +12,0 @@ private _schema; |
@@ -266,2 +266,40 @@ /** | ||
| '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', | ||
| })); | ||
@@ -268,0 +306,0 @@ // Invert _ATTR_TO_PROP. |
@@ -8,3 +8,3 @@ /** | ||
| */ | ||
| import { SecurityContext } from "../core.js"; | ||
| import { SecurityContext } from '../core'; | ||
| export declare function SECURITY_SCHEMA(): { | ||
@@ -11,0 +11,0 @@ [k: string]: SecurityContext; |
@@ -8,3 +8,3 @@ /** | ||
| */ | ||
| import { SchemaMetadata, SecurityContext } from "../core.js"; | ||
| import { SchemaMetadata, SecurityContext } from '../core'; | ||
| export declare abstract class ElementSchemaRegistry { | ||
@@ -11,0 +11,0 @@ abstract hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean; |
+8
-8
| { | ||
| "name": "angular-html-parser", | ||
| "version": "9.2.0", | ||
| "version": "9.3.0", | ||
| "description": "A HTML parser extracted from Angular with some modifications", | ||
@@ -32,14 +32,14 @@ "repository": "https://github.com/prettier/angular-html-parser/blob/HEAD/packages/angular-html-parser", | ||
| "devDependencies": { | ||
| "@types/node": "22.15.23", | ||
| "@vitest/coverage-v8": "3.1.4", | ||
| "@types/node": "24.3.0", | ||
| "@vitest/coverage-v8": "3.2.4", | ||
| "del-cli": "6.0.0", | ||
| "jasmine": "5.7.1", | ||
| "jasmine": "5.10.0", | ||
| "jscodeshift": "17.3.0", | ||
| "prettier": "3.5.3", | ||
| "release-it": "19.0.2", | ||
| "prettier": "3.6.2", | ||
| "release-it": "19.0.4", | ||
| "standard-version": "9.5.0", | ||
| "ts-node": "10.9.2", | ||
| "tsconfig-paths": "4.2.0", | ||
| "typescript": "5.8.3", | ||
| "vitest": "3.1.4" | ||
| "typescript": "5.9.2", | ||
| "vitest": "3.2.4" | ||
| }, | ||
@@ -46,0 +46,0 @@ "engines": { |
318387
0.56%8210
0.98%