From 211cb65c785e6c7cb5ffce242d032b69beb9a75e Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 15 Aug 2024 16:10:56 -0700 Subject: [PATCH 1/3] fix: do not fail on gitignore failures --- .vscode/settings.json | 6 +----- src/astro.cts | 2 +- src/typescript.cts | 21 +++++++++++++++++---- 3 files changed, 19 insertions(+), 10 deletions(-) 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..6b64ab6 100644 --- a/src/typescript.cts +++ b/src/typescript.cts @@ -9,10 +9,23 @@ import { Linter } from "eslint" const tsFiles = ["**/*.tsx", "**/*.ts", "**/*.mts", "**/*.cts"] const project = ["**/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, From a3e36fead7c848d8d2d01a4c24a154ab7ddae346 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 15 Aug 2024 16:27:43 -0700 Subject: [PATCH 2/3] fix: optimize TypeScript project disable search --- src/typescript.cts | 13 +++++-------- src/utils.cts | 29 ++++++++++++++++++----------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/typescript.cts b/src/typescript.cts index 6b64ab6..bab8377 100644 --- a/src/typescript.cts +++ b/src/typescript.cts @@ -2,12 +2,12 @@ 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"] async function globifyGitIgnoreFileWithDeps(cwd: string, include: boolean) { try { @@ -54,18 +54,15 @@ function disableProjectBasedRules() { ) // check if there are any ts files - const hasTsFile = findOneFile(cwd, tsFiles, ignore) + const [hasTscConfig, hasTsFile] = findFilesForGroups(cwd, [tscConfigFiles, 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) - // 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( @@ -132,7 +129,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..88258df 100644 --- a/src/utils.cts +++ b/src/utils.cts @@ -2,23 +2,30 @@ import { readdirSync } from "fs" import { join } from "path" import { default as anymatch } from "anymatch" -export function findOneFile(cwd: string, search: string[], ignored: string[]) { - // 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 }) +export function findFilesForGroups(cwd: string, searchGroups: string[][], ignored: string[]) { + const status = searchGroups.map(() => false); + searchDirectory(cwd, status, searchGroups, ignored); + + return status +} + +function searchDirectory(directory: string, status: boolean[], searchGroups: string[][], ignored: string[]) { + // 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(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 (!anymatch(ignored, path)) { // if the folder is not ignored, search it recursively - const found = findOneFile(path, search, ignored) - if (found) { - return true + searchDirectory(path, status, searchGroups, ignored); + } + } else { + // for each search group, check if the file matches any of the patterns + for (const [index, searchGroup] of searchGroups.entries()) { + if (!status[index] && anymatch(searchGroup, path)) { + status[index] = true; } } - } else if (anymatch(search, path)) { - // if the file ends with the given fileEnding, return true - return true } } - return false } From ea810641f44a6914086cb656c74afbb95c1df4aa Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 15 Aug 2024 16:50:38 -0700 Subject: [PATCH 3/3] fix: combine tsc/ts search for TypeScript project-based rules --- src/typescript.cts | 7 +------ src/utils.cts | 45 +++++++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/typescript.cts b/src/typescript.cts index bab8377..376d1b2 100644 --- a/src/typescript.cts +++ b/src/typescript.cts @@ -54,12 +54,7 @@ function disableProjectBasedRules() { ) // check if there are any ts files - const [hasTscConfig, hasTsFile] = findFilesForGroups(cwd, [tscConfigFiles, tsFiles], ignore) - - // return if there are no ts files - if (!hasTsFile) { - return true - } + 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 = !hasTscConfig && hasTsFile diff --git a/src/utils.cts b/src/utils.cts index 88258df..3c2d012 100644 --- a/src/utils.cts +++ b/src/utils.cts @@ -2,30 +2,47 @@ import { readdirSync } from "fs" import { join } from "path" import { default as anymatch } from "anymatch" -export function findFilesForGroups(cwd: string, searchGroups: string[][], ignored: string[]) { - const status = searchGroups.map(() => false); - searchDirectory(cwd, status, searchGroups, ignored); +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[], searchGroups: string[][], ignored: string[]) { - // 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(directory, { withFileTypes: true, recursive: false }); +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(directory, { withFileTypes: true, recursive: false }) for (const file of files) { - const path = join(directory, 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 - searchDirectory(path, status, searchGroups, ignored); + if (searchDirectory(path, status, earlyExitSearchGroup, exhaustiveSearchGroup, ignored)) { + return true // exit + } } } else { - // for each search group, check if the file matches any of the patterns - for (const [index, searchGroup] of searchGroups.entries()) { - if (!status[index] && anymatch(searchGroup, path)) { - status[index] = true; - } + // 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 // continue }