blog icon indicating copy to clipboard operation
blog copied to clipboard

preconstruct

Open WangShuXian6 opened this issue 3 years ago • 5 comments

preconstruct

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

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

https://github.com/preconstruct/preconstruct

https://preconstruct.tools/

WangShuXian6 avatar Dec 23 '21 03:12 WangShuXian6

preconstruct for single package repos [单包]

WangShuXian6 avatar Dec 23 '21 03:12 WangShuXian6

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.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

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");
}

image

WangShuXian6 avatar Dec 23 '21 03:12 WangShuXian6

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"

WangShuXian6 avatar Dec 23 '21 14:12 WangShuXian6

配置代码格式化 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"
  },

WangShuXian6 avatar Jan 25 '22 10:01 WangShuXian6

配置 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"],

WangShuXian6 avatar Jan 26 '22 02:01 WangShuXian6