UNPKG

@neodx/vfs

Version:

Simple virtual file system - working dir context, lazy changes, different modes, integrations and moreover

190 lines (184 loc) 6.62 kB
'use strict'; var operations = require('../_internal/operations-C-RkTDIe.cjs'); var std = require('@neodx/std'); var plugins_packageJson = require('./package-json.cjs'); var pathe = require('pathe'); var createVfsPlugin = require('../_internal/create-vfs-plugin-1jK9qNm1.cjs'); async function findInVfsUntil(vfs, currentPath, predicate) { do if (await predicate(currentPath)) return currentPath; while ((currentPath = vfs.resolve(currentPath, '..')) !== vfs.resolve('/')); return null; } const createVfsNpmApi = vfs => { const createChecker = predicate => { const check = std.memoize(async path => (await vfs.readDir(path)).some(predicate)); return async (path = '.') => await findInVfsUntil(vfs, path, check); }; const { task } = operations.createTaskRunner({ log: vfs.log.child('npm') }); const tryFindWorkspaceRoot = task('tryFindWorkspaceRoot', createChecker(isWorkspaceFile)); const tryFindPackageRoot = task('tryFindPackageRoot', createChecker(isPackageFile)); return { tryFindWorkspaceRoot, tryFindPackageRoot, findWorkspaceRoot: task('findWorkspaceRoot', tryFindWorkspaceRoot, { invariant: 'Workspace root not found' }), findPackageRoot: task('findPackageRoot', tryFindPackageRoot, { invariant: 'Package root not found' }), // TODO Simplify this logic tryGetDependencyVersion: task('tryGetDependencyVersion', async name => { const lookupOrder = std.compact( std.uniq([await tryFindPackageRoot(), await tryFindWorkspaceRoot()]) ); // TODO Add "find until" common util for (const path of lookupOrder) { const pkg = plugins_packageJson.createVfsPackageJsonFileApi(vfs, path); const content = await pkg.read(); for (const field of dependencyFieldOrder) { if (content[field]?.[name]) { return content[field][name]; } } } return null; }) }; }; const workspaceRootOnlyFiles = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock']; const packageFiles = ['package.json', ...workspaceRootOnlyFiles]; const dependencyFieldOrder = ['dependencies', 'devDependencies']; const isWorkspaceFile = std.includesIn(workspaceRootOnlyFiles); const isPackageFile = std.includesIn(packageFiles); function eslint({ fix = true, auto = true, logErrors = true, logWarnings = false, eslintParams } = {}) { return createVfsPlugin.createVfsPlugin('eslint', (vfs, { context, beforeApply }) => { const { fix: originalFix, fixAll: originalFixAll } = vfs; const npm = createVfsNpmApi(vfs); const getEsLint = std.once(async () => { const { ESLint } = await import('eslint'); return new ESLint({ useEslintrc: true, overrideConfig: { env: { es6: true, node: true }, parserOptions: { ecmaVersion: 2022, sourceType: 'module' } }, ...eslintParams, fix, cwd: context.path }); }); const log = context.log.child('eslint'); const { task } = operations.createTaskRunner({ log }); vfs.fix = task( 'fix', async path => { const lint = await getEsLint(); const allResults = await std.concurrently(std.toArray(path), async path => { const workspaceRoot = await npm.tryFindWorkspaceRoot(); if (!workspaceRoot) { log.debug( 'Skipping %s, because it is not in a workspace', operations.displayPath(context, path) ); return null; } const pkg = plugins_packageJson.createVfsPackageJsonFileApi( vfs, vfs.resolve(workspaceRoot, 'package.json') ); if (!(await pkg.hasDependency('eslint'))) { log.debug( 'Skipping %s, because it has no eslint dependency', operations.displayPath(context, path) ); return null; } if (!(await vfs.isFile(path))) { log.debug( 'Skipping %s, because it is not a file', operations.displayPath(context, path) ); return null; } if (await lint.isPathIgnored(path)) { log.debug('Skipping %s, because it is ignored', operations.displayPath(context, path)); return null; } if (!allSourceExtensions.includes(pathe.extname(path))) { log.debug( 'Skipping %s, because it is not a source file', operations.displayPath(context, path) ); return null; } return await lint.lintText(await vfs.read(path, 'utf-8'), { filePath: vfs.resolve(path) }); }); const results = std.compact(allResults.flat()); const errors = results.filter(result => result.fatalErrorCount > 0); const warnings = results.filter(result => result.fatalErrorCount > 0); const fatalErrors = results.filter(result => result.fatalErrorCount > 0); const formatter = await lint.loadFormatter('stylish'); if (fatalErrors.length > 0) { log.error('ESLint fatal errors:%s', formatter.format(fatalErrors)); } if (logErrors && errors.length > 0) { log.error('ESLint errors:%s', formatter.format(errors)); } if (logWarnings && warnings.length > 0) { log.warn('ESLint warnings:%s', formatter.format(warnings)); } await std.concurrently( results.filter(it => std.isTypeOfString(it.output)), async ({ filePath, output }) => await vfs.write(filePath, output) ); await originalFix?.(path); }, { mapSuccessMessage: (_, path) => `${operations.displayPath(context, '.')}(${operations.formatList(std.toArray(path).map(context.relative))}) fixed` } ); vfs.fixAll = task( 'fix all', async () => { log.debug('Fixing all ESLint issues in changed files...'); const changes = await operations.getVfsActions(context, ['create', 'update']); await vfs.fix(changes.map(change => change.path)); await originalFixAll?.(); }, { mapSuccessMessage: () => 'fixed all changed files' } ); if (auto) { beforeApply(() => vfs.fixAll()); } return vfs; }); } const allSourceExtensions = ['js', 'ts'].flatMap(ext => [ `.${ext}`, `.c${ext}`, `.m${ext}`, `.${ext}x` ]); exports.eslint = eslint; //# sourceMappingURL=eslint.cjs.map