diff --git a/tools/egg-bundler/src/lib/ExternalsResolver.ts b/tools/egg-bundler/src/lib/ExternalsResolver.ts index b4aab83460..3a71fb61ee 100644 --- a/tools/egg-bundler/src/lib/ExternalsResolver.ts +++ b/tools/egg-bundler/src/lib/ExternalsResolver.ts @@ -95,6 +95,7 @@ export class ExternalsResolver { const pkg = await this.#readPackageJson(pkgDir); if (await this.#hasMissingOptionalPeerDependencies(pkgDir, pkg)) return true; if (await this.#hasNativeBinary(pkgDir, pkg)) return true; + if (await this.#hasNativeOptionalDependency(pkgDir, pkg)) return true; return false; } @@ -108,6 +109,17 @@ export class ExternalsResolver { return false; } + async #hasNativeOptionalDependency(pkgDir: string, pkg: PackageJson): Promise { + const optionalDependencies = pkg.optionalDependencies ?? {}; + for (const depName of Object.keys(optionalDependencies)) { + const depDir = await this.#findPackageDir(depName, pkgDir); + if (!depDir) continue; + const depPkg = await this.#readPackageJson(depDir); + if (await this.#hasNativeBinary(depDir, depPkg)) return true; + } + return false; + } + async #findPackageDir(name: string, fromDir = this.#baseDir): Promise { const cacheKey = `${fromDir}\0${name}`; const cached = this.#packageDirCache.get(cacheKey); diff --git a/tools/egg-bundler/test/ExternalsResolver.test.ts b/tools/egg-bundler/test/ExternalsResolver.test.ts index 90ed52cedb..2b2ddb5cc6 100644 --- a/tools/egg-bundler/test/ExternalsResolver.test.ts +++ b/tools/egg-bundler/test/ExternalsResolver.test.ts @@ -31,6 +31,11 @@ describe('ExternalsResolver', () => { const result = await new ExternalsResolver({ baseDir: basicApp }).resolve(); expect(result['native-dotnode']).toBe('native-dotnode'); }); + + it('externalizes a wrapper package whose installed optional dependency is native', async () => { + const result = await new ExternalsResolver({ baseDir: basicApp }).resolve(); + expect(result['native-optional-wrapper']).toBe('native-optional-wrapper'); + }); }); describe('tier 2: ESM-only packages', () => { @@ -131,6 +136,11 @@ describe('ExternalsResolver', () => { expect(result['normal-js']).toBeUndefined(); }); + it('leaves a package with only plain JS optional dependencies out of the externals map', async () => { + const result = await new ExternalsResolver({ baseDir: basicApp }).resolve(); + expect(result['optional-js-wrapper']).toBeUndefined(); + }); + it('does not externalize a declared dep that is not installed and matches no rule', async () => { const result = await new ExternalsResolver({ baseDir: basicApp }).resolve(); expect(result['missing-pkg']).toBeUndefined(); diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/addon.node b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/addon.node new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/addon.node @@ -0,0 +1 @@ + diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/package.json b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/package.json new file mode 100644 index 0000000000..1aeaaa6447 --- /dev/null +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-platform/package.json @@ -0,0 +1,4 @@ +{ + "name": "native-optional-platform", + "version": "1.0.0" +} diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-wrapper/package.json b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-wrapper/package.json new file mode 100644 index 0000000000..0512f9d804 --- /dev/null +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/native-optional-wrapper/package.json @@ -0,0 +1,7 @@ +{ + "name": "native-optional-wrapper", + "version": "1.0.0", + "optionalDependencies": { + "native-optional-platform": "1.0.0" + } +} diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-dep/package.json b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-dep/package.json new file mode 100644 index 0000000000..19ead70bf8 --- /dev/null +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-dep/package.json @@ -0,0 +1,4 @@ +{ + "name": "optional-js-dep", + "version": "1.0.0" +} diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-wrapper/package.json b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-wrapper/package.json new file mode 100644 index 0000000000..d6a573f9cc --- /dev/null +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-js-wrapper/package.json @@ -0,0 +1,7 @@ +{ + "name": "optional-js-wrapper", + "version": "1.0.0", + "optionalDependencies": { + "optional-js-dep": "1.0.0" + } +} diff --git a/tools/egg-bundler/test/fixtures/externals/basic-app/package.json b/tools/egg-bundler/test/fixtures/externals/basic-app/package.json index f839a9d619..88ffffa893 100644 --- a/tools/egg-bundler/test/fixtures/externals/basic-app/package.json +++ b/tools/egg-bundler/test/fixtures/externals/basic-app/package.json @@ -12,9 +12,11 @@ "missing-pkg": "1.0.0", "native-binding": "1.0.0", "native-dotnode": "1.0.0", + "native-optional-wrapper": "1.0.0", "native-prebuilds": "1.0.0", "native-scripts": "1.0.0", "normal-js": "1.0.0", + "optional-js-wrapper": "1.0.0", "optional-peer-host": "1.0.0" }, "peerDependencies": {