---
title: '初めての npm パッケージ公開が思ったより簡単だった'
description: '自作のエディタライブラリをnpmに公開するまでの流れとか、かかった時間とか。'
pubDate: '2026-03-23T00:00:00.000Z'
tags: ['Programming', 'TypeScript']
source: 'https://qwqb.net/blog/first-npm-publish/'
---

少し前に qwq-editor というリッチテキストエディタを作っていました。このサイトの記事作成に組み込む想定で、実装や動作確認もこのサイトのリポジトリ上で進めていた形です。

開発中はローカルのパッケージを `link:` で直接参照していたのですが、npm への公開はそのうちやるつもりでずっと後回しにしていました。手元では普通に動いていたので、特に困ることもなかったというのが正直なところです。

そんな中、[マダめもくん](https://memo.qwqb.net/) のリリースに合わせて Projects ページの追加作業をしていたところ、Cloudflare Pages のビルドでこんなエラーが出ました。

```
ERR_PNPM_FETCH_404  GET https://registry.npmjs.org/@qwq-net%2Fcore: Not Found - 404
```

`package.json` を見てみると、こうなっています。

```json
{
  "@qwq-net/core": "link:../qwq-editor/packages/core",
  "@qwq-net/instant": "link:../qwq-editor/packages/instant"
}
```

`link:` はローカルのファイルパスを直接参照する仕組みなので、CI やホスティング環境にはそのパスが存在せず、当然 404 になります。すっかり忘れていた npm 公開をやらないと先に進めない状況でした。

そのような訳で、初めての npm パッケージ公開をやってみることにしました。

## npm アカウントと Organization の作成

`@qwq-net/core` のようなスコープ付きパッケージを公開するには、npm 上に Organization が必要です。

- [npmjs.com](https://www.npmjs.com/signup) でアカウントを作成
  - とりあえずユーザー名は `qwqb` で作成
- ログイン後、Organization `@qwq-net` を作成
- ターミナルで `npm login` を実行
  - 認証用URLが出たら、そちらへアクセスして認証を実施

アカウントは無料で作れます。自分はそもそも npm のアカウントすら持っていなかったので、ささっと作成してしまいました。特に詰まるようなところはないと思います。

## 公開前の準備

npm に公開するにあたって、いくつか準備が必要でした。

まず LICENSE と README.md を各パッケージに配置します。LICENSE がないと利用者がライセンスを判断できません。README.md は npm のパッケージページにそのまま表示されるので、最低限の説明は書いておきたいところです。

次に、各パッケージの `package.json` に公開用のフィールドを追加します。

```json
{
  "publishConfig": { "access": "public" },
  "files": ["dist", "README.md"],
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts"
}
```

- `publishConfig`: スコープ付きパッケージはデフォルトで private 扱いになるので、`"access": "public"` を明示する必要があります
- `files`: npm に含めるファイルを指定します。`dist/` だけ含めればソースコードは配布されません
- `exports`: ESM / CJS それぞれのエントリポイントと型定義を指定します。`main` と `module` も併記しておくと、古いバンドラとの互換性が取れます

### ビルド設定

qwq-editor は pnpm ワークスペースのモノレポ構成で、ビルドには [tsup](https://tsup.egoist.dev/) を使っています。tsup は esbuild ベースのバンドラで、設定がシンプルなのが特徴です。

自分の場合はすでに設定済みでしたが、以下のような `tsup.config.ts` があると ESM / CJS のデュアル出力と型定義の生成をまとめてやってくれます。

```ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  sourcemap: true,
  clean: true,
});
```

これで `dist/` 配下に `index.js`（ESM）、`index.cjs`（CJS）、`index.d.ts`（型定義）が出力されます。さきほど `files` で `dist/` を指定したのは、この出力を npm に含めるためです。

## ビルドと公開

準備ができたので、早速公開してみます。

```bash
pnpm build
```

モノレポ全体をビルドした後、publish を実行します。

```bash
pnpm -r publish --access public
```

初回は各パッケージごとに OTP（ワンタイムパスワード）認証を求められ、ブラウザでの認証が3回必要でした。パッケージが3つあるので3回です。

### レジストリへの反映ラグ

公開コマンド自体は成功して `+ @qwq-net/core@0.0.1` のような表示が出たものの、直後に `npm view` を叩いても 404 が返ってきました。

```
npm error 404 Not Found - GET https://registry.npmjs.org/@qwq-net%2Fcore - Not found
```

初回は特にレジストリへの伝播に時間がかかるようです。適当に時間を潰しながら待ちましょう。

公開したパッケージは [npm のプロフィールページ](https://www.npmjs.com/~qwqb) から確認できます。

## 公開後の対応

ポートフォリオ側の `package.json` を `link:` からバージョン指定に変更しました。

```json
{
  "@qwq-net/core": "^0.0.1",
  "@qwq-net/instant": "^0.0.1"
}
```

ついでにパッケージマネージャ周りも整理しています。

- `package-lock.json` の削除: pnpm を使っているのに npm の lockfile が残っていた
- `pnpm-workspace.yaml` の修正: pnpm v10 で必須になった `packages` フィールドが欠けていた

どちらも `link:` で手元では動いていたので気づかなかったものです。

## まとめ

Cloudflare Pages のビルドエラーをきっかけに、自作パッケージの npm 公開を初めてやってみました。スコープ付きパッケージは Organization と `access: public` の指定が要ること、初回はレジストリへの反映にラグがあること、`link:` のままだと CI で 404 になることあたりは、初めてだと地味に詰まるポイントでした。とはいえ流れ自体はシンプルで、思っていたより気軽に公開できるので、何かパッケージにできそうなものがあれば積極的にやってみても良いかもしれませんね。

そういえばバージョンは `0.0.1` で適当に公開してしまいましたが、major / minor / patch ってどういう基準で上げていくのが正しいのか……。
