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

zane-anydoor

v1.0.0

Published

``` npm i -g zaneAnyDoor ``` ## 使用方法 ``` zaneAnyDoor # 把当前文件夹作为静态资源服务器根目录

Readme

zaneAnyDoor

安装

npm i -g zaneAnyDoor

使用方法

zaneAnyDoor # 把当前文件夹作为静态资源服务器根目录

zaneAnyDoor -p 4040 # 设置端口号 8080

zaneAnyDoor -h localhost # 设置host为 localhost

zaneAnyDoor -d /usr # 设置根目录为/usr

.npmignore 文件, 配置提交npm 包时忽略哪些文件

npm install eslint @eslint/js globals -D

创建 eslint.config.js 配置eslint规则 ,eslint ignore规则 参考eslint文档[https://eslint.org/docs/latest/use/configure/migration-guide#custom-parsers]

// eslint.config.js
import js from "@eslint/js"
import globals from 'globals'
import babelParser from "@babel/eslint-parser";

export default [
	js.configs.recommended,
	{
		rules: {
			"no-console": ["error", {
				"allow": ["warn", "error", "info"]
			}],


		},
		files: ["src/**/*.js"],
		ignores: ["node_modules", "test", "build", 'dist'],

		languageOptions: {
			parser: babelParser,
			sourceType: "module",
			ecmaVersion: "latest",
			globals: {
				...globals.es2015,
				...globals.mocha,
				...globals.node,
			}
		}
	}];
	// package.json 中 添加 scripts 命令
	{
		"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1",
		"lint": "eslint .",
		"fix": "eslint --fix ."
	},
	}

创建 .editorConfig 文件,设置项目的编辑器设置规范

# editorconfig.org

root = true

# # Apply for all files
[*]

charset = utf-8

indent_style = tab
indent_size = 2

end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.json]
indent_size = 4

npm install pre-commit -D

// package.json  添加pre-commit 命令,在commit前执行eslint检查
{
	"pre-commit": [
		"fix",
		"lint"
	],
}

npm install chalk

引入 chalk 插件,在控制台打印日志内容时,可以通过chalk添加颜色等

	console.info(chalk.green('msg'))

npm install handlebars

引入 handlebars 用于处理html模板语言 参考官方文档[https://handlebarsjs.com/guide/#installation]

// 创建handlebars 标签 文件: templates/dir.tpl
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>{{title}}</title>
	<style>
		body {
			margin:30px;
		}
		a {
			display:block;
			font-size:30px;
		}


	</style>
</head>
<body>
	{{#each files}}
		<a href="{{../dir}}/{{file}}">{{file}} 【{{icon}}】</a>
	{{/each}}
</body>
</html>
使用
// route.js
import Handlebars from 'handlebars'

const tplPath = path.join(__dirname, '../templates/dir.tpl')

const source = fs.readFileSync(tplPath)
const template = Handlebars.compile(source.toString())


const route = (req,res,filepath) => {

 const data = {
				files: files.map(file => ({
					file,
					icon: mimeType(file)
				})),
				title: path.basename(filePath),
				dir: dir ? `/${dir}` : ""
			}

	res.end(template(data))
}

npm install mime

引入 mime 插件,用于获取文件对应的content-type 类型,默认 text/plain

// helper/mime.js
import mime from "mime";
const mimeType = (filepath) => mime.getType(filepath) ?? 'text/plain'
export default mimeType
使用
//route.js

 import mimeType from './mime.js'
// 当找不到或者是目录是,用默认的text/plain
 const contentType =  mimeType(filepath) ?? 'text/plain'

 res.setHeader('Content-Type',contentType)

npm install zlip

引入 zlip 用于使用 gzip压缩文件内容,减小浏览器请求的文件资源大小

浏览器 http请求中 请求头携带 Accept-Encoding : gzip, deflate, br, ... , 这告诉服务器该浏览器接受哪些压缩方式
服务器返回时设置响应头 Content-Encoding:gzip, deflate,br ... , 这告诉浏览器 服务器返回的资源是通过什么压缩的,浏览器可以使用对应的方式解压
// helper/compress.js
import { createGzip, createDeflate } from 'zlib'

const compress = (rs, req, res) => {
	const acceptEncoding = req.headers['accept-encoding']

	if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) {
		return rs
	} else if (acceptEncoding.match(/\bgzip\b/)) {
		res.setHeader('Content-Encoding', 'gzip')
		// gzip压缩格式
		return rs.pipe(createGzip())
	}
	else if (acceptEncoding.match(/\bdeflate\b/)) {
		res.setHeader('Content-Encoding', 'deflate')
		// deflate压缩格式
		return rs.pipe(createDeflate())
	}
}

export default compress
使用
// route.js   gzip 压缩前 1.8k  压缩后 963B

 import compress from './compress.js
 import fs from 'fs'

 compress(fs.createReadStream(filepath),req,res).pipe(res)

 // 在浏览器加载的文件请求响应头可以看到 content-encoding:gzip

range 范围请求

range 包含 请求头 range, 响应头 Accept-Ranges Content-Range 等

	/* helper/range.js
	 * range : bytes=[start]-[end]   http 请求头携带range 参数,表示处理bytes类型数据,从start 到end
	 * Accept-Ranges:bytes           http 响应头 告诉浏览器客户端 是bytes类型的 range
	 * Content-Range:bytes start-end/total   http 响应头,告诉浏览器返回的内容是从start-end 这部分的内容/总大小
	*/

	const range = (totalSize, req, res) => {
	const range = req.headers['range']
	// 没有range 返回code 200
	if (!range) {
		return {
			code: 200
		}
	}

	const sizes = range.match(/bytes=(\d*)-(\d)/)
	const end = sizes?.[2] || totalSize - 1
	const start = sizes?.[1] || totalSize - end

	if (start <= 0 || end > totalSize || start > end) {
		return { code: 200 }
	}

	res.setHeader('Accept-Ranges', 'bytes')
	res.setHeader('Content-Range', `bytes ${start}-${end}/${totalSize}`)
	res.setHeader('Content-Length', end - start)
	return {
		//   http status 206表示这是一部分文件内容
		code: 206,
		start: parseInt(start),
		end: parseInt(end)
	}
}

export default range
使用
  let rs;
	const { code, start, end } = range(stats.size, req, res)
	res.statusCode = code ?? 200
	if (code === 200) {
		// 读取文件全部的内容到可读流里
		rs = fs.createReadStream(filePath)
	} else {
		// 读取文件部分内容到可读流里
		rs = fs.createReadStream(filePath, { start, end })
	}

npm install curl

curl 可以用于在端口调试指定路径的请求,可以便捷设置对应的请求头,便于node调试

在 cmd 窗口中,输入 curl -v -r 0-10 http://127.0.0.1:9527/LICENSE , 返回一下内容,可以看到请求头携带Range:bytes=1-10,响应头返回Accept-Ranges Content-Range 告诉浏览器这是那部分内容
// 命令提示符 - powershell 不可以直接使用curl
C:\Users\zchuangzhen>curl -v -r 0-10  http://127.0.0.1:9527/LICENSE
*   Trying 127.0.0.1:9527...

// 请求头
* Connected to 127.0.0.1 (127.0.0.1) port 9527
> GET /LICENSE HTTP/1.1
> Host: 127.0.0.1:9527
>** Range: bytes=0-10**
> User-Agent: curl/8.9.1
> Accept: */*
>
* Request completely sent off

// 响应头
< HTTP/1.1 206 Partial Content
< Content-Type: text/plain
< **Accept-Ranges: bytes**
< **Content-Range: bytes 0-10/1089**
< **Content-Length: 10**
< Date: Fri, 20 Dec 2024 05:58:46 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
<
// 内容部分   * 后边不是
**MIT Licens***  Excess found writing body: excess = 1, size = 10, maxdownload = 10, bytecount = 10
* shutting down connection #0

缓存

http 缓存策略有几种,常用http请求头如下:

  1. 强缓存 : 通过请求头设置资源过期日期和时间,让浏览器在指定时间内直接使用本地缓存,而不向服务器发送请求

    1. Cache-Control:指定资源从请求时间起的缓存最大秒数,eg:Cache-Control:max-age:5000
    2. Expires:指定资源过期日期和时间,eg: Expires: Wed, 21 Oct 2025 07:28:00 GMT
  2. 对比缓存 : 通过请求头设置(如 If-Modified-SinceIf-None-Match)属性,来向服务器询问资源是否有更新,未更新时返回304,已更新时服务器返回对应Last-ModifiedETag 响应头

    1. Last-Modified : 响应头中 表示该资源上次更新日期时间 eg: Last-Modified:Wed, 21 Oct 2023 07:28:00
    2. ETag : 响应头中 已更新资源的标识 eg: ETag:123asd
    3. If-Modified-Since/If-None-Match : 搭配Last-Modified/ETag(上次请求时响应头存在这俩时)使用,重新发起请求时,请求头自动带上If-Modified-Since/If-None-Match ,服务器拿到后跟原Last-Modified和 ETag的值对比,不一致时返回新的内容,一样时表示资源未更新,返回304
  3. 协商缓存 : 强缓存和对比缓存的结合,在指定强缓存时间内,浏览器使用强缓存,过期后,浏览器通过对比缓存头 去对比检查资源是否变化

  4. 私有缓存/公共缓存

    1. 私有 : 不允许代理服务器缓存,仅针对单个用户 eg: Cacha-Control:private
    2. 公共 : 允许代理服务器缓存, Cache-Control:public
  5. 不缓存 : 明确告诉浏览器不缓存,每次都重新从服务器获取

    1. Cache-Control:no-store : 每次都从服务器获取,不缓存
    2. Cache-Control:no-cache : 每次请求都需要向服务器验证资源是否变化
// helper/cache.js
import { cache } from '../config/defaultConfig'

const cache = (stats, req, res) => {
	const { maxAge } = cache
	let lastModified = stats.mtime.toUTCString()
	let eTag = `${stats.size}-${stats.mtime.getTime()}`

	res.setHeader('Cache-Control', `max-age=${maxAge}`)
	res.setHeader('Expires', new Date(Date.now() + maxAge * 1000).toUTCString())

	if (req.headers['if-modified-sice'] === lastModified || req.headers['if-none-match'] === eTag) {
		console.info(1)
		return {
			// 当返回code是 304时,设置http statsCode = 304, 直接 res.end(), 不用返回资源内容
			cacheCode: 304
		}
	}

	console.info(2)
	res.setHeader('Last-Modified', lastModified)
	res.setHeader('ETag', eTag)
	return {
		cacheCode: 200
	}
}

export default cache
使用
// helper/route.js

	const { cacheCode } = cache(stats, req, res)
	if (cacheCode === 304) {
		res.statusCode = cacheCode
		res.end()
		return
	}

npm i yargs

格式化处理命令行输入的参数, 如 node src/index.js -p 9999

// index.js
import  yargs  from "yargs";
import { hideBin } from 'yargs/helpers'
import Server from "./app.js";

const argv = yargs(hideBin(process.argv))
	.option('p', {
		alias: 'port',
		describe: '端口号',
		default: 9527
	})
	.option('d', {
		alias: 'root',
		describe: 'root path',
		default: process.cwd()
	})
	.option('h', {
		alias: 'host',
		describe: 'host',
		default: '127.0.0.1'
	})
	.version()
	.alias('v', 'version')
	.help()
	.argv;

	argv 是一个对象,合并了 默认的config和 命令行携带的参数

设置package.json bin 参数,指定可执行文件的路径,方便在全局或局部按照包时,能方便运行这些可执行文件

// 指定zaneAnyDoor 指令的执行路径
	"bin": {
		"zaneAnyDoor":"bin/zaneAnyDoor"
	},
// bin/zaneAnyDoor
// 这一句的意思时指定不同操作系统通过node脚本文件解释器执行下边的代码
#! /usr/bin/env node

import '../src/index.js'

chmod +x bin/zaneAnyDoor 修改命令的可执行权限

// 在 git bash 下
ls -l bin/zaneAnyDoor 可以看到该命令是否有权限
// -rwxr-xr-x 1 zchuangzhen 1049089 47 12月 31 17:24 bin/zaneAnyDoo  表示有执行和可读权限

本地验证 bin/zaneAnyDoor 命令

bin/zaneAnyDoor -p 9990
// 执行命令对应的内容,启动src/index.js ,服务启动
// Server started at: http://127.0.0.1:9990