eslint-plugin-export-scope
Version:
Don't leak LOCAL utils, states, components into the global scope
131 lines • 5.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkIsImportable = void 0;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const typescript_1 = require("typescript");
const pathUtils_1 = require("./pathUtils");
const constants_1 = require("./constants");
const parseScopeFile_1 = require("./parseScopeFile");
const checkIsImportable = ({ tsProgram, importPath, exportPath, exportName, }) => {
if (!importPath || !exportPath || exportPath.includes("node_modules"))
return true;
const exportFile = tsProgram.getSourceFile(exportPath);
const exportDir = path_1.default.dirname(exportPath);
let scope;
if (!exportFile)
return true;
const isIndexFile = path_1.default.parse(exportFile.fileName).name === "index";
getLocalScope: {
if (!exportName)
break getLocalScope;
const typeChecker = tsProgram.getTypeChecker();
const fileSymbol = typeChecker.getSymbolAtLocation(exportFile);
const exports = fileSymbol && typeChecker.getExportsOfModule(fileSymbol);
let exportSymbol = exports?.find((x) => x.name === exportName);
if (!exportSymbol)
break getLocalScope;
if (exportName !== "default" && exportSymbol.flags & typescript_1.SymbolFlags.Alias) {
exportSymbol = typeChecker.getImmediateAliasedSymbol(exportSymbol);
}
const jsDocTags = exportSymbol?.getJsDocTags();
if (!jsDocTags)
break getLocalScope;
for (const tag of jsDocTags) {
if (tag.name === "scopeException") {
const exception = tag.text?.at(0)?.text;
if (!exception)
continue;
const exceptionFullPath = (0, pathUtils_1.getFullScopePath)(exportDir, exception);
if (exceptionFullPath && (0, pathUtils_1.isSubPath)(exceptionFullPath, importPath)) {
return true;
}
}
if (tag.name === "scope") {
scope = tag.text?.at(0)?.text;
}
}
}
getFileScope: {
if (scope)
break getFileScope;
const firstStatementEndIndex = exportFile.statements[0]?.getEnd() ?? -1;
const fileComments = exportFile.getFullText().slice(0, firstStatementEndIndex);
[, scope] = fileComments.match(/@scopeDefault\s+([^\s]+)/) ?? [];
}
getFolderScope: {
if (scope)
break getFolderScope;
let scopeFilePath = null;
const rootDir = (0, pathUtils_1.getRootDir)(exportDir);
// First, try regular scope files in current directory only
for (const fileName of [constants_1.SCOPE_TS_FILE_NAME, constants_1.SCOPE_JS_FILE_NAME]) {
const filePath = path_1.default.join(exportDir, fileName);
if (fs_1.default.existsSync(filePath)) {
scopeFilePath = filePath;
break;
}
}
// If index file, also check parent directory for regular scope files
if (!scopeFilePath && isIndexFile) {
const parentDir = path_1.default.dirname(exportDir);
for (const fileName of [constants_1.SCOPE_TS_FILE_NAME, constants_1.SCOPE_JS_FILE_NAME]) {
const filePath = path_1.default.join(parentDir, fileName);
if (fs_1.default.existsSync(filePath)) {
scopeFilePath = filePath;
break;
}
}
}
// Then, recursively look for default scope files in current directory and ancestors
if (!scopeFilePath) {
let currentDir = exportDir;
while (currentDir !== path_1.default.dirname(currentDir)) {
for (const fileName of [constants_1.SCOPE_DEFAULT_TS_FILE_NAME, constants_1.SCOPE_DEFAULT_JS_FILE_NAME]) {
const filePath = path_1.default.join(currentDir, fileName);
if (fs_1.default.existsSync(filePath)) {
scopeFilePath = filePath;
break;
}
}
if (scopeFilePath)
break;
// Stop when we reach or go beyond the root directory
if (rootDir && currentDir === rootDir)
break;
currentDir = path_1.default.dirname(currentDir);
}
}
// Parse the found scope file
if (!scopeFilePath)
break getFolderScope;
try {
const exports = (0, parseScopeFile_1.parseScopeFile)(scopeFilePath);
scope = exports.scope;
for (const exception of exports.exceptions) {
const exceptionFullPath = (0, pathUtils_1.getFullScopePath)(exportDir, exception);
if (!exceptionFullPath)
continue;
if ((0, pathUtils_1.isSubPath)(exceptionFullPath, importPath)) {
return true;
}
}
}
catch {
break getFolderScope;
}
}
// handles index files
scope ?? (scope = isIndexFile ? ".." : ".");
if (scope === "*")
return true;
const fullScopePath = (0, pathUtils_1.getFullScopePath)(exportDir, scope);
if (!fullScopePath)
return true;
return (0, pathUtils_1.isSubPath)(fullScopePath, importPath);
};
exports.checkIsImportable = checkIsImportable;
//# sourceMappingURL=checkIsImportable.js.map