来源:得物技术
目录
一、背景
1. 工程管理
2. 代码风格
3. 文档
4. 质量管理
二、monorepo
1. 什么是monorepo
2. monorepo的优缺点
2.1 优点
2.2 缺点
2.3 选择
三、lerna
1. lerna命令集
1.1 命令行列表
1.2 全局配置项
1.3 过滤器参数
2. lerna原理解析
2.1 文件结构
2.2 命令行注册
2.3 依赖管理
2.4 lerna中涉及的git命令
四、总结
一
背景

工程管理
代码风格
文档
质量控制
二
monorepo
什么是monorepo
├── packages| ├── pkg1| | ├── package.json| ├── pkg2| | ├── package.json├── package.json
├─ lerna.json├─ package.json└─ packages/ # 这里将存放所有子项目目录 ├─ README.md ├─ babel-cli ├─ babel-code-frame ├─ babel-compat-data ├─ babel-core ├─ babel-generator ├─ babel-helper-annotate-as-pure ├─ babel-helper-builder-binary-assignment-operator-visitor ├─ babel-helper-builder-react-jsx ├─ ...
├─ rollup ├─ package.json ├─ ...├─ plugins ├─ package.json ├─ ...├─ awesome ├─ package.json ├─ ...├─ rollup-starter-lib ├─ package.json ├─ ...├─ rollup-plugin-babel ├─ package.json ├─ ...├─ rollup-plugin-commonjs ├─ package.json ├─ ...
monorepo的优缺点
优点
缺点
选择
三
lerna
lerna命令集
命令行列表

全局配置项
lerna publish --concurrency 1
lerna bootstrap --reject-cycles
过滤器参数
lerna exec --scope my-component -- ls -la lerna run --scope toolbar-* test lerna run --scope package-1 --scope *-2 lint
lerna exec --ignore package-{1,2,5} -- ls -la lerna run --ignore package-1 test lerna run --ignore package-@(1|2) --ignore package-3 lint# 列出自最新标记以来发生变化的包的内容$ lerna exec --since -- ls -la# 为自“master”以来所有发生更改的包运行测试$ lerna run test --since master# 列出自“某个分支”以来发生变化的所有包$ lerna ls --since some-branch
lerna bootstrap --scope my-component --include-dependencies # my-component 及其所有依赖项将被引导
lerna bootstrap --scope "package-*" --ignore "package-util-*" --include-dependencies # 所有匹配 "package-util-*" 的包将被忽略,除非它们依赖于名称匹配 "package-*" 的包
lerna exec --since --include-merged-tags -- ls -la
lerna原理解析
文件结构
lerna├─ CHANGELOG.md -- 更新日志├─ README.md -- 文档├─ commands -- 核心子模块├─ core -- 核心子模块├─ utils -- 核心子模块├─ lerna.json -- lerna 配置文件├─ package-lock.json -- 依赖声明├─ package.json -- 依赖声明├─ scripts -- 内置脚本└─ yarn.lock -- 依赖声明
const cli = require("@lerna/cli");const addCmd = require("@lerna/add/command");const bootstrapCmd = require("@lerna/bootstrap/command");const changedCmd = require("@lerna/changed/command");const cleanCmd = require("@lerna/clean/command");const createCmd = require("@lerna/create/command");const diffCmd = require("@lerna/diff/command");const execCmd = require("@lerna/exec/command");const importCmd = require("@lerna/import/command");const infoCmd = require("@lerna/info/command");const initCmd = require("@lerna/init/command");const linkCmd = require("@lerna/link/command");const listCmd = require("@lerna/list/command");const publishCmd = require("@lerna/publish/command");const runCmd = require("@lerna/run/command");const versionCmd = require("@lerna/version/command");

命令行注册
"bin": { "lerna": "cli.js" }#!/usr/bin/env node"use strict";/* eslint-disable import/no-dynamic-require, global-require */const importLocal = require("import-local");if (importLocal(__filename)) {require("npmlog").info("cli", "using local version of lerna");} else {require(".")(process.argv.slice(2));}
;const cli = require("@lerna/cli");const addCmd = require("@lerna/add/command");const bootstrapCmd = require("@lerna/bootstrap/command");const changedCmd = require("@lerna/changed/command");const cleanCmd = require("@lerna/clean/command");const createCmd = require("@lerna/create/command");const diffCmd = require("@lerna/diff/command");const execCmd = require("@lerna/exec/command");const importCmd = require("@lerna/import/command");const infoCmd = require("@lerna/info/command");const initCmd = require("@lerna/init/command");const linkCmd = require("@lerna/link/command");const listCmd = require("@lerna/list/command");const publishCmd = require("@lerna/publish/command");const runCmd = require("@lerna/run/command");const versionCmd = require("@lerna/version/command");const pkg = require("./package.json");module.exports = main;function main(argv) {const context = {lernaVersion: pkg.version,};return cli().command(addCmd).command(bootstrapCmd).command(changedCmd).command(cleanCmd).command(createCmd).command(diffCmd).command(execCmd).command(importCmd).command(infoCmd).command(initCmd).command(linkCmd).command(listCmd).command(publishCmd).command(runCmd).command(versionCmd).parse(argv, context);}
const { Command } = require("@lerna/command");class InitCommand extends Command {...}
class InitCommand extends Command {...initialize() {throw new ValidationError(this.name, "initialize() needs to be implemented.");}execute() {throw new ValidationError(this.name, "execute() needs to be implemented.");}}
"use strict";/* eslint-disable import/no-dynamic-require, global-require */const importLocal = require("import-local");if (importLocal(__filename)) {require("npmlog").info("cli", "using local version of lerna");} else {require(".")(process.argv.slice(2));}
;const path = require('path');const resolveCwd = require('resolve-cwd');const pkgDir = require('pkg-dir');module.exports = filename => {const globalDir = pkgDir.sync(path.dirname(filename));const relativePath = path.relative(globalDir, filename);const pkg = require(path.join(globalDir, 'package.json'));const localFile = resolveCwd.silent(path.join(pkg.name, relativePath));const localNodeModules = path.join(process.cwd(), 'node_modules');const filenameInLocalNodeModules = !path.relative(localNodeModules, filename).startsWith('..');// Use path.relative() to detect local package installation,// because __filename's case is inconsistent on Windows// Can use === when targeting Node.js 8// See https://github.com/nodejs/node/issues/6624return !filenameInLocalNodeModules && localFile && path.relative(localFile, filename) !== '' && require(localFile);};
依赖管理
{ "npmClient": "yarn", "useWorkspaces": true, }{ "workspaces": [ "packages/*" ] }{ "workspaces": { "packages": [ "Packages/*", ], "nohoist": [ "**" ] } }lerna中涉及的git命令
git initgit rev-parsegit describegit rev-listgit taggit loggit configgit diff-indexgit --versiongit showgit amgit resetgit ls-filesgit diff-treegit commitgit ls-remotegit checkoutgit pushgit addgit remotegit show-ref
$ git rev-parse HEADf7f6d6f2b6b47eb8c4cf4b8bf5f83e0b8028c031
getCurrentSHA() {return this.execSync("git", ["rev-parse", "HEAD"]);}getWorkspaceRoot() {return this.execSync("git", ["rev-parse", "--show-toplevel"]);}
$ git describepolaris-release-1.0.0-c12345
export function getLastCommit(execOpts?: ExecOptions) { if (hasTags(execOpts)) { log.silly("getLastTagInBranch", ""); return childProcess.execSync("git", ["describe", "--tags", "--abbrev=0"], execOpts); } log.silly("getFirstCommit", ""); return childProcess.execSync("git", ["rev-list", "--max-parents=0", "HEAD"], execOpts);}$ git rev-list HEADf7f6d6f2b6b47eb8c4cf4b8bf5f83e0b8028c031a3d8b4e1c2e1d0a9b8c6e5f7d6a4b3e8a1b2c3d4
export function getLastCommit(execOpts?: ExecOptions) { if (hasTags(execOpts)) { log.silly("getLastTagInBranch", ""); return childProcess.execSync("git", ["describe", "--tags", "--abbrev=0"], execOpts); } log.silly("getFirstCommit", ""); return childProcess.execSync("git", ["rev-list", "--max-parents=0", "HEAD"], execOpts);}$ git diff-index HEAD:100644 100644 bcd1234... 0123456... M file.txt
export class ImportCommand extends Command<ImportCommandOptions> { ... if (this.execSync("git", ["diff-index", "HEAD"])) { throw new ValidationError("ECHANGES", "Local repository has un-committed changes"); } ...}$ git diff-tree HEAD~2 HEAD100644 blob a3d8b4e1c2e1d0a9b8c6e5f7d6a4b3e8a1b2c3d4 file.txt
export async function getProjectsWithTaggedPackages(projectNodes: ProjectGraphProjectNodeWithPackage[],projectFileMap: ProjectFileMap,execOpts: ExecOptions): Promise<ProjectGraphProjectNodeWithPackage[]> {log.silly("getTaggedPackages", "");// @see https://stackoverflow.com/a/424142/5707// FIXME: --root is only necessary for tests :Pconst result = await childProcess.exec("git",["diff-tree", "--name-only", "--no-commit-id", "--root", "-r", "-c", "HEAD"],execOpts);const stdout: string = result.stdout;const files = new Set(stdout.split("\n"));return projectNodes.filter((node) => projectFileMap[node.name]?.some((file) => files.has(file.file)));}
$ git show-refa3d8b4e1c2e1d0a9b8c6e5f7d6a4b3e8a1b2c3d4 HEADa3d8b4e1c2e1d0a9b8c6e5f7d6a4b3e8a1b2c3d4 refs/heads/mainf7f6d6f2b6b47eb8c4cf4b8bf5f83e0b8028c031 refs/tags/v1.0.0
export function remoteBranchExists(gitRemote: string, branch: string, opts: ExecOptions) {log.silly("remoteBranchExists", "");const remoteBranch = `${gitRemote}/${branch}`;try {childProcess.execSync("git", ["show-ref", "--verify", `refs/remotes/${remoteBranch}`], opts);return true;} catch (e) {return false;}}
四
总结
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70027824/viewspace-3017679/ ,如需转载,请注明出处,否则将追究法律责任。