Skip to content

Commit

Permalink
fix(uni-builder): compat builder plugin api & type (#5174)
Browse files Browse the repository at this point in the history
  • Loading branch information
9aoy authored Jan 2, 2024
1 parent 3f5f300 commit b287898
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 30 deletions.
1 change: 1 addition & 0 deletions packages/builder/uni-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"webpack-subresource-integrity": "5.1.0"
},
"devDependencies": {
"@modern-js/builder-plugin-node-polyfill": "workspace:*",
"@rsbuild/plugin-swc": "0.2.13",
"@scripts/build": "workspace:*",
"@scripts/vitest-config": "workspace:*",
Expand Down
28 changes: 14 additions & 14 deletions packages/builder/uni-builder/src/rspack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import type {
UniBuilderConfig,
CreateUniBuilderOptions,
CreateBuilderCommonOptions,
OverridesUniBuilderInstance,
} from '../types';
import { parseCommonConfig } from '../shared/parseCommonConfig';
import type {
StartDevServerOptions,
UniBuilderStartServerResult,
} from '../shared/devServer';
import { compatLegacyPlugin } from '../shared/compatLegacyPlugin';
import type { StartDevServerOptions } from '../shared/devServer';

export async function parseConfig(
uniBuilderConfig: UniBuilderConfig,
Expand Down Expand Up @@ -56,16 +55,11 @@ export async function parseConfig(
};
}

export type UniBuilderInstance = Omit<RsbuildInstance, 'startDevServer'> & {
/**
* should be used in conjunction with the upper-layer framework:
*
* missing route.json (required in modern server)
*/
startDevServer: (
options: StartDevServerOptions,
) => Promise<UniBuilderStartServerResult>;
};
export type UniBuilderInstance = Omit<
RsbuildInstance,
keyof OverridesUniBuilderInstance
> &
OverridesUniBuilderInstance;

export async function createRspackBuilder(
options: CreateUniBuilderOptions,
Expand All @@ -86,6 +80,12 @@ export async function createRspackBuilder(

return {
...rsbuild,
addPlugins: (plugins, options) => {
const warpedPlugins = plugins.map(plugin => {
return compatLegacyPlugin(plugin, { cwd });
});
rsbuild.addPlugins(warpedPlugins, options);
},
startDevServer: async (options: StartDevServerOptions = {}) => {
const { startDevServer } = await import('../shared/devServer');

Expand Down
88 changes: 88 additions & 0 deletions packages/builder/uni-builder/src/shared/compatLegacyPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type {
UniBuilderPlugin,
UniBuilderPluginAPI,
UniBuilderContext,
} from '../types';
import type { RsbuildPlugin } from '@rsbuild/core';
import { logger } from '@rsbuild/shared';
import { join } from 'path';

function addDeprecatedWarning(
pluginName: string,
name: string,
newName?: string,
) {
logger.warn(
`Plugin(${pluginName})'s api '${name}' is deprecated${
newName ? `, please use '${newName}' instead.` : '.'
}`,
);
}
export function compatLegacyPlugin(
plugin: UniBuilderPlugin,
extraInfo: {
cwd: string;
},
): RsbuildPlugin {
return {
...plugin,
setup: api => {
const builderContext = new Proxy(api.context, {
get(target, prop: keyof UniBuilderContext) {
switch (prop) {
case 'target':
addDeprecatedWarning(
plugin.name,
'context.target',
'context.targets',
);
return target.targets;
case 'srcPath':
addDeprecatedWarning(plugin.name, 'context.srcPath');
return join(extraInfo.cwd, 'src');
case 'framework':
addDeprecatedWarning(plugin.name, 'context.framework');
return '';
default: {
if (prop in target) {
return target[prop];
} else {
return undefined;
}
}
}
},
set(_target, prop: keyof UniBuilderContext) {
logger.error(
`Context is readonly, you can not assign to the "context.${prop}" prop.`,
);
return true;
},
}) as UniBuilderContext;

const legacyAPI: UniBuilderPluginAPI = {
...api,
context: builderContext,
getBuilderConfig: () => {
addDeprecatedWarning(
plugin.name,
'getBuilderConfig',
'getRsbuildConfig',
);
return api.getRsbuildConfig();
},
modifyBuilderConfig: fn => {
api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
addDeprecatedWarning(
plugin.name,
'modifyBuilderConfig',
'modifyRsbuildConfig',
);
return fn(config, { mergeBuilderConfig: mergeRsbuildConfig });
});
},
};
return plugin.setup(legacyAPI);
},
};
}
66 changes: 66 additions & 0 deletions packages/builder/uni-builder/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {
DevConfig,
RequestHandler,
RsbuildEntry,
PromiseOrNot,
RsbuildPluginAPI,
} from '@rsbuild/shared';
import type { RsbuildConfig } from '@rsbuild/core';
import type { PluginAssetsRetryOptions } from '@rsbuild/plugin-assets-retry';
Expand All @@ -24,6 +26,10 @@ import type { PluginCheckSyntaxOptions } from '@rsbuild/plugin-check-syntax';
import type { PluginPugOptions } from '@rsbuild/plugin-pug';
import type { PluginBabelOptions } from '@rsbuild/plugin-babel';
import type { AliasOption } from '@modern-js/utils';
import type {
StartDevServerOptions,
UniBuilderStartServerResult,
} from './shared/devServer';

export type CreateBuilderCommonOptions = {
entry?: RsbuildEntry;
Expand Down Expand Up @@ -265,6 +271,64 @@ export type SriOptions = {
hashLoading?: 'eager' | 'lazy';
};

export type OverridesUniBuilderInstance = {
addPlugins: (
plugins: UniBuilderPlugin[],
options?: {
before?: string;
},
) => void;
/**
* should be used in conjunction with the upper-layer framework:
*
* missing route.json (required in modern server)
*/
startDevServer: (
options: StartDevServerOptions,
) => Promise<UniBuilderStartServerResult>;
};

export type UniBuilderContext = RsbuildPluginAPI['context'] & {
target: RsbuildPluginAPI['context']['targets'];
framework: string;
srcPath: string;
entry: Record<string, string | string[]>;
};

export type UniBuilderPluginAPI = {
[key in keyof RsbuildPluginAPI]: RsbuildPluginAPI[key];
} & {
/** The following APIs only type incompatibility */
onBeforeCreateCompiler: (fn: any) => void;
onAfterCreateCompiler: (fn: any) => void;
onBeforeBuild: (fn: any) => void;
modifyBundlerChain: (fn: any) => void;
getNormalizedConfig: () => any;

/** The following APIs need to be compatible */
context: UniBuilderContext;
getBuilderConfig: () => Readonly<any>;
modifyBuilderConfig: (
fn: (
config: any,
utils: {
mergeBuilderConfig: <T>(...configs: T[]) => T;
},
) => PromiseOrNot<any | void>,
) => void;
};

/**
* compat legacy modern.js builder plugin
*/
export type UniBuilderPlugin = {
name: string;
setup: (api: UniBuilderPluginAPI) => PromiseOrNot<void>;
pre?: string[];
post?: string[];
remove?: string[];
};

export type UniBuilderConfig = {
dev?: RsbuildConfig['dev'];
html?: RsbuildConfig['html'];
Expand All @@ -273,4 +337,6 @@ export type UniBuilderConfig = {
security?: RsbuildConfig['security'];
tools?: RsbuildConfig['tools'];
source?: Omit<NonNullable<RsbuildConfig['source']>, 'alias'>;
// plugins is a new field, should avoid adding modern plugin by mistake
plugins?: RsbuildConfig['plugins'];
} & UniBuilderExtraConfig;
22 changes: 12 additions & 10 deletions packages/builder/uni-builder/src/webpack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import type {
UniBuilderConfig,
CreateUniBuilderOptions,
CreateBuilderCommonOptions,
OverridesUniBuilderInstance,
} from '../types';
import { parseCommonConfig } from '../shared/parseCommonConfig';
import { compatLegacyPlugin } from '../shared/compatLegacyPlugin';
import { pluginModuleScopes } from './plugins/moduleScopes';
import { pluginBabel } from './plugins/babel';
import { pluginReact } from './plugins/react';
import type {
StartDevServerOptions,
UniBuilderStartServerResult,
} from '../shared/devServer';
import type { StartDevServerOptions } from '../shared/devServer';

export async function parseConfig(
uniBuilderConfig: UniBuilderConfig,
Expand Down Expand Up @@ -82,12 +81,9 @@ export async function parseConfig(

export type UniBuilderWebpackInstance = Omit<
RsbuildInstance<RsbuildProvider<'webpack'>>,
'startDevServer'
> & {
startDevServer: (
options: StartDevServerOptions,
) => Promise<UniBuilderStartServerResult>;
};
keyof OverridesUniBuilderInstance
> &
OverridesUniBuilderInstance;

export async function createWebpackBuilder(
options: CreateUniBuilderOptions,
Expand Down Expand Up @@ -121,6 +117,12 @@ export async function createWebpackBuilder(

return {
...rsbuild,
addPlugins: (plugins, options) => {
const warpedPlugins = plugins.map(plugin => {
return compatLegacyPlugin(plugin, { cwd });
});
rsbuild.addPlugins(warpedPlugins, options);
},
startDevServer: async (options: StartDevServerOptions = {}) => {
const { startDevServer } = await import('../shared/devServer');

Expand Down
9 changes: 3 additions & 6 deletions packages/builder/uni-builder/tests/helper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type {
RsbuildInstance,
RspackConfig,
RspackRule,
} from '@rsbuild/shared';
import type { RspackConfig, RspackRule } from '@rsbuild/shared';
import type { UniBuilderInstance } from '../src';

export function matchRules({
config,
Expand All @@ -28,7 +25,7 @@ export function matchRules({
});
}

export const unwrapConfig = async (rsbuild: RsbuildInstance) => {
export const unwrapConfig = async (rsbuild: UniBuilderInstance) => {
const configs = await rsbuild.initConfigs();
return configs[0];
};
Expand Down
47 changes: 47 additions & 0 deletions packages/builder/uni-builder/tests/legacy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, it, expect } from 'vitest';
import { builderPluginNodePolyfill } from '@modern-js/builder-plugin-node-polyfill';
import { createUniBuilder } from '../src';

describe('uni-builder legacy plugins', () => {
it('legacy plugin should works well', async () => {
const rsbuild = await createUniBuilder({
bundlerType: 'rspack',
config: {},
cwd: '',
});

rsbuild.addPlugins([
// plugin type check should passed
builderPluginNodePolyfill(),
{
name: 'builder-plugin-test',
setup: api => {
api.modifyBuilderConfig((config, { mergeBuilderConfig }) => {
const builderConfig = api.getBuilderConfig();

expect(api.context.target).toBe(api.context.targets);

expect(builderConfig.source).toBeDefined();

return mergeBuilderConfig(config, {
tools: {
bundlerChain: (chain: any) => {
chain.devtool(false);
},
},
});
});
},
},
]);

const {
origin: { bundlerConfigs },
} = await rsbuild.inspectConfig();

expect(bundlerConfigs[0].devtool).toBeFalsy();
expect(
Object.keys(bundlerConfigs[0].resolve!.fallback!).length,
).toBeGreaterThan(1);
});
});
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b287898

Please sign in to comment.