var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/suggest-move-out-utils.ts var suggest_move_out_utils_exports = {}; __export(suggest_move_out_utils_exports, { RULE: () => RULE, rule: () => rule }); module.exports = __toCommonJS(suggest_move_out_utils_exports); // src/utils/options.ts var schema = [ { type: "object", properties: { files: { type: "array", items: { type: "string" } }, ignores: { type: "array", items: { type: "string" } }, index: { type: "string", default: "index" }, utils: { type: "string", default: "utils" }, src: { type: "string", default: "src" }, test: { type: "string", default: "test" }, limit: { type: "number" } }, additionalProperties: false } ]; var defaultOptions = { files: ["src/**/*.ts", "test/**/*.test.ts"], ignores: ["dist/**", "coverage/**"], index: "index", utils: "utils", src: "src", test: "test", limit: 3 }; // src/suggest-move-out-utils.ts var import_utils = require("@typescript-eslint/utils"); // src/context/index.ts var import_node_path4 = __toESM(require("path"), 1); // src/path/index.ts var import_node_path = __toESM(require("path"), 1); var import_minimatch = require("minimatch"); // src/path/patterns.ts var patterns = { root: ({ src }) => `${src}/*`, src: ({ src }) => `${src}/**`, module: ({ src }) => `${src}/*/*`, moduleIndex: ({ src, index, utils }, ext) => `${src}/!(${utils})/${index}${ext}`, moduleUtil: ({ src, index, utils }, ext) => `${src}/!(${utils})/!(${index})${ext}`, utils: ({ src, utils }) => `${src}/${utils}/**`, test: ({ test }) => `${test}/**`, testRoot: ({ test }) => `${test}/*`, testModule: ({ test }) => `${test}/*/*`, testSpecial: ({ test }) => `${test}/__*__/*`, json: ({}) => `**/*.json` }; // src/path/index.ts var Path = class _Path { dirname; relative; name; split; filename; options; ext; cwd; parent; length; indexFile; _imports; _importedBy; constructor(opt) { const { filename, options, ext, cwd, parent } = opt; this.filename = filename; this.options = options; this.ext = ext; this.cwd = cwd; if (!import_node_path.default.isAbsolute(filename)) { throw new Error("filename must be an absolute path"); } this.parent = parent; this.filename = filename; this.name = import_node_path.default.basename(filename).split(".")[0]; this.dirname = import_node_path.default.dirname(filename); this.relative = import_node_path.default.relative(cwd, filename); this.split = this.relative.split(import_node_path.default.sep); this.length = this.split.length; this.indexFile = options.index + (this.parent?.ext || ext); } // get imports() { // if (!this._imports) { // throw new Error('imports accessed before being set') // } // return this._imports // } get importedBy() { if (!this._importedBy) { throw new Error("imports accessed before being set"); } return this._importedBy; } addImports(imports) { this._imports = imports.map((v) => this.create(v)); return this; } addImportedBy(importedBy) { this._importedBy = importedBy.map((v) => this.create(v)); return this; } isModule() { return (0, import_minimatch.minimatch)(this.relative, patterns.module(this.options)); } isModuleIndex() { return (0, import_minimatch.minimatch)(this.relative, patterns.moduleIndex(this.options, this.ext)); } isModuleUtil() { return (0, import_minimatch.minimatch)(this.relative, patterns.moduleUtil(this.options, this.ext)); } isRoot() { return (0, import_minimatch.minimatch)(this.relative, patterns.root(this.options)); } // isSrc() { // return minimatch(this.filename, patterns.src(this.options)) // } isUtils() { return (0, import_minimatch.minimatch)(this.relative, patterns.utils(this.options)); } isJson() { return (0, import_minimatch.minimatch)(this.relative, patterns.json(this.options), { nocase: true }); } isTest() { return (0, import_minimatch.minimatch)(this.relative, patterns.test(this.options)); } isTestModule() { return (0, import_minimatch.minimatch)(this.relative, patterns.testModule(this.options)); } isTestRoot() { return (0, import_minimatch.minimatch)(this.relative, patterns.testRoot(this.options)); } isTestSpecial() { return (0, import_minimatch.minimatch)(this.relative, patterns.testSpecial(this.options)); } isModuleUtilOrUtils() { return this.isModuleUtil() || this.isUtils(); } isSibling() { const { parent } = this; if (parent) { return this.dirname === parent.dirname; } throw new Error("isSibling called without context"); } isValidModuleIndexImport() { return this.isModuleIndex() || this.isUtils() || this.isSibling() || this.isJson(); } isValidModuleUtilImport() { return this.isJson(); } modPlaces() { return [this.parent?.mod(), this.mod()].filter(Boolean).map((v) => `"${v}"`).join(" and "); } extraData = {}; addData(data) { this.extraData = { ...this.extraData, ...data }; return this; } data() { return { ...this.extraData, ...this.options, depth: this.length, depthOver: this.length - this.options.limit, modPlaces: this.modPlaces(), indexFile: this.parent?.indexFile || this.indexFile, modForParent: this.parent?.mod(), modForChild: this.mod(), relativeParent: this.parent?.relative, relativeChild: this.relative }; } create(filename) { const { options, cwd, ext } = this; return new _Path({ filename, options, ext, cwd, parent: this }); } mod() { return this.split[1]; } _shebang; addShebang(value) { this._shebang = value; } shebang() { if (this._shebang === void 0) { throw new Error("shebang accessed before being set"); } return this._shebang; } isUnused() { if (this.isRoot()) return; if (this.isTest()) return; if (this.isJson()) return; if (this.shebang()) return; if (this.importedBy.length === 0) return this; } createPath(filename) { const { options, cwd, ext } = this; return new _Path({ filename, options, ext, cwd, parent: this }); } import(importPath) { let filename = import_node_path.default.join(this.dirname, importPath); if (!import_node_path.default.extname(filename)) { filename += this.ext; } return this.createPath(filename); } }; // src/paths/files.ts var import_glob = require("glob"); var import_node_path2 = __toESM(require("path"), 1); function filesSync(cwd, config) { const { files, ignores } = config; const ignorePatterns = [...ignores, "node_modules/**"]; const allFiles = (0, import_glob.globSync)(files, { ignore: ignorePatterns, cwd }); return allFiles.map((v) => import_node_path2.default.resolve(cwd, v)); } // src/paths/find-one.ts function findOne(items, predicate) { const results = items.filter(predicate); if (results.length === 0) { throw new Error(`No results found`); } if (results.length !== 1) { throw new Error(`Multiple results found`); } return results[0]; } // src/paths/imports.ts var ts = __toESM(require("typescript"), 1); function getImports(fileName) { const imports = []; const fileContent = ts.sys.readFile(fileName); if (!fileContent) { throw new Error(`Could not read file: ${fileName}`); } const sourceFile = ts.createSourceFile(fileName, fileContent, ts.ScriptTarget.Latest, true); function visit(node) { if (ts.isImportDeclaration(node)) { const importPath = node.moduleSpecifier.text; imports.push(importPath); } ts.forEachChild(node, visit); } visit(sourceFile); return imports; } // src/utils/local-import.ts var import_node_path3 = __toESM(require("path"), 1); var isLocal = (v) => v.startsWith(".") || v.startsWith(import_node_path3.default.sep); // src/utils/depmap.ts function createDependencyMap(dependencies, getKey = (item) => typeof item === "string" ? item : "") { const dependencyMap = /* @__PURE__ */ new Map(); const dependentMap = /* @__PURE__ */ new Map(); Object.entries(dependencies).forEach(([key, deps]) => { dependencyMap.set(key, deps); deps.forEach((dep) => { const depKey = getKey(dep); if (!dependentMap.has(depKey)) { dependentMap.set(depKey, []); } dependentMap.get(depKey).push(key); }); }); return Array.from(dependencyMap.keys()).map((key) => ({ key, dependencies: dependencyMap.get(key) || [], dependents: dependentMap.get(key) || [] })); } // src/paths/index.ts var import_node_fs = __toESM(require("fs"), 1); var vanityOr = (v) => { return v.filter(Boolean).map((v2) => `"${v2}"`).join(" or "); }; var Paths = class _Paths { paths; options; ext; cwd; parent; constructor(opt) { this.paths = opt.paths; this.options = opt.options; this.ext = opt.ext; this.cwd = opt.cwd; this.parent = opt.parent; } addDependencyMapInfo() { const { paths } = this; const keyedImports = Object.fromEntries( paths.map((parent) => [ parent.filename, getImports(parent.filename).filter(isLocal).map((filename) => parent.import(filename).filename) ]) ); createDependencyMap(keyedImports).map(({ key, dependencies, dependents }) => { this.find(key).addImportedBy(dependents).addImports(dependencies); }); return this; } addFileContentInfo() { this.paths.forEach((file) => { const fileContent = import_node_fs.default.readFileSync(file.filename, "utf8"); const hasShebang = fileContent.startsWith("#!"); file.addShebang(hasShebang); }); return this; } find(filename) { const callback = typeof filename === "function" ? filename : (path5) => path5.filename === filename; return findOne(this.paths, callback); } findMany(filename) { const callback = typeof filename === "function" ? filename : (path5) => path5.filename === filename; return this.paths.filter(callback); } static fromFiles(opt) { const paths = opt.files.map((filename) => new Path({ ...opt, filename })); return new _Paths({ ...opt, paths }); } static fromSync(opt) { const { cwd, options } = opt; const files = filesSync(cwd, options); return this.fromFiles({ ...opt, files }); } // returns Path if the is module and doesn't have an index isMissingIndex(filename) { const path5 = this.find(filename); if (!path5.isModule()) return; const hasIndex = this.paths.some((p) => p.isModuleIndex() && p.mod() === path5.mod()); if (!hasIndex) { return path5; } } // requires a test path to match a corresponding src path isInvalidTest(filename) { const file = this.find(filename); if (file.isTestSpecial()) return; if (file.isTestRoot()) { const match = this.findMany((v) => v.isRoot() && v.name === file.name); if (match.length) return; const locations = this.findMany((v) => v.isModule() && v.name == file.name).map((v) => v.mod()); const message = locations.length ? `Test in the root has no corresponding "${this.options.src}" file possibly ${vanityOr(locations)}` : `Test in the root has no corresponding "${this.options.src}" file`; return file.addData({ message }); } else if (file.isTestModule()) { const match = this.findMany((v) => v.isModule() && v.mod() === file.mod() && v.name == file.name); if (match.length) return; const inOtherModule = this.findMany((v) => v.isModule() && v.name == file.name).map((v) => v.mod()); const message = inOtherModule.length ? `Test nested in module has no corresponding "${this.options.src}" file possibly in ${vanityOr(inOtherModule)}` : `Test nested in module has no corresponding "${this.options.src}" file`; return file.addData({ message }); } } // returns Path if the is util and only has 1 mod importing it isInvalidUtil(filename) { const path5 = this.find(filename); if (!path5.isUtils()) return; const importedMods = new Set(path5.importedBy.map((importedBy) => importedBy.mod())); if (importedMods.size !== 1) return; return path5.addData({ mod: [...importedMods][0] }); } unused() { return this.paths.filter((path5) => path5.isUnused()); } }; // src/context/index.ts var plugin = "treekeeper"; var Context = class extends Path { filename; options; indexFile; plugin; rule; defaultOptions; constructor(context, plugin2, rule2, defaultOptions2) { const filename = context.filename; const cwd = context.cwd; const ext = import_node_path4.default.extname(filename); const pluginSettings = context?.settings?.[`${plugin2}`] || {}; const ruleSettings = context?.settings?.[`${plugin2}/${rule2}`] || {}; const userOptions = Object.assign({}, ...context.options); const options = { ...defaultOptions2, ...pluginSettings, ...ruleSettings, ...userOptions }; super({ filename, options, cwd, ext }); this.filename = filename; this.options = options; this.indexFile = options.index + ext; this.plugin = plugin2; this.rule = rule2; this.defaultOptions = defaultOptions2; } projectFiles() { return Paths.fromSync({ options: this.options, ext: this.ext, cwd: this.cwd, parent: this }); } dependencyMap() { return this.projectFiles().addDependencyMapInfo(); } filesWithContent() { return this.projectFiles().addDependencyMapInfo().addFileContentInfo(); } }; // src/suggest-move-out-utils.ts var RULE = "suggest-move-out-utils"; var cache; var rule = import_utils.ESLintUtils.RuleCreator.withoutDocs({ meta: { messages: { [RULE]: `File should be moved from "{{ utils }}" to "{{ mod }}" because it's only used there.` }, schema, type: "problem" }, defaultOptions: [defaultOptions], create(context) { const ctx = new Context(context, plugin, RULE, defaultOptions); if (!cache) { cache = ctx.dependencyMap(); } if (!ctx.isUtils) return {}; return { Program(node) { const path5 = cache?.isInvalidUtil(context.filename); if (path5) { context.report({ node, messageId: RULE, data: path5.data() }); } } }; } }); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { RULE, rule });