Skip to content
0
/ Packages/NodeJs
6/1/2026
24.2m
AI 摘要

Node 包速查:got/nock、chokidar、sharp、vitest/playwright、vite-node、dotenv 等。

NodeJs ​

NodeJs 相关的包

mono-jsx ---> <html> as a Response
​

mono-jsx 是一个 JSX 运行时,在 nodeJs,deno,bun,bun,cloudflare worker 等中,在 JS Runtime 中呈现响应对象的<html> 元素。

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "mono-jsx"
  }
}

NodeJs 中的使用示例,利用 srvx 启动:

// app.tsx
import { serve } from "srvx";

serve({
  port: 3000,
  fetch: (req) => (
    <html>
      <h1>Welcome to mono-jsx!</h1>
    </html>
  ),
});

synckit ---> worker_threads 同步执行异步工作
​

在 vitepress 的一个 commit 中,使用了这个库,取得了不错的性能提升

// runner.js
import { createSyncFn } from 'synckit'

// the worker path must be absolute
const syncFn = createSyncFn(require.resolve('./worker'), {
  tsRunner: 'tsx', // optional, can be `'ts-node' | 'esbuild-register' | 'esbuild-runner' | 'tsx'`
})

// do whatever you want, you will get the result synchronously!
const result = syncFn(...args)
// worker.js
import { runAsWorker } from 'synckit'

runAsWorker(async (...args) => {
  // do expensive work
  return result
})

std-env ---> 检测当前运行的环境
​

// ESM
import {
  hasTTY,
  hasWindow,
  isDebug,
  isDevelopment,
  isLinux,
  isMacOS,
  isMinimal,
  isProduction,
  isTest,
  isWindows,
  platform,
  isColorSupported,
  nodeVersion,
  nodeMajorVersion
} from "std-env";

verdaccio ---> 私有 npm registry
​

安装和启动:

$ npm install -g verdaccio
$ verdaccio # start a server at http://localhost:4873

安装包的时候指定本地代理:

$ npm install lodash --registry http://localhost:4873

crossws ---> 跨平台的 WebSocket 库
​

优雅、类型化且简单的工具包,用于实现跨平台 WebSocket 服务器。

import { defineHooks } from "crossws";
import crossws from "crossws/adapters/<adapter>";

const ws = crossws({
  hooks: {
    open(peer) {
      console.log("[ws] open", peer);
    },

    message(peer, message) {
      console.log("[ws] message", peer, message);
      if (message.text().includes("ping")) {
        peer.send("pong");
      }
    },

    close(peer, event) {
      console.log("[ws] close", peer, event);
    },

    error(peer, error) {
      console.log("[ws] error", peer, error);
    },
  },
});

forge ---> TLS 的 native 实现
​

可以用于生成证书,例如 vitejs/vite-plugin-basic-ssl 的底层实现就是使用了这个库

// @ts-ignore
import forge from 'node-forge/lib/forge'
// @ts-ignore
import 'node-forge/lib/pki'
const keyPair = forge.pki.rsa.generateKeyPair(keySize)

const cert = forge.pki.createCertificate()

connect ---> 一个 Node.js 的中间件层
​

算比较老的包

var connect = require('connect');
var http = require('http');

var app = connect();

// gzip/deflate outgoing responses
var compression = require('compression');
app.use(compression());

// store session state in browser cookie
var cookieSession = require('cookie-session');
app.use(cookieSession({
    keys: ['secret1', 'secret2']
}));

// parse urlencoded request bodies into req.body
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

// respond to all requests
app.use(function(req, res){
  res.end('Hello from Connect!\n');
});

//create node.js http server and listen on port
http.createServer(app).listen(3000);

fdir ---> 目录读取和 glob 模式抓取
​

类似 fs.readdir 的功能,但是它可以在不到 1 秒的时间内轻松抓取包含 100 万个文件的目录。详细 Benchmark

import { fdir } from "fdir";

// create the builder
const api = new fdir().withFullPaths().crawl("path/to/dir");

// get all files in a directory synchronously
const files = api.sync();

// or asynchronously
api.withPromise().then((files) => {
  // do something with the result here.
});

knip ---> 检测项目中无用的代码
​

检测没用到的导出,文件,甚至是 NPM 依赖...,目前内置了 .vue 的支持

$ pnpm create @knip/config # 安装
$ pnpm knip # 使用

此外,有一个专注于 ts 代码的移除 tsr,功能更少但更加专注。详情查看:line/tsr

bundlesize ---> 检验打包产物大小
​

保证产物的大小不超过预期值

$ npm i bundlesize
{
  "scripts": {
    "test": "bundlesize"
  },
  "bundlesize": [
    {
      "path": "./build/vendor.js",
      "maxSize": "3 kB"
    }
  ]
}

tmp ---> 临时文件和目录的创建器
​

随机创建一个目录或者文件,并自动或者手动在合适的时机清除。更推荐使用 promise 版本的 benjamingr/tmp-promise

import { file } from 'tmp-promise'

(async () => {
  const {fd, path, cleanup} = await file();
  // work with file here in fd
  cleanup();
})();

exegesis ---> 用于实现服务器端 OpenAPI 3.0.0 的工具
​

简而言之,就是通过读取 OpenAPI 的 yaml 文件信息,直接启动一个 NodeJs 服务器, 此外,也提供了 express 版本的服务

import * as path from 'path';
import * as http from 'http';
import * as exegesis from 'exegesis';

// See https://github.com/exegesis-js/exegesis/blob/master/docs/Options.md
const options = {
  controllers: path.resolve(__dirname, './src/controllers'),
};

// `compileApi()` can either be used with a callback, or if none is provided,
// will return a Promise.
exegesis.compileApi(
  path.resolve(__dirname, './openapi/openapi.yaml'),
  options,
  (err, middleware) => {
    if (err) {
      console.error('Error creating middleware', err.stack);
      process.exit(1);
    }

    const server = http.createServer((req, res) =>
      middleware(req, res, (err) => {
        if (err) {
          res.writeHead(err.status || 500);
          res.end(`Internal error: ${err.message}`);
        } else {
          res.writeHead(404);
          res.end();
        }
      })
    );

    server.listen(3000);
  }
);
import express from "express";
import path from "path";
import http from "http";
import * as exegesisExpress from "exegesis-express";

async function createServer() {
  // See https://github.com/exegesis-js/exegesis/blob/master/docs/Options.md
  const options = {
    controllers: path.resolve(__dirname, "./controllers"),
  };

  const exegesisMiddleware = await exegesisExpress.middleware(
    path.resolve(__dirname, "./openapi.yaml"),
    options
  );

  const app = express();

  // If you have any body parsers, this should go before them.
  app.use(exegesisMiddleware);

  app.use((req, res) => {
    res.status(404).json({ message: `Not found` });
  });

  app.use((err, req, res, next) => {
    res.status(500).json({ message: `Internal error: ${err.message}` });
  });

  const server = http.createServer(app);
  server.listen(3000);
}

volta ---> 工具版本固定
​

打开项目时候,自动切换对于的 node 版本和 yarn 版本

$ volta pin node@16

mem ---> 缓存结果
​

缓存相同的输入,以便更快的输出

import mem from 'mem';
import got from 'got';
import delay from 'delay';

const memGot = mem(got, {maxAge: 1000});

await memGot('https://sindresorhus.com');

// This call is cached
await memGot('https://sindresorhus.com');

await delay(2000);

// This call is not cached as the cache has expired
await memGot('https://sindresorhus.com');

depark ---> 通过PangRank算法计算最重要文件
​

$ npx deprank ./fixtures

| Filename               | Lines | Dependents | PageRank |
----------------------------------------------------------
| fixtures/core.js       | 3     | 1          | 0.284098 |
| fixtures/utils.js      | 4     | 3          | 0.268437 |
| fixtures/user/user.js  | 4     | 1          | 0.132253 |
| fixtures/todo.js       | 6     | 1          | 0.089796 |
| fixtures/user/index.js | 1     | 1          | 0.089796 |
| fixtures/concepts.js   | 4     | 1          | 0.079694 |
| fixtures/index.js      | 4     | 0          | 0.055926 |

cosmiconfig ---> 加载配置文件
​

例如当moduleName是myapp时,会依次加载查找:

  • 带有myapp属性的package.json
  • JSON和YAML格式的.myapprc
  • .myapprc.json,.myapprc.yaml,.myapprc.yml,.myapprc.js,.myapprc.cjs
  • myapp.config.js,myapp.config.cjs
const { cosmiconfig, cosmiconfigSync } = require('cosmiconfig');
// ...
const explorer = cosmiconfig(moduleName);

// Search for a configuration by walking up directories.
// See documentation for search, below.
explorer.search()
  .then((result) => {
    // result.config is the parsed configuration object.
    // result.filepath is the path to the config file that was found.
    // result.isEmpty is true if there was nothing to parse in the config file.
  })
  .catch((error) => {
    // Do something constructive.
  });

dotenv ---> 添加环境变量到node进程中
​

# .env
S3_BUCKET="YOURS3BUCKET"
SECRET_KEY="YOURSECRETKEYGOESHERE"
import 'dotenv/config'
console.log(process.env)

/*
  {
    ...
    S3_BUCKET="YOURS3BUCKET"
    SECRET_KEY="YOURSECRETKEYGOESHERE"
  }
*/

socket.io ---> 双向通信
​

依赖于 Engine.IO,并不是 websocket。尽管 Socket.IO 确实尽可能使用 WebSocket 作为传输,但它会为每个数据包添加一些元数据:数据包类型、名称空间和需要消息确认时的 ack id。这就是为什么 WebSocket 客户端将无法成功连接到 Socket.IO 服务器,而 Socket.IO 客户端也将无法连接到 WebSocket 服务器

io.on('connection', socket => {
  socket.emit('request', /* … */); // emit an event to the socket
  io.emit('broadcast', /* … */); // emit an event to all connected sockets
  socket.on('reply', () => { /* … */ }); // listen to the event
});

tinypool ---> nodejs workers pool
​

线程池,也可以试试尤的 yyx990803/okie

import path from 'path'
import Tinypool from 'tinypool'

const pool = new Tinypool({
  filename: new URL('./worker.js', import.meta.url).href,
})

const result = await pool.run({ a: 4, b: 6 })
console.log(result) // Prints 10

birpc ---> 基于消息的双向远程过程调用
​

基于消息的双向远程过程调用。对 WebSockets 和 Workers 通信很有用

// client 
import type { ServerFunctions } from './types'

const ws = new WebSocket('ws://url')

const clientFunctions: ClientFunctions = {
  hey(name: string) {
    return `Hey ${name} from client`
  }
}

const rpc = createBirpc<ServerFunctions>(
  clientFunctions,
  {
    post: data => ws.send(data),
    on: data => ws.on('message', data),
    // these are required when using WebSocket
    serialize: v => JSON.stringify(v),
    deserialize: v => JSON.parse(v),
  },
)

await rpc.hi('Client') // Hi Client from server


// server
import { WebSocketServer } from 'ws'
import type { ClientFunctions } from './types'

const serverFunctions: ServerFunctions = {
  hi(name: string) {
    return `Hi ${name} from server`
  }
}

const wss = new WebSocketServer()

wss.on('connection', (ws) => {
  const rpc = createBirpc<ClientFunctions>(
    serverFunctions,
    {
      post: data => ws.send(data),
      on: data => ws.on('message', data),
      serialize: v => JSON.stringify(v),
      deserialize: v => JSON.parse(v),
    },
  )

  await rpc.hey('Server') // Hey Server from client
})

compare-versions ---> semver 版本比较器
​

比较大小,验证条件等功能

import { compareVersions, compare, satisfies, validate } from 'compare-versions';

compareVersions('11.1.1', '10.0.0'); //  1
compare('10.1.8', '10.0.4', '>');  // true
satisfies('10.0.1', '~10.0.0');  // true
validate('1.0.0-rc.1'); // true

write-file-atomic ---> 原子化写文件
​

(async () => {
  try {
    await writeFileAtomic('message.txt', 'Hello Node', {chown:{uid:100,gid:50}});
    console.log('It\'s saved!');
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
})();

connect ---> 中间件 HTTP 框架
​

var connect = require('connect');
var http = require('http');
 
var app = connect();
 
// gzip/deflate outgoing responses
var compression = require('compression');
app.use(compression());
 
// store session state in browser cookie
var cookieSession = require('cookie-session');
app.use(cookieSession({
    keys: ['secret1', 'secret2']
}));
 
// parse urlencoded request bodies into req.body
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));
 
// respond to all requests
app.use(function(req, res){
  res.end('Hello from Connect!\n');
});
 
//create node.js http server and listen on port
http.createServer(app).listen(3000);

get-node ---> 下载指定版本的 NodeJs
​

import getNode from 'get-node'

// Download a specific Node.js release
const { path, version } = await getNode('8')
console.log(path) // /home/user/.cache/nve/8.17.0/node
console.log(version) // 8.17.0

crawlee ---> NodeJs 爬虫
​

支持 cheerio、playwright、puppeteer

import { PlaywrightCrawler, Dataset } from 'crawlee';

// PlaywrightCrawler crawls the web using a headless
// browser controlled by the Playwright library.
const crawler = new PlaywrightCrawler({
    // Use the requestHandler to process each of the crawled pages.
    async requestHandler({ request, page, enqueueLinks, log }) {
        const title = await page.title();
        log.info(`Title of ${request.loadedUrl} is '${title}'`);

        // Save results as JSON to ./storage/datasets/default
        await Dataset.pushData({ title, url: request.loadedUrl });

        // Extract links from the current page
        // and add them to the crawling queue.
        await enqueueLinks();
    },
    // Uncomment this option to see the browser window.
    // headless: false,
});

// Add first URL to the queue and start the crawl.
await crawler.run(['https://crawlee.dev']);

lucia ---> Authentication 库
​

import { Lucia } from "lucia";

const lucia = new Lucia(new Adapter(db));

const session = await lucia.createSession(userId, {});
await lucia.validateSession(session.id);

Lucia 是一个用 TypeScript 编写的 auth 库,它抽象化了处理会话的复杂

winston ---> logger 工具
​

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  defaultMeta: { service: 'user-service' },
  transports: [
    // - Write all logs with importance level of `error` or less to `error.log`
    // - Write all logs with importance level of `info` or less to `combined.log`
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}

ponycode ---> 域名编码
​

参考维基百科

Punycode 的目的是在于国际化域名标签(IDNA)的框架中,使这些(多语言)的域名可以编码为 ASCII。编码语法在文档 RFC 3492 中规定

// encode domain name parts
punycode.encode('mañana'); // 'maana-pta'
punycode.encode('☃-⌘'); // '--dqo34k'

// decode domain name parts
punycode.decode('maana-pta'); // 'mañana'
punycode.decode('--dqo34k'); // '☃-⌘'

hono ---> Web 框架
​

支持 deno 和 bun,速度快,支持 JSX

import type { FC } from 'hono/jsx'

const app = new Hono()

const Layout: FC = (props) => {
  return (
    <html>
      <body>{props.children}</body>
    </html>
  )
}

const Top: FC<{ messages: string[] }> = (props: { messages: string[] }) => {
  return (
    <Layout>
      <h1>Hello Hono!</h1>
      <ul>
        {props.messages.map((message) => {
          return <li>{message}!!</li>
        })}
      </ul>
    </Layout>
  )
}

app.get('/', (c) => {
  const messages = ['Good Morning', 'Good Evening', 'Good Night']
  return c.html(<Top messages={messages} />)
})

nodemailer ---> Send Email with NodeJs
​

发送邮件服务

const transporter = nodemailer.createTransport({
  host: 'smtp.163.com',
  port: 465,
  secure: true, // `true` for port 465, `false` for all other ports
  auth: {
    user: 'hi@peterroe.me',
    pass: process.env.EMAIL_AUTH_PASS,
  },
})

await transporter.sendMail({
  from: '"Peter Roe👻" <hi@peterroe.me>', // sender address
  to: ['xxx@xx.com'], // list of receivers
  subject: 'Hello World' + name,
  html: `<h1>Hello World</h1>` // html body
})

下面是我写的一个例子,填入你的邮箱,按下按钮之后,你的邮箱将会收到一封来自我的邮件

除此之外,你可以用这个网站来进行可视化的 HTML 电子邮件设计

codspeed ---> benchmark 测试
​

例如可以结合 Vitest 做 benchmark

$ pnpm add -D @codspeed/vitest-plugin vitest

示例:

import { defineConfig } from "vitest/config";
import codspeedPlugin from "@codspeed/vitest-plugin";

export default defineConfig({
  plugins: [codspeedPlugin()],
  // ...
});
export function fib(n: number): number {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
}
import { bench, describe } from "vitest";
import { fib } from "./fib";

describe("fib", () => {
  bench("fibo 10", () => {
    fib(10);
  });

  bench("fibo 15", () => {
    fib(15);
  });
});
$ vitest bench

[CodSpeed] bench detected but no instrumentation found, falling back to default vitest runner
 RUN  v1.0.3 ...

 ✓ fib.bench.ts (2) 1521ms
   ✓ fib (2) 1520ms
     name                hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · fibo 10   1,760,687.42  0.0005  1.0279  0.0006  0.0006  0.0007  0.0007  0.0016  ±0.50%   880344   fastest
   · fibo 15     173,932.15  0.0052  0.0594  0.0057  0.0059  0.0064  0.0070  0.0147  ±0.08%    86967


 BENCH  Summary

  fibo 10 - fib.bench.ts > fib
    10.12x faster than fibo 15

sharp ---> 高性能NodeJs图片加工
​

典型用例是将常见格式的大图像转换为更小的、对 Web 友好的、不同尺寸的 JPEG、PNG、WebP、GIF 和 AVIF 图像

const sharp = require('sharp')

sharp('test.svg')
  .rotate()
  .resize(200)
  .jpeg({ mozjpeg: true })
  .toFile('hh.jpeg')

相似库:jimp-dev/jimp

nock ---> 基于NodeJs的http服务模拟
​

import got from 'got';
import nock from 'nock';

const scope = nock('https://sindresorhus.com')
	.get('/')
	.reply(500, 'Internal server error')
	.persist();

try {
	await got('https://sindresorhus.com')
} catch (error) {
	console.log(error.response.body);
	//=> 'Internal server error'

	console.log(error.response.retryCount);
	//=> 2
}

scope.persist(false);

got ---> 友好的NodeJs的http请求库
​

用法类似axios,但是又很多特性:

  • http2支持
  • 代理
  • 重发
  • Cache
  • Unix域Socket
  • 测试
  • Stream
  • ...

qnm ---> 查看依赖的详情信息
​

$ npm i -g qnm
$ qnm lodash
lodash 4.17.21 ↰ 2 days ago
├── 4.17.21 ✓
├─┬ cli-table2
│ └── 3.10.1 ⇡ 1 year ago
└─┬ karma
  └── 3.10.1 ⇡ 1 year ago

shx ---> node的便携式shell命令
​

无关平台,执行shell命令,只需要加上前缀,例如:

$ npm install -g shx
$ shx rm -rf node_modules
$ shx cp a.txt b.txt

适合在没有shell的环境,如windows上使用

playwright ---> e2e测试框架
​

一些特性:

  • 支持chromium、firefox、webkit
  • 页面截图
  • 模拟手机型号与地理位置
  • 获取浏览器上下文信息
  • 拦截网络请求
import { test, devices } from '@playwright/test';

test.use({
  ...devices['iPhone 13 Pro'],
  locale: 'en-US',
  geolocation: { longitude: 12.492507, latitude: 41.889938 },
  permissions: ['geolocation'],
})

test('Mobile and geolocation', async ({ page }) => {
  await page.goto('https://maps.google.com');
  await page.locator('text="Your location"').click();
  await page.waitForRequest(/.*preview\/pwa/);
  await page.screenshot({ path: 'colosseum-iphone.png' });
});

happy-dom ---> 更加轻量和快速的DOM环境
​

相比于JSDOM,更加轻量和快速,常用于测试框架、SSR框架中

import { Window } from 'happy-dom';

const window = new Window();
const document = window.document;

document.body.innerHTML = '<div class="container"></div>';

const container = document.querySelector('.container');
const button = document.createElement('button');

container.appendChild(button);

// Outputs "<div class="container"><button></button></div>"
console.log(document.body.innerHTML);

jsdom ---> 在NodeJs提供DOM环境
​

属于比较早期的库,很可惜不支持esm

const { JSDOM } = require('jsdom');

const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
console.log(dom.window.document.querySelector("p").textContent); // "Hello world"

chokidar ---> 监听文件修改
​

基于NodeJs的fs.watch,但是有着更多的优点

const chokidar = require('chokidar');

// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
  console.log(event, path);
});

vite-node ---> 给 node 程序 vite 转换的能力
​

命令行方式使用,直接执行一个 TS 文件

$ npx vite-node index.ts

或者是 API 方法使用

import { createServer } from 'vite'
import { ViteNodeServer } from 'vite-node/server'
import { ViteNodeRunner } from 'vite-node/client'
import { installSourcemapsSupport } from 'vite-node/source-map'

// create vite server
const server = await createServer({
  optimizeDeps: {
    disabled: true,
  },
})
// this is need to initialize the plugins
await server.pluginContainer.buildStart({})

// create vite-node server
const node = new ViteNodeServer(server)
installSourcemapsSupport({
  getSourceMap: source => node.getSourceMap(source),
})

// create vite-node runner
const runner = new ViteNodeRunner({
  root: server.config.root,
  base: server.config.base,
  fetchModule(id) {
    return node.fetchModule(id)
  },
  resolveId(id, importer) {
    return node.resolveId(id, importer)
  },
})
await runner.executeFile('./example.ts')
await server.close()

local-pkg ---> find message of local package
​

查找包的信息

import {
  getPackageInfo,
  importModule,
  isPackageExists,
  resolveModule,
} from 'local-pkg'

isPackageExists('local-pkg') // true
isPackageExists('foo') // false

await getPackageInfo('local-pkg')
/* {
 *   name: "local-pkg",
 *   version: "0.1.0",
 *   rootPath: "/path/to/node_modules/local-pkg",
 *   packageJson: {
 *     ...
 *   }
 * }
 */

// similar to `require.resolve` but works also in ESM
resolveModule('local-pkg')
// '/path/to/node_modules/local-pkg/dist/index.cjs'

// similar to `await import()` but works also in CJS
const { importModule } = await importModule('local-pkg')

tinybench ---> for benchmark
​

测试方法的运行时长

import { Bench } from 'tinybench';

const bench = new Bench({ time: 100 });

bench
  .add('faster task', () => {
    console.log('I am faster')
  })
  .add('slower task', async () => {
    await new Promise(r => setTimeout(r, 1)) // we wait 1ms :)
    console.log('I am slower')
  })
  .todo('unimplemented bench')

await bench.run();

console.table(bench.table());

输出:

(index)Task Nameops/secAverage Time (ns)MarginSamples
0'faster task''41,621'24025.791819761525'±20.50%'4257
1'slower task''828'1207382.7838323202'±7.07%'83

why-is-node-running ---> 检测导致进程没结束的原因
​

const log = require('why-is-node-running') // should be your first require
const net = require('net')

function createServer () {
  const server = net.createServer()
  setInterval(function () {}, 1000)
  server.listen(0)
}

createServer()
createServer()

setTimeout(function () {
  log() // logs out active handles that are keeping node running
}, 100)

AdminJS ---> admin pane for NodeJs
​

  • framework: such as express、koa、nestjs

  • database adapter: such as mongoose、sequelize、typeorm

$ npm i adminjs @adminjs/[your framework] @adminjs/[your database adapter]

find-up ---> find a file or directory
​

Find a file or directory for walking up the parent directories

/
└── Users
    └── sindresorhus
        ├── unicorn.png
        └── foo
            └── bar
                ├── baz
                └── example.js
import path from 'node:path';
import {findUp, pathExists} from 'find-up';

console.log(await findUp('unicorn.png'));
//=> '/Users/sindresorhus/unicorn.png'

expect-type ---> Unit test for TS type
​

import {foo, bar} from '../foo'
import {expectTypeOf} from 'expect-type'

test('foo types', () => {
  // make sure `foo` has type {a: number}
  expectTypeOf(foo).toMatchTypeOf<{a: number}>()

  // make sure `bar` is a function taking a string:
  expectTypeOf(bar).parameter(0).toBeString()
  expectTypeOf(bar).returns.not.toBeAny()
})

node-cron ---> cron for NodeJs
​

A simple cron-like job scheduler for Node.js

import cron from 'node-cron';

cron.schedule('* * * * *', () => {
  console.log('running a task every minute');
});

supertest ---> HTTP 测试框架
​

const app = createApp()

describe('GET /users', function() {
  it('responds with json', function() {
    return request(app)
      .get('/users')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .then(response => {
          assert(response.body.email, 'foo@bar.com')
      })
  });
});

autocannon ---> HTTP/1.1 Beachmark
​

Benchmark for http, supports output tables

$ npm i -g autocannon
$ autocannon -c 100 -d 5 -p 10 http://localhost:3000

TIP

Linux 系统上还可以直接使用 ab -n10000 -c100 http://127.0.0.1:3000/ 进行测试,无需安装其他工具

exit-hook ---> run code when process exit
​

Support async tasks, But must use with gracefulExit

import exitHook from 'exit-hook';

exitHook(signal => {
	console.log(`Exiting with signal: ${signal}`);
});

// You can add multiple hooks, even across files
exitHook(() => {
	console.log('Exiting 2');
});

throw new Error('🦄');

//=> 'Exiting'
//=> 'Exiting 2'

Released under the MIT License.