From 0fe5fafb28a47cb80c7d8ea4fb4e152b6ed0d978 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 24 Mar 2023 17:20:27 +0100 Subject: [PATCH] fix: ensure rimraf bin dir can always be resolved (#3614) --- libs/core/src/lib/rimraf-dir.ts | 57 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/libs/core/src/lib/rimraf-dir.ts b/libs/core/src/lib/rimraf-dir.ts index 0a92a32a1f..a763abf003 100644 --- a/libs/core/src/lib/rimraf-dir.ts +++ b/libs/core/src/lib/rimraf-dir.ts @@ -1,36 +1,59 @@ "use strict"; import log from "npmlog"; -import path from "path"; +import path from "node:path"; +import fs from "node:fs"; import pathExists from "path-exists"; // eslint-disable-next-line @typescript-eslint/no-var-requires const childProcess = require("@lerna/child-process"); -// NOTE: if rimraf moves the location of its executable, this will need to be updated -const RIMRAF_CLI = require.resolve("rimraf/bin"); +let rimrafBinPath: string | undefined; -export function rimrafDir(dirPath: string) { +async function useRimrafBinPath(): Promise { + if (typeof rimrafBinPath === "string") { + return rimrafBinPath; + } + + const filePath = require.resolve("rimraf"); + const directoryPath = path.basename(filePath); + + try { + const rawFile = await fs.promises.readFile(path.join(directoryPath, "package.json"), { + encoding: "utf-8", + }); + const file = JSON.parse(rawFile); + + rimrafBinPath = file.bin || require.resolve("rimraf/bin"); + } catch (e) { + rimrafBinPath = require.resolve("rimraf/bin"); + } + + return rimrafBinPath as string; +} + +export async function rimrafDir(dirPath: string) { log.silly("rimrafDir", dirPath); // Shelling out to a child process for a noop is expensive. // Checking if `dirPath` exists to be removed is cheap. // This lets us short-circuit if we don't have anything to do. - return pathExists(dirPath).then((exists) => { - if (!exists) { - return; - } + const fileExists = await pathExists(dirPath); + if (!fileExists) { + return; + } - // globs only return directories with a trailing slash - const slashed = path.normalize(`${dirPath}/`); - const args = [RIMRAF_CLI, "--no-glob", slashed]; + const cliPath = await useRimrafBinPath(); - // We call this resolved CLI path in the "path/to/node path/to/cli <..args>" - // pattern to avoid Windows hangups with shebangs (e.g., WSH can't handle it) - return childProcess.spawn(process.execPath, args).then(() => { - log.verbose("rimrafDir", "removed", dirPath); + // globs only return directories with a trailing slash + const slashed = path.normalize(`${dirPath}/`); + const args = [cliPath, "--no-glob", slashed]; - return dirPath; - }); + // We call this resolved CLI path in the "path/to/node path/to/cli <..args>" + // pattern to avoid Windows hangups with shebangs (e.g., WSH can't handle it) + return childProcess.spawn(process.execPath, args).then(() => { + log.verbose("rimrafDir", "removed", dirPath); + + return dirPath; }); }