Skip to content

Commit

Permalink
feat(preset-umi): support monorepo redirect (umijs#427)
Browse files Browse the repository at this point in the history
* wip(monorepo): add redirect feature [no ci]

* dep: add workspaces utils dep

* docs: add monorepo redirect desc

* chore: update lock file

* chore(preset-umi): replace all `joi` to `Joi`

* chore: not handle `false` value

* chore: replace `source` to `srcDir`

* chore: auto exclude `umi` in local dev

* chore: display throw error when not monorepo scene

* chore: rename collect method name

* dep: precompiled `@manypkg/get-packages`

* fix: add type for getPackages method

* chore: exclude pkg name

* chore: update lock file

* chore: update lock file

* docs: `source` to `srcDir`
  • Loading branch information
fz6m authored Mar 9, 2022
1 parent 80a964d commit 2253783
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 592 deletions.
20 changes: 20 additions & 0 deletions docs/docs/api/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,26 @@ mock: {
mountElementId: 'container'
```

## monorepoRedirect

* 类型:`{ srcDir?: string[], exclude?: RegExp[] }`
* 默认值:`false`

在 monorepo 中使用 umi 时,你可能需要引入其他子包的组件、工具等,通过开启此选项来重定向这些子包的导入到他们的源码位置(默认为 `src` 文件夹),这也可以解决 `MFSU` 场景改动子包不热更新的问题。

通过配置 `srcDir` 来调整识别源码文件夹的优先位置,通过 `exclude` 来设定不需要重定向的依赖范围。

示例:

```js
// 默认重定向到子包的 src 文件夹
monorepoRedirect: {}
// 优先定向到 libs 文件夹
monorepoRedirect: { srcDir: ['libs', 'src'] }
// 不重定向 @scope/* 的子包
monorepoRedirect: { exclude: [/^@scope\/.+/] }
```

## outputPath

* 类型:`string`
Expand Down
37 changes: 37 additions & 0 deletions packages/preset-umi/compiled/@manypkg/get-packages/index.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"@manypkg/get-packages","license":"MIT"}
2 changes: 2 additions & 0 deletions packages/preset-umi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"regenerator-runtime": "0.13.9"
},
"devDependencies": {
"@manypkg/get-packages": "1.1.3",
"@types/body-parser": "1.19.2",
"@types/ini": "1.3.31",
"@types/multer": "1.4.7",
Expand All @@ -63,6 +64,7 @@
"deps": [
"body-parser",
"multer",
"@manypkg/get-packages",
"ini"
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default (api: IApi) => {
};
for (const key of Object.keys(schemas)) {
const config: Record<string, any> = {
schema: schemas[key] || ((joi: any) => joi.any()),
schema: schemas[key] || ((Joi: any) => Joi.any()),
};
if (key in configDefaults) {
config.default = configDefaults[key];
Expand Down
10 changes: 5 additions & 5 deletions packages/preset-umi/src/features/crossorigin/crossorigin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export default (api: IApi) => {
api.describe({
key: 'crossorigin',
config: {
schema(joi) {
return joi.alternatives(
joi.boolean(),
joi.object({
include: joi.array().items(joi.object().instance(RegExp)),
schema(Joi) {
return Joi.alternatives(
Joi.boolean(),
Joi.object({
include: Joi.array().items(Joi.object().instance(RegExp)),
}),
);
},
Expand Down
122 changes: 122 additions & 0 deletions packages/preset-umi/src/features/monorepo/redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// @ts-ignore
import { getPackages } from '../../../compiled/@manypkg/get-packages';
import { logger } from '@umijs/utils';
import { pkgUp } from '@umijs/utils/compiled/pkg-up';
import assert from 'assert';
import { existsSync, statSync } from 'fs';
import { dirname, join } from 'path';
import type { IApi } from '../../types';

interface IConfigs {
srcDir?: string[];
exclude?: RegExp[];
}

export default (api: IApi) => {
api.describe({
key: 'monorepoRedirect',
config: {
schema(Joi) {
return Joi.alternatives(
Joi.boolean(),
Joi.object({
srcDir: Joi.array().items(Joi.string()),
exclude: Joi.array().items(Joi.object().instance(RegExp)),
}),
);
},
},
enableBy: api.EnableBy.config,
});

api.modifyConfig(async (memo) => {
const rootPkg = await pkgUp({ cwd: dirname(api.cwd) });
if (!rootPkg) return memo;
const root = dirname(rootPkg);
assert(
isMonorepo({ root }),
`The 'monorepoRedirect' option can only be used in monorepo, you don't need configure.`,
);

const config: IConfigs = memo.monorepoRedirect || {};
const { exclude = [], srcDir = ['src'] } = config;
// Note: not match `umi` package in local dev
if (__filename.includes(`packages/preset-umi`)) {
logger.info(
`[monorepoRedirect]: Auto excluded 'umi' package in local dev scene`,
);
exclude.push(/^umi$/);
}
// collect use workspace deps
const usingDeps = collectPkgDeps(api.pkg).filter((name) => {
return !exclude.some((reg) => reg.test(name));
});
if (!usingDeps.length) return memo;
// collect all project
const projects = await collectAllProjects({ root });
const alias = usingDeps.reduce<Record<string, string>>((obj, name) => {
const root = projects[name];
if (!root) {
return obj;
}
srcDir.some((dirName) => {
const dirPath = join(root, dirName);
if (existsSync(dirPath) && statSync(dirPath).isDirectory()) {
// redirect to source dir
obj[name] = dirPath;
return true;
}
});
return obj;
}, {});
memo.alias = {
...memo.alias,
...alias,
};

return memo;
});
};

interface IOpts {
root: string;
}

interface IProject {
packageJson: Record<string, any>;
dir: string;
}

const DEP_KEYS = ['devDependencies', 'dependencies'];
function collectPkgDeps(pkg: Record<string, any>) {
const deps: string[] = [];
DEP_KEYS.forEach((type) => {
deps.push(...Object.keys(pkg?.[type] || {}));
});
return deps;
}

async function collectAllProjects(opts: IOpts) {
const workspaces = await getPackages(opts.root);
return workspaces.packages.reduce<Record<string, string>>(
(obj: Record<string, string>, pkg: IProject) => {
const name = pkg.packageJson?.name;
if (name) {
obj[name] = pkg.dir;
}
return obj;
},
{},
);
}

const MONOREPO_FILE = ['pnpm-workspace.yaml', 'lerna.json'];
function isMonorepo(opts: IOpts) {
const pkgExist = existsSync(join(opts.root, 'package.json'));
return (
pkgExist &&
MONOREPO_FILE.some((file) => {
return existsSync(join(opts.root, file));
})
);
}
1 change: 1 addition & 0 deletions packages/preset-umi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default () => {
require.resolve('./features/lowImport/lowImport'),
require.resolve('./features/vite/vite'),
require.resolve('./features/apiRoute/apiRoute'),
require.resolve('./features/monorepo/redirect'),

// commands
require.resolve('./commands/build'),
Expand Down
Loading

0 comments on commit 2253783

Please sign in to comment.