diff --git a/.vscode/settings.json b/.vscode/settings.json index be99108..c3dd14b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,5 @@ { - "cSpell.words": [ - "astrojs", - "Globified", - "globify" - ], + "cSpell.words": ["astrojs", "Globified", "globify"], "explorer.fileNesting.patterns": { "index.js": "*.js" } diff --git a/src/astro.cts b/src/astro.cts index 7e3cd5c..a7fd076 100644 --- a/src/astro.cts +++ b/src/astro.cts @@ -12,7 +12,7 @@ export const astroConfig: Linter.ConfigOverride = { plugins: ["astro", "only-warn"], extends: ["plugin:astro/recommended"], rules: { - ...pluginImportAstroRulesExtra + ...pluginImportAstroRulesExtra, }, globals: { astroHTML: "readonly", diff --git a/src/typescript.cts b/src/typescript.cts index 9692a44..376d1b2 100644 --- a/src/typescript.cts +++ b/src/typescript.cts @@ -2,17 +2,30 @@ import { eslintRulesExtra } from "./official-eslint-rules.cjs" import { pluginImportRulesExtra, pluginImportTypeScriptRulesExtra } from "./plugin-import-rules.cjs" import { pluginNodeRules } from "./plugin-node-rules.cjs" import makeSynchronous from "make-synchronous" -import { findOneFile } from "./utils.cjs" +import { findFilesForGroups } from "./utils.cjs" import type { GlobifiedEntry } from "globify-gitignore" import { Linter } from "eslint" const tsFiles = ["**/*.tsx", "**/*.ts", "**/*.mts", "**/*.cts"] -const project = ["**/tsconfig.json", "!**/node_modules/**/tsconfig.json"] +const tscConfigFiles = ["**/tsconfig.json", "!**/node_modules/**/tsconfig.json"] -function globifyGitIgnoreFileWithDeps(cwd: string, include: boolean) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { globifyGitIgnoreFile } = require("globify-gitignore") as typeof import("globify-gitignore") // prettier-ignore - return globifyGitIgnoreFile(cwd, include) +async function globifyGitIgnoreFileWithDeps(cwd: string, include: boolean) { + try { + // import in the function to allow makeSynchronous to work + /* eslint-disable @typescript-eslint/no-var-requires */ + const { globifyGitIgnoreFile } = require("globify-gitignore") as typeof import("globify-gitignore") // prettier-ignore + const { existsSync } = require("fs") as typeof import("fs") + const { join } = require("path") as typeof import("path") + /* eslint-enable @typescript-eslint/no-var-requires */ + + if (!existsSync(join(cwd, ".gitignore"))) { + return [] + } + return await globifyGitIgnoreFile(cwd, include) + } catch (error) { + console.error(error) + return [] + } } const globifyGitIgnoreFileSync = makeSynchronous(globifyGitIgnoreFileWithDeps) as ( cwd: string, @@ -41,18 +54,10 @@ function disableProjectBasedRules() { ) // check if there are any ts files - const hasTsFile = findOneFile(cwd, tsFiles, ignore) - - // return if there are no ts files - if (!hasTsFile) { - return true - } - - // check if there is a tsconfig.json file - const hasTsConfig = findOneFile(cwd, project, ignore) + const [hasTscConfig, hasTsFile] = findFilesForGroups(cwd, tscConfigFiles, tsFiles, ignore) // if there is no tsconfig.json file, but there are ts files, disable the project-based rules - const disable = !hasTsConfig && hasTsFile + const disable = !hasTscConfig && hasTsFile if (disable) { console.warn( @@ -119,7 +124,7 @@ export const tsConfig: Linter.ConfigOverride = { files: tsFiles, parser: "@typescript-eslint/parser", parserOptions: { - project, + project: tscConfigFiles, createDefaultProgram: true, // otherwise Eslint will error if a ts file is not covered by one of the tsconfig.json files }, plugins: ["@typescript-eslint", "node", "import", "only-warn"], diff --git a/src/utils.cts b/src/utils.cts index 649649c..3c2d012 100644 --- a/src/utils.cts +++ b/src/utils.cts @@ -2,23 +2,47 @@ import { readdirSync } from "fs" import { join } from "path" import { default as anymatch } from "anymatch" -export function findOneFile(cwd: string, search: string[], ignored: string[]) { +export function findFilesForGroups( + cwd: string, + earlyExitSearchGroup: string[], + exhaustiveSearchGroup: string[], + ignored: string[], +) { + const status = [false, false] + searchDirectory(cwd, status, earlyExitSearchGroup, exhaustiveSearchGroup, ignored) + + return status +} + +function searchDirectory( + directory: string, + status: boolean[], + earlyExitSearchGroup: string[], + exhaustiveSearchGroup: string[], + ignored: string[], +): boolean { // recursively search the current folder for a file with the given fileEnding, ignoring the given folders, and return true as soon as one is found - const files = readdirSync(cwd, { withFileTypes: true, recursive: false }) + const files = readdirSync(directory, { withFileTypes: true, recursive: false }) for (const file of files) { - const path = join(cwd, file.name) + const path = join(directory, file.name) if (file.isDirectory()) { + // if the folder is not ignored, search it recursively if (!anymatch(ignored, path)) { - // if the folder is not ignored, search it recursively - const found = findOneFile(path, search, ignored) - if (found) { - return true + if (searchDirectory(path, status, earlyExitSearchGroup, exhaustiveSearchGroup, ignored)) { + return true // exit } } - } else if (anymatch(search, path)) { - // if the file ends with the given fileEnding, return true - return true + } else { + // check the early exit search group first + status[0] = status[0] || anymatch(exhaustiveSearchGroup, path) + if (status[0]) { + return true // exit + } + + // check the exhaustive search group + status[1] = status[1] || anymatch(exhaustiveSearchGroup, path) } } - return false + + return false // continue }