eslint-plugin-esm
Version:
ESLint plugin for linting ESM (import/export syntax)
56 lines • 8.06 kB
JavaScript
import path from "node:path";
import process from "node:process";
import { createRule, getRuleName, getSourceType } from "../common.js";
/**
* Key is the source file, value is its imports.
*/
const store = new Map();
function isCircular(currentFile, visited = new Set()) {
if (visited.has(currentFile)) {
return [true, [...visited]];
}
visited.add(currentFile);
const dependencies = store.get(currentFile) ?? new Set();
for (const dependency of dependencies) {
const [isCircularResult, circularPaths] = isCircular(path.resolve(path.dirname(currentFile), dependency), new Set(visited));
if (isCircularResult) {
return [isCircularResult, circularPaths];
}
}
return [false, []];
}
/**
* Note: This rule do not support importing files without extensions and directory imports.
*/
export const noCircularImports = createRule({
name: getRuleName(import.meta.url),
message: "Circular imports are not allowed.",
create: (context) => {
const filePath = context.filename;
const imports = store.get(filePath) ?? new Set();
store.set(filePath, imports);
return {
ImportDeclaration: (node) => {
if (typeof node.source.value !== "string" ||
getSourceType(node.source.value) !== "local")
return;
imports.add(node.source.value);
},
"ImportDeclaration:exit": (node) => {
if (typeof node.source.value !== "string" ||
getSourceType(node.source.value) !== "local")
return;
const [isCircularResult, circularPaths] = isCircular(path.resolve(path.dirname(filePath), node.source.value), new Set([filePath]));
if (isCircularResult) {
context.report({
node,
message: `Circular import detected: ${circularPaths
.map((p) => path.relative(process.cwd(), p))
.join(" -> ")}`,
});
}
},
};
},
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm8tY2lyY3VsYXItaW1wb3J0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9uby1jaXJjdWxhci1pbXBvcnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRXRFOztHQUVHO0FBQ0gsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7QUFFN0MsU0FBUyxVQUFVLENBQ2pCLFdBQW1CLEVBQ25CLFVBQVUsSUFBSSxHQUFHLEVBQVU7SUFFM0IsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7UUFDN0IsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUM5QixDQUFDO0lBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUV6QixNQUFNLFlBQVksR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxJQUFJLElBQUksR0FBRyxFQUFVLENBQUM7SUFDakUsS0FBSyxNQUFNLFVBQVUsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUN0QyxNQUFNLENBQUMsZ0JBQWdCLEVBQUUsYUFBYSxDQUFDLEdBQUcsVUFBVSxDQUNsRCxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLEVBQUUsVUFBVSxDQUFDLEVBQ25ELElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUNqQixDQUFDO1FBQ0YsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3JCLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDckIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsVUFBVSxDQUFDO0lBQzFDLElBQUksRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDbEMsT0FBTyxFQUFFLG1DQUFtQztJQUM1QyxNQUFNLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUNsQixNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ2xDLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksSUFBSSxHQUFHLEVBQVUsQ0FBQztRQUN6RCxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUU3QixPQUFPO1lBQ0wsaUJBQWlCLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRTtnQkFDMUIsSUFDRSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLFFBQVE7b0JBQ3JDLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLE9BQU87b0JBRTVDLE9BQU87Z0JBQ1QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ2pDLENBQUM7WUFFRCx3QkFBd0IsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFO2dCQUNqQyxJQUNFLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssUUFBUTtvQkFDckMsYUFBYSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssT0FBTztvQkFFNUMsT0FBTztnQkFFVCxNQUFNLENBQUMsZ0JBQWdCLEVBQUUsYUFBYSxDQUFDLEdBQUcsVUFBVSxDQUNsRCxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFDdkQsSUFBSSxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUNwQixDQUFDO2dCQUNGLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztvQkFDckIsT0FBTyxDQUFDLE1BQU0sQ0FBQzt3QkFDYixJQUFJO3dCQUNKLE9BQU8sRUFBRSw2QkFBNkIsYUFBYTs2QkFDaEQsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQzs2QkFDM0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFO3FCQUNsQixDQUFDLENBQUM7Z0JBQ0wsQ0FBQztZQUNILENBQUM7U0FDRixDQUFDO0lBQ0osQ0FBQztDQUNGLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCBwcm9jZXNzIGZyb20gXCJub2RlOnByb2Nlc3NcIjtcbmltcG9ydCB7IGNyZWF0ZVJ1bGUsIGdldFJ1bGVOYW1lLCBnZXRTb3VyY2VUeXBlIH0gZnJvbSBcIi4uL2NvbW1vbi50c1wiO1xuXG4vKipcbiAqIEtleSBpcyB0aGUgc291cmNlIGZpbGUsIHZhbHVlIGlzIGl0cyBpbXBvcnRzLlxuICovXG5jb25zdCBzdG9yZSA9IG5ldyBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4oKTtcblxuZnVuY3Rpb24gaXNDaXJjdWxhcihcbiAgY3VycmVudEZpbGU6IHN0cmluZyxcbiAgdmlzaXRlZCA9IG5ldyBTZXQ8c3RyaW5nPigpLFxuKTogW2Jvb2xlYW4sIHN0cmluZ1tdXSB7XG4gIGlmICh2aXNpdGVkLmhhcyhjdXJyZW50RmlsZSkpIHtcbiAgICByZXR1cm4gW3RydWUsIFsuLi52aXNpdGVkXV07XG4gIH1cbiAgdmlzaXRlZC5hZGQoY3VycmVudEZpbGUpO1xuXG4gIGNvbnN0IGRlcGVuZGVuY2llcyA9IHN0b3JlLmdldChjdXJyZW50RmlsZSkgPz8gbmV3IFNldDxzdHJpbmc+KCk7XG4gIGZvciAoY29uc3QgZGVwZW5kZW5jeSBvZiBkZXBlbmRlbmNpZXMpIHtcbiAgICBjb25zdCBbaXNDaXJjdWxhclJlc3VsdCwgY2lyY3VsYXJQYXRoc10gPSBpc0NpcmN1bGFyKFxuICAgICAgcGF0aC5yZXNvbHZlKHBhdGguZGlybmFtZShjdXJyZW50RmlsZSksIGRlcGVuZGVuY3kpLFxuICAgICAgbmV3IFNldCh2aXNpdGVkKSxcbiAgICApO1xuICAgIGlmIChpc0NpcmN1bGFyUmVzdWx0KSB7XG4gICAgICByZXR1cm4gW2lzQ2lyY3VsYXJSZXN1bHQsIGNpcmN1bGFyUGF0aHNdO1xuICAgIH1cbiAgfVxuICByZXR1cm4gW2ZhbHNlLCBbXV07XG59XG5cbi8qKlxuICogTm90ZTogVGhpcyBydWxlIGRvIG5vdCBzdXBwb3J0IGltcG9ydGluZyBmaWxlcyB3aXRob3V0IGV4dGVuc2lvbnMgYW5kIGRpcmVjdG9yeSBpbXBvcnRzLlxuICovXG5leHBvcnQgY29uc3Qgbm9DaXJjdWxhckltcG9ydHMgPSBjcmVhdGVSdWxlKHtcbiAgbmFtZTogZ2V0UnVsZU5hbWUoaW1wb3J0Lm1ldGEudXJsKSxcbiAgbWVzc2FnZTogXCJDaXJjdWxhciBpbXBvcnRzIGFyZSBub3QgYWxsb3dlZC5cIixcbiAgY3JlYXRlOiAoY29udGV4dCkgPT4ge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gY29udGV4dC5maWxlbmFtZTtcbiAgICBjb25zdCBpbXBvcnRzID0gc3RvcmUuZ2V0KGZpbGVQYXRoKSA/PyBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBzdG9yZS5zZXQoZmlsZVBhdGgsIGltcG9ydHMpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIEltcG9ydERlY2xhcmF0aW9uOiAobm9kZSkgPT4ge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgdHlwZW9mIG5vZGUuc291cmNlLnZhbHVlICE9PSBcInN0cmluZ1wiIHx8XG4gICAgICAgICAgZ2V0U291cmNlVHlwZShub2RlLnNvdXJjZS52YWx1ZSkgIT09IFwibG9jYWxcIlxuICAgICAgICApXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBpbXBvcnRzLmFkZChub2RlLnNvdXJjZS52YWx1ZSk7XG4gICAgICB9LFxuXG4gICAgICBcIkltcG9ydERlY2xhcmF0aW9uOmV4aXRcIjogKG5vZGUpID0+IHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIHR5cGVvZiBub2RlLnNvdXJjZS52YWx1ZSAhPT0gXCJzdHJpbmdcIiB8fFxuICAgICAgICAgIGdldFNvdXJjZVR5cGUobm9kZS5zb3VyY2UudmFsdWUpICE9PSBcImxvY2FsXCJcbiAgICAgICAgKVxuICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBjb25zdCBbaXNDaXJjdWxhclJlc3VsdCwgY2lyY3VsYXJQYXRoc10gPSBpc0NpcmN1bGFyKFxuICAgICAgICAgIHBhdGgucmVzb2x2ZShwYXRoLmRpcm5hbWUoZmlsZVBhdGgpLCBub2RlLnNvdXJjZS52YWx1ZSksXG4gICAgICAgICAgbmV3IFNldChbZmlsZVBhdGhdKSxcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKGlzQ2lyY3VsYXJSZXN1bHQpIHtcbiAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICBub2RlLFxuICAgICAgICAgICAgbWVzc2FnZTogYENpcmN1bGFyIGltcG9ydCBkZXRlY3RlZDogJHtjaXJjdWxhclBhdGhzXG4gICAgICAgICAgICAgIC5tYXAoKHApID0+IHBhdGgucmVsYXRpdmUocHJvY2Vzcy5jd2QoKSwgcCkpXG4gICAgICAgICAgICAgIC5qb2luKFwiIC0+IFwiKX1gLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgIH07XG4gIH0sXG59KTtcbiJdfQ==