惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
J
Java Code Geeks
G
Google Developers Blog
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
月光博客
月光博客
B
Blog
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
Jina AI
Jina AI
S
SegmentFault 最新的问题
H
Help Net Security
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
Google DeepMind News
Google DeepMind News

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I Published My First npm Package: Here's Everything I Wis...
Alex Chen · 2026-05-16 · via DEV Community

Alex Chen

I Published My First npm Package: Here's Everything I Wish I Knew

Publishing to npm isn't hard. But there are gotchas. Here's my experience.

The Package I Published

Name: @armorbreak/fast-safe-stringify
Purpose: Faster and safer JSON.stringify with circular reference handling
Size: 2KB minified, 0 dependencies
Time to build: 2 hours
Time to publish: 30 minutes (including learning curve)

Enter fullscreen mode Exit fullscreen mode

Step 1: Project Setup

mkdir fast-safe-stringify
cd fast-safe-stringify
npm init -y

# Essential files you need:
touch index.js      # Main code
touch README.md     # Documentation
touch .gitignore    # Ignore node_modules, etc.
touch LICENSE       # MIT license (recommended)
touch .npmignore    # What NOT to publish

Enter fullscreen mode Exit fullscreen mode

Step 2: package.json Configuration

{
  "name": "fast-safe-stringify",
  "version": "1.0.0",
  "description": "Fast, safe JSON.stringify with circular reference protection",
  "main": "index.js",
  "types": "index.d.ts",        // TypeScript declarations!
  "files": [                     // What gets published (be explicit)
    "index.js",
    "index.d.ts",
    "README.md",
    "LICENSE"
  ],
  "scripts": {
    "test": "node --test test/*.test.js",
    "prepublishOnly": "npm test", // Tests run before every publish
    "lint": "eslint index.js"
  },
  "keywords": ["json", "stringify", "circular", "fast", "safe"],
  "author": "Alex Chen <contact@agentvote.cc>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/armorbreak001/fast-safe-stringify"
  },
  "engines": {
    "node": ">=18.0.0"           // Minimum Node.js version
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 3: The Code

// index.js
'use strict';

function stringify(value, replacer, space) {
  const seen = new WeakSet();

  return JSON.stringify(value, function(key, val) {
    if (typeof val === 'object' && val !== null) {
      if (seen.has(val)) return '[Circular]';
      seen.add(val);
    }

    // Handle BigInt
    if (typeof val === 'bigint') return val.toString();

    // Handle undefined in arrays
    if (typeof val === 'undefined' && Array.isArray(this)) return null;

    // Apply custom replacer
    if (replacer) {
      const result = typeof replacer === 'function'
        ? replacer(key, val)
        : replacer;
      if (result !== undefined) return result;
    }

    return val;
  }, space);
}

module.exports = stringify;
module.exports.default = stringify;
module.exports.stringify = stringify;

Enter fullscreen mode Exit fullscreen mode

Step 4: TypeScript Declarations

// index.d.ts — Even if you write in JS, provide types!
declare function stringify(
  value: any,
  replacer?: ((key: string, value: any) => any) | string[] | null,
  space?: string | number
): string;

export default stringify;
export { stringify };

Enter fullscreen mode Exit fullscreen mode

Step 5: Tests

// test/stringify.test.js
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const stringify = require('../index.js');

describe('fast-safe-stringify', () => {
  it('stringifies basic objects', () => {
    assert.equal(stringify({ a: 1 }), '{"a":1}');
  });

  it('handles circular references', () => {
    const obj = { name: 'test' };
    obj.self = obj;
    const result = stringify(obj);
    assert.ok(result.includes('[Circular]'));
    assert.ok(!result.includes('TypeError'));
  });

  it('handles BigInt', () => {
    const result = stringify({ big: BigInt(9007199254740991) });
    assert.ok(result.includes('9007199254740991'));
  });

  it('handles undefined in arrays', () => {
    const result = stringify([1, undefined, 3]);
    assert.equal(result, '[1,null,3]');
  });

  it('supports replacer function', () => {
    const result = stringify(
      { password: 'secret', name: 'Alex' },
      (key, val) => key === 'password' ? '***' : val
    );
    assert.equal(result, '{"password":"***","name":"Alex"}');
  });

  it('supports pretty printing', () => {
    const result = stringify({ a: 1 }, null, 2);
    assert.ok(result.includes('\n'));
    assert.ok(result.includes('  '));
  });
});

Enter fullscreen mode Exit fullscreen mode

Step 6: .npmignore

# Don't publish these files:
node_modules/
test/
.github/
.git/
.eslintrc*
.prettierrc*
.vscode/
*.test.js
coverage/
.nyc_output/

Enter fullscreen mode Exit fullscreen mode

Step 7: Publish

# Check what will be published (dry run)
npm pack --dry-run

# If it looks good, publish!
npm publish

# For scoped packages (@username/package), use:
npm publish --access public

# Update version (follow semver!)
npm version patch   # 1.0.0 → 1.0.1 (bug fix)
npm version minor   # 1.0.0 → 1.1.0 (new feature, backwards compatible)
npm version major   # 1.0.0 → 2.0.0 (breaking change)

# Each npm version also creates a git tag automatically

Enter fullscreen mode Exit fullscreen mode

Things I Wish I Knew

1. Name Availability

# Check if name is available before you start!
npm view package-name

# Scoped names are always available:
@armorbreak/anything-here  ← Always available to you

# But public scoped packages need --access public flag

Enter fullscreen mode Exit fullscreen mode

2. package.json "files" Field

// Without "files": npm publishes EVERYTHING (including tests, configs, etc.)
// With "files": npm ONLY publishes what you list
{
  "files": ["index.js", "index.d.ts", "README.md", "LICENSE"]
}
// This keeps your package size small!

Enter fullscreen mode Exit fullscreen mode

3. Two-Factor Auth (REQUIRED for npm)

# npm requires 2FA for publishing. Set it up:
npm profile enable-2fa auth-and-write
# This is mandatory since 2024 — you can't publish without it

Enter fullscreen mode Exit fullscreen mode

4. README Matters

A good README = more downloads

Must include:
- Package name and one-line description
- Installation instructions
- Quick example (copy-paste ready)
- API documentation
- License badge
- Build status badge (if using CI)

Nice to have:
- Performance benchmarks
- Comparison with alternatives
- GIF showing it in action

Enter fullscreen mode Exit fullscreen mode

5. Automate with CI

# .github/workflows/publish.yml
name: Publish
on:
  push:
    tags: ['v*']    # Trigger on version tags
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'https://registry.npmjs.org'
      - run: npm ci
      - run: npm test
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Enter fullscreen mode Exit fullscreen mode

Results After 1 Month

Downloads: ~2,500
Stars: 12
Dependencies: 0
Bundle size: 2KB
No bug reports
1 feature request (custom replacer)

Cost to maintain: ~1 hour/month
Satisfaction: 💯

Enter fullscreen mode Exit fullscreen mode


Have you published an npm package? What was your experience?

Follow @armorbreak for more developer content.