From 7c6cfdc5cda873ddb0681a76a8c14d9d3c78b8e3 Mon Sep 17 00:00:00 2001 From: Keiichiro Amemiya Date: Fri, 27 Dec 2024 09:59:50 +0100 Subject: [PATCH] refactor: remove default exports (#121) --- src/commands/defaultconfig.ts | 6 +- src/commands/install.ts | 8 +- src/commands/setup.ts | 14 ++- src/commands/start.ts | 4 +- src/commands/uninstall.ts | 4 +- src/dts/packages.d.ts | 1 - src/index.ts | 5 +- src/lib/fetch-tags.ts | 2 +- src/lib/install-bundle-deps.ts | 6 +- src/lib/sample/npm-release.json | 98 ++++++++++++++++ src/lib/sample/npm-release.ts | 3 + src/lib/util.ts | 201 +++++++++++++------------------- tsconfig.json | 3 +- 13 files changed, 213 insertions(+), 142 deletions(-) delete mode 100644 src/dts/packages.d.ts create mode 100644 src/lib/sample/npm-release.json create mode 100644 src/lib/sample/npm-release.ts diff --git a/src/commands/defaultconfig.ts b/src/commands/defaultconfig.ts index daa3eaf..34a8f75 100644 --- a/src/commands/defaultconfig.ts +++ b/src/commands/defaultconfig.ts @@ -5,7 +5,7 @@ import { Ajv, type JSONSchemaType } from "ajv"; import chalk from "chalk"; import { Command } from "commander"; -import util from "../lib/util.js"; +import { getNodeCGPath, isBundleFolder } from "../lib/util.js"; const ajv = new Ajv({ useDefaults: true, strict: true }); @@ -18,10 +18,10 @@ export function defaultconfigCommand(program: Command) { function action(bundleName?: string) { const cwd = process.cwd(); - const nodecgPath = util.getNodeCGPath(); + const nodecgPath = getNodeCGPath(); if (!bundleName) { - if (util.isBundleFolder(cwd)) { + if (isBundleFolder(cwd)) { bundleName = bundleName ?? path.basename(cwd); } else { console.error( diff --git a/src/commands/install.ts b/src/commands/install.ts index 34a8826..0a12275 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -9,9 +9,9 @@ import HostedGitInfo from "hosted-git-info"; import npa from "npm-package-arg"; import semver, { SemVer } from "semver"; -import fetchTags from "../lib/fetch-tags.js"; -import installBundleDeps from "../lib/install-bundle-deps.js"; -import util from "../lib/util.js"; +import { fetchTags } from "../lib/fetch-tags.js"; +import { installBundleDeps } from "../lib/install-bundle-deps.js"; +import { getNodeCGPath } from "../lib/util.js"; export function installCommand(program: Command) { program @@ -40,7 +40,7 @@ function action(repo: string, options: { dev: boolean }) { repo = repoParts[0] ?? ""; } - const nodecgPath = util.getNodeCGPath(); + const nodecgPath = getNodeCGPath(); const parsed = npa(repo); if (!parsed.hosted) { console.error( diff --git a/src/commands/setup.ts b/src/commands/setup.ts index cd214ec..13bc053 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -9,8 +9,12 @@ import inquirer from "inquirer"; import semver from "semver"; import * as tar from "tar"; -import fetchTags from "../lib/fetch-tags.js"; -import util from "../lib/util.js"; +import { fetchTags } from "../lib/fetch-tags.js"; +import { + getCurrentNodeCGVersion, + getNodeCGRelease, + pathContainsNodeCG, +} from "../lib/util.js"; const NODECG_GIT_URL = "https://github.com/nodecg/nodecg.git"; @@ -36,7 +40,7 @@ async function decideActionVersion( // If NodeCG exists in the cwd, but the `-u` flag was not supplied, display an error and return. // If it was supplied, fetch the latest tags and set the `isUpdate` flag to true for later use. // Else, if this is a clean, empty directory, then we need to clone a fresh copy of NodeCG into the cwd. - if (util.pathContainsNodeCG(process.cwd())) { + if (pathContainsNodeCG(process.cwd())) { if (!options.update) { console.error("NodeCG is already installed in this directory."); console.error( @@ -101,7 +105,7 @@ async function decideActionVersion( let downgrade = false; if (isUpdate) { - current = util.getCurrentNodeCGVersion(); + current = getCurrentNodeCGVersion(); if (semver.eq(target, current)) { console.log( @@ -230,7 +234,7 @@ async function actionV2( } process.stdout.write(`Downloading ${target} from npm... `); - const release = await util.getNodeCGRelease(target); + const release = await getNodeCGRelease(target); process.stdout.write(chalk.green("done!") + os.EOL); diff --git a/src/commands/start.ts b/src/commands/start.ts index 8a72efb..7ae520e 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -2,7 +2,7 @@ import { execSync } from "node:child_process"; import { Command } from "commander"; -import util from "../lib/util.js"; +import { pathContainsNodeCG } from "../lib/util.js"; export function startCommand(program: Command) { program @@ -11,7 +11,7 @@ export function startCommand(program: Command) { .description("Start NodeCG") .action((options: { disableSourceMaps: boolean }) => { // Check if nodecg is already installed - if (util.pathContainsNodeCG(process.cwd())) { + if (pathContainsNodeCG(process.cwd())) { if (options.disableSourceMaps) { execSync("node index.js", { stdio: "inherit" }); } else { diff --git a/src/commands/uninstall.ts b/src/commands/uninstall.ts index 52e4f5f..9756fd2 100644 --- a/src/commands/uninstall.ts +++ b/src/commands/uninstall.ts @@ -6,7 +6,7 @@ import chalk from "chalk"; import { Command } from "commander"; import inquirer from "inquirer"; -import util from "../lib/util.js"; +import { getNodeCGPath } from "../lib/util.js"; export function uninstallCommand(program: Command) { program @@ -17,7 +17,7 @@ export function uninstallCommand(program: Command) { } function action(bundleName: string, options: { force: boolean }) { - const nodecgPath = util.getNodeCGPath(); + const nodecgPath = getNodeCGPath(); const bundlePath = path.join(nodecgPath, "bundles/", bundleName); if (!fs.existsSync(bundlePath)) { diff --git a/src/dts/packages.d.ts b/src/dts/packages.d.ts deleted file mode 100644 index cccf2a6..0000000 --- a/src/dts/packages.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "json-schema-defaults"; diff --git a/src/index.ts b/src/index.ts index 33a4a50..a390277 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import chalk from "chalk"; import { Command } from "commander"; import semver from "semver"; -import util from "./lib/util.js"; +import { getLatestCLIRelease } from "./lib/util.js"; const program = new Command("nodecg"); const dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -17,8 +17,7 @@ const packageVersion: string = JSON.parse( ); // Check for updates, asynchronously, so as to not make the CLI startup time excessively slow -util - .getLatestCLIRelease() +getLatestCLIRelease() .then((release) => { if (semver.gt(release.version, packageVersion)) { console.log( diff --git a/src/lib/fetch-tags.ts b/src/lib/fetch-tags.ts index edb1e83..638efc0 100644 --- a/src/lib/fetch-tags.ts +++ b/src/lib/fetch-tags.ts @@ -1,6 +1,6 @@ import { execSync } from "child_process"; -export default function (repoUrl: string) { +export function fetchTags(repoUrl: string) { const rawTags = execSync(`git ls-remote --refs --tags ${repoUrl}`) .toString() .trim() diff --git a/src/lib/install-bundle-deps.ts b/src/lib/install-bundle-deps.ts index 350e440..12818d8 100644 --- a/src/lib/install-bundle-deps.ts +++ b/src/lib/install-bundle-deps.ts @@ -5,15 +5,15 @@ import { format } from "node:util"; import chalk from "chalk"; -import util from "./util.js"; +import { isBundleFolder } from "./util.js"; /** * Installs npm and bower dependencies for the NodeCG bundle present at the given path. * @param bundlePath - The path of the NodeCG bundle to install dependencies for. * @param installDev - Whether to install devDependencies. */ -export default function (bundlePath: string, installDev = false) { - if (!util.isBundleFolder(bundlePath)) { +export function installBundleDeps(bundlePath: string, installDev = false) { + if (!isBundleFolder(bundlePath)) { console.error( chalk.red("Error:") + " There doesn't seem to be a valid NodeCG bundle in this folder:" + diff --git a/src/lib/sample/npm-release.json b/src/lib/sample/npm-release.json new file mode 100644 index 0000000..53e9730 --- /dev/null +++ b/src/lib/sample/npm-release.json @@ -0,0 +1,98 @@ +{ + "name": "nodecg-cli", + "version": "8.7.0", + "license": "MIT", + "_id": "nodecg-cli@8.7.0", + "maintainers": [ + { "name": "mattmcnam", "email": "matt@mattmcn.com" }, + { "name": "hoishin", "email": "hoishinxii@gmail.com" } + ], + "contributors": [ + { + "url": "https://alexvan.camp/", + "name": "Alex Van Camp", + "email": "email@alexvan.camp" + }, + { + "url": "http://mattmcn.com/", + "name": "Matthew McNamara", + "email": "matt@mattmcn.com" + }, + { "name": "Keiichiro Amemiya", "email": "kei@hoishin.dev" } + ], + "homepage": "https://github.com/nodecg/nodecg-cli#readme", + "bugs": { "url": "http://github.com/nodecg/nodecg-cli/issues" }, + "bin": { "nodecg": "dist/bin/nodecg.js" }, + "dist": { + "shasum": "bd59db2d98c2077bc03623cb9be59ff45fb511a2", + "tarball": "https://registry.npmjs.org/nodecg-cli/-/nodecg-cli-8.7.0.tgz", + "fileCount": 28, + "integrity": "sha512-RcoE8PhtivBz1dtKDmgBh3MTozckwymcPr9UC1oiagYMlDLoTvLC1Bqssef5h+rNNK/aBeVB33leySFEQWNbjQ==", + "signatures": [ + { + "sig": "MEUCIQDuXO/x48us+Bt7A9SskHcLDaAmDFhEDCgbMcTfolZHUQIgNLF0GtmhOF5COKdZue+HwYIRAeO8KbScnZeE/U6PQe4=", + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" + } + ], + "unpackedSize": 155193 + }, + "type": "module", + "engines": { "node": "^18.17.0 || ^20.9.0 || ^22.11.0" }, + "gitHead": "15d3992ec6d6eeb874d9c20473a63435689acbc9", + "scripts": { + "dev": "tsc --build tsconfig.build.json --watch", + "fix": "run-s fix:*", + "test": "vitest", + "build": "del-cli dist && tsc --build tsconfig.build.json", + "format": "prettier --write \"**/*.ts\"", + "static": "run-s static:*", + "fix:eslint": "eslint --fix", + "fix:prettier": "prettier --write \"**/*.ts\"", + "static:eslint": "eslint --cache", + "static:prettier": "prettier --check \"**/*.ts\"" + }, + "_npmUser": { "name": "hoishin", "email": "hoishinxii@gmail.com" }, + "prettier": {}, + "repository": { + "url": "git://github.com/nodecg/nodecg-cli.git", + "type": "git" + }, + "_npmVersion": "10.9.0", + "description": "The NodeCG command line interface.", + "directories": {}, + "_nodeVersion": "22.12.0", + "dependencies": { + "tar": "^7.4.3", + "chalk": "^5.4.1", + "semver": "^7.6.3", + "inquirer": "^12.3.0", + "commander": "^12.1.0", + "hosted-git-info": "^8.0.2", + "npm-package-arg": "^12.0.1", + "json-schema-defaults": "0.4.0", + "json-schema-to-typescript": "^15.0.3" + }, + "_hasShrinkwrap": false, + "devDependencies": { + "eslint": "^9.17.0", + "vitest": "^2.1.8", + "del-cli": "^6.0.0", + "prettier": "^3.4.2", + "type-fest": "^4.31.0", + "@eslint/js": "^9.17.0", + "typescript": "~5.7.2", + "@types/node": "18", + "npm-run-all2": "^7.0.2", + "@types/semver": "^7.5.8", + "typescript-eslint": "^8.18.2", + "@vitest/coverage-v8": "^2.1.8", + "@types/hosted-git-info": "^3.0.5", + "@types/npm-package-arg": "^6.1.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-simple-import-sort": "^12.1.1" + }, + "_npmOperationalInternal": { + "tmp": "tmp/nodecg-cli_8.7.0_1735252303987_0.756945223884677", + "host": "s3://npm-registry-packages-npm-production" + } +} diff --git a/src/lib/sample/npm-release.ts b/src/lib/sample/npm-release.ts new file mode 100644 index 0000000..96eb3ca --- /dev/null +++ b/src/lib/sample/npm-release.ts @@ -0,0 +1,3 @@ +import npmRelease from "./npm-release.json" with { type: "json" }; + +export type NpmRelease = typeof npmRelease; diff --git a/src/lib/util.ts b/src/lib/util.ts index 37d0ddf..a7b4a70 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -3,130 +3,97 @@ import path from "node:path"; import semver from "semver"; -export default { - /** - * Checks if the given directory contains a NodeCG installation. - * @param pathToCheck - */ - pathContainsNodeCG(pathToCheck: string): boolean { - const pjsonPath = path.join(pathToCheck, "package.json"); - if (fs.existsSync(pjsonPath)) { - const pjson = JSON.parse(fs.readFileSync(pjsonPath, "utf8")); - return pjson.name.toLowerCase() === "nodecg"; - } - - return false; - }, - - /** - * Gets the nearest NodeCG installation folder. First looks in process.cwd(), then looks - * in every parent folder until reaching the root. Throws an error if no NodeCG installation - * could be found. - */ - getNodeCGPath() { - let curr = process.cwd(); - do { - if (this.pathContainsNodeCG(curr)) { - return curr; - } - - const nextCurr = path.resolve(curr, ".."); - if (nextCurr === curr) { - throw new Error( - "NodeCG installation could not be found in this directory or any parent directory.", - ); - } - - curr = nextCurr; - } while (fs.lstatSync(curr).isDirectory()); - - throw new Error( - "NodeCG installation could not be found in this directory or any parent directory.", - ); - }, - - /** - * Checks if the given directory is a NodeCG bundle. - */ - isBundleFolder(pathToCheck: string) { - const pjsonPath = path.join(pathToCheck, "package.json"); - if (fs.existsSync(pjsonPath)) { - const pjson = JSON.parse(fs.readFileSync(pjsonPath, "utf8")); - return typeof pjson.nodecg === "object"; - } +import type { NpmRelease } from "./sample/npm-release.js"; + +/** + * Checks if the given directory contains a NodeCG installation. + * @param pathToCheck + */ +export function pathContainsNodeCG(pathToCheck: string): boolean { + const pjsonPath = path.join(pathToCheck, "package.json"); + if (fs.existsSync(pjsonPath)) { + const pjson = JSON.parse(fs.readFileSync(pjsonPath, "utf8")); + return pjson.name.toLowerCase() === "nodecg"; + } + + return false; +} - return false; - }, - - /** - * Gets the currently-installed NodeCG version string, in the format "vX.Y.Z" - */ - getCurrentNodeCGVersion(): string { - const nodecgPath = this.getNodeCGPath(); - return JSON.parse(fs.readFileSync(`${nodecgPath}/package.json`, "utf8")) - .version; - }, - - /** - * Gets the latest NodeCG release information from npm, including tarball download link. - */ - async getNodeCGRelease(target: string): Promise { - const targetVersion = semver.coerce(target)?.version; - if (!targetVersion) { - throw new Error(`Failed to determine target NodeCG version`); +/** + * Gets the nearest NodeCG installation folder. First looks in process.cwd(), then looks + * in every parent folder until reaching the root. Throws an error if no NodeCG installation + * could be found. + */ +export function getNodeCGPath() { + let curr = process.cwd(); + do { + if (pathContainsNodeCG(curr)) { + return curr; } - const res = await fetch( - `http://registry.npmjs.org/nodecg/${targetVersion}`, - ); - if (res.status !== 200) { + const nextCurr = path.resolve(curr, ".."); + if (nextCurr === curr) { throw new Error( - `Failed to fetch NodeCG release information from npm, status code ${res.status}`, + "NodeCG installation could not be found in this directory or any parent directory.", ); } - return res.json() as Promise; - }, + curr = nextCurr; + } while (fs.lstatSync(curr).isDirectory()); - async getLatestCLIRelease(): Promise { - const res = await fetch("http://registry.npmjs.org/nodecg-cli/latest"); - if (res.status !== 200) { - throw new Error( - `Failed to fetch NodeCG release information from npm, status code ${res.status}`, - ); - } + throw new Error( + "NodeCG installation could not be found in this directory or any parent directory.", + ); +} + +/** + * Checks if the given directory is a NodeCG bundle. + */ +export function isBundleFolder(pathToCheck: string) { + const pjsonPath = path.join(pathToCheck, "package.json"); + if (fs.existsSync(pjsonPath)) { + const pjson = JSON.parse(fs.readFileSync(pjsonPath, "utf8")); + return typeof pjson.nodecg === "object"; + } + + return false; +} + +/** + * Gets the currently-installed NodeCG version string, in the format "vX.Y.Z" + */ +export function getCurrentNodeCGVersion(): string { + const nodecgPath = getNodeCGPath(); + return JSON.parse(fs.readFileSync(`${nodecgPath}/package.json`, "utf8")) + .version; +} + +/** + * Gets the latest NodeCG release information from npm, including tarball download link. + */ +export async function getNodeCGRelease(target: string): Promise { + const targetVersion = semver.coerce(target)?.version; + if (!targetVersion) { + throw new Error(`Failed to determine target NodeCG version`); + } + + const res = await fetch(`http://registry.npmjs.org/nodecg/${targetVersion}`); + if (res.status !== 200) { + throw new Error( + `Failed to fetch NodeCG release information from npm, status code ${res.status}`, + ); + } + + return res.json() as Promise; +} + +export async function getLatestCLIRelease(): Promise { + const res = await fetch("http://registry.npmjs.org/nodecg-cli/latest"); + if (res.status !== 200) { + throw new Error( + `Failed to fetch NodeCG release information from npm, status code ${res.status}`, + ); + } - return res.json() as Promise; - }, -}; - -export interface NPMRelease { - name: string; - version: string; - description?: string; - bugs?: { url: string }; - repository?: { type: string; url: string }; - license: string; - bin?: Record; - scripts?: Record; - dependencies?: Record; - devDependencies?: Record; - engines?: Record; - contributors?: Record[]; - gitHead?: string; - homepage?: string; - dist: { - integrity: string; - shasum: string; - tarball: string; - fileCount: number; - unpackedSize: number; - signatures: { - keyid: string; - sig: string; - }[]; - "npm-signature": string; - }; - directories?: Record; - maintainers: Record[]; + return res.json() as Promise; } diff --git a/tsconfig.json b/tsconfig.json index f4357f5..34fc5ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,12 +5,13 @@ "target": "ES2020", "lib": ["ES2020"], - "module": "Node16", + "module": "NodeNext", "types": ["node"], "isolatedModules": true, "verbatimModuleSyntax": true, "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, "strict": true, "noUnusedLocals": true,