UNPKG

vue-eslint-parser

Version:

The ESLint custom parser for `.vue` files.

1,398 lines (1,376 loc) 286 kB
/** * @author Toru Nagashima <https://github.com/mysticatea> * See LICENSE file in root directory for full license. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var path = require('path'); var Evk = require('eslint-visitor-keys'); var sortedLastIndex = require('lodash/sortedLastIndex'); var assert = require('assert'); var last = require('lodash/last'); var findLastIndex = require('lodash/findLastIndex'); var debugFactory = require('debug'); var first = require('lodash/first'); var sortedIndexBy = require('lodash/sortedIndexBy'); var escope = require('eslint-scope'); var semver = require('semver'); var module$1 = require('module'); var dependencyEspree = require('espree'); var sortedLastIndexBy = require('lodash/sortedLastIndexBy'); var EventEmitter = require('events'); var esquery = require('esquery'); var union = require('lodash/union'); var intersection = require('lodash/intersection'); var memoize = require('lodash/memoize'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var path__namespace = /*#__PURE__*/_interopNamespace(path); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var Evk__namespace = /*#__PURE__*/_interopNamespace(Evk); var sortedLastIndex__default = /*#__PURE__*/_interopDefaultLegacy(sortedLastIndex); var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert); var last__default = /*#__PURE__*/_interopDefaultLegacy(last); var findLastIndex__default = /*#__PURE__*/_interopDefaultLegacy(findLastIndex); var debugFactory__default = /*#__PURE__*/_interopDefaultLegacy(debugFactory); var first__default = /*#__PURE__*/_interopDefaultLegacy(first); var sortedIndexBy__default = /*#__PURE__*/_interopDefaultLegacy(sortedIndexBy); var escope__namespace = /*#__PURE__*/_interopNamespace(escope); var dependencyEspree__namespace = /*#__PURE__*/_interopNamespace(dependencyEspree); var sortedLastIndexBy__default = /*#__PURE__*/_interopDefaultLegacy(sortedLastIndexBy); var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter); var esquery__default = /*#__PURE__*/_interopDefaultLegacy(esquery); var union__default = /*#__PURE__*/_interopDefaultLegacy(union); var intersection__default = /*#__PURE__*/_interopDefaultLegacy(intersection); var memoize__default = /*#__PURE__*/_interopDefaultLegacy(memoize); function isAcornStyleParseError(x) { return (typeof x.message === "string" && typeof x.pos === "number" && typeof x.loc === "object" && x.loc !== null && typeof x.loc.line === "number" && typeof x.loc.column === "number"); } function isTSError(x) { return (!(x instanceof ParseError) && typeof x.message === "string" && typeof x.index === "number" && typeof x.lineNumber === "number" && typeof x.column === "number" && x.name === "TSError"); } class ParseError extends SyntaxError { code; index; lineNumber; column; static fromCode(code, offset, line, column) { return new ParseError(code, code, offset, line, column); } static normalize(x) { if (isTSError(x)) { return new ParseError(x.message, undefined, x.index, x.lineNumber, x.column); } if (ParseError.isParseError(x)) { return x; } if (isAcornStyleParseError(x)) { return new ParseError(x.message, undefined, x.pos, x.loc.line, x.loc.column); } return null; } constructor(message, code, offset, line, column) { super(message); this.code = code; this.index = offset; this.lineNumber = line; this.column = column; } static isParseError(x) { return (x instanceof ParseError || (typeof x.message === "string" && typeof x.index === "number" && typeof x.lineNumber === "number" && typeof x.column === "number")); } } const NS = Object.freeze({ HTML: "http://www.w3.org/1999/xhtml", MathML: "http://www.w3.org/1998/Math/MathML", SVG: "http://www.w3.org/2000/svg", XLink: "http://www.w3.org/1999/xlink", XML: "http://www.w3.org/XML/1998/namespace", XMLNS: "http://www.w3.org/2000/xmlns/", }); const KEYS = Evk__namespace.unionWith({ VAttribute: ["key", "value"], VDirectiveKey: ["name", "argument", "modifiers"], VDocumentFragment: ["children"], VElement: ["startTag", "children", "endTag"], VEndTag: [], VExpressionContainer: ["expression"], VFilter: ["callee", "arguments"], VFilterSequenceExpression: ["expression", "filters"], VForExpression: ["left", "right"], VIdentifier: [], VLiteral: [], VOnExpression: ["body"], VSlotScopeExpression: ["params"], VStartTag: ["attributes"], VText: [], VGenericExpression: ["params"], }); function fallbackKeysFilter(key) { let value = null; return (key !== "comments" && key !== "leadingComments" && key !== "loc" && key !== "parent" && key !== "range" && key !== "tokens" && key !== "trailingComments" && (value = this[key]) !== null && typeof value === "object" && (typeof value.type === "string" || Array.isArray(value))); } function getFallbackKeys(node) { return Object.keys(node).filter(fallbackKeysFilter, node); } function isNode(x) { return x !== null && typeof x === "object" && typeof x.type === "string"; } function traverse(node, parent, visitor) { let i = 0; let j = 0; visitor.enterNode(node, parent); const keys = (visitor.visitorKeys ?? KEYS)[node.type] ?? getFallbackKeys(node); for (i = 0; i < keys.length; ++i) { const child = node[keys[i]]; if (Array.isArray(child)) { for (j = 0; j < child.length; ++j) { if (isNode(child[j])) { traverse(child[j], node, visitor); } } } else if (isNode(child)) { traverse(child, node, visitor); } } visitor.leaveNode(node, parent); } function traverseNodes(node, visitor) { traverse(node, null, visitor); } var index = /*#__PURE__*/Object.freeze({ __proto__: null, ParseError: ParseError, NS: NS, KEYS: KEYS, traverseNodes: traverseNodes, getFallbackKeys: getFallbackKeys }); class LinesAndColumns { ltOffsets; constructor(ltOffsets) { this.ltOffsets = ltOffsets; } getLocFromIndex(index) { const line = sortedLastIndex__default["default"](this.ltOffsets, index) + 1; const column = index - (line === 1 ? 0 : this.ltOffsets[line - 2]); return { line, column }; } createOffsetLocationCalculator(offset) { return { getFixOffset() { return offset; }, getLocFromIndex: this.getLocFromIndex.bind(this), }; } } class LocationCalculatorForHtml extends LinesAndColumns { gapOffsets; baseOffset; baseIndexOfGap; shiftOffset; constructor(gapOffsets, ltOffsets, baseOffset, shiftOffset = 0) { super(ltOffsets); this.gapOffsets = gapOffsets; this.ltOffsets = ltOffsets; this.baseOffset = baseOffset ?? 0; this.baseIndexOfGap = this.baseOffset === 0 ? 0 : sortedLastIndex__default["default"](gapOffsets, this.baseOffset); this.shiftOffset = shiftOffset; } getSubCalculatorAfter(offset) { return new LocationCalculatorForHtml(this.gapOffsets, this.ltOffsets, this.baseOffset + offset, this.shiftOffset); } getSubCalculatorShift(offset) { return new LocationCalculatorForHtml(this.gapOffsets, this.ltOffsets, this.baseOffset, this.shiftOffset + offset); } _getGap(index) { const offsets = this.gapOffsets; let g0 = sortedLastIndex__default["default"](offsets, index + this.baseOffset); let pos = index + this.baseOffset + g0 - this.baseIndexOfGap; while (g0 < offsets.length && offsets[g0] <= pos) { g0 += 1; pos += 1; } return g0 - this.baseIndexOfGap; } getLocation(index) { return this.getLocFromIndex(this.getOffsetWithGap(index)); } getOffsetWithGap(index) { return index + this.getFixOffset(index); } getFixOffset(offset) { const shiftOffset = this.shiftOffset; const gap = this._getGap(offset + shiftOffset); return this.baseOffset + gap + shiftOffset; } } const debug = debugFactory__default["default"]("vue-eslint-parser"); function isScriptElement(node) { return node.type === "VElement" && node.name === "script"; } function isScriptSetupElement(script) { return (isScriptElement(script) && script.startTag.attributes.some((attr) => !attr.directive && attr.key.name === "setup")); } function isTemplateElement(node) { return node.type === "VElement" && node.name === "template"; } function isStyleElement(node) { return node.type === "VElement" && node.name === "style"; } function getOwnerDocument(leafNode) { let node = leafNode; while (node != null && node.type !== "VDocumentFragment") { node = node.parent; } return node; } function isLang(attribute) { return attribute.directive === false && attribute.key.name === "lang"; } function getLang(element) { const langAttr = element?.startTag.attributes.find(isLang); const lang = langAttr?.value?.value; return lang || null; } function isTSLang(element) { const lang = getLang(element); return lang === "ts" || lang === "tsx"; } function findGenericDirective(element) { return (element.startTag.attributes.find((attr) => attr.directive && attr.value?.expression?.type === "VGenericExpression") || null); } function isParserObject(value) { return isEnhancedParserObject(value) || isBasicParserObject(value); } function isEnhancedParserObject(value) { return Boolean(value && typeof value.parseForESLint === "function"); } function isBasicParserObject(value) { return Boolean(value && typeof value.parse === "function"); } function isSFCFile(parserOptions) { if (parserOptions.filePath === "<input>") { return true; } return path__namespace.extname(parserOptions.filePath || "unknown.vue") === ".vue"; } function getScriptParser(parser, getParserLang) { if (isParserObject(parser)) { return parser; } if (parser && typeof parser === "object") { const parserLang = getParserLang(); const parserLangs = parserLang == null ? [] : typeof parserLang === "string" ? [parserLang] : parserLang; for (const lang of parserLangs) { const parserForLang = lang && parser[lang]; if (typeof parserForLang === "string" || isParserObject(parserForLang)) { return parserForLang; } } return parser.js; } return typeof parser === "string" ? parser : undefined; } function getParserLangFromSFC(doc) { if (doc) { const scripts = doc.children.filter(isScriptElement); const script = (scripts.length === 2 && scripts.find(isScriptSetupElement)) || scripts[0]; if (script) { return getLang(script); } } return null; } let escopeCache = null; function getEslintScope() { return escopeCache ?? (escopeCache = getNewest()); } function getNewest() { let newest = escope__namespace; const userEscope = getEslintScopeFromUser(); if (userEscope.version != null && semver.lte(newest.version, userEscope.version)) { newest = userEscope; } return newest; } function getEslintScopeFromUser() { try { const cwd = process.cwd(); const relativeTo = path__default["default"].join(cwd, "__placeholder__.js"); return module$1.createRequire(relativeTo)("eslint-scope"); } catch { return escope__namespace; } } let espreeCache = null; function getEspree() { return espreeCache ?? (espreeCache = getNewestEspree()); } function getEcmaVersionIfUseEspree(parserOptions) { if (parserOptions.parser != null && parserOptions.parser !== "espree") { return undefined; } if (parserOptions.ecmaVersion === "latest" || parserOptions.ecmaVersion == null) { return getDefaultEcmaVersion(); } return normalizeEcmaVersion(parserOptions.ecmaVersion); } function getEspreeFromUser() { try { const cwd = process.cwd(); const relativeTo = path__default["default"].join(cwd, "__placeholder__.js"); return module$1.createRequire(relativeTo)("espree"); } catch { return dependencyEspree__namespace; } } function getNewestEspree() { let newest = dependencyEspree__namespace; const userEspree = getEspreeFromUser(); if (userEspree.version != null && semver.lte(newest.version, userEspree.version)) { newest = userEspree; } return newest; } function getDefaultEcmaVersion() { return getLatestEcmaVersion(getEspree()); } function normalizeEcmaVersion(version) { if (version > 5 && version < 2015) { return version + 2009; } return version; } function getLatestEcmaVersion(espree) { return normalizeEcmaVersion(espree.latestEcmaVersion); } const DEFAULT_ECMA_VERSION = "latest"; const ANALYZE_SCOPE_DEFAULT_ECMA_VERSION = 2022; function isUnique(reference, index, references) { return (index === 0 || reference.identifier !== references[index - 1].identifier); } function hasDefinition(variable) { return variable.defs.length >= 1; } function transformReference(reference) { const ret = { id: reference.identifier, mode: reference.isReadOnly() ? "r" : reference.isWriteOnly() ? "w" : "rw", variable: null, isValueReference: reference.isValueReference, isTypeReference: reference.isTypeReference, }; Object.defineProperty(ret, "variable", { enumerable: false }); return ret; } function transformVariable(variable, kind) { const ret = { id: variable.defs[0].name, kind, references: [], }; Object.defineProperty(ret, "references", { enumerable: false }); return ret; } function getForScope(scope) { const child = scope.childScopes[0]; return child.block === scope.block ? child.childScopes[0] : child; } function analyzeScope(ast, parserOptions) { const ecmaVersion = getEcmaVersionIfUseEspree(parserOptions) ?? ANALYZE_SCOPE_DEFAULT_ECMA_VERSION; const ecmaFeatures = parserOptions.ecmaFeatures ?? {}; const sourceType = parserOptions.sourceType ?? "script"; const result = getEslintScope().analyze(ast, { ignoreEval: true, nodejsScope: false, impliedStrict: ecmaFeatures.impliedStrict, ecmaVersion, sourceType, fallback: getFallbackKeys, }); return result; } function analyze(parserResult, parserOptions) { const scopeManager = parserResult.scopeManager || analyzeScope(parserResult.ast, parserOptions); return scopeManager.globalScope; } function analyzeExternalReferences(parserResult, parserOptions) { const scope = analyze(parserResult, parserOptions); return scope.through.filter(isUnique).map(transformReference); } function analyzeVariablesAndExternalReferences(parserResult, kind, parserOptions) { const scope = analyze(parserResult, parserOptions); return { variables: getForScope(scope) .variables.filter(hasDefinition) .map((v) => transformVariable(v, kind)), references: scope.through.filter(isUnique).map(transformReference), }; } function fixLocations(result, locationCalculator) { fixNodeLocations(result.ast, result.visitorKeys, locationCalculator); for (const token of result.ast.tokens ?? []) { fixLocation(token, locationCalculator); } for (const comment of result.ast.comments ?? []) { fixLocation(comment, locationCalculator); } } function fixNodeLocations(rootNode, visitorKeys, locationCalculator) { const traversed = new Map(); traverseNodes(rootNode, { visitorKeys, enterNode(node, parent) { if (!traversed.has(node)) { traversed.set(node, node); node.parent = parent; if (traversed.has(node.range)) { if (!traversed.has(node.loc)) { node.loc.start = locationCalculator.getLocFromIndex(node.range[0]); node.loc.end = locationCalculator.getLocFromIndex(node.range[1]); traversed.set(node.loc, node); } else if (node.start != null || node.end != null) { const traversedNode = traversed.get(node.range); if (traversedNode.type === node.type) { node.start = traversedNode.start; node.end = traversedNode.end; } } } else { fixLocation(node, locationCalculator); traversed.set(node.range, node); traversed.set(node.loc, node); } } }, leaveNode() { }, }); } function fixLocation(node, locationCalculator) { const range = node.range; const loc = node.loc; const d0 = locationCalculator.getFixOffset(range[0], "start"); const d1 = locationCalculator.getFixOffset(range[1], "end"); if (d0 !== 0) { range[0] += d0; if (node.start != null) { node.start += d0; } loc.start = locationCalculator.getLocFromIndex(range[0]); } if (d1 !== 0) { range[1] += d1; if (node.end != null) { node.end += d0; } loc.end = locationCalculator.getLocFromIndex(range[1]); } return node; } function fixErrorLocation(error, locationCalculator) { const diff = locationCalculator.getFixOffset(error.index, "start"); error.index += diff; const loc = locationCalculator.getLocFromIndex(error.index); error.lineNumber = loc.line; error.column = loc.column; } function extractGeneric(element) { const genericAttr = findGenericDirective(element); if (!genericAttr) { return null; } const genericNode = genericAttr.value.expression; const defineTypes = genericNode.params.map((t, i) => ({ node: t, define: `type ${t.name.name} = ${getConstraint(t, genericNode.rawParams[i])}`, })); return { node: genericNode, defineTypes, postprocess({ result, getTypeBlock, isRemoveTarget, getTypeDefScope }) { const node = getTypeBlock?.(result.ast) ?? result.ast; removeTypeDeclarations(node, isRemoveTarget); if (result.ast.tokens) { removeTypeDeclarationTokens(result.ast.tokens, isRemoveTarget); } if (result.ast.comments) { removeTypeDeclarationTokens(result.ast.comments, isRemoveTarget); } if (result.scopeManager) { const typeDefScope = getTypeDefScope(result.scopeManager); restoreScope(result.scopeManager, typeDefScope, isRemoveTarget); } }, }; function removeTypeDeclarations(node, isRemoveTarget) { for (let index = node.body.length - 1; index >= 0; index--) { if (isRemoveTarget(node.body[index])) { node.body.splice(index, 1); } } } function removeTypeDeclarationTokens(tokens, isRemoveTarget) { for (let index = tokens.length - 1; index >= 0; index--) { if (isRemoveTarget(tokens[index])) { tokens.splice(index, 1); } } } function restoreScope(scopeManager, typeDefScope, isRemoveTarget) { for (const variable of [...typeDefScope.variables]) { let def = variable.defs.find((d) => isRemoveTarget(d.name)); while (def) { removeVariableDef(variable, def, typeDefScope); def = variable.defs.find((d) => isRemoveTarget(d.name)); } } for (const reference of [...typeDefScope.references]) { if (isRemoveTarget(reference.identifier)) { removeReference(reference, typeDefScope); } } for (const scope of [...scopeManager.scopes]) { if (isRemoveTarget(scope.block)) { removeScope(scopeManager, scope); } } } } function getConstraint(node, rawParam) { if (!node.constraint) { return "unknown"; } let index = rawParam.indexOf(node.name.name) + node.name.name.length; let startIndex = null; while (index < rawParam.length) { if (startIndex == null) { if (rawParam.startsWith("extends", index)) { startIndex = index = index + 7; continue; } } else if (rawParam[index] === "=") { if (rawParam[index + 1] === ">") { index += 2; continue; } return rawParam.slice(startIndex, index); } if (rawParam.startsWith("//", index)) { const lfIndex = rawParam.indexOf("\n", index); if (lfIndex >= 0) { index = lfIndex + 1; continue; } return "unknown"; } if (rawParam.startsWith("/*", index)) { const endIndex = rawParam.indexOf("*/", index); if (endIndex >= 0) { index = endIndex + 2; continue; } return "unknown"; } index++; } if (startIndex == null) { return "unknown"; } return rawParam.slice(startIndex); } function removeVariableDef(variable, def, scope) { const defIndex = variable.defs.indexOf(def); if (defIndex < 0) { return; } variable.defs.splice(defIndex, 1); if (variable.defs.length === 0) { referencesToThrough(variable.references, scope); variable.references.forEach((r) => { if (r.init) { r.init = false; } r.resolved = null; }); scope.variables.splice(scope.variables.indexOf(variable), 1); const name = variable.name; if (variable === scope.set.get(name)) { scope.set.delete(name); } } else { const idIndex = variable.identifiers.indexOf(def.name); if (idIndex >= 0) { variable.identifiers.splice(idIndex, 1); } } } function referencesToThrough(references, baseScope) { let scope = baseScope; while (scope) { addAllReferences(scope.through, references); scope = scope.upper; } } function addAllReferences(list, elements) { list.push(...elements); list.sort((a, b) => a.identifier.range[0] - b.identifier.range[0]); } function removeReference(reference, baseScope) { if (reference.resolved) { if (reference.resolved.defs.some((d) => d.name === reference.identifier)) { const varIndex = baseScope.variables.indexOf(reference.resolved); if (varIndex >= 0) { baseScope.variables.splice(varIndex, 1); } const name = reference.identifier.name; if (reference.resolved === baseScope.set.get(name)) { baseScope.set.delete(name); } } else { const refIndex = reference.resolved.references.indexOf(reference); if (refIndex >= 0) { reference.resolved.references.splice(refIndex, 1); } } } let scope = baseScope; while (scope) { const refIndex = scope.references.indexOf(reference); if (refIndex >= 0) { scope.references.splice(refIndex, 1); } const throughIndex = scope.through.indexOf(reference); if (throughIndex >= 0) { scope.through.splice(throughIndex, 1); } scope = scope.upper; } } function removeScope(scopeManager, scope) { for (const childScope of scope.childScopes) { removeScope(scopeManager, childScope); } while (scope.references[0]) { removeReference(scope.references[0], scope); } const upper = scope.upper; if (upper) { const index = upper.childScopes.indexOf(scope); if (index >= 0) { upper.childScopes.splice(index, 1); } } const index = scopeManager.scopes.indexOf(scope); if (index >= 0) { scopeManager.scopes.splice(index, 1); } } const ALIAS_ITERATOR = /^([\s\S]*?(?:\s|\)))(\bin\b|\bof\b)([\s\S]*)$/u; const PARENS = /^(\s*\()([\s\S]*?)(\)\s*)$/u; const DUMMY_PARENT$2 = {}; const IS_FUNCTION_EXPRESSION = /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/u; const IS_SIMPLE_PATH = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?'\]|\["[^"]*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/u; function processVForAliasAndIterator(code) { const match = ALIAS_ITERATOR.exec(code); if (match != null) { const aliases = match[1]; const parenMatch = PARENS.exec(aliases); return { aliases, hasParens: Boolean(parenMatch), aliasesWithBrackets: parenMatch ? `${parenMatch[1].slice(0, -1)}[${parenMatch[2]}]${parenMatch[3].slice(1)}` : `[${aliases.slice(0, -1)}]`, delimiter: match[2] || "", iterator: match[3], }; } return { aliases: "", hasParens: false, aliasesWithBrackets: "", delimiter: "", iterator: code, }; } function getCommaTokenBeforeNode(tokens, node) { let tokenIndex = sortedIndexBy__default["default"](tokens, { range: node.range }, (t) => t.range[0]); while (tokenIndex >= 0) { const token = tokens[tokenIndex]; if (token.type === "Punctuator" && token.value === ",") { return token; } tokenIndex -= 1; } return null; } function throwEmptyError(locationCalculator, expected) { const loc = locationCalculator.getLocation(0); const err = new ParseError(`Expected to be ${expected}, but got empty.`, undefined, 0, loc.line, loc.column); fixErrorLocation(err, locationCalculator); throw err; } function throwUnexpectedTokenError(name, token) { const err = new ParseError(`Unexpected token '${name}'.`, undefined, token.range[0], token.loc.start.line, token.loc.start.column); throw err; } function throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator) { if (ParseError.isParseError(err)) { const endOffset = locationCalculator.getOffsetWithGap(code.length); if (err.index >= endOffset) { err.message = "Unexpected end of expression."; } } throw err; } function parseScriptFragment(code, locationCalculator, parserOptions) { return parseScriptFragmentWithOption(code, locationCalculator, parserOptions); } function parseScriptFragmentWithOption(code, locationCalculator, parserOptions, processOptions) { try { const result = parseScript$1(code, parserOptions); processOptions?.preFixLocationProcess?.(result); fixLocations(result, locationCalculator); return result; } catch (err) { const perr = ParseError.normalize(err); if (perr) { fixErrorLocation(perr, locationCalculator); throw perr; } throw err; } } const validDivisionCharRE = /[\w).+\-_$\]]/u; function splitFilters(exp) { const result = []; let inSingle = false; let inDouble = false; let inTemplateString = false; let inRegex = false; let curly = 0; let square = 0; let paren = 0; let lastFilterIndex = 0; let c = 0; let prev = 0; for (let i = 0; i < exp.length; i++) { prev = c; c = exp.charCodeAt(i); if (inSingle) { if (c === 0x27 && prev !== 0x5c) { inSingle = false; } } else if (inDouble) { if (c === 0x22 && prev !== 0x5c) { inDouble = false; } } else if (inTemplateString) { if (c === 0x60 && prev !== 0x5c) { inTemplateString = false; } } else if (inRegex) { if (c === 0x2f && prev !== 0x5c) { inRegex = false; } } else if (c === 0x7c && exp.charCodeAt(i + 1) !== 0x7c && exp.charCodeAt(i - 1) !== 0x7c && !curly && !square && !paren) { result.push(exp.slice(lastFilterIndex, i)); lastFilterIndex = i + 1; } else { switch (c) { case 0x22: inDouble = true; break; case 0x27: inSingle = true; break; case 0x60: inTemplateString = true; break; case 0x28: paren++; break; case 0x29: paren--; break; case 0x5b: square++; break; case 0x5d: square--; break; case 0x7b: curly++; break; case 0x7d: curly--; break; } if (c === 0x2f) { let j = i - 1; let p; for (; j >= 0; j--) { p = exp.charAt(j); if (p !== " ") { break; } } if (!p || !validDivisionCharRE.test(p)) { inRegex = true; } } } } result.push(exp.slice(lastFilterIndex)); return result; } function parseExpressionBody(code, locationCalculator, parserOptions, allowEmpty = false) { debug('[script] parse expression: "0(%s)"', code); try { const result = parseScriptFragment(`0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions); const { ast } = result; const tokens = ast.tokens ?? []; const comments = ast.comments ?? []; const references = analyzeExternalReferences(result, parserOptions); const statement = ast.body[0]; const callExpression = statement.expression; const expression = callExpression.arguments[0]; if (!allowEmpty && !expression) { return throwEmptyError(locationCalculator, "an expression"); } if (expression?.type === "SpreadElement") { return throwUnexpectedTokenError("...", expression); } if (callExpression.arguments[1]) { const node = callExpression.arguments[1]; return throwUnexpectedTokenError(",", getCommaTokenBeforeNode(tokens, node) || node); } tokens.shift(); tokens.shift(); tokens.pop(); return { expression, tokens, comments, references, variables: [] }; } catch (err) { return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator); } } function parseFilter(code, locationCalculator, parserOptions) { debug('[script] parse filter: "%s"', code); try { const expression = { type: "VFilter", parent: null, range: [0, 0], loc: {}, callee: null, arguments: [], }; const tokens = []; const comments = []; const references = []; const paren = code.indexOf("("); const calleeCode = paren === -1 ? code : code.slice(0, paren); const argsCode = paren === -1 ? null : code.slice(paren); if (calleeCode.trim()) { const spaces = /^\s*/u.exec(calleeCode)[0]; const subCalculator = locationCalculator.getSubCalculatorShift(spaces.length); const { ast } = parseScriptFragment(`"${calleeCode.trim()}"`, subCalculator, parserOptions); const statement = ast.body[0]; const callee = statement.expression; if (callee.type !== "Literal") { const { loc, range } = ast.tokens[0]; return throwUnexpectedTokenError('"', { range: [range[1] - 1, range[1]], loc: { start: { line: loc.end.line, column: loc.end.column - 1, }, end: loc.end, }, }); } expression.callee = { type: "Identifier", parent: expression, range: [ callee.range[0], subCalculator.getOffsetWithGap(calleeCode.trim().length), ], loc: { start: callee.loc.start, end: subCalculator.getLocation(calleeCode.trim().length), }, name: String(callee.value), }; tokens.push({ type: "Identifier", value: calleeCode.trim(), range: expression.callee.range, loc: expression.callee.loc, }); } else { return throwEmptyError(locationCalculator, "a filter name"); } if (argsCode != null) { const result = parseScriptFragment(`0${argsCode}`, locationCalculator .getSubCalculatorAfter(paren) .getSubCalculatorShift(-1), parserOptions); const { ast } = result; const statement = ast.body[0]; const callExpression = statement.expression; ast.tokens.shift(); if (callExpression.type !== "CallExpression" || callExpression.callee.type !== "Literal") { let nestCount = 1; for (const token of ast.tokens.slice(1)) { if (nestCount === 0) { return throwUnexpectedTokenError(token.value, token); } if (token.type === "Punctuator" && token.value === "(") { nestCount += 1; } if (token.type === "Punctuator" && token.value === ")") { nestCount -= 1; } } const token = last__default["default"](ast.tokens); return throwUnexpectedTokenError(token.value, token); } for (const argument of callExpression.arguments) { argument.parent = expression; expression.arguments.push(argument); } tokens.push(...ast.tokens); comments.push(...ast.comments); references.push(...analyzeExternalReferences(result, parserOptions)); } const firstToken = tokens[0]; const lastToken = last__default["default"](tokens); expression.range = [firstToken.range[0], lastToken.range[1]]; expression.loc = { start: firstToken.loc.start, end: lastToken.loc.end }; return { expression, tokens, comments, references, variables: [] }; } catch (err) { return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator); } } function loadParser(parser) { if (parser !== "espree") { return require(parser); } return getEspree(); } function parseScript$1(code, parserOptions) { const parser = typeof parserOptions.parser === "string" ? loadParser(parserOptions.parser) : isParserObject(parserOptions.parser) ? parserOptions.parser : getEspree(); const result = isEnhancedParserObject(parser) ? parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions); if (result.ast != null) { return result; } return { ast: result }; } function parseScriptElement(node, sfcCode, linesAndColumns, originalParserOptions) { const parserOptions = { ...originalParserOptions, ecmaVersion: originalParserOptions.ecmaVersion ?? DEFAULT_ECMA_VERSION, }; let generic = null; let code; let offset; const textNode = node.children[0]; if (textNode?.type === "VText") { const [scriptStartOffset, scriptEndOffset] = textNode.range; code = sfcCode.slice(scriptStartOffset, scriptEndOffset); offset = scriptStartOffset; generic = extractGeneric(node); if (generic) { const defineTypesCode = `${generic.defineTypes .map((e) => e.define) .join(";")};\n`; code = defineTypesCode + code; offset -= defineTypesCode.length; } } else { code = ""; offset = node.startTag.range[1]; } const locationCalculator = linesAndColumns.createOffsetLocationCalculator(offset); const result = parseScriptFragment(code, locationCalculator, parserOptions); if (generic) { generic.postprocess({ result, isRemoveTarget(nodeOrToken) { return nodeOrToken.range[1] <= textNode.range[0]; }, getTypeDefScope(scopeManager) { return (scopeManager.globalScope.childScopes.find((s) => s.type === "module") ?? scopeManager.globalScope); }, }); const startToken = [ result.ast.body[0], result.ast.tokens?.[0], result.ast.comments?.[0], ] .filter((e) => Boolean(e)) .sort((a, b) => a.range[0] - b.range[0]) .find((t) => Boolean(t)); if (startToken && result.ast.range[0] !== startToken.range[0]) { result.ast.range[0] = startToken.range[0]; if (result.ast.start != null) { result.ast.start = startToken.start; } result.ast.loc.start = { ...startToken.loc.start }; } } if (result.ast.tokens != null) { const startTag = node.startTag; const endTag = node.endTag; result.ast.tokens.unshift({ type: "Punctuator", range: startTag.range, loc: startTag.loc, value: "<script>", }); if (endTag != null) { result.ast.tokens.push({ type: "Punctuator", range: endTag.range, loc: endTag.loc, value: "</script>", }); } } return result; } function parseExpression(code, locationCalculator, parserOptions, { allowEmpty = false, allowFilters = false } = {}) { debug('[script] parse expression: "%s"', code); const [mainCode, ...filterCodes] = allowFilters && (parserOptions.vueFeatures?.filter ?? true) ? splitFilters(code) : [code]; if (filterCodes.length === 0) { return parseExpressionBody(code, locationCalculator, parserOptions, allowEmpty); } const retB = parseExpressionBody(mainCode, locationCalculator, parserOptions); if (!retB.expression) { return retB; } const ret = retB; ret.expression = { type: "VFilterSequenceExpression", parent: null, expression: retB.expression, filters: [], range: [...retB.expression.range], loc: { ...retB.expression.loc }, }; ret.expression.expression.parent = ret.expression; let prevLoc = mainCode.length; for (const filterCode of filterCodes) { ret.tokens.push(fixLocation({ type: "Punctuator", value: "|", range: [prevLoc, prevLoc + 1], loc: {}, }, locationCalculator)); const retF = parseFilter(filterCode, locationCalculator.getSubCalculatorShift(prevLoc + 1), parserOptions); if (retF) { if (retF.expression) { ret.expression.filters.push(retF.expression); retF.expression.parent = ret.expression; } ret.tokens.push(...retF.tokens); ret.comments.push(...retF.comments); ret.references.push(...retF.references); } prevLoc += 1 + filterCode.length; } const lastToken = last__default["default"](ret.tokens); ret.expression.range[1] = lastToken.range[1]; ret.expression.loc.end = lastToken.loc.end; return ret; } function parseVForExpression(code, locationCalculator, parserOptions) { if (code.trim() === "") { throwEmptyError(locationCalculator, "'<alias> in <expression>'"); } if (isEcmaVersion5(parserOptions)) { return parseVForExpressionForEcmaVersion5(code, locationCalculator, parserOptions); } const processed = processVForAliasAndIterator(code); if (!processed.aliases.trim()) { return throwEmptyError(locationCalculator, "an alias"); } try { debug('[script] parse v-for expression: "for(%s%s%s);"', processed.aliasesWithBrackets, processed.delimiter, processed.iterator); const result = parseScriptFragment(`for(let ${processed.aliasesWithBrackets}${processed.delimiter}${processed.iterator});`, locationCalculator.getSubCalculatorShift(processed.hasParens ? -8 : -9), parserOptions); const { ast } = result; const tokens = ast.tokens ?? []; const comments = ast.comments ?? []; const scope = analyzeVariablesAndExternalReferences(result, "v-for", parserOptions); const references = scope.references; const variables = scope.variables; const statement = ast.body[0]; const varDecl = statement.left; const id = varDecl.declarations[0].id; const left = id.elements; const right = statement.right; if (!processed.hasParens && !left.length) { return throwEmptyError(locationCalculator, "an alias"); } tokens.shift(); tokens.shift(); tokens.shift(); tokens.pop(); tokens.pop(); const closeOffset = statement.left.range[1] - 1; const closeIndex = tokens.findIndex((t) => t.range[0] === closeOffset); if (processed.hasParens) { const open = tokens[0]; if (open != null) { open.value = "("; } const close = tokens[closeIndex]; if (close != null) { close.value = ")"; } } else { tokens.splice(closeIndex, 1); tokens.shift(); } const firstToken = tokens[0] || statement.left; const lastToken = tokens[tokens.length - 1] || statement.right; const expression = { type: "VForExpression", range: [firstToken.range[0], lastToken.range[1]], loc: { start: firstToken.loc.start, end: lastToken.loc.end }, parent: DUMMY_PARENT$2, left, right, }; for (const l of left) { if (l != null) { l.parent = expression; } } right.parent = expression; return { expression, tokens, comments, references, variables }; } catch (err) { return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator); } } function isEcmaVersion5(parserOptions) { const ecmaVersion = getEcmaVersionIfUseEspree(parserOptions); return ecmaVersion != null && ecmaVersion <= 5; } function parseVForExpressionForEcmaVersion5(code, locationCalculator, parserOptions) { const processed = processVForAliasAndIterator(code); if (!processed.aliases.trim()) { return throwEmptyError(locationCalculator, "an alias"); } try { const tokens = []; const comments = []; const parsedAliases = parseVForAliasesForEcmaVersion5(processed.aliasesWithBrackets, locationCalculator.getSubCalculatorShift(processed.hasParens ? 0 : -1), parserOptions); if (processed.hasParens) { const open = parsedAliases.tokens[0]; if (open != null) { open.value = "("; } const close = last__default["default"](parsedAliases.tokens); if (close != null) { close.value = ")"; } } else { parsedAliases.tokens.shift(); parsedAliases.tokens.pop(); } tokens.push(...parsedAliases.tokens); comments.push(...parsedAliases.comments); const { left, variables } = parsedAliases; if (!processed.hasParens && !left.length) { return throwEmptyError(locationCalculator, "an alias"); } const delimiterStart = processed.aliases.length; const delimiterEnd = delimiterStart + processed.delimiter.length; tokens.push(fixLocation({ type: processed.delimiter === "in" ? "Keyword" : "Identifier", value: processed.delimiter, start: delimiterStart, end: delimiterEnd, loc: {}, range: [delimiterStart, delimiterEnd], }, locationCalculator)); const parsedIterator = parseVForIteratorForEcmaVersion5(processed.iterator, locationCalculator.getSubCalculatorShift(delimiterEnd), parserOptions); tokens.push(...parsedIterator.tokens); comments.push(...parsedIterator.comments); const { right, references } = parsedIterator; const firstToken = tokens[0]; const lastToken = last__default["default"](tokens) || firstToken; const expression = { type: "VForExpression", range: [firstToken.range[0], lastToken.range[1]], loc: { start: firstToken.loc.start, end: lastToken.loc.end }, parent: DUMMY_PARENT$2, left, right, }; for (const l of left) { if (l != null) { l.parent = expression; } } right.parent = expression; return { expression, tokens, comments, references, variables }; } catch (err) { return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator); } } function parseVForAliasesForEcmaVersion5(code, locationCalculator, parserOptions) { const result = parseScriptFragment(`0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions); const { ast } = result; const tokens = ast.tokens ?? []; const comments = ast.comments ?? []; const variables = analyzeExternalReferences(result, parserOptions).map(transformVariable); const statement = ast.body[0]; const callExpression = statement.expression; const expression = callExpression.arguments[0]; const left = expression.elements.filter((e) => { if (e == null || e.type === "Identifier") { return true; } const errorToken = tokens.find((t) => e.range[0] <= t.range[0] && t.range[1] <= e.range[1]); return throwUnexpectedTokenError(errorToken.value, errorToken); }); tokens.shift(); tokens.shift(); tokens.pop(); return { left, tokens, comments, variables }; function transformVariable(reference) { const ret = { id: reference.id, kind: "v-for", references: [], }; Object.defineProperty(ret, "references", { enumerable: false }); return ret; } } function parseVForIteratorForEcmaVersion5(code, locationCalculator, parserOptions) { const result = parseScriptFragment(`0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions); const { ast } = result; const tokens = ast.tokens ?? []; const comments = ast.comments ?? []; const references = analyzeExternalReferences(result, parserOptions); const statement = ast.body[0]; const callExpression = statement.expression; const expression = callExpression.arguments[0]; if (!expression) { return throwEmptyError(locationCalculator, "an expression"); } if (expression?.type === "SpreadElement") { return throwUnexpectedTokenError("...", expression); } const right = expression; tokens.shift(); tokens.shift(); tokens.pop(); return { right, tokens, comments, references }; } function parseVOnExpression(code, locationCalculator, parserOptions) { if (IS_FUNCTION_EXPRESSION.test(code) || IS_SIMPLE_PATH.test(code)) { return parseExpressionBody(code, locationCalculator, parserOptions); } return parseVOnExpressionBody(code, locationCalculator, parserOptions); } function parseVOnExpressionBody(code