WangShuXian6 / blog

FE-BLOG
https://wangshuxian6.github.io/blog/
MIT License
46 stars 10 forks source link

preconstruct #129

Open WangShuXian6 opened 2 years ago

WangShuXian6 commented 2 years ago

preconstruct

在 monorepos 中轻松开发和构建您的代码 暂不支持css等样式文件的打包

https://www.npmjs.com/package/@preconstruct/cli

https://github.com/preconstruct/preconstruct

https://preconstruct.tools/

WangShuXian6 commented 2 years ago

preconstruct for single package repos [单包]

WangShuXian6 commented 2 years ago

preconstruct for js monorepos [多包]

初始化仓库

mkdir preconstruct-demo
cd preconstruct-demo
yarn init
// 按提示输入仓库信息

设置仓库的 Yarn Workspaces

package.json添加 workspaces字段 将private设置为true防止将仓库意外发布 @my 为 @ + npm账户名称

{
"name": "@my",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
],
"license": "MIT"
}

创建修复脚本

在仓库根目录package.json 添加 修复脚本 该命令可以交互式的修复大部分的配置问题 package.json

"scripts": {
"fix":"preconstruct fix"
}

项目错误修复

yarn fix

packages 目录下创建两个示例包 a,b

创建一个packages/a目录和packages/b目录

在两个包目录中分别运行yarn init并对所有问题回答是


cd preconstruct-demo/packages/a
yarn init

cd preconstruct-demo/packages/b yarn init

>修改包名为 `@my/a` `@my/b`

### 创建源文件
>packages/a/src/index.js
```js
export default function () {
  console.log("a");
}

packages/b/src/index.js

export default function () {
console.log("b");
}

安装和设置 Preconstruct 来构建所有的包

在仓库根目录执行 以安装依赖 参数​-W​ 指定 Yarn 为整个 workspace 安装指定依赖。 所有依赖为组件间共享组件。 在工作空间的根将允许在根 package.json 的脚本中使用

yarn add @preconstruct/cli -W

在仓库根目录执行 以设置包

yarn preconstruct init

按提示使用空格键选中需要 preconstruct 处理的包 a,b

手动添加umd配置

注意:parcel 会再次编译umd包导致错误,无法取得全局变量 packages/a/package.json下添加配置

"umd:main": "dist/my-a.umd.min.js",
"preconstruct": {
"umdName": "My.a"
}

packages/b/package.json下添加配置

"umd:main": "dist/my-b.umd.min.js",
"preconstruct": {
"umdName": "My.b"
}

preconstruct.umdName将指定附加到浏览器window对象上的库的变量名称 例如 a 库将通过 window.My.a 使用 例如 b 库将通过 window.My.a 使用

配置 preconstruct dev

preconstruct dev是 Preconstruct 中的一个命令,它可以让你导入你的包, 但不需要实时将包的源文件编译到dist目录供其他包导入

把它添加到安装后脚本中,以便yarn在安装包后运行它 在仓库根目录的package.json添加脚本 preconstruct-demo/package.json

"scripts": {
"postinstall": "preconstruct dev",
}

添加 Babel,以便我们在编写代码时可以使用现代功能,但消费者在使用它时不会遇到问题

在仓库根目录执行

yarn add -W @babel/core @babel/preset-env

仓库根目录添加 babel.config.js

babel.config.js

module.exports = {
presets: ["@babel/preset-env"],
};

构建包 preconstruct build

在仓库根目录package.json添加脚本

"scripts": {
"build":"preconstruct build"
}

在仓库根目录运行

yarn build

将在 a,b包目录分别构建出包

导出多个入口点

可以将单个包中的功能模块单独导出,消费者可以为此做摇树优化,去除未使用的功能

注意:禁止在.npmignore中排除dist目录,否则会引发错误error @xxx/xxx the entrypoint func isn't included in the published files for this package, please add it to the files field in the package's package.json

https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package

为 a 包添加 func 功能模块 packages/a/src/func.js

export default function (){
console.log('a.func')
}

在a包的默认导出文件中使用并导出 func 功能 packages/a/src/index.js

import func from "./func";
func();
export { func };
export default function () {
console.log("a");
}

在 a 包 的package.json添加多入口配置 packages/a/package.json entrypoints文件后缀需与src目录文件后缀保持一致

"preconstruct": {
"entrypoints": [
"index.js",
"func.js"
]
}

修复func 按提示输入把func附加到window对象的上的变量名称 [例如 My.a.func]

yarn fix

这将自动创建 packages/a/func/package.json

{
"main": "dist/my-a-func.cjs.js",
"module": "dist/my-a-func.esm.js",
"umd:main": "dist/my-a-func.umd.min.js",
"preconstruct": {
"umdName": "My.a.func"
}
}

添加本地包到依赖项

将 a 包作为依赖加入b包 如果包未发布必须使用未发布的版本号 packages/b/package.json添加依赖

"dependencies": {
"@my/a": "1.0.0"
}

在b包目录下安装依赖

cd packages/b
yarn

在 b 包引入本地a包并使用

import a, { func } from "@my/a";
a();
func();
export default function () {
console.log("b");
}

修复

由于a包使用了My.aumd名称,由b包引入a包,故需要指定my 和 a 在 umd模式下的window挂载变量名称 按提示输入 名称即可,例如 My.a My

验证依赖项

添加 Manypkg 以帮助验证您的依赖项 从项目根目录运行以下命令

yarn add @manypkg/cli -W
yarn manypkg check
yarn manypkg fix

添加脚本

"scripts": {
"check": "manypkg check",
"fix:manypkg": "manypkg fix"
}

发布包

在仓库根目录安装 changesets

yarn add @changesets/cli -W

设置 changesets

yarn changeset init

这将添加一个.changeset包含您的变更集配置

更改:.changeset/config.json 的"access": "restricted"为"access": "public"(假设您想在 npm 上公开发布您的包,如果您希望它们私下发布,请不要这样做)

添加变更集 选择更改的包、更改的 ( semver )类型以及更改的描述

注意:由于types包只包含类型定义,不会监测到改动,需要在每次发包的时候修改index.ts的console方法出发该包变更[或者修改readme.md]

yarn changeset add

版本的格式 major.minor.patch 主版本号.次版本号.修补版本号 patch:修复bug,兼容老版本 minor:新增功能,兼容老版本 major:新的架构调整,不兼容老版本

“消耗”变更集 它将消耗并巧妙地组合所有存在的变更集 根据提示输入新的递增的版本号

yarn changeset version

登陆npm 需要从邮箱查看一次性密码

npm adduser

发布包

yarn preconstruct build && yarn changeset publish

在仓库根目录package.json添加脚本

"scripts": {
"postinstall": "preconstruct dev",
"build": "preconstruct build",
"release:old": "preconstruct build && yarn publish:packages",
"fix": "preconstruct fix",
"check": "manypkg check",
"fix:manypkg": "manypkg fix",
"cadd": "changeset add",
"version": "changeset version",
"release": "preconstruct build && changeset publish"
}

测试b包对a包的使用

node packages/b/dist/my-b.cjs.js

输出

a.func
a
a.func

完成后源码

package.json


{
"name": "my",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
],
"license": "MIT",
"dependencies": {
"@babel/core": "^7.16.5",
"@babel/preset-env": "^7.16.5",
"@preconstruct/cli": "^2.1.5"
},
"preconstruct": {
"packages": [
"packages/*"
],
"globals": {
"My": "My"
}
},
"scripts": {
"postinstall": "preconstruct dev",
"build": "preconstruct build",
"release:old": "preconstruct build && yarn publish:packages",
"fix": "preconstruct fix",
"check": "manypkg check",
"fix:manypkg": "manypkg fix",
"cadd": "changeset add",
"version": "changeset version",
"release": "preconstruct build && changeset publish"
}
}

>babel.config.js
```js
module.exports = {
  presets: ["@babel/preset-env"],
};

packages/a/package.json


{
"name": "@my/a",
"version": "1.0.0",
"main": "dist/my-a.cjs.js",
"module": "dist/my-a.esm.js",
"umd:main": "dist/my-a.umd.min.js",
"license": "MIT",
"preconstruct": {
"umdName": "My.a",
"entrypoints": [
"index.js",
"func.js"
]
}
}
>packages/a/src/func.js
```js
export default function (){
    console.log('a.func')
}

packages/a/src/index.js


import func from "./func";
func();
export { func };
export default function () {
console.log("a");
}

>packages/a/func/package.json 自动生成
```JSON
{
  "main": "dist/my-a-func.cjs.js",
  "module": "dist/my-a-func.esm.js",
  "umd:main": "dist/my-a-func.umd.min.js",
  "preconstruct": {
    "umdName": "My.a.func"
  }
}

packages/b/package.json

{
"name": "@my/b",
"version": "1.0.0",
"main": "dist/my-b.cjs.js",
"module": "dist/my-b.esm.js",
"umd:main": "dist/my-b.umd.min.js",
"license": "MIT",
"preconstruct": {
"umdName": "My.b"
},
"dependencies": {
"@my/a": "1.0.0"
}
}

packages/b/src/index.js

import a, { func } from "@my/a";
a();
func();
export default function () {
console.log("b");
}

image

WangShuXian6 commented 2 years ago

preconstruct for typescript monorepos [多包]

安装依赖包

在仓库根目录下

yarn add typescript @babel/preset-typescript -W

更新 bable 支持 typescript

babel.config.js

module.exports = {
presets: ["@babel/preset-typescript", "@babel/preset-env"],
};

添加 tsconfig.json

仓库根目录添加公共配置 isolatedModules 必须为 true 因为 Preconstruct 使用 Babel 编译 TypeScript 包,所以建议设置isolatedModules编译器选项以确保您的 TypeScript 源可以使用 Babel 构建。 tsconfig.json


{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"noImplicitAny":false,
},
"exclude": ["node_modules"]
}

>包a,b分别添加配置,继承公共配置
>packages/a/tsconfig.json
```JSON
{
    "extends": "../../tsconfig.json",

    "include": ["src/**/*"],

    "compilerOptions": {
      // package-specific options
      "esModuleInterop": true,
    }
  }

packages/b/tsconfig.json


{
"extends": "../../tsconfig.json",
"include": ["src/**/*"],

"compilerOptions": {
  // package-specific options
  "esModuleInterop": true,
}

}


>各包所有源码改为 `ts` 后缀

## 更新 entrypoints 文件后缀为 ts
>packages/a/package.json
```JSON
{
  "name": "@my/a",
  "version": "1.0.0",
  "main": "dist/my-a.cjs.js",
  "module": "dist/my-a.esm.js",
  "umd:main": "dist/my-a.umd.min.js",
  "license": "MIT",
  "preconstruct": {
    "umdName": "My.a",
    "entrypoints": [
      "index.ts",
      "func.ts"
    ]
  }
}

编译 tsx

安装 @babel/preset-react 在仓库根目录下

yarn add @babel/preset-react -W

更新 bable 支持 tsx

babel.config.js

module.exports = {
presets: ['@babel/preset-react', '@babel/preset-typescript', '@babel/preset-env']
}

更新入口文件后缀为 tsx

index.tsx

更新 tsconfig.json

compilerOptions.jsx="react-jsx"

WangShuXian6 commented 2 years ago

配置代码格式化 Lint for preconstruct Monorepo

react 项目 lint 规则参考 linthttps://github.com/WangShuXian6/blog/issues/132

分别为子项目安装包

以下包需要为每个 monorepo 子项目单独安装

yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks husky lint-staged  prettier stylelint stylelint-config-prettier stylelint-config-rational-order stylelint-config-standard stylelint-prettier  -D

生成 husky pre-commit hook

npx husky add .husky/pre-commit "npm run lint"

执行该命令后,会看到.husky/目录下新增了一个名为pre-commit的shell脚本。 这样,在之后执行git commit命令时会先触发pre-commit这个脚本。 pre-commit脚本内容如下:


#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run lint


## 分别为子项目配置脚本
>在 子项目package 根结点添加脚本字段
```JSON
 "scripts": {
    "lint": "lint-staged"
  },
  "lint-staged": {
    "src/**/*.{html,css,scss,less}": [
      "stylelint 'src/**/*.{html,css,scss,less}' --fix",
      "prettier --write"
    ],
    "src/**/*.{ts,tsx}": [
      "eslint 'src/**/*.{ts,tsx}'  --fix",
      "prettier --parser=typescript --write"
    ],
    "src/**/*.{js,jsx}": [
      "eslint 'src/**/*.{js,jsx}'  --fix",
      "prettier --write"
    ]
  },

在项目根目录package.json添加脚本

  "scripts": {
    "prepare": "husky install",
    "lint": "yarn workspaces run lint",
  },

prepare 为初始化 husky

lint为运行所有子项目 lint 命令

如果示例项目没有 lint 命令,可使用

"scripts": {
"lint":"npm -v"
},
WangShuXian6 commented 2 years ago

配置 css 导入

在子项目根目录添加 index.d.ts


declare module '*.scss' {
const content: { [key: string]: any }
export = content
}

declare module '*.css' { const content: { [key: string]: any } export = content }

declare module '*.less' { const content: { [key: string]: any } export = content }

declare module '*.sass' { const content: { [key: string]: any } export = content }

>子项目tsconfig包含该定义
>`tsconfig.json`
```JSON
"include": [ "index.d.ts"],