philikon / ReactNativify

How to use node.js libraries in React Native
242 stars 23 forks source link

Built-in alternative to babel-plugin-rewrite-require (extraNodeModules) #4

Open parshap opened 7 years ago

parshap commented 7 years ago

Update

See this library for convenience: https://github.com/parshap/node-libs-react-native

And this gist for more details: https://gist.github.com/parshap/e3063d9bf6058041b34b26b7166fd6bd

Original post

The React Native Packager has a built-in option, extraNodeModules, which seems to act as a module alias mapping. I couldn't find much information about this option, but here's the PR where it was added: https://github.com/facebookarchive/node-haste/pull/69. Here's my rn-cli.config.js:

const extraNodeModules = require('node-libs-browser');

module.exports = {
  extraNodeModules,
};

And it seems to be working! I had to find this alternative approach because babel-plugin-rewrite-require was failing on a module with a dynamic require: readable-stream does require('st'+'ream').

This may be a better approach than using babel-plugin-rewrite-require because it doesn't need a custom transformer.js or a static mapping in .babelrc, and it seems to work on dynamic requires!

@davidaurelio: Is this an appropriate use of the extraNodeModules option?

/cc @mvayngrib

mvayngrib commented 7 years ago

@parshap thanks for cc'ing me, hope to check it out very soon!

philikon commented 7 years ago

Woah! I'll check it out for sure!

And it seems to be working! I had to find this alternative approach because babel-plugin-rewrite-require was failing on a module with a dynamic require: readable-stream does require('st'+'ream').

This is what the throwForNonStringLiteral option in babel-plugin-rewrite-require is meant for. You want to enable it when you're using browserify modules (e.g. via node-libs-browser).

wswoodruff commented 7 years ago

Hey @parshap I'm running react-native 0.44.1 here, tried the extraNodeModules approach with node-libs-browser and I'm getting Unable to resolve net when trying to require a node package. Any reasons you can think of why that might not be working?

parshap commented 7 years ago

node-libs-browser doesn't include a "polyfill" for net. You'll have to include react-native-tcp.

Also check out https://www.npmjs.com/package/node-libs-react-native

wswoodruff commented 7 years ago

Yo @parshap! react-native-tcp is giving me this error when I require it in rn-cli.config.js:

.../project/path/node_modules/react-native-tcp/TcpSockets.js:16
exports.createServer = function(connectionListener: (socket: Socket)  => void) : Server {
                                                  ^
SyntaxError: Unexpected token :
    at Object.exports.runInThisContext (vm.js:76:16)

I'm not sure, I've also seen modules brought in via rn-cli.config.js trip over import statements. So confusing! Does rn-cli.config.js get babel transpiles or not? I'm using const and other es6 features, it didn't catch an object rest feature either. So it threw for that on:

{ ...extraNodeModules, net: require('react-native-tcp') }
wswoodruff commented 7 years ago

The only thing that works for me right now is this .babelrc file. I deleted my rn-cli.config.js!

{
  "presets": ["react-native"],
  "sourceMaps": true,
  "plugins": [
    ["rewrite-require", {
        "aliases": {
            "constants": "constants-browserify",
            "crypto": "react-native-crypto",
            "dns": "node-libs-browser/mock/dns",
            "domain": "domain-browser",
            "fs": "node-libs-browser/mock/empty",
            "http": "stream-http",
            "https": "https-browserify",
            "net": "node-libs-browser/mock/net",
            "os": "os-browserify/browser",
            "path": "path-browserify",
            "pbkdf2": "react-native-pbkdf2-shim",
            "querystring": "querystring-es3",
            "stream": "stream-browserify",
            "_stream_duplex": "readable-stream/duplex",
            "_stream_passthrough": "readable-stream/passthrough",
            "_stream_readable": "readable-stream/readable",
            "_stream_transform": "readable-stream/transform",
            "_stream_writable": "readable-stream/writable",
            "sys": "util",
            "timers": "timers-browserify",
            "tls": "node-libs-browser/mock/tls",
            "tty": "tty-browserify",
            "vm": "vm-browserify",
            "zlib": "browserify-zlib"
        },
        "throwForNonStringLiteral": true
    }]
  ]
}

and react-native-pbkdf2-shim looks like this in package.json:

"react-native-pbkdf2-shim": "git+https://git@github.com/wswoodruff/react-native-pbkdf2-shim.git",
parshap commented 7 years ago

@wswoodruff: extraNodeModules should have paths to packages, not imported packages themselves.

module.exports = {
  extraNodeModules: {
    ...extraNodeModules,
    net: require.resolve('react-native-tcp'),
  },
};

See what node-libs-react-native itself exports.

tslater commented 7 years ago

@parshap I'm having trouble with your approach. Are you doing anything besides adding your rn-cli.config.js? I'm having trouble trying to load the bitcoin example. It seems like the module loading isn't working. I get:

bscript.compile is not a function. (In 'bscript.compile([OPS.OP_DUP, OPS.OP_HASH160, pubKeyHash, OPS.OP_EQUALVERIFY, OPS.OP_CHECKSIG])', 'bscript.compile' is undefined)

encode
    output.js:26:25
toOutputScript
    address.js:42:85
addOutput
    transaction_builder.js:592:43
example
    bitcoin_example.js:13:15
<unknown>
    rn.js:16:6
run
    browser.js:153:19
drainQueue
    browser.js:123:16
callTimer
    JSTimersExecution.js:96:8
callTimers
    JSTimersExecution.js:138:34
__callFunction
    MessageQueue.js:260:47
<unknown>
    MessageQueue.js:101:26
__guard
    MessageQueue.js:228:6
callFunctionReturnFlushedQueue
    MessageQueue.js:100:17

In the original file: var bscript = require('../../script') seems to be loading an empty object, instead of the one exported in ../../script

mnzaki commented 5 years ago

For people arriving in the future:

To use this properly now you should edit your metro.config.js (not rn-cli.config.js anymore) and add a resolver section like so:

module.exports = {
  resolver: {
    extraNodeModules: {
      // Polyfills for node libraries
      "crypto": require.resolve("crypto-browserify"),
      "stream": require.resolve("stream-browserify")
    }
  },
  // other metro config, etc
}

extraNodeModules docs: https://facebook.github.io/metro/docs/en/configuration#extranodemodules

pcowgill commented 4 years ago

@philikon Do you think this ^ alternative approach is worth documenting in the README?

pcowgill commented 4 years ago

@parshap Would you consider updating your gist to say metro.config.js rather than rn-cli.config.js and to reflect the info in the comment in this issue that require('st'+'ream') is possible with babel-plugin-rewrite-require using the throwForNonStringLiteral option?

parshap commented 4 years ago

Updated, thanks! Can you clarify what you mean by the throwForNonStringLiteral option and make a specific suggestion in the gist?

pcowgill commented 4 years ago

@parshap Thanks!!

The author of ReactNativify mentioned that that's an option in babel-plugin-rewrite-require in this comment

Here is the feature itself: https://www.npmjs.com/package/babel-plugin-rewrite-require#non-string-literals

I don't think there's a way to make suggestions in the gist directly other than adding a comment, but my suggestion would be to remove this line:

"not supporting require() calls with an expression (such as require('cyrp' + 'to'))."

...since it does support that if you use the throwForNonStringLiteral setting.

eliw00d commented 3 years ago

Does this work if crypto is required by a dependency of a dependency? For example, we use yarn workspaces and have a shared library. That library depends on crypto-hash which requires crypto. If I set crypto as an extraNodeModule, will that get triggered for the library's usage of crypto? If not, what would be a way to get this to work?

JamesGs1994 commented 1 year ago

getting error for fs: unable to resolve fs I am using google cloud/logging package for logs image