typescript + vitest で npm パッケージの開発 2024 の変更点

更新


#author("2024-09-09T09:14:55+00:00","default:administrator","administrator")
#author("2024-09-10T15:24:38+00:00","default:administrator","administrator")
[[プログラミング/svelte]]

* 2024 年の記事です [#i90fe2a5]

prettier + eslint + typescript + vitest + publint で npm パッケージを作る環境を整えます。

出来上がったコードはこちら:~
https://github.com/osamutake/typescript-vitest-skeleton


*** 目次 [#k93348e0]

#contents

** vite を使わない場合にも jest より vitest を使った方がいいそうです [#qe979dc7]

- jest を typescript で使うとトランスパイルに時間がかかり設定も大変?
-- TypeScript: JestでES Modulesは問題なくテストできるのか?~
https://zenn.dev/suin/scraps/126d311493a9a1 ~
など参照すればできそうだけど

- vitest は簡単に動かせる
-- Vite は使ってないけど Jest を Vitest に移行する~
https://zenn.dev/sa2knight/articles/migrating_vitest_from_jest

** 手順をメモ [#o0f17fdd]

*** プロジェクトフォルダを作成 [#we1015b2]

 LANG:console
 $ mkdir -p mypackage/src/lib
 $ mkdir -p mypackage/src/test
 $ cd mypackage

その下は、

- dist/ トランスパイル済みの publish するコードがここに入る
- src/ ソースコードをここに入れる。
-- lib/ パッケージのコードをここに。index.ts ですべてを export する。
-- test/ テストコードをここに入れる。

とした。

*** package.json を作成 [#m6ba778b]

 LANG: console
 $ npm run prepare

でビルド&テストができるようにした。

- eslint で静的チェックを行う
-- npm run format を忘れていればそれもここではじかれる
- tcs でトランスパイルする
- vitest で *.test.ts や *.spec.ts を実行する
-- vitest はトランスパイル後のコードに対して実行されるので build より前に呼んではいけない
- publint  でパッケージ内容をチェックする

 LANG:json
 {
    "name": "### @yourname/mypackage ###",
    "version": "0.0.1",
    "description": "### description of mypackage ###",
    "exports": "./dist/index.js",
    "type": "module",
    "scripts": {
      "build": "tsc",
      "format": "prettier --write .",
      "lint": "prettier --check . && eslint .",
      "test": "vitest",
      "prepare": "npm run lint && npm run build && vitest run && publint"
    },
    "files": [
      "dist",
      "!dist/**/*.test.*",
      "!dist/**/*.spec.*"
    ],
    "author": "### Your Name ###",
    "license": "### MIT ###"
  }

あーと、ちゃんと勉強すると prepare の使い方間違ってそう。
あーと、少し試しただけで prepare の使い方間違ってそうなことがわかるな。

https://docs.npmjs.com/cli/v10/using-npm/scripts

後でちゃんとこれ見て勉強しないと。

*** 必須パッケージを導入 [#z047e4da]

 LANG:console
 $ npm install -D typescript prettier \
   eslint @types/eslint eslint-config-prettier typescript-eslint \
   vitest globals @types/jest vite-tsconfig-paths

あとは個別設定をいくつか↓

*** .gitignore [#id4eb687]

- /dist を除外
- あとはいつも通り

 node_modules
 /dist
 
 # OS
 .DS_Store
 Thumbs.db
 
 # Env
 .env
 .env.*
 !.env.example
 !.env.test
 
 # Vite
 vite.config.js.timestamp-*
 vite.config.ts.timestamp-*
 
 # WinMerge etc.
 *.bak

*** .prettierignore [#l5d10ab0]

- /dist を prettier が読まないようにする

 # Package Managers
 package-lock.json
 pnpm-lock.yaml
 yarn.lock
 
 /dist

*** .prettierrc [#l3af8acd]

これはお好みで。

 LANG:json
 {
   "useTabs": false,
   "singleQuote": true,
   "trailingComma": "es5",
   "printWidth": 100
 }

*** eslint.config.js [#j650a185]

- globals.jest を入れることで、テストコードで vitest を import しなくて良いようにする
-- そうしておくと vitest でも jest でもどちらでも動くコードにできる

- eslint は dist/ フォルダで動かないようにしておく

 LANG:ts
 import js from '@eslint/js';
 import ts from 'typescript-eslint';
 import prettier from 'eslint-config-prettier';
 import globals from 'globals';
 
 /** @type {import('eslint').Linter.Config[]} */
 export default [
   js.configs.recommended,
   ...ts.configs.recommended,
   prettier,
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
         ...globals.jest,
       },
     },
   },
   {
     languageOptions: {
       parserOptions: {
         parser: ts.parser,
       },
     },
   },
   {
     ignores: ['dist/'],
   },
 ];

*** tsconfig.json [#j0c1007c]

- vitest/globals を指定する
- outDir を /dist に
- *.test.ts や *.spec.ts は無視する (build 時)
- import { ... } from "パッケージ名" でアクセスできるよう alias を張る

 LANG:json
 {
   "compilerOptions": {
     "target": "ES2015",
     "module": "NodeNext",
     "moduleResolution": "NodeNext",
     "types": ["vitest/globals"],
     "declaration": true,
     "sourceMap": true,
     "outDir": "./dist",
     "esModuleInterop": true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "skipLibCheck": true
   },
   "include": ["src/**/*.ts"],
   "exclude": ["**/*.test.ts", "**/*.spec.ts"],
   "alias": {
     "### @yourbane/mypackage ###": "src/lib/index.ts"
   }
 }

*** vitest.config.ts [#l6c72058]

 LANG:ts
 import { defineConfig } from 'vitest/config';
 import tsconfigPaths from 'vite-tsconfig-paths';
 
 export default defineConfig({
   plugins: [tsconfigPaths()],
   test: {
     globals: true,
   },
 });

*** src/lib/mypackage.ts [#a18e133d]

 LANG:ts
 export function hello() {
   return 'Hello!';
 }

*** src/lib/index.ts [#z44164bd]

 LANG:ts
 export { hello } from './mypackage.js';  // this is not '.ts'

*** src/test/mypacage.test.ts [#v59f041c]

 LANG:ts
 import { hello } from '@yourname/mypackage';
 
 describe('mypackage.hello', ()=>{
   it('says hello', ()=>{
     expect(hello()).toBe('Hello!');
   });
 });

** これでビルド&テスト可能 [#f45f8a72]

 LANG:console
 $ npm run prepare

&ref(prepare-result.png);

 LANG:console
 $ ls dist/
 index.d.ts  index.js  index.js.map  mypackage.d.ts  mypackage.js  mypackage.js.map

** パッケージをパブリッシュする [#c5f8be4c]

*** npm publish --access=public [#j8898a4f]

npm へ公開するならそのままこれでOK?

 LANG:console
 $ npm publish --access=public
 > @osamu_takeuchi/mypackage@0.0.1 prepare
 > npm run lint && npm run build && vitest run && publint
 
 > @osamu_takeuchi/mypackage@0.0.1 lint
 > prettier --check . && eslint .
  
 Checking formatting...
 All matched files use Prettier code style!
  
 > @osamu_takeuchi/mypackage@0.0.1 build
 > tsc
 
 RUN  v2.0.5 C:/Users/osamu/Desktop/svelte/lib/mypackage
  
 ✓ src/test/mypackage.test.ts (1)
    ✓ mypackage.hello (1)
      ✓ says hello
 
 Test Files  1 passed (1)
       Tests  1 passed (1)
    Start at  19:10:10
    Duration  1.29s (transform 45ms, setup 0ms, collect 39ms, tests 3ms, environment 0ms, prepare 419ms)
  
 @osamu_takeuchi/mypackage lint results:
 All good!
 
 npm notice
 npm notice 📦  @osamu_takeuchi/mypackage@0.0.1
 npm notice Tarball Contents
 npm notice 40B dist/index.d.ts
 npm notice 73B dist/index.js
 npm notice 147B dist/index.js.map
 npm notice 41B dist/mypackage.d.ts
 npm notice 86B dist/mypackage.js
 npm notice 165B dist/mypackage.js.map
 npm notice 893B package.json
 npm notice Tarball Details
 npm notice name: @osamu_takeuchi/mypackage
 npm notice version: 0.0.1
 npm notice filename: osamu_takeuchi-mypackage-0.0.1.tgz
 npm notice package size: 891 B
 npm notice unpacked size: 1.4 kB
 npm notice shasum: 98564f270bbef1eb6c6625420a35620f7fad0dfd
 npm notice integrity: sha512-4GGxrYblUAfL6[...]ubWYlCHO0tq5Q==
 npm notice total files: 7
 npm notice
 npm error code ENEEDAUTH
 npm error need auth This command requires you to be logged in to https://registry.npmjs.org/
 npm error need auth You need to authorize this machine using `npm adduser`

publish するとその前に自動的に prepare を呼んでくれるので、
先の設定であれば lint + build + test が自動で行われる。

ここでは npm へログインしていなかったのでアクセス権の問題でコケている。

*** npm login [#dd65e256]

自動でブラウザが立ち上がるので、ブラウザ上でログインする。

 LANG: console
 $ npm login
 npm notice Log in on https://registry.npmjs.org/
 Login at:
 https://www.npmjs.com/login?next=/login/cli/1bb69932-2d0f-4d99-9629-83c7becb9870
 Press ENTER to open in the browser...
 
 Logged in on https://registry.npmjs.org/.

*** npm publish --access=public [#s327c54e]

今度はうまく行く。

 LANG:console
 $ npm publish --access=public

&ref(publish-result.png);

できちゃった。

https://www.npmjs.com/package/@osamu_takeuchi/mypackage

** github に上げておく [#f5552607]

うっかりして github 側で作ったリポジトリで main をデフォルトブランチにしてしまったのでちょっとややこしくなってしまった・・・

 LANG: console
 $ git init
 $ git add .
 $ git status
 On branch master
 No commits yet
 Changes to be committed:
   (use "git rm --cached <file>..." to unstage)
         new file:   .gitignore
         new file:   .prettierignore
         new file:   .prettierrc
         new file:   eslint.config.js
         new file:   package-lock.json
         new file:   package.json
         new file:   src/lib/index.ts
         new file:   src/lib/mypackage.ts
         new file:   src/test/mypackage.test.ts
         new file:   tsconfig.json
         new file:   vitest.config.ts
 $ git commit -m "skelton npm package with typescript + vitest"
 $ git push origin --all -f
 Enumerating objects: 16, done.
 Counting objects: 100% (16/16), done.
 Delta compression using up to 12 threads
 Compressing objects: 100% (13/13), done.
 Writing objects: 100% (16/16), 31.84 KiB | 3.18 MiB/s, done.
 Total 16 (delta 0), reused 0 (delta 0), pack-reused 0
 remote: 
 remote: Create a pull request for 'master' on GitHub by visiting:
 remote:      https://github.com/osamutake/typescript-vitest-skeleton/pull/new/master
 remote:
 To https://github.com/osamutake/typescript-vitest-skeleton.git
  * [new branch]      master -> master

その後、github.com 上で master をデフォルトブランチとし、main ブランチを削除した。

https://github.com/osamutake/typescript-vitest-skeleton

** npm version でバージョン番号を増やす [#b96e08a4]

元が 0.0.1 のとき、

 LANG: console
 $ npm version patch  # 0.0.2
 $ npm version minor  # 0.1.0
 $ npm version major  # 1.0.0

のようになる。

* コメント・質問 [#b2108392]

#article_kcaptcha

Counter: 486 (from 2010/06/03), today: 1, yesterday: 3