← All posts

Node.js: Factory and Strategy for a cross-platform npm package

EADDRINUSE on Windows, macOS, and Linux without OS if/else chains. Strategy, Factory, TypeScript contracts, and shell-injection-safe runners.

Node.js: Factory and Strategy for a cross-platform npm package
Contents

In brief

Cross-platform Node.js CLIs often devolve into if (process.platform === …) mazes. A Dev.to guide uses Strategy and Factory to build a port-cleanup utility for EADDRINUSE: OS-specific commands hide behind interfaces; core logic stays testable.

What happened

Classic pain: dev server fails because the port is busy. Windows wants netstat/taskkill; macOS/Linux want lsof/kill. Nested platform branches break Single Responsibility and make unit tests painful.

The layered design:

  • TypeScript interfaces define “find process on port / terminate” without shell strings in business code.
  • A Factory picks the platform Strategy.
  • A stateless command runner avoids string-concatenated shells — less shell injection risk.
  • Shared API and CLI on one core.

The example domain is “free a port before dev,” but the pattern applies to any environmental variance: binaries, network tools, error handling.

Why it matters

npm tools that “work on my Mac” often fail on Linux CI or Windows teammates. Strategy objects make behavior predictable and mockable — swap the strategy in tests instead of parsing real lsof output.

Same split helps any adapter to external systems (cloud APIs, CI, local Docker): policy vs mechanism.

In practice

  1. Define the domain interface before the first shell command.
  2. One Factory at the platform decision point — do not scatter platform checks everywhere.
  3. Runner takes argv arrays, not exec(`kill ${pid}`) — especially if pid ever comes from user input.
  4. Tests: fake Strategy recording calls; integration only on target OS in a CI matrix.
  5. CLI (commander/yargs) wraps the same service you export from "exports".

Takeaway

Factory + Strategy in Node.js keeps cross-platform npm packages readable. Read the Dev.to post if your utilities are drowning in switch (os.platform()).