Skip to content

Commit

Permalink
Merge pull request #3788 from github/koesie10/fix-update-node-version
Browse files Browse the repository at this point in the history
Fix update-node-version script for non-existent @types/node version
  • Loading branch information
koesie10 authored Nov 7, 2024
2 parents 8e99bc9 + 20a8976 commit e3c79e4
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/update-node-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Update Node version
working-directory: extensions/ql-vscode
run: |
npx ts-node scripts/update-node-version.ts
npx vite-node scripts/update-node-version.ts
shell: bash
- name: Get current Node version
working-directory: extensions/ql-vscode
Expand Down
94 changes: 75 additions & 19 deletions extensions/ql-vscode/scripts/update-node-version.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
import { join, resolve } from "path";
import { execSync } from "child_process";
import { outputFile, readFile, readJSON } from "fs-extra";
import { outputFile, readJSON } from "fs-extra";
import { getVersionInformation } from "./util/vscode-versions";
import { fetchJson } from "./util/fetch";
import { SemVer } from "semver";

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

interface Release {
tag_name: string;
}

interface NpmViewError {
error: {
code: string;
summary: string;
detail: string;
};
}

interface ExecError extends Error {
status: number;
stdout: string;
}

function isExecError(e: unknown): e is ExecError {
return (
e instanceof Error &&
"status" in e &&
typeof e.status === "number" &&
"stdout" in e &&
typeof e.stdout === "string"
);
}

async function updateNodeVersion() {
const latestVsCodeRelease = await fetchJson<Release>(
"https://api.github.com/repos/microsoft/vscode/releases/latest",
Expand All @@ -23,19 +47,7 @@ async function updateNodeVersion() {
`VS Code ${versionInformation.vscodeVersion} uses Electron ${versionInformation.electronVersion} and Node ${versionInformation.nodeVersion}`,
);

let currentNodeVersion = (
await readFile(join(extensionDirectory, ".nvmrc"), "utf8")
).trim();
if (currentNodeVersion.startsWith("v")) {
currentNodeVersion = currentNodeVersion.slice(1);
}

if (currentNodeVersion === versionInformation.nodeVersion) {
console.log("Node version is already up to date");
return;
}

console.log("Node version needs to be updated, updating now");
console.log("Updating files related to the Node version");

await outputFile(
join(extensionDirectory, ".nvmrc"),
Expand All @@ -49,20 +61,64 @@ async function updateNodeVersion() {
"utf8",
);

const nodeVersion = new SemVer(versionInformation.nodeVersion);

// The @types/node version needs to match the first two parts of the Node
// version, e.g. if the Node version is 18.17.3, the @types/node version
// should be 18.17.*. This corresponds with the documentation at
// https://github.com/definitelytyped/definitelytyped#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library
// "The patch version of the type declaration package is unrelated to the library patch version. This allows
// Definitely Typed to safely update type declarations for the same major/minor version of a library."
// 18.17.* is equivalent to >=18.17.0 <18.18.0
const typesNodeVersion = versionInformation.nodeVersion
.split(".")
.slice(0, 2)
.join(".");
// In some cases, the @types/node version matching the exact Node version may not exist, in which case we'll try
// the next lower minor version, and so on, until we find a version that exists.
const typesNodeSemver = new SemVer(nodeVersion);
typesNodeSemver.patch = 0;

// eslint-disable-next-line no-constant-condition
while (true) {
const typesNodeVersion = `${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;

try {
// Check that this version actually exists
console.log(`Checking if @types/node@${typesNodeVersion} exists`);

execSync(`npm view --json "@types/node@${typesNodeVersion}"`, {
encoding: "utf-8",
stdio: "pipe",
});

console.log(`@types/node@${typesNodeVersion} exists`);

// If it exists, we can break out of this loop
break;
} catch (e: unknown) {
if (!isExecError(e)) {
throw e;
}

const error = JSON.parse(e.stdout) as NpmViewError;
if (error.error.code !== "E404") {
throw new Error(error.error.detail);
}

console.log(
`@types/node package doesn't exist for ${typesNodeVersion}, trying a lower version (${error.error.summary})`,
);

// This means the version doesn't exist, so we'll try decrementing the minor version
typesNodeSemver.minor -= 1;
if (typesNodeSemver.minor < 0) {
throw new Error(
`Could not find a suitable @types/node version for Node ${nodeVersion.format()}`,
);
}
}
}

packageJson.engines.node = `^${versionInformation.nodeVersion}`;
packageJson.devDependencies["@types/node"] = `${typesNodeVersion}.*`;
packageJson.devDependencies["@types/node"] =
`${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;

await outputFile(
join(extensionDirectory, "package.json"),
Expand Down

0 comments on commit e3c79e4

Please sign in to comment.