danielgindi/rollup-plugin-natives

Handle optional, environment-dependent native dependencies

Closed this issue · 9 comments

Thought process explained in greater detail here mscdex/ssh2#1201

Some of the native dependencies bundled by rollup-plugin-natives are optional, e.g. in the above example, when installing the ssh2 module, the files sshcrypto.node and cpu-features.node may or may not be present depending on the environment installing the module (In that example, Windows has both, MacOS only has sshcrypto, and Unix has none, from what I could observe), and the ssh2 module defaults to another behavior if the file cannot be found or read.

But when bundling, rollup-plugin-natives writes an explicit require var sshcrypto = require('sshcrypto.node') in the bundled js file, which fails at runtime if the file is not present.

Workaround would be to wrap the require in a try/catch
e.g. var sshcrypto;try{sshcrypto=require("./sshcrypto.node")}catch(err){};

Is it originally in try-catch clause?

Original code in ssh2

try {
binding = require('./crypto/build/Release/sshcrypto.node');
({ AESGCMCipher, ChaChaPolyCipher, GenericCipher,
AESGCMDecipher, ChaChaPolyDecipher, GenericDecipher } = binding);
} catch {}

Code in the bundled file

var sshcrypto=require("./sshcrypto.node");

var sshcrypto$1 = /#PURE/Object.freeze({
proto: null,
'default': sshcrypto
});

var require$$2 = /@PURE/getAugmentedNamespace(sshcrypto$1);

And further in the code, in method requireCrypto() so not top-level

try {
binding = require$$2;
({ AESGCMCipher, ChaChaPolyCipher, GenericCipher,
AESGCMDecipher, ChaChaPolyDecipher, GenericDecipher } = binding);
} catch {}

So it looks like we're still doing this conditionally aren't we?

Btw you may need to use ignoreTryCatch feature in commonjs plugin.

Oopsie, I copy-pasted the code that's generated after I applied the band-aid (which is wrapping in a silent try/catch)

I edited the previous message and the issue stays: A conditional require (wrapped in a try/catch, in a method) has been transformed into a top-level require which fails at runtime.

I haven't had the time to tinker with ignoreTryCatch yet, I'll have a read about that :)

I think that that's your solution. As not all modules are optional within a try-catch, it's not something that the current module should generate. It's handled by commonjs plugin.

The more I play with rollup and the less I understand this issue :(
Whatever the order in which I put the commonsJs, nodeResolve and nativePlugin plugins, and the boolean/'remove' in ignoreTryCatch, the top-level var sshcrypto=require(./sshcrypto) is generated in the bundle.

Does it become an issue with the commonJs plugin which doesn't, for some reason, detect that this specific require should be removed with ignoreTryCatch=true?

You don't want this require to be removed by commonjs, you want it to stay and inside try-catch :)

Confusion around ignoreTryCatch was solved by a fix in the docs