Home >Web Front-end >JS Tutorial >Build and run your project in Monorepo with PNPM

Build and run your project in Monorepo with PNPM

Patricia Arquette
Patricia ArquetteOriginal
2025-01-19 16:33:12313browse

Build and run your project in Monorepo with PNPM

Project code organization: Polyrepo vs. Monorepo

There are two main ways to organize project code:

  1. Polyrepo: Spread project code in different repositories. This is the current standard practice. Multiple teams have their own warehouses, build products and pipelines, and have autonomy.
  2. Monorepo: Centralizes all project code in one repository. Projects are divided into applications and packages to facilitate code reuse and share standard functionality, which helps unify third-party package versions, recursively build source code for dependencies, and use the same tools for CI/CD pipelines.

Monorepo is used by many large tech companies, such as Yandex. Monorepo helps share ideas and best practices among teams and business units. For more information about Monorepo and related tools, please visit https://www.php.cn/link/b01ccf4f29b57b0b1bdb9407050db28d. This article will focus on a simple solution to build Monorepo using PNPM.

PNPM uses workspaces to integrate multiple projects in a single repository.

Environment setup

First, create an empty folder as a new Monorepo project. Initialize PNPM in the warehouse:

<code class="language-bash">pnpm init</code>

Then, create a pnpm-workspace.yaml file describing the package folder:

<code class="language-yaml">// pnpm-workspace.yaml

packages:
  - 'packages/**'
  - 'apps/**'</code>
The

/packages folder holds shared libraries, and the /apps folder holds applications (for example, a standalone React Native mobile application and a web application that uses the same component or connection library to communicate with the API server).

This article will use a Telegram publishing robot as an example. Its source code is located on GitHub: https://www.php.cn/link/8164ca2fe04767628ac1c6813e8a0867. Download and extract to /apps/publish-bot folder, then run the installation command:

<code class="language-bash">pnpm install</code>

Create Telegram Toolkit

Create a folder named /packages in the telegram-utils folder and initialize PNPM and TypeScript:

<code class="language-bash">pnpm init && pnpm add -D typescript && pnpm tsc --init</code>

This package will provide a function for combining text and captions from all messages (text, video and photo). Telegraf package needs to be installed:

<code class="language-bash">pnpm add telegraf</code>

All source code should be located in the /src directory. To facilitate functional grouping, it is recommended to create different folders. The function of combining text is located in the /texts folder, the code is as follows:

<code class="language-typescript">// packages/telegram-utils/src/texts/combineTexts.ts

import { Message } from 'telegraf/types';
import { FmtString, join } from 'telegraf/format';

type GroupedMessages = {
    photos: Array<Message.PhotoMessage>;
    videos: Array<Message.VideoMessage>;
    text: Array<Message.TextMessage>;
};

export const combineTexts = ({ photos, videos, text }: GroupedMessages) => {
    const photoTexts = photos
        .map(photo => photo.caption ? new FmtString(photo.caption, photo.caption_entities) : undefined)
        .filter((t): t is Required<FmtString> => t !== undefined);

    const videoTexts = videos
        .map(video => video.caption ? new FmtString(video.caption, video.caption_entities) : undefined)
        .filter((t): t is Required<FmtString> => t !== undefined);

    const allTexts = [];

    if (text.length) allTexts.push(join(text.map(t => new FmtString(t.text, t.entities))), '\n');
    if (photoTexts.length) allTexts.push(join(photoTexts, '\n'));
    if (videoTexts.length) allTexts.push(join(videoTexts, '\n'));

    return join(allTexts, '\n');
};</code>

Code description:

  • The function inputs messages grouped by type: photo, video or text;
  • Media messages should be converted to FMT strings containing title and title entities. For subsequent filtering, undefined should be returned;
  • Concatenate text arrays step by step, eventually combining all texts into one large message.

Create an index file for the /texts folder:

<code class="language-bash">pnpm init</code>

Use the package.json field in the exports file to set up the export of the package function:

<code class="language-yaml">// pnpm-workspace.yaml

packages:
  - 'packages/**'
  - 'apps/**'</code>

To identify Monorepo packages in your application, add the prefix @monorepo to all packages. Rename the package.json package in the telegram-utils file:

<code class="language-bash">pnpm install</code>

Add build script:

<code class="language-bash">pnpm init && pnpm add -D typescript && pnpm tsc --init</code>

Complete package.json file:

<code class="language-bash">pnpm add telegraf</code>

Configure the TypeScript compiler: enable incremental compilation to save build time and process only changed parts; enable compound compilation to use project references. Define the /src folder as rootDir and the package's outDir as /dist. Updated tsconfig.json:

<code class="language-typescript">// packages/telegram-utils/src/texts/combineTexts.ts

import { Message } from 'telegraf/types';
import { FmtString, join } from 'telegraf/format';

type GroupedMessages = {
    photos: Array<Message.PhotoMessage>;
    videos: Array<Message.VideoMessage>;
    text: Array<Message.TextMessage>;
};

export const combineTexts = ({ photos, videos, text }: GroupedMessages) => {
    const photoTexts = photos
        .map(photo => photo.caption ? new FmtString(photo.caption, photo.caption_entities) : undefined)
        .filter((t): t is Required<FmtString> => t !== undefined);

    const videoTexts = videos
        .map(video => video.caption ? new FmtString(video.caption, video.caption_entities) : undefined)
        .filter((t): t is Required<FmtString> => t !== undefined);

    const allTexts = [];

    if (text.length) allTexts.push(join(text.map(t => new FmtString(t.text, t.entities))), '\n');
    if (photoTexts.length) allTexts.push(join(photoTexts, '\n'));
    if (videoTexts.length) allTexts.push(join(videoTexts, '\n'));

    return join(allTexts, '\n');
};</code>

Integration

Go back to /apps/publish-bot and add the @monorepo/telegram-utils package to dependencies. Note that you do not need to specify the package version, use workspace:* to indicate:

<code class="language-typescript">// packages/telegram-utils/src/texts/index.ts

export * from './combineTexts';</code>

Install dependencies:

<code class="language-json">// packages/telegram-utils/package.json

"exports": {
    "./texts": {
        "import": "./src/texts/index.ts",
        "require": "./dist/texts/index.js"
    }
}</code>

Update the preview command for publishing bots:

<code class="language-json">// packages/telegram-utils/package.json

"name": "@monorepo/telegram-utils"</code>

Update/apps/publish-bot/tsconfig.json:

<code class="language-json">// packages/telegram-utils/package.json

"scripts": {
    "build": "tsc -p tsconfig.json"
}</code>

Before building the application code, you need to build all dependencies:

<code class="language-json">// packages/telegram-utils/package.json

{
    "name": "@monorepo/telegram-utils",
    "version": "1.0.0",
    "main": "index.js",
    "scripts": {
        "build": "tsc -p tsconfig.json"
    },
    "keywords": [],
    "license": "ISC",
    "exports": {
        "./texts": {
            "import": "./src/texts/index.ts",
            "require": "./dist/texts/index.js"
        }
    },
    "devDependencies": {
        "typescript": "^5.7.3"
    },
    "dependencies": {
        "telegraf": "^4.16.3"
    }
}</code>

Summary

The release bot now uses internal shared libraries/packages and is located in Monorepo. This makes it possible to quickly build new features and reuse code across multiple applications.

Image via Gabriel Heinzer on Unsplash

The above is the detailed content of Build and run your project in Monorepo with PNPM. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn