|
"use strict"; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.parseAttributes = parseAttributes; |
|
const espree_1 = require("./espree"); |
|
const RE_IS_SPACE = /^\s$/u; |
|
class State { |
|
constructor(code, index) { |
|
this.code = code; |
|
this.index = index; |
|
} |
|
getCurr() { |
|
return this.code[this.index]; |
|
} |
|
skipSpaces() { |
|
while (this.currIsSpace()) { |
|
this.advance(); |
|
if (this.eof()) |
|
break; |
|
} |
|
} |
|
currIsSpace() { |
|
return RE_IS_SPACE.test(this.getCurr() || ""); |
|
} |
|
currIs(expect) { |
|
return this.code.startsWith(expect, this.index); |
|
} |
|
eof() { |
|
return this.index >= this.code.length; |
|
} |
|
eat(expect) { |
|
if (!this.currIs(expect)) { |
|
return null; |
|
} |
|
this.index += expect.length; |
|
return expect; |
|
} |
|
advance() { |
|
this.index++; |
|
return this.getCurr(); |
|
} |
|
} |
|
|
|
function parseAttributes(code, startIndex) { |
|
const attributes = []; |
|
const state = new State(code, startIndex); |
|
while (!state.eof()) { |
|
state.skipSpaces(); |
|
if (state.currIs(">") || state.currIs("/>") || state.eof()) |
|
break; |
|
attributes.push(parseAttribute(state)); |
|
} |
|
return { attributes, index: state.index }; |
|
} |
|
|
|
function parseAttribute(state) { |
|
const start = state.index; |
|
|
|
const key = parseAttributeKey(state); |
|
const keyEnd = state.index; |
|
state.skipSpaces(); |
|
if (!state.eat("=")) { |
|
return { |
|
type: "Attribute", |
|
name: key, |
|
value: true, |
|
start, |
|
end: keyEnd, |
|
metadata: null, |
|
parent: null, |
|
}; |
|
} |
|
state.skipSpaces(); |
|
if (state.eof()) { |
|
return { |
|
type: "Attribute", |
|
name: key, |
|
value: true, |
|
start, |
|
end: keyEnd, |
|
metadata: null, |
|
parent: null, |
|
}; |
|
} |
|
|
|
const value = parseAttributeValue(state); |
|
return { |
|
type: "Attribute", |
|
name: key, |
|
value: [value], |
|
start, |
|
end: state.index, |
|
metadata: null, |
|
parent: null, |
|
}; |
|
} |
|
|
|
function parseAttributeKey(state) { |
|
const start = state.index; |
|
while (state.advance()) { |
|
if (state.currIs("=") || |
|
state.currIs(">") || |
|
state.currIs("/>") || |
|
state.currIsSpace()) { |
|
break; |
|
} |
|
} |
|
const end = state.index; |
|
return state.code.slice(start, end); |
|
} |
|
|
|
function parseAttributeValue(state) { |
|
const start = state.index; |
|
const quote = state.eat('"') || state.eat("'"); |
|
const startBk = state.index; |
|
const expression = parseAttributeMustache(state); |
|
if (expression) { |
|
if (!quote || state.eat(quote)) { |
|
const end = state.index; |
|
return { |
|
type: "ExpressionTag", |
|
expression, |
|
start, |
|
end, |
|
metadata: null, |
|
parent: null, |
|
}; |
|
} |
|
} |
|
state.index = startBk; |
|
if (quote) { |
|
if (state.eof()) { |
|
return { |
|
type: "Text", |
|
data: quote, |
|
raw: quote, |
|
start, |
|
end: state.index, |
|
parent: null, |
|
}; |
|
} |
|
let c; |
|
while ((c = state.getCurr())) { |
|
state.advance(); |
|
if (c === quote) { |
|
const end = state.index; |
|
const data = state.code.slice(start + 1, end - 1); |
|
return { |
|
type: "Text", |
|
data, |
|
raw: data, |
|
start: start + 1, |
|
end: end - 1, |
|
parent: null, |
|
}; |
|
} |
|
} |
|
} |
|
else { |
|
while (state.advance()) { |
|
if (state.currIsSpace() || state.currIs(">") || state.currIs("/>")) { |
|
break; |
|
} |
|
} |
|
} |
|
const end = state.index; |
|
const data = state.code.slice(start, end); |
|
return { |
|
type: "Text", |
|
data, |
|
raw: data, |
|
start, |
|
end, |
|
parent: null, |
|
}; |
|
} |
|
|
|
function parseAttributeMustache(state) { |
|
if (!state.eat("{")) { |
|
return null; |
|
} |
|
|
|
const leadingComments = []; |
|
const startBk = state.index; |
|
state.skipSpaces(); |
|
let start = state.index; |
|
while (!state.eof()) { |
|
if (state.eat("//")) { |
|
leadingComments.push(parseInlineComment(state.index - 2)); |
|
state.skipSpaces(); |
|
start = state.index; |
|
continue; |
|
} |
|
if (state.eat("/*")) { |
|
leadingComments.push(parseBlockComment(state.index - 2)); |
|
state.skipSpaces(); |
|
start = state.index; |
|
continue; |
|
} |
|
const stringQuote = state.eat('"') || state.eat("'"); |
|
if (stringQuote) { |
|
skipString(stringQuote); |
|
state.skipSpaces(); |
|
continue; |
|
} |
|
const endCandidate = state.index; |
|
state.skipSpaces(); |
|
if (state.eat("}")) { |
|
const end = endCandidate; |
|
try { |
|
const espree = (0, espree_1.getEspree)(); |
|
const expression = espree.parse(state.code.slice(start, end), { |
|
ecmaVersion: espree.latestEcmaVersion, |
|
}).body[0].expression; |
|
delete expression.range; |
|
return Object.assign(Object.assign({}, expression), { leadingComments, |
|
start, |
|
end }); |
|
} |
|
catch (_a) { |
|
break; |
|
} |
|
} |
|
state.advance(); |
|
} |
|
state.index = startBk; |
|
return null; |
|
function parseInlineComment(tokenStart) { |
|
const valueStart = state.index; |
|
let valueEnd = null; |
|
while (!state.eof()) { |
|
if (state.eat("\n")) { |
|
valueEnd = state.index - 1; |
|
break; |
|
} |
|
state.advance(); |
|
} |
|
if (valueEnd == null) { |
|
valueEnd = state.index; |
|
} |
|
return { |
|
type: "Line", |
|
value: state.code.slice(valueStart, valueEnd), |
|
start: tokenStart, |
|
end: state.index, |
|
}; |
|
} |
|
function parseBlockComment(tokenStart) { |
|
const valueStart = state.index; |
|
let valueEnd = null; |
|
while (!state.eof()) { |
|
if (state.eat("*/")) { |
|
valueEnd = state.index - 2; |
|
break; |
|
} |
|
state.advance(); |
|
} |
|
if (valueEnd == null) { |
|
valueEnd = state.index; |
|
} |
|
return { |
|
type: "Block", |
|
value: state.code.slice(valueStart, valueEnd), |
|
start: tokenStart, |
|
end: state.index, |
|
}; |
|
} |
|
function skipString(stringQuote) { |
|
while (!state.eof()) { |
|
if (state.eat(stringQuote)) { |
|
break; |
|
} |
|
if (state.eat("\\")) { |
|
|
|
state.advance(); |
|
} |
|
state.advance(); |
|
} |
|
} |
|
} |
|
|