eslint-plugin-export-scope
Version:
Don't leak LOCAL utils, states, components into the global scope
193 lines • 9.06 kB
JavaScript
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
;