From 84192e3494613a01dbd6f20fde20cbad0061431c Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:58:57 -0500 Subject: [PATCH] Use async file access (#577) --- lib/cli.ts | 2 +- lib/generator.ts | 19 ++++++++++--------- lib/package-json.ts | 21 +++++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/cli.ts b/lib/cli.ts index 19ed293c..aabc848a 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -186,7 +186,7 @@ export async function run( // Documentation for options should be kept in sync with README.md and the JSDocs for the `GenerateOptions` type. await program - .version(getCurrentPackageVersion()) + .version(await getCurrentPackageVersion()) .addArgument( new Argument('[path]', 'path to ESLint plugin root').default('.') ) diff --git a/lib/generator.ts b/lib/generator.ts index 75c92150..0931cfe4 100644 --- a/lib/generator.ts +++ b/lib/generator.ts @@ -1,5 +1,5 @@ import { EOL } from 'node:os'; -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { existsSync } from 'node:fs'; import { dirname, join, relative, resolve } from 'node:path'; import { getAllNamedOptions, hasOptions } from './rule-options.js'; import { @@ -33,6 +33,7 @@ import type { GenerateOptions } from './types.js'; import { OPTION_TYPE, RuleModule } from './types.js'; import { replaceRulePlaceholder } from './rule-link.js'; import { updateRuleOptionsList } from './rule-options-list.js'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; function stringOrArrayWithFallback( stringOrArray: undefined | T, @@ -63,7 +64,7 @@ function stringOrArrayToArrayWithFallback( // eslint-disable-next-line complexity export async function generate(path: string, options?: GenerateOptions) { const plugin = await loadPlugin(path); - const pluginPrefix = getPluginPrefix(path); + const pluginPrefix = await getPluginPrefix(path); const configsToRules = await resolveConfigsToRules(plugin); if (!plugin.rules) { @@ -183,8 +184,8 @@ export async function generate(path: string, options?: GenerateOptions) { newRuleDocContents = `${EOL}${newRuleDocContents}${EOL}`; } - mkdirSync(dirname(pathToDoc), { recursive: true }); - writeFileSync(pathToDoc, newRuleDocContents); + await mkdir(dirname(pathToDoc), { recursive: true }); + await writeFile(pathToDoc, newRuleDocContents); initializedRuleDoc = true; } @@ -206,7 +207,7 @@ export async function generate(path: string, options?: GenerateOptions) { urlRuleDoc ); - const contentsOld = readFileSync(pathToDoc).toString(); + const contentsOld = (await readFile(pathToDoc)).toString(); // eslint-disable-line unicorn/no-await-expression-member const contentsNew = await postprocess( updateRuleOptionsList( replaceOrCreateHeader( @@ -231,7 +232,7 @@ export async function generate(path: string, options?: GenerateOptions) { process.exitCode = 1; } } else { - writeFileSync(pathToDoc, contentsNew); + await writeFile(pathToDoc, contentsNew); } // Check for potential issues with the rule doc. @@ -284,7 +285,7 @@ export async function generate(path: string, options?: GenerateOptions) { for (const pathRuleListItem of pathRuleList) { // Find the exact filename. - const pathToFile = getPathWithExactFileNameCasing( + const pathToFile = await getPathWithExactFileNameCasing( join(path, pathRuleListItem) ); if (!pathToFile || !existsSync(pathToFile)) { @@ -294,7 +295,7 @@ export async function generate(path: string, options?: GenerateOptions) { } // Update the rules list in this file. - const fileContents = readFileSync(pathToFile, 'utf8'); + const fileContents = await readFile(pathToFile, 'utf8'); const fileContentsNew = await postprocess( updateConfigsList( updateRulesList( @@ -336,7 +337,7 @@ export async function generate(path: string, options?: GenerateOptions) { process.exitCode = 1; } } else { - writeFileSync(pathToFile, fileContentsNew, 'utf8'); + await writeFile(pathToFile, fileContentsNew, 'utf8'); } } } diff --git a/lib/package-json.ts b/lib/package-json.ts index 1b328d60..e82b7b66 100644 --- a/lib/package-json.ts +++ b/lib/package-json.ts @@ -6,11 +6,12 @@ import { isAbsolute, extname, } from 'node:path'; -import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import { existsSync } from 'node:fs'; import { importAbs } from './import.js'; import { createRequire } from 'node:module'; import type { Plugin } from './types.js'; import type { PackageJson } from 'type-fest'; +import { readdir, readFile } from 'node:fs/promises'; const require = createRequire(import.meta.url); @@ -18,14 +19,14 @@ export function getPluginRoot(path: string) { return isAbsolute(path) ? path : join(process.cwd(), path); } -function loadPackageJson(path: string): PackageJson { +async function loadPackageJson(path: string): Promise { const pluginRoot = getPluginRoot(path); const pluginPackageJsonPath = join(pluginRoot, 'package.json'); if (!existsSync(pluginPackageJsonPath)) { throw new Error('Could not find package.json of ESLint plugin.'); } const pluginPackageJson = JSON.parse( - readFileSync(join(pluginRoot, 'package.json'), 'utf8') + await readFile(join(pluginRoot, 'package.json'), 'utf8') ) as PackageJson; return pluginPackageJson; @@ -38,7 +39,7 @@ export async function loadPlugin(path: string): Promise { return require(pluginRoot) as Plugin; // eslint-disable-line import/no-dynamic-require } catch (error) { // Otherwise, for ESM plugins, we'll have to try to resolve the exact plugin entry point and import it. - const pluginPackageJson = loadPackageJson(path); + const pluginPackageJson = await loadPackageJson(path); let pluginEntryPoint; const exports = pluginPackageJson.exports; if (typeof exports === 'string') { @@ -88,8 +89,8 @@ export async function loadPlugin(path: string): Promise { } } -export function getPluginPrefix(path: string): string { - const pluginPackageJson = loadPackageJson(path); +export async function getPluginPrefix(path: string): Promise { + const pluginPackageJson = await loadPackageJson(path); if (!pluginPackageJson.name) { throw new Error( "Could not find `name` field in ESLint plugin's package.json." @@ -103,10 +104,10 @@ export function getPluginPrefix(path: string): string { /** * Resolve the path to a file but with the exact filename-casing present on disk. */ -export function getPathWithExactFileNameCasing(path: string) { +export async function getPathWithExactFileNameCasing(path: string) { const dir = dirname(path); const fileNameToSearch = basename(path); - const filenames = readdirSync(dir, { withFileTypes: true }); + const filenames = await readdir(dir, { withFileTypes: true }); for (const dirent of filenames) { if ( dirent.isFile() && @@ -118,7 +119,7 @@ export function getPathWithExactFileNameCasing(path: string) { return undefined; // eslint-disable-line unicorn/no-useless-undefined } -export function getCurrentPackageVersion(): string { +export async function getCurrentPackageVersion(): Promise { // When running as compiled code, use path relative to compiled version of this file in the dist folder. // When running as TypeScript (in a test), use path relative to this file. const pathToPackageJson = import.meta.url.endsWith('.ts') @@ -126,7 +127,7 @@ export function getCurrentPackageVersion(): string { : /* istanbul ignore next -- can't test the compiled version in test */ '../../package.json'; const packageJson = JSON.parse( - readFileSync(new URL(pathToPackageJson, import.meta.url), 'utf8') + await readFile(new URL(pathToPackageJson, import.meta.url), 'utf8') ) as PackageJson; if (!packageJson.version) { throw new Error('Could not find package.json `version`.');