React and Typescript Components Lib ، قسمت 1: تنظیم و انتشار

مقدمه
در طول یک سال گذشته ، من مقالاتی راجع به موضوعات مختلف مرتبط با React نوشتم. اکنون ، هدف این است که اصول ایجاد یک کتابخانه React را که به قسمت های مختلف تقسیم شده است ، بپوشانیم و آنچه را که سال گذشته به همراه برخی مباحث جدید مورد بحث قرار گرفت ، استفاده کنید: تنظیم برنامه و انتشار ، استفاده از مؤلفه های یک ظاهر طراحی شده ، اضافه کردن TypeScript ، آزمایش واحد با JEST و تست-لیبراسیون ، ادغام ESLINT و زیباتر برای استاندارد سازی کد ، مستند سازی با کتاب داستان ، تنظیم قلاب های پیش از سازگار با Husky و خودکار سازی تولید کد با Hygen.
از آنجا که مباحثی را که قبلاً در مورد آنها نوشتم ، پوشش می دهم ، آن مقالات را در کتابهای مربوط به کتابخانه ارجاع می دهم. برای مواردی که نسخه جدید در مقایسه با آنچه قبلاً نوشتم تغییرات قابل توجهی را ایجاد کرده است ، ابتدا قبل از اجرای آن در کتابخانه ، مقاله اختصاصی را در مورد این موضوع منتشر خواهم کرد.
قسمت 1 نحوه تنظیم کتابخانه را پوشش می دهد ، اولین اجزای مثال را با استفاده از TypeScript با استفاده از اجزای سبک ایجاد کرده و آن را در NPMJ ها منتشر می کند تا دیگران بتوانند به آن دسترسی پیدا کنند.
غلتک
این یک دسته ماژول برای JavaScript است که امکان جمع آوری قطعات کوچک کد را در موجودات بزرگتر و پیچیده تر مانند کتابخانه ها فراهم می کند. در ایجاد کتابخانه استفاده خواهد شد.
npmjs
مکانی که در آن کتابخانه منتشر و برای استفاده از برنامه های دیگر در دسترس قرار خواهد گرفت.
اجزاء
یک کتابخانه یک ظاهر طراحی شده برای مؤلفه های برنامه های React ، انعطاف پذیری در تعریف سبک ها بر اساس غرفه های دریافت شده. من سال گذشته مقاله ای نوشتم که توضیح می دهد که چگونه کار می کند:
مؤلفه های یک ظاهر طراحی شده در واکنش با TypeScript
شرح
تایپ کردن را به یک برنامه JavaScript اضافه می کند و خطاها را در زمان کامپایل تشخیص می دهد.
تنظیم اولیه
یک پوشه با نام کتابخانه ایجاد کنید (توصیه می کنم NPMJ ها را بررسی کنید تا اطمینان حاصل کنید که یک کتابخانه با همین نام از قبل وجود ندارد ، برای جلوگیری از مشکلات هنگام انتشار آن) ، سپس پروژه را در داخل آن آغاز کنید.
yarn init
در حال حاضر ، می توانید تمام تنظیمات پیش فرض را بپذیرید. بعداً ، اطلاعات موجود در بسته تولید شده تنظیم می شود.
به عنوان وابستگی به همسالان ، واکنش های React و یک سبک را اضافه کنید زیرا آنها با کتابخانه همراه نخواهند شد (برنامه با استفاده از کتابخانه کامپوننت نیاز به افزودن آنها دارد). همچنین ، آنها را به عنوان وابستگی های انحرافی برای استفاده در هنگام توسعه کتابخانه مؤلفه اضافه کنید:
yarn add react react-dom styled-components --peer
yarn add react react-dom styled-components --dev
در زمان نوشتن این مقاله ، نسخه های زیر تولید شده است:
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
TypeScript را به پروژه اضافه کنید
TypeScript را به عنوان وابستگی DEV اضافه کنید:
yarn add typescript @types/react --dev
در زمان نوشتن این مقاله ، نسخه های زیر تولید شده است:
"@types/react": "^19.0.8",
"typescript": "^5.7.3"
فایل پیکربندی Typescript را به ریشه پروژه اضافه کنید.
{
"compilerOptions": {
"target": "ES2016",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist",
"declarationDir": "types",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
- “هدف”: “ES2016” ، نسخه هدف ECMAScript را مشخص کنید
- “JSX”: “واکنش” ، کد JSX را به React تبدیل کنید
- “ماژول”: “esnext” ، ماژول های مدرن جاوا اسکریپت را برای کتابخانه تولید کنید
- “Moduleresolution”: “گره” ، برای یافتن ماژول ها از قوانین Node.js پیروی کنید
- “اعلامیه”: درست ، یک پرونده .d.ts را برای انواع کتابخانه تولید کنید
- “EmitDeclarationonly”: درست است ، فقط اعلامیه های نوع را بدون تولید JS صادر کنید ، زیرا Rollup آن را اداره می کند.
- “Outdir”: “Dist” ، دایرکتوری که در آن پروژه تولید می شود
- “DemorationDir”: “انواع” ، که در آن پرونده .d.ts قرار می گیرد
- “AllowsyntheticDefaultImports”: درست است ، فرض کنید در صورت عدم وجود صادرات به صورت دستی ، صادرات را پیش فرض کنید
- “EsmoduleInterop”: درست ، امکان سازگاری بهتر بین ماژول های مشترک و ES را فراهم می کند
- “forceconsientcassinginfilenames”: درست است ، بررسی کنید که آیا واردات پرونده با نام پرونده حساس به پرونده سازگار است
- “سخت”: درست است ، تعداد زیادی از نوع چک های نوع را به همراه می آورد که یکپارچگی کد عالی را تضمین می کند
- “SkiplibCheck”: درست است ، اعتبار نوع را برای پرونده .d.ts نادیده بگیرید
ایجاد اجزای مثال
دو مؤلفه مثال ایجاد می شود: یکی ساده برای متن و دیگری برای یک برچسب. اول ، الف src
پوشه در ریشه پروژه و در داخل آن تولید می شود. components
پوشه ای که تمام اجزای ایجاد شده در آن قرار می گیرند.
شروع با مؤلفه متن ، a Text
پوشه در داخل ایجاد می شود components
، با دو پرونده:
import React from "react";
import styled from "styled-components";
export interface TextProps {
children: React.ReactNode;
color?: string;
weight?: "normal" | "bold";
fontWeight?: number;
fontSize?: string;
fontFamily?: string;
}
export interface StyledTextProps {
$color?: string;
$weight?: "normal" | "bold";
$fontWeight?: number;
$fontSize?: string;
$fontFamily?: string;
}
export const StyledText = styled.span<StyledTextProps>`
color: ${(props) =>
props.$color
? props.$color
: "#000"};
font-size: ${(props) =>
props.$fontSize
? props.$fontSize
: "16px"};
font-weight: ${(props) =>
props.$fontWeight
? props.$fontWeight
: props.$weight
? props.$weight
: "normal"};
font-family: ${(props) =>
props.$fontFamily
? props.$fontFamily
: "Arial"};
`;
const Text = ({
children,
color,
weight,
fontWeight,
fontSize,
fontFamily,
}: TextProps) => (
<StyledText
$color={color}
$weight={weight}
$fontWeight={fontWeight}
$fontSize={fontSize}
$fontFamily={fontFamily}
>
{children}
</StyledText>
);
export default Text;
این 5 غرفه را می پذیرد: children
، که خود متن را نشان می دهد ، color
، که رنگ متن را تعریف می کند ، fontWeight
، که وزن قلم متن را تعریف می کند ، fontSize
، که اندازه قلم متن را تعریف می کند ، و در نهایت fontFamily
، که خانواده قلم متن را تعریف می کند. اگر هیچ یک از این غرفه ها ارائه نشده است ، به جز موارد children
، StyledText
در حال حاضر یک مقدار پیش فرض برای هر خاصیت تعریف شده است.
export { default } from "./Text";
این مؤلفه در دسترس خواهد بود.
اکنون ، برای مؤلفه برچسب ، a Tag
پوشه در داخل ایجاد می شود components
، با دو پرونده:
import React from "react";
import styled from "styled-components";
import Text from "../Text/Text";
export interface TagProps {
type?: "default" | "success" | "alert" | "error";
text: string;
textColor?: string;
textWeight?: "normal" | "bold";
textFontWeight?: number;
textFontSize?: string;
textFontFamily?: string;
backgroundColor?: string;
format?: "default" | "semiRounded" | "rounded";
borderRadius?: string;
size?: "small" | "medium" | "large";
padding?: string;
}
export interface StyledTagProps {
$type?: "default" | "success" | "alert" | "error";
$textColor?: string;
$textWeight?: "normal" | "bold";
$textFontWeight?: number;
$textFontSize?: string;
$textFontFamily?: string;
$backgroundColor?: string;
$format?: "default" | "semiRounded" | "rounded";
$borderRadius?: string;
$size?: "small" | "medium" | "large";
$padding?: string;
}
export const StyledTag = styled.div<StyledTagProps>`
border: none;
padding: ${(props) =>
props.$padding
? props.$padding
: props.$size === "large"
? "15px 20px"
: props.$size === "medium"
? "10px 12px"
: "7px"};
background-color: ${(props) =>
props.$backgroundColor
? props.$backgroundColor
: props.$type === "error"
? "#e97451"
: props.$type === "alert"
? "#f8de7e"
: props.$type === "success"
? "#50c878"
: "#d3d3d3"};
pointer-events: none;
border-radius: ${(props) =>
props.$borderRadius
? props.$borderRadius
: props.$format === "rounded"
? "30px"
: props.$format === "semiRounded"
? "5px"
: "0"};
width: fit-content;
`;
const Tag = ({
text,
type,
textColor,
textWeight,
textFontWeight,
textFontSize,
textFontFamily,
backgroundColor,
format,
borderRadius,
size,
padding,
}: TagProps) => (
<StyledTag
$type={type}
$backgroundColor={backgroundColor}
$format={format}
$borderRadius={borderRadius}
$size={size}
$padding={padding}
>
<Text
color={textColor || "#fff"}
weight={textWeight}
fontWeight={textFontWeight}
fontSize={textFontSize}
fontFamily={textFontFamily}
>
{text}
</Text>
</StyledTag>
);
export default Tag;
11 غرفه را می پذیرد.
3 از قبل تعریف شده اند: size
که بالشتک را تعریف می کند (اگر large
تنظیم می کند 15px 20px
، اگر medium
تنظیم می کند 10px 12px
) type
که رنگ پس زمینه را تعریف می کند (اگر error
تنظیم می کند #e97451
، اگر alert
تنظیم می کند #f8de7e
، اگر success
تنظیم می کند #50c878
)) ، و format
که شعاع مرزی را تعریف می کند (اگر rounded
تنظیم می کند 30px
، اگر semiRounded
تنظیم می کند 5px
).
و 8 قابل تنظیم هستند: text
که متن برچسب را تعریف می کند ، textColor
که رنگ متن را تعریف می کند ، textFontWeight
که وزن فونت متن را تعریف می کند ، textFontSize
که اندازه قلم متن را تعریف می کند ، textFontFamily
که فونت خانواده متن را تعریف می کند ، backgroundColor
که رنگ پس زمینه برچسب را تعریف می کند ، borderRadius
که شعاع مرزی برچسب را تعریف می کند ، و سرانجام padding
که بالشتک برچسب را تعریف می کند.
اگر هیچ یک از آنها ارائه نشده است ، به جز موارد text
، StyledTag
در حال حاضر یک مقدار پیش فرض برای هر خاصیت تعریف شده است.
export { default } from "./Tag";
این مؤلفه در دسترس خواهد بود.
پس از تعریف دو مؤلفه ، صادرات آنها در داخل متمرکز خواهد شد src/components
پوشه:
export { default as Tag } from "./Tag";
export { default as Text } from "./Text";
سرانجام ، آنچه کتابخانه ارائه می دهد وقتی اضافه شود در داخل متمرکز خواهد شد src
پوشه:
export * from "./components";
به این ترتیب ، دو مؤلفه تعریف شده و قرار گرفتن در معرض آنها متمرکز است و این امکان را می دهد تا پیکربندی Rollup را ادامه دهند.
راه اندازی
برای استفاده از Rollup ، ابتدا کتابخانه های زیر اضافه می شوند:
yarn add rollup rollup-plugin-dts rollup-plugin-peer-deps-external @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript@11.1.6 @rollup/plugin-terser --dev
در زمان نوشتن این مقاله ، نسخه های زیر تولید شده است:
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4"
برخلاف سایر کتابخانه ها که اضافه می شوند ، نسخه خاصی برای آن تعریف شده است @rollup/plugin-typescript
به دلیل موضوعی که از استفاده از آن در نسخه های بعدی جلوگیری می کند.
در مرحله بعد ، ایجاد فایل پیکربندی Rollup در ریشه پروژه ، که از کتابخانه های اضافه شده استفاده می شود ، لازم است:
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import terser from "@rollup/plugin-terser";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
const packageJson = require("./package.json");
export default [
{
input: "src/index.ts",
output: [
{
file: packageJson.main,
format: "cjs",
},
{
file: packageJson.module,
format: "esm",
},
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript({ tsconfig: "./tsconfig.json" }),
terser(),
],
external: ["react", "react-dom", "styled-components"],
},
{
input: "src/index.ts",
output: [{ file: "dist/index.d.ts", format: "esm" }],
plugins: [dts.default()],
},
];
در این پرونده از کتابخانه های اضافه شده به برنامه استفاده می شود.
در input: "src/index.ts"
، پرونده ای که اجزای آن صادر می شود تعریف شده است. در output
، دو output
پرونده ها ایجاد می شوند: اولین مورد در قالب CJS و دوم در قالب ESM ، که در Pack.json با مسیرهای آنها مشخص می شود. در peerDepsExternal()
وت external: ["react", "react-dom", "styled-components"]
، وابستگی های خارجی کتابخانه تعریف شده است ، به این معنی که این وابستگی ها هنگام افزودن کتابخانه به هم بسته نمی شوند. در Resolve () ، از الگوریتم گره برای یافتن ماژول های شخص ثالث در node_modules
بشر در commonjs()
، ماژول های مشترک به ماژول های ES تبدیل می شوند و امکان استفاده از هر دو قالب را فراهم می کنند. در typescript({ tsconfig: "./tsconfig.json" })
، ادغام بین Rollup و TypeScript تعریف شده است ، و مکان فایل پیکربندی Typescript ارائه شده است. در terser()
، اندازه بسته نرم افزاری کاهش می یابد. سرانجام ، قسمت مربوط به dts.default()
افزونه یک فایل خروجی را با انواع مورد استفاده در کتابخانه تعریف می کند.
بسته
در حال حاضر ، package.json
باید چیزی شبیه به زیر باشد:
{
"name": "react-example-lib", // the name of the folder where the project was initialized
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@types/react": "^19.0.8",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"styled-components": "^6.1.14",
"typescript": "^5.7.3"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
}
}
برخی از تغییرات و اضافات در آن مورد نیاز خواهد بود و به نظر می رسد:
{
"name": "react-example-lib",
"version": "0.1.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/griseduardo/react-example-lib.git"
},
"scripts": {
"build": "rollup -c --bundleConfigAsCjs"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@types/react": "^19.0.8",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"styled-components": "^6.1.14",
"typescript": "^5.7.3"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
}
}
- نام: نام کتابخانه تعریف شده است.
- نسخه: نسخه کتابخانه از 1.0.0 به 0.1.0 تغییر یافت ، زیرا هنوز برای استفاده آماده نیست
- اصلی: مسیر خروجی برای قالب CJS
- ماژول: مسیر خروجی برای فرمت ESM
- انواع: مسیر انواع کتابخانه
- پرونده ها: پوشه ای که کل کتابخانه در آن قرار خواهد گرفت
- مخزن: در صورتی که برنامه در آن قرار دارد نیز در GitHub قرار دارد
- اسکریپت ها: ساخت تعریف شده است ، که اجرای آن را اجرا می کند
فایل
با اصلاح این کتابخانه ، نشان دادن آنچه با افزایش هر نسخه برای کسانی که از آن استفاده می کنند تغییر کرده است. برای این کار ، یک پرونده در ریشه ایجاد می شود:
## 0.1.0
_Jan. 29, 2025_
- initial config
پرونده readme
بخش مهم دیگر اضافه کردن یک ReadMe است که توضیح می دهد برنامه در مورد چیست ، نحوه اضافه کردن آن و نحوه استفاده از اجزای آن. به ریشه اضافه می شود:
پرونده gitignore
در مورد انتشار کتابخانه در NPMJ ها و ایجاد یک مخزن GitHub برای آن ، مهم نیست که پوشه های خاصی را در GitHub بارگذاری نکنید. یک پرونده .gitignore به ریشه پروژه اضافه می شود:
dist
node_modules
پوشه ها
سرانجام ، ساختار پوشه اولیه به این شکل خواهد بود:
انتشار کتابخانه
اولین قدم که باید انجام شود بررسی اینکه آیا اجرای برنامه با موفقیت انجام می شود یا خیر. برای انجام این کار ، yarn build
در ترمینال اجرا می شود ، همانطور که در package.json
بشر در صورت موفقیت ، الف dist
پوشه به طور خودکار در ریشه پروژه ایجاد می شود.
پس از آن ، ایجاد یک حساب کاربری در NPMJS ، جایی که این کتابخانه منتشر خواهد شد ، لازم است.
پس از ایجاد حساب در سایت ، باید از طریق ترمینال در داخل پروژه وارد شوید:
npm login
جایی که شما نیاز به وارد کردن نام کاربری/ایمیل و رمز عبور (همان موارد ایجاد شده در سایت) دارید. پس از ورود به سیستم ، می توانید کتابخانه را منتشر کنید:
npm publish --access public
پس از اجرای موفقیت آمیز ، این کتابخانه در سایت NPMJS قابل مشاهده خواهد بود و باعث می شود برنامه های دیگر برای اضافه کردن و استفاده از مؤلفه های تعریف شده در داخل آن (متن و برچسب) در دسترس باشند. پس از react
با react-dom
وت styled-components
به عنوان وابستگی همسالان اضافه شدند ، اگر برنامه اضافه شده کتابخانه هیچ یک از آنها را نداشته باشد ، مطلع می شود که اینها وابستگی های لازم برای عملکرد کتابخانه در زمان افزودن هستند.
پایان
هدف از این مقاله نشان دادن نحوه ایجاد و انتشار یک کتابخانه مؤلفه در واکنش با TypeScript بود. برای دستیابی به این هدف ، نحوه تنظیم TypeScript و پیکربندی Rollup را پوشش می دهد ، دو مؤلفه را با استفاده از TypeScript با اجزای سبک تعریف می کند ، برخی از پرونده ها را ارائه می دهد که من معتقدم برای ردیابی تغییرات در کتابخانه و نشان دادن نحوه استفاده از آن مفید هستند و در آخر ، مراحل انتشار آن در NPMJS. مراحل بعدی شامل اجرای ویژگی های جدید در کتابخانه ، مانند آزمون ها ، قوانین قبل از تعهد و سایر موارد ذکر شده در مقدمه خواهد بود. من مخزن را در GitHub قرار می دهم و آن را در NPMJ ها منتشر می کنم تا با آنچه در مقاله می نویسم دنبال کنم.