Skip to content

Commit e3c79e4

Browse files
authored
Merge pull request github#3788 from github/koesie10/fix-update-node-version
Fix update-node-version script for non-existent @types/node version
2 parents 8e99bc9 + 20a8976 commit e3c79e4

File tree

2 files changed

+76
-20
lines changed

2 files changed

+76
-20
lines changed

.github/workflows/update-node-version.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Update Node version
3535
working-directory: extensions/ql-vscode
3636
run: |
37-
npx ts-node scripts/update-node-version.ts
37+
npx vite-node scripts/update-node-version.ts
3838
shell: bash
3939
- name: Get current Node version
4040
working-directory: extensions/ql-vscode

extensions/ql-vscode/scripts/update-node-version.ts

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
import { join, resolve } from "path";
22
import { execSync } from "child_process";
3-
import { outputFile, readFile, readJSON } from "fs-extra";
3+
import { outputFile, readJSON } from "fs-extra";
44
import { getVersionInformation } from "./util/vscode-versions";
55
import { fetchJson } from "./util/fetch";
6+
import { SemVer } from "semver";
67

78
const extensionDirectory = resolve(__dirname, "..");
89

910
interface Release {
1011
tag_name: string;
1112
}
1213

14+
interface NpmViewError {
15+
error: {
16+
code: string;
17+
summary: string;
18+
detail: string;
19+
};
20+
}
21+
22+
interface ExecError extends Error {
23+
status: number;
24+
stdout: string;
25+
}
26+
27+
function isExecError(e: unknown): e is ExecError {
28+
return (
29+
e instanceof Error &&
30+
"status" in e &&
31+
typeof e.status === "number" &&
32+
"stdout" in e &&
33+
typeof e.stdout === "string"
34+
);
35+
}
36+
1337
async function updateNodeVersion() {
1438
const latestVsCodeRelease = await fetchJson<Release>(
1539
"https://api.github.com/repos/microsoft/vscode/releases/latest",
@@ -23,19 +47,7 @@ async function updateNodeVersion() {
2347
`VS Code ${versionInformation.vscodeVersion} uses Electron ${versionInformation.electronVersion} and Node ${versionInformation.nodeVersion}`,
2448
);
2549

26-
let currentNodeVersion = (
27-
await readFile(join(extensionDirectory, ".nvmrc"), "utf8")
28-
).trim();
29-
if (currentNodeVersion.startsWith("v")) {
30-
currentNodeVersion = currentNodeVersion.slice(1);
31-
}
32-
33-
if (currentNodeVersion === versionInformation.nodeVersion) {
34-
console.log("Node version is already up to date");
35-
return;
36-
}
37-
38-
console.log("Node version needs to be updated, updating now");
50+
console.log("Updating files related to the Node version");
3951

4052
await outputFile(
4153
join(extensionDirectory, ".nvmrc"),
@@ -49,20 +61,64 @@ async function updateNodeVersion() {
4961
"utf8",
5062
);
5163

64+
const nodeVersion = new SemVer(versionInformation.nodeVersion);
65+
5266
// The @types/node version needs to match the first two parts of the Node
5367
// version, e.g. if the Node version is 18.17.3, the @types/node version
5468
// should be 18.17.*. This corresponds with the documentation at
5569
// https://github.com/definitelytyped/definitelytyped#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library
5670
// "The patch version of the type declaration package is unrelated to the library patch version. This allows
5771
// Definitely Typed to safely update type declarations for the same major/minor version of a library."
5872
// 18.17.* is equivalent to >=18.17.0 <18.18.0
59-
const typesNodeVersion = versionInformation.nodeVersion
60-
.split(".")
61-
.slice(0, 2)
62-
.join(".");
73+
// In some cases, the @types/node version matching the exact Node version may not exist, in which case we'll try
74+
// the next lower minor version, and so on, until we find a version that exists.
75+
const typesNodeSemver = new SemVer(nodeVersion);
76+
typesNodeSemver.patch = 0;
77+
78+
// eslint-disable-next-line no-constant-condition
79+
while (true) {
80+
const typesNodeVersion = `${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;
81+
82+
try {
83+
// Check that this version actually exists
84+
console.log(`Checking if @types/node@${typesNodeVersion} exists`);
85+
86+
execSync(`npm view --json "@types/node@${typesNodeVersion}"`, {
87+
encoding: "utf-8",
88+
stdio: "pipe",
89+
});
90+
91+
console.log(`@types/node@${typesNodeVersion} exists`);
92+
93+
// If it exists, we can break out of this loop
94+
break;
95+
} catch (e: unknown) {
96+
if (!isExecError(e)) {
97+
throw e;
98+
}
99+
100+
const error = JSON.parse(e.stdout) as NpmViewError;
101+
if (error.error.code !== "E404") {
102+
throw new Error(error.error.detail);
103+
}
104+
105+
console.log(
106+
`@types/node package doesn't exist for ${typesNodeVersion}, trying a lower version (${error.error.summary})`,
107+
);
108+
109+
// This means the version doesn't exist, so we'll try decrementing the minor version
110+
typesNodeSemver.minor -= 1;
111+
if (typesNodeSemver.minor < 0) {
112+
throw new Error(
113+
`Could not find a suitable @types/node version for Node ${nodeVersion.format()}`,
114+
);
115+
}
116+
}
117+
}
63118

64119
packageJson.engines.node = `^${versionInformation.nodeVersion}`;
65-
packageJson.devDependencies["@types/node"] = `${typesNodeVersion}.*`;
120+
packageJson.devDependencies["@types/node"] =
121+
`${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;
66122

67123
await outputFile(
68124
join(extensionDirectory, "package.json"),

0 commit comments

Comments
 (0)