Эффективно делитесь кодом с Lerna
Привет кодетор! Сегодня я хотел бы изучить с вами, как настроить монорепозиторий с помощью Учить. Мы будем создавать монорепозиторий с внешним пакетом, внутренним пакетом и общим пакетом, который будет использовать общие типы и методы между ними. Я четко изложу проблему, с которой мы сталкиваемся, менее красивое решение и наше оптимизированное решение с использованием Lerna.
Эта проблема
Потому что я люблю Машинопись (и вы тоже должны!), я не могу не определить действительно потрясающие интерфейсы объектов, поэтому я всегда знаю, с чем я работаю.
Итак, вот мой удивительный пользователь. Я также хочу настроить функцию, которая возвращает мне полное имя пользователя.
Так в чем проблема? Ну, скажем, я использую этот код в своем бэкенде, чтобы настроить свои API и определить правильную типизацию. Это правильный способ написания API! Но что, если я ТАКЖЕ хочу использовать интерфейс и метод, чтобы убедиться, что я извлекаю правильные типы и для целей отображения во внешнем интерфейсе? что теперь?
Грязное решение
Вытащите свой старый добрый CopyPaste и получите common
папка во внешнем и внутреннем интерфейсе с одним и тем же кодом. Звучит красиво, но что, если мой пользователь станет более сложным? Я хочу добавить список увлечений, адрес электронной почты, пароль. Теперь мне нужно не забыть обновить код в двух местах. А что, если у меня теперь есть сервер отчетов, который служит той же цели?
Список можно продолжить.
Решение несостоятельное. Что же нам теперь делать?
Решение для обучения
Мы используем монорепо! По сути, это единый репозиторий для всего вашего кода. Это позволяет разным репозиториям использовать общий код, как если бы это была библиотека npm. Лерна делает все символические ссылки и магию за кулисами. Давайте посмотрим, как это работает!
Настраивать
Откройте пустую папку в вашем любимом редакторе кода. Затем запуститеnpx lerna init
npm i
Это создало репозиторий lerna с packages
папка и lerna.json
конфигурационный файл. Вам не нужно возиться с этим, если вы не должны.
внутри packages
создайте три папки: common
, frontend
, backend
давайте углубимся common
набрав в терминале: cd packages/common
Затем инициализируйте репозиторий npm, используя npm init -y
.
Во вновь созданном package.json
измените имя проекта на что-то вроде этого:
в формате «@{projectName}/{module}», как я сделал. Это стандартное соглашение и лучшая практика для монорепозиториев.
Я добавил скрипт «dev-common», который просто компилирует машинописный текст и следит за изменениями.
NB: «main» должен указывать на «dist/index.js», как мы настроим позже.
Теперь, пока общее, давайте установим зависимости: npm i typescript
И давайте создадим 3 файла: index.ts
, types.ts
, functions.ts
В types.ts
Добавить:
interface User {
firstName: string;
lastName: string;
}
export { User };
и для functions.ts
:
import { User } from "./types";
const getUserFullName = (user: User) => `${user.firstName} ${user.lastName}`;
export { getUserFullName };
и для index.ts
:
export * from "./functions";
export * from "./types";
Наконец, нам нужен tsconfig.json
файл:
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"$schema": "
"display": "Recommended"
}
NB: убедитесь, что целью является ES5, чтобы мы могли скомпилировать низкоуровневый javascript, а ключи Declaration и DeclarationMap были истинными, чтобы он создавал для нас файлы объявлений Typescript.
Вернемся к нашему корню (cd ../..) и в нашем package.json определим скрипт:
"dev-common": "lerna run dev-common",
и давайте запустим его: npm run dev-common
Надеюсь, мы получим это. Обратите внимание, что он строит dist
папка в общем пакете
Большой! Мы установили некоторые общие вещи. Давайте теперь перейдем в папку бэкэнда:cd ../backend
npm init -y
а также изменить название проекта.
Сделаем быстрый сервер:
npm i express @types/express typescript
npm i --save-dev nodemon ts-node
Скопируйте и вставьте tsconfig.json
из common
в backend
также.
Создать файл index.ts
:
import { User } from "@seanh/common";
import express from "express";
const users: User[] = [
{ firstName: "Sean", lastName: "Hurwitz" },
{ firstName: "Batman", lastName: "Michaels" },
];
const app = express();
app.use(express.json());
app.get("/", (req, res) => {
res.send("Hello!");
});
app.get("/users", (req, res) => {
res.json(users);
});
app.listen(2050, () => {
console.log(`server up on http://localhost:${2050}`);
});
Обратите внимание, как мы импортируем пользовательский интерфейс из @seanh/common
. Но откуда он знает? Потому что в package.json
это то что ты делаешь:
{
"name": "@seanh/backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev-backend": "tsc -w & nodemon dist/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@seanh/common": "1.0.0",
"@types/express": "^4.17.13",
"express": "^4.18.1",
"typescript": "^4.8.2"
},
"devDependencies": {
"nodemon": "^2.0.19",
"ts-node": "^10.9.1"
}
}
Я вручную добавил первую строку в зависимости. Убедитесь, что версия точно соответствует версии в package.json
из common
папка. (Кроме того, я добавил простой скрипт для запуска сервера).
Теперь давайте перейдем к корню нашего проекта в терминале. вы можете использовать либо cd ../..
или открыть новый терминал. Затем запустите:npx lerna bootstrap
Это побуждает lerna создавать символические ссылки, символические ссылки на различные пакеты в репозитории в соответствии с их зависимостью друг от друга. Поскольку общая папка является зависимостью от внутренней папки, lerna свяжет ее с серверной частью и придаст ей поведение пакета, как если бы он был загружен из npm!
в корневой package.json можно добавить скрипт:"dev-backend": "lerna run dev-backend"
Это запустит скрипт dev-backend в любом пакете, в котором он есть (в данном случае это только бэкэнд).
Мы надеемся, что запуск этого запустит сервер и перейдет к локальный хост вернет вам:
Итак, вы можете видеть, что внутренняя папка успешно использует код нашей общей папки!
Теперь добавим интерфейс. пойти в frontend
папка:cd packages/frontend
Я просто разверну приложение React для простоты использования, но на самом деле вы можете сделать что угодно:
npx create-react-app --template typescript .
(примечание: вы можете получить сообщение об ошибке, потому что lerna уже добавила package.json в папку внешнего интерфейса. Просто удалите файл и перезапустите create-react-app)
Очистить App.tsx
и добавьте этот код:
import { getUserFullName, User } from "@seanh/common";
import { useEffect, useState } from "react";
function App() {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
fetch("http://localhost:2050/users")
.then((r) => r.json())
.then((r) => setUsers(r));
}, []);
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.firstName}>{getUserFullName(user)}</li>
))}
</ul>
</div>
);
}
export default App;
не забудьте добавить "@seanh/common": "1.0.0",
в зависимостях фронтенда package.json
.
Вернуться к корню(cd ../..
) и беги npx lerna bootstrap
очередной раз
Я добавил этот скрипт в корень package.json
:"dev-frontend": "lerna run start"
Теперь вы можете бежать npm run dev-frontend
и, надеюсь, приложение раскручивается!
Вуаля. Фронтенд использует типы и метод из общей библиотеки!
Заключение
В этой краткой статье рассказывается о монорепозиториях и о том, как настроить их для повторного использования кода. Дайте мне знать, что вы думаете в разделе комментариев ниже!