Skip to content

Commit

Permalink
feat: support custom config files (#6142)
Browse files Browse the repository at this point in the history
* feat: support custom config files

* test: add test cases

* docs: add custom config files document
  • Loading branch information
chemdemo authored Feb 23, 2021
1 parent e1da820 commit 14ea121
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 37 deletions.
81 changes: 48 additions & 33 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ toc: menu

# API


## 基本 API

### dynamic
Expand All @@ -19,16 +18,17 @@ Load component dynamically on demand.

Usually work with [dynamic import syntax](https://github.com/tc39/proposal-dynamic-import).


**Create dynamic component**

```js
import { dynamic } from 'umi';

export default dynamic({
loader: async function() {
loader: async function () {
// webpackChunkName tells webpack create separate bundle for HugeA
const { default: HugeA } = await import(/* webpackChunkName: "external_A" */ './HugeA');
const { default: HugeA } = await import(
/* webpackChunkName: "external_A" */ './HugeA'
);
return HugeA;
},
});
Expand All @@ -47,7 +47,7 @@ import AsyncHugeA from './AsyncHugeA';
// 3. display HugeA whenever component downloaded
export default () => {
return <AsyncHugeA />;
}
};
```

### history
Expand Down Expand Up @@ -135,21 +135,21 @@ plugin.applyPlugins({

参数属性包含:

* **key**,坑位的 key
* **type**,执行方式类型,详见 [ApplyPluginsType](#ApplyPluginsType)
* **initialValue**,初始值
* **args**,参数
* **async**,是否异步执行且返回 Promise
- **key**,坑位的 key
- **type**,执行方式类型,详见 [ApplyPluginsType](#ApplyPluginsType)
- **initialValue**,初始值
- **args**,参数
- **async**,是否异步执行且返回 Promise

### ApplyPluginsType

> 主要在插件利用,项目代码中一般用不到。
运行时插件执行类型,enum 类型,包含三个属性:

* **compose**,用于合并执行多个函数,函数可决定前序函数的执行时机
* **modify**,用于修改值
* **event**,用于执行事件,前面没有依赖关系
- **compose**,用于合并执行多个函数,函数可决定前序函数的执行时机
- **modify**,用于修改值
- **event**,用于执行事件,前面没有依赖关系

## 路由

Expand All @@ -175,9 +175,9 @@ export default () => {
{/* An object representation of the Link location */}
<Link
to={{
pathname: "/list",
search: "?sort=name",
hash: "#the-hash",
pathname: '/list',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true },
}}
>
Expand All @@ -190,8 +190,8 @@ export default () => {
or as an object
*/}
<Link
to={location => {
return { ...location, pathname: "/profile" };
to={(location) => {
return { ...location, pathname: '/profile' };
}}
/>

Expand All @@ -206,7 +206,7 @@ export default () => {
*/}
<Link
to="/courses"
innerRef={node => {
innerRef={(node) => {
// `node` refers to the mounted DOM element
// or null when unmounted
}}
Expand Down Expand Up @@ -241,8 +241,8 @@ export default () => {
<NavLink
to="/faq"
activeStyle={{
fontWeight: "bold",
color: "red",
fontWeight: 'bold',
color: 'red',
}}
>
FAQs
Expand Down Expand Up @@ -276,7 +276,7 @@ export default () => {
if (!match) {
return false;
}
return location.search.includes("name");
return location.search.includes('name');
}}
>
Profile
Expand Down Expand Up @@ -307,8 +307,10 @@ export default () => {
transition
*/}
<Prompt
message={location => {
return location.pathname !== "/" ? true : `Are are sure you want to back to home page?`;
message={(location) => {
return location.pathname !== '/'
? true
: `Are are sure you want to back to home page?`;
}}
/>

Expand All @@ -327,7 +329,7 @@ export default () => {
You can get access to the `history`, `location`, `match` objects via the `withRouter` higher-order component. `withRouter` will pass updated `match`, `location`, and `history` props to the wrapped component whenever it renders

```tsx
import { withRouter } from "umi";
import { withRouter } from 'umi';

export default withRouter(({ history, location, match }) => {
return (
Expand All @@ -347,10 +349,10 @@ export default withRouter(({ history, location, match }) => {
The `useHistory` hook gives you access to the `history` instance that you may use to navigate.

```tsx
import { useHistory } from "umi";
import { useHistory } from 'umi';

export default () => {
const history = useHistory()
const history = useHistory();
return (
<div>
<ul>
Expand All @@ -366,10 +368,10 @@ export default () => {
The `useLocation` hook returns the `location` object that represents the current URL. You can think about it like a `useState` that returns a new location whenever the URL changes.

```tsx
import { useLocation } from "umi";
import { useLocation } from 'umi';

export default () => {
const location = useLocation()
const location = useLocation();
return (
<div>
<ul>
Expand All @@ -385,10 +387,10 @@ export default () => {
`useParams` returns an object of key/value pairs of URL parameters. Use it to access `match.params` of the current route.

```tsx
import { useParams } from "umi";
import { useParams } from 'umi';

export default () => {
const params = useParams()
const params = useParams();
return (
<div>
<ul>
Expand All @@ -404,10 +406,10 @@ export default () => {
The `useRouteMatch` hook attempts to match the current URL in the same way that a Route would. It’s mostly useful for getting access to the match data without actually rendering a `<Route />`

```tsx
import { useRouteMatch } from "umi";
import { useRouteMatch } from 'umi';

export default () => {
const match = useRouteMatch()
const match = useRouteMatch();
return (
<div>
<ul>
Expand All @@ -426,6 +428,19 @@ export default () => {

Umi 内核的 Service 方法,用于测试,或调用 Umi 底层命令。

#### 自定义配置文件

umi 默认会依次(相对应用根目录)读取`.umirc.ts``.umirc.js``config/config.ts``config/config.js`作为用户配置文件,也可以自定义用户配置文件(优先级高于默认的配置文件):

```tsx
new Service({
configFiles: ['.mycustomrc.ts', 'config/mycustom.ts'],
// ... other options
});
```

自定义用户配置文件通常用于基于 umi 或 umi-core 深度定制开发框架的场景。

### utils

utils 方法,给插件使用,和插件里的 api.utils 是同一个底层库。
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/Config/Config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ test('umirc-typescript', async () => {
expect(service.userConfig).toEqual({ foo: 'bar' });
});

test('custom-config-file', async () => {
const cwd = join(fixtures, 'custom-config-file');
const service = new Service({
cwd,
configFiles: ['.foorc.js'],
});
expect(service.userConfig).toEqual({ foo: 'bar' });
});

test('custom-config-file-ts', async () => {
const cwd = join(fixtures, 'custom-config-file-ts');
const service = new Service({
cwd,
configFiles: ['.foorc.ts'],
});
expect(service.userConfig).toEqual({ foo: 'bar' });
});

test('config-config', async () => {
const cwd = join(fixtures, 'config-config');
const service = new Service({
Expand Down
17 changes: 13 additions & 4 deletions packages/core/src/Config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ interface IOpts {
cwd: string;
service: Service;
localConfig?: boolean;
configFiles?: string[];
}

const CONFIG_FILES = [
const DEFAULT_CONFIG_FILES = [
'.umirc.ts',
'.umirc.js',
'config/config.ts',
Expand All @@ -51,11 +52,17 @@ export default class Config {
config?: object;
localConfig?: boolean;
configFile?: string | null;
configFiles = DEFAULT_CONFIG_FILES;

constructor(opts: IOpts) {
this.cwd = opts.cwd || process.cwd();
this.service = opts.service;
this.localConfig = opts.localConfig;

if (Array.isArray(opts.configFiles)) {
// 配置的优先读取
this.configFiles = lodash.uniq(opts.configFiles.concat(this.configFiles));
}
}

async getDefaultConfig() {
Expand Down Expand Up @@ -212,14 +219,16 @@ export default class Config {

getConfigFile(): string | null {
// TODO: support custom config file
const configFile = CONFIG_FILES.find((f) => existsSync(join(this.cwd, f)));
const configFile = this.configFiles.find((f) =>
existsSync(join(this.cwd, f)),
);
return configFile ? winPath(configFile) : null;
}

getWatchFilesAndDirectories() {
const umiEnv = process.env.UMI_ENV;
const configFiles = lodash.clone(CONFIG_FILES);
CONFIG_FILES.forEach((f) => {
const configFiles = lodash.clone(this.configFiles);
this.configFiles.forEach((f) => {
if (this.localConfig) configFiles.push(this.addAffix(f, 'local'));
if (umiEnv) configFiles.push(this.addAffix(f, umiEnv));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default {
foo: 'bar',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default {
foo: 'biz',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default {
foo: 'bar',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default {
foo: 'biz',
};
6 changes: 6 additions & 0 deletions packages/core/src/Service/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface IServiceOpts {
pkg?: IPackage;
presets?: string[];
plugins?: string[];
configFiles?: string[];
env?: NodeEnv;
}

Expand Down Expand Up @@ -113,10 +114,15 @@ export default class Service extends EventEmitter {

// get user config without validation
logger.debug('get user config');
const configFiles = opts.configFiles;
this.configInstance = new Config({
cwd: this.cwd,
service: this,
localConfig: this.env === 'development',
configFiles:
Array.isArray(configFiles) && !!configFiles[0]
? configFiles
: undefined,
});
this.userConfig = this.configInstance.getUserConfig();
logger.debug('userConfig:');
Expand Down

0 comments on commit 14ea121

Please sign in to comment.