UNPKG

7.28 kBMarkdownView Raw
1# require-hacker
2
3[![NPM Version][npm-image]][npm-url]
4[![NPM Downloads][downloads-image]][downloads-url]
5[![Build Status][travis-image]][travis-url]
6[![Test Coverage][coveralls-image]][coveralls-url]
7
8<!---
9[![Gratipay][gratipay-image]][gratipay-url]
10-->
11
12Is a small helper module providing tools for instrumenting Node.js `require()` calls.
13
14## Topics
15
16- [What it does and why is it needed?](#what-it-does-and-why-is-it-needed)
17- [Installation](#installation)
18- [Usage](#usage)
19- [Configuration](#configuration)
20- [API](#api)
21- [Gotchas](#gotchas)
22- [References](#references)
23- [Contributing](#contributing)
24
25## What it does and why is it needed?
26
27Standard Node.js `require()` calls simply loaded javascript files from disk and evaluated them.
28
29Some time after various hackers hacked the [Module module](https://github.com/nodejs/node/blob/master/lib/module.js) 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").
30
31This 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.
32
33## Installation
34
35```bash
36$ npm install require-hacker --save
37```
38
39## Usage
40
41Something basic
42
43```javascript
44import Require_hacker from 'require-hacker'
45import fs from 'fs'
46
47const require_hacker = new Require_hacker({ debug: false })
48
49// mount require() hook
50const hook = require_hacker.hook('txt', path =>
51{
52 return `module.exports = "${fs.readFileSync(path).replace(/"/g, '\"')}"`
53})
54
55// will output text file contents
56console.log(require('./test.txt'))
57
58// unmount require() hook
59hook.unmount()
60
61// will throw "SyntaxError: Unexpected token ILLEGAL"
62require('./test without hook.txt')
63```
64
65Something unusual
66
67```javascript
68const hook = require_hacker.global_hook('network', path =>
69{
70 if (!path.starts_with('http://xhamster.com'))
71 {
72 return
73 }
74
75 // returns javascript module source code, something like:
76 //
77 // "module.exports =
78 // {
79 // category : 'redhead',
80 // videos : [12345, 12346, 12347],
81 // unsubscribe: function()
82 // {
83 // http.post('http://xhamster.com/unsubscribe', { user: 123 })
84 // }
85 // }"
86 //
87 return synchronous_http.get(path)
88})
89
90const readheads = require('http://xhamster.com/category/redhead')
91readheads.unsubscribe()
92```
93
94Or
95
96```javascript
97const hook = require_hacker.global_hook('database', path =>
98{
99 if (!path.starts_with('postgresql://'))
100 {
101 return
102 }
103
104 // returns javascript module source code, something like:
105 //
106 // "module.exports =
107 // {
108 // words: ['a', 'b', 'c']
109 // sum: function()
110 // {
111 // return words.join('')
112 // }
113 // }"
114 //
115 const schema = path.substring(0, 'postgresql://'.length)
116 return pg.sql(`select * from ${schema}.generate_javascript()`)
117})
118
119const summator = require('postgresql://summator')
120console.log(summator.sum())
121```
122
123And don't ask me what for.
124
125## Configuration
126
127Available configuration parameters:
128
129```javascript
130{
131 // debug mode.
132 // when set to true, lets you see debugging messages in the console.
133 debug: true // is false by default
134}
135```
136
137## API
138
139#### Constructor
140
141Takes an object with options (see [Configuration](#configuration) section above)
142
143#### .hook(file_extension, resolve)
144
145Will intercept all `require()` calls for paths with this `file_extension` and reroute them to the `resolve` function.
146
147Returns an object with `.unmount()` method which unmounts this `require()` hook from the system.
148
149The `resolve` function takes one parameter: the `path` which is `require()`d.
150
151The `resolve` function must return either a valid CommonJS javascript module source code or it can simply `return` nothing and in that case it will skip this hook.
152
153#### .global_hook(meaningful_id, resolve, [options])
154
155Can intercept all `require()` calls. The behaviour is controlled by `precede_node_loader` option:
156
157 * when it's `false` (default) it will intercept only those `require()` calls which failed to be resolved by the original Node.js `require()` loader
158 * when it's `true` it will intercept all `require()` calls before they are passed to the original Node.js `require()` loader
159
160Returns an object with `.unmount()` method which unmounts this `require()` hook from the system.
161
162The `resolve` function takes one parameter: the `path` which is `require()`d.
163
164The `resolve` function must return either a valid CommonJS javascript module source code or it can simply `return` nothing and in that case it will skip this resolver.
165
166#### (static) .to_javascript_module_source(anything)
167
168Converts anyting (an undefined, a string, a JSON object, a function, a regular expression - anything) to a valid CommonJS javascript module source code.
169
170## Gotchas
171
172None whatsoever
173
174## References
175
176There are various articles on this sort of `require()` hook trickery on the internets.
177
178[How require() actually works](http://thenodeway.io/posts/how-require-actually-works/)
179
180[Hooking into Node loader for fun and profit](http://glebbahmutov.com/blog/hooking-into-node-loader-for-fun-and-profit/)
181
182## Contributing
183
184After cloning this repo, ensure dependencies are installed by running:
185
186```sh
187npm install
188```
189
190This module is written in ES6 and uses [Babel](http://babeljs.io/) for ES5
191transpilation. Widely consumable JavaScript can be produced by running:
192
193```sh
194npm run build
195```
196
197Once `npm run build` has run, you may `import` or `require()` directly from
198node.
199
200After developing, the full test suite can be evaluated by running:
201
202```sh
203npm test
204```
205
206While actively developing, one can use (personally I don't use it)
207
208```sh
209npm run watch
210```
211
212in a terminal. This will watch the file system and run tests automatically
213whenever you save a js file.
214
215When you're ready to test your new functionality on a real project, you can run
216
217```sh
218npm run tryout
219```
220
221It will `build`, `test` and then create a `.tar.gz` archive which you can then install in your project folder
222
223```sh
224npm install [module name with version].tar.gz
225```
226
227## License
228
229[MIT](LICENSE)
230[npm-image]: https://img.shields.io/npm/v/require-hacker.svg
231[npm-url]: https://npmjs.org/package/require-hacker
232[travis-image]: https://img.shields.io/travis/halt-hammerzeit/require-hacker/master.svg
233[travis-url]: https://travis-ci.org/halt-hammerzeit/require-hacker
234[downloads-image]: https://img.shields.io/npm/dm/require-hacker.svg
235[downloads-url]: https://npmjs.org/package/require-hacker
236[coveralls-image]: https://img.shields.io/coveralls/halt-hammerzeit/require-hacker/master.svg
237[coveralls-url]: https://coveralls.io/r/halt-hammerzeit/require-hacker?branch=master
238
239<!---
240[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg
241[gratipay-url]: https://gratipay.com/dougwilson/
242-->
\No newline at end of file