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 7 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
117 changes: 104 additions & 13 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,24 @@ import {
cssExternalHandler,
isCssGlobalFile,
} from './css/cssConfig';
import { pluginCjsShim } from './plugins/cjsShim';
import {
pluginCjsImportMetaUrlShim,
pluginEsmRequireShim,
} from './plugins/shims';
import type {
AutoExternal,
BannerAndFooter,
Format,
LibConfig,
PkgJson,
Redirect,
ResolvedShims,
RsbuildConfigOutputTarget,
RslibConfig,
RslibConfigAsyncFn,
RslibConfigExport,
RslibConfigSyncFn,
Shims,
Syntax,
} from './types';
import { getDefaultExtension } from './utils/extension';
Expand Down Expand Up @@ -447,12 +452,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 +470,10 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
rspack: {
module: {
parser: {
javascript: jsParserOptions,
javascript: {
...jsParserOptions.esm,
...jsParserOptions.cjs,
},
},
},
optimization: {
Expand All @@ -486,12 +498,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 +542,85 @@ const composeFormatConfig = (format: Format): RsbuildConfig => {
}
};

const resolveShims = (shims?: Shims) => {
const resolvedShims = {
cjs: {
'import.meta.url': true,
},
esm: {
__filename: true,
__dirname: true,
require: false,
},
};

if (!shims) {
return resolvedShims;
}

if (shims.cjs) {
if (typeof shims.cjs === 'boolean') {
if (shims.cjs === true) {
resolvedShims.cjs['import.meta.url'] = true;
} else {
resolvedShims.cjs['import.meta.url'] = false;
}
} else {
resolvedShims.cjs['import.meta.url'] =
shims.cjs['import.meta.url'] ?? false;
}
}

if (shims.esm) {
if (typeof shims.esm === 'boolean') {
if (shims.esm === true) {
resolvedShims.esm.__filename = true;
resolvedShims.esm.__dirname = true;
resolvedShims.esm.require = true;
}
} else {
resolvedShims.esm.__filename = shims.esm.__filename ?? false;
resolvedShims.esm.__dirname = shims.esm.__dirname ?? false;
resolvedShims.esm.require = shims.esm.require ?? false;
}
}

return resolvedShims;
};

const composeShimsConfig = (
format: Format,
resolvedShims: ResolvedShims,
): RsbuildConfig => {
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 +922,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 @@ -915,6 +1002,8 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) {
externalHelpers = false,
redirect = {},
} = config;
const resolvedShims = resolveShims(config.shims);
const shimsConfig = composeShimsConfig(format!, resolvedShims);
const formatConfig = composeFormatConfig(format!);
const externalHelpersConfig = composeExternalHelpersConfig(
externalHelpers,
Expand Down Expand Up @@ -967,6 +1056,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 +1136,7 @@ export async function composeCreateRsbuildConfig(
'banner',
'footer',
'dts',
'shims',
]),
),
};
Expand Down
25 changes: 0 additions & 25 deletions packages/core/src/plugins/cjsShim.ts

This file was deleted.

47 changes: 47 additions & 0 deletions packages/core/src/plugins/shims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { type RsbuildPlugin, rspack } from '@rsbuild/core';

const importMetaUrlShim = `/*#__PURE__*/ (function () {
return typeof document === 'undefined'
? new (module.require('url'.replace('', '')).URL)('file:' + __filename).href
: (document.currentScript && document.currentScript.src) ||
new URL('main.js', document.baseURI).href;
})()`;

// This Rsbuild plugin will shim `import.meta.url` for CommonJS modules.
// - 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 pluginCjsImportMetaUrlShim = (): RsbuildPlugin => ({
name: 'rsbuild-plugin-cjs-import-meta-url-shim',
setup(api) {
api.modifyEnvironmentConfig((config) => {
config.source.define = {
...config.source.define,
'import.meta.url': importMetaUrlShim,
};
});
},
});

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-plugin-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)$/,
}),
);
});
},
});
21 changes: 21 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,26 @@ export type BannerAndFooter = {
dts?: string;
};

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

export type ResolvedShims = {
cjs: Required<NonNullable<Exclude<Shims['cjs'], boolean>>>;
esm: Required<NonNullable<Exclude<Shims['esm'], boolean>>>;
};

export type Redirect = {
// TODO: support other redirects
// alias?: boolean;
Expand All @@ -67,6 +87,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": "node-module",
"__filename": "node-module",
},
"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-plugin-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
Loading
Loading