UNPKG

astro-eslint-parser

Version:
1,656 lines (1,631 loc) 89.1 kB
var __defProp = Object.defineProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; // src/visitor-keys.ts import { unionWith } from "eslint-visitor-keys"; var astroKeys = { Program: ["body"], AstroFragment: ["children"], AstroHTMLComment: [], AstroDoctype: [], AstroShorthandAttribute: ["name", "value"], AstroTemplateLiteralAttribute: ["name", "value"], AstroRawText: [] }; var KEYS = unionWith( astroKeys ); // src/parser/index.ts import { AST_TOKEN_TYPES as AST_TOKEN_TYPES2 } from "@typescript-eslint/types"; // src/debug.ts import debugFactory from "debug"; var debug = debugFactory("astro-eslint-parser"); // src/parser/ts-patch.ts import { createRequire as createRequire2 } from "module"; import path4 from "path"; import fs2 from "fs"; import { satisfies } from "semver"; // src/parser/ts-for-v5/get-project-config-files.ts import * as fs from "fs"; import * as path from "path"; function getProjectConfigFiles(parseSettings, project) { if (project !== true) { return project === void 0 || Array.isArray(project) ? project : [project]; } let directory = path.dirname(parseSettings.filePath); const checkedDirectories = [directory]; do { const tsconfigPath = path.join(directory, "tsconfig.json"); const cached = fs.existsSync(tsconfigPath) && tsconfigPath; if (cached) { return [cached]; } directory = path.dirname(directory); checkedDirectories.push(directory); } while (directory.length > 1 && directory.length >= parseSettings.tsconfigRootDir.length); throw new Error( `project was set to \`true\` but couldn't find any tsconfig.json relative to '${parseSettings.filePath}' within '${parseSettings.tsconfigRootDir}'.` ); } // src/parser/ts-for-v5/programs.ts import path2 from "path"; var tsServices = /* @__PURE__ */ new Map(); function getTSProgram(code, options, ts) { const tsconfigPath = options.project; let service2 = tsServices.get(tsconfigPath); if (!service2) { service2 = new TSService(tsconfigPath, ts); tsServices.set(tsconfigPath, service2); } return service2.getProgram(code, options.filePath); } var TSService = class { constructor(tsconfigPath, ts) { this.patchedHostSet = /* @__PURE__ */ new WeakSet(); this.currTarget = { code: "", filePath: "" }; this.fileWatchCallbacks = /* @__PURE__ */ new Map(); this.ts = ts; this.watch = this.createWatch(tsconfigPath); } getProgram(code, filePath) { const normalized = normalizeFileName(this.ts, filePath); const lastTarget = this.currTarget; this.currTarget = { code, filePath: normalized }; for (const { filePath: targetPath } of [this.currTarget, lastTarget]) { if (!targetPath) continue; this.fileWatchCallbacks.get(targetPath)?.update(); } const program = this.watch.getProgram().getProgram(); program.getTypeChecker(); return program; } createWatch(tsconfigPath) { const { ts } = this; const createAbstractBuilder = (...buildArgs) => { const [ rootNames, options, argHost, oldProgram, configFileParsingDiagnostics, projectReferences ] = buildArgs; const host = argHost; if (!this.patchedHostSet.has(host)) { this.patchedHostSet.add(host); const getTargetSourceFile = (fileName, languageVersionOrOptions) => { var _a; if (this.currTarget.filePath === normalizeFileName(ts, fileName)) { return (_a = this.currTarget).sourceFile ?? (_a.sourceFile = ts.createSourceFile( this.currTarget.filePath, this.currTarget.code, languageVersionOrOptions, true, ts.ScriptKind.TSX )); } return null; }; const original2 = { getSourceFile: host.getSourceFile, getSourceFileByPath: host.getSourceFileByPath }; host.getSourceFile = (fileName, languageVersionOrOptions, ...args) => { const originalSourceFile = original2.getSourceFile.call( host, fileName, languageVersionOrOptions, ...args ); return getTargetSourceFile(fileName, languageVersionOrOptions) ?? originalSourceFile; }; host.getSourceFileByPath = (fileName, filePath, languageVersionOrOptions, ...args) => { const originalSourceFile = original2.getSourceFileByPath.call( host, fileName, filePath, languageVersionOrOptions, ...args ); return getTargetSourceFile(fileName, languageVersionOrOptions) ?? originalSourceFile; }; } return ts.createAbstractBuilder( rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences ); }; const watchCompilerHost = ts.createWatchCompilerHost( tsconfigPath, { noEmit: true, jsx: ts.JsxEmit.Preserve, // This option is required if `includes` only includes `*.astro` files. // However, the option is not in the documentation. // https://github.com/microsoft/TypeScript/issues/28447 allowNonTsExtensions: true }, ts.sys, createAbstractBuilder, (diagnostic) => { throw new Error(formatDiagnostics(ts, [diagnostic])); }, () => { }, void 0, [ { extension: ".astro", isMixedContent: true, scriptKind: ts.ScriptKind.Deferred } ] ); const original = { readFile: watchCompilerHost.readFile }; watchCompilerHost.readFile = (fileName, ...args) => { const normalized = normalizeFileName(ts, fileName); if (this.currTarget.filePath === normalized) { return this.currTarget.code; } return original.readFile.call(watchCompilerHost, fileName, ...args); }; watchCompilerHost.watchFile = (fileName, callback) => { const normalized = normalizeFileName(ts, fileName); this.fileWatchCallbacks.set(normalized, { update: () => callback(fileName, ts.FileWatcherEventKind.Changed) }); return { close: () => { this.fileWatchCallbacks.delete(normalized); } }; }; watchCompilerHost.watchDirectory = () => { return { close: () => { } }; }; watchCompilerHost.afterProgramCreate = (program) => { const originalDiagnostics = program.getConfigFileParsingDiagnostics(); const configFileDiagnostics = originalDiagnostics.filter( (diag) => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18003 ); if (configFileDiagnostics.length > 0) { throw new Error(formatDiagnostics(ts, configFileDiagnostics)); } }; const watch = ts.createWatchProgram(watchCompilerHost); return watch; } }; function formatDiagnostics(ts, diagnostics) { return ts.formatDiagnostics(diagnostics, { getCanonicalFileName: (f) => f, getCurrentDirectory: () => process.cwd(), getNewLine: () => "\n" }); } function normalizeFileName(ts, fileName) { let normalized = path2.normalize(fileName); if (normalized.endsWith(path2.sep)) { normalized = normalized.slice(0, -1); } if (ts.sys.useCaseSensitiveFileNames) { return toAbsolutePath(normalized, null); } return toAbsolutePath(normalized.toLowerCase(), null); } function toAbsolutePath(filePath, baseDir) { return path2.isAbsolute(filePath) ? filePath : path2.join(baseDir || process.cwd(), filePath); } // src/parser/ts-for-v5/resolve-project-list.ts import { sync as globSync } from "fast-glob"; import isGlob from "is-glob"; import * as path3 from "path"; import { createRequire } from "module"; function resolveProjectList(options) { const sanitizedProjects = []; if (typeof options.project === "string") { sanitizedProjects.push(options.project); } else if (Array.isArray(options.project)) { for (const project of options.project) { if (typeof project === "string") { sanitizedProjects.push(project); } } } if (sanitizedProjects.length === 0) { return []; } const projectFolderIgnoreList = (options.projectFolderIgnoreList ?? ["**/node_modules/**"]).reduce((acc, folder) => { if (typeof folder === "string") { acc.push(folder); } return acc; }, []).map((folder) => folder.startsWith("!") ? folder : `!${folder}`); const nonGlobProjects = sanitizedProjects.filter( (project) => !isGlob(project) ); const globProjects = sanitizedProjects.filter((project) => isGlob(project)); const uniqueCanonicalProjectPaths = new Set( nonGlobProjects.concat( globProjects.length === 0 ? [] : globSync([...globProjects, ...projectFolderIgnoreList], { cwd: options.tsconfigRootDir }) ).map( (project) => getCanonicalFileName( ensureAbsolutePath(project, options.tsconfigRootDir) ) ) ); const returnValue = Array.from(uniqueCanonicalProjectPaths); return returnValue; } var _correctPathCasing; function correctPathCasing(filePath) { if (_correctPathCasing === void 0) { const ts = createRequire( path3.join(process.cwd(), "__placeholder__.js") )("typescript"); const useCaseSensitiveFileNames = ts.sys !== void 0 ? ts.sys.useCaseSensitiveFileNames : true; _correctPathCasing = useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase(); } return _correctPathCasing(filePath); } function getCanonicalFileName(filePath) { let normalized = path3.normalize(filePath); if (normalized.endsWith(path3.sep)) { normalized = normalized.slice(0, -1); } return correctPathCasing(normalized); } function ensureAbsolutePath(p, tsconfigRootDir) { return path3.isAbsolute(p) ? p : path3.join(tsconfigRootDir || process.cwd(), p); } // src/parser/ts-for-v5/parse-tsx-for-typescript.ts var DEFAULT_EXTRA_FILE_EXTENSIONS = [".vue", ".svelte", ".astro"]; function parseTsxForTypeScript(code, options, tsEslintParser, ts) { const programs = []; for (const option of iterateOptions(options)) { programs.push(getTSProgram(code, option, ts)); } const parserOptions = { ...options, programs }; return tsEslintParser.parseForESLint(code, parserOptions); } function* iterateOptions(options) { if (!options) { throw new Error("`parserOptions` is required."); } if (!options.filePath) { throw new Error("`filePath` is required."); } if (!options.project) { throw new Error( "Specify `parserOptions.project`. Otherwise there is no point in using this parser." ); } const tsconfigRootDir = typeof options.tsconfigRootDir === "string" ? options.tsconfigRootDir : process.cwd(); const projects = resolveProjectList({ project: getProjectConfigFiles( { tsconfigRootDir, filePath: options.filePath }, options.project ), projectFolderIgnoreList: options.projectFolderIgnoreList, tsconfigRootDir }); for (const project of projects) { yield { project, filePath: options.filePath, extraFileExtensions: options.extraFileExtensions || DEFAULT_EXTRA_FILE_EXTENSIONS }; } } // src/parser/ts-patch.ts function tsPatch(scriptParserOptions, tsParserName) { if (tsParserName === "typescript-eslint-parser-for-extra-files") { return { terminate() { } }; } let targetExt = ".astro"; if (scriptParserOptions.filePath) { const ext = path4.extname(scriptParserOptions.filePath); if (ext) { targetExt = ext; } } try { const cwd = process.cwd(); const relativeTo = path4.join(cwd, "__placeholder__.js"); const ts = createRequire2(relativeTo)("typescript"); if (satisfies(ts.version, ">=5")) { const result = tsPatchForV5(ts, scriptParserOptions); if (result) { return result; } } else { const result = tsPatchForV4(ts, targetExt); if (result) { return result; } } } catch { } const tsxFilePath = `${scriptParserOptions.filePath}.tsx`; scriptParserOptions.filePath = tsxFilePath; if (!fs2.existsSync(tsxFilePath)) { fs2.writeFileSync(tsxFilePath, "/* temp for astro-eslint-parser */"); return { terminate() { fs2.unlinkSync(tsxFilePath); } }; } return null; } function tsPatchForV5(ts, scriptParserOptions) { return { terminate() { }, parse(code, parser) { return parseTsxForTypeScript( code, scriptParserOptions, parser, ts ); } }; } function tsPatchForV4(ts, targetExt) { const { ensureScriptKind, getScriptKindFromFileName } = ts; if (typeof ensureScriptKind !== "function" || typeof getScriptKindFromFileName !== "function") { return null; } ts.ensureScriptKind = function(fileName, ...args) { if (fileName.endsWith(targetExt)) { return ts.ScriptKind.TSX; } return ensureScriptKind.call(this, fileName, ...args); }; ts.getScriptKindFromFileName = function(fileName, ...args) { if (fileName.endsWith(targetExt)) { return ts.ScriptKind.TSX; } return getScriptKindFromFileName.call(this, fileName, ...args); }; if (ts.ensureScriptKind === ensureScriptKind || ts.getScriptKindFromFileName === getScriptKindFromFileName) { return null; } return { terminate() { ts.ensureScriptKind = ensureScriptKind; ts.getScriptKindFromFileName = getScriptKindFromFileName; } }; } // src/context/resolve-parser/parser-object.ts 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 maybeTSESLintParserObject(value) { return isEnhancedParserObject(value) && isBasicParserObject(value) && typeof value.createProgram === "function" && typeof value.clearCaches === "function" && typeof value.version === "string"; } function getTSParserNameFromObject(value) { if (!isEnhancedParserObject(value)) { return null; } if (value.name === "typescript-eslint-parser-for-extra-files") return "typescript-eslint-parser-for-extra-files"; if (value.meta?.name === "typescript-eslint/parser") return "@typescript-eslint/parser"; return null; } function isTSESLintParserObject(value) { if (!isEnhancedParserObject(value)) return false; if (value.name === "typescript-eslint-parser-for-extra-files") return true; if (value.meta?.name === "typescript-eslint/parser") return true; try { const result = value.parseForESLint("", {}); const services = result.services; return Boolean( services && services.esTreeNodeToTSNodeMap && services.tsNodeToESTreeNodeMap && services.program ); } catch { return false; } } // src/parser/script.ts import { AST_NODE_TYPES } from "@typescript-eslint/types"; import { analyze as analyzeForTypeScript, Reference } from "@typescript-eslint/scope-manager"; import { Referencer as BaseReferencer, ScopeManager } from "eslint-scope"; // src/traverse.ts 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 getKeys(node, visitorKeys) { const keys = (visitorKeys || KEYS)[node.type] || getFallbackKeys(node); return keys.filter((key) => !getNodes(node, key).next().done); } function* getNodes(node, key) { const child = node[key]; if (Array.isArray(child)) { for (const c of child) { if (isNode(c)) { yield c; } } } else if (isNode(child)) { yield child; } } function isNode(x) { return x !== null && typeof x === "object" && typeof x.type === "string"; } function traverse(node, parent, visitor) { visitor.enterNode(node, parent); const keys = getKeys(node, visitor.visitorKeys); for (const key of keys) { for (const child of getNodes(node, key)) { traverse(child, node, visitor); } } visitor.leaveNode(node, parent); } function traverseNodes(node, visitor) { traverse(node, null, visitor); } // src/parser/scope/index.ts import { Reference as ReferenceClass, Variable as VariableClass } from "@typescript-eslint/scope-manager"; // src/util/index.ts function sortedLastIndex(array, compare) { let lower = 0; let upper = array.length; while (lower < upper) { const mid = Math.floor(lower + (upper - lower) / 2); const target = compare(array[mid]); if (target < 0) { lower = mid + 1; } else if (target > 0) { upper = mid; } else { return mid + 1; } } return upper; } function addElementToSortedArray(array, element, compare) { const index = sortedLastIndex(array, (target) => compare(target, element)); array.splice(index, 0, element); } function addElementsToSortedArray(array, elements, compare) { if (!elements.length) { return; } let last = elements[0]; let index = sortedLastIndex(array, (target) => compare(target, last)); for (const element of elements) { if (compare(last, element) > 0) { index = sortedLastIndex(array, (target) => compare(target, element)); } let e = array[index]; while (e && compare(e, element) <= 0) { e = array[++index]; } array.splice(index, 0, element); last = element; } } // src/parser/scope/index.ts var READ_FLAG = 1; var WRITE_FLAG = 2; var READ_WRITE_FLAG = 3; var REFERENCE_TYPE_VALUE_FLAG = 1; var REFERENCE_TYPE_TYPE_FLAG = 2; function getProgramScope(scopeManager) { const globalScope = scopeManager.globalScope; return globalScope.childScopes.find((s) => s.type === "module") || globalScope; } function removeAllScopeAndVariableAndReference(target, info) { const removeTargetScopes = /* @__PURE__ */ new Set(); traverseNodes(target, { visitorKeys: info.visitorKeys, enterNode(node) { const scope = info.scopeManager.acquire(node); if (scope) { removeTargetScopes.add(scope); return; } if (node.type === "Identifier" || node.type === "JSXIdentifier") { let targetScope = getInnermostScopeFromNode(info.scopeManager, node); while (targetScope && targetScope.block.type !== "Program" && target.range[0] <= targetScope.block.range[0] && targetScope.block.range[1] <= target.range[1]) { targetScope = targetScope.upper; } if (removeTargetScopes.has(targetScope)) { return; } removeIdentifierVariable(node, targetScope); removeIdentifierReference(node, targetScope); } }, leaveNode() { } }); for (const scope of removeTargetScopes) { removeScope(info.scopeManager, scope); } } function addVirtualReference(node, variable, scope, status) { const reference = new ReferenceClass( node, scope, status.write && status.read ? READ_WRITE_FLAG : status.write ? WRITE_FLAG : READ_FLAG, void 0, // writeExpr void 0, // maybeImplicitGlobal void 0, // init status.typeRef ? REFERENCE_TYPE_TYPE_FLAG : REFERENCE_TYPE_VALUE_FLAG ); reference.astroVirtualReference = true; addReference(variable.references, reference); reference.resolved = variable; if (status.forceUsed) { variable.eslintUsed = true; } return reference; } function addGlobalVariable(reference, scopeManager) { const globalScope = scopeManager.globalScope; const name2 = reference.identifier.name; let variable = globalScope.set.get(name2); if (!variable) { variable = new VariableClass(name2, globalScope); globalScope.variables.push(variable); globalScope.set.set(name2, variable); } reference.resolved = variable; variable.references.push(reference); return variable; } function removeReferenceFromThrough(reference, baseScope) { const variable = reference.resolved; const name2 = reference.identifier.name; let scope = baseScope; while (scope) { for (const ref of [...scope.through]) { if (reference === ref) { scope.through.splice(scope.through.indexOf(ref), 1); } else if (ref.identifier.name === name2) { ref.resolved = variable; if (!variable.references.includes(ref)) { addReference(variable.references, ref); } scope.through.splice(scope.through.indexOf(ref), 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 index2 = upper.childScopes.indexOf(scope); if (index2 >= 0) { upper.childScopes.splice(index2, 1); } } const index = scopeManager.scopes.indexOf(scope); if (index >= 0) { scopeManager.scopes.splice(index, 1); } } 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 name2 = reference.identifier.name; if (reference.resolved === baseScope.set.get(name2)) { baseScope.set.delete(name2); } } 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 removeIdentifierVariable(node, scope) { for (let varIndex = 0; varIndex < scope.variables.length; varIndex++) { const variable = scope.variables[varIndex]; const defIndex = variable.defs.findIndex((def) => def.name === node); if (defIndex < 0) { continue; } 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(varIndex, 1); const name2 = node.name; if (variable === scope.set.get(name2)) { scope.set.delete(name2); } } else { const idIndex = variable.identifiers.indexOf(node); if (idIndex >= 0) { variable.identifiers.splice(idIndex, 1); } } return; } } function removeIdentifierReference(node, scope) { const reference = scope.references.find((ref) => ref.identifier === node); if (reference) { removeReference(reference, scope); return true; } const location = node.range[0]; const pendingScopes = []; for (const childScope of scope.childScopes) { const range = childScope.block.range; if (range[0] <= location && location < range[1]) { if (removeIdentifierReference(node, childScope)) { return true; } } else { pendingScopes.push(childScope); } } for (const childScope of pendingScopes) { if (removeIdentifierReference(node, childScope)) { return true; } } return false; } function getInnermostScopeFromNode(scopeManager, currentNode) { return getInnermostScope( getScopeFromNode(scopeManager, currentNode), currentNode ); } function getScopeFromNode(scopeManager, currentNode) { let node = currentNode; for (; node; node = node.parent || null) { const scope = scopeManager.acquire(node, false); if (scope) { if (scope.type === "function-expression-name") { return scope.childScopes[0]; } if (scope.type === "global" && node.type === "Program" && node.sourceType === "module") { return scope.childScopes.find((s) => s.type === "module") || scope; } return scope; } } const global = scopeManager.globalScope; return global; } function getInnermostScope(initialScope, node) { for (const childScope of initialScope.childScopes) { const range = childScope.block.range; if (range[0] <= node.range[0] && node.range[1] <= range[1]) { return getInnermostScope(childScope, node); } } return initialScope; } function referencesToThrough(references, baseScope) { let scope = baseScope; while (scope) { addAllReferences(scope.through, references); scope = scope.upper; } } function addAllReferences(list, elements) { addElementsToSortedArray( list, elements, (a, b) => a.identifier.range[0] - b.identifier.range[0] ); } function addReference(list, reference) { addElementToSortedArray( list, reference, (a, b) => a.identifier.range[0] - b.identifier.range[0] ); } // src/parser/script.ts function parseScript(code, ctx, parserOptionsCtx) { const result = parseScriptInternal(code, ctx, parserOptionsCtx); const parserOptions = parserOptionsCtx.parserOptions; if (!result.scopeManager && parserOptions.eslintScopeManager) { result.scopeManager = analyzeScope(result, parserOptions); } return result; } function analyzeScope(result, parserOptions) { try { return analyzeForTypeScript(result.ast, { globalReturn: parserOptions.ecmaFeatures?.globalReturn, jsxPragma: parserOptions.jsxPragma, jsxFragmentName: parserOptions.jsxFragmentName, lib: parserOptions.lib, sourceType: parserOptions.sourceType }); } catch { } const ecmaFeatures = parserOptions.ecmaFeatures || {}; return analyzeForEcmaScript(result.ast, { ignoreEval: true, nodejsScope: ecmaFeatures.globalReturn, impliedStrict: ecmaFeatures.impliedStrict, ecmaVersion: 1e8, sourceType: parserOptions.sourceType === "commonjs" ? "script" : parserOptions.sourceType || "script", // @ts-expect-error -- Type bug? childVisitorKeys: result.visitorKeys || KEYS, fallback: getKeys }); } function parseScriptInternal(code, _ctx, parserOptionsCtx) { const parser = parserOptionsCtx.getParser(); let patchResult; try { const parserOptions = parserOptionsCtx.parserOptions; if (parserOptionsCtx.isTypeScript() && parserOptions.filePath && parserOptions.project) { patchResult = tsPatch(parserOptions, parserOptionsCtx.getTSParserName()); } else if (parserOptionsCtx.isTypeScript() && parserOptions.filePath && parserOptions.projectService) { console.warn( "`astro-eslint-parser` does not support the `projectService` option, it will parse it as `project: true` instead." ); patchResult = tsPatch( { ...parserOptions, project: true, projectService: void 0 }, parserOptionsCtx.getTSParserName() ); } const result = isEnhancedParserObject(parser) ? patchResult?.parse ? patchResult.parse(code, parser) : parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions); if ("ast" in result && result.ast != null) { return result; } return { ast: result }; } catch (e) { debug( "[script] parsing error:", e.message, `@ ${JSON.stringify(code)} ${code}` ); throw e; } finally { patchResult?.terminate(); } } var Referencer = class extends BaseReferencer { JSXAttribute(node) { this.visit(node.value); } JSXClosingElement() { } JSXFragment(node) { this.visitChildren(node); } JSXIdentifier(node) { const scope = this.currentScope(); const ref = new Reference( node, scope, READ_FLAG, void 0, void 0, false, REFERENCE_TYPE_VALUE_FLAG ); scope.references.push(ref); scope.__left.push(ref); } JSXMemberExpression(node) { if (node.object.type !== AST_NODE_TYPES.JSXIdentifier) { this.visit(node.object); } else { if (node.object.name !== "this") { this.visit(node.object); } } } JSXOpeningElement(node) { if (node.name.type === AST_NODE_TYPES.JSXIdentifier) { if (node.name.name[0].toUpperCase() === node.name.name[0] || node.name.name === "this") { this.visit(node.name); } } else { this.visit(node.name); } for (const attr of node.attributes) { this.visit(attr); } } }; function analyzeForEcmaScript(tree, providedOptions) { const options = Object.assign( { optimistic: false, nodejsScope: false, impliedStrict: false, sourceType: "script", // one of ['script', 'module', 'commonjs'] ecmaVersion: 5, childVisitorKeys: null, fallback: "iteration" }, providedOptions ); const scopeManager = new ScopeManager( // @ts-expect-error -- No typings options ); const referencer = new Referencer(options, scopeManager); referencer.visit(tree); return scopeManager; } // src/parser/sort.ts function sort(tokens) { return tokens.sort((a, b) => { if (a.range[0] !== b.range[0]) { return a.range[0] - b.range[0]; } return a.range[1] - b.range[1]; }); } // src/parser/process-template.ts import { AST_TOKEN_TYPES, AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/types"; // src/astro/index.ts import { EntityDecoder, DecodingMode, htmlDecodeTree } from "entities/decode"; // src/errors.ts var ParseError = class extends SyntaxError { /** * Initialize this ParseError instance. */ constructor(message, offset, ctx) { super(message); if (typeof offset === "number") { this.index = offset; const loc = ctx.getLocFromIndex(offset); this.lineNumber = loc.line; this.column = loc.column; } else { this.index = ctx.getIndexFromLoc(offset); this.lineNumber = offset.line; this.column = offset.column; } this.originalAST = ctx.originalAST; } }; // src/astro/index.ts function isTag(node) { return node.type === "element" || node.type === "custom-element" || node.type === "component" || node.type === "fragment"; } function isParent(node) { return Array.isArray(node.children); } function walkElements(parent, code, enter, leave, parents = []) { const children = getSortedChildren(parent, code); const currParents = [parent, ...parents]; for (const node of children) { enter(node, currParents); if (isParent(node)) { walkElements(node, code, enter, leave, currParents); } leave(node, currParents); } } function walk(parent, code, enter, leave) { walkElements( parent, code, (node, parents) => { enter(node, parents); if (isTag(node)) { const attrParents = [node, ...parents]; for (const attr of node.attributes) { enter(attr, attrParents); leave(attr, attrParents); } } }, leave ); } function calcStartTagEndOffset(node, ctx) { const lastAttr = node.attributes[node.attributes.length - 1]; let beforeCloseIndex; if (lastAttr) { beforeCloseIndex = calcAttributeEndOffset(lastAttr, ctx); } else { const info2 = getTokenInfo( ctx, [`<${node.name}`], node.position.start.offset ); beforeCloseIndex = info2.index + info2.match.length; } const info = getTokenInfo(ctx, [[">", "/>"]], beforeCloseIndex); return info.index + info.match.length; } function calcAttributeEndOffset(node, ctx) { let info; if (node.kind === "empty") { info = getTokenInfo(ctx, [node.name], node.position.start.offset); } else if (node.kind === "quoted") { info = getTokenInfo( ctx, [ [ { token: `"${node.value}"`, htmlEntityDecode: true }, { token: `'${node.value}'`, htmlEntityDecode: true }, { token: node.value, htmlEntityDecode: true } ] ], calcAttributeValueStartOffset(node, ctx) ); } else if (node.kind === "expression") { info = getTokenInfo( ctx, ["{", node.value, "}"], calcAttributeValueStartOffset(node, ctx) ); } else if (node.kind === "shorthand") { info = getTokenInfo( ctx, ["{", node.name, "}"], node.position.start.offset ); } else if (node.kind === "spread") { info = getTokenInfo( ctx, ["{", "...", node.name, "}"], node.position.start.offset ); } else if (node.kind === "template-literal") { info = getTokenInfo( ctx, [`\`${node.value}\``], calcAttributeValueStartOffset(node, ctx) ); } else { throw new ParseError( `Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx ); } return info.index + info.match.length; } function calcAttributeValueStartOffset(node, ctx) { let info; if (node.kind === "quoted") { info = getTokenInfo( ctx, [ node.name, "=", [`"`, `'`, { token: node.value, htmlEntityDecode: true }] ], node.position.start.offset ); } else if (node.kind === "expression") { info = getTokenInfo( ctx, [node.name, "=", "{"], node.position.start.offset ); } else if (node.kind === "template-literal") { info = getTokenInfo( ctx, [node.name, "=", "`"], node.position.start.offset ); } else { throw new ParseError( `Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx ); } return info.index; } function getEndOffset(node, ctx) { if (node.position.end?.offset != null) { return node.position.end.offset; } if (isTag(node)) return calcTagEndOffset(node, ctx); if (node.type === "expression") return calcExpressionEndOffset(node, ctx); if (node.type === "comment") return calcCommentEndOffset(node, ctx); if (node.type === "frontmatter") { const start = node.position.start.offset; return ctx.code.indexOf("---", start + 3) + 3; } if (node.type === "doctype") { const start = node.position.start.offset; return ctx.code.indexOf(">", start) + 1; } if (node.type === "text") { const start = node.position.start.offset; return start + node.value.length; } if (node.type === "root") { return ctx.code.length; } throw new Error(`unknown type: ${node.type}`); } function calcContentEndOffset(parent, ctx) { const code = ctx.code; if (isTag(parent)) { const end = getEndOffset(parent, ctx); if (code[end - 1] !== ">") { return end; } const index = code.lastIndexOf("</", end - 1); if (index >= 0 && code.slice(index + 2, end - 1).trim() === parent.name) { return index; } return end; } else if (parent.type === "expression") { const end = getEndOffset(parent, ctx); return code.lastIndexOf("}", end); } else if (parent.type === "root") { return code.length; } throw new Error(`unknown type: ${parent.type}`); } function getSelfClosingTag(node, ctx) { if (node.children.length > 0) { return null; } const code = ctx.code; const startTagEndOffset = calcStartTagEndOffset(node, ctx); if (code.startsWith("/>", startTagEndOffset - 2)) { return { offset: startTagEndOffset, end: "/>" }; } if (code.startsWith(`</${node.name}`, startTagEndOffset)) { return null; } return { offset: startTagEndOffset, end: ">" }; } function getEndTag(node, ctx) { let beforeIndex; if (node.children.length) { const lastChild = node.children[node.children.length - 1]; beforeIndex = getEndOffset(lastChild, ctx); } else { beforeIndex = calcStartTagEndOffset(node, ctx); } beforeIndex = skipSpaces(ctx.code, beforeIndex); if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) { const offset = beforeIndex; beforeIndex = beforeIndex + 2 + node.name.length; const info = getTokenInfo(ctx, [">"], beforeIndex); const end = info.index + info.match.length; return { offset, tag: ctx.code.slice(offset, end) }; } return null; } function calcCommentEndOffset(node, ctx) { const info = getTokenInfo( ctx, ["<!--", node.value, "-->"], node.position.start.offset ); return info.index + info.match.length; } function calcTagEndOffset(node, ctx) { let beforeIndex; if (node.children.length) { const lastChild = node.children[node.children.length - 1]; beforeIndex = getEndOffset(lastChild, ctx); } else { beforeIndex = calcStartTagEndOffset(node, ctx); } beforeIndex = skipSpaces(ctx.code, beforeIndex); if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) { beforeIndex = beforeIndex + 2 + node.name.length; const info = getTokenInfo(ctx, [">"], beforeIndex); return info.index + info.match.length; } return beforeIndex; } function calcExpressionEndOffset(node, ctx) { if (node.children.length) { const lastChild = node.children[node.children.length - 1]; const beforeIndex = getEndOffset(lastChild, ctx); const info2 = getTokenInfo(ctx, ["}"], beforeIndex); return info2.index + info2.match.length; } const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset); return info.index + info.match.length; } function getTokenInfo(ctx, tokens, position) { let lastMatch; for (const t of tokens) { const index = lastMatch ? lastMatch.index + lastMatch.match.length : position; const m = Array.isArray(t) ? matchOfForMulti(t, index) : match(t, index); if (m == null) { throw new ParseError( `Unknown token at ${index}, expected: ${JSON.stringify( t )}, actual: ${JSON.stringify(ctx.code.slice(index, index + 10))}`, index, ctx ); } lastMatch = m; } return lastMatch; function match(token, position2) { const search = typeof token === "string" ? token : token.token; const index = search.trim() === search ? skipSpaces(ctx.code, position2) : position2; if (ctx.code.startsWith(search, index)) { return { match: search, index }; } if (typeof token !== "string") { return matchWithHTMLEntity(token, index); } return null; } function matchOfForMulti(search, position2) { for (const s of search) { const m = match(s, position2); if (m) { return m; } } return null; } function matchWithHTMLEntity(token, position2) { const search = token.token; let codeOffset = position2; let searchOffset = 0; while (searchOffset < search.length) { const searchChar = search[searchOffset]; if (ctx.code[codeOffset] === searchChar) { if (searchChar === "&") { const entityCandidate = ctx.code.slice(codeOffset, codeOffset + 5); if (entityCandidate === "&amp;" || entityCandidate === "&AMP;") { codeOffset += 5; searchOffset++; continue; } } codeOffset++; searchOffset++; continue; } const entity = getHTMLEntity(codeOffset); if (entity?.entity === searchChar) { codeOffset += entity.length; searchOffset++; continue; } return null; } return { match: ctx.code.slice(position2, codeOffset), index: position2 }; function getHTMLEntity(position3) { let codeOffset2 = position3; if (ctx.code[codeOffset2++] !== "&") return null; let entity = ""; const entityDecoder = new EntityDecoder( htmlDecodeTree, (cp) => entity += String.fromCodePoint(cp) ); entityDecoder.startEntity(DecodingMode.Attribute); const length = entityDecoder.write(ctx.code, codeOffset2); if (length < 0) { return null; } if (length === 0) { return null; } return { entity, length }; } } } function skipSpaces(string, position) { const re = /\s*/g; re.lastIndex = position; const match = re.exec(string); if (match) { return match.index + match[0].length; } return position; } function getSortedChildren(parent, code) { if (parent.type === "root" && parent.children[0]?.type === "frontmatter") { const children = [...parent.children]; if (children.every((n) => n.position)) { return children.sort( (a, b) => a.position.start.offset - b.position.start.offset ); } let start = skipSpaces(code, 0); if (code.startsWith("<!", start)) { const frontmatter = children.shift(); const before = []; let first; while (first = children.shift()) { start = skipSpaces(code, start); if (first.type === "comment" && code.startsWith("<!--", start)) { start = code.indexOf("-->", start + 4) + 3; before.push(first); } else if (first.type === "doctype" && code.startsWith("<!", start)) { start = code.indexOf(">", start + 2) + 1; before.push(first); } else { children.unshift(first); break; } } return [...before, frontmatter, ...children]; } } return parent.children; } // src/context/restore.ts var RestoreNodeProcessContext = class { constructor(result, nodeMap) { this.removeTokens = /* @__PURE__ */ new Set(); this.result = result; this.nodeMap = nodeMap; } addRemoveToken(test) { this.removeTokens.add(test); } getParent(node) { return this.nodeMap.get(node) || null; } findToken(startIndex) { const tokens = this.result.ast.tokens || []; return tokens.find((t) => t.range[0] === startIndex) || null; } }; var RestoreContext = class { constructor(ctx) { this.offsets = []; this.virtualFragments = []; this.restoreNodeProcesses = []; this.tokens = []; this.ctx = ctx; } addRestoreNodeProcess(process2) { this.restoreNodeProcesses.push(process2); } addOffset(offset) { this.offsets.push(offset); } addVirtualFragmentRange(start, end) { const peek = this.virtualFragments[this.virtualFragments.length - 1]; if (peek && peek.end === start) { peek.end = end; return; } this.virtualFragments.push({ start, end }); } addToken(type, range) { if (range[0] >= range[1]) { return; } this.tokens.push(this.ctx.buildToken(type, range)); } /** * Restore AST nodes */ restore(result) { const nodeMap = remapLocationsAndGetNodeMap(result, this.tokens, { remapLocation: (n) => this.remapLocation(n), removeToken: (token) => this.virtualFragments.some( (f) => f.start <= token.range[0] && token.range[1] <= f.end ) }); restoreNodes(result, nodeMap, this.restoreNodeProcesses); const firstOffset = Math.min( ...[result.ast.body[0], result.ast.tokens?.[0], result.ast.comments?.[0]].filter((t) => Boolean(t)).map((t) => t.range[0]) ); if (firstOffset < result.ast.range[0]) { result.ast.range[0] = firstOffset; result.ast.loc.start = this.ctx.getLocFromIndex(firstOffset); } } remapLocation(node) { let [start, end] = node.range; const startFragment = this.virtualFragments.find( (f) => f.start <= start && start < f.end ); if (startFragment) { start = startFragment.end; } const endFragment = this.virtualFragments.find( (f) => f.start < end && end <= f.end ); if (endFragment) { end = endFragment.start; if (startFragment === endFragment) { start = startFragment.start; } } if (end < start) { const w = start; start = end; end = w; } const locs = this.ctx.getLocations(...this.getRemapRange(start, end)); node.loc = locs.loc; node.range = locs.range; if (node.start != null) { delete node.start; } if (node.end != null) { delete node.end; } } getRemapRange(start, end) { if (!this.offsets.length) { return [start, end]; } let lastStart = this.offsets[0]; let lastEnd = this.offsets[0]; for (const offset of this.offsets) { if (offset.dist <= start) { lastStart = offset; } if (offset.dist < end) { lastEnd = offset; } else { if (offset.dist === end && start === end) { lastEnd = offset; } break; } } const remapStart = lastStart.original + (start - lastStart.dist); const remapEnd = lastEnd.original + (end - lastEnd.dist); if (remapEnd < remapStart) { return [remapEnd, remapStart]; } return [remapStart, remapEnd]; } }; function remapLocationsAndGetNodeMap(result, restoreTokens, { remapLocation, removeToken }) { const traversed = /* @__PURE__ */ new Map(); traverseNodes(result.ast, { visitorKeys: result.visitorKeys, enterNode: (node, p) => { if (!traversed.has(node)) { traversed.set(node, p); remapLocation(node); } }, leaveNode: (_node) => { } }); const tokens = [...restoreTokens]; for (const token of result.ast.tokens || []) { if (removeToken(token)) { continue; } remapLocation(token); tokens.push(token); } result.ast.tokens = tokens; for (const token of result.ast.comments || []) { remapLocation(token); } return traversed; } function restoreNodes(result, nodeMap, restoreNodeProcesses) { const context = new RestoreNodeProcessContext(result, nodeMap); const restoreNodeProcessesSet = new Set(restoreNodeProcesses); for (const [node] of nodeMap) { if (!restoreNodeProcessesSet.size) { break; } for (const proc of [...restoreNodeProcessesSet]) { if (proc(node, context)) { restoreNodeProcessesSet.delete(proc); } } } if (context.removeTokens.size) { const tokens = result.ast.tokens || []; for (let index = tokens.length - 1; index >= 0; index--) { const token = tokens[index]; for (const rt of context.removeTokens) { if (rt(token)) { tokens.splice(index, 1); context.removeTokens.delete(rt); if (!context.removeTokens.size) { break; } } } } } } // src/context/script.ts var VirtualScriptContext = class { constructor(ctx) { this.script = ""; this.consumedIndex = 0; this.originalCode = ctx.code; this.restoreContext = new RestoreContext(ctx); } skipOriginalOffset(offset) { this.consumedIndex += offset; } skipUntilOriginalOffset(offset) { this.consumedIndex = Math.max(offset, this.consumedIndex); } appendOriginal(index) { if (this.consumedIndex >= index) { return; } this.restoreContext.addOffset({ original: this.consumedIndex, dist: this.script.length }); this.script += this.originalCode.slice(this.consumedIndex, index); this.consumedIndex = index; } appendVirtualScript(virtualFragment) { const start = this.script.length; this.script += virtualFragment; this.restoreContext.addVirtualFragmentRange(start, this.script.length); } }; // src/parser/process-template.ts function processTemplate(ctx, resultTemplate) { let uniqueIdSeq = 0; const usedUniqueIds = /* @__PURE__ */ new Set(); const script = new VirtualScriptContext(ctx); let fragmentOpened = false; function openRootFragment(startOffset) { script.appendVirtualScript("<>"); fragmentOpened = true; script.restoreContext.addRestoreNodeProcess((scriptNode, { result }) => { if (scriptNode.type === AST_NODE_TYPES2.ExpressionStatement && scriptNode.expression.type === AST_NODE_TYPES2.JSXFragment && scriptNode.range[0] === startOffset && result.ast.body.includes(scriptNode)) { const index = result.ast.body.indexOf(scriptNode); const rootFragment = result.ast.body[index] = scriptNode.expression; delete rootFragment.closingFragment; delete rootFragment.openingFragment; rootFragment.type = "AstroFragment"; return true; } return false; }); } walkElements( resultTemplate.ast, ctx.code, // eslint-disable-next-line complexity -- X( (node, [parent]) => { if (node.type === "frontmatter") { const start = node.position.start.offset; if (fragmentOpened) { script.appendVirtualScript("</>;"); fragmentOpened = false; } script.appendOriginal(start); script.skipOriginalOffset(3); const end = getEndOffset(node, ctx); const scriptStart = start + 3; let scriptEnd = end - 3; let endChar; while (scriptStart < scriptEnd - 1 && (endChar = ctx.code[scriptEnd - 1]) && !endChar.trim()) { scriptEnd--; } script.appendOriginal(scriptEnd); script.appendVirtualScript("\n;"); script.skipOriginalOffset(end - scriptEnd); script.restoreContext.add