Commit 608f424

Anton Golub <antongolub@antongolub.com>
2025-06-06 09:29:57
refactor: decompose `installDeps` (#1225)
* chore: minor code improvements * refactor: decompose deps installer
1 parent 14f8bf8
build/cli.cjs
@@ -27,6 +27,7 @@ __export(cli_exports, {
 });
 module.exports = __toCommonJS(cli_exports);
 var import_node_url = __toESM(require("url"), 1);
+var import_node_process2 = __toESM(require("process"), 1);
 var import_index = require("./index.cjs");
 var import_deps = require("./deps.cjs");
 
@@ -136,7 +137,6 @@ function transformMarkdown(buf) {
 
 // src/cli.ts
 var import_vendor = require("./vendor.cjs");
-var import_node_process2 = __toESM(require("process"), 1);
 var import_meta = {};
 var EXT = ".mjs";
 var EXT_RE = /^\.[mc]?[jt]sx?$/;
build/core.cjs
@@ -689,8 +689,8 @@ var _ProcessPromise = class _ProcessPromise extends Promise {
   }
   get exitCode() {
     return this.then(
-      (p) => p.exitCode,
-      (p) => p.exitCode
+      (o) => o.exitCode,
+      (o) => o.exitCode
     );
   }
   get signal() {
@@ -742,19 +742,19 @@ var _ProcessPromise = class _ProcessPromise extends Promise {
   }
   // Output formatters
   json() {
-    return this.then((p) => p.json());
+    return this.then((o) => o.json());
   }
   text(encoding) {
-    return this.then((p) => p.text(encoding));
+    return this.then((o) => o.text(encoding));
   }
   lines(delimiter) {
-    return this.then((p) => p.lines(delimiter));
+    return this.then((o) => o.lines(delimiter));
   }
   buffer() {
-    return this.then((p) => p.buffer());
+    return this.then((o) => o.buffer());
   }
   blob(type) {
-    return this.then((p) => p.blob(type));
+    return this.then((o) => o.blob(type));
   }
   // Status checkers
   isQuiet() {
build/deps.cjs
@@ -15,22 +15,31 @@ __export(deps_exports, {
 module.exports = __toCommonJS(deps_exports);
 var import_index = require("./index.cjs");
 var import_vendor = require("./vendor.cjs");
-function installDeps(dependencies, prefix, registry) {
+function installDeps(dependencies, prefix, registry, installerType = "npm") {
   return __async(this, null, function* () {
-    const prefixFlag = prefix ? `--prefix=${prefix}` : "";
-    const registryFlag = registry ? `--registry=${registry}` : "";
+    const installer = installers[installerType];
     const packages = Object.entries(dependencies).map(
       ([name, version]) => `${name}@${version}`
     );
-    if (packages.length === 0) {
-      return;
+    if (packages.length === 0) return;
+    if (!installer) {
+      throw new Error(
+        `Unsupported installer type: ${installerType}. Supported types: ${Object.keys(installers).join(", ")}`
+      );
     }
     yield (0, import_index.spinner)(
-      `npm i ${packages.join(" ")}`,
-      () => import_index.$`npm install --no-save --no-audit --no-fund ${registryFlag} ${prefixFlag} ${packages}`.nothrow()
+      `${installerType} i ${packages.join(" ")}`,
+      () => installer({ packages, prefix, registry })
     );
   });
 }
+var installers = {
+  npm: (_0) => __async(null, [_0], function* ({ packages, prefix, registry }) {
+    const prefixFlag = prefix ? `--prefix=${prefix}` : "";
+    const registryFlag = registry ? `--registry=${registry}` : "";
+    yield import_index.$`npm install --no-save --no-audit --no-fund ${registryFlag} ${prefixFlag} ${packages}`.nothrow();
+  })
+};
 var builtins = /* @__PURE__ */ new Set([
   "_http_agent",
   "_http_client",
@@ -106,9 +115,7 @@ function parsePackageName(path) {
   var _a, _b;
   if (!path) return;
   const name = (_b = (_a = nameRe.exec(path)) == null ? void 0 : _a.groups) == null ? void 0 : _b.name;
-  if (name && !builtins.has(name)) {
-    return name;
-  }
+  if (name && !builtins.has(name)) return name;
 }
 function parseVersion(line) {
   var _a, _b;
build/deps.d.ts
@@ -3,6 +3,7 @@
  * @param dependencies object of dependencies
  * @param prefix  path to the directory where npm should install the dependencies
  * @param registry custom npm registry URL when installing dependencies
+ * @param installerType package manager: npm, yarn, pnpm, bun, etc.
  */
-export declare function installDeps(dependencies: Record<string, string>, prefix?: string, registry?: string): Promise<void>;
+export declare function installDeps(dependencies: Record<string, string>, prefix?: string, registry?: string, installerType?: string): Promise<void>;
 export declare function parseDeps(content: string): Record<string, string>;
src/cli.ts
@@ -15,6 +15,7 @@
 // limitations under the License.
 
 import url from 'node:url'
+import process from 'node:process'
 import {
   $,
   ProcessOutput,
@@ -34,7 +35,6 @@ import { startRepl } from './repl.ts'
 import { randomId } from './util.ts'
 import { transformMarkdown } from './md.ts'
 import { createRequire, type minimist } from './vendor.ts'
-import process from 'node:process'
 
 const EXT = '.mjs'
 const EXT_RE = /^\.[mc]?[jt]sx?$/
src/core.ts
@@ -494,8 +494,8 @@ export class ProcessPromise extends Promise<ProcessOutput> {
 
   get exitCode(): Promise<number | null> {
     return this.then(
-      (p) => p.exitCode,
-      (p) => p.exitCode
+      (o) => o.exitCode,
+      (o) => o.exitCode
     )
   }
 
@@ -566,23 +566,23 @@ export class ProcessPromise extends Promise<ProcessOutput> {
 
   // Output formatters
   json<T = any>(): Promise<T> {
-    return this.then((p) => p.json<T>())
+    return this.then((o) => o.json<T>())
   }
 
   text(encoding?: Encoding): Promise<string> {
-    return this.then((p) => p.text(encoding))
+    return this.then((o) => o.text(encoding))
   }
 
   lines(delimiter?: string | RegExp): Promise<string[]> {
-    return this.then((p) => p.lines(delimiter))
+    return this.then((o) => o.lines(delimiter))
   }
 
   buffer(): Promise<Buffer> {
-    return this.then((p) => p.buffer())
+    return this.then((o) => o.buffer())
   }
 
   blob(type?: string): Promise<Blob> {
-    return this.then((p) => p.blob(type))
+    return this.then((o) => o.blob(type))
   }
 
   // Status checkers
src/deps.ts
@@ -20,25 +20,44 @@ import { depseek } from './vendor.ts'
  * @param dependencies object of dependencies
  * @param prefix  path to the directory where npm should install the dependencies
  * @param registry custom npm registry URL when installing dependencies
+ * @param installerType package manager: npm, yarn, pnpm, bun, etc.
  */
 export async function installDeps(
   dependencies: Record<string, string>,
   prefix?: string,
-  registry?: string
+  registry?: string,
+  installerType = 'npm'
 ): Promise<void> {
-  const prefixFlag = prefix ? `--prefix=${prefix}` : ''
-  const registryFlag = registry ? `--registry=${registry}` : ''
+  const installer = installers[installerType]
   const packages = Object.entries(dependencies).map(
     ([name, version]) => `${name}@${version}`
   )
-  if (packages.length === 0) {
-    return
+  if (packages.length === 0) return
+  if (!installer) {
+    throw new Error(
+      `Unsupported installer type: ${installerType}. Supported types: ${Object.keys(installers).join(', ')}`
+    )
   }
-  await spinner(`npm i ${packages.join(' ')}`, () =>
-    $`npm install --no-save --no-audit --no-fund ${registryFlag} ${prefixFlag} ${packages}`.nothrow()
+
+  await spinner(`${installerType} i ${packages.join(' ')}`, () =>
+    installer({ packages, prefix, registry })
   )
 }
 
+type DepsInstaller = (opts: {
+  packages: string[]
+  registry?: string
+  prefix?: string
+}) => Promise<void>
+
+const installers: Record<any, DepsInstaller> = {
+  npm: async ({ packages, prefix, registry }) => {
+    const prefixFlag = prefix ? `--prefix=${prefix}` : ''
+    const registryFlag = registry ? `--registry=${registry}` : ''
+    await $`npm install --no-save --no-audit --no-fund ${registryFlag} ${prefixFlag} ${packages}`.nothrow()
+  },
+}
+
 const builtins = new Set([
   '_http_agent',
   '_http_client',
@@ -119,10 +138,9 @@ export function parseDeps(content: string): Record<string, string> {
 
 function parsePackageName(path?: string): string | undefined {
   if (!path) return
+
   const name = nameRe.exec(path)?.groups?.name
-  if (name && !builtins.has(name)) {
-    return name
-  }
+  if (name && !builtins.has(name)) return name
 }
 
 function parseVersion(line: string) {
test/deps.test.js
@@ -74,6 +74,16 @@ describe('deps', () => {
       out = await t$`node ${cli}  -i --registry=https://npm.jsr.io <<< ${code}`
       assert.match(out.stdout, /true/)
     })
+
+    test('throws on invalid installer type', async () => {
+      await assert.rejects(
+        () =>
+          installDeps({ foo: 'latest' }, cwd, 'https://npm.jsr.io', 'invalid'),
+        {
+          message: /Unsupported installer type: invalid. Supported types: npm/,
+        }
+      )
+    })
   })
 
   describe('parseDeps()', () => {
.size-limit.json
@@ -24,14 +24,14 @@
   {
     "name": "js parts",
     "path": "build/*.{js,cjs}",
-    "limit": "816.85 kB",
+    "limit": "817.2 kB",
     "brotli": false,
     "gzip": false
   },
   {
     "name": "libdefs",
     "path": "build/*.d.ts",
-    "limit": "40.26 kB",
+    "limit": "40.36 kB",
     "brotli": false,
     "gzip": false
   },
@@ -45,7 +45,7 @@
   {
     "name": "all",
     "path": ["build/*", "man/*", "README.md", "LICENSE"],
-    "limit": "873.45 kB",
+    "limit": "873.95 kB",
     "brotli": false,
     "gzip": false
   }