wip: check duplicated attributes

This commit is contained in:
Evan You 2023-11-15 19:36:05 +08:00
parent ec33e61e87
commit 70edd1c61e
2 changed files with 39 additions and 31 deletions

View File

@ -143,6 +143,7 @@ export interface Callbacks {
onattribentity(codepoint: number): void onattribentity(codepoint: number): void
onattribend(quote: QuoteType, endIndex: number): void onattribend(quote: QuoteType, endIndex: number): void
onattribname(start: number, endIndex: number): void onattribname(start: number, endIndex: number): void
onattribnameend(endIndex: number): void
ondirname(start: number, endIndex: number): void ondirname(start: number, endIndex: number): void
ondirarg(start: number, endIndex: number): void ondirarg(start: number, endIndex: number): void
@ -218,7 +219,7 @@ export default class Tokenizer {
* processed index, so all the newlines up to this index should have been * processed index, so all the newlines up to this index should have been
* recorded. * recorded.
*/ */
public getPositionForIndex(index: number): Position { public getPos(index: number): Position {
let line = 1 let line = 1
let column = index + 1 let column = index + 1
for (let i = this.newlines.length - 1; i >= 0; i--) { for (let i = this.newlines.length - 1; i >= 0; i--) {
@ -513,17 +514,13 @@ export default class Tokenizer {
private stateInAttributeName(c: number): void { private stateInAttributeName(c: number): void {
if (c === CharCodes.Eq || isEndOfTagSection(c)) { if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.onattribname(this.sectionStart, this.index) this.cbs.onattribname(this.sectionStart, this.index)
this.sectionStart = this.index this.handleAttributeNameEnd(c)
this.state = State.AfterAttributeName
this.stateAfterAttributeName(c)
} }
} }
private stateInDirectiveName(c: number): void { private stateInDirectiveName(c: number): void {
if (c === CharCodes.Eq || isEndOfTagSection(c)) { if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.ondirname(this.sectionStart, this.index) this.cbs.ondirname(this.sectionStart, this.index)
this.sectionStart = this.index this.handleAttributeNameEnd(c)
this.state = State.AfterAttributeName
this.stateAfterAttributeName(c)
} else if (c === CharCodes.Colon) { } else if (c === CharCodes.Colon) {
this.cbs.ondirname(this.sectionStart, this.index) this.cbs.ondirname(this.sectionStart, this.index)
this.state = State.InDirectiveArg this.state = State.InDirectiveArg
@ -537,9 +534,7 @@ export default class Tokenizer {
private stateInDirectiveArg(c: number): void { private stateInDirectiveArg(c: number): void {
if (c === CharCodes.Eq || isEndOfTagSection(c)) { if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.ondirarg(this.sectionStart, this.index) this.cbs.ondirarg(this.sectionStart, this.index)
this.sectionStart = this.index this.handleAttributeNameEnd(c)
this.state = State.AfterAttributeName
this.stateAfterAttributeName(c)
} else if (c === CharCodes.LeftSqaure) { } else if (c === CharCodes.LeftSqaure) {
this.state = State.InDirectiveDynamicArg this.state = State.InDirectiveDynamicArg
} else if (c === CharCodes.Dot) { } else if (c === CharCodes.Dot) {
@ -558,14 +553,18 @@ export default class Tokenizer {
private stateInDirectiveModifier(c: number): void { private stateInDirectiveModifier(c: number): void {
if (c === CharCodes.Eq || isEndOfTagSection(c)) { if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.ondirmodifier(this.sectionStart, this.index) this.cbs.ondirmodifier(this.sectionStart, this.index)
this.sectionStart = this.index this.handleAttributeNameEnd(c)
this.state = State.AfterAttributeName
this.stateAfterAttributeName(c)
} else if (c === CharCodes.Dot) { } else if (c === CharCodes.Dot) {
this.cbs.ondirmodifier(this.sectionStart, this.index) this.cbs.ondirmodifier(this.sectionStart, this.index)
this.sectionStart = this.index + 1 this.sectionStart = this.index + 1
} }
} }
private handleAttributeNameEnd(c: number): void {
this.sectionStart = this.index
this.state = State.AfterAttributeName
this.cbs.onattribnameend(this.index)
this.stateAfterAttributeName(c)
}
private stateAfterAttributeName(c: number): void { private stateAfterAttributeName(c: number): void {
if (c === CharCodes.Eq) { if (c === CharCodes.Eq) {
this.state = State.BeforeAttributeValue this.state = State.BeforeAttributeValue

View File

@ -116,7 +116,6 @@ const tokenizer = new Tokenizer(
}, },
ondirname(start, end) { ondirname(start, end) {
// console.log('name ' + getSlice(start, end))
const raw = getSlice(start, end) const raw = getSlice(start, end)
const name = const name =
raw === '.' || raw === ':' raw === '.' || raw === ':'
@ -135,8 +134,8 @@ const tokenizer = new Tokenizer(
loc: getLoc(start) loc: getLoc(start)
} }
}, },
ondirarg(start, end) { ondirarg(start, end) {
// console.log('arg ' + getSlice(start, end))
const arg = getSlice(start, end) const arg = getSlice(start, end)
const isStatic = arg[0] !== `[` const isStatic = arg[0] !== `[`
;(currentProp as DirectiveNode).arg = { ;(currentProp as DirectiveNode).arg = {
@ -158,19 +157,28 @@ const tokenizer = new Tokenizer(
if (currentAttrStartIndex < 0) currentAttrStartIndex = start if (currentAttrStartIndex < 0) currentAttrStartIndex = start
currentAttrEndIndex = end currentAttrEndIndex = end
}, },
onattribentity(codepoint) { onattribentity(codepoint) {
currentAttrValue += fromCodePoint(codepoint) currentAttrValue += fromCodePoint(codepoint)
}, },
onattribnameend(end) {
// check duplicate attrs
const start = currentProp!.loc.start.offset
const name = getSlice(start, end)
if (currentAttrs.has(name)) {
currentProp = null
// TODO emit error DUPLICATE_ATTRIBUTE
throw new Error(`duplicate attr ${name}`)
} else {
currentAttrs.add(name)
}
},
onattribend(quote, end) { onattribend(quote, end) {
// TODO check duplicate if (currentElement && currentProp) {
// if (currentAttrs.has(name)) {
// // emit error DUPLICATE_ATTRIBUTE
// } else {
// currentAttrs.add(name)
// }
if (currentElement) {
if (currentAttrValue) { if (currentAttrValue) {
if (currentProp!.type === NodeTypes.ATTRIBUTE) { if (currentProp.type === NodeTypes.ATTRIBUTE) {
// assign value // assign value
currentProp!.value = { currentProp!.value = {
type: NodeTypes.TEXT, type: NodeTypes.TEXT,
@ -182,7 +190,7 @@ const tokenizer = new Tokenizer(
} }
} else { } else {
// directive // directive
currentProp!.exp = { currentProp.exp = {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: currentAttrValue, content: currentAttrValue,
isStatic: false, isStatic: false,
@ -194,7 +202,7 @@ const tokenizer = new Tokenizer(
} }
} }
} }
currentProp!.loc.end = tokenizer.getPositionForIndex(end) currentProp.loc.end = tokenizer.getPos(end)
currentElement.props.push(currentProp!) currentElement.props.push(currentProp!)
} }
currentAttrValue = '' currentAttrValue = ''
@ -232,7 +240,7 @@ function emitOpenTag(name: string, start: number) {
props: [], props: [],
children: [], children: [],
loc: { loc: {
start: tokenizer.getPositionForIndex(start - 1), start: tokenizer.getPos(start - 1),
// @ts-expect-error to be attached on tag close // @ts-expect-error to be attached on tag close
end: undefined, end: undefined,
source: '' source: ''
@ -273,8 +281,8 @@ function onText(content: string, start: number, end: number) {
type: NodeTypes.TEXT, type: NodeTypes.TEXT,
content, content,
loc: { loc: {
start: tokenizer.getPositionForIndex(start), start: tokenizer.getPos(start),
end: tokenizer.getPositionForIndex(end), end: tokenizer.getPos(end),
source: content source: content
} }
}) })
@ -287,7 +295,7 @@ function onCloseTag(el: ElementNode, end: number) {
while (currentInput.charCodeAt(end + offset) !== CharCodes.Gt) { while (currentInput.charCodeAt(end + offset) !== CharCodes.Gt) {
offset++ offset++
} }
el.loc.end = tokenizer.getPositionForIndex(end + offset + 1) el.loc.end = tokenizer.getPos(end + offset + 1)
// whitepsace management // whitepsace management
el.children = condenseWhitespace(el.children) el.children = condenseWhitespace(el.children)
} }
@ -388,9 +396,9 @@ function getParent() {
function getLoc(start: number, end?: number): SourceLocation { function getLoc(start: number, end?: number): SourceLocation {
return { return {
start: tokenizer.getPositionForIndex(start), start: tokenizer.getPos(start),
// @ts-expect-error allow late attachment // @ts-expect-error allow late attachment
end: end && tokenizer.getPositionForIndex(end) end: end && tokenizer.getPos(end)
} }
} }
@ -411,6 +419,7 @@ export function baseParse(input: string, options?: ParserOptions): RootNode {
currentOptions = extend({}, defaultParserOptions, options) currentOptions = extend({}, defaultParserOptions, options)
const root = (currentRoot = createRoot([])) const root = (currentRoot = createRoot([]))
tokenizer.parse(currentInput) tokenizer.parse(currentInput)
root.loc.end = tokenizer.getPos(input.length)
root.children = condenseWhitespace(root.children) root.children = condenseWhitespace(root.children)
return root return root
} }