แนวคิด Monorepo เคล็ดลับและกลเม็ดที่มุ่งเน้นไปรอบ ๆ NextJS
Howtos สำหรับ monorepo ใหม่กับ monorepos? ตรวจสอบคำถามที่พบบ่อยนี้ ตัวอย่างนี้ได้รับการจัดการโดย turborepo และเส้นด้าย 4 ด้วยวิธีการนามแฝงเส้นทาง A / typescript ไม่ใช่วิธีเดียวที่จะทำ
เป็นประโยชน์
หากคุณกำลังเพลิดเพลินกับงาน OSS ของฉันใน บริษัท ของคุณฉันขอขอบคุณผู้สนับสนุนกาแฟหรือดาราที่ตกหล่น นั่นทำให้ฉันมีเวลามากขึ้นในการปรับปรุงในระดับต่อไป
| เจ็ทครีนส์ | embie.be | vercel |
corepack enable
yarn install .
├── apps
│ ├── nextjs-app (i18n, ssr, api, vitest)
│ └── vite-app
└── packages
├── common-i18n (locales...)
├── core-lib
├── db-main-prisma
├── eslint-config-bases (to shared eslint configs)
└── ui-lib (emotion, storybook)
แอพไม่ควรขึ้นอยู่กับแอพพวกเขาสามารถขึ้นอยู่กับแพ็คเกจ
แอพสามารถขึ้นอยู่กับแพ็คเกจแพ็คเกจสามารถขึ้นอยู่กับกันและกัน ...
.
├── apps
│ ├── vite-app (Vite app as an example)
│ │ ├── src/
│ │ ├── package.json (define package workspace:package deps)
│ │ └── tsconfig.json (define path to packages)
│ │
│ └── nextjs-app (NextJS app with api-routes)
│ ├── e2e/ (E2E tests with playwright)
│ ├── public/
│ ├── src/
│ │ └── pages/api (api routes)
│ ├── CHANGELOG.md
│ ├── next.config.mjs
│ ├── package.json (define package workspace:package deps)
│ ├── tsconfig.json (define path to packages)
│ └── vitest.config.ts
│
├── packages
│ ├── core-lib (basic ts libs)
│ │ ├── src/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ ├── db-main-prisma (basic db layer with prisma)
│ │ ├── e2e/ (E2E tests)
│ │ ├── prisma/
│ │ ├── src/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ ├── eslint-config-bases
│ │ ├── src/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ └── ui-lib (basic design-system in react)
│ ├── src/
│ ├── CHANGELOG.md
│ ├── package.json
│ └── tsconfig.json
│
├── static (no code: images, json, locales,...)
│ ├── assets
│ └── locales
├── docker (docker...)
│ ├── .dockerignore
│ ├── docker-compose.yml (compose specific for nextjs-app)
│ ├── docker-compose.db.yml (general services like postgresql...)
│ └── Dockerfile (multistage build for nextjs-app)
├── .yarnrc.yml
├── package.json (the workspace config)
└── tsconfig.base.json (base typescript config)
{
"name" : "nextjs-monorepo-example" ,
// Set the directories where your apps, packages will be placed
"workspaces" : [ "apps/*" , "packages/*" ] ,
//...
} ผู้จัดการแพ็คเกจจะสแกนไดเรกทอรีเหล่านั้นและค้นหา package.json เด็ก json เนื้อหาของพวกเขาใช้เพื่อกำหนดทอพอโลยีพื้นที่ทำงาน (แอพ, libs, การพึ่งพา ... )
สร้างโฟลเดอร์ใน ./packages/ ไดเรกทอรีพร้อมชื่อแพ็คเกจของคุณ
mkdir packages/magnificent-poney
mkdir packages/magnificent-poney/src
cd packages/magnificent-poneyเริ่มต้น package.json ด้วยชื่อแพ็คเกจของคุณ
แทนที่จะพิมพ์
yarn initชอบที่จะใช้./packages/ui-lib/package.json เป็นตัวอย่างการทำงานและแก้ไขค่าของมัน
{
"name" : "@your-org/magnificent-poney" ,
"version" : "0.0.0" ,
"private" : true ,
"scripts" : {
"clean" : "rimraf ./tsconfig.tsbuildinfo" ,
"lint" : "eslint . --ext .ts,.tsx,.js,.jsx" ,
"typecheck" : "tsc --project ./tsconfig.json --noEmit" ,
"test" : "run-s 'test:*'" ,
"test:unit" : "echo "No tests yet"" ,
"fix:staged-files" : "lint-staged --allow-empty" ,
"fix:all-files" : "eslint . --ext .ts,.tsx,.js,.jsx --fix" ,
} ,
"devDependencies" : {
"@your-org/eslint-config-bases" : "workspace:^" ,
} ,
}ก่อนอื่นเพิ่มแพ็คเกจลงในแพ็คเกจแอป json วิธีที่แนะนำคือใช้โปรโตคอลพื้นที่ทำงานที่รองรับโดยเส้นด้ายและ PNPM
cd apps/my-app
yarn add @your-org/magnificent-poney@ ' workspace:^ 'แรงบันดาลใจสามารถพบได้ในแอพ/nextjs-app/package.json
{
"name" : "my-app" ,
"dependencies" : {
"@your-org/magnificient-poney" : "workspace:^" ,
} ,
} จากนั้นเพิ่มนามแฝงเส้นทาง typeScript ในแอพ tsconfig.json สิ่งนี้จะช่วยให้คุณสามารถนำเข้าได้โดยตรง (ไม่จำเป็นต้องมีการสร้าง)
แรงบันดาลใจสามารถพบได้ในแอพ/nextjs-app/tsconfig.json
{
"compilerOptions" : {
"baseUrl" : "./src" ,
"paths" : {
// regular app aliases
"@/components/*" : [ "./components/*" ] ,
// packages aliases, relative to app_directory/baseUrl
"@your-org/magnificent-poney/*" : [
"../../../packages/magnificent-poney/src/*" ,
] ,
"@your-org/magnificent-poney" : [
"../../../packages/magnificent-poney/src/index" ,
] ,
} ,
} ,
}PS:
- อย่าพยายามตั้งค่านามแฝงใน Global tsonfig.base.json เพื่อให้เข้มงวดกับการพึ่งพากราฟ
- ดาว ใน
@your-org/magnificent-poney/*ช่วยให้คุณสามารถนำเข้าโฟลเดอร์ย่อย หากคุณใช้ไฟล์บาร์เรล (index.ts) นามแฝงที่มีดาวสามารถลบออกได้
แก้ไข next.config.mjs ของคุณและเปิดใช้งานตัวเลือก Experimental.externaldir คำติชมที่นี่
const nextConfig = {
experimental : {
externalDir : true ,
} ,
} ;หากคุณใช้เวอร์ชัน NextJS รุ่นเก่าและไม่มีธงทดลองคุณสามารถแทนที่ config webpack ของคุณได้
const nextConfig = {
webpack : ( config , { defaultLoaders } ) => {
// Will allow transpilation of shared packages through tsonfig paths
// @link https://github.com/vercel/next.js/pull/13542
const resolvedBaseUrl = path . resolve ( config . context , "../../" ) ;
config . module . rules = [
... config . module . rules ,
{
test : / .(tsx|ts|js|jsx|json)$ / ,
include : [ resolvedBaseUrl ] ,
use : defaultLoaders . babel ,
exclude : ( excludePath ) => {
return / node_modules / . test ( excludePath ) ;
} ,
} ,
] ;
return config ;
} ,
} ;PS: หากแพ็คเกจที่ใช้ร่วมกันของคุณใช้ประโยชน์จาก SCSS Bundler ... การกำหนดค่า webpack ที่กำหนดเองจะจำเป็นหรือใช้โมดูลการถ่ายโอนครั้งต่อไปดูที่คำถามที่พบบ่อยด้านล่าง
แพ็คเกจนี้เชื่อมโยงกับแอปของคุณเพียงแค่นำเข้าเช่นแพ็คเกจปกติ: import { poney } from '@your-org/magnificent-poney'
ไม่จำเป็น
หากคุณต้องการแบ่งปันแพ็คเกจนอก Monorepo คุณสามารถเผยแพร่ไปยัง NPM หรือที่เก็บส่วนตัว ตัวอย่างที่อิงกับ microbundle มีอยู่ในแต่ละแพ็คเกจ การกำหนดเวอร์ชันและการเผยแพร่สามารถทำได้ด้วย Atlassian/ChangeSet และมันง่ายเหมือนการพิมพ์:
$ yarn g:changesetทำตามคำแนะนำ ... และส่งไฟล์การเปลี่ยนแปลง "แพ็คเกจเวอร์ชัน" P/R จะปรากฏขึ้นหลังจากตรวจสอบ CI เมื่อรวมเข้าด้วยกันการกระทำของ GitHub จะเผยแพร่แพ็คเกจด้วยเวอร์ชัน SEMVER ที่เกิดขึ้นและสร้าง Changelogs ให้คุณ
PS:
- แม้ว่าคุณจะไม่จำเป็นต้องเผยแพร่ แต่การเปลี่ยนแปลงสามารถรักษา changelog อัตโนมัติสำหรับแอพของคุณ ดี !
- หากต้องการปิดใช้งานการเผยแพร่แพ็คเกจโดยอัตโนมัติเพียงแค่ตั้งค่า
"private": "true"ในแพ็คเกจของพวกเขา json- ต้องการปรับพฤติกรรมดู. changeset/config.json
สคริปต์ความสะดวกสบายบางอย่างสามารถเรียกใช้ในโฟลเดอร์ใด ๆ ของ repo นี้และจะเรียกคู่ของพวกเขาที่กำหนดไว้ในแพ็คเกจและแอพ
| ชื่อ | คำอธิบาย |
|---|---|
yarn g:changeset | เพิ่มชุดการเปลี่ยนแปลงเพื่อประกาศเวอร์ชันใหม่ |
yarn g:codegen | เรียกใช้ codegen ในพื้นที่ทำงานทั้งหมด |
yarn g:typecheck | เรียกใช้ typechecks ในพื้นที่ทำงานทั้งหมด |
yarn g:lint | แสดงปัญหา linter ในพื้นที่ทำงานทั้งหมด |
yarn g:lint --fix | พยายามเรียกใช้ Linter Auto-Fix ในพื้นที่ทำงานทั้งหมด |
yarn g:lint-styles | แสดงปัญหา CSS Stylelint ในพื้นที่ทำงานทั้งหมด |
yarn g:lint-styles --fix | พยายามเรียกใช้ปัญหา Auto-Fix Stylelint ในพื้นที่ทำงานทั้งหมด |
yarn g:test | เรียกใช้หน่วยและการทดสอบ E2E ในพื้นที่ทำงานทั้งหมด |
yarn g:test-unit | เรียกใช้การทดสอบหน่วยในพื้นที่ทำงานทั้งหมด |
yarn g:test-e2e | เรียกใช้การทดสอบ E2E ในพื้นที่ทำงานทั้งหมด |
yarn g:build | เรียกใช้งานสร้างในพื้นที่ทำงานทั้งหมด |
yarn g:clean | ทำความสะอาดสร้างในพื้นที่ทำงานทั้งหมด |
yarn g:check-dist | ตรวจสอบให้แน่ใจว่ามีการสร้างไฟล์ Dist ผ่าน ES2017 (เรียกใช้ g:build ก่อน) |
yarn g:check-size | ตรวจสอบให้แน่ใจว่าไฟล์ Dist ของเบราว์เซอร์อยู่ในขนาดที่ จำกัด (เรียกใช้ g:build ก่อน) |
yarn clean:global-cache | Clean Tooling Caches (Eslint, Jest ... ) |
yarn deps:check --dep dev | จะพิมพ์แพ็คเกจใดที่สามารถอัพเกรดทั่วโลก (ดู. ncurc.yml) |
yarn deps:update --dep dev | ใช้การอัปเดตที่เป็นไปได้ (เรียกใช้ yarn install && yarn dedupe หลังจากนั้น) |
yarn install:playwright | ติดตั้ง Playwright สำหรับ E2E |
yarn dedupe | การขจัดความซ้ำซ้อนของเส้นด้ายในตัวของไฟล์ล็อค |
ทำไมต้องใช้
:คำนำหน้าสคริปต์ชื่อ? มันสะดวกในเส้นด้าย 3+ เราสามารถเรียกสคริปต์เหล่านั้นจากโฟลเดอร์ใด ๆ ในโมเนโรg:เป็นทางลัดสำหรับglobal:ดูรายการที่สมบูรณ์ใน root package.json
คำสั่งทั่วโลก yarn deps:check และ yarn deps:update จะช่วยรักษาเวอร์ชันเดียวกันทั่วทั้งโมโนเรป พวกเขาจะขึ้นอยู่กับการตรวจสอบ NPM ที่ยอดเยี่ยม (ดูตัวเลือกเช่น: yarn check:deps -t minor )
หลังจากเรียกใช้
yarn deps:updateต้องมีyarn installเพื่อป้องกันไม่ให้มีการทำซ้ำในเส้นด้ายล็อคคุณสามารถเรียกใช้yarn dedupe --checkและyarn dedupeเพื่อใช้การซ้ำซ้อน การตรวจสอบที่ซ้ำกันถูกบังคับใช้ในการกระทำของ GitHub
ดูตัวอย่างใน ./apps/nextjs-app/.eslintrc.js และ Eslint-config-bases ของเรา
ตรวจสอบเนื้อหาโฟลเดอร์. husky เพื่อดูว่ามีการเปิดใช้งาน hooks อะไร Lint-Staged ใช้เพื่อรับประกันว่าผ้าสำลีและ Prettier จะถูกนำไปใช้โดยอัตโนมัติในการกระทำและ/หรือผลักดัน
การทดสอบขึ้นอยู่กับ TS-JEST หรือ VATEST ขึ้นอยู่กับแอพ การตั้งค่าทั้งหมดรองรับนามแฝงเส้นทาง TypeScript Reacting-testing-Library เปิดใช้งานเมื่อใดก็ตามที่เกี่ยวข้องกับการตอบสนอง
การกำหนดค่าใช้ชีวิตในโฟลเดอร์รูทของแต่ละแอพ/แพ็คเกจ เป็นตัวอย่างดู
คุณจะพบเวิร์กโฟลว์ตัวอย่างสำหรับการกระทำของ GitHub ใน. github/เวิร์กโฟลว์ โดยค่าเริ่มต้นพวกเขาจะตรวจสอบให้แน่ใจว่า
แต่ละขั้นตอนเหล่านั้นสามารถเลือกเข้าร่วมได้
เพื่อให้แน่ใจว่าประสิทธิภาพที่เหมาะสมคุณสมบัติเหล่านั้นมีอยู่ในการกระทำตัวอย่าง:
การแคช แพ็คเกจ (node_modules ... ) - ติดตั้งประมาณ 25s
การแคช ของ Nextjs ก่อนหน้านี้สร้างขึ้นประมาณ 20 ปี
ทริกเกอร์เมื่อมีการเปลี่ยนแปลง โดยใช้เส้นทางการกระทำเช่น:
paths: - "apps/nextjs-app/**" - "packages/**" - "package.json" - "tsconfig.base.json" - "yarn.lock" - ".yarnrc.yml" - ".github/workflows/**" - ".eslintrc.base.json" - ".eslintignore"
ปลั๊กอิน ESLINT ต้องการให้ตั้งค่าการตั้งค่า eslint.workingDirectories :
"eslint.workingDirectories": [
{
"pattern": "./apps/*/"
},
{
"pattern": "./packages/*/"
}
],
ข้อมูลเพิ่มเติมที่นี่
Vercel สนับสนุน monorepos โดยธรรมชาติดูเอกสาร vercel-monorepo-deploy
มีตัวอย่างพื้นฐานสำหรับการสร้างภาพนักเทียบท่าอ่านเอกสาร Docker
Netlify, AWS-amplify, K8S-Docker, สูตรเซิร์ฟเวอร์แบบไม่มีเน็กซ์อาจถูกเพิ่มในอนาคต ยินดีต้อนรับ PR ด้วย
แอพพึ่งพาและการพึ่งพาอาศัยกันถูกตรึงไว้ในเวอร์ชันที่แน่นอน แพ็คเกจ deps จะใช้ชุดที่เข้ากันได้กับ Semver สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงนี้ดูการให้เหตุผลที่นี่และไฟล์การกำหนดค่า renovabot.json5 ของเรา
เพื่อช่วยรักษา deps ให้ทันสมัยดู yarn deps:check && yarn deps:update สคริปต์และ / หรือใช้การปรับปรุงใหม่
เมื่อเพิ่ม DEP ผ่านเส้นด้าย CLI (เช่น: เส้นด้ายเพิ่มบางสิ่ง) เป็นไปได้ที่จะตั้งค่าพฤติกรรมการบันทึกการถอนโดยอัตโนมัติโดยการตั้งค่า
defaultSemverRangePrefix: ""ใน yarnrc.yml แต่สิ่งนี้จะทำให้ค่าเริ่มต้นสำหรับแพ็คเกจ/* เช่นกัน ดีกว่าที่จะจัดการกับyarn add something --exactการสำรวจต่อกรณี