Skip to content

Commit b6b21b3

Browse files
authored
feat!: disable autoExternal in bundleless mode and only redirect request not in node_modules (#624)
1 parent b1cffee commit b6b21b3

File tree

17 files changed

+138
-57
lines changed

17 files changed

+138
-57
lines changed

packages/core/src/config.ts

+30-13
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,18 @@ const getAutoExternalDefaultValue = (
250250
};
251251

252252
export const composeAutoExternalConfig = (options: {
253+
bundle: boolean;
253254
format: Format;
254255
autoExternal?: AutoExternal;
255256
pkgJson?: PkgJson;
256257
userExternals?: NonNullable<EnvironmentConfig['output']>['externals'];
257258
}): EnvironmentConfig => {
258-
const { format, pkgJson, userExternals } = options;
259+
const { bundle, format, pkgJson, userExternals } = options;
260+
261+
// If bundle is false, autoExternal will be disabled
262+
if (bundle === false) {
263+
return {};
264+
}
259265

260266
const autoExternal = getAutoExternalDefaultValue(
261267
format,
@@ -1020,23 +1026,33 @@ const composeBundlelessExternalConfig = (
10201026

10211027
if (jsRedirectPath) {
10221028
try {
1029+
// use resolver to resolve the request
10231030
resolvedRequest = await resolver(context, resolvedRequest);
1024-
resolvedRequest = normalizeSlash(
1025-
path.relative(
1026-
path.dirname(contextInfo.issuer),
1027-
resolvedRequest,
1028-
),
1029-
);
1030-
// Requests that fall through here cannot be matched by any other externals config ahead.
1031-
// Treat all these requests as relative import of source code. Node.js won't add the
1032-
// leading './' to the relative path resolved by `path.relative`. So add manually it here.
1033-
if (resolvedRequest[0] !== '.') {
1034-
resolvedRequest = `./${resolvedRequest}`;
1031+
1032+
// only handle the request that is not in node_modules
1033+
if (!resolvedRequest.includes('node_modules')) {
1034+
resolvedRequest = normalizeSlash(
1035+
path.relative(
1036+
path.dirname(contextInfo.issuer),
1037+
resolvedRequest,
1038+
),
1039+
);
1040+
// Requests that fall through here cannot be matched by any other externals config ahead.
1041+
// Treat all these requests as relative import of source code. Node.js won't add the
1042+
// leading './' to the relative path resolved by `path.relative`. So add manually it here.
1043+
if (resolvedRequest[0] !== '.') {
1044+
resolvedRequest = `./${resolvedRequest}`;
1045+
}
1046+
} else {
1047+
// NOTE: If request is a phantom dependency, which means it can be resolved but not specified in dependencies or peerDependencies in package.json, the output will be incorrect to use when the package is published
1048+
// return the original request instead of the resolved request
1049+
return callback(undefined, request);
10351050
}
10361051
} catch (e) {
1052+
// catch error when request can not be resolved by resolver
10371053
// e.g. A react component library importing and using 'react' but while not defining
10381054
// it in devDependencies and peerDependencies. Preserve 'react' as-is if so.
1039-
logger.warn(
1055+
logger.debug(
10401056
`Failed to resolve module ${color.green(`"${resolvedRequest}"`)} from ${color.green(contextInfo.issuer)}. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.`,
10411057
);
10421058
}
@@ -1271,6 +1287,7 @@ async function composeLibRsbuildConfig(
12711287
} = composeTargetConfig(config.output?.target, format!);
12721288
const syntaxConfig = composeSyntaxConfig(target, config?.syntax);
12731289
const autoExternalConfig = composeAutoExternalConfig({
1290+
bundle,
12741291
format: format!,
12751292
autoExternal,
12761293
pkgJson,

pnpm-lock.yaml

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "auto-external-bundle-false-test",
3+
"private": true,
4+
"devDependencies": {
5+
"ora": "8.1.1",
6+
"react": "^19.0.0"
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { defineConfig } from '@rslib/core';
2+
import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper';
3+
4+
export default defineConfig({
5+
lib: [
6+
generateBundleEsmConfig({
7+
bundle: false,
8+
}),
9+
generateBundleCjsConfig({
10+
bundle: false,
11+
}),
12+
],
13+
source: {
14+
entry: {
15+
index: ['./src/**'],
16+
},
17+
},
18+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { oraPromise } from 'ora';
2+
import React from 'react';
3+
4+
export type { oraPromise };
5+
export const foo = () => {
6+
return React.version;
7+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "@rslib/tsconfig/base",
3+
"compilerOptions": {
4+
"baseUrl": "./"
5+
},
6+
"include": ["src"]
7+
}

tests/integration/auto-external/index.test.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ test('auto external sub path should works', async () => {
4040
);
4141
});
4242

43+
test('auto external should be disabled when bundle is false', async () => {
44+
const fixturePath = join(__dirname, 'bundle-false');
45+
const { js } = await buildAndGetResults({ fixturePath, type: 'all' });
46+
47+
expect(Object.values(js.contents.esm)[0]).toContain(
48+
'import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react"',
49+
);
50+
51+
expect(Object.values(js.contents.cjs)[0]).toContain(
52+
'const external_react_namespaceObject = require("react");',
53+
);
54+
});
55+
4356
test('auto external false should works', async () => {
4457
const fixturePath = join(__dirname, 'false');
4558
const { js, dts } = await buildAndGetResults({ fixturePath, type: 'all' });
@@ -49,7 +62,7 @@ test('auto external false should works', async () => {
4962
);
5063

5164
expect(js.entries.cjs).not.toContain(
52-
'var external_react_namespaceObject = require("react");',
65+
'const external_react_namespaceObject = require("react");',
5366
);
5467

5568
// dts should bundled
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
// can not be resolved
12
import lodash from 'lodash';
3+
// can be resolved but not specified -- phantom dependency
4+
import prettier from 'prettier';
25
import bar from './bar.js';
36
import foo from './foo';
47

8+
console.log('prettier: ', prettier);
9+
510
export default lodash.toUpper(foo + bar);

tests/integration/redirect/js.test.ts

+10
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ test('redirect.js default', async () => {
2121

2222
expect(indexContent).toMatchInlineSnapshot(`
2323
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
24+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
2425
import * as __WEBPACK_EXTERNAL_MODULE__bar_index_js__ from "./bar/index.js";
2526
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
2627
import * as __WEBPACK_EXTERNAL_MODULE__baz_js__ from "./baz.js";
28+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
2729
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__baz_js__.baz);
2830
export { src_rslib_entry_ as default };
2931
"
@@ -44,11 +46,13 @@ test('redirect.js.path false', async () => {
4446

4547
expect(indexContent).toMatchInlineSnapshot(`
4648
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
49+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
4750
import * as __WEBPACK_EXTERNAL_MODULE__bar__ from "@/bar";
4851
import * as __WEBPACK_EXTERNAL_MODULE__foo__ from "@/foo";
4952
import * as __WEBPACK_EXTERNAL_MODULE__baz__ from "~/baz";
5053
import * as __WEBPACK_EXTERNAL_MODULE__bar_js__ from "./bar.js";
5154
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
55+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
5256
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__bar_js__.bar + __WEBPACK_EXTERNAL_MODULE__foo__.foo + __WEBPACK_EXTERNAL_MODULE__bar__.bar + __WEBPACK_EXTERNAL_MODULE__baz__.baz);
5357
export { src_rslib_entry_ as default };
5458
"
@@ -67,11 +71,13 @@ test('redirect.js.path with user override externals', async () => {
6771

6872
expect(indexContent).toMatchInlineSnapshot(`
6973
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
74+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
7075
import * as __WEBPACK_EXTERNAL_MODULE__others_bar_index_js__ from "./others/bar/index.js";
7176
import * as __WEBPACK_EXTERNAL_MODULE__others_foo_js__ from "./others/foo.js";
7277
import * as __WEBPACK_EXTERNAL_MODULE__baz_js__ from "./baz.js";
7378
import * as __WEBPACK_EXTERNAL_MODULE__bar_index_js__ from "./bar/index.js";
7479
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
80+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
7581
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__others_foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__others_bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__baz_js__.baz);
7682
export { src_rslib_entry_ as default };
7783
"
@@ -98,11 +104,13 @@ test('redirect.js.path with user override alias', async () => {
98104

99105
expect(indexContent).toMatchInlineSnapshot(`
100106
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
107+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
101108
import * as __WEBPACK_EXTERNAL_MODULE__others_bar_index_js__ from "./others/bar/index.js";
102109
import * as __WEBPACK_EXTERNAL_MODULE__others_foo_js__ from "./others/foo.js";
103110
import * as __WEBPACK_EXTERNAL_MODULE__baz_js__ from "./baz.js";
104111
import * as __WEBPACK_EXTERNAL_MODULE__bar_index_js__ from "./bar/index.js";
105112
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
113+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
106114
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__others_foo_js__.foo + __WEBPACK_EXTERNAL_MODULE__others_bar_index_js__.bar + __WEBPACK_EXTERNAL_MODULE__baz_js__.baz);
107115
export { src_rslib_entry_ as default };
108116
"
@@ -124,9 +132,11 @@ test('redirect.js.extension: false', async () => {
124132
);
125133
expect(indexContent).toMatchInlineSnapshot(`
126134
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
135+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
127136
import * as __WEBPACK_EXTERNAL_MODULE__bar_index_ts__ from "./bar/index.ts";
128137
import * as __WEBPACK_EXTERNAL_MODULE__foo_ts__ from "./foo.ts";
129138
import * as __WEBPACK_EXTERNAL_MODULE__baz_ts__ from "./baz.ts";
139+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
130140
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_ts__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_ts__.bar + __WEBPACK_EXTERNAL_MODULE__foo_ts__.foo + __WEBPACK_EXTERNAL_MODULE__bar_index_ts__.bar + __WEBPACK_EXTERNAL_MODULE__baz_ts__.baz);
131141
export { src_rslib_entry_ as default };
132142
"
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
// can not be resolved
12
import lodash from 'lodash';
3+
// can be resolved but not specified -- phantom dependency
4+
import prettier from 'prettier';
25

36
import { bar as bar2 } from '@/bar';
47
import { foo as foo2 } from '@/foo';
58
import { baz } from '~/baz';
69
import { bar } from './bar';
710
import { foo } from './foo';
811

12+
console.log('prettier: ', prettier);
13+
914
export default lodash.toUpper(foo + bar + foo2 + bar2 + baz);

tests/integration/redirect/jsNotResolved.test.ts

+7-41
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,23 @@
11
import path from 'node:path';
2-
import stripAnsi from 'strip-ansi';
3-
import { buildAndGetResults, proxyConsole, queryContent } from 'test-helper';
2+
import { buildAndGetResults, queryContent } from 'test-helper';
43
import { expect, test } from 'vitest';
54

65
test('redirect.js default', async () => {
76
const fixturePath = path.resolve(__dirname, './js-not-resolve');
8-
const { logs } = proxyConsole();
97
const contents = (await buildAndGetResults({ fixturePath, lib: ['esm0'] }))
108
.contents;
119

12-
const logStrings = logs
13-
.map((log) => stripAnsi(log))
14-
.filter((log) => log.startsWith('warn'))
15-
.sort();
16-
17-
expect(logStrings).toMatchInlineSnapshot(
18-
`
19-
[
20-
"warn Failed to resolve module "./bar.js" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
21-
"warn Failed to resolve module "./foo" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
22-
"warn Failed to resolve module "lodash" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
23-
]
24-
`,
25-
);
26-
2710
const { content: indexContent } = queryContent(
2811
contents.esm0!,
2912
/esm\/index\.js/,
3013
);
3114

3215
expect(indexContent).toMatchInlineSnapshot(`
3316
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
17+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
3418
import * as __WEBPACK_EXTERNAL_MODULE__bar_js__ from "./bar.js";
3519
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
20+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
3621
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__["default"] + __WEBPACK_EXTERNAL_MODULE__bar_js__["default"]);
3722
export { src_rslib_entry_ as default };
3823
"
@@ -41,25 +26,20 @@ test('redirect.js default', async () => {
4126

4227
test('redirect.js.path false', async () => {
4328
const fixturePath = path.resolve(__dirname, './js-not-resolve');
44-
const { logs } = proxyConsole();
4529
const contents = (await buildAndGetResults({ fixturePath, lib: ['esm1'] }))
4630
.contents;
4731

48-
const logStrings = logs
49-
.map((log) => stripAnsi(log))
50-
.filter((log) => log.startsWith('warn'));
51-
52-
expect(logStrings.length).toBe(0);
53-
5432
const { content: indexContent } = queryContent(
5533
contents.esm1!,
5634
/esm\/index\.js/,
5735
);
5836

5937
expect(indexContent).toMatchInlineSnapshot(`
6038
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
39+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
6140
import * as __WEBPACK_EXTERNAL_MODULE__bar_js__ from "./bar.js";
6241
import * as __WEBPACK_EXTERNAL_MODULE__foo_js__ from "./foo.js";
42+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
6343
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo_js__["default"] + __WEBPACK_EXTERNAL_MODULE__bar_js__["default"]);
6444
export { src_rslib_entry_ as default };
6545
"
@@ -68,34 +48,20 @@ test('redirect.js.path false', async () => {
6848

6949
test('redirect.js.extension: false', async () => {
7050
const fixturePath = path.resolve(__dirname, './js-not-resolve');
71-
const { logs } = proxyConsole();
7251
const contents = (await buildAndGetResults({ fixturePath, lib: ['esm2'] }))
7352
.contents;
7453

75-
const logStrings = logs
76-
.map((log) => stripAnsi(log))
77-
.filter((log) => log.startsWith('warn'))
78-
.sort();
79-
80-
expect(logStrings).toMatchInlineSnapshot(
81-
`
82-
[
83-
"warn Failed to resolve module "./bar.js" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
84-
"warn Failed to resolve module "./foo" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
85-
"warn Failed to resolve module "lodash" from <ROOT>/tests/integration/redirect/js-not-resolve/src/index.js. If it's an npm package, consider adding it to dependencies or peerDependencies in package.json to make it externalized.",
86-
]
87-
`,
88-
);
89-
9054
const { content: indexContent } = queryContent(
9155
contents.esm2!,
9256
/esm\/index\.js/,
9357
);
9458

9559
expect(indexContent).toMatchInlineSnapshot(`
9660
"import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";
61+
import * as __WEBPACK_EXTERNAL_MODULE_prettier__ from "prettier";
9762
import * as __WEBPACK_EXTERNAL_MODULE__bar_js__ from "./bar.js";
9863
import * as __WEBPACK_EXTERNAL_MODULE__foo__ from "./foo";
64+
console.log('prettier: ', __WEBPACK_EXTERNAL_MODULE_prettier__["default"]);
9965
const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE_lodash__["default"].toUpper(__WEBPACK_EXTERNAL_MODULE__foo__["default"] + __WEBPACK_EXTERNAL_MODULE__bar_js__["default"]);
10066
export { src_rslib_entry_ as default };
10167
"

website/docs/en/config/lib/auto-external.mdx

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ overviewHeaders: [2, 3]
44

55
# lib.autoExternal
66

7+
:::info
8+
9+
`autoExternal` is a specific configuration for bundle mode. It will not take effect in bundleless mode (set [lib.bundle](/config/lib/bundle) to `false`) since deps will not be bundled in bundleless mode.
10+
11+
:::
12+
713
- **Type:**
814

915
```ts

website/docs/en/config/lib/redirect.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ overviewHeaders: [2, 3]
66

77
:::info
88

9-
Redirect is the unique configuration for bundleless mode (set [lib.bundle](/config/lib/bundle) to `false`). It will not take effect in bundle mode where all output files are packaged into a single file, eliminating the need for import path redirection.
9+
`redirect` is the unique configuration for bundleless mode (set [lib.bundle](/config/lib/bundle) to `false`). It will not take effect in bundle mode where all output files are packaged into a single file, eliminating the need for import path redirection.
1010

1111
As bundleless mode is still under development, additional redirect configurations will be introduced in the future.
1212

website/docs/en/guide/advanced/third-party-deps.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Handle Third-Party Dependencies
22

3+
This section introduces how to handle third-party dependencies in bundle mode.
4+
35
Generally, third-party dependencies required by a project can be installed via the `install` command in the package manager. After the third-party dependencies are successfully installed, they will generally appear under `dependencies` and `devDependencies` in the project `package.json`.
46

57
```json title="package.json"

0 commit comments

Comments
 (0)