UNPKG

eslint-mdx

Version:
387 lines 20.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getRemarkProcessor = exports.processorCache = void 0; const tslib_1 = require("tslib"); const node_path_1 = tslib_1.__importDefault(require("node:path")); const node_url_1 = require("node:url"); const synckit_1 = require("synckit"); const assert_1 = require("uvu/assert"); const helpers_1 = require("./helpers"); const tokens_1 = require("./tokens"); let config; let acorn; let acornJsx; let acornParser; let tokTypes; let jsxTokTypes; let tt; let TokenTranslator; exports.processorCache = new Map(); const getRemarkConfig = (searchFrom) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { if (!config) { const { Configuration } = yield (0, helpers_1.loadEsmModule)('unified-engine'); config = new Configuration({ cwd: process.cwd(), packageField: 'remarkConfig', pluginPrefix: 'remark', rcName: '.remarkrc', detectConfig: true, }); } return new Promise((resolve, reject) => config.load(searchFrom, (error, result) => { error ? reject(error) : resolve(result); })); }); const getRemarkMdxOptions = (tokens) => ({ acorn: acornParser, acornOptions: { ecmaVersion: 'latest', sourceType: 'module', locations: true, ranges: true, onToken: tokens, }, }); const sharedTokens = []; const getRemarkProcessor = (searchFrom, isMdx, ignoreRemarkConfig) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const initCacheKey = `${String(isMdx)}-${searchFrom}`; let cachedProcessor = exports.processorCache.get(initCacheKey); if (cachedProcessor) { return cachedProcessor; } const result = ignoreRemarkConfig ? null : yield getRemarkConfig(searchFrom); const cacheKey = (result === null || result === void 0 ? void 0 : result.filePath) ? `${String(isMdx)}-${result.filePath}` : String(isMdx); cachedProcessor = exports.processorCache.get(cacheKey); if (cachedProcessor) { return cachedProcessor; } const { unified } = yield (0, helpers_1.loadEsmModule)('unified'); const remarkParse = (yield (0, helpers_1.loadEsmModule)('remark-parse')).default; const remarkStringify = (yield (0, helpers_1.loadEsmModule)('remark-stringify')).default; const remarkMdx = (yield (0, helpers_1.loadEsmModule)('remark-mdx')).default; const remarkProcessor = unified().use(remarkParse).freeze(); if (result === null || result === void 0 ? void 0 : result.filePath) { const { plugins, settings } = result; if (plugins.length > 0) { try { plugins.push([ (yield (0, helpers_1.loadEsmModule)('remark-lint-file-extension')).default, false, ]); } catch (_a) { } } const initProcessor = remarkProcessor() .use({ settings }) .use(remarkStringify); if (isMdx) { initProcessor.use(remarkMdx, getRemarkMdxOptions(sharedTokens)); } cachedProcessor = plugins .reduce((processor, plugin) => processor.use(...plugin), initProcessor) .freeze(); } else { const initProcessor = remarkProcessor().use(remarkStringify); if (isMdx) { initProcessor.use(remarkMdx, getRemarkMdxOptions(sharedTokens)); } cachedProcessor = initProcessor.freeze(); } exports.processorCache .set(initCacheKey, cachedProcessor) .set(cacheKey, cachedProcessor); return cachedProcessor; }); exports.getRemarkProcessor = getRemarkProcessor; function isExpressionStatement(statement) { (0, assert_1.ok)(!statement || statement.type === 'ExpressionStatement'); } (0, synckit_1.runAsWorker)(({ fileOptions, physicalFilename, isMdx, process, ignoreRemarkConfig, }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { sharedTokens.length = 0; if (!acorn) { acorn = yield (0, helpers_1.loadEsmModule)('acorn'); acornJsx = yield (0, helpers_1.loadEsmModule)('acorn-jsx'); acornParser = acorn.Parser.extend(acornJsx.default()); } const processor = yield (0, exports.getRemarkProcessor)(physicalFilename, isMdx, ignoreRemarkConfig); if (process) { const { VFile } = yield (0, helpers_1.loadEsmModule)('vfile'); const file = new VFile(fileOptions); try { yield processor.process(file); } catch (err) { const error = err; if (!file.messages.includes(error)) { file.message(error).fatal = true; } } return { messages: file.messages.map(message => (0, synckit_1.extractProperties)(message)), content: file.toString(), }; } if (!tokTypes) { tokTypes = acorn.tokTypes; } if (!jsxTokTypes) { jsxTokTypes = acornJsx.default({ allowNamespacedObjects: true, })(acorn.Parser).acornJsx.tokTypes; } if (!TokenTranslator) { TokenTranslator = (yield (0, helpers_1.loadEsmModule)((0, node_url_1.pathToFileURL)(node_path_1.default.resolve(require.resolve('espree/package.json'), '../lib/token-translator.js')))).default; } if (!tt) { tt = Object.assign(Object.assign({}, tokTypes), jsxTokTypes); } const text = fileOptions.value; const tokenTranslator = new TokenTranslator(tt, text); const root = processor.parse(fileOptions); const body = []; const comments = []; const tokens = []; const { visit } = yield (0, helpers_1.loadEsmModule)('unist-util-visit'); const processed = new WeakSet(); if (isMdx) { const prevCharOffset = (0, helpers_1.prevCharOffsetFactory)(text); const nextCharOffset = (0, helpers_1.nextCharOffsetFactory)(text); const normalizeNode = (start, end) => (Object.assign(Object.assign({}, (0, helpers_1.normalizePosition)({ start: { offset: start }, end: { offset: end }, text, })), { raw: text.slice(start, end) })); const handleJsxName = (nodeName, start) => { const name = nodeName.trim(); const nameIndex = nodeName.indexOf(name); const colonIndex = nodeName.indexOf(':'); if (colonIndex !== -1) { const [fullNamespace, fullName] = nodeName.split(':'); return Object.assign(Object.assign({}, normalizeNode(start + nameIndex, start + nameIndex + name.length)), { type: 'JSXNamespacedName', namespace: handleJsxName(fullNamespace, start), name: handleJsxName(fullName, start + colonIndex + 1) }); } const lastPointIndex = nodeName.lastIndexOf('.'); if (lastPointIndex === -1) { return Object.assign(Object.assign({}, normalizeNode(start + nameIndex, start + nameIndex + name.length)), { type: 'JSXIdentifier', name }); } const objectName = nodeName.slice(0, lastPointIndex); const propertyName = nodeName.slice(lastPointIndex + 1); return Object.assign(Object.assign({}, normalizeNode(start + nameIndex, start + nameIndex + name.length)), { type: 'JSXMemberExpression', object: handleJsxName(objectName, start), property: handleJsxName(propertyName, start + lastPointIndex + 1) }); }; visit(root, node => { if (processed.has(node) || (node.type !== 'mdxFlowExpression' && node.type !== 'mdxJsxFlowElement' && node.type !== 'mdxJsxTextElement' && node.type !== 'mdxTextExpression' && node.type !== 'mdxjsEsm')) { return; } processed.add(node); function handleChildren(node) { return 'children' in node ? node.children.reduce((acc, child) => { processed.add(child); if (child.data && 'estree' in child.data && child.data.estree) { const { estree } = child.data; (0, assert_1.ok)(estree.body.length <= 1); const statement = estree.body[0]; isExpressionStatement(statement); const expression = statement === null || statement === void 0 ? void 0 : statement.expression; if (child.type === 'mdxTextExpression') { const { start: { offset: start }, end: { offset: end }, } = node.position; const expressionContainer = Object.assign(Object.assign({}, normalizeNode(start, end)), { type: 'JSXExpressionContainer', expression: expression || Object.assign(Object.assign({}, normalizeNode(start + 1, end - 1)), { type: 'JSXEmptyExpression' }) }); acc.push(expressionContainer); } else if (expression) { acc.push(expression); } comments.push(...estree.comments); } else { const expression = handleNode(child); if (Array.isArray(expression)) { acc.push(...expression); } else if (expression) { acc.push(expression); } } return acc; }, []) : []; } function handleNode(node) { if (node.type === 'paragraph') { return handleChildren(node); } const { start: { offset: start }, end: { offset: end }, } = node.position; if (node.type === 'code') { const { lang, meta, value } = node; const mdxJsxCode = Object.assign(Object.assign({}, normalizeNode(start, end)), { type: 'MDXCode', lang, meta, value }); return mdxJsxCode; } if (node.type === 'heading') { const { depth } = node; const mdxJsxHeading = Object.assign(Object.assign({}, normalizeNode(start, end)), { type: 'MDXHeading', depth, children: handleChildren(node) }); return mdxJsxHeading; } if (node.type === 'text') { const jsxText = Object.assign(Object.assign({}, normalizeNode(start, end)), { type: 'JSXText', value: node.value }); return jsxText; } if (node.type !== 'mdxJsxTextElement' && node.type !== 'mdxJsxFlowElement') { return; } const children = handleChildren(node); const nodePos = node.position; const nodeStart = nodePos.start.offset; const nodeEnd = nodePos.end.offset; const lastCharOffset = prevCharOffset(nodeEnd - 2); let expression; if ('name' in node && node.name) { const nodeNameLength = node.name.length; const nodeNameStart = nextCharOffset(nodeStart + 1); const selfClosing = text[lastCharOffset] === '/'; let lastAttrOffset = nodeNameStart + nodeNameLength - 1; let closingElement = null; if (!selfClosing) { const prevOffset = prevCharOffset(lastCharOffset); const slashOffset = prevCharOffset(prevOffset - nodeNameLength); (0, assert_1.ok)(text[slashOffset] === '/', `expect \`${text[slashOffset]}\` to be \`/\`, the node is ${node.name}`); const tagStartOffset = prevCharOffset(slashOffset - 1); (0, assert_1.ok)(text[tagStartOffset] === '<'); closingElement = Object.assign(Object.assign({}, normalizeNode(tagStartOffset, nodeEnd)), { type: 'JSXClosingElement', name: handleJsxName(node.name, prevOffset + 1 - nodeNameLength) }); } const jsxEl = Object.assign(Object.assign({}, normalizeNode(nodeStart, nodeEnd)), { type: 'JSXElement', openingElement: { type: 'JSXOpeningElement', name: handleJsxName(node.name, nodeNameStart), attributes: node.attributes.map(attr => { if (attr.type === 'mdxJsxExpressionAttribute') { (0, assert_1.ok)(attr.data); (0, assert_1.ok)(attr.data.estree); (0, assert_1.ok)(attr.data.estree.range); let [attrValStart, attrValEnd] = attr.data.estree.range; attrValStart = prevCharOffset(attrValStart - 1); attrValEnd = nextCharOffset(attrValEnd); (0, assert_1.ok)(text[attrValStart] === '{'); (0, assert_1.ok)(text[attrValEnd] === '}'); lastAttrOffset = attrValEnd; return Object.assign(Object.assign({}, normalizeNode(attrValStart, attrValEnd + 1)), { type: 'JSXSpreadAttribute', argument: attr.data.estree.body[0] .expression.properties[0].argument }); } const attrStart = nextCharOffset(lastAttrOffset + 1); (0, assert_1.ok)(attrStart != null); const attrName = attr.name; const attrNameLength = attrName.length; const attrValue = attr.value; lastAttrOffset = attrStart + attrNameLength; const attrNamePos = normalizeNode(attrStart, lastAttrOffset); if (attrValue == null) { return Object.assign(Object.assign({}, normalizeNode(attrStart, lastAttrOffset)), { type: 'JSXAttribute', name: Object.assign(Object.assign({}, attrNamePos), { type: 'JSXIdentifier', name: attrName }), value: null }); } const attrEqualOffset = nextCharOffset(attrStart + attrNameLength); (0, assert_1.ok)(text[attrEqualOffset] === '='); let attrValuePos; if (typeof attrValue === 'string') { const attrQuoteOffset = nextCharOffset(attrEqualOffset + 1); const attrQuote = text[attrQuoteOffset]; (0, assert_1.ok)(attrQuote === '"' || attrQuote === "'"); lastAttrOffset = nextCharOffset(attrQuoteOffset + attrValue.length + 1); (0, assert_1.ok)(text[lastAttrOffset] === attrQuote); attrValuePos = normalizeNode(attrQuoteOffset, lastAttrOffset + 1); } else { const data = attrValue.data; let [attrValStart, attrValEnd] = data.estree.range; attrValStart = prevCharOffset(attrValStart - 1); attrValEnd = nextCharOffset(attrValEnd); (0, assert_1.ok)(text[attrValStart] === '{'); (0, assert_1.ok)(text[attrValEnd] === '}'); lastAttrOffset = attrValEnd; attrValuePos = normalizeNode(attrValStart, attrValEnd + 1); } return Object.assign(Object.assign({}, normalizeNode(attrStart, lastAttrOffset + 1)), { type: 'JSXAttribute', name: Object.assign(Object.assign({}, attrNamePos), { type: 'JSXIdentifier', name: attrName }), value: typeof attr.value === 'string' ? Object.assign(Object.assign({}, attrValuePos), { type: 'Literal', value: attr.value }) : Object.assign(Object.assign({}, attrValuePos), { type: 'JSXExpressionContainer', expression: attr.value.data.estree .body[0].expression }) }); }), selfClosing, }, closingElement, children }); let nextOffset = nextCharOffset(lastAttrOffset + 1); let nextChar = text[nextOffset]; const expectedNextChar = selfClosing ? '/' : '>'; if (nextChar !== expectedNextChar) { nextOffset = nextCharOffset(lastAttrOffset); nextChar = text[nextOffset]; } (0, assert_1.ok)(nextChar === expectedNextChar, `\`nextChar\` must be '${expectedNextChar}' but actually is '${nextChar}'`); Object.assign(jsxEl.openingElement, normalizeNode(nodeStart, selfClosing ? nodeEnd : nextOffset + 1)); expression = jsxEl; } else { const openEndOffset = nextCharOffset(nodeStart + 1); const openPos = normalizeNode(nodeStart, openEndOffset); const closeStartOffset = prevCharOffset(lastCharOffset - 1); const jsxFrg = Object.assign(Object.assign({}, openPos), { type: 'JSXFragment', openingFragment: Object.assign(Object.assign({}, openPos), { type: 'JSXOpeningFragment' }), closingFragment: Object.assign(Object.assign({}, normalizeNode(closeStartOffset, nodeEnd)), { type: 'JSXClosingFragment' }), children }); expression = jsxFrg; } return expression; } const expression = handleNode(node); if (expression) { body.push(Object.assign(Object.assign({}, (0, helpers_1.normalizePosition)(node.position)), { type: 'ExpressionStatement', expression: expression })); } const estree = ((node.data && 'estree' in node.data && node.data.estree) || { body: [], comments: [], }); body.push(...estree.body); comments.push(...estree.comments); }); } const { visit: visitEstree } = yield (0, helpers_1.loadEsmModule)('estree-util-visit'); visitEstree({ type: 'Program', sourceType: 'module', body, }, node => { if (node.type !== 'TemplateElement') { return; } const templateElement = node; const startOffset = -1; const endOffset = templateElement.tail ? 1 : 2; templateElement.start += startOffset; templateElement.end += endOffset; if (templateElement.range) { templateElement.range[0] += startOffset; templateElement.range[1] += endOffset; } if (templateElement.loc) { templateElement.loc.start.column += startOffset; templateElement.loc.end.column += endOffset; } }); for (const token of (0, tokens_1.restoreTokens)(text, root, sharedTokens, tt, visit)) { tokenTranslator.onToken(token, { ecmaVersion: 'latest', tokens: tokens, }); } return { root, body, comments, tokens, }; })); //# sourceMappingURL=worker.js.map