Improve error message when other package has a path mapping to a version you’re deleting

This commit is contained in:
Andrew Branch
2020-10-29 16:12:43 -07:00
parent 27d8f2f266
commit 0aa87777b7
3 changed files with 109 additions and 18 deletions

View File

@@ -122,7 +122,12 @@ export class AllPackages {
for (const [name, version] of Object.entries(pkg.dependencies)) {
const versions = this.data.get(getMangledNameForScopedPackage(name));
if (versions) {
yield versions.get(version);
yield versions.get(
version,
pkg.pathMappings[name]
? `${pkg.name} references this version of ${name} in its path mappings in tsconfig.json. If you are deleting this version, update ${pkg.name}s path mappings accordingly.\n`
: undefined
);
}
}
@@ -487,8 +492,8 @@ export class TypingsVersions {
return this.map.values();
}
get(version: DependencyVersion): TypingsData {
return version === "*" ? this.getLatest() : this.getLatestMatch(version);
get(version: DependencyVersion, errorMessage?: string): TypingsData {
return version === "*" ? this.getLatest() : this.getLatestMatch(version, errorMessage);
}
tryGet(version: DependencyVersion): TypingsData | undefined {
@@ -499,10 +504,10 @@ export class TypingsVersions {
return this.map.get(this.versions[0])!;
}
private getLatestMatch(version: TypingVersion): TypingsData {
private getLatestMatch(version: TypingVersion, errorMessage?: string): TypingsData {
const data = this.tryGetLatestMatch(version);
if (!data) {
throw new Error(`Could not find version ${version.major}.${version.minor ?? "*"}`);
throw new Error(`Could not find version ${version.major}.${version.minor ?? "*"}. ${errorMessage || ""}`);
}
return data;
}

View File

@@ -14,7 +14,8 @@
"url": "git+https://github.com/microsoft/DefinitelyTyped-tools.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
"test": "echo \"Error: run tests from root\" && exit 1",
"build": "tsc -b ."
},
"bugs": {
"url": "https://github.com/microsoft/DefinitelyTyped-tools/issues"

View File

@@ -1,5 +1,6 @@
import assert from "assert";
import { ChildProcess, exec as node_exec, fork } from "child_process";
import { Socket } from "net";
const DEFAULT_CRASH_RECOVERY_MAX_OLD_SPACE_SIZE = 4096;
@@ -39,7 +40,7 @@ export function runWithChildProcesses<In>({
nProcesses,
handleOutput
}: RunWithChildProcessesOptions<In>): Promise<void> {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
const nPerProcess = Math.floor(inputs.length / nProcesses);
let processesLeft = nProcesses;
let rejected = false;
@@ -53,7 +54,9 @@ export function runWithChildProcesses<In>({
processesLeft--;
continue;
}
const child = fork(workerFile, commandLineArgs);
const child = fork(workerFile, commandLineArgs, {
execArgv: await getChildProcessExecArgv(i)
});
allChildren.push(child);
child.send(inputs.slice(lo, hi));
child.on("message", outputMessage => {
@@ -123,7 +126,7 @@ export function runWithListeningChildProcesses<In>({
handleCrash,
softTimeoutMs = Infinity
}: RunWithListeningChildProcessesOptions<In>): Promise<void> {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
let inputIndex = 0;
let processesLeft = nProcesses;
let rejected = false;
@@ -162,7 +165,7 @@ export function runWithListeningChildProcesses<In>({
}
};
const onClose = () => {
const onClose = async () => {
if (rejected || !runningChildren.has(child)) {
return;
}
@@ -195,10 +198,10 @@ export function runWithListeningChildProcesses<In>({
switch (crashRecoveryState) {
case CrashRecoveryState.Retry:
restartChild(resumeTask, process.execArgv);
await restartChild(resumeTask, process.execArgv);
break;
case CrashRecoveryState.RetryWithMoreMemory:
restartChild(resumeTask, [
await restartChild(resumeTask, [
...getExecArgvWithoutMaxOldSpaceSize(),
`--max_old_space_size=${crashRecoveryMaxOldSpaceSize}`
]);
@@ -208,7 +211,7 @@ export function runWithListeningChildProcesses<In>({
if (inputIndex === inputs.length || Date.now() - startTime > softTimeoutMs) {
stopChild(/*done*/ true);
} else {
restartChild(nextTask, process.execArgv);
await restartChild(nextTask, process.execArgv);
}
break;
default:
@@ -225,9 +228,9 @@ export function runWithListeningChildProcesses<In>({
fail(err);
};
const startChild = (taskAction: () => void, execArgv: string[]) => {
const startChild = async (taskAction: () => void, execArgv: string[]) => {
try {
child = fork(workerFile, commandLineArgs, { cwd, execArgv });
child = fork(workerFile, commandLineArgs, { cwd, execArgv: await getChildProcessExecArgv(i, execArgv) });
runningChildren.add(child);
} catch (e) {
fail(e);
@@ -280,12 +283,12 @@ export function runWithListeningChildProcesses<In>({
}
};
const restartChild = (taskAction: () => void, execArgv: string[]) => {
const restartChild = async (taskAction: () => void, execArgv: string[]) => {
try {
assert(runningChildren.has(child), `${processIndex}> Child not running`);
console.log(`${processIndex}> Restarting...`);
stopChild(/*done*/ false);
startChild(taskAction, execArgv);
await startChild(taskAction, execArgv);
} catch (e) {
onError(e);
}
@@ -314,7 +317,7 @@ export function runWithListeningChildProcesses<In>({
}
};
startChild(nextTask, process.execArgv);
await startChild(nextTask, process.execArgv);
}
function fail(err?: Error): void {
@@ -375,3 +378,85 @@ function getExecArgvWithoutMaxOldSpaceSize(): readonly string[] {
}
return execArgvWithoutMaxOldSpaceSize;
}
async function getChildProcessExecArgv(portOffset = 0, execArgv = process.execArgv) {
const debugArg = execArgv.findIndex(
arg => arg === "--inspect" || arg === "--inspect-brk" || arg.startsWith("--inspect=")
);
if (debugArg < 0) return execArgv;
const port = parseInt(execArgv[debugArg].split("=")[1], 10) || 9229;
return [
...execArgv.slice(0, debugArg),
`--inspect=${await findFreePort(port + 1 + portOffset, 100, 1000)}`,
...execArgv.slice(debugArg + 1)
];
}
// From VS Code: https://github.com/microsoft/vscode/blob/7d57a8f6f546b5e30027e7cfa87bd834eb5c7bbb/src/vs/base/node/ports.ts
function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Promise<number> {
let done = false;
return new Promise(resolve => {
const timeoutHandle = setTimeout(() => {
if (!done) {
done = true;
return resolve(0);
}
}, timeout);
doFindFreePort(startPort, giveUpAfter, port => {
if (!done) {
done = true;
clearTimeout(timeoutHandle);
return resolve(port);
}
});
});
}
function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: number) => void): void {
if (giveUpAfter === 0) {
return clb(0);
}
const client = new Socket();
// If we can connect to the port it means the port is already taken so we continue searching
client.once("connect", () => {
dispose(client);
return doFindFreePort(startPort + 1, giveUpAfter - 1, clb);
});
client.once("data", () => {
// this listener is required since node.js 8.x
});
client.once("error", (err: Error & { code?: string }) => {
dispose(client);
// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect
if (err.code !== "ECONNREFUSED") {
return doFindFreePort(startPort + 1, giveUpAfter - 1, clb);
}
// Otherwise it means the port is free to use!
return clb(startPort);
});
client.connect(startPort, "127.0.0.1");
function dispose(socket: Socket): void {
try {
socket.removeAllListeners("connect");
socket.removeAllListeners("error");
socket.end();
socket.destroy();
socket.unref();
} catch (error) {
console.error(error); // otherwise this error would get lost in the callback chain
}
}
}