htmljs-parser
Advanced tools
Comparing version 3.2.1 to 3.2.2
@@ -1,2 +0,2 @@ | ||
import { STATE, Range, ParserOptions as Options } from "../internal"; | ||
import { STATE, Range, ParserOptions as Options, ErrorCode } from "../internal"; | ||
export interface Meta extends Range { | ||
@@ -59,3 +59,3 @@ parent: Meta; | ||
beginHtmlBlock(delimiter: string | undefined, singleLine: boolean): void; | ||
emitError(range: number | Range, code: string, message: string): void; | ||
emitError(range: number | Range, code: ErrorCode, message: string): void; | ||
closeTag(start: number, end: number, value: Range | undefined): void; | ||
@@ -62,0 +62,0 @@ consumeWhitespaceIfBefore(str: string, start?: number): boolean; |
import { type ParserOptions, type Range } from "./internal"; | ||
export { TagType, type ParserOptions as Handlers, type Position, type Location, type Ranges, type Range, } from "./internal"; | ||
export { TagType, ErrorCode, type ParserOptions as Handlers, type Position, type Location, type Ranges, type Range, } from "./internal"; | ||
/** | ||
@@ -18,3 +18,3 @@ * Creates a new Marko parser. | ||
*/ | ||
positionAt(index: number): import("./internal").Position; | ||
positionAt(offset: number): import("./internal").Position; | ||
/** | ||
@@ -21,0 +21,0 @@ * Given a offset range in the current source code, returns a Location object with a start & end position information. |
@@ -22,2 +22,3 @@ var __defProp = Object.defineProperty; | ||
__export(src_exports, { | ||
ErrorCode: () => ErrorCode, | ||
TagType: () => TagType, | ||
@@ -29,2 +30,31 @@ createParser: () => createParser | ||
// src/util/constants.ts | ||
var ErrorCode = /* @__PURE__ */ ((ErrorCode3) => { | ||
ErrorCode3[ErrorCode3["EXTRA_CLOSING_TAG"] = 0] = "EXTRA_CLOSING_TAG"; | ||
ErrorCode3[ErrorCode3["INVALID_ATTRIBUTE_ARGUMENT"] = 1] = "INVALID_ATTRIBUTE_ARGUMENT"; | ||
ErrorCode3[ErrorCode3["INVALID_ATTRIBUTE_NAME"] = 2] = "INVALID_ATTRIBUTE_NAME"; | ||
ErrorCode3[ErrorCode3["INVALID_ATTRIBUTE_VALUE"] = 3] = "INVALID_ATTRIBUTE_VALUE"; | ||
ErrorCode3[ErrorCode3["INVALID_CHARACTER"] = 4] = "INVALID_CHARACTER"; | ||
ErrorCode3[ErrorCode3["INVALID_CODE_AFTER_SEMICOLON"] = 5] = "INVALID_CODE_AFTER_SEMICOLON"; | ||
ErrorCode3[ErrorCode3["INVALID_EXPRESSION"] = 6] = "INVALID_EXPRESSION"; | ||
ErrorCode3[ErrorCode3["INVALID_INDENTATION"] = 7] = "INVALID_INDENTATION"; | ||
ErrorCode3[ErrorCode3["INVALID_LINE_START"] = 8] = "INVALID_LINE_START"; | ||
ErrorCode3[ErrorCode3["INVALID_REGULAR_EXPRESSION"] = 9] = "INVALID_REGULAR_EXPRESSION"; | ||
ErrorCode3[ErrorCode3["INVALID_STRING"] = 10] = "INVALID_STRING"; | ||
ErrorCode3[ErrorCode3["INVALID_TAG_ARGUMENT"] = 11] = "INVALID_TAG_ARGUMENT"; | ||
ErrorCode3[ErrorCode3["INVALID_TAG_SHORTHAND"] = 12] = "INVALID_TAG_SHORTHAND"; | ||
ErrorCode3[ErrorCode3["INVALID_TEMPLATE_STRING"] = 13] = "INVALID_TEMPLATE_STRING"; | ||
ErrorCode3[ErrorCode3["MALFORMED_CDATA"] = 14] = "MALFORMED_CDATA"; | ||
ErrorCode3[ErrorCode3["MALFORMED_CLOSE_TAG"] = 15] = "MALFORMED_CLOSE_TAG"; | ||
ErrorCode3[ErrorCode3["MALFORMED_COMMENT"] = 16] = "MALFORMED_COMMENT"; | ||
ErrorCode3[ErrorCode3["MALFORMED_DECLARATION"] = 17] = "MALFORMED_DECLARATION"; | ||
ErrorCode3[ErrorCode3["MALFORMED_DOCUMENT_TYPE"] = 18] = "MALFORMED_DOCUMENT_TYPE"; | ||
ErrorCode3[ErrorCode3["MALFORMED_OPEN_TAG"] = 19] = "MALFORMED_OPEN_TAG"; | ||
ErrorCode3[ErrorCode3["MALFORMED_PLACEHOLDER"] = 20] = "MALFORMED_PLACEHOLDER"; | ||
ErrorCode3[ErrorCode3["MISMATCHED_CLOSING_TAG"] = 21] = "MISMATCHED_CLOSING_TAG"; | ||
ErrorCode3[ErrorCode3["MISSING_END_TAG"] = 22] = "MISSING_END_TAG"; | ||
ErrorCode3[ErrorCode3["MISSING_TAG_VARIABLE"] = 23] = "MISSING_TAG_VARIABLE"; | ||
ErrorCode3[ErrorCode3["RESERVED_TAG_NAME"] = 24] = "RESERVED_TAG_NAME"; | ||
ErrorCode3[ErrorCode3["ROOT_TAG_ONLY"] = 25] = "ROOT_TAG_ONLY"; | ||
return ErrorCode3; | ||
})(ErrorCode || {}); | ||
var TagType = /* @__PURE__ */ ((TagType2) => { | ||
@@ -78,3 +108,3 @@ TagType2[TagType2["html"] = 0] = "html"; | ||
} else { | ||
return this.emitError(this.activeTag, "MISSING_END_TAG", 'Missing ending "' + this.read(this.activeTag.tagName) + '" tag'); | ||
return this.emitError(this.activeTag, 22 /* MISSING_END_TAG */, 'Missing ending "' + this.read(this.activeTag.tagName) + '" tag'); | ||
} | ||
@@ -449,3 +479,3 @@ } | ||
} else { | ||
this.emitError(attr, "MALFORMED_OPEN_TAG", 'EOF reached while parsing attribute "' + (attr.name ? this.read(attr.name) : "default") + '" for the "' + this.read(this.activeTag.tagName) + '" tag'); | ||
this.emitError(attr, 19 /* MALFORMED_OPEN_TAG */, 'EOF reached while parsing attribute "' + (attr.name ? this.read(attr.name) : "default") + '" for the "' + this.read(this.activeTag.tagName) + '" tag'); | ||
} | ||
@@ -466,3 +496,3 @@ }, | ||
if (attr.args) { | ||
this.emitError(child, "ILLEGAL_ATTRIBUTE_ARGUMENT", "An attribute can only have one set of arguments"); | ||
this.emitError(child, 1 /* INVALID_ATTRIBUTE_ARGUMENT */, "An attribute can only have one set of arguments"); | ||
return; | ||
@@ -514,3 +544,3 @@ } | ||
if (child.start === child.end) { | ||
return this.emitError(child, "ILLEGAL_ATTRIBUTE_VALUE", "Missing value for attribute"); | ||
return this.emitError(child, 3 /* INVALID_ATTRIBUTE_VALUE */, "Missing value for attribute"); | ||
} | ||
@@ -616,3 +646,3 @@ if (attr.spread) { | ||
} else { | ||
parser.emitError(parser.pos, "INVALID_CHARACTER", "A concise mode closing block delimiter can only be followed by whitespace."); | ||
parser.emitError(parser.pos, 4 /* INVALID_CHARACTER */, "A concise mode closing block delimiter can only be followed by whitespace."); | ||
} | ||
@@ -663,3 +693,3 @@ } else if (parser.lookAheadFor(indent, parser.pos + newLineLength)) { | ||
eof(cdata) { | ||
this.emitError(cdata, "MALFORMED_CDATA", "EOF reached while parsing CDATA"); | ||
this.emitError(cdata, 14 /* MALFORMED_CDATA */, "EOF reached while parsing CDATA"); | ||
}, | ||
@@ -703,3 +733,3 @@ return() { | ||
eof(closeTag) { | ||
this.emitError(closeTag, "MALFORMED_CLOSE_TAG", "EOF reached while parsing closing tag"); | ||
this.emitError(closeTag, 15 /* MALFORMED_CLOSE_TAG */, "EOF reached while parsing closing tag"); | ||
}, | ||
@@ -741,3 +771,3 @@ return() { | ||
if (!activeTag) { | ||
parser.emitError(closeTag, "EXTRA_CLOSING_TAG", 'The closing "' + parser.read({ start: closeTagNameStart, end: closeTagNameEnd }) + '" tag was not expected'); | ||
parser.emitError(closeTag, 0 /* EXTRA_CLOSING_TAG */, 'The closing "' + parser.read({ start: closeTagNameStart, end: closeTagNameEnd }) + '" tag was not expected'); | ||
return false; | ||
@@ -755,3 +785,3 @@ } | ||
})) { | ||
parser.emitError(closeTag, "MISMATCHED_CLOSING_TAG", 'The closing "' + parser.read(closeTagNamePos) + '" tag does not match the corresponding opening "' + (parser.read(activeTag.tagName) || "div") + '" tag'); | ||
parser.emitError(closeTag, 21 /* MISMATCHED_CLOSING_TAG */, 'The closing "' + parser.read(closeTagNamePos) + '" tag does not match the corresponding opening "' + (parser.read(activeTag.tagName) || "div") + '" tag'); | ||
return false; | ||
@@ -792,3 +822,3 @@ } | ||
if (!parentTag && curIndent) { | ||
this.emitError(this.pos, "BAD_INDENTATION", "Line has extra indentation at the beginning"); | ||
this.emitError(this.pos, 7 /* INVALID_INDENTATION */, "Line has extra indentation at the beginning"); | ||
return; | ||
@@ -798,3 +828,3 @@ } | ||
if (parentTag.type === 1 /* text */ && code !== 45 /* HTML_BLOCK_DELIMITER */) { | ||
this.emitError(this.pos, "ILLEGAL_LINE_START", 'A line within a tag that only allows text content must begin with a "-" character'); | ||
this.emitError(this.pos, 8 /* INVALID_LINE_START */, 'A line within a tag that only allows text content must begin with a "-" character'); | ||
return; | ||
@@ -805,3 +835,3 @@ } | ||
} else if (parentTag.nestedIndent !== this.indent) { | ||
this.emitError(this.pos, "BAD_INDENTATION", "Line indentation does match indentation of previous line"); | ||
this.emitError(this.pos, 7 /* INVALID_INDENTATION */, "Line indentation does match indentation of previous line"); | ||
return; | ||
@@ -828,3 +858,3 @@ } | ||
} else { | ||
this.emitError(this.pos, "ILLEGAL_LINE_START", 'A line in concise mode cannot start with a single hyphen. Use "--" instead. See: https://github.com/marko-js/htmljs-parser/issues/43'); | ||
this.emitError(this.pos, 8 /* INVALID_LINE_START */, 'A line in concise mode cannot start with a single hyphen. Use "--" instead. See: https://github.com/marko-js/htmljs-parser/issues/43'); | ||
} | ||
@@ -843,3 +873,3 @@ return; | ||
default: | ||
this.emitError(this.pos, "ILLEGAL_LINE_START", 'A line in concise mode cannot start with "/" unless it starts a "//" or "/*" comment'); | ||
this.emitError(this.pos, 8 /* INVALID_LINE_START */, 'A line in concise mode cannot start with "/" unless it starts a "//" or "/*" comment'); | ||
return; | ||
@@ -881,3 +911,3 @@ } | ||
if (!this.consumeWhitespaceOnLine(0)) { | ||
this.emitError(this.pos, "INVALID_CHARACTER", "In concise mode a javascript comment block can only be followed by whitespace characters and a newline."); | ||
this.emitError(this.pos, 4 /* INVALID_CHARACTER */, "In concise mode a javascript comment block can only be followed by whitespace characters and a newline."); | ||
} | ||
@@ -916,3 +946,3 @@ break; | ||
eof(declaration) { | ||
this.emitError(declaration, "MALFORMED_DECLARATION", "EOF reached while parsing declaration"); | ||
this.emitError(declaration, 17 /* MALFORMED_DECLARATION */, "EOF reached while parsing declaration"); | ||
}, | ||
@@ -968,3 +998,3 @@ return() { | ||
eof(documentType) { | ||
this.emitError(documentType, "MALFORMED_DOCUMENT_TYPE", "EOF reached while parsing document type"); | ||
this.emitError(documentType, 18 /* MALFORMED_DOCUMENT_TYPE */, "EOF reached while parsing document type"); | ||
}, | ||
@@ -1049,7 +1079,7 @@ return() { | ||
if (!expression.groupStack.length) { | ||
return this.emitError(expression, "INVALID_EXPRESSION", 'Mismatched group. A closing "' + String.fromCharCode(code) + '" character was found but it is not matched with a corresponding opening character.'); | ||
return this.emitError(expression, 6 /* INVALID_EXPRESSION */, 'Mismatched group. A closing "' + String.fromCharCode(code) + '" character was found but it is not matched with a corresponding opening character.'); | ||
} | ||
const expectedCode = expression.groupStack.pop(); | ||
if (expectedCode !== code) { | ||
return this.emitError(expression, "INVALID_EXPRESSION", 'Mismatched group. A "' + String.fromCharCode(code) + '" character was found when "' + String.fromCharCode(expectedCode) + '" was expected.'); | ||
return this.emitError(expression, 6 /* INVALID_EXPRESSION */, 'Mismatched group. A "' + String.fromCharCode(code) + '" character was found when "' + String.fromCharCode(expectedCode) + '" was expected.'); | ||
} | ||
@@ -1074,12 +1104,12 @@ break; | ||
if (!attr.spread && !attr.name) { | ||
return this.emitError(expression, "MALFORMED_OPEN_TAG", 'EOF reached while parsing attribute name for the "' + this.read(this.activeTag.tagName) + '" tag'); | ||
return this.emitError(expression, 19 /* MALFORMED_OPEN_TAG */, 'EOF reached while parsing attribute name for the "' + this.read(this.activeTag.tagName) + '" tag'); | ||
} | ||
return this.emitError(expression, "MALFORMED_OPEN_TAG", `EOF reached while parsing attribute value for the ${attr.spread ? "..." : attr.name ? `"${this.read(attr.name)}"` : `"default"`} attribute`); | ||
return this.emitError(expression, 19 /* MALFORMED_OPEN_TAG */, `EOF reached while parsing attribute value for the ${attr.spread ? "..." : attr.name ? `"${this.read(attr.name)}"` : `"default"`} attribute`); | ||
} | ||
case states_exports.TAG_NAME: | ||
return this.emitError(expression, "MALFORMED_OPEN_TAG", "EOF reached while parsing tag name"); | ||
return this.emitError(expression, 19 /* MALFORMED_OPEN_TAG */, "EOF reached while parsing tag name"); | ||
case states_exports.PLACEHOLDER: | ||
return this.emitError(expression, "MALFORMED_PLACEHOLDER", "EOF reached while parsing placeholder"); | ||
return this.emitError(expression, 20 /* MALFORMED_PLACEHOLDER */, "EOF reached while parsing placeholder"); | ||
} | ||
return this.emitError(expression, "INVALID_EXPRESSION", "EOF reached while parsing expression"); | ||
return this.emitError(expression, 6 /* INVALID_EXPRESSION */, "EOF reached while parsing expression"); | ||
} | ||
@@ -1176,3 +1206,3 @@ }, | ||
eof(comment) { | ||
this.emitError(comment, "MALFORMED_COMMENT", "EOF reached while parsing comment"); | ||
this.emitError(comment, 16 /* MALFORMED_COMMENT */, "EOF reached while parsing comment"); | ||
}, | ||
@@ -1342,3 +1372,3 @@ return() { | ||
eof(comment) { | ||
this.emitError(comment, "MALFORMED_COMMENT", "EOF reached while parsing multi-line JavaScript comment"); | ||
this.emitError(comment, 16 /* MALFORMED_COMMENT */, "EOF reached while parsing multi-line JavaScript comment"); | ||
}, | ||
@@ -1466,3 +1496,3 @@ return() { | ||
if (child.start === child.end) { | ||
this.emitError(child, "PLACEHOLDER_EXPRESSION_REQUIRED", "Invalid placeholder, the expression cannot be missing"); | ||
this.emitError(child, 20 /* MALFORMED_PLACEHOLDER */, "Invalid placeholder, the expression cannot be missing"); | ||
} | ||
@@ -1541,6 +1571,6 @@ this.pos++; | ||
eol(_, regExp) { | ||
this.emitError(regExp, "INVALID_REGULAR_EXPRESSION", "EOL reached while parsing regular expression"); | ||
this.emitError(regExp, 9 /* INVALID_REGULAR_EXPRESSION */, "EOL reached while parsing regular expression"); | ||
}, | ||
eof(regExp) { | ||
this.emitError(regExp, "INVALID_REGULAR_EXPRESSION", "EOF reached while parsing regular expression"); | ||
this.emitError(regExp, 9 /* INVALID_REGULAR_EXPRESSION */, "EOF reached while parsing regular expression"); | ||
}, | ||
@@ -1579,3 +1609,3 @@ return() { | ||
eof(string) { | ||
this.emitError(string, "INVALID_STRING", "EOF reached while parsing string expression"); | ||
this.emitError(string, 10 /* INVALID_STRING */, "EOF reached while parsing string expression"); | ||
}, | ||
@@ -1608,3 +1638,3 @@ return() { | ||
if (this.activeTag.hasShorthandId) { | ||
return this.emitError(tagName, "INVALID_TAG_SHORTHAND", "Multiple shorthand ID parts are not allowed on the same tag"); | ||
return this.emitError(tagName, 12 /* INVALID_TAG_SHORTHAND */, "Multiple shorthand ID parts are not allowed on the same tag"); | ||
} | ||
@@ -1641,6 +1671,6 @@ this.activeTag.hasShorthandId = true; | ||
if (!tag.concise) { | ||
return this.emitError(tagName, "RESERVED_TAG_NAME", `The "${this.read(tagName)}" tag is reserved and cannot be used as an HTML tag.`); | ||
return this.emitError(tagName, 24 /* RESERVED_TAG_NAME */, `The "${this.read(tagName)}" tag is reserved and cannot be used as an HTML tag.`); | ||
} | ||
if (tag.parentTag) { | ||
return this.emitError(tagName, "ROOT_TAG_ONLY", `"${this.read(tagName)}" can only be used at the root of the template.`); | ||
return this.emitError(tagName, 25 /* ROOT_TAG_ONLY */, `"${this.read(tagName)}" can only be used at the root of the template.`); | ||
} | ||
@@ -1680,3 +1710,3 @@ this.enterState(states_exports.EXPRESSION).terminatedByEOL = true; | ||
if (child.start === child.end) { | ||
this.emitError(child, "PLACEHOLDER_EXPRESSION_REQUIRED", "Invalid placeholder, the expression cannot be missing"); | ||
this.emitError(child, 20 /* MALFORMED_PLACEHOLDER */, "Invalid placeholder, the expression cannot be missing"); | ||
} | ||
@@ -1686,3 +1716,3 @@ const { quasis, expressions } = tagName; | ||
const end = ++this.pos; | ||
const nextStart = end + 1; | ||
const nextStart = end; | ||
expressions.push({ | ||
@@ -1730,3 +1760,3 @@ start, | ||
eof(templateString) { | ||
this.emitError(templateString, "INVALID_TEMPLATE_STRING", "EOF reached while parsing template string expression"); | ||
this.emitError(templateString, 13 /* INVALID_TEMPLATE_STRING */, "EOF reached while parsing template string expression"); | ||
}, | ||
@@ -1737,3 +1767,3 @@ eol() { | ||
if (child.start === child.end) { | ||
this.emitError(child, "PLACEHOLDER_EXPRESSION_REQUIRED", "Invalid placeholder, the expression cannot be missing"); | ||
this.emitError(child, 20 /* MALFORMED_PLACEHOLDER */, "Invalid placeholder, the expression cannot be missing"); | ||
} | ||
@@ -1820,3 +1850,3 @@ this.pos++; | ||
if (tag.stage === 4 /* ATTR_GROUP */) { | ||
this.emitError(tag, "MALFORMED_OPEN_TAG", 'EOF reached while within an attribute group (e.g. "[ ... ]").'); | ||
this.emitError(tag, 19 /* MALFORMED_OPEN_TAG */, 'EOF reached while within an attribute group (e.g. "[ ... ]").'); | ||
return; | ||
@@ -1826,3 +1856,3 @@ } | ||
} else { | ||
this.emitError(tag, "MALFORMED_OPEN_TAG", "EOF reached while parsing open tag"); | ||
this.emitError(tag, 19 /* MALFORMED_OPEN_TAG */, "EOF reached while parsing open tag"); | ||
} | ||
@@ -1857,3 +1887,3 @@ }, | ||
} | ||
this.emitError(this.pos, "INVALID_CODE_AFTER_SEMICOLON", "A semicolon indicates the end of a line. Only comments may follow it."); | ||
this.emitError(this.pos, 5 /* INVALID_CODE_AFTER_SEMICOLON */, "A semicolon indicates the end of a line. Only comments may follow it."); | ||
} | ||
@@ -1864,7 +1894,7 @@ return; | ||
if (this.lookAtCharCodeAhead(1) !== 45 /* HTML_BLOCK_DELIMITER */) { | ||
this.emitError(tag, "MALFORMED_OPEN_TAG", '"-" not allowed as first character of attribute name'); | ||
this.emitError(tag, 19 /* MALFORMED_OPEN_TAG */, '"-" not allowed as first character of attribute name'); | ||
return; | ||
} | ||
if (tag.stage === 4 /* ATTR_GROUP */) { | ||
this.emitError(this.pos, "MALFORMED_OPEN_TAG", "Attribute group was not properly ended"); | ||
this.emitError(this.pos, 19 /* MALFORMED_OPEN_TAG */, "Attribute group was not properly ended"); | ||
return; | ||
@@ -1894,3 +1924,3 @@ } | ||
if (tag.stage === 4 /* ATTR_GROUP */) { | ||
this.emitError(this.pos, "MALFORMED_OPEN_TAG", 'Unexpected "[" character within open tag.'); | ||
this.emitError(this.pos, 19 /* MALFORMED_OPEN_TAG */, 'Unexpected "[" character within open tag.'); | ||
return; | ||
@@ -1902,3 +1932,3 @@ } | ||
if (tag.stage !== 4 /* ATTR_GROUP */) { | ||
this.emitError(this.pos, "MALFORMED_OPEN_TAG", 'Unexpected "]" character within open tag.'); | ||
this.emitError(this.pos, 19 /* MALFORMED_OPEN_TAG */, 'Unexpected "]" character within open tag.'); | ||
return; | ||
@@ -1920,3 +1950,3 @@ } | ||
if (code === 60 /* OPEN_ANGLE_BRACKET */) { | ||
return this.emitError(this.pos, "ILLEGAL_ATTRIBUTE_NAME", 'Invalid attribute name. Attribute name cannot begin with the "<" character.'); | ||
return this.emitError(this.pos, 2 /* INVALID_ATTRIBUTE_NAME */, 'Invalid attribute name. Attribute name cannot begin with the "<" character.'); | ||
} | ||
@@ -1943,3 +1973,3 @@ if (code === 47 /* FORWARD_SLASH */ && this.lookAtCharCodeAhead(1) === 42 /* ASTERISK */) { | ||
if (tag.hasArgs) { | ||
this.emitError(this.pos, "ILLEGAL_TAG_ARGUMENT", "A tag can only have one argument"); | ||
this.emitError(this.pos, 11 /* INVALID_TAG_ARGUMENT */, "A tag can only have one argument"); | ||
return; | ||
@@ -1980,3 +2010,3 @@ } | ||
if (child.start === child.end) { | ||
return this.emitError(child, "MISSING_TAG_VARIABLE", "A slash was found that was not followed by a variable name or lhs expression"); | ||
return this.emitError(child, 23 /* MISSING_TAG_VARIABLE */, "A slash was found that was not followed by a variable name or lhs expression"); | ||
} | ||
@@ -2045,4 +2075,4 @@ (_b = (_a = this.options).onTagVar) == null ? void 0 : _b.call(_a, { | ||
}, | ||
positionAt(index) { | ||
return parser.positionAt(index); | ||
positionAt(offset) { | ||
return parser.positionAt(offset); | ||
}, | ||
@@ -2056,4 +2086,5 @@ locationAt(range) { | ||
0 && (module.exports = { | ||
ErrorCode, | ||
TagType, | ||
createParser | ||
}); |
@@ -73,3 +73,3 @@ export declare const enum CODE { | ||
interface Error extends Range { | ||
code: string; | ||
code: ErrorCode; | ||
message: string; | ||
@@ -100,2 +100,30 @@ } | ||
} | ||
export declare enum ErrorCode { | ||
EXTRA_CLOSING_TAG = 0, | ||
INVALID_ATTRIBUTE_ARGUMENT = 1, | ||
INVALID_ATTRIBUTE_NAME = 2, | ||
INVALID_ATTRIBUTE_VALUE = 3, | ||
INVALID_CHARACTER = 4, | ||
INVALID_CODE_AFTER_SEMICOLON = 5, | ||
INVALID_EXPRESSION = 6, | ||
INVALID_INDENTATION = 7, | ||
INVALID_LINE_START = 8, | ||
INVALID_REGULAR_EXPRESSION = 9, | ||
INVALID_STRING = 10, | ||
INVALID_TAG_ARGUMENT = 11, | ||
INVALID_TAG_SHORTHAND = 12, | ||
INVALID_TEMPLATE_STRING = 13, | ||
MALFORMED_CDATA = 14, | ||
MALFORMED_CLOSE_TAG = 15, | ||
MALFORMED_COMMENT = 16, | ||
MALFORMED_DECLARATION = 17, | ||
MALFORMED_DOCUMENT_TYPE = 18, | ||
MALFORMED_OPEN_TAG = 19, | ||
MALFORMED_PLACEHOLDER = 20, | ||
MISMATCHED_CLOSING_TAG = 21, | ||
MISSING_END_TAG = 22, | ||
MISSING_TAG_VARIABLE = 23, | ||
RESERVED_TAG_NAME = 24, | ||
ROOT_TAG_ONLY = 25 | ||
} | ||
export declare const enum TagType { | ||
@@ -110,2 +138,3 @@ html = 0, | ||
onText?(data: Range): void; | ||
onPlaceholder?(data: Ranges.Placeholder): void; | ||
onComment?(data: Ranges.Value): void; | ||
@@ -116,3 +145,2 @@ onCDATA?(data: Ranges.Value): void; | ||
onScriptlet?(data: Ranges.Scriptlet): void; | ||
onPlaceholder?(data: Ranges.Placeholder): void; | ||
onTagName?(data: Ranges.TagName): TagType | void; | ||
@@ -119,0 +147,0 @@ onTagShorthandId?(data: Ranges.Template): void; |
import { type Parser } from "../internal"; | ||
import type { Location, Position, Range } from "./constants"; | ||
import { Location, Position, Range } from "./constants"; | ||
export declare function isWhitespaceCode(code: number): boolean; | ||
@@ -4,0 +4,0 @@ export declare function getLoc(lines: number[], range: Range): Location; |
{ | ||
"name": "htmljs-parser", | ||
"description": "An HTML parser recognizes content and string placeholders and allows JavaScript expressions as attribute values", | ||
"version": "3.2.1", | ||
"version": "3.2.2", | ||
"author": "Phillip Gates-Idem <phillip.idem@gmail.com>", | ||
@@ -6,0 +6,0 @@ "devDependencies": { |
870
README.md
@@ -1,591 +0,397 @@ | ||
# htmljs-parser | ||
<h1 align="center"> | ||
<!-- Logo --> | ||
<br/> | ||
htmljs-parser | ||
<br/> | ||
HTML parsers written according to the HTML spec will interpret all | ||
attribute values as strings which makes it challenging to properly | ||
describe a value's type (boolean, string, number, array, etc.) | ||
or to provide a complex JavaScript expression as a value. | ||
The ability to describe JavaScript expressions within attributes | ||
is important for HTML-based template compilers. | ||
<!-- Format --> | ||
<a href="https://github.com/prettier/prettier"> | ||
<img src="https://img.shields.io/badge/styled_with-prettier-ff69b4.svg" alt="Styled with prettier"/> | ||
</a> | ||
<!-- CI --> | ||
<a href="https://github.com/marko-js/htmljs-parser/actions/workflows/ci.yml"> | ||
<img src="https://github.com/marko-js/htmljs-parser/actions/workflows/ci.yml/badge.svg" alt="Build status"/> | ||
</a> | ||
<!-- Coverage --> | ||
<a href="https://codecov.io/gh/marko-js/htmljs-parser"> | ||
<img src="https://codecov.io/gh/marko-js/htmljs-parser/branch/main/graph/badge.svg?token=Sv8ePs16ix" alt="Code Coverage"/> | ||
</a> | ||
<!-- NPM Version --> | ||
<a href="https://npmjs.org/package/htmljs-parser"> | ||
<img src="https://img.shields.io/npm/v/htmljs-parser.svg" alt="NPM version"/> | ||
</a> | ||
<!-- Downloads --> | ||
<a href="https://npmjs.org/package/htmljs-parser"> | ||
<img src="https://img.shields.io/npm/dm/htmljs-parser.svg" alt="Downloads"/> | ||
</a> | ||
</h1> | ||
For example, consider a HTML-based template that wishes to | ||
support a custom tag named `<say-hello>` that supports an | ||
attribute named `message` that can be a string literal or a JavaScript expression. | ||
An HTML parser with super powers used by [Marko](https://markojs.com/docs/syntax/). | ||
Ideally, the template compiler should be able to handle any of the following: | ||
# Installation | ||
```html | ||
<say-hello message="Hello world!" /> | ||
<say-hello message=("Hello " + personName + "!") /> | ||
<say-hello message="Hello ${personName}!" /> | ||
```console | ||
npm install htmljs-parser | ||
``` | ||
This parser extends the HTML grammar to add these important features: | ||
# Creating A Parser | ||
- JavaScript expressions as attribute values | ||
First we must create a parser instance and pass it some handlers for the various parse events shown below. | ||
```html | ||
<say-hello message=("Hello " + personName) count=2+2 large=true /> | ||
``` | ||
Each parse event is called a `Range` and is an object with start and end properties which are zero-based offsets from the beginning of th parsed code. | ||
- Placeholders in the content of an element | ||
Additional meta data and nested ranges are exposed on some events shown below. | ||
```html | ||
<div>Hello ${personName}</div> | ||
``` | ||
You can get the raw string from any range using `parser.read(range)`. | ||
- Placeholders within attribute value strings | ||
```javascript | ||
import { createParser, ErrorCode, TagType } from "htmljs-parser"; | ||
```html | ||
<div data-message="Hello ${personName}!"></div> | ||
``` | ||
const parser = createParser({ | ||
/** | ||
* Called when the parser encounters an error. | ||
* | ||
* @example | ||
* 1╭─ <a><b | ||
* ╰─ ╰─ error(code: 19, message: "EOF reached while parsing open tag") | ||
*/ | ||
onError(range) { | ||
range.code; // An error code id. You can see the list of error codes in ErrorCode imported above. | ||
range.message; // A human readable (hopefully) error message. | ||
}, | ||
- JavaScript flow-control statements within HTML elements | ||
/** | ||
* Called when some static text is parsed within some body content. | ||
* | ||
* @example | ||
* 1╭─ <div>Hi</div> | ||
* ╰─ ╰─ text "Hi" | ||
*/ | ||
onText(range) {}, | ||
```html | ||
<div for(a in b) /> | ||
<div if(a === b) /> | ||
``` | ||
/** | ||
* Called after parsing a placeholder within body content. | ||
* | ||
* @example | ||
* 1╭─ <div>${hello} $!{world}</div> | ||
* │ │ │ │ ╰─ placeholder.value "world" | ||
* │ │ │ ╰─ placeholder "$!{world}" | ||
* │ │ ╰─ placeholder:escape.value "hello" | ||
* ╰─ ╰─ placeholder:escape "${hello}" | ||
*/ | ||
onPlaceholder(range) { | ||
range.escape; // true for ${} placeholders and false for $!{} placeholders. | ||
range.value; // Another range that includes only the placeholder value itself without the wrapping braces. | ||
}, | ||
- JavaScript flow-control statements as elements | ||
```html | ||
<for (a in b)> <if (a in b)></if></for> | ||
``` | ||
# Installation | ||
```bash | ||
npm install htmljs-parser | ||
``` | ||
# Usage | ||
```javascript | ||
var parser = require("htmljs-parser").createParser({ | ||
onText: function (event) { | ||
// Text within an HTML element | ||
var value = event.value; | ||
/** | ||
* Called when we find a comment at the root of the document or within a tags contents. | ||
* It will not be fired for comments within expressions, such as attribute values. | ||
* | ||
* @example | ||
* 1╭─ <!-- hi --> | ||
* │ │ ╰─ comment.value " hi " | ||
* ╰─ ╰─ comment "<!-- hi -->" | ||
* 2╭─ // hi | ||
* │ │ ╰─ comment.value " hi" | ||
* ╰─ ╰─ comment "// hi" | ||
*/ | ||
onComment(range) { | ||
range.value; // Another range that only includes the contents of the comment. | ||
}, | ||
onPlaceholder: function (event) { | ||
// ${<value>]} // escape = true | ||
// $!{<value>]} // escape = false | ||
var value = event.value; // String | ||
var escaped = event.escaped; // boolean | ||
var withinBody = event.withinBody; // boolean | ||
var withinAttribute = event.withinAttribute; // boolean | ||
var withinOpenTag = event.withinOpenTag; // boolean | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after parsing a CDATA section. | ||
* // https://developer.mozilla.org/en-US/docs/Web/API/CDATASection | ||
* | ||
* @example | ||
* 1╭─ <![CDATA[hi]]> | ||
* │ │ ╰─ cdata.value "hi" | ||
* ╰─ ╰─ cdata "<![CDATA[hi]]>" | ||
*/ | ||
onCDATA(range) { | ||
range.value; // Another range that only includes the contents of the CDATA. | ||
}, | ||
onString: function (event) { | ||
// Text within "" | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after parsing a DocType comment. | ||
* https://developer.mozilla.org/en-US/docs/Web/API/DocumentType | ||
* | ||
* @example | ||
* 1╭─ <!DOCTYPE html> | ||
* │ │ ╰─ doctype.value "DOCTYPE html" | ||
* ╰─ ╰─ doctype "<!DOCTYPE html>" | ||
*/ | ||
onDoctype(range) { | ||
range.value; // Another range that only includes the contents of the DocType. | ||
}, | ||
onCDATA: function (event) { | ||
// <![CDATA[<value>]]> | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after parsing an XML declaration. | ||
* https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction#xml_declaration | ||
* | ||
* @example | ||
* 1╭─ <?xml version="1.0" encoding="UTF-8"?> | ||
* │ │ ╰─ declaration.value "xml version=\"1.0\" encoding=\"UTF-8\"" | ||
* ╰─ ╰─ declaration "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | ||
*/ | ||
onDeclaration(range) { | ||
range.value; // Another range that only includes the contents of the declaration. | ||
}, | ||
onOpenTag: function (event) { | ||
var tagName = event.tagName; // String | ||
var attributes = event.attributes; // Array | ||
var argument = event.argument; // Object | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after parsing a scriptlet (new line followed by a $). | ||
* | ||
* @example | ||
* 1╭─ $ foo(); | ||
* │ │╰─ scriptlet.value "foo();" | ||
* ╰─ ╰─ scriptlet " foo();" | ||
* 2╭─ $ { bar(); } | ||
* │ │ ╰─ scriptlet:block.value " bar(); " | ||
* ╰─ ╰─ scriptlet:block " { bar(); }" | ||
*/ | ||
onScriptlet(range) { | ||
range.block; // true if the scriptlet was contained within braces. | ||
range.value; // Another range that includes only the value itself without the leading $ or surrounding braces (if applicable). | ||
}, | ||
onCloseTag: function (event) { | ||
// close tag | ||
var tagName = event.tagName; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called when a tag name, which can include placeholders, has been parsed. | ||
* | ||
* @example | ||
* 1╭─ <div/> | ||
* ╰─ ╰─ tagName "div" | ||
* 2╭─ <hello-${test}-again/> | ||
* │ │ │ ╰─ tagName.quasis[1] "-again" | ||
* │ │ ╰─ tagName.expressions[0] "${test}" | ||
* │ ├─ tagName.quasis[0] "hello-" | ||
* ╰─ ╰─ tagName "hello-${test}-again" | ||
*/ | ||
onTagName(range) { | ||
range.concise; // true if this tag is a concise mode tag. | ||
range.quasis; // An array of ranges that indicate the string literal parts of the tag name. | ||
range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder). | ||
// Return a different tag type enum value to enter a different parse mode. | ||
// Below is approximately what Marko uses: | ||
switch (parser.read(range)) { | ||
case "area": | ||
case "base": | ||
case "br": | ||
case "col": | ||
case "embed": | ||
case "hr": | ||
case "img": | ||
case "input": | ||
case "link": | ||
case "meta": | ||
case "param": | ||
case "source": | ||
case "track": | ||
case "wbr": | ||
// TagType.void makes this a void element (cannot have children). | ||
return TagType.void; | ||
case "html-comment": | ||
case "script": | ||
case "style": | ||
case "textarea": | ||
// TagType.text makes the child content text only (with placeholders). | ||
return TagType.text; | ||
case "class": | ||
case "export": | ||
case "import": | ||
case "static": | ||
// TagType.statement makes this a statement tag where the content following the tag name will be parsed as script code until we reach a new line, eg for `import x from "y"`). | ||
return TagType.statement; | ||
} | ||
// TagType.html is the default which allows child content as html with placeholders. | ||
return TagType.html; | ||
}, | ||
onDocumentType: function (event) { | ||
// Document Type/DTD | ||
// <!<value>> | ||
// Example: <!DOCTYPE html> | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called when a shorthand id, which can include placeholders, has been parsed. | ||
* | ||
* @example | ||
* 1╭─ <div#hello-${test}-again/> | ||
* │ ││ │ ╰─ tagShorthandId.quasis[1] "-again" | ||
* │ ││ ╰─ tagShorthandId.expressions[0] "${test}" | ||
* │ │╰─ tagShorthandId.quasis[0] "hello-" | ||
* ╰─ ╰─ tagShorthandId "#hello-${test}-again" | ||
*/ | ||
onTagShorthandId(range) { | ||
range.quasis; // An array of ranges that indicate the string literal parts of the shorthand id name. | ||
range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder). | ||
}, | ||
onDeclaration: function (event) { | ||
// Declaration | ||
// <?<value>?> | ||
// Example: <?xml version="1.0" encoding="UTF-8" ?> | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called when a shorthand class name, which can include placeholders, has been parsed. | ||
* Note there can be multiple of these. | ||
* | ||
* @example | ||
* 1╭─ <div.hello-${test}-again/> | ||
* │ ││ │ ╰─ tagShorthandClassName.quasis[1] "-again" | ||
* │ ││ ╰─ tagShorthandClassName.expressions[0] "${test}" | ||
* │ │╰─ tagShorthandClassName.quasis[0] "hello-" | ||
* ╰─ ╰─ tagShorthandClassName "#hello-${test}-again" | ||
*/ | ||
onTagShorthandClass(range) { | ||
range.quasis; // An array of ranges that indicate the string literal parts of the shorthand id name. | ||
range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder). | ||
}, | ||
onComment: function (event) { | ||
// Text within XML comment | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after a tag variable has been parsed. | ||
* | ||
* @example | ||
* 1╭─ <div/el/> | ||
* │ │╰─ tagVar.value "el" | ||
* ╰─ ╰─ tagVar "/el" | ||
*/ | ||
onTagVar(range) { | ||
range.value; // Another range that includes only the tag var itself and not the leading slash. | ||
}, | ||
onScriptlet: function (event) { | ||
// Text within <% %> | ||
var value = event.value; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after tag arguments have been parsed. | ||
* | ||
* @example | ||
* 1╭─ <if(x)> | ||
* │ │╰─ tagArgs.value "x" | ||
* ╰─ ╰─ tagArgs "(x)" | ||
*/ | ||
onTagArgs(range) { | ||
range.value; // Another range that includes only the args themselves and not the outer parenthesis. | ||
}, | ||
onError: function (event) { | ||
// Error | ||
var message = event.message; // String | ||
var code = event.code; // String | ||
var pos = event.pos; // Integer | ||
/** | ||
* Called after tag parameters have been parsed. | ||
* | ||
* @example | ||
* 1╭─ <for|item| of=list> | ||
* │ │╰─ tagParams.value "item" | ||
* ╰─ ╰─ tagParams "|item|" | ||
*/ | ||
onTagParams(range) { | ||
range.value; // Another range that includes only the params themselves and not the outer pipes. | ||
}, | ||
}); | ||
parser.parse(str); | ||
``` | ||
/** | ||
* Called after an attribute name as been parsed. | ||
* Note this may be followed by the related AttrArgs, AttrValue or AttrMethod. It can also be directly followed by another AttrName, AttrSpread or the OpenTagEnd if this is a boolean attribute. | ||
* | ||
* @example | ||
* 1╭─ <div class="hi"> | ||
* ╰─ ╰─ attrName "class" | ||
*/ | ||
onAttrName(range) {}, | ||
## Content Parsing Modes | ||
/** | ||
* Called after attr arguments have been parsed. | ||
* | ||
* @example | ||
* 1╭─ <div if(x)> | ||
* │ │╰─ attrArgs.value "x" | ||
* ╰─ ╰─ attrArgs "(x)" | ||
*/ | ||
onAttrArgs(range) { | ||
range.value; // Another range that includes only the args themselves and not the outer parenthesis. | ||
}, | ||
The parser, by default, will look for HTML tags within content. This behavior | ||
might not be desirable for certain tags, so the parser allows the parsing mode | ||
to be changed (usually in response to an `onOpenTag` event). | ||
/** | ||
* Called after an attr value has been parsed. | ||
* | ||
* @example | ||
* 1╭─ <input name="hi" value:=x> | ||
* │ ││ │ ╰─ attrValue:bound.value | ||
* │ ││ ╰─ attrValue:bound ":=x" | ||
* │ │╰─ attrValue.value "\"hi\"" | ||
* ╰─ ╰─ attrValue "=\"hi\"" | ||
*/ | ||
onAttrValue(range) { | ||
range.bound; // true if the attribute value was preceded by :=. | ||
range.value; // Another range that includes only the value itself without the leading = or :=. | ||
}, | ||
There are three content parsing modes: | ||
/** | ||
* Called after an attribute method shorthand has been parsed. | ||
* | ||
* @example | ||
* 1╭─ <div onClick(ev) { foo(); }> | ||
* │ ││ │╰─ attrMethod.body.value " foo(); " | ||
* │ ││ ╰─ attrMethod.body "{ foo(); }" | ||
* │ │╰─ attrMethod.params.value "ev" | ||
* │ ├─ attrMethod.params "(ev)" | ||
* ╰─ ╰─ attrMethod "(ev) { foo(); }" | ||
*/ | ||
onAttrMethod(range) { | ||
range.params; // Another range which includes the params for the method. | ||
range.params.value; // Another range which includes the method params without outer parenthesis. | ||
- **HTML Content (DEFAULT):** | ||
The parser will look for any HTML tag and content placeholders while in | ||
this mode and parse opening and closing tags accordingly. | ||
range.body; // Another range which includes the entire body block. | ||
range.body.value; // Another range which includes the body block without outer braces. | ||
}, | ||
- **Parsed Text Content**: The parser will look for the closing tag that matches | ||
the current open tag as well as content placeholders but all other content | ||
will be interpreted as text. | ||
/** | ||
* Called after we've parsed a spread attribute. | ||
* | ||
* @example | ||
* 1╭─ <div ...attrs> | ||
* │ │ ╰─ attrSpread.value "attrs" | ||
* ╰─ ╰─ attrSpread "...attrs" | ||
*/ | ||
onAttrSpread(range) { | ||
range.value; // Another range that includes only the value itself without the leading ... | ||
}, | ||
- **Static Text Content**: The parser will look for the closing tag that matches | ||
the current open tag but all other content will be interpreted as raw text. | ||
/** | ||
* Called once we've completed parsing the open tag. | ||
* | ||
* @example | ||
* 1╭─ <div><span/></div> | ||
* │ │ ╰─ openTagEnd:selfClosed "/>" | ||
* ╰─ ╰─ openTagEnd ">" | ||
*/ | ||
onOpenTagEnd(range) { | ||
range.selfClosed; // true if this tag was self closed (onCloseTag would not be called if so). | ||
}, | ||
```javascript | ||
var htmljs = require('htmljs-parser'); | ||
var parser = htmljs.createParser({ | ||
onOpenTag: function(event) { | ||
// open tag | ||
switch(event.tagName) { | ||
case 'textarea': | ||
//fall through | ||
case 'script': | ||
//fall through | ||
case 'style': | ||
// parse the content within these tags but only | ||
// look for placeholders and the closing tag. | ||
parser.enterParsedTextContentState(); | ||
break; | ||
case 'dummy' | ||
// treat content within <dummy>...</dummy> as raw | ||
// text and ignore other tags and placeholders | ||
parser.enterStaticTextContentState(); | ||
break; | ||
default: | ||
// The parser will switch to HTML content parsing mode | ||
// if the parsing mode is not explicitly changed by | ||
// "onOpenTag" function. | ||
} | ||
} | ||
/** | ||
* Called once the closing tag (or in concise mode an outdent or eof) is parsed. | ||
* Note this is not called for selfClosed, void or statement tags. | ||
* | ||
* @example | ||
* 1╭─ <div><span/></div> | ||
* │ │ ╰─ closeTag(div).value "div" | ||
* ╰─ ╰─ closeTag(div) "</div>" | ||
*/ | ||
onCloseTag(range) { | ||
range.value; // The raw content of the closing tag (undefined in concise mode). | ||
}, | ||
}); | ||
parser.parse(str); | ||
``` | ||
## Parsing Events | ||
Finally after setting up the parser with it's handlers, it's time to pass in some source code to parse. | ||
The `htmljs-parser` is an event-based parser which means that it will emit | ||
events as it is parsing the document. Events are emitted via calls | ||
to `on<eventname>` function which are supplied as properties in the options | ||
via call to `require('htmljs-parser').createParser(options)`. | ||
### onOpenTag | ||
The `onOpenTag` function will be called each time an opening tag is | ||
encountered. | ||
**EXAMPLE: Simple tag** | ||
INPUT: | ||
```html | ||
<div></div> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'openTag', | ||
tagName: 'div', | ||
attributes: [] | ||
} | ||
parser.parse("<div></div>"); | ||
``` | ||
**EXAMPLE: Tag with literal attribute values** | ||
# Parser Helpers | ||
INPUT: | ||
The parser instance provides a few helpers to make it easier to work with the parsed content. | ||
```html | ||
<div class="demo" disabled="false" data-number="123"></div> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'openTag', | ||
tagName: 'div', | ||
attributes: [ | ||
{ | ||
name: 'class', | ||
value: '"demo"' | ||
}, | ||
{ | ||
name: 'disabled', | ||
value: 'false' | ||
}, | ||
{ | ||
name: 'data-number', | ||
value: '123' | ||
} | ||
] | ||
} | ||
``` | ||
// Pass any range object into this method to get the raw string from the source for the range. | ||
parser.read(range); | ||
**EXAMPLE: Tag with expression attribute** | ||
// Given an zero based offset within the source code, returns a position object that contains line and column properties. | ||
parser.positionAt(offset); | ||
INPUT: | ||
```html | ||
<say-something message=("Hello "+data.name)/> | ||
// Given a range object returns a location object with start and end properties which are each position objects as returned from the "positionAt" api. | ||
parser.locationAt(range); | ||
``` | ||
OUTPUT EVENT: | ||
# Code of Conduct | ||
```javascript | ||
{ | ||
type: 'openTag', | ||
tagName: 'div', | ||
attributes: [ | ||
{ | ||
name: 'message', | ||
value: '"Hello "+data.name' | ||
} | ||
] | ||
} | ||
``` | ||
**EXAMPLE: Tag with an argument** | ||
INPUT: | ||
```html | ||
<for(var i="0;" i < 10; i++)></for(var> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'openTag', | ||
tagName: 'for', | ||
argument: { | ||
value: 'var i = 0; i < 10; i++', | ||
pos: ... // Integer | ||
}, | ||
attributes: [] | ||
} | ||
``` | ||
**EXAMPLE: Attribute with an argument** | ||
INPUT: | ||
```html | ||
<div if(x>y)></div> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'openTag', | ||
tagName: 'div', | ||
attributes: [ | ||
{ | ||
name: 'if', | ||
argument: { | ||
value: 'x > y', | ||
pos: ... // Integer | ||
} | ||
} | ||
] | ||
} | ||
``` | ||
### onCloseTag | ||
The `onCloseTag` function will be called each time a closing tag is | ||
encountered. | ||
**EXAMPLE: Simple close tag** | ||
INPUT: | ||
```html | ||
</div> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'closeTag', | ||
tagName: 'div' | ||
} | ||
``` | ||
### onText | ||
The `onText` function will be called each time within an element | ||
when textual data is encountered. | ||
**NOTE:** Text within `<![CDATA[` `]]>` will be emitted via call | ||
to `onCDATA`. | ||
**EXAMPLE** | ||
In the following example code, the `TEXT` sequences will be emitted as | ||
text events. | ||
INPUT: | ||
```html | ||
Simple text | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'text', | ||
value: 'Simple text' | ||
} | ||
``` | ||
### onCDATA | ||
The `onCDATA` function will be called when text within `<![CDATA[` `]]>` | ||
is encountered. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<![CDATA[This is text]]> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'cdata', | ||
value: 'This is text' | ||
} | ||
``` | ||
### onPlaceholder | ||
The `onPlaceholder` function will be called each time a placeholder | ||
is encountered. | ||
If the placeholder starts with the `$!{` sequence then `event.escape` | ||
will be `false`. | ||
If the placeholder starts with the `${` sequence then `event.escape` will be | ||
`true`. | ||
Text within `<![CDATA[` `]]>` and `<!--` `-->` will not be parsed so you | ||
cannot use placeholders for these blocks of code. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
${"This is an escaped placeholder"} $!{"This is a non-escaped placeholder"} | ||
``` | ||
OUTPUT EVENTS | ||
```html | ||
${name} | ||
``` | ||
```javascript | ||
{ | ||
type: 'placeholder', | ||
value: 'name', | ||
escape: true | ||
} | ||
``` | ||
--- | ||
```html | ||
$!{name} | ||
``` | ||
```javascript | ||
{ | ||
type: 'placeholder', | ||
value: 'name', | ||
escape: true | ||
} | ||
``` | ||
**NOTE:** | ||
The `escape` flag is merely informational. The application code is responsible | ||
for interpreting this flag to properly escape the expression. | ||
Here's an example of modifying the expression based on the `event.escape` flag: | ||
```javascript | ||
onPlaceholder: function(event) { | ||
if (event.escape) { | ||
event.value = 'escapeXml(' + event.value + ')'; | ||
} | ||
} | ||
``` | ||
### onDocumentType | ||
The `onDocumentType` function will be called when the document type declaration | ||
is encountered _anywhere_ in the content. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'documentType', | ||
value: 'DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"' | ||
} | ||
``` | ||
### onDeclaration | ||
The `onDeclaration` function will be called when an XML declaration | ||
is encountered _anywhere_ in the content. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'declaration', | ||
value: 'xml version="1.0" encoding="UTF-8"' | ||
} | ||
``` | ||
### onComment | ||
The `onComment` function will be called when text within `<!--` `-->` | ||
is encountered. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<!--This is a comment--> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'comment', | ||
value: 'This is a comment' | ||
} | ||
``` | ||
### onScriptlet | ||
The `onScriptlet` function will be called when text within `<%` `%>` | ||
is encountered. | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<% console.log("Hello World!"); %> | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'scriptlet', | ||
value: ' console.log("Hello World!"); ' | ||
} | ||
``` | ||
### onError | ||
The `onError` function will be called when malformed content is detected. | ||
The most common cause for an error is due to reaching the end of the | ||
input while still parsing an open tag, close tag, XML comment, CDATA section, | ||
DTD, XML declaration, or placeholder. | ||
Possible error codes: | ||
- `MISSING_END_TAG` | ||
- `MISSING_END_DELIMITER` | ||
- `MALFORMED_OPEN_TAG` | ||
- `MALFORMED_CLOSE_TAG` | ||
- `MALFORMED_CDATA` | ||
- `MALFORMED_PLACEHOLDER` | ||
- `MALFORMED_DOCUMENT_TYPE` | ||
- `MALFORMED_DECLARATION` | ||
- `MALFORMED_COMMENT` | ||
- `EXTRA_CLOSING_TAG` | ||
- `MISMATCHED_CLOSING_TAG` | ||
- ... | ||
**EXAMPLE:** | ||
INPUT: | ||
```html | ||
<a href=" | ||
``` | ||
OUTPUT EVENT: | ||
```javascript | ||
{ | ||
type: 'error', | ||
code: 'MALFORMED_OPEN_TAG', | ||
message: 'EOF reached while parsing open tag.', | ||
pos: 0, | ||
endPos: 9 | ||
} | ||
``` | ||
This project adheres to the [eBay Code of Conduct](./.github/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
150816
4427
398