• Stars
    star
    653
  • Rank 66,270 (Top 2 %)
  • Language
    JavaScript
  • License
    Other
  • Created 8 months ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

tshy - TypeScript HYbridizer

Hybrid (CommonJS/ESM) TypeScript node package builder. Write modules that Just Work in ESM and CommonJS, in easy mode.

This tool manages the exports in your package.json file, and builds your TypeScript program using tsc 5.2, emitting both ESM and CommonJS variants, providing the full strength of TypeScript’s checking for both output formats.

USAGE

Install tshy:

npm i -D tshy

Put this in your package.json to use it with the default configs:

{
  "files": ["dist"],
  "scripts": {
    "prepare": "tshy"
  }
}

Put your source code in ./src.

The built files will end up in ./dist/esm (ESM) and ./dist/commonjs (CommonJS).

Your exports will be edited to reflect the correct module entry points.

Dual Package Hazards

If you are exporting both CommonJS and ESM forms of a package, then it is possible for both versions to be loaded at run-time. However, the CommonJS build is a different module from the ESM build, and thus a different thing from the point of view of the JavaScript interpreter in Node.js.

Consider this contrived example:

// import the class from ESM
import { SomeClass } from 'module-built-by-tshy'
import { createRequire } from 'node:module'
const req = createRequire(import.meta.url)

// create an object using the commonjs version
function getObject() {
  const { SomeClass } = require('module-built-by-tshy')
  return new SomeClass()
}

const obj = getObject()
console.log(obj instanceof SomeClass) // false!!

In a real program, this might happen because one part of the code loads the package using require() and another loads it using import.

The Node.js documentation recommends exporting an ESM wrapper that re-exports the CommonJS code, or isolating state into a single module used by both CommonJS and ESM. While these strategies do work, they are not what tshy does.

What Does tshy Do Instead?

It builds your program twice, into two separate folders, and sets up exports. By default, the ESM and CommonJS forms live in separate universes, unaware of one another, and treats the "Dual Module Hazard" as a simple fact of life.

Which it is.

"Dual Module Hazard" is a fact of life anyway

Since the advent of npm, circa 2010, module in node have been potentially duplicated in the dependency graph. Node's nested node_modules resolution algorithm, added in Node 0.4, made this even easier to leverage, and more likely to occur.

So: as a package author, you cannot safely rely on there being exactly one copy of your library loaded at run-time.

This doesn't mean you shouldn't care about it. It means that you should take it into consideration always, whether you are using a hybrid build or not.

If you need to ensure that exactly one copy of something exists at run-time, whether using a hybrid build or not, you need to guard this with a check that is not dependent on the dependency graph, such as a global variable.

const ThereCanBeOnlyOne = Symbol.for('there can be only one')
const g = globalThis as typeof globalThis & {
  [ThereCanBeOnlyOne]?: Thing
}
import { Thing } from './thing.js'
g[ThereCanBeOnlyOne] ??= new Thing
export const thing = g[ThereCanBeOnlyOne]

If you find yourself doing this, it's a good idea to pause and consider if you would be better off with a type check function or something other than relying on instanceof. There are certainly cases where it's unavoidable, but it can be tricky to work with.

Module Local State

There are some cases where you need something to be the same value whether loaded with CommonJS or ESM, but not necessarily unique to the entire program.

For example, say that there is some package-local set of data, and it needs to be updated and accessible whether the user is accessing your package via import or require.

In this case, we can use a dialect polyfill that pulls in the state module from a single dialect.

In Node, it's easy for ESM to load CommonJS, but since ESM cannot be loaded synchronously by CommonJS, I recommend putting the state in the polyfill, and having the "normal" module access it from that location.

For example:

// src/index.ts
import { state } from './state.js'
export const setValue = (key: string, value: any) => {
  state[key] = value
}
export const getValue = (key: string) => state[key]
// src/state-cjs.cts
// this is the actual "thing"
export const state: Record<string, any> = {}
// src/state.ts
// this is what will end up in the esm build
// need a ts-ignore because this is a hack.
//@ts-ignore
import cjsState from '../commonjs/state.js'
export const { state } = cjsState as { state: Record<string, any> }

If you need a provide an ESM dialect that doesn't support CommonJS (eg, deno, browser, etc), then you can do this:

// src/state-deno.mts
// can't load the CJS version, so no dual package hazard
export const state: Record<string, any> = {}

See below for more on using dialect specific polyfills.

Handling Default Exports

export default is the bane of hybrid TypeScript modules.

When compiled as CommonJS, this results in creating an export named default, which is not the same as setting module.exports.

// esm, beautiful and clean
import foo from 'foo'
// commonjs, unnecessarily ugly and confusing
// even if you like it for some reason, it's not "the same"
const { default: foo } = require('foo')

You can tell TypeScript to do a true default export for CommonJS by using export = <whatever>. However:

  • This is not compatible with an ESM build.
  • You cannot export types along with it.

In general, when publishing TypeScript packages as both CommonJS and ESM, it is a good idea to avoid default exports for any public interfaces.

  • No need to polyfill anything.
  • Can export types alongside the values.

However, if you are publishing something that does need to provide a default export (for example, porting a project to hybrid and/or TypeScript, and want to keep the interface consistent), you can do it with a CommonJS polyfill.

// index.ts
// the thing that gets exported for ESM
import { thing } from './main.ts'
import type { SomeType } from './main.ts'

export default thing
export type { SomeType }
// index-cjs.cts
// the polyfill for CommonJS
import * as items from './main.ts'
declare global {
  namespace mything {
    export type SomeType = items.SomeType
  }
}
export = items.thing

Then, CommonJS users will get the appropriate thing when they import 'mything', and can access the type via the global namespace like mything.SomeType.

But in almost all cases, it's much simpler to just use named exports exclusively.

Configuration

Mostly, this just uses opinionated convention, and so there is very little to configure.

Source must be in ./src. Builds are in ./dist/commonjs for CommonJS and ./dist/esm for ESM.

There is very little configuration for this, but a lot of things can be configured.

exports

By default, if there is a src/index.ts file, then that will be set as the "." export, and the package.json file will be exported as "./package.json", because that's just convenient to expose.

You can set other entry points by putting something like this in your package.json file:

{
  "tshy": {
    "exports": {
      "./foo": "./src/foo.ts",
      "./bar": "./src/bar.ts",
      ".": "./src/something-other-than-index.ts",
      "./package.json": "./package.json"
    }
  }
}

Any exports pointing to files in ./src will be updated to their appropriate build target locations, like:

{
  "exports": {
    "./foo": {
      "import": {
        "types": "./dist/esm/foo.d.ts",
        "default": "./dist/esm/foo.js"
      },
      "require": {
        "types": "./dist/commonjs/foo.d.ts",
        "default": "./dist/commonjs/foo.js"
      }
    }
  }
}

Any exports that are not within ./src will not be built, and can be anything supported by package.json exports, as they will just be passed through as-is.

{
  "tshy": {
    "exports": {
      ".": "./src/my-built-module.ts",
      "./package.json": "./package.json"
      "./thing": {
        "import": "./lib/thing.mjs",
        "require": "./lib/thing.cjs",
        "types": "./lib/thing.d.ts"
      },
      "./arraystyle": [
        { "import": "./no-op.js" },
        { "browser": "./browser-thing.js" },
        { "require": [{ "types": "./using-require.d.ts" }, "./using-require.js"]},
        { "types": "./blah.d.ts" },
        "./etc.js"
      ]
    }
  }
}

Package #imports

You can use "imports" in your package.json, and it will be handled in the following ways.

Any "imports" that resolve to a file built as part of your program must be a non-conditional string value pointing to the file in ./src/. For example:

{
  "imports": {
    "#name": "./src/path/to/name.ts",
    "#utils/*": "./src/path/to/utils/*.ts"
  }
}

In the ESM build, import * from '#name' will resolve to ./dist/esm/path/to/name.js, and will be built for ESM. In the CommonJS build, require('#name') will resolve to ./dist/commonjs/path/to/name.js and will be built for CommonJS.

tl;dr how this works and why it can't be conditional

In the built dist/{dialect}/package.json files, the ./src will be stripped from the path and their file extension changed from ts to js (cts to cjs and mts to mjs).

It shouldn't be conditional, because the condition is already implicit in the build. In the CommonJS build, they should be required, and in the ESM builds, they should be imported, and there's only one thing that it can resolve to from any given build.

If there are any "imports" that resolve to something not built by tshy, then tshy will set scripts.preinstall to set up symbolic links at install time to make it work. This just means that you can't use scripts.preinstall for anything else if you have "imports" that aren't managed by tshy. For example:

{
  "imports": {
    "#dep": "@scope/dep/submodule",
    "#conditional": {
      "types": "./vendor/blah.d.ts",
      "require": "./vendor/blah.cjs",
      "import": "./vendor/blah.mjs"
    }
  }
}
tl;dr explanation

The "imports" field in package.json allows you to set local package imports, which have the same kind of conditional import logic as "exports". This is especially useful when you have a vendored dependency with require and import variants, modules that have to be bundled in different ways for different environments, or different dependencies for different environments.

These package imports are always resolved against the nearest package.json file, and tshy uses generated package.json files to set the module dialect to "type":"module" in dist/esm and "type":"commonjs" in dist/commonjs, and it swaps the src/package.json file between this during the tsc builds.

Furthermore, local package imports may not be relative files outside the package folder. They may only be local files within the local package, or dependencies resolved in node_modules.

To support this, tshy copies the imports field from the project's package.json into these dialect-setting generated package.json files, and creates symlinks into the appropriate places so that they resolve to the same files on disk.

Because symlinks may not be included in npm packages (and even if they are included, they won't be unpacked at install time), the symlinks it places in ./dist wouldn't do much good. In order to work around this restriction, tshy creates a node program at dist/.tshy-link-imports.mjs, which generates the symlinks at install time via the preinstall script.

If a tshy.imports is present (a previous iteration of this behavior), it will be merged into the top-level "imports" and deleted from the tshy section.

Making Noise

On failure, all logs will be printed.

To print error logs and a success! message at the end, set TSHY_VERBOSE=1 in the environment.

To print debugging and other extra information, set TSHY_VERBOSE=2 in the environment.

Selecting Dialects

You can tell tshy which dialect you're building for by setting the dialects config to an array of strings:

{
  "tshy": {
    "dialects": ["esm", "commonjs"]
  }
}

The default is ["esm", "commonjs"] (ie, both of them). If you set it to just one, then only that dialect will be built and exported.

Suppressing the self-link

See below about Local Package exports for an explanation of what this is.

Suppress the symlink to the project folder into a node_modules folder in dist and src by doing this:

{
  "tshy": {
    "selfLink": false
  }
}

If the selfLink config is not explicitly set, and creating the symlink fails (common on Windows systems where fs.symlink() may require elevated permissions), then the error will be ignored.

Old Style Exports

Versions of node prior to 12.10.0, published in early to mid 2016, did not have support for exports as a means for defining package entry points. Unfortunately, even 7 years later at the time of this writing, some projects are still using outdated tools that are not capable of understanding this interface.

If there is a commonjs export of the "." subpath, and the tshy.main field in package.json is not set to false, then tshy will use that to set the main and types fields, for compatibility with these tools.

Warning: relying on top-level main/types will likely cause incorrect types to be loaded in some scenarios.

Use with extreme caution. It's almost always better to not define top-level main and types fields if you are shipping a hybrid module. Users will need to update their module and moduleResolution tsconfigs appropriately. That is a good thing, and will save them future headaches.

If the commonjs dialect is not built, or if a "." export is not created, or if the "." export does not support the commonjs dialect, and main is explicitly set to true, then the build will fail.

For example, this config:

{
  "tshy": {
    "exports": {
      ".": "./src/index.ts"
    }
  }
}

will produce:

{
  "main": "./dist/commonjs/index.js",
  "types": "./dist/commonjs/index.d.ts",
  "type": "module",
  "exports": {
    ".": {
      "require": {
        "types": "./dist/commonjs/index.d.ts",
        "default": "./dist/commonjs/index.js"
      },
      "import": {
        "types": "./dist/esm/index.d.ts",
        "default": "./dist/esm/index.js"
      }
    }
  }
}

CommonJS Dialect Polyfills

Sometimes you have to do something in different ways depending on the JS dialect in use. For example, maybe you have to use import.meta.url in ESM, but polyfill with pathToFileURL(__filename) in CommonJS.

To do this, create a polyfill file with the CommonJS code in <name>-cjs.cts. (The cts extension matters.)

// src/source-dir-cjs.cts
//     ^^^^^^^^^^--------- matching name
//               ^^^^----- "-cjs" tag
//                   ^^^^- ".cts" filename suffix
// this one has a -cjs.cts suffix, so it will override the
// module at src/source-dir.ts in the CJS build,
// and be excluded from the esm build.
import { pathToFileURL } from 'node:url'
//@ts-ignore - Have to ignore because TSC thinks this is ESM
export const sourceDir = pathToFileURL(__dirname)

Then put the "real" ESM code in <name>.ts (not .mts!)

You will generally have to //@ts-ignore a bunch of stuff to get the CommonJS build to ignore it, so it's best to keep the polyfill surface as small as possible.

// src/source-dir.ts
// This is the ESM version of the module
//@ts-ignore
export const sourceDir = new URL('.', import.meta.url)

Then in your code, you can just import { sourceDir } from './source-dir.js' and it'll work in both builds.

Excluding from a build using .cts and .mts files

Files named *.mts will be excluded from the CommonJS build.

Files named *.cts will be excluded from the ESM build.

If you need to do something one way for CommonJS and another way for esm, use the "Dialect Switching" trick, with the ESM code living in src/<whatever>.ts and the CommonJS polyfill living in src/<whatever>-cjs.cts.

Other Targets: browser, deno, etc.

If you have any other dialects that you'd like to support, you can list them as either commonjsDialects or esmDialects, depending on whether you want them to be built as CommonJS or ESM.

Note that each added dialect you create will result in another build in the ./dist folder, so you may wish to use sparingly if shipping a large project.

For example:

{
  "tshy": {
    "exports": {
      ".": "./src/index.ts"
    },
    "esmDialects": ["deno", "browser"],
    "commonjsDialects": ["webpack"]
  }
}

Will result in:

{
  "exports": {
    ".": {
      "deno": {
        "types": "./dist/deno/index.d.ts",
        "default": "./dist/deno/index.js"
      },
      "browser": {
        "types": "./dist/browser/index.d.ts",
        "default": "./dist/browser/index.js"
      },
      "webpack": {
        "types": "./dist/webpack/index.d.ts",
        "default": "./dist/webpack/index.js"
      },
      "require": {
        "types": "./dist/commonjs/index.d.ts",
        "default": "./dist/commonjs/index.js"
      },
      "import": {
        "types": "./dist/esm/index.d.ts",
        "default": "./dist/esm/index.js"
      }
    }
  }
}

In each of these, you can use the same kind of dialect override that works for CommonJS polyfills described above. For commonjsDialects types, create a file named <filename>-<dialect>.cts, and for esmDialects types, create a file named <filename>-<dialect>.mts.

For example, to provide deno, browser, and webpack overrides in the setup above, the following files would be relevant:

src/index.ts           # normal esm/cjs version
src/index-cjs.cts      # cjs variant for default commonjs
src/index-browser.mts  # esm variant for the browser
src/index-deno.mts     # esm variant for deno
src/index-webpack.cts  # cjs variant for webpack

Note that the commonjs override uses the abbreviated cjs name (historical reasons, it was originally the only override supported), and that the file extension must be cts or mts depending on the dialect type that it is.

Atomic Builds

Code is built in ./.tshy-build and then copied over only if the build succeeds. This makes it work in monorepo cases where you may have packages that depend on one another and are all being built in parallel (as long as they've been built one time, of course).

If you use "incremental": true in your tsconfig, then this folder will persist, so that TSC can benefit from the .tsbuildinfo files it creates in there.

Exports Management

The exports field in your package.json file will be updated based on the tshy.exports configuration, as described above.

If you don't provide that config, then the default is:

{
  "tshy": {
    "exports": {
      ".": "./src/index.ts",
      "./package.json": "./package.json"
    }
  }
}

TSConfigs

Put whatever configuration you want in tsconfig.json, with the following caveats:

  • include - will be overridden based on build, best omitted
  • exclude - will be overridden based on build, best omitted
  • compilerOptions:
    • outDir - will be overridden based on build, best omitted
    • rootDir - will be set to ./src in the build, can only cause annoying errors otherwise.
    • target - will be set to es2022
    • module - will be set to NodeNext
    • moduleResolution - will be set to NodeNext

If you don't have a tsconfig.json file, then one will be provided for you.

Then the tsconfig.json file will be used as the default project for code hints in VSCode, neovim, tests, etc.

src/package.json

As of TypeScript 5.2, the only way to emit JavaScript to ESM or cjs, and also import packages using node-style "exports"-aware module resolution, is to set the type field in the package.json file closest to the TypeScript source code.

During the build, tshy will create a file at src/package.json for this purpose, and then delete it afterwards. If that file exists and wasn't put there by tshy, then it will be destroyed.

Local Package exports

In order to facilitate local package exports, tshy will create a symlink to the current package temporarily in ./src/node_modules and permanently in ./dist/node_modules.

If you rely on this feature, you may need to add a paths section to your tsconfig.json so that you don't get nagged constantly by your editor about missing type references.

You can suppress the self-linking by putting this config in package.json but be advised this means that you won't be able to import from local package exports:

{
  "tshy": {
    "selfLink": false
  }
}
tl;dr explanation

Similar to local module imports, Node supports importing the exports of the current package as if it was a dependency of itself. The generated package.json files mess with this similar to imports, but it's much easier to work around.

For example, if you had this in your package.json:

{
  "name": "@my/package",
  "exports": {
    "./foo": {
      "import": "./lib/foo.mjs",
      "require": "./lib/foo.cjs"
    }
  }
}

Then any module in the package could do import('@my/package/foo') or require('@my/package/foo') to pull in the appropriate file.

In order to make this wort, tshy links the current project directory into ./src/node_modules/<pkgname> during the builds, and removes the link afterwards, so that TypeScript knows what those things refer to.

The link is also created in the dist folder, but it's only relevant if your tests load the code from ./dist rather than from ./src. In the install, there's no need to re-create this link, because the package will be in a node_modules folder already.

If you use this feature, you can put something like this in your tsconfig.json file so that your editor knows what those things refer to:

{
  "compilerOptions": {
    "paths": {
      "@my/package/foo": ["./src/foo.js"],
      "@my/package/bar": ["./src/bar.js"]
    }
  }
}

Note the .js extension, rather than .ts. Add this for each submodule path that you use in this way, or use a wildcard if you prefer, though this might result in failing to catch errors if you use a submodule identifier that isn't actually exported:

{
  "compilerOptions": {
    "paths": {
      "@my/package/*": ["./src/*.js"]
    }
  }
}

More Repositories

1

node-glob

glob functionality for node.js
TypeScript
8,123
star
2

rimraf

A `rm -rf` util for nodejs
JavaScript
5,309
star
3

node-lru-cache

A fast cache that automatically deletes the least recently used items
TypeScript
4,844
star
4

minimatch

a glob matcher in javascript
JavaScript
3,074
star
5

github

Just a place to track issues and feature requests that I have for github
2,196
star
6

nave

Virtual Environments for Node
Shell
1,580
star
7

node-graceful-fs

fs with incremental backoff on EMFILE
JavaScript
1,254
star
8

sax-js

A sax style parser for JS
JavaScript
1,046
star
9

node-tar

tar for node
JavaScript
755
star
10

st

A node module for serving static files. Does etags, caching, etc.
JavaScript
376
star
11

inherits

Easy simple tiny inheritance in JavaScript
JavaScript
352
star
12

cluster-master

Take advantage of node built-in cluster module behavior
JavaScript
276
star
13

minipass

A stream implementation that does more by doing less
TypeScript
237
star
14

once

Run a function exactly one time
JavaScript
216
star
15

yallist

Yet Another Linked List
JavaScript
198
star
16

server-destroy

When close() is just not enough
JavaScript
184
star
17

semicolons

When you require("semicolons"), THEY ARE REQUIRED.
JavaScript
145
star
18

slide-flow-control

A flow control library that fits in a slideshow
JavaScript
134
star
19

treeverse

Walk any kind of tree structure depth- or breadth-first. Supports promises and advanced map-reduce operations with a very small API.
JavaScript
126
star
20

multipart-js

JavaScript
123
star
21

reading-list

a list of books I recommend
121
star
22

node-touch

touch(1) for node
JavaScript
121
star
23

async-cache

Cache your async lookups and don't fetch the same thing more than necessary.
JavaScript
119
star
24

catcher

TypeScript
116
star
25

ttlcache

TypeScript
116
star
26

core-util-is

The util.is* functions from Node core
JavaScript
98
star
27

dezalgo

Contain async insanity so that the dark pony lord doesn't eat souls
JavaScript
89
star
28

github-flavored-markdown

Deprecated. Use marked instead.
JavaScript
79
star
29

node-bench

JavaScript
71
star
30

free-as-in-hugs-license

A (Not OSI-Approved) software license you may use if you wish
70
star
31

sigmund

Quick and dirty psychoanalysis for objects
JavaScript
67
star
32

minizlib

A smaller, faster, zlib stream built on http://npm.im/minipass and Node.js's zlib binding.
JavaScript
66
star
33

inflight

Add callbacks to requests in flight to avoid async duplication
JavaScript
66
star
34

fast-list

A fast O(1) push/pop/shift/unshift thing
JavaScript
66
star
35

gist-cli

A gist cli client written in Node
JavaScript
64
star
36

dotfiles

My Dot Files
Shell
63
star
37

wrappy

Callback wrapping utility
JavaScript
56
star
38

block-stream

A stream of fixed-size blocks
JavaScript
52
star
39

isexe

Minimal module to check if a file is executable.
TypeScript
48
star
40

.vim

My vim settings
Vim Script
47
star
41

char-spinner

Put a little spinner on process.stderr, as unobtrusively as possible.
JavaScript
43
star
42

st-example

an example of serving static files easily in node using the st module
JavaScript
40
star
43

jackspeak

A very strict and proper argument parser.
TypeScript
38
star
44

templar

A lightweight template thing for node http servers
JavaScript
37
star
45

nosync

Prevent sync functions in your node programs after first tick
JavaScript
37
star
46

use-strict

Makes all subsequent modules in Node get loaded in strict mode.
JavaScript
37
star
47

ssh-key-decrypt

Decrypt and encrypted ssh private keys
JavaScript
35
star
48

ejsgi

Like JSGI, but using streams.
JavaScript
35
star
49

node-eliza

A Robotic Rogerian Therapist, on IRC
JavaScript
34
star
50

natives

Do stuff with Node.js's native JavaScript modules
JavaScript
31
star
51

goosh

Front-end old-style terminal interface, for web services like those provided by Google and Yahoo.
JavaScript
31
star
52

simple-node-server

A simple fast node http server toolkit.
JavaScript
30
star
53

util-extend

Node's internal object extension function, for you!
JavaScript
30
star
54

chownr

Like `chown -R`
JavaScript
28
star
55

csrf-lite

CSRF protection utility for framework-free node sites.
JavaScript
28
star
56

chmodr

Like `chmod -R` in node
JavaScript
28
star
57

path-scurry

TypeScript
27
star
58

node-hexedit

hexadecimal editor in node
JavaScript
27
star
59

back-to-markdown.css

Turns any markdown editor into a WYSIWYG editor
CSS
26
star
60

node-async-simple

Multiply two numbers, slowly, on the thread pool.
C++
26
star
61

node-strict

Makes your Node programs strict about stuff when loaded
JavaScript
25
star
62

json-stringify-nice

Stringify an object sorting scalars before objects, and defaulting to 2-space indent
JavaScript
25
star
63

promise-all-reject-late

Like Promise.all, but save rejections until all promises are resolved
JavaScript
24
star
64

promise-call-limit

Call an array of promise-returning functions, restricting concurrency to a specified limit.
TypeScript
24
star
65

fs.realpath

Use node's fs.realpath, but fall back to the JS implementation if the native one fails
JavaScript
24
star
66

node6-module-system-change

A demonstration of what changed in node 6's module loading logic
JavaScript
24
star
67

color-support

A module which will endeavor to guess your terminal's level of color support.
JavaScript
24
star
68

polite-json

TypeScript
23
star
69

ircretary

A note-taking IRC bot
JavaScript
23
star
70

yamlish

A parser for the yamlish format
JavaScript
22
star
71

sock-daemon

TypeScript
21
star
72

pseudomap

Like `new Map` but for older JavaScripts
JavaScript
21
star
73

node-fuse

Fuse bindings for nodejs
21
star
74

slocket

A locking socket alternative to file-system mutex locks
JavaScript
21
star
75

proto-list

A list of objects bound by prototype chain
JavaScript
20
star
76

retry-until

A function that will keep running a function you give it as long as it throws for a period of time
JavaScript
20
star
77

node-srand

srand bindings for node - Seedable predictable pseudorandom number generator
C++
20
star
78

mutate-fs

Mutate the Node.js filesystem behavior for tests.
JavaScript
20
star
79

ryp

Featureless npm-package bundling.
Shell
19
star
80

filewatcherthing

a thing to watch a file and then run a command
JavaScript
19
star
81

gatsby-remark-tumble-media

A plugin for gatsby-transformer-remark to support photosets, video, and audio in markdown frontmatter.
JavaScript
19
star
82

sodn

SOcial DNodes
JavaScript
19
star
83

joyent-node-on-smart-example

A blog post.
JavaScript
18
star
84

error-page

Easily send errors in Node.js HTTP servers. Think like the `ErrorDocument` declarations in Apache config files.
JavaScript
17
star
85

_ify

an itty bitty curry utility
JavaScript
17
star
86

url-parse-as-address

Parse a URL assuming that it's http/https, even if protocol or // isn't present
JavaScript
17
star
87

http-https

A wrapper that chooses http or https for requests
JavaScript
17
star
88

perfalize

TypeScript
16
star
89

cssmin

A cross-platform regular-expression based minifier for CSS
16
star
90

duplex-passthrough

like a passthrough, but in both directions
JavaScript
16
star
91

mintee

a tiny module for piping an input to multiple output streams
JavaScript
16
star
92

tap-assert

An assert module that outputs tap result objects
JavaScript
16
star
93

create-isaacs

An npm init module to create modules like I do
JavaScript
16
star
94

domain-http-server

A module thingie to use domains in Express or Restify or just regular HTTP servers
JavaScript
15
star
95

canonical-host

Node module to redirect users to the canonical hostname for your site.
JavaScript
15
star
96

fs-readstream-seek

A fs.ReadStream that supports seeking to arbtrary locations within a file.
JavaScript
15
star
97

hardhttps

Slightly hardened https for node
JavaScript
14
star
98

exit-code

`process.exitCode` behavior back-ported from io.js and Node.js 0.12+
JavaScript
14
star
99

mcouch

Put your CouchDB in Manta, attachments and docs and all
JavaScript
14
star
100

emcee

A bridge between the M and C bits of MVC
JavaScript
13
star