Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way to require.extensions with Node.js

I'm testing a bunch of React JSX components. They all need to be transpiled with React, or Babel or whatever, but we have special needs for stubbing requirements, so I'm trying to override requires with a special compiler that's run with Mocha. The solution below works well, but you'll notice that we're using require.extensions[] to capture all the .jsx files. What concerns me is that require.extensions is locked and deprecated. Is there any better way to do this?

// Install the compiler.
require.extensions['.jsx'] = function(module, filename) {
    return module._compile(transform(filename), filename);
};

Here's the whole transpiler for reference:

// Based on https://github.com/Khan/react-components/blob/master/test/compiler.js
var fs = require('fs'),
    ReactTools = require('react-tools');

// A module that exports a single, stubbed-out React Component.
var reactStub = 'module.exports = require("react").createClass({render:function(){return null;}});';

// Should this file be stubbed out for testing?
function shouldStub(filename) {
    if (!global.reactModulesToStub) return false;

    // Check if the file name ends with any stub path.
    var stubs = global.reactModulesToStub;

    for (var i = 0; i < stubs.length; i++) {
        if (filename.substr(-stubs[i].length) == stubs[i]) {
            console.log('should stub', filename);
            return true;
        }
    }
    return false;
}

// Transform a file via JSX/Harmony or stubbing.
function transform(filename) {
    if (shouldStub(filename)) {
        delete require.cache[filename];
        return reactStub;
    } else {
        var content = fs.readFileSync(filename, 'utf8');
        return ReactTools.transform(content, {harmony: true});
    }
}

// Install the compiler.
require.extensions['.jsx'] = function(module, filename) {
    return module._compile(transform(filename), filename);
};

And some links to simalar solutions...

  • https://github.com/danvk/mocha-react/issues/1
  • https://github.com/Automattic/jsx-require-extension
  • https://www.npmjs.com/package/node-jsx
  • https://github.com/olalonde/better-require
  • http://mochajs.org/#usage
  • http://nodejs.org/api/globals.html#globals_require_extensions

A solution can be forked from here: https://github.com/danvk/mocha-react

like image 919
4m1r Avatar asked Mar 05 '15 17:03

4m1r


People also ask

What can I use instead of require in NodeJS?

Use Import Instead of Require in Node App.

Which is the correct way to get extension of file in NodeJS?

extname() method is used to get the extension portion of a file path. The extension string returned from the last occurrence of a period (.) in the path to the end of the path string. If there are no periods in the file path, then an empty string is returned.

Can we use import instead of require in NodeJS?

If you want to use require module then you have to save file with '. js' extension. If you want to use import module then you have to save file with '.

How do I use require in NodeJS?

You can think of the require module as the command and the module module as the organizer of all required modules. Requiring a module in Node isn't that complicated of a concept. const config = require('/path/to/file'); The main object exported by the require module is a function (as used in the above example).


4 Answers

There are two reasons that API has been deprecated. One, the node module resolution algorithm is VERY complicated, it has to look at the specified file, if it doesn't exist it looks for that file and all the possible extensions in the keys of require.extensions, and if it's a directory, look for a package.json or index.js. Oh, and don't forget, if there is no ./ at the beginning, it looks in the node_modules directory, looking at the parent directory if it can't be found in that node_modules. Ryan Dahl said he regrets making it so complicated in his talk at JsConf 2018, and uses a much simpler module resolution algorithm in his deno project. Two, it needs more filesystem calls if there are more extensions in require.extensions, because it has to match extensionless files.

A solution to the second problem is require-extension. I haven't used it myself, but it abstracts the require.extensions API and makes it much more performant.

like image 129
coolreader18 Avatar answered Oct 23 '22 15:10

coolreader18


There is no other way to do this, and this is how everybody does transpiling (babel, etc). @uni_nake's answer - to use node-hook - is OK in that it hides this from you, but it essentially uses the same mechanism: a look in it's code shows that it uses Module._extensions, but this is the same as require.extensions, as shown by a test I wrote: https://github.com/giltayar/playing/blob/1f04f6ddc1a0028974b403b4d1974afa872edba4/javascript/node/test/is-module-extensions-same-as-require-extensions.test.js

So final answer - I would assume that nobody at Node will break Babel, and if they do, they will probably give another solution for the same problem. I would not hesitate to use it!

like image 4
giltayar Avatar answered Oct 23 '22 13:10

giltayar


I use node-hook to stub all .scss calls in my tests.

You'll see from the docs that when a matching file is required it will execute the containing string instead and is really quite powerful as it also passes you the original source.

Hope that is what you're looking for.

like image 2
peter.mouland Avatar answered Oct 23 '22 14:10

peter.mouland


I think you should use pirates

I think the PR when require.extensions used in babel-register was replaced with pirates would be helpful.

https://github.com/babel/babel/pull/3670/files#diff-75a0292ed78043766c2d5564edd84ad2L85-L93

Hope that is what you're looking for.

like image 1
yukihirop Avatar answered Oct 23 '22 14:10

yukihirop