使用 PNPM 从零搭建 Monorepo,测试组件并发布

news2024/12/28 18:06:11

1 目标

通过 PNPM 创建一个 monorepo(多个项目在一个代码仓库)项目,形成一个通用的仓库模板。

这里以在该 monorepo 项目中搭建 web components 类型的组件库为例,介绍从仓库搭建、组件测试到组件发布的整个流程。

这个仓库既可以用于公司存放和管理所有的项目,也可以用于将个人班余的所有积累整合其中。

2 环境要求

核心是 PNPM 和 Node.js,没有特殊的版本要求,只要他俩能对应上即可。
在这里插入图片描述

当前项目使用的 PNPM 版本为 9.3.0,Node.js 为 18.20.3。

除了以上两个,项目中也使用到了以下工具或插件,可以按需添加,如不使用则不用考虑其环境要求。
vite(v5.2.0):主要用于项目运行和构建,要求 Node.js v18+ 或者 v20+。
Storybook(v8.1.7):用于组件的测试和展示,要求 Vite v4.0 +。

3 仓库搭建

3.1 新建项目

新建一个文件夹作为项目容器。

这里起名叫 ease-life,意为轻松生活。所有的学习、工作都是为了更好地、更轻松的生活。

3.2 创建目录

3.2.1 apps

在项目根目录下创建 apps 文件夹。
在 apps 下创建 storybook 文件夹。用于测试和展示自定义的 web components。

apps 文件夹主要存放应用程序,如:Storybook、VitePress,还可以加上 vue-test、react-test 来对 web components 做测试。

3.2.2 packages

在项目根目录下创建 apps 文件夹。
在 packages 下分别创建 config(配置信息)、web-components(实现组件与框架无关) 文件夹。

  • 在 config 文件下创建 eslint、stylelint、commitlint 以及 typescript,用于存放对应的配置
  • 在 web-components 创建 text 文件夹,实现一个简单的文本组件。 text 文件夹下创建 src 文件夹。

packages 底下主要包含插件、组件、命令行、类库等,除了以上的内容还可以按需加上 vue-components、react-components、cli、map-library 等等。

形成的目录结构如下:

ease-life
|-- apps
|   |-- storybook
|-- packages
    |-- config
    |   |-- commitlint
    |   |-- eslint
    |   |-- stylelint
    |   |-- typescript
    |-- web-components
        |-- text
            |-- src

3.3 添加文件

3.3.1 PNPM 相关

  1. 在项目根目录下添加文件:pnpm-workspace.yaml,定义 PNPM 的工作空间:
packages:
  # 匹配 packages 目录下(任意文件夹下)的所有模块
  - 'packages/**'
  # 匹配 apps 直接子文件夹下的所有模块
  - 'apps/*'

这里的模块,说的是:包含 package.json,可以被发布到 NPM 远程仓库的项目。

  1. 在项目根目录下添加文件:.npmrc,定义 PNPM 的配置项:
# 允许链接工作空间中的包
link-workspace-packages = true

# 在引用工作空间中的包时,设置前缀为 *,即:使用最新版本的包
save-prefix = ''

3.3.2 Vite 相关

  1. 在根目录下运行以下内容:
pnpm init

从而生成 package.json,如下:

{
  "name": "ease-life",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified1\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
}
  1. 在 web-components 以及 web-components/text 下都执行 pnpm init,或直接将根目录下的 package.json 拷贝过去。

本文的目的是要每个组件都能够被单独被发布至 NPM 仓库,如:@ease-life/text。如只需要做整个组件库的发布,则无需在 web-components/text 下执行 pnpm init。

  1. 在项目最外层空间下添加 vite:
pnpm add vite -Dw

packages 里的所有模块如无特殊情况,统一使用 vite 来运行、打包,因此只需要在项目最外层安装一次即可。

  1. 在项目根目录下,添加文件 vite.config.js:
import { defineConfig } from 'vite'

export default defineConfig({
    build: {
        lib: {
            entry: 'index.ts',
            fileName: 'index'
        },
    }
})
  1. 修改之前生成的 package.json:
{
  "name": "ease-life",
  "version": "1.0.0",
  "description": "哥的幸福生活全靠你啦",
  "scripts": {
    "dev": "vite --open",
    "build": "vite build",
    "preview": "vite preview --open"
  },
  "keywords": [
    "monorepo",
    "web components",
    "pnpm",
    "storybook",
    "changeset"
  ],
  "author": "zqc",
  "repository": {
    "type": "git",
    "url": ""
  },
  "license": "MIT",
  "type": "module",
  "devDependencies": {
    "vite": "^5.2.0"
  },
  "engines": {
    "node": ">= 18.20.3",
    "pnpm": ">= 9.3.0"
  }
}

3.3.3 添加 config

待完善

3.3.4 添加其他

  1. 在项目跟目录下添加 .gitignore:
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3.3.5 自定义 Web Components

  1. 在 packages/web-components/text/src 下创建 text.ts:
import { html, css, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('el-text')
export class ELText extends LitElement {
    static styles = css`p { color: blue }`;


    @property()
    name = 'Somebody';

    render() {
        return html`<p>Hello, ${this.name}!</p>`;
    }
}
  1. 在 packages/web-components/text 下创建 index.ts(导出当前组件):
export { ELText as default } from './src/text.ts';
  1. 在 packages/web-components/text 下添加 tsconfig.json:
{
  "compilerOptions": {
    "target": "ESNext",
    "experimentalDecorators": true,
    "useDefineForClassFields": false,
    "module": "ESNext",
    "lib": [
      "ES2020",
      "DOM",
      "DOM.Iterable"
    ],
    "skipLibCheck": true,
    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "src"
  ]
}

以上内容将会被移至 packages/config/typescript 中,待修改

  1. 修改 在 packages/web-components/text 下的 package.json:
{
  "name": "@ease-life/text",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "files": [
    "dist"
  ],
  "main": "./dist/index.umd.cjs",
  "module": "./dist/index.js",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.umd.cjs"
    }
  },
  "scripts": {
    "build": "vite build -c ../../../vite.config.js"
  },
  "keywords": [
    "ELText"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lit": "^3.1.2"
  }
}

3.4 生成 storybook

  1. 在 apps/storybook 文件夹的路径下运行以下内容:
pnpm dlx storybook@latest init

选择最后一个选项,回车。
在这里插入图片描述
此时就会在 apps/storybook 下有对应的 storybook 的内容。

  1. 删除 apps/storybook/src/stories 下自带的 button.css、Button.stories.ts、Button.ts、header.css、Header.stories.ts、Header.ts、page.css、Page.stories.ts、Page.ts 六个文件。

最终项目文件目录结构如下:

ease-life
    |-- .gitignore
    |-- .npmrc
    |-- package.json
    |-- pnpm-lock.yaml
    |-- pnpm-workspace.yaml
    |-- vite.config.js
    |-- apps
    |   |-- package.json
    |   |-- storybook
    |       |-- .gitignore
    |       |-- index.html
    |       |-- package.json
    |       |-- tsconfig.json
    |       |-- .storybook
    |       |   |-- main.ts
    |       |   |-- preview.ts
    |       |-- public
    |       |   |-- vite.svg
    |       |-- src
    |           |-- index.css
    |           |-- my-element.ts
    |           |-- vite-env.d.ts
    |           |-- assets
    |           |   |-- lit.svg
    |           |-- stories
    |               |-- Configure.mdx
    |               |-- Text.stories.ts
    |               |-- assets
    |                   |-- accessibility.png
    |                   |-- accessibility.svg
    |                   |-- addon-library.png
    |                   |-- assets.png
    |                   |-- avif-test-image.avif
    |                   |-- context.png
    |                   |-- discord.svg
    |                   |-- docs.png
    |                   |-- figma-plugin.png
    |                   |-- github.svg
    |                   |-- share.png
    |                   |-- styling.png
    |                   |-- testing.png
    |                   |-- theming.png
    |                   |-- tutorials.svg
    |                   |-- youtube.svg
    |-- packages
        |-- config
        |   |-- commitlint
        |   |-- eslint
        |   |-- stylelint
        |   |-- typescript
        |-- map-library
        |-- web-components
            |-- text
                |-- index.ts
                |-- package.json
                |-- tsconfig.json
                |-- src
                    |-- text.ts

4 组件测试

  1. 在项目根目录下运行以下内容,来对 text 进行构建:
pnpm -F @ease-life/text build

会在 packages/web-components/text 下生成 dist 文件夹,里边有 index.js(ESM) 以及 index.umd.cjs(CommonJS)。

  1. 在 apps/storybook/src/stories 下添加一个 Text.stories.ts:
import type { Meta, StoryObj } from '@storybook/web-components';
import '@ease-life/text';


const meta: Meta = {
    component: 'el-text'
};

export default meta;
type Story = StoryObj;

export const Default: Story = {
    args: {
        name: 'world',
    },
};
  1. 修改 apps/storybook 下的 package.json,将其中的 name 改为:
  "name": "@ease-life/storybook",
  1. 在项目根目录下运行以下内容来安装刚才定义的 web components:
pnpm -F @ease-life/storybook add @ease-life/text
  1. 在项目根目录下运行以下内容,来启动 storybook:
pnpm -F @ease-life/storybook storybook

在浏览器中显示以下内容,则证明组件没有问题了。
在这里插入图片描述

5 组件发布

5.1 在 NPM 官网注册

如果没有注册过,则打开 NPM,点击右上角的 Sign Up,按提示填入信息。

5.2 登录账户

注册完后直接登录。

5.3 创建组织

打开创建组织的页面,在其中添加组织名称,组织名称就是 scope 的名称,也就是这里 @ 后面的内容。

@ease-life/tex,我这里创建了 ease-life 的组织。

5.3 组件发布

  1. 用户登录,在项目根目录下运行:
pnpm login

看到提示后,再次回车,在浏览器弹出的页面中进行登录,成功后显示以下内容:
在这里插入图片描述

  1. 组件发布,在项目根目录下运行:
pnpm publish -r

会自动发布仓库中版本发生改变的组件。
在这里插入图片描述
出现以上类似内容,就证明发布成功了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1819223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

eclipse 老的s2sh(Struts2+Spring+Hibernate) 项目 用import导入直接导致死机(CPU100%)的解决

1、下载Apache Tomcat - Apache Tomcat 8 Software Downloads 图中是8.5.100的版本&#xff0c;下面的设置用的是另一个版本的&#xff0c;其实是一样。 2、先将Server配好&#xff0c;然后再进行导入操作。 2、选择jdk 当然&#xff0c;这里也可以直接“Download and instal…

Vue指令的使用以及自定义指令

文章目录 1. 指令概述2.常用指令的分类2.1 内容渲染指令2.1.1 v-text2.1.2 插值语法{{ }}2.1.2.1 插值语法{{}}可以写什么2.1.2.2 插值语法可以解决什么 2.1.3 v-html 2.2 属性绑定指令(v-bind)2.3 事件绑定指令(v-on)2.4 双向绑定指令(v-model)2.4.1 v-bind和v-model的区别和联…

【Java】解决Java报错:IllegalStateException during HTTP Request

文章目录 引言一、IllegalStateException的定义与概述1. 什么是IllegalStateException&#xff1f;2. IllegalStateException在HTTP请求中的常见触发场景3. 示例代码 二、解决方案1. 确保响应只被提交一次2. 正确管理Servlet的生命周期3. 避免重复访问输入流和输出流4. 使用框架…

光储充行业市场前景以及未来发展趋势

光储充行业作为新能源汽车产业链的重要环节&#xff0c;其市场前景广阔&#xff0c;未来发展趋势也呈现出积极向上的态势。随着新能源汽车市场的不断扩大&#xff0c;光储充设施的需求也日益增长&#xff0c;为行业的发展提供了强大的动力。 从未来发展趋势来看&#xff0c;光储…

基于FreeRTOS+STM32CubeMX+LCD1602+MCP4251(SPI接口)的数字电位器Proteus仿真

一、简介&#xff1a; MCP4251是一款SPI接口的8位数字电位器。 二、主要技术参数&#xff1a; 属性参数值抽头数257接口类型SPI通道数2供电电压1.8V~5.5V精度20%温度系数(典型值)150ppm/℃工作温度-40℃~125℃ 三、引脚定义 &#xff1a; 四、内部原理框图&#xff1a; 五、电…

Github2024-06-12 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4JavaScript项目2Lua项目1PHP项目1Blade项目1非开发语言项目1TypeScript项目1Shell项目1从零开始构建你喜爱的技术 创建周期:2156 天…

爬虫-电影影评爬取

先上代码 import requests import timeheaders {"referer": "http://movie.mtime.com/","user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36" } fo…

linux 安装sftp及使用sftp上传和下载

一、centos7 安装sftp 1.安装 OpenSSH 服务&#xff1a; sudo yum install openssh-server2.启动 SSH 服务&#xff0c;并设置为开机启动&#xff1a; sudo systemctl start sshd sudo systemctl enable sshd3.创建一个新用户&#xff0c;用于SFTP连接&#xff08;替换your_…

Go基础编程 - 06 - 指针

指针 1.1 指针类型、指针地址1.2 指针取值&#xff08;*&#xff09;1.3. 空指针1.4. 语法糖1.5. new()、make() 上一篇&#xff1a;数组与切片 下一篇&#xff1a;map 及其约束 Go不能进行偏移或运算&#xff0c;是安全指针。取址符&#xff08;&&#xff09;&#xff0c…

嵌套查询(二)-谓词EXISTS实现嵌套查询

一、EXISTS谓词 1、作用&#xff1a;用于判断一个子查询的结果是否为空 2、使用语法&#xff1a; 【NOT】EXISTS&#xff08;子查询&#xff09; 语义&#xff1a;如果子查询的查询结果不为空&#xff0c;则EXISTS为真&#xff0c;否则为假 二、举例 1、举例1&#xff1a…

有没有硅基生命?AGI在哪里?

摘要 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;和生命科学的探索逐渐成为人们关注的焦点。其中&#xff0c;关于硅基生命的可能性与AGI&#xff08;Artificial General Intelligence&#xff0c;即人工通用智能&#xff09;的实现&#xff0c;更是引…

函数式开发接口( Consumer、Function)在实际开发中的应用场景

之前有个扫码下载文件需求&#xff0c;由于要同时进行记录下载人的记录。一开始用的是异步进行日志记录。发现有的用户扫码下载了一次文件&#xff0c;日志记录了三条。这种很容易联想到是因为网络抖动造成的。 问题代码 由于日志记录是异步的&#xff0c;文件下载需要时间。同…

场外期权能不能开户?场外期权在哪里开?

今天带你了解场外期权能不能开户&#xff1f;场外期权在哪里开&#xff1f;近年来&#xff0c;场外期权交易在金融市场上逐渐盛行起来。有许多人对于场外期权的开户问题感到困惑。 场外期权能不能开户&#xff1f; 资质要求&#xff1a; 个人投资者需要具备一定的金融知识和投…

Excel根据身份证号提取信息

概览 本篇文章主要对根据身份证号码提取出生年月日、年龄、性别、退休年龄这三项进行讲解。 一. 提取出生年月日 公式&#xff1a;TEXT(MID(B2,7,8),“0000-00-00”) MID(B2,7,8)&#xff1a;表示从单元格 B2 中的字符串&#xff08;这里是身份证号&#xff09;&#xff0c…

SolarONE:新一代开放灵活高效的车载网络仿真测试软件

在当前汽车设计中&#xff0c;车载总线系统扮演着至关重要的角色&#xff0c;它连接了车辆内部的各种电子控制单元&#xff08;ECU&#xff09;&#xff0c;实现了数据的传输与交换。为了更好地理解和优化车载总线系统&#xff0c;软件工具的使用变得至关重要。 本文将介绍一款…

二十三、生成帮助文档

二十一、Java工具类的创建 二十二、Jar包制作及使用 这一篇开始学习如何生成帮助文档。为什么要学习生成帮助文档&#xff1f; 1、工具类已经制作好了&#xff0c;Java工具类的创建的类是一个.java文件&#xff0c;编译后成.class文件看不懂&#xff0c;所以需要对应的帮助文档…

光伏电站阵列式冲击波声压光伏驱鸟器

光伏电站内鸟群的聚集可不是一件好事&#xff0c;鸟类排泄物&#xff0c;因其粘度大、具有腐蚀性的特点&#xff0c;一旦堆积在太阳能板上&#xff0c;会严重影响光伏电站的发电效率。长期积累的鸟粪不仅难以清洗&#xff0c;还可能引发组件的热斑效应&#xff0c;严重时甚至可…

大模型推理优化

什么是大模型推理 **大模型推理其实就是大模型如何输出&#xff0c;怎么输出&#xff0c;输出什么的过程。**在人工智能的基础模型下&#xff0c;各种推理任务涵盖了多个领域&#xff0c;包括常识推理、数学推理、逻辑推理、因果推理、视觉推理、听觉推理、多模态推理和代理推…

【SpringBoot+Vue】后端代码学习记录——登录功能开发

参考资料 https://www.cnblogs.com/three-fighter/p/14332288.html 零、前端开发 略。之前有过。 一、后端开发 1、统一结果封装 创建了一个 Result 类&#xff0c;用于异步统一返回的结果封装。一般来说&#xff0c;结果里面有几个要素必要的 是否成功&#xff0c;可用 c…

哪个牌子的儿童护眼灯好?五款平价护眼台灯推荐

护眼台灯在近年来成为家长和长时间使用电子设备人群关注的家电/学生产品。对于家中有孩子或经常面对电子屏幕的人士来说&#xff0c;很多人可能已经对这类产品有所了解并进行了购买。然而&#xff0c;部分家长对护眼台灯的认识还不够深入&#xff0c;因此尚未为孩子选择一款合适…