https://www.totaltypescript.com/how-to-create-an-npm-package#39-set-up-a-ci-script
ν΄λΉ κΈμ λ°νμΌλ‘ NPM ν¨ν€μ§ μ€μ κ³Όμ μ κ°λ¨ν μμ½ν΄ 보μμ΅λλ€.
https://github.com/mattpocock/tt-package-demo
1. Git μ€μ
ν¨ν€μ§λ₯Ό λ²μ κ΄λ¦¬νκΈ° μν΄ Gitμ μ¬μ©ν©λλ€.
Git μ΄κΈ°ν
git init
.gitignore νμΌ μμ±
node_modules
μ»€λ° + 리ν¬μ§ν 리 μμ± + νΈμ
gh repo create tt-package-demo --source=. --public
git push --set-upstream origin main
2. package.json μ€μ
package.json νμΌμ ν¨ν€μ§μ λ©νλ°μ΄ν°λ₯Ό μ μνλ νμΌλ‘, ν¨ν€μ§ μ΄λ¦, λ²μ , λΌμ΄μ μ€, README λ±μ μ 보λ₯Ό ν¬ν¨λ©λλ€.
{
"name": "tt-package-demo",
"version": "1.0.0",
"description": "A demo package for Total TypeScript",
"keywords": ["demo", "typescript"],
"license": "MIT",
"author": "Matt Pocock <team@totaltypescript.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/mattpocock/tt-package-demo.git"
},
"files": ["dist"],
"type": "module"
}
- name: ν¨ν€μ§μ κ³ μ ν μ΄λ¦μ λλ€. NPM λ μ§μ€νΈλ¦¬μμ μ μΌν΄μΌ νλ©°, μ‘°μ§ λ²μλ‘ ν¨ν€μ§λ₯Ό μμ±νλ €λ©΄ @your-org/package-nameκ³Ό κ°μ ννλ₯Ό μ¬μ©ν μ μμ΅λλ€.
- version: ν¨ν€μ§μ λ²μ μ λλ€. SemVer κ·μΉμ λ°λΌ κ΄λ¦¬νλ©°, λ°°ν¬ν λλ§λ€ μ μ ν μ¦κ°μμΌμΌ ν©λλ€.
- files: NPMμ λ°°ν¬ν λ ν¬ν¨λ νμΌ λͺ©λ‘μ λλ€. μ¬κΈ°μλ λΉλλ μ½λκ° ν¬ν¨λ dist ν΄λλ§ μ§μ νμ΅λλ€.
- type: ECMAScript λͺ¨λμ μ¬μ©νλ €λ©΄ "module"λ‘ μ€μ ν©λλ€.
λΌμ΄μ μ€ μ€μ
{
"license": "MIT"
}
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
...
NPM ν¨ν€μ§ λ μ§μ€νΈλ¦¬μμ ν¨ν€μ§μ μ€λͺ μ 보μ¬μ£Όλ README.md νμΌλ ν¬ν¨ν΄μΌ ν©λλ€.
# tt-package-demo
A demo package for Total TypeScript.
3. TypeScript μ€μ
TypeScriptλ₯Ό μ€μΉνκ³ tsconfig.json νμΌμ μ€μ ν©λλ€.
npm install --save-dev typescript
tsconfig.json μμ± μ μ΅μ ES λͺ¨λμ νκΉμΌλ‘ νλ©°, λΉλ κ²°κ³Όλ₯Ό dist ν΄λμ μΆλ ₯νλλ‘ μ€μ ν©λλ€.
{
"compilerOptions": {
"target": "es2022",
"module": "NodeNext",
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"strict": true
}
}
μμ€ νμΌ μμ± λ° λΉλ μ€ν¬λ¦½νΈ μ€μ ν©λλ€.
export const add = (a: number, b: number) => a + b;
{
"scripts": {
"build": "tsc"
}
}
.gitignore νμΌμ dist ν΄λλ₯Ό μΆκ°νμ¬, μ»΄νμΌλ μ½λκ° Git 리ν¬μ§ν 리μ ν¬ν¨λμ§ μλλ‘ ν©λλ€.
package.jsonμ CI μ€ν¬λ¦½νΈλ₯Ό μΆκ°νμ¬, CIμμ νμν μμ μ λΉ λ₯΄κ² μ€νν μ μλλ‘ μ€μ ν©λλ€.
{
"scripts": {
"ci": "npm run build"
}
}
4. Prettier μ€μ
Prettierλ μ½λλ₯Ό μΌκ΄λ μ€νμΌλ‘ μλ ν¬λ§·ν ν΄μ£Όλ λꡬμ λλ€.
npm install --save-dev prettier
.prettierrc νμΌμ μμ±νμ¬ κΈ°λ³Έ μ€μ μ μ μν©λλ€.
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2
}
package.jsonμ Prettier ν¬λ§·ν μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€.
{
"scripts": {
"format": "prettier --write ."
}
}
μλ λͺ λ Ήμ΄λ‘ νλ‘μ νΈ λ΄ λͺ¨λ νμΌμ ν¬λ§·ν ν μ μμ΅λλ€.
npm run format
package.jsonμ ν¬λ§·μ΄ μ λλ‘ μ μ©λμλμ§ νμΈν μ μλ μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€.
{
"scripts": {
"check-format": "prettier --check ."
}
}
ci μ€ν¬λ¦½νΈμ ν¬λ§· 체ν¬λ₯Ό μΆκ°νμ¬ CI κ³Όμ μμλ μ½λ μ€νμΌμ΄ μΌκ΄λκ² μ μ§λλλ‘ ν©λλ€.
{
"scripts": {
"ci": "npm run build && npm run check-format"
}
}
5. @arethetypeswrong/cliλ‘ exports κ²μ¬
@arethetypeswrong/cliλ₯Ό μ€μΉνκ³ , check-exports μ€ν¬λ¦½νΈλ₯Ό μ€μ νκ³ μ€ννλ©°, main νλλ₯Ό μΆκ°ν ν λ€μ check-exportsλ₯Ό μ€ννκ³ , λ§μ§λ§μΌλ‘ ci μ€ν¬λ¦½νΈμ μ΄λ₯Ό μΆκ°ν ν μ€νν©λλ€.
( ++ @arethetypeswrong/cliλ ν¨ν€μ§μ exportsκ° μ¬λ°λ₯΄κ² μ€μ λμλμ§ νμΈνλ λꡬμ΄λ©°, μ΄λ¬ν μ€μ μ μ€μνκΈ° μ¬μ°λ©°, μλͺ»λ κ²½μ° ν¨ν€μ§λ₯Ό μ¬μ©νλ μ¬λλ€μκ² λ¬Έμ κ° λ°μν μ μλ€κ³ ν¨!! )
μΌλ¨ μ€μΉ ν package.json μ μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€.
npm install --save-dev @arethetypeswrong/cli
{
"scripts": {
"check-exports": "attw --pack ."
}
}
check-exports μ€ν¬λ¦½νΈ μ€νν©λλ€.
npm run check-exports
βββββββββββββββββββββ¬βββββββββββββββββββββββ
β β "tt-package-demo" β
βββββββββββββββββββββΌβββββββββββββββββββββββ€
β node10 β π Resolution failed β
βββββββββββββββββββββΌβββββββββββββββββββββββ€
β node16 (from CJS) β π Resolution failed β
βββββββββββββββββββββΌβββββββββββββββββββββββ€
β node16 (from ESM) β π Resolution failed β
βββββββββββββββββββββΌβββββββββββββββββββββββ€
β bundler β π Resolution failed β
βββββββββββββββββββββ΄βββββββββββββββββββββββ
μ κ²°κ³Όλ νμ¬ ν¨ν€μ§κ° μ΄λ€ λ²μ μ Nodeλ λ²λ€λ¬μμλ μ¬μ©λ μ μλ€λ κ²μ μλ―Έν©λλ€.
λ€μ μμ νμ¬ package.jsonμ main νλλ₯Ό μΆκ°νμ¬, Node.jsκ° ν¨ν€μ§μ μ§μ μ μ μ°Ύμ μ μλλ‘ ν©λλ€.
{
"main": "dist/index.js"
}
μ΄μ λ€μ exports μ€μ μ νμΈν©λλ€.
npm run check-exports
βββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β β "tt-package-demo" β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββ€
β node10 β π’ β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββ€
β node16 (from CJS) β β οΈ ESM (dynamic import only) β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββ€
β node16 (from ESM) β π’ (ESM) β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββ€
β bundler β π’ β
βββββββββββββββββββββ΄βββββββββββββββββββββββββββββββ
μ΄ λ©μμ§λ ν¨ν€μ§κ° ESM(ECMAScript Module) νμμμ νΈνλλ€λ κ²μ μλ―Έν©λλ€. CJS(CommonJS)λ₯Ό μ¬μ©νλ μ¬λλ€μ λμ importλ‘ ν¨ν€μ§λ₯Ό λΆλ¬μμΌ ν©λλ€.
CJS(CommonJS)λ₯Ό μ§μνμ§ μμΌλ €λ©΄ check-exports μ€ν¬λ¦½νΈλ₯Ό λ€μκ³Ό κ°μ΄ μμ ν©λλ€.
{
"scripts": {
"check-exports": "attw --pack . --ignore-rules=cjs-resolves-to-esm"
}
}
βββββββββββββββββββββ¬ββββββββββββββββββββ
β β "tt-package-demo" β
βββββββββββββββββββββΌββββββββββββββββββββ€
β node10 β π’ β
βββββββββββββββββββββΌββββββββββββββββββββ€
β node16 (from CJS) β π’ (ESM) β
βββββββββββββββββββββΌββββββββββββββββββββ€
β node16 (from ESM) β π’ (ESM) β
βββββββββββββββββββββΌββββββββββββββββββββ€
β bundler β π’ β
βββββββββββββββββββββ΄ββββββββββββββββββββ
ci μ€ν¬λ¦½νΈμ check-exportsλ₯Ό μΆκ°νμ¬, μ§μμ ν΅ν©(CI) νλ‘μΈμ€μμ μ΄ κ²μ¬λ₯Ό μλμΌλ‘ μ€ννλλ‘ μ€μ ν©λλ€.
{
"scripts": {
"ci": "npm run build && npm run check-format && npm run check-exports"
}
}
μ΄ μ€ν¬λ¦½νΈλ ν¨ν€μ§ λΉλ, μ½λ ν¬λ§· νμΈ, exports κ²μ¬λ₯Ό μμ°¨μ μΌλ‘ μ€ννκ² λ©λλ€.
6. tsupμ μ¬μ©ν΄ CJSμ ESM λμ λ°°ν¬
tsupμ ES λͺ¨λκ³Ό CommonJSλ₯Ό λμμ λ°°ν¬ν μ μκ² ν΄μ£Όλ λꡬμ λλ€. ν΄λΉ κ°μ΄λλ κΈ°λ³Έμ μΌλ‘ ESMλ§ λ°°ν¬νλλ‘ μ€μ λμ΄ μμ§λ§, CJSλ μ§μνλ €λ©΄ tsupμ μ¬μ©ν μ μμ΅λλ€.
μΌλ¨ μ€μΉνκ³ , μ€μ νμΌμ μμ±ν©λλ€.
npm install --save-dev tsup
import { defineConfig } from "tsup";
export default defineConfig({
entryPoints: ["src/index.ts"],
// esm λͺ¨λκ³Ό commonJS λΉλλλλ‘ μ€μ
format: ["cjs", "esm"],
dts: true,
outDir: "dist",
clean: true,
});
package.jsonμ λΉλ μ€ν¬λ¦½νΈλ₯Ό tsc λμ tsupμ μ¬μ©νλλ‘ λ³κ²½ν©λλ€.
{
"scripts": {
"build": "tsup"
}
}
package.json μ exports νλλ₯Ό μΆκ°νμ¬ CJSμ ESM λͺ¨λμ κ°κ° μ§μν©λλ€.
{
"exports": {
"./package.json": "./package.json",
".": {
"import": "./dist/index.js",
"default": "./dist/index.cjs"
}
}
}
exports κ²μ¬λ₯Ό λ€μ μ€ννμ¬ λͺ¨λ κ²μ΄ μ μμ μΌλ‘ μ€μ λμλμ§ νμΈν©λλ€.
npm run check-exports
tsupμ TypeScript μ€λ₯λ₯Ό 체ν¬νμ§ μκ³ JavaScriptλ‘λ§ λ³νν©λλ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ TypeScriptλ₯Ό λ¦°ν°λ‘ μ¬μ©ν μ μλλ‘ μ€μ ν©λλ€.
tsconfig.json μ noEmit νλλ₯Ό μΆκ°ν©λλ€.
{
"compilerOptions": {
"noEmit": true
}
}
tsconfig.jsonμμ tsupμ΄ μ§μνλ κΈ°λ₯λ€μΈ outDir, rootDir, sourceMap, declaration, declarationMap νλλ₯Ό μ κ±°ν©λλ€.
tsconfig.jsonμμ moduleμ Preserveλ‘ λ³κ²½νμ¬ .js νμ₯μ μμ΄λ μ¬μ©ν μ μκ² ν©λλ€.
{
"compilerOptions": {
"module": "Preserve"
}
}
package.jsonμ TypeScriptλ₯Ό λ¦°ν°λ‘ μ¬μ©νλ μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€.
{
"scripts": {
"lint": "tsc"
}
}
ci μ€ν¬λ¦½νΈμ lintλ₯Ό μΆκ°νμ¬ CI κ³Όμ μμ TypeScript μ€λ₯κ° μλμ§ νμΈν©λλ€.
{
"scripts": {
"ci": "npm run build && npm run check-format && npm run check-exports && npm run lint"
}
}
7. Vitestλ‘ ν μ€νΈ μ€μ
Vitestλ ESMκ³Ό TypeScriptλ₯Ό μ§μνλ ν μ€νΈ λꡬμ λλ€.
μ€μΉ + ν μ€νΈ νμΌ μμ± + ν μ€νΈ μ€ν¬λ¦½νΈ μΆκ°
npm install --save-dev vitest
import { add } from "./utils.js";
import { test, expect } from "vitest";
test("add", () => {
expect(add(1, 2)).toBe(3);
});
{
"scripts": {
"test": "vitest run"
}
}
κ°λ° λμ€ ν μ€νΈλ₯Ό μλμΌλ‘ μ€ννλλ‘ dev μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€.
{
"scripts": {
"dev": "vitest"
}
}
ci μ€ν¬λ¦½νΈμ testλ₯Ό μΆκ°νμ¬, CI κ³Όμ μμ ν μ€νΈκ° νμ μ€νλλλ‘ μ€μ ν©λλ€.
{
"scripts": {
"ci": "npm run build && npm run check-format && npm run check-exports && npm run lint && npm run test"
}
}
8. GitHub Actionsλ‘ CI μ€μ
GitHub Actionsλ₯Ό μ¬μ©νλ©΄ μ½λλ₯Ό νΈμν λλ§λ€ μλμΌλ‘ CI(Continuous Integration) νλ‘μΈμ€λ₯Ό μ€νν μ μμ΅λλ€.
.github/workflows/ci.yml νμΌμ μμ±νμ¬ κΈ°λ³Έ CI νλ‘μΈμ€λ₯Ό μ€μ ν©λλ€.
name: CI
on:
pull_request:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: npm install
- name: Run CI
run: npm run ci
- name: μν¬νλ‘μ°μ μ΄λ¦μ λλ€.
- on: μν¬νλ‘μ°κ° μ€νλ 쑰건μ μ μν©λλ€. μ¬κΈ°μλ ν 리νμ€νΈ λ° main λΈλμΉλ‘μ νΈμ μ μ€νλ©λλ€.
- concurrency: λμΌν μν¬νλ‘μ°κ° μ€λ³΅ μ€νλλ κ²μ λ°©μ§ν©λλ€.
- jobs: μ€νν μμ
λ€μ μ μν©λλ€.(ci μμ
λ§ μμ΅λλ€.)
- actions/checkout@v4: μ½λλ² μ΄μ€λ₯Ό 체ν¬μμν©λλ€.
- actions/setup-node@v4: Node.js νκ²½μ μ€μ ν©λλ€.
- npm install: νλ‘μ νΈμ μμ‘΄μ±μ μ€μΉν©λλ€.
- npm run ci: CI μ€ν¬λ¦½νΈλ₯Ό μ€νν©λλ€.
μ½λλ₯Ό GitHubμ νΈμν ν, GitHub 리ν¬μ§ν 리μ Actions νμμ μν¬νλ‘μ°κ° μ±κ³΅μ μΌλ‘ μ€νλμλμ§ νμΈν©λλ€.
9. Changesetsλ‘ λ²μ κ΄λ¦¬ λ° λ°°ν¬
Changesetsλ ν¨ν€μ§μ λ²μ κ΄λ¦¬ λ° λ°°ν¬λ₯Ό μλννλ λꡬμ λλ€. λ³κ²½ μ¬νμ μ½κ² κ·Έλ£Ήννμ¬, NPMμ λ°°ν¬ν μ μμ΅λλ€.
μ€μΉ + μ΄κΈ°ν λ° κΈ°λ³Έ μ€μ νμΌ μμ±ν©λλ€.
npm install --save-dev @changesets/cli
npx changeset init
.changeset/config.json νμΌμ μ΄κ³ , access νλλ₯Ό publicμΌλ‘ μ€μ ν©λλ€.
μ΄λ κ² νλ©΄ Changesetsκ° NPMμ ν¨ν€μ§λ₯Ό λ°°ν¬ν μ μμ΅λλ€.
{
"access": "public"
}
.changeset/config.json νμΌμμ commit νλλ₯Ό trueλ‘ μ€μ ν©λλ€.
( -> λ²μ κ΄λ¦¬ ν λ³κ²½ μ¬νμ΄ μλμΌλ‘ 컀λ°λ©λλ€.)
{
"commit": true
}
package.jsonμ local-release μ€ν¬λ¦½νΈ(CI κ³Όμ μ μ€ννκ³ , ν¨ν€μ§λ₯Ό NPMμ λ°°ν¬)λ₯Ό μΆκ°ν©λλ€.
ν΄λΉ μ€ν¬λ¦½νΈλ λ‘컬μμ μλ‘μ΄ λ²μ μ λ°°ν¬ν λ μ¬μ©λ©λλ€.
{
"scripts": {
"local-release": "changeset version && changeset publish"
}
}
package.jsonμ prepublishOnly μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν©λλ€. μ΄ μ€ν¬λ¦½νΈλ NPMμ λ°°ν¬νκΈ° μ μ CI κ³Όμ μ μλμΌλ‘ μ€νν©λλ€.
μ΄ μ€μ μ μ¬μ©μκ° μ€μλ‘ npm publish λͺ λ Ήμ΄λ₯Ό μ€ννμ λλ, 미리 CIλ₯Ό ν΅ν΄ κ²μ¬λ₯Ό μννμ¬ μ€λ₯λ₯Ό λ°©μ§ν μ μμ΅λλ€.
{
"scripts": {
"prepublishOnly": "npm run ci"
}
}
Changesetμ μΆκ°νμ¬ λ³κ²½ μ¬νμ κΈ°λ‘ν©λλ€.
npx changeset
μ΄ λͺ λ Ήμ΄λ₯Ό μ€ννλ©΄ μΈν°λν°λΈ ν둬ννΈκ° μ΄λ¦¬κ³ , μ¬κΈ°μ λ³κ²½ μ¬νμ κ·Έλ£Ήννκ³ ν΄λΉ 릴리μ€λ₯Ό ν¨μΉ λ²μ μΌλ‘ μ§μ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ "Initial release"λΌλ μ€λͺ μ μΆκ°ν μ μμ΅λλ€. -> μ΄ κ³Όμ μμ .changeset ν΄λμ μλ‘μ΄ Changeset νμΌμ΄ μμ±λ©λλ€.
λ³κ²½ μ¬νμ 컀λ°νμ¬ μ μ₯μμ λ°μν©λλ€.
git add .
git commit -m "Prepare for initial release"
NPMμ ν¨ν€μ§λ₯Ό λ°°ν¬ν©λλ€.
npm run local-release
NPM λ μ§μ€νΈλ¦¬μμ ν¨ν€μ§κ° μ±κ³΅μ μΌλ‘ λ°°ν¬λμλμ§ νμΈν©λλ€.
https://npmjs.com/package/<your-package-name>
'π WIL > π Etc' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
νλ‘ νΈμλμκ² λ°°ν¬νλ«νΌμ΄λ - 2024 λΉκ·Ό ν ν¬ λ°μ (1) | 2024.11.24 |
---|---|
π€¨ lingui (1) | 2024.11.03 |
WildCard Domain..? (0) | 2024.10.13 |
π¦ λ§μ΄κ·Έλ μ΄μ ν΄ Grit (1) (1) | 2024.09.08 |
λ²νΌ ν΄λ¦νλ©΄ ν μ€νΈ νλ μΆκ°λλλ‘ ν΄μ£ΌμΈμ. (with ProseMirror) (1) | 2024.09.01 |