diff --git a/.prettierrc.js b/.prettierrc.cjs similarity index 100% rename from .prettierrc.js rename to .prettierrc.cjs diff --git a/index.js b/index.js index 0d3861e..aa395a6 100755 --- a/index.js +++ b/index.js @@ -5,4 +5,4 @@ // but the entry-point needs to be executable so we can't have it in src/index.ts directly // because the resulting file won't have the executable flag and you can't properly use it that way. -require("./build/src/index"); +import "./build/index.js"; diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..6183c6a --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,12 @@ +/** @type {import("ts-jest").JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest/presets/default-esm", + testEnvironment: "node", + // We don't want to test nodecg, and without including this jest fails because it includes a invalid json + modulePathIgnorePatterns: ["/nodecg/"], + testMatch: ["/test/**/*.ts", "!**/*.util.ts"], + extensionsToTreatAsEsm: [".ts"], + moduleNameMapper: { + "^(\\.{1,2}/.*)\\.js$": "$1" + }, +}; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index ff805b4..0000000 --- a/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: "ts-jest", - testEnvironment: "node", - // We don't want to test nodecg, and without including it jest fails because it includes a invalid json - modulePathIgnorePatterns: ["/nodecg/"], - testMatch: ["/test/**/*.ts", "!**/*.util.ts"], -}; diff --git a/package.json b/package.json index de1a45a..62a22b8 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.3.1", "description": "The CLI to install and manage nodecg-io installations. Also helps you with nodecg-io bundle related development.", "main": "index.js", + "type": "module", "scripts": { "build": "tsc -b", "run": "tsc -b && node build/index.js", diff --git a/src/generate/extension.ts b/src/generate/extension.ts index d98a718..b811446 100644 --- a/src/generate/extension.ts +++ b/src/generate/extension.ts @@ -1,8 +1,8 @@ import CodeBlockWriter from "code-block-writer"; -import { getServiceClientName } from "../nodecgIOVersions"; -import { Installation } from "../utils/installation"; -import { CodeLanguage, GenerationOptions } from "./prompt"; -import { writeBundleFile } from "./utils"; +import { getServiceClientName } from "../nodecgIOVersions.js"; +import { Installation } from "../utils/installation.js"; +import { CodeLanguage, GenerationOptions } from "./prompt.js"; +import { writeBundleFile } from "./utils.js"; interface ServiceNames { name: string; @@ -39,7 +39,11 @@ export async function genExtension(opts: GenerationOptions, install: Installatio // the service names for each version are hardcoded and unknown for a development version. const services = install.dev === false ? opts.services.map((svc) => getServiceNames(svc, install.version)) : []; - const writer = new CodeBlockWriter(); + // FIXME: Types and jest(running via node.js) require "new CodeBlockWriter()", but when running with node.js + // this needs to be "new CodeBlockWriter.default()" to function, wtf?!. + // Needs figuring out why this is happening. + //@ts-ignore + const writer = new CodeBlockWriter.default(); // imports genImport(writer, "requireService", opts.corePackage.name, opts.language); diff --git a/src/generate/index.ts b/src/generate/index.ts index 8ac6168..5613564 100644 --- a/src/generate/index.ts +++ b/src/generate/index.ts @@ -1,17 +1,17 @@ import { CommandModule } from "yargs"; -import * as fs from "fs"; -import { logger } from "../utils/log"; -import { directoryExists } from "../utils/fs"; -import { Installation, readInstallInfo } from "../utils/installation"; -import { corePackages } from "../nodecgIOVersions"; -import { GenerationOptions, promptGenerationOpts } from "./prompt"; -import { runNpmBuild, runNpmInstall } from "../utils/npm"; -import { genExtension } from "./extension"; -import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation"; -import { genDashboard, genGraphic } from "./panel"; -import { genTsConfig } from "./tsConfig"; -import { writeBundleFile, yellowInstallCommand } from "./utils"; -import { genPackageJson } from "./packageJson"; +import { promises as fs } from "fs"; +import { logger } from "../utils/log.js"; +import { directoryExists } from "../utils/fs.js"; +import { Installation, readInstallInfo } from "../utils/installation.js"; +import { corePackages } from "../nodecgIOVersions.js"; +import { GenerationOptions, promptGenerationOpts } from "./prompt.js"; +import { runNpmBuild, runNpmInstall } from "../utils/npm.js"; +import { genExtension } from "./extension.js"; +import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation.js"; +import { genDashboard, genGraphic } from "./panel.js"; +import { genTsConfig } from "./tsConfig.js"; +import { writeBundleFile, yellowInstallCommand } from "./utils.js"; +import { genPackageJson } from "./packageJson.js"; export const generateModule: CommandModule = { command: "generate", @@ -64,11 +64,11 @@ export function ensureValidInstallation(install: Installation | undefined): inst export async function generateBundle(opts: GenerationOptions, install: Installation): Promise { // Create dir if necessary if (!(await directoryExists(opts.bundlePath))) { - await fs.promises.mkdir(opts.bundlePath); + await fs.mkdir(opts.bundlePath); } // In case some re-executes the command in a already used bundle name we should not overwrite their stuff and error instead. - const filesInBundleDir = await fs.promises.readdir(opts.bundlePath); + const filesInBundleDir = await fs.readdir(opts.bundlePath); if (filesInBundleDir.length > 0) { throw new Error( `Directory for bundle at ${opts.bundlePath} already exists and contains files.\n` + diff --git a/src/generate/packageJson.ts b/src/generate/packageJson.ts index ad0cc6a..0e2e178 100644 --- a/src/generate/packageJson.ts +++ b/src/generate/packageJson.ts @@ -1,10 +1,10 @@ -import { GenerationOptions } from "./prompt"; -import { logger } from "../utils/log"; -import { getLatestPackageVersion } from "../utils/npm"; -import { genNodeCGDashboardConfig, genNodeCGGraphicConfig } from "./panel"; +import { GenerationOptions } from "./prompt.js"; +import { logger } from "../utils/log.js"; +import { getLatestPackageVersion } from "../utils/npm.js"; +import { genNodeCGDashboardConfig, genNodeCGGraphicConfig } from "./panel.js"; import { SemVer } from "semver"; -import { writeBundleFile } from "./utils"; -import { Installation } from "../utils/installation"; +import { writeBundleFile } from "./utils.js"; +import { Installation } from "../utils/installation.js"; // Loaction where the development tarballs are hosted. export const developmentPublishRootUrl = "https://codeoverflow-org.github.io/nodecg-io-publish/"; diff --git a/src/generate/panel.ts b/src/generate/panel.ts index 8dc93a8..de7736a 100644 --- a/src/generate/panel.ts +++ b/src/generate/panel.ts @@ -1,5 +1,5 @@ -import { GenerationOptions } from "./prompt"; -import { writeBundleFile } from "./utils"; +import { GenerationOptions } from "./prompt.js"; +import { writeBundleFile } from "./utils.js"; type PanelType = "graphic" | "dashboard"; diff --git a/src/generate/prompt.ts b/src/generate/prompt.ts index 03ff338..64a4740 100644 --- a/src/generate/prompt.ts +++ b/src/generate/prompt.ts @@ -1,13 +1,13 @@ -import * as semver from "semver"; -import * as inquirer from "inquirer"; +import semver from "semver"; +import inquirer from "inquirer"; import * as path from "path"; -import { directoryExists } from "../utils/fs"; -import { Installation } from "../utils/installation"; -import { getServicesFromInstall } from "../install/prompt"; -import { yellowInstallCommand } from "./utils"; -import { findNpmPackages, NpmPackage } from "../utils/npm"; -import { corePackage } from "../nodecgIOVersions"; -import { getNodeCGIODirectory } from "../utils/nodecgInstallation"; +import { directoryExists } from "../utils/fs.js"; +import { Installation } from "../utils/installation.js"; +import { getServicesFromInstall } from "../install/prompt.js"; +import { yellowInstallCommand } from "./utils.js"; +import { findNpmPackages, NpmPackage } from "../utils/npm.js"; +import { corePackage } from "../nodecgIOVersions.js"; +import { getNodeCGIODirectory } from "../utils/nodecgInstallation.js"; /** * Describes all options for bundle generation a user has answered with inside the inquirer prompt diff --git a/src/generate/tsConfig.ts b/src/generate/tsConfig.ts index 9a5873d..9f7ed0a 100644 --- a/src/generate/tsConfig.ts +++ b/src/generate/tsConfig.ts @@ -1,5 +1,5 @@ -import { GenerationOptions } from "./prompt"; -import { writeBundleFile } from "./utils"; +import { GenerationOptions } from "./prompt.js"; +import { writeBundleFile } from "./utils.js"; /** * Generates a tsconfig.json for a bundle if the language was set to typescript. diff --git a/src/generate/utils.ts b/src/generate/utils.ts index 7745a45..346109e 100644 --- a/src/generate/utils.ts +++ b/src/generate/utils.ts @@ -1,8 +1,8 @@ import * as path from "path"; -import { logger } from "../utils/log"; -import { directoryExists } from "../utils/fs"; -import * as fs from "fs"; -import * as chalk from "chalk"; +import { logger } from "../utils/log.js"; +import { directoryExists } from "../utils/fs.js"; +import { promises as fs } from "fs"; +import chalk from "chalk"; // Colored commands for logging purposes. export const yellowInstallCommand = chalk.yellow("nodecg-io install"); @@ -20,9 +20,9 @@ export async function writeBundleFile(content: string | Record, // Create directory if missing const parent = path.dirname(finalPath); if (!(await directoryExists(parent))) { - await fs.promises.mkdir(parent); + await fs.mkdir(parent); } const str = typeof content === "string" ? content : JSON.stringify(content, null, 4); - await fs.promises.writeFile(finalPath, str); + await fs.writeFile(finalPath, str); } diff --git a/src/index.ts b/src/index.ts index 41abe28..a435d15 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,29 +1,32 @@ import yargs from "yargs"; -import { installModule } from "./install"; -import { uninstallModule } from "./uninstall"; -import { version } from "../package.json"; -import { checkForCliUpdate, ensureMinimumNodeVersion } from "./utils/cli"; -import { generateModule } from "./generate"; +import { installModule } from "./install/index.js"; +import { uninstallModule } from "./uninstall/index.js"; +import { checkForCliUpdate, ensureMinimumNodeVersion, getCliVersion } from "./utils/cli.js"; +import { generateModule } from "./generate/index.js"; // This file gets imported by the index.js file of the repository root. -const args = yargs(process.argv.slice(2)) - .scriptName("nodecg-io") - .usage("$0 [args]") - .version(version) - .command(installModule) - .command(uninstallModule) - .command(generateModule) - .option("disable-updates", { type: "boolean", description: "Disables check for nodecg-io-cli updates" }) - .strict() - .demandCommand() - .parserConfiguration({ - "dot-notation": false, - }) - .parse(); - ensureMinimumNodeVersion(); (async () => { + const cliVersion = await getCliVersion(); + + const args = yargs(process.argv.slice(2)) + .scriptName("nodecg-io") + .usage("$0 [args]") + .version(cliVersion) + .command(installModule) + .command(uninstallModule) + .command(generateModule) + .option("disable-updates", { type: "boolean", description: "Disables check for nodecg-io-cli updates" }) + .strict() + .demandCommand() + .parserConfiguration({ + "dot-notation": false, + }) + .parse(); + + ensureMinimumNodeVersion(); + const opts = await args; if (!opts["disable-updates"]) { checkForCliUpdate(); diff --git a/src/install/development.ts b/src/install/development.ts index 4656f34..f6967a8 100644 --- a/src/install/development.ts +++ b/src/install/development.ts @@ -1,13 +1,13 @@ -import * as chalk from "chalk"; +import chalk from "chalk"; import * as git from "isomorphic-git"; import * as fs from "fs"; -import * as http from "isomorphic-git/http/node"; -import { directoryExists } from "../utils/fs"; -import { DevelopmentInstallation, writeInstallInfo } from "../utils/installation"; -import { logger } from "../utils/log"; +import * as http from "isomorphic-git/http/node/index.js"; +import { directoryExists } from "../utils/fs.js"; +import { DevelopmentInstallation, writeInstallInfo } from "../utils/installation.js"; +import { logger } from "../utils/log.js"; import * as path from "path"; -import * as glob from "glob"; -import { runNpmBuild, runNpmInstall } from "../utils/npm"; +import glob from "glob"; +import { runNpmBuild, runNpmInstall } from "../utils/npm.js"; type CloneRepository = "nodecg-io" | "nodecg-io-docs"; const nodecgIOCloneURL = "https://github.com/codeoverflow-org/nodecg-io.git"; diff --git a/src/install/index.ts b/src/install/index.ts index ca48c18..aad4d39 100644 --- a/src/install/index.ts +++ b/src/install/index.ts @@ -1,15 +1,15 @@ import { CommandModule } from "yargs"; import * as path from "path"; import * as fs from "fs"; -import { directoryExists } from "../utils/fs"; -import { createDevInstall } from "./development"; -import { manageBundleDir } from "../utils/nodecgConfig"; -import { promptForInstallInfo } from "./prompt"; -import { readInstallInfo } from "../utils/installation"; -import { createProductionInstall } from "./production"; -import { logger } from "../utils/log"; -import { requireNpmV7 } from "../utils/npm"; -import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation"; +import { directoryExists } from "../utils/fs.js"; +import { createDevInstall } from "./development.js"; +import { manageBundleDir } from "../utils/nodecgConfig.js"; +import { promptForInstallInfo } from "./prompt.js"; +import { readInstallInfo } from "../utils/installation.js"; +import { createProductionInstall } from "./production.js"; +import { logger } from "../utils/log.js"; +import { requireNpmV7 } from "../utils/npm.js"; +import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation.js"; export interface InstallCommandOptions { "nodecg-io-version"?: string; diff --git a/src/install/production.ts b/src/install/production.ts index 80f2e2a..7e72297 100644 --- a/src/install/production.ts +++ b/src/install/production.ts @@ -1,4 +1,4 @@ -import { ProductionInstallation, writeInstallInfo } from "../utils/installation"; +import { ProductionInstallation, writeInstallInfo } from "../utils/installation.js"; import { NpmPackage, removeNpmPackage, @@ -8,12 +8,12 @@ import { downloadNpmPackage, createNpmSymlinks, getSubPackages, -} from "../utils/npm"; -import { directoryExists, ensureDirectory } from "../utils/fs"; -import { logger } from "../utils/log"; +} from "../utils/npm.js"; +import { directoryExists, ensureDirectory } from "../utils/fs.js"; +import { logger } from "../utils/log.js"; import { promises as fs } from "fs"; -import path = require("path"); -import chalk = require("chalk"); +import * as path from "path"; +import chalk from "chalk"; export async function createProductionInstall( requested: ProductionInstallation, diff --git a/src/install/prompt.ts b/src/install/prompt.ts index 21d0c1d..46cf4a0 100644 --- a/src/install/prompt.ts +++ b/src/install/prompt.ts @@ -1,8 +1,8 @@ -import { Installation } from "../utils/installation"; -import * as inquirer from "inquirer"; -import { getHighestPatchVersion, getMinorVersions, NpmPackage } from "../utils/npm"; -import * as semver from "semver"; -import { logger } from "../utils/log"; +import { Installation } from "../utils/installation.js"; +import inquirer from "inquirer"; +import { getHighestPatchVersion, getMinorVersions, NpmPackage } from "../utils/npm.js"; +import semver from "semver"; +import { logger } from "../utils/log.js"; import { corePackage, corePackages, @@ -11,8 +11,8 @@ import { developmentVersion, getServicesForVersion, supportedNodeCGIORange, -} from "../nodecgIOVersions"; -import { InstallCommandOptions } from "."; +} from "../nodecgIOVersions.js"; +import { InstallCommandOptions } from "./index.js"; interface PromptInput { version: string; diff --git a/src/nodecgIOVersions.ts b/src/nodecgIOVersions.ts index 38d3812..7ea02e6 100644 --- a/src/nodecgIOVersions.ts +++ b/src/nodecgIOVersions.ts @@ -1,4 +1,4 @@ -import * as semver from "semver"; +import semver from "semver"; import * as path from "path"; export const corePackage = "nodecg-io-core"; diff --git a/src/uninstall/index.ts b/src/uninstall/index.ts index 0e36ce4..5a935ce 100644 --- a/src/uninstall/index.ts +++ b/src/uninstall/index.ts @@ -1,10 +1,10 @@ import * as path from "path"; -import * as fs from "fs"; +import { promises as fs } from "fs"; import { CommandModule } from "yargs"; -import { directoryExists } from "../utils/fs"; -import { logger } from "../utils/log"; -import { manageBundleDir } from "../utils/nodecgConfig"; -import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation"; +import { directoryExists } from "../utils/fs.js"; +import { logger } from "../utils/log.js"; +import { manageBundleDir } from "../utils/nodecgConfig.js"; +import { findNodeCGDirectory, getNodeCGIODirectory } from "../utils/nodecgInstallation.js"; export const uninstallModule: CommandModule = { command: "uninstall", @@ -37,7 +37,7 @@ export async function uninstall(): Promise { // Delete directory logger.debug(`Uninstalling nodecg-io from nodecg installation at ${nodecgDir}...`); - await fs.promises.rm(nodecgIODir, { recursive: true, force: true }); + await fs.rm(nodecgIODir, { recursive: true, force: true }); logger.success("Successfully uninstalled nodecg-io."); } diff --git a/src/utils/cli.ts b/src/utils/cli.ts index a3aca62..7928f4f 100644 --- a/src/utils/cli.ts +++ b/src/utils/cli.ts @@ -1,14 +1,18 @@ -import { version as cliVersion, name as cliPkgName } from "../../package.json"; -import { logger } from "./log"; +import { logger } from "./log.js"; import * as chalk from "chalk"; -import { getLatestPackageVersion } from "./npm"; -import * as semver from "semver"; +import { getLatestPackageVersion } from "./npm.js"; +import semver from "semver"; import { SemVer } from "semver"; +import { promises as fs } from "fs"; /** * Minimum node.js version that is required to use nodecg-io and this cli. */ const minimumNodeVersion = "14.14.0"; +/** + * Name of the package containing the nodecg-io-cli. + */ +const cliPkgName = "nodecg-io-cli"; /** * Ensures that the node.js installation that is used to execute the cli @@ -29,6 +33,15 @@ export function ensureMinimumNodeVersion(): void { } } +/** + * Reads the version of the cli from the package.json and returns it. + */ +export async function getCliVersion(): Promise { + const pkgJsonString = await fs.readFile(new URL("../../package.json", import.meta.url)); + const pkgJson = JSON.parse(pkgJsonString.toString()); + return pkgJson.version; +} + /** * Checks for a update of nodecg-io-cli and logs a message with the newest version and update instructions * when exiting. @@ -36,19 +49,22 @@ export function ensureMinimumNodeVersion(): void { export async function checkForCliUpdate(): Promise { try { const newestVersion = await getLatestPackageVersion(cliPkgName); + const cliVersion = await getCliVersion(); if (newestVersion && semver.gt(newestVersion, cliVersion)) { - logUpdateOnExit(newestVersion); + logUpdateOnExit(cliVersion, newestVersion); } } catch (e) { logger.warn(`Cannot check for cli updates: ${e}`); } } -function logUpdateOnExit(newestVersion: SemVer) { +function logUpdateOnExit(currentVersion: string, newestVersion: SemVer) { process.on("exit", () => { logger.info( - `There is a nodecg-io-cli update available: ${chalk.yellow(cliVersion)} -> ${chalk.green(newestVersion)}`, + `There is a nodecg-io-cli update available: ${chalk.yellow(currentVersion)} -> ${chalk.green( + newestVersion, + )}`, ); logger.info(`Run the following command to update nodecg-io-cli: ${chalk.blue(`npm i -g ${cliPkgName}`)}`); }); diff --git a/src/utils/fs.ts b/src/utils/fs.ts index a93521a..e2b6fae 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -1,7 +1,6 @@ import { promises as fs } from "fs"; -import * as path from "path"; import { spawn } from "child_process"; -import { logger } from "./log"; +import { logger } from "./log.js"; /** * Checks whether the specified directory exists or not. diff --git a/src/utils/log.ts b/src/utils/log.ts index 57df072..9ffd8c1 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -1,4 +1,4 @@ -import * as chalk from "chalk"; +import chalk from "chalk"; // This is a very small logger that just adapts the color of the messages that you log // unimportant messages get dim colors and important ones get brighter colors. diff --git a/src/utils/nodecgConfig.ts b/src/utils/nodecgConfig.ts index 9692a7b..c9f3b52 100644 --- a/src/utils/nodecgConfig.ts +++ b/src/utils/nodecgConfig.ts @@ -1,7 +1,7 @@ import * as path from "path"; import { promises as fs } from "fs"; -import { logger } from "./log"; -import { directoryExists } from "./fs"; +import { logger } from "./log.js"; +import { directoryExists } from "./fs.js"; /** * A configuration of nodecg according to https://www.nodecg.dev/docs/nodecg-configuration/ diff --git a/src/utils/nodecgInstallation.ts b/src/utils/nodecgInstallation.ts index f117b0b..64315cb 100644 --- a/src/utils/nodecgInstallation.ts +++ b/src/utils/nodecgInstallation.ts @@ -1,7 +1,7 @@ import * as path from "path"; import * as fs from "fs"; -import { SemVer } from "semver"; -import { directoryExists } from "./fs"; +import * as semver from "semver"; +import { directoryExists } from "./fs.js"; /** * Traverses the filesystem and uses {@link isNodeCGDirectory} to find a local nodecg installation. @@ -45,14 +45,14 @@ async function isNodeCGDirectory(dir: string): Promise { * @param nodecgDir the directory in which nodecg is located. * @returns the version of the nodecg installation */ -export async function getNodeCGVersion(nodecgDir: string): Promise { +export async function getNodeCGVersion(nodecgDir: string): Promise { const packageJson = await readPackageJson(nodecgDir); const version = packageJson["version"]; if (version === undefined) { throw new Error("Version field is missin in the NodeCG package.json."); } - return new SemVer(version); + return new semver.SemVer(version); } async function readPackageJson(nodecgDir: string): Promise> { diff --git a/src/utils/npm.ts b/src/utils/npm.ts index bdfa1f6..4d98554 100644 --- a/src/utils/npm.ts +++ b/src/utils/npm.ts @@ -1,9 +1,9 @@ import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; -import * as fs from "fs"; +import { promises as fs, ReadStream } from "fs"; import * as path from "path"; -import { executeCommand } from "./fs"; +import { executeCommand } from "./fs.js"; import { exec } from "child_process"; -import { maxSatisfying, satisfies, SemVer } from "semver"; +import semver from "semver"; import * as zlib from "zlib"; import * as tar from "tar-fs"; @@ -49,12 +49,14 @@ export function isPackageEquals(a: NpmPackage, b: NpmPackage): boolean { * @param packageName which package you want the versions to. * @returns the versions of the package */ -export async function getPackageVersions(packageName: string): Promise { - const response = (await axios(npmRegistryEndpoint + packageName, axiosNpmMetadataConfig)) as AxiosResponse<{ +export async function getPackageVersions(packageName: string): Promise { + // FIXME: types require "axios" while node.js requires "axios.defaults". Needs to be figured out. + // @ts-ignore + const response = (await axios.default(npmRegistryEndpoint + packageName, axiosNpmMetadataConfig)) as AxiosResponse<{ versions?: Record; }>; if (response.data.versions) { - return Object.keys(response.data.versions).map((versionString) => new SemVer(versionString)); + return Object.keys(response.data.versions).map((versionString) => new semver.SemVer(versionString)); } else { // Version field is missing when e.g. the package has been fully unpublished // see https://www.npmjs.com/policies/unpublish for further details. @@ -67,8 +69,10 @@ export async function getPackageVersions(packageName: string): Promise * @param packageName the package for which you want to get the latest published version. * @return the latest version if the package was found and null if the package was not found on the npm registry. */ -export async function getLatestPackageVersion(packageName: string): Promise { - const response = await axios(npmRegistryEndpoint + packageName, axiosNpmMetadataConfig); +export async function getLatestPackageVersion(packageName: string): Promise { + // FIXME: types require "axios" while node.js requires "axios.defaults". Needs to be figured out. + // @ts-ignore + const response = await axios.default(npmRegistryEndpoint + packageName, axiosNpmMetadataConfig); // Gets version through npm tag "latest" so we don't use any pre-release or beta versions const latest = response.data["dist-tags"]["latest"]; @@ -76,7 +80,7 @@ export async function getLatestPackageVersion(packageName: string): Promise { * @param packageName the package you want the highest patch version to * @param majorMinor the major.minor version that you want to get the highest patch version of */ -export async function getHighestPatchVersion(packageName: string, majorMinor: string): Promise { +export async function getHighestPatchVersion(packageName: string, majorMinor: string): Promise { const allVersions = await getPackageVersions(packageName); - return maxSatisfying(allVersions, "~" + majorMinor); + return semver.maxSatisfying(allVersions, "~" + majorMinor); } /** @@ -123,7 +127,7 @@ export function buildNpmPackagePath(pkg: NpmPackage, nodecgIODir: string): strin * Creates a read stream that will be fed the tarball of the passed package directly from the official npm registry. * @param pkg the package to fetch */ -export async function createNpmPackageReadStream(pkg: NpmPackage): Promise { +export async function createNpmPackageReadStream(pkg: NpmPackage): Promise { const response = await axios({ url: buildNpmPackageURL(pkg), method: "GET", @@ -139,11 +143,7 @@ export async function createNpmPackageReadStream(pkg: NpmPackage): Promise { +export async function extractNpmPackageTar(pkg: NpmPackage, tarStream: ReadStream, nodecgIODir: string): Promise { const extractStream = tarStream.pipe(zlib.createGunzip({ level: 3 })).pipe( tar.extract(buildNpmPackagePath(pkg, nodecgIODir), { map: (header) => { @@ -204,12 +204,12 @@ export async function createNpmSymlinks(packages: NpmPackage[], nodecgIODir: str return pkg.symlink.map(async (linkPkg) => { const pkgNodeModules = path.join(buildNpmPackagePath(pkg, nodecgIODir), nodeModulesDir); - await fs.promises.mkdir(pkgNodeModules); + await fs.mkdir(pkgNodeModules); const linkModulePath = path.join(pkgNodeModules, linkPkg); const hoistedPath = path.join(nodecgIODir, nodeModulesDir, linkPkg); - await fs.promises.symlink(hoistedPath, linkModulePath, "junction"); + await fs.symlink(hoistedPath, linkModulePath, "junction"); }); }) .flat(); @@ -223,7 +223,7 @@ export async function createNpmSymlinks(packages: NpmPackage[], nodecgIODir: str * @param nodecgIODir the directory in which nodecg-io is installed */ export async function removeNpmPackage(pkg: NpmPackage, nodecgIODir: string): Promise { - await fs.promises.rm(buildNpmPackagePath(pkg, nodecgIODir), { recursive: true, force: true }); + await fs.rm(buildNpmPackagePath(pkg, nodecgIODir), { recursive: true, force: true }); } /** @@ -244,7 +244,7 @@ export async function findNpmPackages(basePath: string): Promise { const pkg = await getNpmPackageFromPath(basePath); // Enumerate sub directories and get any packages in these too - const subDirs = await fs.promises.readdir(basePath, { withFileTypes: true }); + const subDirs = await fs.readdir(basePath, { withFileTypes: true }); const subPackages = await Promise.all( subDirs .filter((f) => f.isDirectory()) @@ -264,7 +264,7 @@ export async function findNpmPackages(basePath: string): Promise { async function getNpmPackageFromPath(basePath: string): Promise { const packageJsonPath = `${basePath}/package.json`; try { - const packageJson = await fs.promises.readFile(packageJsonPath, "utf8"); + const packageJson = await fs.readFile(packageJsonPath, "utf8"); const pkg = JSON.parse(packageJson); if (pkg.private) return undefined; @@ -282,7 +282,7 @@ async function getNpmPackageFromPath(basePath: string): Promise { +export function getNpmVersion(): Promise { return new Promise((resolve, reject) => { const child = exec("npm --version", (err, stdout) => { if (err) { @@ -294,7 +294,7 @@ export function getNpmVersion(): Promise { reject(err); } } else { - const ver = new SemVer(stdout); + const ver = new semver.SemVer(stdout); resolve(ver); } }); @@ -311,7 +311,7 @@ export async function requireNpmV7(): Promise { throw new Error("Could not find npm. Make sure npm is installed and in your $PATH."); } - if (!satisfies(version, ">=7")) { + if (!semver.satisfies(version, ">=7")) { throw new Error( `The nodecg-io cli requires a npm version of 7.0.0 or higher. You have ${version.version}.` + '\nUpdate npm by running "npm install -g npm".', diff --git a/test/generate/index.ts b/test/generate/index.ts index 21e76d1..a94b3bb 100644 --- a/test/generate/index.ts +++ b/test/generate/index.ts @@ -1,3 +1,4 @@ +import { jest } from "@jest/globals"; import { vol } from "memfs"; import { corePkg, fsRoot, nodecgPackageJsonStr, twitchChatPkg, validDevInstall, validProdInstall } from "../test.util"; import { SemVer } from "semver"; diff --git a/test/generate/panel.ts b/test/generate/panel.ts index 773ceb6..7878a4c 100644 --- a/test/generate/panel.ts +++ b/test/generate/panel.ts @@ -1,7 +1,8 @@ +import { jest } from "@jest/globals"; import { vol } from "memfs"; import * as path from "path"; -import { defaultOpts } from "./opts.util"; -import { genDashboard, genGraphic } from "../../src/generate/panel"; +import { defaultOpts } from "./opts.util.js"; +import { genDashboard, genGraphic } from "../../src/generate/panel.js"; jest.mock("fs", () => vol); beforeEach(() => vol.promises.mkdir(defaultOpts.bundlePath, { recursive: true })); diff --git a/test/generate/tsConfig.ts b/test/generate/tsConfig.ts index 6bfbdff..36431b8 100644 --- a/test/generate/tsConfig.ts +++ b/test/generate/tsConfig.ts @@ -1,6 +1,6 @@ import { vol } from "memfs"; -import { genTsConfig } from "../../src/generate/tsConfig"; -import { defaultOpts, jsOpts } from "./opts.util"; +import { genTsConfig } from "../../src/generate/tsConfig.js"; +import { defaultOpts, jsOpts } from "./opts.util.js"; import * as path from "path"; jest.mock("fs", () => vol); diff --git a/test/install/production.ts b/test/install/production.ts index 310e066..b9dc1ee 100644 --- a/test/install/production.ts +++ b/test/install/production.ts @@ -1,11 +1,12 @@ import { vol } from "memfs"; import * as path from "path"; import * as fs from "fs"; -import { corePkg, dashboardPkg, nodecgIODir, twitchChatPkg, validProdInstall } from "../test.util"; -import { diffPackages, installPackages, removePackages, validateInstall } from "../../src/install/production"; -import * as installation from "../../src/utils/installation"; -import * as fsUtils from "../../src/utils/fs"; -import * as npm from "../../src/utils/npm"; +import { corePkg, dashboardPkg, nodecgIODir, twitchChatPkg, validProdInstall } from "../test.util.js"; +import { diffPackages, installPackages, removePackages, validateInstall } from "../../src/install/production.js"; +import * as installation from "../../src/utils/installation.js"; +import * as fsUtils from "../../src/utils/fs.js"; +import * as npm from "../../src/utils/npm.js"; +import { jest } from "@jest/globals"; jest.mock("fs", () => vol); beforeEach(() => vol.promises.mkdir(nodecgIODir)); @@ -57,7 +58,7 @@ describe("diffPackages", () => { describe("removePackages", () => { test("should rm each package directory", async () => { - const rmMock = jest.spyOn(fs.promises, "rm").mockClear().mockResolvedValue(); + const rmMock = jest.spyOn(vol.promises, "rm").mockClear().mockResolvedValue(); const i = { ...validProdInstall, packages: [...packages] }; await removePackages(packages, i, nodecgIODir); @@ -108,7 +109,7 @@ describe("installPackages", () => { test("should revert changes if npm install fails", async () => { npmInstallMock.mockRejectedValue(new Error("random error")); - const rmMock = jest.spyOn(fs.promises, "rm").mockClear().mockResolvedValue(); + const rmMock = jest.spyOn(vol.promises, "rm").mockClear().mockResolvedValue(); // should return the error await expect(installPackages(packages, createInstall(), nodecgIODir)).rejects.toThrow("random error"); diff --git a/test/uninstall/index.ts b/test/uninstall/index.ts index deb000c..fb50671 100644 --- a/test/uninstall/index.ts +++ b/test/uninstall/index.ts @@ -4,7 +4,6 @@ import * as nodecgInstall from "../../src/utils/nodecgInstallation"; import * as nodecgConfig from "../../src/utils/nodecgConfig"; import { fsRoot } from "../test.util"; import * as path from "path"; -import * as fs from "fs"; jest.mock("fs", () => vol); afterEach(() => vol.reset()); @@ -13,7 +12,7 @@ const nodecgIODir = path.join(fsRoot, "nodecg-io"); jest.spyOn(nodecgInstall, "findNodeCGDirectory").mockResolvedValue(fsRoot); const spyManageBundleDir = jest.spyOn(nodecgConfig, "manageBundleDir"); -const spyRm = jest.spyOn(fs.promises, "rm"); +const spyRm = jest.spyOn(vol.promises, "rm"); afterEach(() => { spyManageBundleDir.mockClear(); diff --git a/test/utils/fs.ts b/test/utils/fs.ts index 8d2c94e..af565ce 100644 --- a/test/utils/fs.ts +++ b/test/utils/fs.ts @@ -1,10 +1,15 @@ import { vol } from "memfs"; import { directoryExists, ensureDirectory, executeCommand } from "../../src/utils/fs"; -import * as path from "path"; import * as child_process from "child_process"; import { testDir } from "../test.util"; import { logger } from "../../src/utils/log"; +// FIXME: this only works with this mock that fakes child_process to be a es module. Don't know why, yet. +jest.mock("child_process", () => ({ + __esModule: true, + ...(jest.requireActual("child_process") as object), +})); + jest.mock("fs", () => vol); afterEach(() => vol.reset()); diff --git a/test/utils/npm/npmVersion.ts b/test/utils/npm/npmVersion.ts index 902478f..18258da 100644 --- a/test/utils/npm/npmVersion.ts +++ b/test/utils/npm/npmVersion.ts @@ -3,6 +3,12 @@ import { ExecException } from "child_process"; import { getNpmVersion, requireNpmV7 } from "../../../src/utils/npm"; import { oldNpmVersion, validNpmVersion } from "../../test.util"; +// FIXME: this only works with this mock that fakes child_process to be a es module. Don't know why, yet. +jest.mock("child_process", () => ({ + __esModule: true, + ...jest.requireActual("child_process"), +})); + function createExecMock(versionValue: string | undefined) { const execSpy = jest.spyOn(child_process, "exec"); diff --git a/test/utils/npm/registryVersions.ts b/test/utils/npm/registryVersions.ts index b264057..91649ad 100644 --- a/test/utils/npm/registryVersions.ts +++ b/test/utils/npm/registryVersions.ts @@ -1,4 +1,4 @@ -import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from "axios"; +import type { AxiosResponse, AxiosRequestConfig, AxiosPromise } from "axios"; import { getHighestPatchVersion, getLatestPackageVersion, diff --git a/tsconfig.json b/tsconfig.json index 847ccb2..d6dd3d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ // Module resolution "moduleResolution": "node", - "resolveJsonModule": true, + "esModuleInterop": true, // Type checking "lib": ["es2019"], @@ -20,7 +20,7 @@ "skipLibCheck": true, "noUncheckedIndexedAccess": true, - "module": "CommonJS", + "module": "ES2022", "outDir": "build" }, "include": ["src/**/*"],