Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit df5e96b

Browse files
authored
Merge pull request #297 from codeoverflow-org/feat/minimum-node-14
Bump minimum node.js version from 12 to 14.14.0
2 parents 255238e + 8046fe9 commit df5e96b

File tree

13 files changed

+50
-86
lines changed

13 files changed

+50
-86
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
fail-fast: false
88
matrix:
99
os: [ubuntu-latest, windows-2019]
10-
node: [12, 14, 16, 17]
10+
node: [14, 16, 18]
1111

1212
runs-on: ${{ matrix.os }}
1313
steps:
@@ -31,7 +31,7 @@ jobs:
3131
matrix:
3232
os: [ubuntu-latest, windows-2019]
3333
version: ['0.1', '0.2', 'development']
34-
node: [14, 16]
34+
node: [14, 18]
3535

3636
runs-on: ${{ matrix.os }}
3737
steps:
@@ -77,7 +77,7 @@ jobs:
7777
- name: Setup Node.js
7878
uses: actions/setup-node@v3
7979
with:
80-
node-version: 16
80+
node-version: 18
8181

8282
- name: Install dependencies
8383
run: npm ci

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import yargs from "yargs";
22
import { installModule } from "./install";
33
import { uninstallModule } from "./uninstall";
44
import { version } from "../package.json";
5-
import { checkForCliUpdate, ensureNode12 } from "./utils/cli";
5+
import { checkForCliUpdate, ensureMinimumNodeVersion } from "./utils/cli";
66
import { generateModule } from "./generate";
77

88
// This file gets imported by the index.js file of the repository root.
@@ -22,7 +22,7 @@ const args = yargs(process.argv.slice(2))
2222
})
2323
.parse();
2424

25-
ensureNode12();
25+
ensureMinimumNodeVersion();
2626
(async () => {
2727
const opts = await args;
2828
if (!opts["disable-updates"]) {

src/install/development.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as chalk from "chalk";
22
import * as git from "isomorphic-git";
33
import * as fs from "fs";
44
import * as http from "isomorphic-git/http/node";
5-
import { directoryExists, removeDirectory } from "../utils/fs";
5+
import { directoryExists } from "../utils/fs";
66
import { DevelopmentInstallation, writeInstallInfo } from "../utils/installation";
77
import { logger } from "../utils/log";
88
import * as path from "path";
@@ -43,7 +43,7 @@ async function manageDocs(nodecgIODir: string, cloneDocs: boolean): Promise<void
4343
} else if (await directoryExists(docsPath)) {
4444
// Docs are not wanted but exists (they probably were selected previously) => delete
4545
logger.debug("Removing docs...");
46-
await removeDirectory(docsPath);
46+
await fs.promises.rm(docsPath, { recursive: true, force: true });
4747
}
4848
}
4949

@@ -122,7 +122,7 @@ async function deleteNodeModuleDirectories(nodecgIODir: string): Promise<void> {
122122

123123
for (const nodeModuleDir of nodeModuleDirs) {
124124
if (await directoryExists(nodeModuleDir)) {
125-
await removeDirectory(nodeModuleDir);
125+
await fs.promises.rm(nodeModuleDir, { recursive: true, force: true });
126126
}
127127
}
128128

src/install/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CommandModule } from "yargs";
22
import * as path from "path";
3-
import { directoryExists, removeDirectory } from "../utils/fs";
3+
import * as fs from "fs";
4+
import { directoryExists } from "../utils/fs";
45
import { createDevInstall } from "./development";
56
import { manageBundleDir } from "../utils/nodecgConfig";
67
import { promptForInstallInfo } from "./prompt";
@@ -76,7 +77,7 @@ async function install(opts: InstallCommandOptions): Promise<void> {
7677
// If the minor version changed and we already have another one installed, we need to delete it, so it can be properly installed.
7778
if (currentInstall && currentInstall.version !== requestedInstall.version && (await directoryExists(nodecgIODir))) {
7879
logger.info(`Deleting nodecg-io version ${currentInstall.version}...`);
79-
await removeDirectory(nodecgIODir);
80+
await fs.promises.rm(nodecgIODir, { recursive: true, force: true });
8081
currentInstall = undefined;
8182
}
8283

src/uninstall/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import path = require("path");
1+
import * as path from "path";
2+
import * as fs from "fs";
23
import { CommandModule } from "yargs";
3-
import { directoryExists, removeDirectory } from "../utils/fs";
4+
import { directoryExists } from "../utils/fs";
45
import { logger } from "../utils/log";
56
import { manageBundleDir } from "../utils/nodecgConfig";
67
import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation";
@@ -36,7 +37,7 @@ export async function uninstall(): Promise<void> {
3637

3738
// Delete directory
3839
logger.debug(`Uninstalling nodecg-io from nodecg installation at ${nodecgDir}...`);
39-
await removeDirectory(nodecgIODir);
40+
await fs.promises.rm(nodecgIODir, { recursive: true, force: true });
4041

4142
logger.success("Successfully uninstalled nodecg-io.");
4243
}

src/utils/cli.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@ import * as semver from "semver";
66
import { SemVer } from "semver";
77

88
/**
9-
* Ensures that the cli is executed with at least node 12. This is the minimum version that is required
10-
* and if it is older the cli will exit.
9+
* Minimum node.js version that is required to use nodecg-io and this cli.
1110
*/
12-
export function ensureNode12(): void {
11+
const minimumNodeVersion = "14.14.0";
12+
13+
/**
14+
* Ensures that the node.js installation that is used to execute the cli
15+
* meets the required minimum node.js version for nodecg-io,
16+
* If it is older the cli will log an error about it and exit.
17+
*/
18+
export function ensureMinimumNodeVersion(): void {
1319
const nodeVersion = process.versions.node;
14-
const range = new semver.Range(">=12");
20+
const range = new semver.Range(`>=${minimumNodeVersion}`);
1521

1622
if (!semver.satisfies(nodeVersion, range)) {
1723
logger.error("Please update your node installation.");
18-
logger.error(
19-
`nodecg-io-cli requires at least node ${chalk.yellowBright("12")}. You have ${chalk.yellowBright(
20-
nodeVersion,
21-
)}`,
22-
);
24+
25+
const minVer = chalk.yellowBright(minimumNodeVersion);
26+
const curVer = chalk.yellowBright(nodeVersion);
27+
logger.error(`nodecg-io-cli requires at least node ${minVer}. You have ${curVer}`);
2328
process.exit(1);
2429
}
2530
}

src/utils/fs.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,6 @@ export async function ensureDirectory(dir: string): Promise<void> {
2929
}
3030
}
3131

32-
/**
33-
* Deletes a directory at the specified path in the filesystem.
34-
* @param dirPath the directory which should be deleted.
35-
*/
36-
export async function removeDirectory(dirPath: string): Promise<void> {
37-
// Delete all contents of this directory because otherwise we cannot remove it (why can't that be part of fs, oh node 14+ only...)
38-
const contents = await fs.readdir(dirPath); // get entries of directory
39-
40-
const rmPromises = contents.reverse().map(async (f) => {
41-
const subpath = path.join(dirPath, f);
42-
43-
try {
44-
const stat = await fs.lstat(subpath);
45-
// rm if file or symlink and use this function recursively if directory
46-
if (stat.isDirectory() && !stat.isSymbolicLink()) {
47-
await removeDirectory(subpath);
48-
} else {
49-
await fs.unlink(subpath);
50-
}
51-
} catch (_e) {
52-
// ignore that file cannot be removed. Maybe was already removed.
53-
}
54-
});
55-
56-
await Promise.all(rmPromises);
57-
58-
// now that the directory is empty we can delete it.
59-
await fs.rmdir(dirPath);
60-
}
61-
6232
/**
6333
* Executes the given command and optionally streams the output to the console.
6434
* @param command the command that should be executed.

src/utils/npm.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
22
import * as fs from "fs";
33
import * as path from "path";
4-
import { executeCommand, removeDirectory } from "./fs";
4+
import { executeCommand } from "./fs";
55
import { exec } from "child_process";
66
import { maxSatisfying, satisfies, SemVer } from "semver";
77
import * as zlib from "zlib";
@@ -223,7 +223,7 @@ export async function createNpmSymlinks(packages: NpmPackage[], nodecgIODir: str
223223
* @param nodecgIODir the directory in which nodecg-io is installed
224224
*/
225225
export async function removeNpmPackage(pkg: NpmPackage, nodecgIODir: string): Promise<void> {
226-
await removeDirectory(buildNpmPackagePath(pkg, nodecgIODir));
226+
await fs.promises.rm(buildNpmPackagePath(pkg, nodecgIODir), { recursive: true, force: true });
227227
}
228228

229229
/**

test/install/development.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { vol } from "memfs";
22
import * as git from "isomorphic-git";
3+
import * as fs from "fs";
34
import * as fsUtils from "../../src/utils/fs";
45
import { fsRoot, validDevInstall, nodecgIODir } from "../test.util";
56
import * as dev from "../../src/install/development";
6-
import { removeDirectory } from "../../src/utils/fs";
77

88
const defaultFetchResult: git.FetchResult = {
99
defaultBranch: "main",
@@ -69,7 +69,7 @@ describe("getGitRepo", () => {
6969

7070
test("should clone repo if directory does not exists", async () => {
7171
// remove dir so it should clone
72-
await removeDirectory(nodecgIODir);
72+
await fs.promises.rm(nodecgIODir, { recursive: true, force: true });
7373

7474
await dev.getGitRepo(nodecgIODir, "nodecg-io");
7575
expect(cloneSpy).toHaveBeenCalled();

test/install/production.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { vol } from "memfs";
22
import * as path from "path";
3+
import * as fs from "fs";
34
import { corePkg, dashboardPkg, nodecgIODir, twitchChatPkg, validProdInstall } from "../test.util";
45
import { diffPackages, installPackages, removePackages, validateInstall } from "../../src/install/production";
56
import * as installation from "../../src/utils/installation";
@@ -56,13 +57,14 @@ describe("diffPackages", () => {
5657

5758
describe("removePackages", () => {
5859
test("should rm each package directory", async () => {
59-
const rmMock = jest.spyOn(fsUtils, "removeDirectory").mockClear().mockResolvedValue();
60+
const rmMock = jest.spyOn(fs.promises, "rm").mockClear().mockResolvedValue();
6061
const i = { ...validProdInstall, packages: [...packages] };
6162
await removePackages(packages, i, nodecgIODir);
6263

6364
expect(rmMock).toHaveBeenCalledTimes(2);
64-
expect(rmMock).toHaveBeenCalledWith(path.join(nodecgIODir, corePkg.path));
65-
expect(rmMock).toHaveBeenLastCalledWith(path.join(nodecgIODir, twitchChatPkg.path));
65+
const rmOpts = { recursive: true, force: true };
66+
expect(rmMock).toHaveBeenCalledWith(path.join(nodecgIODir, corePkg.path), rmOpts);
67+
expect(rmMock).toHaveBeenLastCalledWith(path.join(nodecgIODir, twitchChatPkg.path), rmOpts);
6668
expect(i.packages.length).toBe(0);
6769
});
6870

@@ -106,7 +108,7 @@ describe("installPackages", () => {
106108

107109
test("should revert changes if npm install fails", async () => {
108110
npmInstallMock.mockRejectedValue(new Error("random error"));
109-
const rmMock = jest.spyOn(fsUtils, "removeDirectory").mockClear().mockResolvedValue();
111+
const rmMock = jest.spyOn(fs.promises, "rm").mockClear().mockResolvedValue();
110112

111113
// should return the error
112114
await expect(installPackages(packages, createInstall(), nodecgIODir)).rejects.toThrow("random error");

test/uninstall/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { vol } from "memfs";
22
import { uninstall } from "../../src/uninstall";
3-
import * as fsUtils from "../../src/utils/fs";
43
import * as nodecgInstall from "../../src/utils/nodecgInstallation";
54
import * as nodecgConfig from "../../src/utils/nodecgConfig";
65
import { fsRoot } from "../test.util";
76
import * as path from "path";
7+
import * as fs from "fs";
88

99
jest.mock("fs", () => vol);
1010
afterEach(() => vol.reset());
@@ -13,18 +13,18 @@ const nodecgIODir = path.join(fsRoot, "nodecg-io");
1313

1414
jest.spyOn(nodecgInstall, "findNodeCGDirectory").mockResolvedValue(fsRoot);
1515
const spyManageBundleDir = jest.spyOn(nodecgConfig, "manageBundleDir");
16-
const spyRemoveDirectory = jest.spyOn(fsUtils, "removeDirectory");
16+
const spyRm = jest.spyOn(fs.promises, "rm");
1717

1818
afterEach(() => {
1919
spyManageBundleDir.mockClear();
20-
spyRemoveDirectory.mockClear();
20+
spyRm.mockClear();
2121
});
2222

2323
describe("uninstall", () => {
2424
test("should not do anything if there is no nodecg-io directory", async () => {
2525
await uninstall();
2626

27-
expect(spyRemoveDirectory).not.toHaveBeenCalled();
27+
expect(spyRm).not.toHaveBeenCalled();
2828
expect(spyManageBundleDir).not.toHaveBeenCalled();
2929
});
3030

@@ -48,6 +48,6 @@ describe("uninstall", () => {
4848
await vol.promises.mkdir(nodecgIODir);
4949
await uninstall();
5050

51-
expect(spyRemoveDirectory).toHaveBeenCalledWith(nodecgIODir);
51+
expect(spyRm).toHaveBeenCalledWith(nodecgIODir, { recursive: true, force: true });
5252
});
5353
});

test/utils/fs.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { vol } from "memfs";
2-
import { directoryExists, ensureDirectory, executeCommand, removeDirectory } from "../../src/utils/fs";
2+
import { directoryExists, ensureDirectory, executeCommand } from "../../src/utils/fs";
33
import * as path from "path";
44
import * as child_process from "child_process";
55
import { testDir } from "../test.util";
@@ -40,24 +40,6 @@ describe("ensureDirectory", () => {
4040
});
4141
});
4242

43-
describe("removeDirectory", () => {
44-
test("should remove directory recursively", async () => {
45-
await vol.promises.mkdir(testDir);
46-
await vol.promises.writeFile(path.join(testDir, "test.txt"), "abc");
47-
await vol.promises.mkdir(path.join(testDir, "sub-dir"));
48-
49-
await removeDirectory(testDir);
50-
51-
// Directory should not be there anymore.
52-
await expect(vol.promises.stat(testDir)).rejects.toThrow("no such file or directory");
53-
});
54-
55-
test("should fail if directory does not exist", async () => {
56-
// should fail because the directory does not exist
57-
await expect(removeDirectory(testDir)).rejects.toThrow("no such file or directory");
58-
});
59-
});
60-
6143
describe("executeCommand", () => {
6244
test("should not error if the command successfully exits (code 0)", async () => {
6345
await executeCommand("exit", ["0"]);

test/utils/npm/pkgManagement.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { tempDir, corePkg, fsRoot, twitchChatPkg, dashboardPkg } from "../../test.util";
1010
import * as fsUtils from "../../../src/utils/fs";
1111
import * as path from "path";
12+
import * as fs from "fs";
1213

1314
jest.mock("fs", () => createFsFromVolume(vol));
1415
afterEach(() => vol.reset());
@@ -69,10 +70,12 @@ describe("createNpmSymlinks", () => {
6970
});
7071

7172
describe("removeNpmPackage", () => {
72-
test("should call to fsUtils.removeDirectory", async () => {
73-
const spy = jest.spyOn(fsUtils, "removeDirectory").mockResolvedValue();
73+
test("should call to fs.promises.rm to delete directory", async () => {
74+
const spy = jest.spyOn(fs.promises, "rm").mockResolvedValue();
7475
await removeNpmPackage(corePkg, await tempDir());
7576
expect(spy).toHaveBeenCalled();
77+
// Ensure that recursive deletion is enabled
78+
expect(spy.mock.calls[0]?.[1]?.recursive).toBe(true);
7679
});
7780
});
7881

0 commit comments

Comments
 (0)