[React]yarn berry, vite, React TS 환경에서 라이브러리 포함한 모노 레포 만들기

bluejoy·2023년 4월 29일
2

React

목록 보기
12/19
post-thumbnail

개요

레퍼런스

정말 감사합니다.

목적

  • 예전부터 몇번 시도했지만 실패해서 더 집념이 생긴듯…

코드

여기서 사용된 모든 코드는 https://github.com/bluejoyq/package-test 에서 확인하실 수 있습니다.

추가

  • 이 코드가 틀린 점이나 개선 점이 있다면 지적해주시면 정말 감사하게 받아들이겠습니다!!!

모노 레포 세팅하기

루트

폴더 생성 및 패키지 초기화

mkdir package-test
cd package-test
# yarn 2 선택(yarn version 3, yarn2, yarn berry는 다 같은 표현)
yarn set version berry
# 워크 스페이스 초기화
yarn init -w

yarnrc.yml 수정

  • 설정하지 않으면 zero-install을 누릴 수 있으나 호환성 에러가 발생해 설정해준다.
yarnPath: .yarn/releases/yarn-3.5.0.cjs
nodeLinker: node-modules #추가

루트 package.json

/package.json

  • 위 명령어를 실행하면 다음과 같이 세팅된다.
    • packageManager 버젼이 3.5
    • private private이 true인 패키지만 워크스페이스가 가능, publish를 막기 위해
    • workspaces 기본적으로 packages 폴더 아래의 모든 폴더를 워크스페이스로 본다.
{
  "name": "package-test",
  "packageManager": "yarn@3.5.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

타입스크립트 구성하기

  • 루트에서 실행한다 (워크스페이스에서 공유할 것이기 때문)
yarn add -D typescript @typescript-eslint/parser
# tsconfig.json 생성
touch tsconfig.base.json
  • tsconfig.base.json 내용 채우기
{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "moduleResolution": "Node",
        "resolveJsonModule": true,
        "esModuleInterop": true,
        "strict": true,
        "target": "es6",
        "lib": [
            "ES5",
            "ES6",
            "ESNext",
            "DOM"
        ],
        "skipLibCheck": true,
        "baseUrl": ".",
        "forceConsistentCasingInFileNames": true,
        "module": "esnext",
        "isolatedModules": true
    },
    "references": [ // 미래에 추가할 패키지들
        {
            "path": "packages/core"
        },
        {
            "path": "packages/example"
        },
    ],
    "exclude": [
        "packages/**/dist/**"
    ],
    "include": []
}

에디터에 맞는 설정 진행

  • nodeModules를 사용하기 때문에 필요 없음

eslint,prettier 구성하기

yarn add -D eslint prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin 
  • .eslintrc.cjs 생성 및 수정 하기
module.exports = {
  env: { browser: true, es2020: true },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
    project: ["./tsconfig.base.json", "./packages/**/tsconfig.json"],
  }
};

core 워크스페이스

초기화

yarn create vite packages/core --template react-ts
  • 구조는 다음과 같다.
├── .eslintrc.cjs # 삭제할 것
├── .gitignore
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
  • 위에 언급한대로 워크스페이스의 packages/core/.eslintrc.cjs를 삭제한다.

tsconfig.json 수정

{
  "extends": "../../tsconfig.base.json", // 추가
  "compilerOptions": {
    "target": "ESNext",
    "lib": [
      "DOM",
      "DOM.Iterable",
      "ESNext"
    ],
    "module": "ESNext",
    "skipLibCheck": true,
    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "src"
  ],
  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

.eslintrc.cjs 수정

  • eslint 관련 패키지 루트에 설치
yarn add -D eslint-plugin-react-hooks eslint-plugin-react-refresh
  • 워크스페이스에서는 삭제했으니 루트에서 수정해야한다.
module.exports = {
  env: { browser: true, es2020: true },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
    project: ["./tsconfig.base.json", "./packages/**/tsconfig.json"],
  },

  overrides: [
    {
      extends: ["plugin:react-hooks/recommended"],
      files: ["packages/core/**/*.ts?(x)", "packages/core/**/*.js?(x)"],
      plugins: ["react-refresh"],
      rules: {
        "react-refresh/only-export-components": "warn",
      },
    },
  ],
};

example 워크스페이스

같은 과정

  • 초기화
yarn create vite packages/example --template react-ts
  • packages/example/tsconfig.json에 extend 추가
  • .packages/example/eslintrc.cjs 삭제
  • 루트 .eslintrc.cjs 에 구성 추가(이번에는 둘다 vite 기반의 react-ts이기에 걍 files만 추가해줬음)

core 워크스페이스를 라이브러리로 바꾸기!

구조 변경

├── .gitignore
├── package.json
├── src
│   ├── components
│   │   └── Button.tsx
│   ├── index.ts
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
  • packages/core/index.ts
export { Button } from "./components/Button";
  • packages/core/components/Button.tsx
import { ReactElement } from "react";

export function Button(): ReactElement {
  return <button>Click Me</button>;
}

vite 패키지 설치

  • in packages/core
yarn add -D vite-plugin-dts

package.json 수정

  • packages/core/package.json
{
  "name": "@package-test/core", // 이것도 생각난 김에 수정!
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.57.1",
    "@typescript-eslint/parser": "^5.57.1",
    "@vitejs/plugin-react": "^4.0.0",
    "eslint": "^8.39.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.3.4",
    "typescript": "^5.0.2",
    "vite": "^4.3.2"
  },
  "main": "dist/core.js", // 추가
  "types": "dist/index.d.ts" // 추가
}

vite.confing.ts 수정

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import dts from 'vite-plugin-dts'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(),dts()],
  build: {
    lib: {
      entry: 'src/index.ts',
      name: '@package-test/core',
      formats: ['es']
    },
    rollupOptions: {
      external: ['react'],
      output: {
        globals: {
          react: 'React'
        }
      }
    }
  }

})

example 워크스페이스에서 core import 해보기!!

package.json

  • packages/example/package.json
  • core 패키지 import 하기
{
  "name": "@package-test/example",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@package-test/core": "0.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.57.1",
    "@typescript-eslint/parser": "^5.57.1",
    "@vitejs/plugin-react": "^4.0.0",
    "eslint": "^8.38.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.3.4",
    "typescript": "^5.0.2",
    "vite": "^4.3.2"
  }
}

App.tsx

  • packages/example/src/App.tsx
  • 컴포넌트 import 해서 사용해보기!
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import { Button } from "@package-test/core";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <Button />
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

나도 이제 모노 레포 구성할 수 있따!!!

profile
개발자 지망생입니다.

9개의 댓글

comment-user-thumbnail
2024년 3월 20일

안녕하세요 잘 읽었습니다.!
읽다보니 궁금한 점이 있는데요.
혹시 core폴더에서의 컴포넌트 생성할 때 화면에서와 확인이나 디버깅은 어떻게 진행하셨는지 궁금합니다..!
감사합니다 :)

1개의 답글