npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hwy-fm/cli

v0.1.2

Published

Build toolchain and CLI for `@hwy-fm` applications — project scaffolding, dev server with HMR, production builds, and interactive REPL.

Downloads

1,121

Readme

@hwy-fm/cli

License: MIT TypeScript

Build toolchain and CLI for @hwy-fm applications — project scaffolding, dev server with HMR, production builds, and interactive REPL.

Quick Start

npm install -g @hwy-fm/cli
hwy create my-app
cd my-app && npm install && hwy start

Commands

| Command | Description | |---------|-------------| | hwy create <name> --template <url> | Scaffold a new project from a git template | | hwy start | Start dev server with hot module replacement | | hwy build | Production build + generate deployment package.json | | hwy bundle | Production bundle only (no deployment package.json) | | hwy clean | Remove build output directories | | hwy repl | Interactive REPL for script-mode commands | | hwy version | Print CLI version | | hwy help | Print usage and available commands |

Global Options

| Option | Description | |--------|-------------| | --project <path> | Project root directory (default: cwd) | | --env <name> | Target environment (e.g. production, staging) | | --verbose | Enable detailed logging | | --debug | Enable pipeline tracing and topology output | | --bundler <name> | Bundler to use (default: webpack) | | --no_clean | Skip output directory cleanup before build/bundle |

Project Configuration

Create a project.config.ts (or .js, .json) at the project root:

import { defineConfig } from '@hwy-fm/cli';

export default defineConfig({
  root: '.',
  source: 'src',
  output: 'dist',

  client: {
    entry: 'app/main.ts',
    index: 'index.html',
    port: 4200,
    proxy: [
      { host: 'http://localhost:3000', proxyApi: '/api' },
    ],
  },

  server: {
    entry: 'server/main.ts',
  },

  env: {
    production: {
      sourceMap: false,
      client: { publicPath: '/cdn/' },
    },
  },
});

Shared Options

| Option | Default | Description | |--------|---------|-------------| | root | '.' | Project root directory (relative to cwd) | | source | 'src' | Source directory relative to root | | output | 'dist' | Output directory relative to root | | nodeModules | 'node_modules' | node_modules path relative to root | | tsConfig | 'tsconfig.json' | TypeScript config path | | sourceMap | false | Enable source maps | | resolveAlias | — | Module resolve aliases, e.g. { '@app': 'src/app' } | | define | — | Compile-time constants injected via DefinePlugin | | minimize | true | Minify production output |

Platforms

The CLI builds up to four platforms in a single project. Set any platform to false to disable it.

| Platform | Config Key | Description | |----------|------------|-------------| | Client | client | Browser bundle — entry, HTML template, styles, assets | | Server | server | Node.js SSR bundle — supports hot reload | | Server Entry | serverEntry | SSR entry for client-side hydration | | DLL | dll | Vendor pre-bundle — shared dependencies |

Client

client: {
  entry: 'app/main.ts',
  index: 'index.html',
  publicPath: '/',
  port: 3000,
  assets: ['assets/'],
  styles: ['styles/global.scss'],
  themeVariable: 'styles/variables.scss',
  browserTarget: '> 1%, last 4 versions',
  proxy: { '/api': 'http://localhost:4000' },
  templateContent: (params) => `<!DOCTYPE html>...`,
  styleLoaderOptions: { attributes: { nonce: 'abc' } },
}

Server

server: {
  entry: 'server/main.ts',
  outputFormat: 'cjs',           // 'cjs' | 'esm'
  nodeExternals: true,           // keep node_modules out of bundle
  nodeExternalsAllowlist: [],    // packages to include despite nodeExternals
  hotContext: 'server.context',  // hot-reload context module
  watchFile: ['config/'],        // extra paths to watch for restart
  buildAssets(platforms, readSource) {
    const client = platforms['client'];
    return {
      scripts: client.entrypoints.main?.js || [],
      inlineStyles: (client.entrypoints.main?.css || []).map(readSource),
    };
  },
}

DLL

dll: {
  entry: {
    vendor: ['react', 'react-dom'],
    charts: ['echarts'],           // built after vendor
    shared: ['./src/shared/index.ts'],
  },
}

Environment Overrides

env: {
  production: {
    sourceMap: false,
    minimize: true,
    client: { publicPath: '/cdn/' },
    configureWebpack: (config, { platform, mode }) => { /* ... */ },
  },
  staging: {
    client: { publicPath: '/staging/' },
  },
}

Merge strategy: extends → base → env.development (if start) → env[targetEnv]

Config Extension

// project.config.ts
export default defineConfig({
  extends: './base.config.ts',
  client: { port: 4200 },
});

configureWebpack

Modify the final webpack configuration for every platform:

export default defineConfig({
  configureWebpack(config, { platform, mode }) {
    if (platform === 'client') {
      config.devtool = 'eval-source-map';
    }
    // return void to mutate in-place, or return a new object to replace
  },
});

Plugin System

Custom Commands

Register additional CLI commands via plugins:

// plugins/info.ts
import { defineCmd } from '@hwy-fm/cli';

defineCmd({
  name: 'info',
  description: 'Print project information',
  options: [
    { flag: '--all, -a', desc: 'Show all details' },
  ],
  execute(ctx) {
    console.log('Project info...');
    ctx.result = { exitCode: 0 };
  },
});

Load plugins in your config:

export default defineConfig({
  plugins: [
    './plugins/info.ts',
    '@my-scope/cli-plugin-lint',
  ],
});

Custom Bundler

Replace or add a bundler strategy:

import { defineBundlerStrategy } from '@hwy-fm/cli';
import { KernelModel } from '@hwy-fm/kernel';

const MyBundlerModel = KernelModel();

// Register seeds and instructions on MyBundlerModel for 'build' and 'start'...

defineBundlerStrategy('my-bundler', MyBundlerModel);

Then use it: hwy build --bundler my-bundler

Custom Webpack Rules

Extend webpack's module rule pipeline:

import { defineRuleStrategy } from '@hwy-fm/cli/webpack';

defineRuleStrategy((context) => ({
  test: /\.svg$/,
  type: 'asset/resource',
}));

Return a { rules, plugins } object when the strategy also needs plugins:

import { defineRuleStrategy } from '@hwy-fm/cli/webpack';
import { VueLoaderPlugin } from 'vue-loader';

defineRuleStrategy((context) => ({
  rules: { test: /\.vue$/, loader: 'vue-loader' },
  plugins: new VueLoaderPlugin(),
}));

REPL

Two ways to enter interactive mode:

Direct REPL

hwy repl

Enters REPL mode — dispatches input to user-defined seeds (not build/start/bundle/clean):

hwy> exit

Script REPL

hwy my-app.ts

When the first argument is a .ts, .js, or .mjs file, the CLI:

  1. Loads the file (side-effect imports register seeds/instructions into the kernel)
  2. Automatically enters REPL in script mode, dispatching commands to the script's kernel

This lets you write a standalone executable file:

// my-app.ts
import { Kernel } from '@hwy-fm/kernel';
import '@hwy-fm/std';

@Kernel.Process.seed('hello')
class HelloSeed {
  execute(ctx) {
    console.log('Hello from script!');
  }
}

@Kernel.Process.seed('greet')
class GreetSeed {
  execute(ctx) {
    const name = ctx.input.args?.[0] || 'World';
    console.log(`Hi, ${name}!`);
  }
}

@Kernel.bootstrap()
class App {}

Run it:

hwy my-app.ts
🧩 REPL [script] — type "exit" to quit
hwy> hello
Hello from script!
hwy> greet Alice
Hi, Alice!
hwy> exit

REPL Features

| Feature | Example | |---------|---------| | Chain commands | hello && greet Alice | | Pipe input | echo "hello" \| hwy repl | | Clear screen | cls or clear | | Exit | exit or quit |

Piped input mode (non-TTY) runs commands line by line and exits automatically.

Dev Server

hwy start compiles your code, starts a dev server, and hot-reloads on file changes.

Modes

| Mode | Activation | How it works | |------|-----------|--------------| | VM (default) | — | Runs compiled server code in vm.runInNewContext. Hot-reload is instant (no process restart). | | Nodemon | --nodemon flag | Spawns a child process with tsx/ts-node. Restarts on file changes via chokidar. Supports --inspect debugging. |

Server-only mode

If your project has no client config, hwy start skips the Express dev server entirely — no port 3000, no proxy. The server runs standalone, and file-watch / hot-reload still works.

The hotReload() contract (VM mode)

In VM mode, the CLI injects a global hotReload function into your server code. Your server must call it once listening is ready:

const server = http.createServer(app);
server.listen(3001, () => {
  hotReload({
    hotHost: 'http://localhost:3001',  // proxy target (optional)
    hotReload: () => server.close(),   // cleanup before next reload
  });
});

| Field | Required | Description | |-------|----------|-------------| | hotHost | No | The address to reverse-proxy from the dev server. Omit for non-HTTP protocols (WebSocket, TCP, gRPC). | | hotReload | No | Cleanup callback — called before the next hot-reload cycle to release resources (close sockets, etc.). |

When hotHost is omitted, the dev server won't set up a reverse proxy — useful for protocols that don't need one.

Custom server context

Create a server.context.ts (or configure server.hotContext) to inject variables into the VM sandbox or replace the default execution entirely:

// server.context.ts
export default (config, fullConfig) => ({
  // Extra globals available in VM sandbox
  MY_CONFIG: { port: 3001 },

  // Or replace VM execution with a custom handler:
  // hotHandler: (config) => { ... return { hotHost, hotReload } }
});

Architecture

The CLI is built on @hwy-fm/kernel + @hwy-fm/std — it uses the same pipeline architecture it helps you build:

argv
  │
  ▼
Catch      [ timing — measures build/bundle/clean/create time ]
  │
  ▼
Guard      [ load project config + plugins ]
  │
  ▼
Check      [ validate configs — platforms, entries, paths ]
  │
  ▼
Process    [ command seed — create / start / build / ... ]
  │
  ▼
Deliver    [ set process.exitCode ]
  │
  ▼
Trace      [ debug output — topology, traces ]

Build commands (start, build, bundle, clean) dispatch into a separate bundler sub-kernel (e.g. webpack), maintaining physical isolation between CLI orchestration and bundler logic.

Public API

import {
  defineConfig,          // Type-safe project config helper
  defineCmd,             // Register a custom CLI command
  CliKernel,             // The CLI Kernel model instance
  CLI_EXT_COMMANDS,      // DI token for plugin commands
  BUNDLER_STRATEGY,      // DI token for bundler strategies
  defineBundlerStrategy, // Register a custom bundler
} from '@hwy-fm/cli';

import {
  defineRuleStrategy,    // Register a custom webpack rule
  RULE_STRATEGY,         // DI token for webpack rules
} from '@hwy-fm/cli/webpack';

Related Packages

| Package | Description | |---------|-------------| | @hwy-fm/kernel | The pipeline engine powering the CLI | | @hwy-fm/std | Pre-configured Slots + Gateway | | @hwy-fm/di | Dependency injection |

License

MIT © hwyn