129 lines
3.4 KiB
JavaScript
129 lines
3.4 KiB
JavaScript
import path from 'node:path'
|
|
import fs from 'node:fs/promises'
|
|
import { getESMDirname } from '../eslint.config.mjs'
|
|
import * as zx from 'zx'
|
|
import events from 'events'
|
|
|
|
events.setMaxListeners(31)
|
|
process.env.FORCE_COLOR = '1'
|
|
const rootDir = path.resolve(getESMDirname(import.meta.url), '../')
|
|
|
|
/**
|
|
* Util to run eslint on the entire monorepo
|
|
* w/o args: run on everything
|
|
* w/ space delimited file names: run on those files
|
|
*/
|
|
|
|
const getFileNames = () => {
|
|
// Read argv file names
|
|
const fileNames = process.argv.slice(2).filter((f) => !['.', '*', '**/*'].includes(f))
|
|
const absoluteFileNames = fileNames.map((f) =>
|
|
path.isAbsolute(f) ? f : path.resolve(process.cwd(), f)
|
|
)
|
|
return absoluteFileNames
|
|
}
|
|
|
|
const resolvePackageContexts = async (absoluteFileNames) => {
|
|
const ignoredPackageNames = ['ifc-import-service']
|
|
const allPackages = [
|
|
...(await fs.readdir(path.join(rootDir, './packages'), { withFileTypes: true }))
|
|
.filter((d) => {
|
|
return d.isDirectory() && !ignoredPackageNames.includes(d.name)
|
|
})
|
|
.map((d) => ({
|
|
absolutePath: path.join(rootDir, './packages', d.name)
|
|
})),
|
|
{
|
|
absolutePath: rootDir
|
|
}
|
|
]
|
|
|
|
if (!absoluteFileNames.length) {
|
|
return allPackages.map((p) => ({
|
|
...p,
|
|
files: ['.']
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* @type {Map<string, Set<string>>}
|
|
*/
|
|
const contexts = new Map()
|
|
|
|
// Group filenames by packages
|
|
for (const absoluteFileName of absoluteFileNames) {
|
|
const fittingPkgs = allPackages.filter((p) =>
|
|
absoluteFileName.startsWith(p.absolutePath)
|
|
)
|
|
|
|
// get pkg w/ longest path to get the most appropriate/fitting one
|
|
fittingPkgs.sort((a, b) => b.absolutePath.length - a.absolutePath.length)
|
|
const pkg = fittingPkgs[0]
|
|
|
|
if (!pkg) {
|
|
throw new Error(`File ${absoluteFileName} does not belong to any package`)
|
|
}
|
|
|
|
const contextsKey = pkg.absolutePath
|
|
if (!contexts.has(contextsKey)) {
|
|
contexts.set(contextsKey, new Set())
|
|
}
|
|
contexts.get(contextsKey).add(absoluteFileName)
|
|
}
|
|
|
|
return [...contexts.entries()].map(([pkgPath, files]) => ({
|
|
absolutePath: pkgPath,
|
|
files: [...files]
|
|
}))
|
|
}
|
|
|
|
const execEslintFromPackageContexts = async (packageContexts) => {
|
|
const ac = new AbortController()
|
|
|
|
/** @type {Array<import('zx').ProcessPromise>} */
|
|
const processes = packageContexts.map(async ({ absolutePath, files }) => {
|
|
const prefix = `[${path.relative(rootDir, absolutePath)}]`
|
|
process.stdout.write(`${prefix} Starting...\n`)
|
|
|
|
const exec = zx.$({ cwd: absolutePath, signal: ac.signal })
|
|
const run =
|
|
exec`yarn eslint --cache --max-warnings=0 --no-warn-ignored ${files}`.pipe(
|
|
process.stdout
|
|
)
|
|
|
|
try {
|
|
await run
|
|
process.stdout.write(`${prefix} Finished\n`)
|
|
} catch (e) {
|
|
process.stdout.write(`${prefix} Error occurred\n`)
|
|
throw e
|
|
}
|
|
})
|
|
|
|
// Wait for all to finish
|
|
const res = await Promise.allSettled(processes)
|
|
const failed = res.filter((r) => r.status === 'rejected')
|
|
if (failed.length) {
|
|
throw failed[0].reason
|
|
}
|
|
}
|
|
|
|
const main = async () => {
|
|
const fileNames = getFileNames()
|
|
const packageContexts = await resolvePackageContexts(fileNames)
|
|
|
|
try {
|
|
await execEslintFromPackageContexts(packageContexts)
|
|
} catch (e) {
|
|
if (e instanceof zx.ProcessOutput) {
|
|
console.error('Error occurred while linting')
|
|
process.exit(e.exitCode)
|
|
return
|
|
}
|
|
|
|
throw e
|
|
}
|
|
}
|
|
|
|
await main()
|