라이브러리 만드는 과정 기록
라이브러리 만드는 과정 기록
환경 준비
폴더, 파일 생성
md jslib
cd jslib
npm init
md src,test
ni .gitignore
.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
TS
npm i -D typescript
node_module/.bin/tsc --init
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
구현
// src/trim.ts
export default function (value: string) {
return value.length < 1 ? 0 : value.trim();
}
// src/index.ts
import trim from './trim';
export default Object.freeze({ trim });
ESLint
npm i -D eslint
.\
ode_modules\\.bin\\eslint --init
Need to install the following packages:
@eslint/create-config
Ok to proceed? (y) y
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · none
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser, node
√ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
@typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
√ Would you like to install them now with npm? · No / Yes
Installing @typescript-eslint/eslint-plugin@latest, @typescript-eslint/parser@latest
// package.json
"scripts":{
"eslint": "eslint --fix src/**/*.ts"
}
npm run eslint
Jest
npm i -D jest ts-jest @types/jest
// package.json
"scripts": {
"eslint": "eslint --fix src/**/*.ts",
"test": "jest"
},
"jest": {
"moduleFileExtensions": [
"ts",
"js"
],
"transform": {
"^.+\\.ts$": "ts-jest"
},
"globals": {
"ts-jest": {
"tsconfig": "tsconfig.json"
}
},
"testMatch": [
"**/test/**/*.test.ts"
]
},
// test/trim.test.ts
import trim from '../src/trim';
describe('trim test', (): void => {
test('no space', (): void => {
const response = trim('test');
expect(response).toBe('test');
});
test('left space', (): void => {
const response = trim(' test');
expect(response).toBe('test');
});
test('right space', (): void => {
const response = trim('test ');
expect(response).toBe('test');
});
test('both space', (): void => {
const response = trim(' test ');
expect(response).toBe('test');
});
});
npm run test
# > js-lib@1.0.0 test
# > jest
# PASS test/trim.test.ts
# trim test
# √ no space (2 ms)
# √ left space
# √ right space
# √ both space
# Test Suites: 1 passed, 1 total
# Tests: 4 passed, 4 total
# Snapshots: 0 total
# Time: 4.55 s
# Ran all test suites.
rollup
npm i -D rollup rollup-plugin-terser @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript tslib
ni rollup.config.js
- rollup
- rollup-plugin-terser
minify
- @rollup/plugin-babel
Babel 컴파일
- @rollup/plugin-commonjs
CommonJS를 ES6로 변환
- @rollup/plugin-node-resolve
서드파티 라이브러리 읽기 위함
- @rollup/plugin-typescript
rollup,TS seamless 결합
- tslib
TS 헬퍼를 포함한 라이브러리
// rollup.config.js
import { terser as pluginTerser } from 'rollup-plugin-terser';
import pluginTypescript from '@rollup/plugin-typescript';
import pluginCommonjs from '@rollup/plugin-commonjs';
import pluginNodeResolve from '@rollup/plugin-node-resolve';
import { babel as pluginBabel } from '@rollup/plugin-babel';
import * as path from 'path';
import pkg from './package.json';
const moduleName = pkg.name.replace(/^@.*\//, '');
const inputFileName = 'src/index.ts';
const banner = `
/**
* @license
* ${moduleName}.js v${pkg.version}
* Released under the ${pkg.license} License.
*/
`;
export default [
// 브라우저
{
input: inputFileName,
output: [
// uncompressed
{
name: moduleName,
file: pkg.browser,
format: 'iife',
sourcemap: 'inline',
banner,
},
// minified
{
name: moduleName,
file: pkg.browser.replace('.js', '.min.js'),
format: 'iife',
sourcemap: 'inline',
banner,
plugins: [pluginTerser()],
},
],
plugins: [
pluginTypescript(),
pluginCommonjs({
extensions: ['.js', '.ts'],
}),
pluginBabel({
babelHelpers: 'bundled',
configFile: path.resolve(__dirname, '.babelrc.js'),
}),
pluginNodeResolve({
browser: true,
}),
],
},
// ES Module
{
input: inputFileName,
output: [
{
file: pkg.module,
format: 'es',
sourcemap: 'inline',
banner,
exports: 'named',
},
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.devDependencies || {}),
],
plugins: [
pluginTypescript(),
pluginCommonjs({
extensions: ['.js', '.ts'],
}),
pluginBabel({
babelHelpers: 'bundled',
configFile: path.resolve(__dirname, '.babelrc.js'),
}),
pluginNodeResolve({
browser: false,
}),
],
},
// CommonJS
{
input: inputFileName,
output: [
{
file: pkg.main,
format: 'cjs',
sourcemap: 'inline',
banner,
exports: 'default',
},
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.devDependencies || {}),
],
plugins: [
pluginTypescript(),
pluginCommonjs({
extensions: ['.js', '.ts'],
}),
pluginBabel({
babelHelpers: 'bundled',
configFile: path.resolve(__dirname, '.babelrc.js'),
}),
pluginNodeResolve({
browser: false,
}),
],
},
];
// package.json
"main": "dist/lib.cjs.js", // Node.js
"module": "dist/lib.es.js", // ES Module
"browser": "dist/lib.js", // 브라우저
"scripts": {
"build": "rollup -c",
"lint": "eslint --fix 'src/**/*.ts'",
"test": "jest"
},
Babel
npm i -D @babel/core @babel/preset-env
ni .babelrc.js
- @babel/core
- @babel/preset-env
실행 환경에 따라 변환
// .babelrc.js
module.exports = {
presets: [['@babel/preset-env']],
};
npm run build
# > jslib@1.0.0 build
# > rollup -c
# src/index.ts → dist/trim-lib.js, dist/trim-lib.min.js...
# created dist/trim-lib.js, dist/trim-lib.min.js in 3.1s
# src/index.ts → dist/trim-lib.es.js...
# created dist/trim-lib.es.js in 2s
# src/index.ts → dist/trim-lib.cjs.js...
# created dist/trim-lib.cjs.js in 1.4s
실행해보기
폴더 벗어나서 폴더 만들기
md jslib-test
cd jslib-test
npm init -y
npm i ../jslib
ni test.js
const { trim } = require('jslib');
const value = trim(' move? ');
console.log(value); // "move?"
CI
https://circleci.com/ 접속해서 해당 Git 저장소로 가입 & 로그인
프로젝트에서 해당하는 프로젝트
Set Up Project
선택해당하는 사항 선택하며 넘어가기
저장소로 가서
Compare & pull request
,Merge
하기로컬 저장소
fetch
혹은pull
yml 수정하기
- sample: # This is the name of the workflow,
+ run-test: # This is the name of the workflow,
push
branch
하나 만들어서 틀린 테스트 작성
// test/trim.test.ts
// ...
test('wrong test', (): void => {
const response = trim(' test ');
expect(response).toBe(' test');
});
push
배포하기
https://www.npmjs.com/ 접속해서 가입 & 로그인하기
package.json
의name
으로 배포되니 중복 확인
npm info jslib_trim
npm login
npm publish