Skip to content

Commit da145e9

Browse files
authored
feat!: add shims config (#291)
1 parent 3419b1a commit da145e9

File tree

52 files changed

+609
-167
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+609
-167
lines changed

.npmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
registry = 'https://registry.npmjs.org/'
22
strict-peer-dependencies=false
3-
hoist-patterns[]=[]
3+
hoist-patterns[]=[]

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"test": "pnpm run test:unit && pnpm run test:integration && pnpm run test:e2e",
2222
"test:benchmark": "cd ./tests && pnpm run test:benchmark",
2323
"test:e2e": "pnpm run build:examples && cd tests && pnpm run test:e2e",
24-
"test:integration": "vitest run --project integration",
25-
"test:integration:watch": "vitest --project integration",
24+
"test:integration": "cross-env NODE_OPTIONS='--experimental-vm-modules' vitest run --project integration",
25+
"test:integration:watch": "cross-env NODE_OPTIONS='--experimental-vm-modules' vitest --project integration",
2626
"test:unit": "vitest run --project unit*",
2727
"test:unit:watch": "vitest --project unit*",
2828
"testu": "pnpm run test:unit -u && pnpm run test:integration -u",

packages/core/src/config.ts

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import {
2424
cssExternalHandler,
2525
isCssGlobalFile,
2626
} from './css/cssConfig';
27-
import { pluginCjsShim } from './plugins/cjsShim';
27+
import {
28+
pluginCjsImportMetaUrlShim,
29+
pluginEsmRequireShim,
30+
} from './plugins/shims';
2831
import type {
2932
AutoExternal,
3033
BannerAndFooter,
@@ -37,6 +40,7 @@ import type {
3740
RslibConfigAsyncFn,
3841
RslibConfigExport,
3942
RslibConfigSyncFn,
43+
Shims,
4044
Syntax,
4145
} from './types';
4246
import { getDefaultExtension } from './utils/extension';
@@ -447,12 +451,16 @@ export async function createConstantRsbuildConfig(): Promise<RsbuildConfig> {
447451

448452
const composeFormatConfig = (format: Format): RsbuildConfig => {
449453
const jsParserOptions = {
450-
importMeta: false,
451-
requireResolve: false,
452-
requireDynamic: false,
453-
requireAsExpression: false,
454-
importDynamic: false,
455-
};
454+
cjs: {
455+
requireResolve: false,
456+
requireDynamic: false,
457+
requireAsExpression: false,
458+
},
459+
esm: {
460+
importMeta: false,
461+
importDynamic: false,
462+
},
463+
} as const;
456464

457465
switch (format) {
458466
case 'esm':
@@ -461,7 +469,10 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
461469
rspack: {
462470
module: {
463471
parser: {
464-
javascript: jsParserOptions,
472+
javascript: {
473+
...jsParserOptions.esm,
474+
...jsParserOptions.cjs,
475+
},
465476
},
466477
},
467478
optimization: {
@@ -486,12 +497,11 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
486497
};
487498
case 'cjs':
488499
return {
489-
plugins: [pluginCjsShim()],
490500
tools: {
491501
rspack: {
492502
module: {
493503
parser: {
494-
javascript: jsParserOptions,
504+
javascript: { ...jsParserOptions.esm, ...jsParserOptions.cjs },
495505
},
496506
},
497507
output: {
@@ -531,6 +541,47 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
531541
}
532542
};
533543

544+
const composeShimsConfig = (format: Format, shims?: Shims): RsbuildConfig => {
545+
const resolvedShims = {
546+
cjs: {
547+
'import.meta.url': shims?.cjs?.['import.meta.url'] ?? true,
548+
},
549+
esm: {
550+
__filename: shims?.esm?.__filename ?? false,
551+
__dirname: shims?.esm?.__dirname ?? false,
552+
require: shims?.esm?.require ?? false,
553+
},
554+
};
555+
556+
switch (format) {
557+
case 'esm':
558+
return {
559+
tools: {
560+
rspack: {
561+
node: {
562+
// "__dirname" and "__filename" shims will automatically be enabled when `output.module` is `true`
563+
__dirname: resolvedShims.esm.__dirname ? 'node-module' : false,
564+
__filename: resolvedShims.esm.__filename ? 'node-module' : false,
565+
},
566+
},
567+
},
568+
plugins: [resolvedShims.esm.require && pluginEsmRequireShim()].filter(
569+
Boolean,
570+
),
571+
};
572+
case 'cjs':
573+
return {
574+
plugins: [
575+
resolvedShims.cjs['import.meta.url'] && pluginCjsImportMetaUrlShim(),
576+
].filter(Boolean),
577+
};
578+
case 'umd':
579+
return {};
580+
default:
581+
throw new Error(`Unsupported format: ${format}`);
582+
}
583+
};
584+
534585
export const composeModuleImportWarn = (request: string): string => {
535586
return `The externalized commonjs request ${color.green(`"${request}"`)} will use ${color.blue('"module"')} external type in ESM format. If you want to specify other external type, considering set the request and type with ${color.blue('"output.externals"')}.`;
536587
};
@@ -832,9 +883,6 @@ const composeTargetConfig = (
832883
tools: {
833884
rspack: {
834885
target: ['node'],
835-
// "__dirname" and "__filename" shims will automatically be enabled when `output.module` is `true`,
836-
// and leave them as-is in the rest of the cases. Leave the comments here to explain the behavior.
837-
// { node: { __dirname: ..., __filename: ... } }
838886
},
839887
},
840888
output: {
@@ -908,13 +956,15 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) {
908956

909957
const {
910958
format,
959+
shims,
911960
banner = {},
912961
footer = {},
913962
autoExtension = true,
914963
autoExternal = true,
915964
externalHelpers = false,
916965
redirect = {},
917966
} = config;
967+
const shimsConfig = composeShimsConfig(format!, shims);
918968
const formatConfig = composeFormatConfig(format!);
919969
const externalHelpersConfig = composeExternalHelpersConfig(
920970
externalHelpers,
@@ -967,6 +1017,7 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) {
9671017

9681018
return mergeRsbuildConfig(
9691019
formatConfig,
1020+
shimsConfig,
9701021
externalHelpersConfig,
9711022
// externalsWarnConfig should before other externals config
9721023
externalsWarnConfig,
@@ -1046,6 +1097,7 @@ export async function composeCreateRsbuildConfig(
10461097
'banner',
10471098
'footer',
10481099
'dts',
1100+
'shims',
10491101
]),
10501102
),
10511103
};

packages/core/src/plugins/cjsShim.ts renamed to packages/core/src/plugins/shims.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RsbuildPlugin } from '@rsbuild/core';
1+
import { type RsbuildPlugin, rspack } from '@rsbuild/core';
22

33
const importMetaUrlShim = `/*#__PURE__*/ (function () {
44
return typeof document === 'undefined'
@@ -11,9 +11,8 @@ const importMetaUrlShim = `/*#__PURE__*/ (function () {
1111
// - Replace `import.meta.url` with `importMetaUrl`.
1212
// - Inject `importMetaUrl` to the end of the module (can't inject at the beginning because of `"use strict";`).
1313
// This is a short-term solution, and we hope to provide built-in polyfills like `node.__filename` on Rspack side.
14-
export const pluginCjsShim = (): RsbuildPlugin => ({
15-
name: 'rsbuild-plugin-cjs-shim',
16-
14+
export const pluginCjsImportMetaUrlShim = (): RsbuildPlugin => ({
15+
name: 'rsbuild:cjs-import-meta-url-shim',
1716
setup(api) {
1817
api.modifyEnvironmentConfig((config) => {
1918
config.source.define = {
@@ -23,3 +22,26 @@ export const pluginCjsShim = (): RsbuildPlugin => ({
2322
});
2423
},
2524
});
25+
26+
const requireShim = `// Rslib ESM shims
27+
import __rslib_shim_module__ from 'module';
28+
const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(import.meta.url);
29+
`;
30+
31+
export const pluginEsmRequireShim = (): RsbuildPlugin => ({
32+
name: 'rsbuild:esm-require-shim',
33+
setup(api) {
34+
api.modifyRspackConfig((config) => {
35+
config.plugins ??= [];
36+
config.plugins.push(
37+
new rspack.BannerPlugin({
38+
banner: requireShim,
39+
// Just before minify stage, to perform tree shaking.
40+
stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE - 1,
41+
raw: true,
42+
include: /\.(js|cjs)$/,
43+
}),
44+
);
45+
});
46+
},
47+
});

packages/core/src/types/config/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ export type BannerAndFooter = {
4848
dts?: string;
4949
};
5050

51+
export type Shims = {
52+
cjs?: {
53+
'import.meta.url'?: boolean;
54+
};
55+
esm?: {
56+
__filename?: boolean;
57+
__dirname?: boolean;
58+
require?: boolean;
59+
};
60+
};
61+
5162
export type Redirect = {
5263
// TODO: support other redirects
5364
// alias?: boolean;
@@ -67,6 +78,7 @@ export interface LibConfig extends RsbuildConfig {
6778
externalHelpers?: boolean;
6879
banner?: BannerAndFooter;
6980
footer?: BannerAndFooter;
81+
shims?: Shims;
7082
dts?: Dts;
7183
}
7284

packages/core/tests/__snapshots__/config.test.ts.snap

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
4848
"not dead",
4949
],
5050
},
51+
"plugins": [],
5152
"source": {
5253
"alias": {
5354
"bar": "bar",
@@ -110,6 +111,10 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
110111
},
111112
},
112113
},
114+
"node": {
115+
"__dirname": false,
116+
"__filename": false,
117+
},
113118
"optimization": {
114119
"concatenateModules": true,
115120
"sideEffects": "flag",
@@ -196,7 +201,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
196201
},
197202
"plugins": [
198203
{
199-
"name": "rsbuild-plugin-cjs-shim",
204+
"name": "rsbuild:cjs-import-meta-url-shim",
200205
"setup": [Function],
201206
},
202207
],

tests/benchmark/index.bench.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ describe('run rslib in examples', () => {
66
'examples/express-plugin',
77
async () => {
88
const cwd = getCwdByExample('express-plugin');
9-
await rslibBuild(cwd);
9+
await rslibBuild({ cwd });
1010
},
1111
{ time: 5 },
1212
);
1313
bench(
1414
'examples/react-component-bundle',
1515
async () => {
1616
const cwd = getCwdByExample('react-component-bundle');
17-
await rslibBuild(cwd);
17+
await rslibBuild({ cwd });
1818
},
1919
{ time: 5 },
2020
);
2121
bench(
2222
'examples/react-component-bundle-false',
2323
async () => {
2424
const cwd = getCwdByExample('react-component-bundle-false');
25-
await rslibBuild(cwd);
25+
await rslibBuild({ cwd });
2626
},
2727
{ time: 5 },
2828
);

tests/integration/alias/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { expect, test } from 'vitest';
33

44
test('source.alias', async () => {
55
const fixturePath = __dirname;
6-
const { entries } = await buildAndGetResults(fixturePath);
6+
const { entries } = await buildAndGetResults({ fixturePath });
77

88
expect(entries.esm).toContain('hello world');
99
expect(entries.cjs).toContain('hello world');

tests/integration/asset/index.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { expect, test } from 'vitest';
44

55
test('set the size threshold to inline static assets', async () => {
66
const fixturePath = join(__dirname, 'limit');
7-
const { contents } = await buildAndGetResults(fixturePath);
7+
const { contents } = await buildAndGetResults({ fixturePath });
88

99
// inline when bundle
1010
expect(Object.values(contents.esm0!)[0]).toContain(
@@ -29,7 +29,7 @@ test('set the size threshold to inline static assets', async () => {
2929

3030
test('set the assets name', async () => {
3131
const fixturePath = join(__dirname, 'name');
32-
const { contents } = await buildAndGetResults(fixturePath);
32+
const { contents } = await buildAndGetResults({ fixturePath });
3333

3434
// bundle
3535
expect(Object.values(contents.esm0!)[0]).toContain(
@@ -44,7 +44,7 @@ test('set the assets name', async () => {
4444

4545
test('set the assets output path', async () => {
4646
const fixturePath = join(__dirname, 'path');
47-
const { contents } = await buildAndGetResults(fixturePath);
47+
const { contents } = await buildAndGetResults({ fixturePath });
4848

4949
// bundle
5050
expect(Object.values(contents.esm0!)[0]).toContain(
@@ -59,7 +59,7 @@ test('set the assets output path', async () => {
5959

6060
test('set the assets public path', async () => {
6161
const fixturePath = join(__dirname, 'public-path');
62-
const { contents } = await buildAndGetResults(fixturePath);
62+
const { contents } = await buildAndGetResults({ fixturePath });
6363

6464
// bundle
6565
expect(Object.values(contents.esm0!)[0]).toContain(
@@ -74,7 +74,7 @@ test('set the assets public path', async () => {
7474

7575
test('use svgr', async () => {
7676
const fixturePath = join(__dirname, 'svgr');
77-
const { contents } = await buildAndGetResults(fixturePath);
77+
const { contents } = await buildAndGetResults({ fixturePath });
7878

7979
// bundle -- default export with react query
8080
expect(Object.values(contents.esm0!)[0]).toMatchSnapshot();

tests/integration/async-chunks/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { expect, test } from 'vitest';
44

55
test('should get correct value from async chunks', async () => {
66
const fixturePath = join(__dirname, 'default');
7-
const { entryFiles } = await buildAndGetResults(fixturePath);
7+
const { entryFiles } = await buildAndGetResults({ fixturePath });
88

99
for (const format of ['esm', 'cjs']) {
1010
const { foo } = await import(entryFiles[format]);

0 commit comments

Comments
 (0)