Skip to content

feat!: add shims config #291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
registry = 'https://registry.npmjs.org/'
strict-peer-dependencies=false
hoist-patterns[]=[]
hoist-patterns[]=[]
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"test": "pnpm run test:unit && pnpm run test:integration && pnpm run test:e2e",
"test:benchmark": "cd ./tests && pnpm run test:benchmark",
"test:e2e": "pnpm run build:examples && cd tests && pnpm run test:e2e",
"test:integration": "vitest run --project integration",
"test:integration:watch": "vitest --project integration",
"test:integration": "cross-env NODE_OPTIONS='--experimental-vm-modules' vitest run --project integration",
"test:integration:watch": "cross-env NODE_OPTIONS='--experimental-vm-modules' vitest --project integration",
"test:unit": "vitest run --project unit*",
"test:unit:watch": "vitest --project unit*",
"testu": "pnpm run test:unit -u && pnpm run test:integration -u",
Expand Down
78 changes: 65 additions & 13 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import {
cssExternalHandler,
isCssGlobalFile,
} from './css/cssConfig';
import { pluginCjsShim } from './plugins/cjsShim';
import {
pluginCjsImportMetaUrlShim,
pluginEsmRequireShim,
} from './plugins/shims';
import type {
AutoExternal,
BannerAndFooter,
Expand All @@ -37,6 +40,7 @@ import type {
RslibConfigAsyncFn,
RslibConfigExport,
RslibConfigSyncFn,
Shims,
Syntax,
} from './types';
import { getDefaultExtension } from './utils/extension';
Expand Down Expand Up @@ -447,12 +451,16 @@ export async function createConstantRsbuildConfig(): Promise<RsbuildConfig> {

const composeFormatConfig = (format: Format): RsbuildConfig => {
const jsParserOptions = {
importMeta: false,
requireResolve: false,
requireDynamic: false,
requireAsExpression: false,
importDynamic: false,
};
cjs: {
requireResolve: false,
requireDynamic: false,
requireAsExpression: false,
},
esm: {
importMeta: false,
importDynamic: false,
},
} as const;

switch (format) {
case 'esm':
Expand All @@ -461,7 +469,10 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
rspack: {
module: {
parser: {
javascript: jsParserOptions,
javascript: {
...jsParserOptions.esm,
...jsParserOptions.cjs,
},
},
},
optimization: {
Expand All @@ -486,12 +497,11 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
};
case 'cjs':
return {
plugins: [pluginCjsShim()],
tools: {
rspack: {
module: {
parser: {
javascript: jsParserOptions,
javascript: { ...jsParserOptions.esm, ...jsParserOptions.cjs },
},
},
output: {
Expand Down Expand Up @@ -531,6 +541,47 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
}
};

const composeShimsConfig = (format: Format, shims?: Shims): RsbuildConfig => {
const resolvedShims = {
cjs: {
'import.meta.url': shims?.cjs?.['import.meta.url'] ?? true,
},
esm: {
__filename: shims?.esm?.__filename ?? false,
__dirname: shims?.esm?.__dirname ?? false,
require: shims?.esm?.require ?? false,
},
};

switch (format) {
case 'esm':
return {
tools: {
rspack: {
node: {
// "__dirname" and "__filename" shims will automatically be enabled when `output.module` is `true`
__dirname: resolvedShims.esm.__dirname ? 'node-module' : false,
__filename: resolvedShims.esm.__filename ? 'node-module' : false,
},
},
},
plugins: [resolvedShims.esm.require && pluginEsmRequireShim()].filter(
Boolean,
),
};
case 'cjs':
return {
plugins: [
resolvedShims.cjs['import.meta.url'] && pluginCjsImportMetaUrlShim(),
].filter(Boolean),
};
case 'umd':
return {};
default:
throw new Error(`Unsupported format: ${format}`);
}
};

export const composeModuleImportWarn = (request: string): string => {
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"')}.`;
};
Expand Down Expand Up @@ -832,9 +883,6 @@ const composeTargetConfig = (
tools: {
rspack: {
target: ['node'],
// "__dirname" and "__filename" shims will automatically be enabled when `output.module` is `true`,
// and leave them as-is in the rest of the cases. Leave the comments here to explain the behavior.
// { node: { __dirname: ..., __filename: ... } }
},
},
output: {
Expand Down Expand Up @@ -908,13 +956,15 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) {

const {
format,
shims,
banner = {},
footer = {},
autoExtension = true,
autoExternal = true,
externalHelpers = false,
redirect = {},
} = config;
const shimsConfig = composeShimsConfig(format!, shims);
const formatConfig = composeFormatConfig(format!);
const externalHelpersConfig = composeExternalHelpersConfig(
externalHelpers,
Expand Down Expand Up @@ -967,6 +1017,7 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) {

return mergeRsbuildConfig(
formatConfig,
shimsConfig,
externalHelpersConfig,
// externalsWarnConfig should before other externals config
externalsWarnConfig,
Expand Down Expand Up @@ -1046,6 +1097,7 @@ export async function composeCreateRsbuildConfig(
'banner',
'footer',
'dts',
'shims',
]),
),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { RsbuildPlugin } from '@rsbuild/core';
import { type RsbuildPlugin, rspack } from '@rsbuild/core';

const importMetaUrlShim = `/*#__PURE__*/ (function () {
return typeof document === 'undefined'
Expand All @@ -11,9 +11,8 @@ const importMetaUrlShim = `/*#__PURE__*/ (function () {
// - Replace `import.meta.url` with `importMetaUrl`.
// - Inject `importMetaUrl` to the end of the module (can't inject at the beginning because of `"use strict";`).
// This is a short-term solution, and we hope to provide built-in polyfills like `node.__filename` on Rspack side.
export const pluginCjsShim = (): RsbuildPlugin => ({
name: 'rsbuild-plugin-cjs-shim',

export const pluginCjsImportMetaUrlShim = (): RsbuildPlugin => ({
name: 'rsbuild:cjs-import-meta-url-shim',
setup(api) {
api.modifyEnvironmentConfig((config) => {
config.source.define = {
Expand All @@ -23,3 +22,26 @@ export const pluginCjsShim = (): RsbuildPlugin => ({
});
},
});

const requireShim = `// Rslib ESM shims
import __rslib_shim_module__ from 'module';
const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(import.meta.url);
`;

export const pluginEsmRequireShim = (): RsbuildPlugin => ({
name: 'rsbuild:esm-require-shim',
setup(api) {
api.modifyRspackConfig((config) => {
config.plugins ??= [];
config.plugins.push(
new rspack.BannerPlugin({
banner: requireShim,
// Just before minify stage, to perform tree shaking.
stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE - 1,
raw: true,
include: /\.(js|cjs)$/,
}),
);
});
},
});
12 changes: 12 additions & 0 deletions packages/core/src/types/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ export type BannerAndFooter = {
dts?: string;
};

export type Shims = {
cjs?: {
'import.meta.url'?: boolean;
};
esm?: {
__filename?: boolean;
__dirname?: boolean;
require?: boolean;
};
};

export type Redirect = {
// TODO: support other redirects
// alias?: boolean;
Expand All @@ -67,6 +78,7 @@ export interface LibConfig extends RsbuildConfig {
externalHelpers?: boolean;
banner?: BannerAndFooter;
footer?: BannerAndFooter;
shims?: Shims;
dts?: Dts;
}

Expand Down
7 changes: 6 additions & 1 deletion packages/core/tests/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
"not dead",
],
},
"plugins": [],
"source": {
"alias": {
"bar": "bar",
Expand Down Expand Up @@ -110,6 +111,10 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
},
},
},
"node": {
"__dirname": false,
"__filename": false,
},
"optimization": {
"concatenateModules": true,
"sideEffects": "flag",
Expand Down Expand Up @@ -196,7 +201,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
},
"plugins": [
{
"name": "rsbuild-plugin-cjs-shim",
"name": "rsbuild:cjs-import-meta-url-shim",
"setup": [Function],
},
],
Expand Down
6 changes: 3 additions & 3 deletions tests/benchmark/index.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ describe('run rslib in examples', () => {
'examples/express-plugin',
async () => {
const cwd = getCwdByExample('express-plugin');
await rslibBuild(cwd);
await rslibBuild({ cwd });
},
{ time: 5 },
);
bench(
'examples/react-component-bundle',
async () => {
const cwd = getCwdByExample('react-component-bundle');
await rslibBuild(cwd);
await rslibBuild({ cwd });
},
{ time: 5 },
);
bench(
'examples/react-component-bundle-false',
async () => {
const cwd = getCwdByExample('react-component-bundle-false');
await rslibBuild(cwd);
await rslibBuild({ cwd });
},
{ time: 5 },
);
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/alias/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect, test } from 'vitest';

test('source.alias', async () => {
const fixturePath = __dirname;
const { entries } = await buildAndGetResults(fixturePath);
const { entries } = await buildAndGetResults({ fixturePath });

expect(entries.esm).toContain('hello world');
expect(entries.cjs).toContain('hello world');
Expand Down
10 changes: 5 additions & 5 deletions tests/integration/asset/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { expect, test } from 'vitest';

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

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

test('set the assets name', async () => {
const fixturePath = join(__dirname, 'name');
const { contents } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetResults({ fixturePath });

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

test('set the assets output path', async () => {
const fixturePath = join(__dirname, 'path');
const { contents } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetResults({ fixturePath });

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

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

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

test('use svgr', async () => {
const fixturePath = join(__dirname, 'svgr');
const { contents } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetResults({ fixturePath });

// bundle -- default export with react query
expect(Object.values(contents.esm0!)[0]).toMatchSnapshot();
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/async-chunks/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { expect, test } from 'vitest';

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

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