refactor: move dom-specific options to compiler-dom
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
// Vue template is a platform-agnostic superset of HTML (syntax only).
|
||||
// More namespaces like SVG and MathML are declared by platform specific
|
||||
// compilers.
|
||||
export type Namespace = number
|
||||
|
||||
export const enum Namespaces {
|
||||
HTML
|
||||
}
|
||||
|
||||
export const enum NodeTypes {
|
||||
TEXT,
|
||||
COMMENT,
|
||||
@@ -15,12 +24,6 @@ export const enum ElementTypes {
|
||||
TEMPLATE // template, component
|
||||
}
|
||||
|
||||
export const enum Namespaces {
|
||||
HTML,
|
||||
SVG, // allows CDATA section and forbids end tag omission.
|
||||
MATH_ML // allows CDATA section and forbids end tag omission.
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
type: NodeTypes
|
||||
loc: SourceLocation
|
||||
@@ -33,7 +36,7 @@ export interface RootNode extends Node {
|
||||
|
||||
export interface ElementNode extends Node {
|
||||
type: NodeTypes.ELEMENT
|
||||
ns: Namespaces
|
||||
ns: Namespace
|
||||
tag: string
|
||||
tagType: ElementTypes
|
||||
isSelfClosing: boolean
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { parse } from './parser'
|
||||
export { parse, ParserOptions, TextModes } from './parser'
|
||||
export * from './ast'
|
||||
export * from './errorTypes'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ParserErrorTypes } from './errorTypes'
|
||||
import { ParserErrorTypes, errorMessages } from './errorTypes'
|
||||
import {
|
||||
Node,
|
||||
Namespace,
|
||||
Namespaces,
|
||||
AttributeNode,
|
||||
CommentNode,
|
||||
DirectiveNode,
|
||||
ElementNode,
|
||||
ElementTypes,
|
||||
ExpressionNode,
|
||||
Namespaces,
|
||||
NodeTypes,
|
||||
Position,
|
||||
RootNode,
|
||||
@@ -16,30 +16,46 @@ import {
|
||||
} from './ast'
|
||||
|
||||
export interface ParserOptions {
|
||||
isVoidTag: (tag: string) => boolean // e.g. img, br, hr
|
||||
getNamespace: (tag: string, parent: ElementNode | undefined) => Namespaces
|
||||
getTextMode: (tag: string, ns: Namespaces) => TextModes
|
||||
delimiters: [string, string] // ['{{', '}}']
|
||||
transform: (node: Node) => Node // --
|
||||
ignoreSpaces: boolean
|
||||
isVoidTag?: (tag: string) => boolean // e.g. img, br, hr
|
||||
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
||||
getTextMode?: (tag: string, ns: Namespace) => TextModes
|
||||
delimiters?: [string, string] // ['{{', '}}']
|
||||
ignoreSpaces?: boolean
|
||||
|
||||
// Map to HTML entities. E.g., `{ "amp;": "&" }`
|
||||
// The full set is https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references
|
||||
namedCharacterReferences: { [name: string]: string | undefined }
|
||||
namedCharacterReferences?: { [name: string]: string | undefined }
|
||||
|
||||
onError: (type: ParserErrorTypes, loc: Position) => void
|
||||
onError?: (type: ParserErrorTypes, loc: Position) => void
|
||||
}
|
||||
|
||||
export const defaultParserOptions: Required<ParserOptions> = {
|
||||
delimiters: [`{{`, `}}`],
|
||||
ignoreSpaces: true,
|
||||
getNamespace: () => Namespaces.HTML,
|
||||
getTextMode: () => TextModes.DATA,
|
||||
isVoidTag: () => false,
|
||||
namedCharacterReferences: {},
|
||||
onError(code: ParserErrorTypes, loc: Position): void {
|
||||
const error: any = new SyntaxError(
|
||||
`${errorMessages[code]} (${loc.line}:${loc.column})`
|
||||
)
|
||||
error.code = code
|
||||
error.loc = loc
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const enum TextModes {
|
||||
// | Elements | Entities | End sign | Inside of
|
||||
DATA, // | ✔ | ✔ | End tags of ancestors |
|
||||
RCDATA, // | ✘ | ✔ | End tag of the parent | <textarea>
|
||||
// | Elements | Entities | End sign | Inside of
|
||||
DATA, // | ✔ | ✔ | End tags of ancestors |
|
||||
RCDATA, // | ✘ | ✔ | End tag of the parent | <textarea>
|
||||
RAWTEXT, // | ✘ | ✘ | End tag of the parent | <style>,<script>
|
||||
CDATA,
|
||||
ATTRIBUTE_VALUE
|
||||
}
|
||||
|
||||
interface ParserContext extends ParserOptions {
|
||||
interface ParserContext extends Required<ParserOptions> {
|
||||
readonly originalSource: string
|
||||
source: string
|
||||
offset: number
|
||||
@@ -48,7 +64,7 @@ interface ParserContext extends ParserOptions {
|
||||
maxCRNameLength: number
|
||||
}
|
||||
|
||||
export function parse(content: string, options: ParserOptions): RootNode {
|
||||
export function parse(content: string, options: ParserOptions = {}): RootNode {
|
||||
const context = createParserContext(content, options)
|
||||
const start = getCursor(context)
|
||||
|
||||
@@ -64,16 +80,17 @@ function createParserContext(
|
||||
options: ParserOptions
|
||||
): ParserContext {
|
||||
return {
|
||||
...defaultParserOptions,
|
||||
...options,
|
||||
column: 1,
|
||||
line: 1,
|
||||
offset: 0,
|
||||
originalSource: content,
|
||||
source: content,
|
||||
maxCRNameLength: Object.keys(options.namedCharacterReferences).reduce(
|
||||
(max, name) => Math.max(max, name.length),
|
||||
0
|
||||
)
|
||||
maxCRNameLength: Object.keys(
|
||||
options.namedCharacterReferences ||
|
||||
defaultParserOptions.namedCharacterReferences
|
||||
).reduce((max, name) => Math.max(max, name.length), 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { TextModes, ParserOptions } from './parser'
|
||||
import { ElementNode, Namespaces, Position, Node } from './ast'
|
||||
import { ParserErrorTypes, errorMessages } from './errorTypes'
|
||||
|
||||
export const parserOptionsMinimal: ParserOptions = {
|
||||
delimiters: [`{{`, `}}`],
|
||||
ignoreSpaces: true,
|
||||
|
||||
getNamespace(tag: string, parent: ElementNode | undefined): Namespaces {
|
||||
const ns = parent ? parent.ns : Namespaces.HTML
|
||||
if (ns === Namespaces.HTML) {
|
||||
if (tag === 'svg') {
|
||||
return Namespaces.SVG
|
||||
}
|
||||
if (tag === 'math') {
|
||||
return Namespaces.MATH_ML
|
||||
}
|
||||
}
|
||||
return ns
|
||||
},
|
||||
|
||||
getTextMode(tag: string, ns: Namespaces): TextModes {
|
||||
if (ns === Namespaces.HTML) {
|
||||
if (/^textarea$/i.test(tag)) {
|
||||
return TextModes.RCDATA
|
||||
}
|
||||
if (/^(?:style|script)$/i.test(tag)) {
|
||||
return TextModes.RAWTEXT
|
||||
}
|
||||
}
|
||||
return TextModes.DATA
|
||||
},
|
||||
|
||||
isVoidTag(tag: string): boolean {
|
||||
return /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(
|
||||
tag
|
||||
)
|
||||
},
|
||||
|
||||
namedCharacterReferences: {
|
||||
'gt;': '>',
|
||||
'lt;': '<',
|
||||
'amp;': '&',
|
||||
'apos;': "'",
|
||||
'quot;': '"'
|
||||
},
|
||||
|
||||
onError(code: ParserErrorTypes, loc: Position): void {
|
||||
const error: any = new SyntaxError(
|
||||
`${errorMessages[code]} (${loc.line}:${loc.column})`
|
||||
)
|
||||
error.code = code
|
||||
error.loc = loc
|
||||
throw error
|
||||
},
|
||||
|
||||
transform(node: Node): Node {
|
||||
return node
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user