Skip to content

Commit

Permalink
feat: add concurrency
Browse files Browse the repository at this point in the history
  • Loading branch information
pnodet committed Sep 21, 2024
1 parent 8025020 commit f4e73c8
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 78 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"dependencies": {
"@biomejs/js-api": "^0.6.2",
"eslint": "^9.11.0",
"fast-glob": "^3.3.2",
"p-limit": "^6.1.0",
"yargs": "^17.7.2"
}
}
87 changes: 74 additions & 13 deletions src/biome.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Biome, Distribution } from "@biomejs/js-api";
import { readFileSync, existsSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
import fg from "fast-glob";
import fs from "node:fs";
import path from "node:path";
import { readFileSync, existsSync, writeFileSync } from "node:fs";
import type { Biome, Configuration, LintResult } from "@biomejs/js-api";
import os from "node:os";
import type { Configuration, LintResult } from "@biomejs/js-api";
import type { ESLint } from "eslint";
import pLimit from "p-limit";

const getSeverity = (severity: LintResult["diagnostics"][0]["severity"]) => {
switch (severity) {
Expand Down Expand Up @@ -103,8 +109,8 @@ const convertBiomeResult = (
};
};

const biomeLintFile = (biome: Biome, filePath: string, fix = true) => {
const initialContent = readFileSync(filePath, "utf8");
const biomeLintFile = async (biome: Biome, filePath: string, fix = true) => {
const initialContent = await readFile(filePath, "utf8");

const result = biome.lintContent(initialContent, {
filePath,
Expand All @@ -117,21 +123,21 @@ const biomeLintFile = (biome: Biome, filePath: string, fix = true) => {
});

if (formatted.content !== result.content) {
writeFileSync(filePath, formatted.content);
await writeFile(filePath, formatted.content);
}
}

return convertBiomeResult(result, filePath, result.content);
};

export const biomeLintFiles = (biome: Biome, files: string[], fix = true) => {
const results = [];
const biomeLintFiles = async (biome: Biome, files: string[], fix = true) => {
const limit = pLimit(os.cpus().length);

for (const file of files) {
const result = biomeLintFile(biome, file, fix);

results.push(result);
}
const results = await Promise.all(
files.map((file) =>
limit(async () => await biomeLintFile(biome, file, fix)),
),
);

return results;
};
Expand Down Expand Up @@ -198,7 +204,7 @@ const defaultConfig: Configuration = {
},
};

export const getBiomeConfig = (): Configuration => {
const getBiomeConfig = (): Configuration => {
try {
let config = defaultConfig;
const biomeConfigPath = findNearestBiomeConfig();
Expand All @@ -214,3 +220,58 @@ export const getBiomeConfig = (): Configuration => {
return defaultConfig;
}
};

export const lintWithBiome = async (
eslint: ESLint,
patterns: string[],
fix = true,
debug = false,
) => {
const globPatterns = patterns.flatMap((pattern) => {
if (!fs.existsSync(pattern)) {
return pattern;
}

const stats = fs.lstatSync(pattern);

if (stats.isDirectory()) {
return path.join(pattern, "**/*.{js,jsx,ts,tsx}");
}

if (stats.isFile()) {
return pattern;
}

return [];
});

const files = await fg(globPatterns, { dot: true, absolute: true });

const biome = await Biome.create({
distribution: Distribution.NODE,
});

biome.applyConfiguration(getBiomeConfig());

const allFiles: string[] = (
await Promise.all(
files.map(async (file) => {
const isIgnored = await eslint.isPathIgnored(file);
return !isIgnored ? file : null;
}),
)
).filter(Boolean) as string[];

if (debug) {
performance.mark("biome-start");
}

const biomeResults = await biomeLintFiles(biome, allFiles, fix);

if (debug) {
performance.mark("biome-end");
performance.measure("biome", "biome-start", "biome-end");
}

return biomeResults;
};
28 changes: 27 additions & 1 deletion src/eslint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
import type { ESLint, Linter } from "eslint";
import { ESLint } from "eslint";
import type { Linter } from "eslint";
import { performance } from "node:perf_hooks";

export const lintWithEslint = async (
eslint: ESLint,
patterns: string[],
fix = true,
debug = false,
) => {
if (debug) {
performance.mark("eslint-start");
}

const eslintResults = await eslint.lintFiles(patterns);

if (fix && eslintResults.length > 0) {
await ESLint.outputFixes(eslintResults);
}

if (debug) {
performance.mark("eslint-end");
performance.measure("eslint", "eslint-start", "eslint-end");
}

return eslintResults;
};

export const mergeResults = (
results: ESLint.LintResult[],
Expand Down
75 changes: 11 additions & 64 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#!/usr/bin/env node

import { Biome, Distribution } from "@biomejs/js-api";
import { ESLint } from "eslint";
import { biomeLintFiles, getBiomeConfig } from "./biome";
import { mergeResults, overrideConfig } from "./eslint";
import { lintWithBiome } from "./biome";
import { lintWithEslint, mergeResults, overrideConfig } from "./eslint";
import pkgJson from "../package.json" assert { type: "json" };
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
Expand Down Expand Up @@ -31,10 +30,6 @@ const instance = yargs(hideBin(process.argv))
default: true,
description: "Automatically fix linting errors",
})
.option("only", {
choices: ["eslint"],
description: "Only run ESLint",
})
.option("debug", {
type: "boolean",
default: false,
Expand All @@ -43,79 +38,31 @@ const instance = yargs(hideBin(process.argv))
.help(),
async (args) => {
try {
const { files: files_, fix, only, debug } = args;
const files = Array.isArray(files_) ? files_ : [files_];
const eslintOnly = only === "eslint";

if (files.length === 0) {
throw new Error("No files provided");
}
const { files: files_, fix, debug } = args;
const patterns = Array.isArray(files_) ? files_ : [files_];

const eslint = new ESLint({
fix,
stats: debug,
cache: true,
overrideConfig: eslintOnly ? [] : overrideConfig,
cache: false,
overrideConfig: overrideConfig,
});

if (debug) {
performance.mark("eslint-start");
}
const eslintResults = await eslint.lintFiles(files);

if (debug) {
performance.mark("eslint-end");
performance.measure("eslint", "eslint-start", "eslint-end");
}

if (fix && eslintResults.length > 0) {
await ESLint.outputFixes(eslintResults);
}

const formatter = await eslint.loadFormatter("stylish");

if (eslintOnly) {
console.warn("Running only ESLint");
const resultText = await formatter.format(eslintResults);

console.log(resultText ? resultText : "No issues found");

return;
}

const allFiles: string[] = eslintResults.map(
(result) => result.filePath,
);

const biome = await Biome.create({
distribution: Distribution.NODE,
});

biome.applyConfiguration(getBiomeConfig());

if (debug) {
performance.mark("biome-start");
}

const biomeResults = biomeLintFiles(biome, allFiles, fix);

if (debug) {
performance.mark("biome-end");
performance.measure("biome", "biome-start", "biome-end");
}
const eslntResults = await lintWithEslint(eslint, patterns, fix, debug);
const biomeResults = await lintWithBiome(eslint, patterns, fix, debug);

const resultText = await formatter.format(
mergeResults([...biomeResults, ...eslintResults]),
mergeResults([...biomeResults, ...eslntResults]),
);

console.log(resultText ? resultText : "No issues found");

if (debug) {
const measurements = performance.getEntriesByType("measure");
for (const measurement of measurements) {
console.log(`${measurement.name}: ${measurement.duration}ms`);
}
}

console.log(resultText ? resultText : "No issues found");
} catch (error) {
console.error(error);
process.exit(1);
Expand Down

0 comments on commit f4e73c8

Please sign in to comment.