Skip to content

Commit

Permalink
fix(plugin): run async setup function in the correct order (#4681)
Browse files Browse the repository at this point in the history
* fix(plugin): run async setup function in the correct order

* docs: add
  • Loading branch information
chenjiahan authored Sep 20, 2023
1 parent 14b0906 commit 14f95cf
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 14 deletions.
7 changes: 7 additions & 0 deletions .changeset/fifty-apes-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modern-js/plugin': patch
---

fix(plugin): run async setup function in the correct order

fix(plugin): 以正确的顺序执行异步的 setup 函数
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export const myPlugin = (): CliPlugin => ({
});
```

Note that the setup function of the next plugin is not executed until the async setup function of the current plugin has finished. Therefore, you should avoid performing time-consuming asynchronous operations in the setup function to avoid slowing down the startup performance of the CLI.

## Adding Plugins

Custom plugins can be used by following the instructions in the [plugins](/configure/app/plugins) section of the documentation. Below is the recommended way to implement plugins in Modern.js.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export const myPlugin = (): CliPlugin => ({
});
```

注意,只有当前插件的 setup 异步函数执行完毕,才会继续执行下一个插件的 setup 函数。因此,你需要避免在 setup 函数中进行耗时过长的异步操作,防止影响 CLI 启动性能。

## 添加插件

自定义插件的使用方式可以查看:[plugins (框架插件)](/configure/app/plugins)。下面会介绍 Modern.js 中推荐的插件实现方法。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export const myPlugin = (): CliPlugin<ModuleTools> => ({
});
```

Note that the setup function of the next plugin is not executed until the async setup function of the current plugin has finished. Therefore, you should avoid performing time-consuming asynchronous operations in the setup function to avoid slowing down the startup performance of the CLI.

## Life cycle hooks

We know that the `setup` function returns a Hooks object, which can also be understood as an object with Modern.js Module lifecycle hooks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export const myPlugin = (): CliPlugin<ModuleTools> => ({
});
```

注意,只有当前插件的 setup 异步函数执行完毕,才会继续执行下一个插件的 setup 函数。因此,你需要避免在 setup 函数中进行耗时过长的异步操作,防止影响 CLI 启动性能。

## 生命周期钩子

我们知道 `setup` 函数会返回一个 Hooks 对象,所谓 Hooks 对象也可以理解是具有 Modern.js Module 生命周期钩子的对象。
Expand Down
15 changes: 8 additions & 7 deletions packages/toolkit/plugin/src/manager/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const createAsyncManager = <
plugins = [];
};

const init: AsyncManager<Hooks, API>['init'] = () => {
const init: AsyncManager<Hooks, API>['init'] = async () => {
const sortedPlugins = sortPlugins(plugins);
const mergedPluginAPI = {
...pluginAPI,
Expand All @@ -191,12 +191,13 @@ export const createAsyncManager = <

checkPlugins(sortedPlugins);

return Promise.all(
sortedPlugins.map(plugin => plugin.setup(mergedPluginAPI)),
).then(hooksList => {
runners = generateRunner<Hooks>(hooksList, currentHooks);
return runners;
});
const hooksList: (void | Partial<ToThreads<Hooks>>)[] = [];
for (const plugin of sortedPlugins) {
hooksList.push(await plugin.setup(mergedPluginAPI));
}

runners = generateRunner<Hooks>(hooksList, currentHooks);
return runners;
};

const run: AsyncManager<Hooks, API>['run'] = cb => cb();
Expand Down
7 changes: 3 additions & 4 deletions packages/toolkit/plugin/src/workflow/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export type AsyncWorkflow<I, O> = {
[ASYNC_WORKFLOW_SYMBOL]: true;
};

const isPromise = (obj: any): obj is Promise<any> =>
obj && typeof obj.then === 'function';

export const isAsyncWorkflow = (input: any): input is AsyncWorkflow<any, any> =>
Boolean(input?.[ASYNC_WORKFLOW_SYMBOL]);

Expand Down Expand Up @@ -53,7 +56,3 @@ const mapAsyncWorkerToAsyncMiddleware =
Promise.resolve(worker(input)).then(result =>
Promise.resolve(next(input)).then(nextResult => [result, ...nextResult]),
);

function isPromise(obj: any): obj is Promise<any> {
return obj && typeof obj.then === 'function';
}
32 changes: 29 additions & 3 deletions packages/toolkit/plugin/tests/async.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,33 @@ describe('async manager', () => {
expect(result0).toBe(1);
});

it('support support dynamicly register', async () => {
it('should run async setup function in the correct order', async () => {
const manager = createAsyncManager();

const countContext = createContext(0);
const useCount = () => countContext.use().value;

const plugin1 = manager.createPlugin(async () => {
await sleep(0);
countContext.set(1);
expect(useCount()).toBe(1);
});
const plugin2 = manager.createPlugin(async () => {
countContext.set(2);
expect(useCount()).toBe(2);
});

manager.usePlugin(plugin1);
manager.usePlugin(plugin2);

await manager.init();

const result0 = countContext.get();

expect(result0).toBe(2);
});

it('support support dynamically register', async () => {
const manager = main.clone().usePlugin(dFoo).usePlugin(dBar);

expect(getNumber()).toBe(0);
Expand Down Expand Up @@ -282,7 +308,7 @@ describe('async manager', () => {

manager.usePlugin(plugin0, plugin1);

expect(manager.init).toThrowError();
await expect(manager.init()).rejects.toThrowError();
});

it('should not throw error without attaching rival plugin', async () => {
Expand Down Expand Up @@ -324,7 +350,7 @@ describe('async manager', () => {

manager.usePlugin(plugin0);

expect(manager.init).toThrowError();
await expect(manager.init()).rejects.toThrowError();
});

it('should not throw error without attaching rival plugin', async () => {
Expand Down

0 comments on commit 14f95cf

Please sign in to comment.