require-hacker
v3.0.1
Published
Provides a hooking mechanism for Node.js require() calls
Downloads
39,184
Readme
require-hacker
Is a small helper module providing tools for instrumenting Node.js require()
calls.
Topics
What it does and why is it needed?
Standard Node.js require()
calls simply loaded javascript files from disk and evaluated them.
Some time after various hackers hacked the Module module and various solutions emerged such as coffee-script/register
and babel-core/register
allowing everyone to require()
code written in any language out there (coffeescript and ES7 in case of the aforementioned "require hooks").
This module provides a tool to perform such tricks along with a possibility to also intercept require()
calls not just for specific file extensions but for an arbitrary abstract path. Consider, for example, require("http://thor.onion/module?user=123")
or require("春秋左傳·僖公二十二年")
, whatever. Who might need this? You never know.
Installation
$ npm install require-hacker --save
Usage
Something basic
import require_hacker from 'require-hacker'
import fs from 'fs'
// mount require() hook
const hook = require_hacker.hook('txt', path =>
{
return `module.exports = "${fs.readFileSync(path).replace(/"/g, '\"')}"`
})
// will output text file contents
console.log(require('./test.txt'))
// unmount require() hook
hook.unmount()
// will throw "SyntaxError: Unexpected token ILLEGAL"
require('./test without hook.txt')
Something unusual
const hook = require_hacker.global_hook('network', path =>
{
if (!path.starts_with('http://xhamster.com'))
{
return
}
// returns javascript module source code, something like:
//
// "module.exports =
// {
// category : 'redhead',
// videos : [12345, 12346, 12347],
// unsubscribe: function()
// {
// http.post('http://xhamster.com/unsubscribe', { user: 123 })
// }
// }"
//
const source = synchronous_http.get(path)
return { source, path }
})
const readheads = require('http://xhamster.com/category/redhead')
readheads.unsubscribe()
Or
const hook = require_hacker.global_hook('database', path =>
{
if (!path.starts_with('postgresql://'))
{
return
}
// returns javascript module source code, something like:
//
// "module.exports =
// {
// words: ['a', 'b', 'c']
// sum: function()
// {
// return words.join('')
// }
// }"
//
const schema = path.substring(0, 'postgresql://'.length)
const source pg.sql(`select * from ${schema}.generate_javascript()`)
return { source, path }
})
const summator = require('postgresql://summator')
console.log(summator.sum())
And don't ask me what for.
Configuration
To see debug logs in the console one can use this code
require_hacker.log.options.debug = true
API
.hook(file_extension, resolve)
Will intercept all require()
calls for paths with this file_extension
and reroute them to the resolve
function. The require()
d path must exist in the filesystem, otherwise an exception will be thrown: Cannot find module
.
Returns an object with .unmount()
method which unmounts this require()
hook from the system.
The resolve
function takes two parameters:
- the
path
which isrequire()
d - the
module
in which therequire()
call was originated (thismodule
parameter can be used forrequire_hacker.resolve(path, module)
function call)
The resolve
function must return either a valid CommonJS javascript module source code (i.e. "module.exports = ...", etc) or it can simply return
nothing and in that case it will skip this hook.
.global_hook(meaningful_id, resolve, [options])
Can intercept all require()
calls. The behaviour is controlled by precede_node_loader
option:
- when it's
true
(default) it will intercept allrequire()
calls before they are passed to the original Node.jsrequire()
loader - when it's
false
it will intercept only thoserequire()
calls which failed to be resolved by the original Node.jsrequire()
loader
Returns an object with .unmount()
method which unmounts this require()
hook from the system.
The resolve
function takes two parameters:
- the
path
which isrequire()
d (e.g. a relative one) - the
module
in which therequire()
call was originated (thismodule
parameter can be used forrequire_hacker.resolve(path, module)
function call)
The resolve
function must return either undefined
(in which case it will skip this hook and proceed as normal) or an object { source, path }
where
source
is a valid CommonJS javascript module source code (i.e. "module.exports = ...", etc)path
is the absolute path of thepath
argument passed to thisrequire()
function (which could be relative). This returnedpath
is only gonna matter ifrequire()
ing some other relative path from thesource
being returned (because it would get resolved against this absolutepath
).
.resolver(resolve)
Can intercept all require(path)
calls and tamper with the path
modifying it if needed (this process is called "resolving").
Returns an object with .unmount()
method which unmounts this interceptor.
The resolve
function takes two parameters:
- the
path
which isrequire()
d. - the
module
in which therequire()
call was originated (thismodule
parameter can be used forrequire_hacker.resolve(path, module)
function call)
The resolve
function must either return a real filesystem path to a javascript (or json) file or it can simply return
nothing and in that case it will take no effect.
.to_javascript_module_source(anything)
Converts anyting (an undefined, a string, a JSON object, a function, a regular expression - anything) to a valid CommonJS javascript module source code.
.resolve(path, module)
Resolves a requireable path
to a real filesystem path to a javascript (or json) file. Resolution is performed relative to the module
(javascript file) passed as the second parameter (resolves npm link
, global node_modules
, etc). It's just an alias to the native Node.js path resolution function. Will throw Error: Cannot find module '...'
if the path
isn't resolved to an existing javascript (or json) file.
Gotchas
None whatsoever
References
There are various articles on this sort of require()
hook trickery on the internets.
Hooking into Node loader for fun and profit
Contributing
After cloning this repo, ensure dependencies are installed by running:
npm install
This module is written in ES6 and uses Babel for ES5 transpilation. Widely consumable JavaScript can be produced by running:
npm run build
Once npm run build
has run, you may import
or require()
directly from
node.
After developing, the full test suite can be evaluated by running:
npm test
When you're ready to test your new functionality on a real project, you can run
npm pack
It will build
, test
and then create a .tgz
archive which you can then install in your project folder
npm install [module name with version].tar.gz