برنامه نویسی

ساخت و انتشار بسته های NPM با تایپ اسکریپت، چندین نقطه ورودی، tailwind، tsup و npm

درس های آموخته شده از ساخت و انتشار 3 بسته NPM

در این آموزش، نحوه ایجاد و انتشار بسته های NPM خود را با استفاده از TypeScript یاد خواهیم گرفت. ما همه چیز از راه اندازی پروژه شما با TypeScript را با استفاده از آن پوشش خواهیم داد tsup برای ساختن

یک بسته NPM را منتشر کنید که باید 3 پیکربندی را که در یک پروژه متوسط ​​جاوا اسکریپت به آنها فکر نمی کنیم، درک کنیم.

  1. package.json: این فایل برای پیکربندی بسته NPM شما استفاده می شود. این شامل اطلاعاتی در مورد بسته شما مانند نام، نسخه، توضیحات و وابستگی ها است.
{
    "name": "my-package",
    "version": "1.0.0",
    "description": "My package description",
    "repository": {

    },
    "main": "index.js",
    "exports": {
        "*":{
            "types": "./index.d.ts",
            "require": "./index.js",
            "import": "./index.js"
        }
    },
    "scripts": {
        "build":"tsup"

    },
    "dependencies": {

    },
    "devDependencies": {

    },


}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مهمترین زمینه برای هر بسته NPM هستند

  • name: نام بسته شما. این باید منحصر به فرد باشد و قبلاً روی NPM گرفته نشده باشد.

  • version:` شماره نسخه بسته شما. این باید به دنبال نسخه‌سازی معنایی باشد.

  • توضیحات: شرح کوتاهی از بسته شما.

  • مخزن: محل کد منبع بسته شما.

  • main: نقطه ورود بسته شما. این فایلی است که زمانی بارگذاری می شود که شخصی به بسته شما نیاز داشته باشد.

  • exports:این فیلد برای مشخص کردن فایل‌هایی استفاده می‌شود که باید هنگام وارد کردن بسته‌های شما شامل شوند. از این فیلد می توان برای اشاره به d.ts فایل ها و چندین نقطه ورودی

هنگامی که چندین نقطه ورودی را در فیلد “صادرات” مشخص می کنید، می توانید یک رابط عمومی برای بسته خود تعریف کنید و آن را کپسوله کنید و از هر نقطه ورودی دیگری غیر از مواردی که در “صادرات” تعریف شده است جلوگیری کنید. این می تواند زمانی مفید باشد که بخواهید فقط بخش های خاصی از بسته خود را در معرض ماژول های دیگر قرار دهید.

به عنوان مثال، در شما package.jsoفایل n`، می توانید چندین نقطه ورودی را مانند این تعریف کنید:

src/index.ts

export function foo(){
return "FOO";
}

export function bar(){
return "BAR";
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

src/math/foo.ts

export function foo(){
return "FOO";
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

src/math/bar.ts

export function bar(){
return "BAR";
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

"exports": {
    ".": {
        "require": "./dist/index.js",
        "import": "./dist/index.js",
        "types": "./dist/index.d.ts"
    },
    "./foo": {
        "require": "./dist/foo.js",
        "import": "./dist/foo.js",
        "types": "./dist/foo.d.ts"
    },
    "./bar": {
        "require": "./dist/bar.js",
        "import": "./dist/bar.js",
        "types": "./dist/bar.d.ts"
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این به کسی اجازه می دهد بسته شما را به این صورت وارد کند:

import { foo, bar } from 'my-package';
import foo from 'my-package/foo';
import bar from 'my-package/bar';

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این می تواند زمانی مفید باشد که می خواهید قسمت های مختلف بسته خود را به عنوان ماژول های جداگانه در معرض دید قرار دهید.

زمینه صادرات بسیار مهم است زیرا به پروژه ای که بسته شما را نصب می کند می گوید کجا منابع خاص را پیدا کند

require: "./dist/index.js": این فیلد مسیر ماژول CommonJS را مشخص می کند که زمانی که شخصی به بسته شما نیاز دارد باید استفاده شود. require()

const bar = require('my-package/bar');
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

import: "./dist/index.js": این فیلد مسیر ماژول ES را مشخص می کند که وقتی شخصی بسته شما را با استفاده از آن وارد می کند، باید استفاده شود import

import bar from 'my-package/bar';
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

types: "./dist/index.d.ts": این فیلد مسیر فایل اعلان TypeScript را مشخص می کند که زمانی که شخصی می خواهد از بسته شما با TypeScript استفاده کند باید استفاده شود.

  • scripts: از این فیلد برای تعریف اسکریپت هایی استفاده می شود که می توانند با اجرای NPM اجرا شوند.

  • dependencies: این فیلد برای تعیین بسته هایی که بسته شما به آنها وابسته است استفاده می شود.

  • devDependencies: این فیلد برای تعیین بسته هایی که فقط در حین توسعه مورد نیاز هستند استفاده می شود.

  • peerDependencies: این فیلد برای تعیین بسته هایی استفاده می شود که بسته شما به آنها وابسته است اما انتظار دارد که نصب کننده قبلاً داشته باشد.

    اگر بسته خاص کتابخانه ای باشد، می توانید فرضیاتی مانند این داشته باشید react/vue کتابخانه کامپوننت، اما می‌توانید آن را برای چیزی عمومی‌تر مانند کمک‌کننده ریاضی جاوا اسکریپت خالی بگذارید.
    react،vue … نمونه های رایج وابستگی به همتایان هستند

  1. tsconfig.json: این فایل برای پیکربندی کامپایلر TypeScript استفاده می شود. این شامل اطلاعاتی در مورد نحوه کامپایل کد شما توسط TypeScript است.

  2. tsconfig.node.json: این فایل برای پیکربندی کامپایلر TypeScript برای Node.js استفاده می شود. این شامل اطلاعاتی در مورد اینکه چگونه TypeScript باید کد شما را برای Node.js کامپایل کند.

پروژه 1: پلاگین Tailwind ساده برای shadcn/ui

در این پروژه، من فقط یک راه تمیزتر برای پیکربندی می خواستم shadcn/ui tailwind comfig

و اسناد tailwind بسیار آسان و مستقیم بودند، تنها کاری که باید انجام می‌دادم صادر کردن بود plugin با پیکربندی اولیه مورد نظر من عمل کنید

src/index.ts

import plugin from 'tailwindcss/plugin'
export default plugin(
    function () { },
    {
        content: [
            './node_modules/shadcn-fe-ui/dist/**/*.{js,ts,jsx,tsx}',
        ],
        theme: {
            container: {
                center: true,
                padding: "2rem",
                screens: {
                    "2xl": "1400px",
                },
            },
        }
        }
        )

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و می دویم tsup برای ساختن با tsup config

import { defineConfig } from 'tsup';

export default defineConfig({
  dts: true, // Generate .d.ts files
  minify: true, // Minify output
  sourcemap: true, // Generate sourcemaps
  treeshake: true, // Remove unused code
  splitting: true, // Split output into chunks
  clean: true, // Clean output directory before building
  outDir:"dist", // Output directory
  entry: ['src/index.ts'], // Entry point(s)
  format: ['esm'], // Output format(s)
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • dts: این فیلد مشخص می کند که تولید شود یا نه d.ts فایل های کد TypeScript شما.
  • minify: این فیلد مشخص می کند که خروجی ساخت خود را کوچک کنید یا خیر.
  • sourcemap: این فیلد مشخص می کند که آیا نقشه های منبع برای ساخت شما تولید شود یا خیر.
  • treeshake: این فیلد مشخص می کند که آیا کدهای استفاده نشده را از بیلد خود حذف کنید یا خیر.
  • splitting: این فیلد مشخص می کند که آیا خروجی خود را به قطعات تقسیم کنید یا خیر.
    clean: این فیلد مشخص می کند که دایرکتوری خروجی قبل از ساخت پاک شود یا خیر.
  • outDir: این فیلد دایرکتوری خروجی ساخت شما را مشخص می کند.
  • entry: این فیلد نقطه(های) ورودی ساخت شما را مشخص می کند.
  • format: این فیلد فرمت(های) خروجی ساخت شما را مشخص می کند.

را tsup config را نیز می توان در تعریف کرد package.json

از آنجایی که این یک بسته ساده است که فقط یک فایل را صادر می کند، داشتن یک نقطه ورودی تنها خوب است. پس از ساخت شما یک dist پوشه

  • index.js کد واقعی را دارد
  • index.d.ts انواع تایپ را دارد
  • index.js.map نقشه های منبع را دارد

که با آنچه ما در خود تعریف کرده ایم مطابقت دارد package.json

  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پس از انتشار در NPM می توانیم آن را نصب کرده و مانند آن استفاده کنیم

 npm i shadcn-fe-tw
 yarn add shadcn-fe-tw
 pnpm add shadcn-fe-tw
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

export default {
  content: [
    // vite
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}"
        // next
    "app/**/*.{ts,tsx}",
    "components/**/*.{ts,tsx}",
  ],

plugins: [
    require("tailwindcss-animate"),
    require("shadcn-fe-tw")
  ],
};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه 2: اصلاح شد shadcn/ui CLI

این یکی نیز شبیه به اول است اما دارای فایل های بیشتری است که همه به فایل ورودی وارد می شوند

و سپس ما و اصلی خود را اعلام می کنیم dts در package.json

"type": "module",
  "exports": "./dist/index.js",
  "bin": "./dist/index.js",
    "files": [
      "dist"
    ]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

را bin میدان در package.json فایل برای تعیین محل فایل های اجرایی که باید در PATH نصب شوند استفاده می شود. این یک نقشه از نام فرمان به نام فایل محلی است. هنگامی که بسته ای را با یک bin مشخص نصب می کنید، NPM این کار را انجام می دهد symlink آن فایل به پیشوند/بین برای نصب های جهانی یا ./node_modules/.bin/ for local installs12

پروژه 3: shadcn/ui اجزای موجود در NPM

در این پروژه من ساختم shadcn/ui اجزای یک بسته NPM قابل نصب

36 جزء در مجموع استفاده از چندین نقطه ورودی را ایده خوبی می کند

ابتدا کامپوننت ها را در پوشه ها مرتب می کنیم و سپس آنها را وارد می کنیم src/index.ts

در مورد من، من از ابزار cli برای دانلود تمام کامپوننت ها استفاده کردم src/shadcn ، سپس از یک اسکریپت برای سازماندهی آنها در پوشه ها استفاده کردم

ساختار پوشه مورد انتظار باید باشد src/components/[component name]

و داخل کامپوننت کارگردان خواهیم داشت

  • index.ts : برای صادرات همه چیز از
  • [compnent name].tsx : جزء واقعی
  • [component name].stories.ts : داستان جزء.
  • هر فایل مرتبط دیگری

و با آن ساختار پوشه می توانیم آن را به آن وارد کنیم src/index.ts به عنوان نقطه ورود اصلی
در این مرحله می توانیم آن را در قسمت اعلام کنیم package.json

  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما ما می‌توانیم آن را بیشتر بهینه‌سازی کنیم، از آزمایش‌های من که با این تنظیمات آنطور که هست، همه چیز وارد می‌شود وقتی شما

import { Button } from "shadcn-fe-ui";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برخی از مؤلفه‌های موجود در این فهرست دارای قلاب‌ها و سایر کنترل‌کننده‌های رویداد هستند که در برنامه بعدی مجاز نیستند!3 appDir بدون افزودن یک "use client" بخشنامه

این بدان معناست که استفاده از آن به این صورت ما را از هرگونه دستاوردی که RSCها با مجبور کردن مؤلفه‌ها به یک مؤلفه مشتری به ما می‌دهند، منصرف می‌کند.

خوشبختانه tsup می تواند چندین نقطه ورودی را وارد کند و چندین فایل را در ما ایجاد کند dist دایرکتوری، به ما کمک می کند تا یک مؤلفه را با قلاب جدا کنیم تا بتوان آن را مانند آن نصب کرد

import { ClientButton } from "shadcn-fe-ui/client-button";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ابتدا باید نقاط ورودی جدید خود را به آن اضافه کنیم tsup.config.ts

import { defineConfig } from 'tsup';

export default defineConfig({
  dts: true, // Generate .d.ts files
  minify: true, // Minify output
  sourcemap: true, // Generate sourcemaps
  treeshake: true, // Remove unused code
  splitting: true, // Split output into chunks
  clean: true, // Clean output directory before building
  outDir:"dist", // Output directory
  entry: ['src/index.ts', 'src/client-buttonts'], // Entry point(s)
  format: ['esm'], // Output format(s)
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس پروژه را می سازیم و آن را تنظیم می کنیم package.json

  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
      },
    "./client-button": {
        "import":"./dist/client-button.js",
        "types":"./dist/client-button.d.ts"
        }
  }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و حالا وقتی نصب می کنیم می توانیم آن را مانند آن وارد کنیم

import { ClientButton } from "shadcn-fe-ui/client-button";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تکرار این فرآیند برای 36 جزء می تواند خسته کننده باشد، بنابراین من یک اسکریپت نوشتم تا فیلدهای مناسب را به آن اضافه کنم. package.json

همانطور که برای tsup.config.ts ما می توانیم از یک regex تطبیق الگو برای تعیین نقاط ورودی استفاده کنیم

import { defineConfig } from 'tsup';

export default defineConfig({
  dts: true, // Generate .d.ts files
  minify: true, // Minify output
  sourcemap: true, // Generate sourcemaps
  treeshake: true, // Remove unused code
  splitting: true, // Split output into chunks
  clean: true, // Clean output directory before building
  outDir:"dist", // Output directory
  entry: ['src/index.ts','src/components/**/index.ts'], // Entry point(s)
  format: ['esm'], // Output format(s)
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

من متوجه یک اشکال در این شدم که در هنگام تولید با شکست مواجه می شود d.ts و خطای مربوط به تایم اوت کارگر را پرتاب کنید.
اگر ساخت را فقط به 5 فایل محدود کنید، این اشکال ظاهر نمی شود

اما یک تابع ساخت را ارائه می دهد که به ما اجازه می دهد برنامه ای بسازیم.
بنابراین من از آن برای دسته بندی بیلدهای 3 در یک زمان استفاده کردم (شما می توانید تا 7 بالا بروید).

import glob from "glob"
import { build } from 'tsup'
import _ from 'lodash';

async function buildStage({ clean, entry }) {
    console.log("🚀 ~ building entry ", entry)

    try {
        await build({
            dts: true,
            minify: true,
            sourcemap: true,
            treeshake: true,
            // splitting: true,
            outDir: 'dist',
            clean,
            entry,
            external: ['react', 'react-dom'],
            format: ['esm'],
        });
    } catch (error) {
        console.log("🚀 ~ error while building entries :", entry);
        console.log(error);
        throw error;
    }
}

export async function buildAllStages() {

    const root_file = glob.sync('src/index.ts');
    const files = glob.sync('src/components/**/index.ts');
    const chunkSize = 3;
    const chunks = _.chunk(files, chunkSize);

        for await (const [index, chunk] of chunks.entries()) {
      console.log('🚀 ~ chnk === ', chunk);
        await buildStage({ clean:index===0, entry: chunk });
    }
    await buildStage({ clean:false, entry: root_file });
    //    await buildStage({ clean:true, entry: root_file });


}



export function invokeBuild(){

buildAllStages().then(()=>{
    console.log("🚀 ~ buildAllStages success");
}).catch((error)=>{
    console.log("🚀 ~ buildAllStages error === ", error);
})
}
invokeBuild()

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و یک اسکریپت جدید را به ما اضافه کرد package.json

  "scripts": {
          "build": "node scripts/tsup-build-stages.js",
  }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اکنون باید اجزای خود را به عنوان یک بسته NPM قابل نصب دریافت کنیم

افکار نهایی

  • tsup یک ابزار ساخت عالی است و به شما امکان می دهد تا با حداقل پیکربندی بسیار دور بروید، آنها همچنین تنها ابزار ساخت بدون درز هستند d.tsتولید با ابزار دیگری که از شما می خواهد استفاده کنید tsc به طور مستقیم برای d.ts خروجی ها. اشاره های قابل توجه شامل

    • bun: ساخت عالی پیکربندی بسیار آسان، اما نه d.ts خروجی
    • بسته: من نتوانستم بفهمم چگونه چند نقطه ورود را انجام دهم و پیکربندی آنها باید در یکی باشد package.json یا الف .parcel فایلی که پیشنهاد کدی را ارائه نمی دهد و کاوش گزینه ها را دشوار می کند.
    • vite : vite عالی می سازد، دارای یک d.ts پلاگین اما خروجی های آن کم است، آنها همچنین انجام چندین نقطه ورودی آسان ندارند و باید به صورت دستی فایل ها را مشخص کنید بدون الگوی regex مطابقت داشته باشند و حتی با استفاده از یک تابع مسیرهایی را ایجاد کنید که هنوز به همان شکل نبوده است. آسان به عنوان tsup

تنها چیزی که مدتی طول کشید تا سرم را به اطراف بپیچم، رابطه بین آن ها بود build tool config و package.json .

  • build tool config اراده:

    • نقاط ورود را مشخص کنید
    • دایرکتوری خروجی را مشخص کنید
    • فرمت خروجی را مشخص کنید
    • نقشه منبع را مشخص کنید
    • minify را مشخص کنید یا خیر
    • تقسیم یا نه را مشخص کنید
    • package.json اراده:
    • نام پروژه را مشخص کنید
    • نسخه پروژه را مشخص کنید
    • محل خروجی/صادرات را مشخص کنید
    • محل پیدا کردن انواع مختلف را مشخص کنید (import:esm ، require:cjs ،…)
    • مکان انواع را مشخص کنید

منابع

shadcn/ui : پروژه بزرگی که الهام بخش من شد

راهنمای ساده: راهنمای اولیه ایجاد و انتشار یک کتابخانه NPM

npm + release-it: آموزش ساده در مورد نحوه انتشار و خودکار کردن انتشار یک npm بسته ها

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا