Skip to main content

Typescript Lib Publish Guide

Goal : 타입스크립트 기반의 라이브러리를 배포합니다.

Background

error 객체는 typescript에서 unknown으로 처리된다.

  • 타입 가드를 통해서 특정 타입으로 좁혀야 한다.
  • 타입가드 함수를 통해서, 특정 타입에 대한 필드검사 등을 마치면 'asserts e is AxiosError' 같은 syntax를 통해서 Type을 명시한다.

init

yarn init -y
yarn add axios
yarn add typescript -D
npx tsc --init

package.json

{
"name": "@dosimpact/axios-error",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json",
"deploy": "yarn build && yarn publish --access=public"
},
"devDependencies": {
"typescript": "^5.5.3"
},
"dependencies": {
"axios": "^1.7.2"
},
"private": false,
"types": "dist/index.d.ts",
"main": "dist/index.js",
"module": "dist/esm"
}
  • 패키지 이름에는 @을 이용해서 패키지에 대한 네임스페이스를 걸어줍니다.

  • 패키지 버전은 중요합니다. 메이저, 마이너, 패치 버전에 따른 배포를 할 수 있습니다.

  • scripts부분에서는 빌드는 cjs, esm 모듈 모두 emit 할 수 있습니다.

  • deploy는 npm registry로 배포하며 access=public 옵션을 통해서 공개 저장소에 배포합니다.

  • private 설정을 false로 변경해서 배포합니다.

  • types 필드는 타입정의 파일 경로를 지정합니다.

  • main 필드는 cjs entry point 입니다.

  • module 필드는 esm entry point 입니다.

single entry, multi entry

  • single : types, main, module
  • multi : exports, require, import
// case1. 진입점이 1개인 경우  
"types": "dist/index.d.ts",
"main": "dist/index.js",
"module": "dist/esm"

// case2. 진입점이 2개 이상인 경우
"exports": {
".": {
"require": {
"types": "./index.d.ts",
"default": "./index.js"
},
"import": {
"types": "./esm/index.d.ts",
"default": "./esm/index.js"
}
},
"./feature": {
"require": {
"types": "./index.d.ts",
"default": "./index.js"
},
"import": {
"types": "./index.d.mts",
"default": "./index.mjs"
}
}
}

tsconfig.json, tsconfig.esm.json

---tsconfig.json
{
"include": ["src"],
"exclude": ["**/__test___/**"],
"compilerOptions": {
"target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["DOM","DOM.Iterable","ESNext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"jsx": "react", /* Specify what JSX code is generated. */
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
"module": "NodeNext", /* Specify what module code is generated. */
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"sourceMap": false, /* Create source map files for emitted JavaScript files. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"noEmit": false, /* Disable emitting files from a compilation. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
"noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

---tsconfig.esm.json
{
"extends":"./tsconfig.json",
"include": ["src"],
"exclude": ["**/__test___/**"],
"compilerOptions": {
"outDir": "./dist/esm",
"module": "ESNext", /* Skip type checking all .d.ts files. */
"moduleResolution": "Bundler"
}
}

.npmignore

src

src

// src/index.ts
export * from "./assertAxiosError";
---
// src/assertAxiosError
import type { AxiosError } from "axios";

export function assertAxiosError(e: unknown): asserts e is AxiosError {
if (typeof e === "object" && e != null && "isAxiosError" in e) {
return;
}
throw e;
}

Background Knowledge

Prettier & eslint 설정

Prettier 설정

Prettier는 코드 포맷터로, 코드를 일관된 스타일로 자동으로 정리해줍니다.

  • Install VS Code Prettier Extension
# 0.설치
yarn add prettier -D

# 1.설정 파일을 루트에 추가
# .prettierrc
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid"
}

---
# 2.포멧팅 제외 파일(선택사항)
# .prettierignore
node_modules
dist
tsconfig.json
tsconfig.esm.json

---
# 3.package.json 스크립트 추가
"prettier": "prettier --write ./src"

ESLint 설정

ESLint는 JavaScript 코드의 문제를 찾아내고 스타일을 강제하는 도구입니다.

  • Install VS Code Eslint Extension and restart VS Code
# 1. ESLint 설치
yarn add eslint -D


# 2. ESLint 초기화
# ESLint를 초기화하여 기본 설정 파일을 생성합니다. 다음 명령어를 실행하세요:
npx eslint --init
# eslint, globals, @eslint/js, typescript-eslint 가 추가됨
# eslint.config.mjs 설정파일이 생성됨.

# 3. ESLint 실행 스크립트 추가
"scripts": {
"lint": "eslint --fix ./src"
},

Prettier와 ESLint 통합

  • Prettier와 ESLint를 함께 사용하면 코드 스타일과 코드 품질을 동시에 유지할 수 있습니다.
  • 이를 위해 eslint-config-prettiereslint-plugin-prettier 패키지를 설치합니다:
  • eslint-config-prettier를 사용하여 충돌하는 ESLint 규칙을 비활성화하고 Prettier가 형식 지정을 처리하도록 합니다.
  • eslint-plugin-prettier를 사용하여 Prettier를 ESLint 규칙으로 실행하고 형식 지정 문제를 ESLint 출력 내에서 보고합니다.
  • 두 가지를 결합하여 Prettier가 형식 지정을 처리하고 ESLint가 코드 품질에 집중할 수 있도록 하여 충돌 없이 원활한 통합을 보장합니다.
yarn add eslint-config-prettier eslint-plugin-prettier -D

---
# 그리고 `.eslintrc.json` 파일을 다음과 같이 수정합니다:

import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

export default [
{ files: ['**/*.js'], languageOptions: { sourceType: 'script' } },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier, // 추가
eslintPluginPrettierRecommended, // 추가
];

ESLint 세부 규칙 조정하기

  • 규칙설정배열은 후속 구성 객체가 앞서 정의된 설정을 덮어쓸 수 있다.

예) let 선언 후 변수에 재할당이 없다면 오류 발생, 이는 autofix 가능.

import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

export default [
{ files: ['**/*.js'], languageOptions: { sourceType: 'script' } },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier,
eslintPluginPrettierRecommended,
{
files: ['**/*.{js,mjs,cjs,ts}'],
rules: {
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
];

모든 규칙

https://eslint.org/docs/latest/rules/