blog
blog copied to clipboard
preconstruct
preconstruct
在 monorepos 中轻松开发和构建您的代码 暂不支持css等样式文件的打包
https://www.npmjs.com/package/@preconstruct/cli
https://github.com/preconstruct/preconstruct
https://preconstruct.tools/
preconstruct for single package repos [单包]
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
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.a
umd名称,由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
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
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 自动生成
{
"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");
}
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
{
"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
{
"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"
配置代码格式化 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 根结点添加脚本字段
"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"
},
配置 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
"include": [ "index.d.ts"],