UNPKG

eslint-plugin-export-scope

Version:

Don't leak LOCAL utils, states, components into the global scope

193 lines 9.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateAbsolutePosition = exports.getPartialDirectiveCompletions = exports.generateFileSystemCompletions = exports.generateParentCompletions = exports.parsePartialPathFromQuotes = exports.parseDirectiveFromJSDoc = exports.filterPathsByPartial = exports.createPathCompletion = exports.createCompletionEntry = exports.DIRECTIVE_REGEXES = exports.REGEXES = exports.DIRECTIVE_NAMES = exports.COMPLETION_KIND_MODIFIERS = exports.COMPLETION_SORT_TEXT = void 0; const path_1 = require("path"); const typescript_1 = require("typescript"); const tsUtils_1 = require("./tsUtils"); const pathUtils_1 = require("../pathUtils"); // Constants exports.COMPLETION_SORT_TEXT = "10"; exports.COMPLETION_KIND_MODIFIERS = ""; exports.DIRECTIVE_NAMES = ["@scope", "@scopeDefault", "@scopeException"]; exports.REGEXES = { JSDOC_EMPTY: /(\/\*\*|\s\*)\s*$/, JSDOC_AFTER_AT: /(\/\*\*|\s\*)\s*@$/, JSDOC_PARTIAL_DIRECTIVE: /(\/\*\*|\s\*)\s*(@\w+)$/, QUOTE_PARTIAL: /['"](.*?)$/, }; // Directive-specific regexes (compiled once) exports.DIRECTIVE_REGEXES = { "@scope": /@scope\s+([^\s]*)$/, "@scopeDefault": /@scopeDefault\s+([^\s]*)$/, "@scopeException": /@scopeException\s+([^\s]*)$/, }; // Core completion entry factory const createCompletionEntry = (name, kind, replacementSpan) => ({ name, kind, kindModifiers: exports.COMPLETION_KIND_MODIFIERS, sortText: exports.COMPLETION_SORT_TEXT, ...(replacementSpan && { replacementSpan }), }); exports.createCompletionEntry = createCompletionEntry; // Path-specific completion factory const createPathCompletion = (path, rootDir, config) => { const relativePath = toPosix((0, path_1.relative)(rootDir, path)); const finalPath = config.pathPrefix ? `${config.pathPrefix}${relativePath}` : relativePath; return (0, exports.createCompletionEntry)(finalPath, typescript_1.ScriptElementKind.string, { start: config.startPos, length: config.partialPath.length, }); }; exports.createPathCompletion = createPathCompletion; // Normalize path separators for cross-platform compatibility const toPosix = (path) => path.replace(/\\/g, "/"); // Unified path filtering const filterPathsByPartial = (paths, partialPath, rootDir) => { if (!partialPath) return paths; return paths.filter((path) => { const relativePath = toPosix((0, path_1.relative)(rootDir, path)); return relativePath.startsWith(partialPath); }); }; exports.filterPathsByPartial = filterPathsByPartial; // Parse directive with partial path from JSDoc const parseDirectiveFromJSDoc = (jsDoc, jsDocStartPos) => { // Try each directive type for (const directive of exports.DIRECTIVE_NAMES) { const regex = exports.DIRECTIVE_REGEXES[directive]; const match = jsDoc.match(regex); if (match) { const partialPath = match[1] || ""; // Find position of the captured path text itself, not the directive const relativeStartPos = partialPath ? jsDoc.lastIndexOf(partialPath) : jsDoc.lastIndexOf(directive) + directive.length + 1; const absoluteStartPos = jsDocStartPos + relativeStartPos; return { partialPath, startPos: absoluteStartPos, directive, }; } } return null; }; exports.parseDirectiveFromJSDoc = parseDirectiveFromJSDoc; // Parse partial path from quoted string in scope files const parsePartialPathFromQuotes = (line, lineStartInFile) => { const quoteMatch = line.match(exports.REGEXES.QUOTE_PARTIAL); if (!quoteMatch) return null; const partialPath = quoteMatch[1] || ""; const lineStartPos = line.lastIndexOf(quoteMatch[0]) + 1; // +1 to skip quote const absoluteStartPos = lineStartInFile + lineStartPos; return { partialPath, startPos: absoluteStartPos }; }; exports.parsePartialPathFromQuotes = parsePartialPathFromQuotes; // Generate parent directory completions const generateParentCompletions = (config) => { const { rootDir, importDir, partialPath, startPos } = config; const completions = (0, tsUtils_1.getNewCompletions)(); // Add all ancestor paths (both relative and absolute) const ancestorPaths = (0, pathUtils_1.generateAncestorPaths)(importDir, rootDir); ancestorPaths .filter((path) => path.startsWith(partialPath)) .forEach((path) => { completions.entries.push((0, exports.createCompletionEntry)(path, typescript_1.ScriptElementKind.string, { start: startPos, length: partialPath.length, })); }); // Add wildcard for global access if ("*".startsWith(partialPath)) { completions.entries.push((0, exports.createCompletionEntry)("*", typescript_1.ScriptElementKind.string, { start: startPos, length: partialPath.length, })); } return completions; }; exports.generateParentCompletions = generateParentCompletions; const resolveRelativePath = (importDir, partialPath) => { if (!partialPath.startsWith("../")) { return { effectiveDir: importDir, remainingPath: partialPath }; } const resolvedBase = (0, path_1.resolve)(importDir, partialPath); // if the path ends with "/" or the resolved path would be a directory, // search in that directory. Otherwise, search in the parent with the basename as remaining. if (partialPath.endsWith("/")) { return { effectiveDir: resolvedBase, remainingPath: "" }; } else { const effectiveDir = (0, path_1.dirname)(resolvedBase); const remainingPath = (0, path_1.relative)(effectiveDir, resolvedBase); return { effectiveDir, remainingPath: remainingPath || "" }; } }; const generateFileSystemCompletions = (config) => { const { rootDir, importDir, partialPath, startPos } = config; const { effectiveDir, remainingPath } = resolveRelativePath(importDir, partialPath); const isRelative = partialPath.startsWith("../"); const searchDir = isRelative ? effectiveDir : rootDir; const { filePaths, dirPaths } = (0, tsUtils_1.getAutocompletionFileTree)(searchDir); const filteredDirs = (0, exports.filterPathsByPartial)(dirPaths, isRelative ? remainingPath : partialPath, searchDir); const filteredFiles = (0, exports.filterPathsByPartial)(filePaths, isRelative ? remainingPath : partialPath, searchDir); // Extract the relative path prefix for all relative paths let pathPrefix; if (isRelative) { if (remainingPath === "") { // Complete navigation like "../" or "../folder/" - use the full partialPath pathPrefix = partialPath; } else { // Partial match like "../folder" - extract just the relative navigation part pathPrefix = partialPath.substring(0, partialPath.length - remainingPath.length); } } return { ...(0, tsUtils_1.getNewCompletions)(), entries: [ ...filteredDirs.map((path) => (0, exports.createPathCompletion)(path, searchDir, { partialPath: isRelative ? remainingPath : partialPath, startPos, pathPrefix, })), ...filteredFiles.map((path) => (0, exports.createPathCompletion)(path, searchDir, { partialPath: isRelative ? remainingPath : partialPath, startPos, pathPrefix, })), ], }; }; exports.generateFileSystemCompletions = generateFileSystemCompletions; // Check for partial directive completion (like "@scop") const getPartialDirectiveCompletions = (jsDoc, jsDocStartPos, completions) => { const match = jsDoc.match(exports.REGEXES.JSDOC_PARTIAL_DIRECTIVE); if (!match || !match[2]) return null; const partialDirective = match[2]; // "@scop" let hasMatches = false; exports.DIRECTIVE_NAMES.forEach((directive) => { if (directive.startsWith(partialDirective) && directive !== partialDirective) { const directiveStartPos = jsDoc.lastIndexOf(partialDirective); const absoluteStartPos = jsDocStartPos + directiveStartPos; completions.entries.push((0, exports.createCompletionEntry)(directive, typescript_1.ScriptElementKind.keyword, { start: absoluteStartPos, length: partialDirective.length, })); hasMatches = true; } }); return hasMatches ? completions : null; }; exports.getPartialDirectiveCompletions = getPartialDirectiveCompletions; // Calculate absolute position for scope file text const calculateAbsolutePosition = (fileTextToPosition, lineStartPos) => { const linesBeforeCurrent = fileTextToPosition.split("\n").slice(0, -1); return linesBeforeCurrent.reduce((acc, line) => acc + line.length + 1, 0) + lineStartPos; }; exports.calculateAbsolutePosition = calculateAbsolutePosition; //# sourceMappingURL=completionUtils.js.map