}
}
-function getCursor(context: ParserContext): Position {
- const { column, line, offset } = context
- return { column, line, offset }
-}
-
function parseChildren(
context: ParserContext,
mode: TextModes,
return nodes
}
-function getSelection(
- context: ParserContext,
- start: Position,
- end?: Position
-): SourceLocation {
- end = end || getCursor(context)
- return {
- start,
- end,
- source: context.originalSource.slice(start.offset, end.offset)
- }
-}
-
-function last<T>(xs: T[]): T | undefined {
- return xs[xs.length - 1]
-}
-
-function startsWith(source: string, searchString: string): boolean {
- return source.startsWith(searchString)
-}
-
-function advanceBy(context: ParserContext, numberOfCharacters: number): void {
- __DEV__ && assert(numberOfCharacters <= context.source.length)
-
- const { column, source } = context
- const str = source.slice(0, numberOfCharacters)
- const lines = str.split(/\r?\n/)
-
- context.source = source.slice(numberOfCharacters)
- context.offset += numberOfCharacters
- context.line += lines.length - 1
- context.column =
- lines.length === 1
- ? column + numberOfCharacters
- : Math.max(1, lines.pop()!.length)
-}
-
-function advanceSpaces(context: ParserContext): void {
- const match = /^[\t\r\n\f ]+/.exec(context.source)
- if (match) {
- advanceBy(context, match[0].length)
- }
-}
-
-function getNewPosition(
- context: ParserContext,
- start: Position,
- numberOfCharacters: number
-): Position {
- const { originalSource } = context
- const str = originalSource.slice(start.offset, numberOfCharacters)
- const lines = str.split(/\r?\n/)
-
- const newPosition = {
- column: start.column,
- line: start.line,
- offset: start.offset
- }
-
- newPosition.offset += numberOfCharacters
- newPosition.line += lines.length - 1
- newPosition.column =
- lines.length === 1
- ? start.column + numberOfCharacters
- : Math.max(1, lines.pop()!.length)
-
- return newPosition
-}
-
-function emitError(
- context: ParserContext,
- type: ParserErrorTypes,
- offset?: number
-): void {
- const loc = getCursor(context)
- if (offset) {
- loc.offset += offset
- loc.column += offset
- }
- context.onError(type, loc)
-}
-
-function isEnd(
- context: ParserContext,
- mode: TextModes,
- ancestors: ElementNode[]
-): boolean {
- const s = context.source
-
- switch (mode) {
- case TextModes.DATA:
- if (startsWith(s, '</')) {
- //TODO: probably bad performance
- for (let i = ancestors.length - 1; i >= 0; --i) {
- if (startsWithEndTagOpen(s, ancestors[i].tag)) {
- return true
- }
- }
- }
- break
-
- case TextModes.RCDATA:
- case TextModes.RAWTEXT: {
- const parent = last(ancestors)
- if (parent && startsWithEndTagOpen(s, parent.tag)) {
- return true
- }
- break
- }
-
- case TextModes.CDATA:
- if (startsWith(s, ']]>')) {
- return true
- }
- break
- }
-
- return !s
-}
-
-function startsWithEndTagOpen(source: string, tag: string): boolean {
- return (
- startsWith(source, '</') &&
- source.substr(2, tag.length).toLowerCase() === tag.toLowerCase() &&
- /[\t\n\f />]/.test(source[2 + tag.length] || '>')
- )
-}
-
function pushNode(
context: ParserContext,
nodes: RootNode['children'],
return text
}
+function getCursor(context: ParserContext): Position {
+ const { column, line, offset } = context
+ return { column, line, offset }
+}
+
+function getSelection(
+ context: ParserContext,
+ start: Position,
+ end?: Position
+): SourceLocation {
+ end = end || getCursor(context)
+ return {
+ start,
+ end,
+ source: context.originalSource.slice(start.offset, end.offset)
+ }
+}
+
+function last<T>(xs: T[]): T | undefined {
+ return xs[xs.length - 1]
+}
+
+function startsWith(source: string, searchString: string): boolean {
+ return source.startsWith(searchString)
+}
+
+function advanceBy(context: ParserContext, numberOfCharacters: number): void {
+ __DEV__ && assert(numberOfCharacters <= context.source.length)
+
+ const { column, source } = context
+ const str = source.slice(0, numberOfCharacters)
+ const lines = str.split(/\r?\n/)
+
+ context.source = source.slice(numberOfCharacters)
+ context.offset += numberOfCharacters
+ context.line += lines.length - 1
+ context.column =
+ lines.length === 1
+ ? column + numberOfCharacters
+ : Math.max(1, lines.pop()!.length)
+}
+
+function advanceSpaces(context: ParserContext): void {
+ const match = /^[\t\r\n\f ]+/.exec(context.source)
+ if (match) {
+ advanceBy(context, match[0].length)
+ }
+}
+
+function getNewPosition(
+ context: ParserContext,
+ start: Position,
+ numberOfCharacters: number
+): Position {
+ const { originalSource } = context
+ const str = originalSource.slice(start.offset, numberOfCharacters)
+ const lines = str.split(/\r?\n/)
+
+ const newPosition = {
+ column: start.column,
+ line: start.line,
+ offset: start.offset
+ }
+
+ newPosition.offset += numberOfCharacters
+ newPosition.line += lines.length - 1
+ newPosition.column =
+ lines.length === 1
+ ? start.column + numberOfCharacters
+ : Math.max(1, lines.pop()!.length)
+
+ return newPosition
+}
+
+function emitError(
+ context: ParserContext,
+ type: ParserErrorTypes,
+ offset?: number
+): void {
+ const loc = getCursor(context)
+ if (offset) {
+ loc.offset += offset
+ loc.column += offset
+ }
+ context.onError(type, loc)
+}
+
+function isEnd(
+ context: ParserContext,
+ mode: TextModes,
+ ancestors: ElementNode[]
+): boolean {
+ const s = context.source
+
+ switch (mode) {
+ case TextModes.DATA:
+ if (startsWith(s, '</')) {
+ //TODO: probably bad performance
+ for (let i = ancestors.length - 1; i >= 0; --i) {
+ if (startsWithEndTagOpen(s, ancestors[i].tag)) {
+ return true
+ }
+ }
+ }
+ break
+
+ case TextModes.RCDATA:
+ case TextModes.RAWTEXT: {
+ const parent = last(ancestors)
+ if (parent && startsWithEndTagOpen(s, parent.tag)) {
+ return true
+ }
+ break
+ }
+
+ case TextModes.CDATA:
+ if (startsWith(s, ']]>')) {
+ return true
+ }
+ break
+ }
+
+ return !s
+}
+
+function startsWithEndTagOpen(source: string, tag: string): boolean {
+ return (
+ startsWith(source, '</') &&
+ source.substr(2, tag.length).toLowerCase() === tag.toLowerCase() &&
+ /[\t\n\f />]/.test(source[2 + tag.length] || '>')
+ )
+}
+
+function assert(condition: boolean, msg?: string) {
+ if (!condition) {
+ throw new Error(msg || `unexpected parser condition`)
+ }
+}
+
// https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state
const CCR_REPLACEMENTS: { [key: number]: number | undefined } = {
0x80: 0x20ac,
0x9e: 0x017e,
0x9f: 0x0178
}
-
-function assert(condition: boolean, msg?: string) {
- if (!condition) {
- throw new Error(msg || `unexpected parser condition`)
- }
-}
X_MISSING_INTERPOLATION_END,
X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END
}
+
+export const errorMessages: { [code: number]: string } = {
+ [ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT]: 'Illegal comment.',
+ [ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE]:
+ 'Illegal numeric character reference: invalid character.',
+ [ParserErrorTypes.CDATA_IN_HTML_CONTENT]:
+ 'CDATA section is allowed only in XML context.',
+ [ParserErrorTypes.CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE]:
+ 'Illegal numeric character reference: too big.',
+ [ParserErrorTypes.CONTROL_CHARACTER_REFERENCE]:
+ 'Illegal numeric character reference: control character.',
+ [ParserErrorTypes.DUPLICATE_ATTRIBUTE]: 'Duplicate attribute.',
+ [ParserErrorTypes.END_TAG_WITH_ATTRIBUTES]: 'End tag cannot have attributes.',
+ [ParserErrorTypes.END_TAG_WITH_TRAILING_SOLIDUS]: "Illegal '/' in tags.",
+ [ParserErrorTypes.EOF_BEFORE_TAG_NAME]: 'Unexpected EOF in tag.',
+ [ParserErrorTypes.EOF_IN_CDATA]: 'Unexpected EOF in CDATA section.',
+ [ParserErrorTypes.EOF_IN_COMMENT]: 'Unexpected EOF in comment.',
+ [ParserErrorTypes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT]:
+ 'Unexpected EOF in script.',
+ [ParserErrorTypes.EOF_IN_TAG]: 'Unexpected EOF in tag.',
+ [ParserErrorTypes.INCORRECTLY_CLOSED_COMMENT]: 'Incorrectly closed comment.',
+ [ParserErrorTypes.INCORRECTLY_OPENED_COMMENT]: 'Incorrectly opened comment.',
+ [ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME]:
+ "Illegal tag name. Use '<' to print '<'.",
+ [ParserErrorTypes.MISSING_ATTRIBUTE_VALUE]: 'Attribute value was expected.',
+ [ParserErrorTypes.MISSING_END_TAG_NAME]: 'End tag name was expected.',
+ [ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE]:
+ 'Semicolon was expected.',
+ [ParserErrorTypes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES]:
+ 'Whitespace was expected.',
+ [ParserErrorTypes.NESTED_COMMENT]: "Unexpected '<!--' in comment.",
+ [ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE]:
+ 'Illegal numeric character reference: non character.',
+ [ParserErrorTypes.NULL_CHARACTER_REFERENCE]:
+ 'Illegal numeric character reference: null character.',
+ [ParserErrorTypes.SURROGATE_CHARACTER_REFERENCE]:
+ 'Illegal numeric character reference: non-pair surrogate.',
+ [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME]:
+ 'Attribute name cannot contain U+0022 ("), U+0027 (\'), and U+003C (<).',
+ [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE]:
+ 'Unquoted attribute value cannot contain U+0022 ("), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).',
+ [ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME]:
+ "Attribute name cannot start with '='.",
+ [ParserErrorTypes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME]:
+ "'<?' is allowed only in XML context.",
+ [ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG]: "Illegal '/' in tags.",
+ [ParserErrorTypes.UNKNOWN_NAMED_CHARACTER_REFERENCE]: 'Unknown entity name.',
+ [ParserErrorTypes.X_INVALID_END_TAG]: 'Invalid end tag.',
+ [ParserErrorTypes.X_MISSING_END_TAG]: 'End tag was not found.',
+ [ParserErrorTypes.X_MISSING_INTERPOLATION_END]:
+ 'Interpolation end sign was not found.'
+}
import { TextModes, ParserOptions } from './parser'
import { ElementNode, Namespaces, Position, Node } from './ast'
-import { ParserErrorTypes } from './parserErrorTypes'
+import { ParserErrorTypes, errorMessages } from './parserErrorTypes'
export const parserOptionsMinimal: ParserOptions = {
delimiters: [`{{`, `}}`],
onError(code: ParserErrorTypes, loc: Position): void {
const error: any = new SyntaxError(
- `${messages[code]} (${loc.line}:${loc.column})`
+ `${errorMessages[code]} (${loc.line}:${loc.column})`
)
error.code = code
error.loc = loc
return node
}
}
-
-const messages: { [code: number]: string } = {
- [ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT]: 'Illegal comment.',
- [ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE]:
- 'Illegal numeric character reference: invalid character.',
- [ParserErrorTypes.CDATA_IN_HTML_CONTENT]:
- 'CDATA section is allowed only in XML context.',
- [ParserErrorTypes.CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE]:
- 'Illegal numeric character reference: too big.',
- [ParserErrorTypes.CONTROL_CHARACTER_REFERENCE]:
- 'Illegal numeric character reference: control character.',
- [ParserErrorTypes.DUPLICATE_ATTRIBUTE]: 'Duplicate attribute.',
- [ParserErrorTypes.END_TAG_WITH_ATTRIBUTES]: 'End tag cannot have attributes.',
- [ParserErrorTypes.END_TAG_WITH_TRAILING_SOLIDUS]: "Illegal '/' in tags.",
- [ParserErrorTypes.EOF_BEFORE_TAG_NAME]: 'Unexpected EOF in tag.',
- [ParserErrorTypes.EOF_IN_CDATA]: 'Unexpected EOF in CDATA section.',
- [ParserErrorTypes.EOF_IN_COMMENT]: 'Unexpected EOF in comment.',
- [ParserErrorTypes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT]:
- 'Unexpected EOF in script.',
- [ParserErrorTypes.EOF_IN_TAG]: 'Unexpected EOF in tag.',
- [ParserErrorTypes.INCORRECTLY_CLOSED_COMMENT]: 'Incorrectly closed comment.',
- [ParserErrorTypes.INCORRECTLY_OPENED_COMMENT]: 'Incorrectly opened comment.',
- [ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME]:
- "Illegal tag name. Use '<' to print '<'.",
- [ParserErrorTypes.MISSING_ATTRIBUTE_VALUE]: 'Attribute value was expected.',
- [ParserErrorTypes.MISSING_END_TAG_NAME]: 'End tag name was expected.',
- [ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE]:
- 'Semicolon was expected.',
- [ParserErrorTypes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES]:
- 'Whitespace was expected.',
- [ParserErrorTypes.NESTED_COMMENT]: "Unexpected '<!--' in comment.",
- [ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE]:
- 'Illegal numeric character reference: non character.',
- [ParserErrorTypes.NULL_CHARACTER_REFERENCE]:
- 'Illegal numeric character reference: null character.',
- [ParserErrorTypes.SURROGATE_CHARACTER_REFERENCE]:
- 'Illegal numeric character reference: non-pair surrogate.',
- [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME]:
- 'Attribute name cannot contain U+0022 ("), U+0027 (\'), and U+003C (<).',
- [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE]:
- 'Unquoted attribute value cannot contain U+0022 ("), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).',
- [ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME]:
- "Attribute name cannot start with '='.",
- [ParserErrorTypes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME]:
- "'<?' is allowed only in XML context.",
- [ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG]: "Illegal '/' in tags.",
- [ParserErrorTypes.UNKNOWN_NAMED_CHARACTER_REFERENCE]: 'Unknown entity name.',
- [ParserErrorTypes.X_INVALID_END_TAG]: 'Invalid end tag.',
- [ParserErrorTypes.X_MISSING_END_TAG]: 'End tag was not found.',
- [ParserErrorTypes.X_MISSING_INTERPOLATION_END]:
- 'Interpolation end sign was not found.'
-}