Skip to content

Commit 260c3c2

Browse files
committed
Fix dual ESM/CJS support on various JS runtimes
Node.js does not support 'await import' with binaries
1 parent c7774c0 commit 260c3c2

4 files changed

Lines changed: 46 additions & 38 deletions

File tree

lib/libvips.mjs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55

66
import { spawnSync } from 'node:child_process';
77
import { createHash } from 'node:crypto';
8-
import semverCoerce from 'semver/functions/coerce';
9-
import semverGreaterThanOrEqualTo from 'semver/functions/gte';
10-
import semverSatisfies from 'semver/functions/satisfies';
8+
import semver from 'semver';
119
import detectLibc from 'detect-libc';
1210
import pkg from '../package.json' with { type: 'json' };
1311

1412
/* node:coverage ignore next */
1513
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || pkg.config.libvips;
16-
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
14+
const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
1715

1816
const prebuiltPlatforms = [
1917
'darwin-arm64', 'darwin-x64',
@@ -82,7 +80,7 @@ const buildSharpLibvipsLibDir = () => {
8280

8381
const isUnsupportedNodeRuntime = () => {
8482
if (process.release?.name === 'node' && process.versions) {
85-
if (!semverSatisfies(process.versions.node, pkg.engines.node)) {
83+
if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
8684
return { found: process.versions.node, expected: pkg.engines.node };
8785
}
8886
}
@@ -108,7 +106,7 @@ const sha512 = (s) => createHash('sha512').update(s).digest('hex');
108106
const yarnLocator = () => {
109107
try {
110108
const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
111-
const npmVersion = semverCoerce(pkg.optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`], {
109+
const npmVersion = semver.coerce(pkg.optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`], {
112110
includePrerelease: true
113111
}).version;
114112
return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
@@ -193,7 +191,7 @@ const useGlobalLibvips = (logger) => {
193191
}
194192
const globalVipsVersion = globalLibvipsVersion();
195193
/* node:coverage ignore next */
196-
return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
194+
return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
197195
};
198196

199197
export default {

lib/sharp.mjs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,30 @@
55

66
// Inspects the runtime environment and exports the relevant sharp.node binary
77

8+
import { createRequire } from "node:module"
89
import { familySync, versionSync } from "detect-libc";
910

10-
import { version } from '../package.json';
11-
import { runtimePlatformArch, isUnsupportedNodeRuntime, prebuiltPlatforms, minimumLibvipsVersion } from "./libvips.mjs";
11+
import libvips from "./libvips.mjs";
12+
import pkg from '../package.json' with { type: 'json' };
13+
14+
const require = createRequire(import.meta.url);
15+
const { version } = pkg;
16+
17+
const { runtimePlatformArch, isUnsupportedNodeRuntime, prebuiltPlatforms, minimumLibvipsVersion } = libvips;
1218
const runtimePlatform = runtimePlatformArch();
1319

1420
/* node:coverage disable */
1521

1622
let sharp;
1723
const errors = [];
1824
try {
19-
sharp = await import(`../src/build/Release/sharp-${runtimePlatform}-${version}.node`);
25+
sharp = require(`../src/build/Release/sharp-${runtimePlatform}-${version}.node`);
2026
} catch (err) {
2127
errors.push(err);
2228
}
2329
if (!sharp) {
2430
try {
25-
sharp = await import(`../src/build/Release/sharp-wasm32-${version}.node`);
31+
sharp = require(`../src/build/Release/sharp-wasm32-${version}.node`);
2632
} catch (err) {
2733
errors.push(err);
2834
}
@@ -31,53 +37,53 @@ if (!sharp) {
3137
try {
3238
switch (runtimePlatform) {
3339
case "darwin-arm64":
34-
sharp = await import("@img/sharp-darwin-arm64/sharp.node");
40+
sharp = require("@img/sharp-darwin-arm64/sharp.node");
3541
break;
3642
case "darwin-x64":
37-
sharp = await import("@img/sharp-darwin-x64/sharp.node");
43+
sharp = require("@img/sharp-darwin-x64/sharp.node");
3844
break;
3945
case "linux-arm":
40-
sharp = await import("@img/sharp-linux-arm/sharp.node");
46+
sharp = require("@img/sharp-linux-arm/sharp.node");
4147
break;
4248
case "linux-arm64":
43-
sharp = await import("@img/sharp-linux-arm64/sharp.node");
49+
sharp = require("@img/sharp-linux-arm64/sharp.node");
4450
break;
4551
case "linux-ppc64":
46-
sharp = await import("@img/sharp-linux-ppc64/sharp.node");
52+
sharp = require("@img/sharp-linux-ppc64/sharp.node");
4753
break;
4854
case "linux-riscv64":
49-
sharp = await import("@img/sharp-linux-riscv64/sharp.node");
55+
sharp = require("@img/sharp-linux-riscv64/sharp.node");
5056
break;
5157
case "linux-s390x":
52-
sharp = await import("@img/sharp-linux-s390x/sharp.node");
58+
sharp = require("@img/sharp-linux-s390x/sharp.node");
5359
break;
5460
case "linux-x64":
55-
sharp = await import("@img/sharp-linux-x64/sharp.node");
61+
sharp = require("@img/sharp-linux-x64/sharp.node");
5662
break;
5763
case "linuxmusl-arm64":
58-
sharp = await import("@img/sharp-linuxmusl-arm64/sharp.node");
64+
sharp = require("@img/sharp-linuxmusl-arm64/sharp.node");
5965
break;
6066
case "linuxmusl-x64":
61-
sharp = await import("@img/sharp-linuxmusl-x64/sharp.node");
67+
sharp = require("@img/sharp-linuxmusl-x64/sharp.node");
6268
break;
6369
case "win32-arm64":
64-
sharp = await import("@img/sharp-win32-arm64/sharp.node");
70+
sharp = require("@img/sharp-win32-arm64/sharp.node");
6571
break;
6672
case "win32-ia32":
67-
sharp = await import("@img/sharp-win32-ia32/sharp.node");
73+
sharp = require("@img/sharp-win32-ia32/sharp.node");
6874
break;
6975
case "win32-x64":
70-
sharp = await import("@img/sharp-win32-x64/sharp.node");
76+
sharp = require("@img/sharp-win32-x64/sharp.node");
7177
break;
7278
case "freebsd-arm64":
7379
case "freebsd-x64":
74-
sharp = await import("@img/sharp-freebsd-wasm32/sharp.node");
80+
sharp = require("@img/sharp-freebsd-wasm32/sharp.node");
7581
break;
7682
case "linux-wasm32":
77-
sharp = await import("@img/sharp-webcontainers-wasm32/sharp.node");
83+
sharp = require("@img/sharp-webcontainers-wasm32/sharp.node");
7884
break;
7985
default:
80-
sharp = await import("@img/sharp-wasm32/sharp.node");
86+
sharp = require("@img/sharp-wasm32/sharp.node");
8187
break;
8288
}
8389
if (["linux-x64", "linuxmusl-x64"].includes(runtimePlatform) && !sharp._isUsingX64V2()) {
@@ -96,7 +102,7 @@ if (!sharp) {
96102

97103
const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
98104
errors.forEach((err) => {
99-
if (err.code !== "MODULE_NOT_FOUND") {
105+
if (!err.code.endsWith("MODULE_NOT_FOUND")) {
100106
help.push(`${err.code}: ${err.message}`);
101107
}
102108
});
@@ -127,7 +133,7 @@ if (!sharp) {
127133
}
128134
if (isLinux && /(symbol not found|CXXABI_)/i.test(messages)) {
129135
try {
130-
const { config } = await import(`@img/sharp-libvips-${runtimePlatform}/package`);
136+
const { config } = require(`@img/sharp-libvips-${runtimePlatform}/package`);
131137
const libcFound = `${familySync()} ${versionSync()}`;
132138
const libcRequires = `${config.musl ? "musl" : "glibc"} ${config.musl || config.glibc}`;
133139
help.push("- Update your OS:", ` Found ${libcFound}`, ` Requires ${libcRequires}`);

lib/utility.mjs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
*/
55

66
import events from 'node:events';
7+
import { createRequire } from "node:module";
8+
import { availableParallelism } from "node:os";
79
import detectLibc from 'detect-libc';
810

911
import is from './is.mjs';
10-
import { runtimePlatformArch } from './libvips.mjs';
12+
import libvips from './libvips.mjs';
1113
import sharp from './sharp.mjs';
1214
import pkg from "../package.json" with { type: "json" };
1315

14-
const runtimePlatform = runtimePlatformArch();
16+
const require = createRequire(import.meta.url);
17+
const runtimePlatform = libvips.runtimePlatformArch();
1518
const libvipsVersion = sharp.libvipsVersion();
1619

1720
/**
@@ -62,15 +65,15 @@ let versions = {
6265
if (!libvipsVersion.isGlobal) {
6366
if (!libvipsVersion.isWasm) {
6467
try {
65-
versions = await import(`@img/sharp-${runtimePlatform}/versions`);
68+
versions = require(`@img/sharp-${runtimePlatform}/versions`);
6669
} catch (_) {
6770
try {
68-
versions = await import(`@img/sharp-libvips-${runtimePlatform}/versions`);
71+
versions = require(`@img/sharp-libvips-${runtimePlatform}/versions`);
6972
} catch (_) {}
7073
}
7174
} else {
7275
try {
73-
versions = await import('@img/sharp-wasm32/versions');
76+
versions = require('@img/sharp-wasm32/versions');
7477
} catch (_) {}
7578
}
7679
}
@@ -157,7 +160,6 @@ if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
157160
sharp.concurrency(1);
158161
} else if (detectLibc.familySync() === detectLibc.MUSL && sharp.concurrency() === 1024) {
159162
// Reduce default concurrency when musl thread over-subscription detected
160-
const { availableParallelism } = await import('node:os')
161163
sharp.concurrency(availableParallelism());
162164
}
163165

scripts/build.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ function cjsToEsm(input) {
2121
)
2222
.replace(/import\s+('[^']+'|"[^"]+")(?:\s+with\s+\{[^}]*\})?;?/g, (_, path) => `require(${path.replace(".mjs", ".cjs")});`)
2323
// Transforms TLA
24-
.replaceAll("await import(", "require(")
2524
.replaceAll("export default", "module.exports =")
25+
// Remove createRequire
26+
.replaceAll("const { createRequire } = require(\"node:module\");\n", "")
27+
.replaceAll("const require = createRequire(import.meta.url);\n", "")
2628
}
2729

28-
const entries = (await fs.readdir(libDir, { recursive: true })).filter(e => e.endsWith('.mjs'));
30+
const entries = (await fs.readdir(libDir)).filter(e => e.endsWith('.mjs'));
2931

3032
for (const entry of entries) {
3133
await fs.cp(new URL(entry, libDir), new URL(entry, distDir));
3234
const contents = await fs.readFile(new URL(entry, libDir), "utf-8");
3335
await fs.writeFile(new URL(entry.replace(".mjs", ".cjs"), distDir), cjsToEsm(contents));
34-
}
36+
}

0 commit comments

Comments
 (0)