@neodx/vfs
Version:
Simple virtual file system - working dir context, lazy changes, different modes, integrations and moreover
190 lines (184 loc) • 6.62 kB
JavaScript
;
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