eslint-mdx
Version:
ESLint Parser for MDX
131 lines • 6.15 kB
JavaScript
import { ok as assert } from 'uvu/assert';
import { getPositionAtFactory, nextCharOffsetFactory, prevCharOffsetFactory, } from './helpers.js';
export const restoreTokens = (text, root, tokens, tt, visit) => {
tokens = [...tokens];
const getPositionAt = getPositionAtFactory(text);
const prevCharOffset = prevCharOffsetFactory(text);
const nextCharOffset = nextCharOffsetFactory(text);
const newToken = (type, start, end, value) => ({
type,
value,
start,
end,
loc: {
start: getPositionAt(start),
end: getPositionAt(end),
},
range: [start, end],
});
visit(root, node => {
if (node.type !== 'mdxFlowExpression' &&
node.type !== 'mdxJsxFlowElement' &&
node.type !== 'mdxJsxTextElement' &&
node.type !== 'text') {
return;
}
const nodePos = node.position;
assert(nodePos);
const nodeStart = nodePos.start.offset;
const nodeEnd = nodePos.end.offset;
const lastCharOffset = prevCharOffset(nodeEnd - 2);
assert(nodeStart != null);
assert(nodeEnd != null);
if (node.type === 'mdxFlowExpression') {
tokens.push(newToken(tt.braceL, nodeStart, nodeStart + 1), newToken(tt.braceR, nodeEnd - 1, nodeEnd));
return;
}
if (node.type === 'text') {
tokens.push(newToken(tt.jsxText, nodeStart, nodeEnd, node.value));
return;
}
tokens.push(newToken(tt.jsxTagStart, nodeStart, nodeStart + 1));
const nodeName = node.name;
const nodeNameLength = nodeName?.length ?? 0;
const selfClosing = text[lastCharOffset] === '/';
let nodeNameStart = nodeStart + 1;
if (nodeName) {
nodeNameStart = nextCharOffset(nodeStart + 1);
assert(nodeNameStart);
tokens.push(newToken(tt.jsxName, nodeNameStart, nodeNameStart + nodeNameLength, nodeName));
}
let lastAttrOffset = nodeNameStart + nodeNameLength - 1;
for (const attr of node.attributes) {
if (attr.type === 'mdxJsxExpressionAttribute') {
assert(attr.data);
assert(attr.data.estree);
assert(attr.data.estree.range);
let [attrValStart, attrValEnd] = attr.data.estree.range;
attrValStart = prevCharOffset(attrValStart - 1);
attrValEnd = nextCharOffset(attrValEnd);
assert(text[attrValStart] === '{');
assert(text[attrValEnd] === '}');
lastAttrOffset = attrValEnd;
tokens.push(newToken(tt.braceL, attrValStart, attrValStart + 1), newToken(tt.braceR, attrValEnd, attrValEnd + 1));
continue;
}
const attrStart = nextCharOffset(lastAttrOffset + 1);
assert(attrStart != null);
const attrName = attr.name;
const attrNameLength = attrName.length;
tokens.push(newToken(tt.jsxName, attrStart, attrStart + attrNameLength, attrName));
const attrValue = attr.value;
if (attrValue == null) {
lastAttrOffset = attrStart + attrNameLength;
continue;
}
const attrEqualOffset = nextCharOffset(attrStart + attrNameLength);
assert(text[attrEqualOffset] === '=');
tokens.push(newToken(tt.eq, attrEqualOffset, attrEqualOffset + 1, '='));
if (typeof attrValue === 'object') {
const data = attrValue.data;
let [attrValStart, attrValEnd] = data.estree.range;
attrValStart = prevCharOffset(attrValStart - 1);
attrValEnd = nextCharOffset(attrValEnd);
assert(text[attrValStart] === '{');
assert(text[attrValEnd] === '}');
lastAttrOffset = attrValEnd;
tokens.push(newToken(tt.braceL, attrValStart, attrValStart + 1), newToken(tt.braceR, attrValEnd, attrValEnd + 1));
continue;
}
const attrQuoteOffset = nextCharOffset(attrEqualOffset + 1);
const attrQuote = text[attrQuoteOffset];
assert(attrQuote === '"' || attrQuote === "'");
tokens.push(newToken(tt.string, attrQuoteOffset, attrQuoteOffset + attrValue.length + 2, attrValue));
lastAttrOffset = nextCharOffset(attrQuoteOffset + attrValue.length + 1);
assert(text[lastAttrOffset] === attrQuote);
}
let nextOffset = nextCharOffset(lastAttrOffset + 1);
let nextChar = text[nextOffset];
const expectedNextChar = selfClosing ? '/' : '>';
if (nextChar !== expectedNextChar) {
nextOffset = nextCharOffset(lastAttrOffset);
nextChar = text[nextOffset];
}
if (selfClosing) {
tokens.push(newToken(tt.slash, nextOffset, nextOffset + 1, '/'));
}
else {
assert(nextChar === '>', `\`nextChar\` must be '>' but actually is '${nextChar}'`);
const prevOffset = prevCharOffset(nodeEnd - 2);
if (nodeName) {
tokens.push(newToken(tt.jsxName, prevOffset + 1 - nodeNameLength, prevOffset + 1, nodeName));
}
const slashOffset = prevCharOffset(prevOffset - nodeNameLength);
assert(text[slashOffset] === '/');
tokens.push(newToken(tt.slash, slashOffset, slashOffset + 1, '/'));
const tagStartOffset = prevCharOffset(slashOffset - 1);
assert(text[tagStartOffset] === '<');
tokens.push(newToken(tt.jsxTagStart, tagStartOffset, tagStartOffset + 1));
}
tokens.push(newToken(tt.jsxTagEnd, nodeEnd - 1, nodeEnd));
});
tokens.push(newToken(tt.eof, text.length, text.length));
return tokens
.filter(t => !(t.start === t.end &&
(t.type === tt.braceL ||
t.type === tt.braceR ||
t.type === tt.parenL ||
t.type === tt.parenR)))
.sort((a, b) => a.start - b.start);
};
//# sourceMappingURL=tokens.js.map