├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── README.md
├── index.js
├── package.json
├── src
├── main.js
└── rend.js
└── test
├── main-to-rend
├── index.html
├── index.js
└── rend.js
└── rend-to-main
├── index.html
├── index.js
└── rend.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test/
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 0.2.0 / 2016-03-19
2 | ------------------
3 | - upgraded to `ipc-main`/`ipc-renderer` [#1][#1]
4 |
5 | 0.1.0 / 2015-07-11
6 | ------------------
7 | - initial release
8 |
9 |
10 | [#1]: https://github.com/jprichardson/electron-ipc-stream/pull/1
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | electron-ipc-stream
2 | ===================
3 |
4 | Duplex stream that run over [Electron's IPC](https://github.com/atom/electron/tree/master/docs) mechanism.
5 |
6 |
7 | Why?
8 | ---
9 |
10 | This allows you to use any Node.js stream readable/writable and easily communicate between your
11 | main/renderer process.
12 |
13 | Since your `renderer` process is also responsible for UI/DOM, etc, you may not want to do any heavy
14 | processing on the renderer process. You could leverage this module to have the renderer stream
15 | data to the `main` process for processing and then the `main` module could stream results
16 | back to the `renderer` process for consumption.
17 |
18 |
19 | Install
20 | -------
21 |
22 | npm i --save electron-ipc-stream
23 |
24 |
25 | Usage
26 | -----
27 |
28 | ### Example 1: Pipe file from main process to renderer.
29 |
30 | **main.js:**
31 |
32 | ```js
33 | var app = require('app')
34 | var fs = require('fs')
35 | var path = require('path')
36 | var window = require('electron-window')
37 | var IPCStream = require('electron-ipc-stream')
38 |
39 | app.on('ready', function () {
40 | var win = window.createWindow({ height: 600, with: 1000 })
41 |
42 | var ipcs = new IPCStream('any-arbitrary-channel-name', win)
43 | win.showUrl(path.resolve(__dirname, './index.html'), function () {
44 | // window is visible, dom is ready in window
45 | fs.createReadStream('/tmp/mainfile').pipe(ipcs)
46 | })
47 | })
48 | ```
49 |
50 | **rend.js:**
51 |
52 | ```js
53 | var fs = require('fs')
54 | var ipc = require('ipc')
55 | var IPCStream = require('electron-ipc-stream')
56 | var ipcs = new IPCStream('any-arbitrary-channel-name')
57 |
58 | document.addEventListener('DOMContentLoaded', function () {
59 | ipcs.pipe(fs.createWriteStream('/tmp/rendfile')).on('finish', function () {
60 | console.log('done')
61 | })
62 | })
63 | ```
64 |
65 |
66 | ### Example 2: Pipe file from renderer process to main.
67 |
68 | **main.js:**
69 |
70 | ```js
71 | var app = require('app')
72 | var fs = require('fs')
73 | var path = require('path')
74 | var window = require('electron-window')
75 | var IPCStream = require('electron-ipc-stream')
76 |
77 | var tmpfile = '/tmp/mainfile'
78 | app.on('ready', function () {
79 | var win = window.createWindow({ height: 600, with: 1000 })
80 | var ipcs = new IPCStream('any-arbitrary-channel-name', win)
81 | ipcs.pipe(fs.createWriteStream(tmpfile)).on('finish', function () {
82 | console.log('done')
83 | })
84 | win.showUrl(path.resolve(__dirname, './index.html'), function () { })
85 | })
86 | ```
87 |
88 | **rend.js:**
89 |
90 | ```js
91 | var crypt = require('crypto') // notice this is 'crypt' and not 'crypto'
92 | var fs = require('fs')
93 | var ipc = require('ipc')
94 | var IPCStream = require('electron-ipc-stream')
95 | var ipcs = new IPCStream('any-arbitrary-channel-name')
96 |
97 | fs.writeFileSync('/tmp/rendfile', crypt.randomBytes(10000))
98 | document.addEventListener('DOMContentLoaded', function () {
99 | fs.createReadStream(tmpfile).pipe(ipcs)
100 | })
101 | ```
102 |
103 |
104 | API
105 | ----
106 |
107 | ### Main Process
108 |
109 | #### IPCStream(channel, [browserWindow], [streamOptions])
110 |
111 | Create a new IPCStream in the `main` process.
112 |
113 |
114 | ### Renderer Process
115 |
116 | #### IPCStream(channel, [streamOptions])
117 |
118 | Create a new IPCStream in the `renderer` process.
119 |
120 |
121 | ### Stream Options
122 |
123 | You shouldn't have to mess with `objectMode`. Under the hood, `objectMode` is `true`.
124 | Buffers are serialized to JSON. This is because of the way that Electron handles buffers
125 | in renderer. See: https://github.com/atom/electron/blob/master/docs/api/remote.md for
126 | more detail. You also may need to adjust [`highWaterMark`](https://nodejs.org/api/stream.html).
127 |
128 |
129 | ### JSON Objects
130 |
131 | It is completely safe to call `write` on either end of the stream with objects.
132 |
133 | source:
134 |
135 | ```js
136 | myStream.write({name: 'JP'})
137 | ```
138 |
139 | dest:
140 |
141 | ```js
142 | // streams 1 (flowing):
143 | myStream.on('data', function (data) {
144 | console.dir(data) // => {name: 'JP'}
145 | })
146 |
147 | // streams 2/3 (pull, if you prefer):
148 | myStream.on('readable', function () {
149 | var data
150 | while (null !=== (data = myStream.read())) {
151 | console.dir(data) // => {name: 'JP'}
152 | }
153 | })
154 |
155 | ```
156 |
157 |
158 |
159 | ### Examples
160 |
161 | In the `./test` folder, you'll see two examples. You can run these by
162 | installing [electron-prebuilt](https://www.npmjs.com/package/electron-prebuilt):
163 |
164 | npm i -g electron-prebuilt
165 | electron ./test/main-to-rend
166 | electron ./test/rend-to-main
167 |
168 |
169 |
170 | License
171 | -------
172 |
173 | MIT Copyright [JP Richardson](https://github.com/jprichardson)
174 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var isRenderer = require('is-electron-renderer')
2 |
3 | if (isRenderer) {
4 | module.exports = require('./src/rend')
5 | } else {
6 | module.exports = require('./src/main')
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-ipc-stream",
3 | "version": "0.2.0",
4 | "description": "Duplex stream over IPC for main/renderer to communicate with each other.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "standard"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/jprichardson/electron-ipc-stream.git"
12 | },
13 | "keywords": [
14 | "electron",
15 | "electron-component",
16 | "ipc",
17 | "stream",
18 | "streams",
19 | "duplex",
20 | "readable",
21 | "writeable"
22 | ],
23 | "author": "JP Richardson",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/jprichardson/electron-ipc-stream/issues"
27 | },
28 | "homepage": "https://github.com/jprichardson/electron-ipc-stream#readme",
29 | "devDependencies": {
30 | "electron-window": "^0.6.2"
31 | },
32 | "dependencies": {
33 | "buffer-json": "^1.0.0",
34 | "is-electron-renderer": "^2.0.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | const bufferJson = require('buffer-json')
2 | const Duplex = require('stream').Duplex
3 | const ipcMain = require('electron').ipcMain
4 | const util = require('util')
5 |
6 | function MainIPCStream (channel, browserWindow, streamOpts) {
7 | if (!(this instanceof MainIPCStream)) {
8 | return new MainIPCStream(channel, browserWindow, streamOpts)
9 | }
10 | streamOpts = streamOpts || {}
11 | streamOpts.objectMode = streamOpts.objectMode ? streamOpts.objectMode : true
12 |
13 | this.browserWindow = browserWindow
14 | this.channel = channel
15 |
16 | const ipcCallback = (event, data) => {
17 | if (typeof data === 'string') {
18 | data = JSON.parse(data, bufferJson.reviver)
19 | }
20 | this.push(data)
21 | }
22 | ipcMain.on(this.channel, ipcCallback)
23 |
24 | this.on('finish', () => {
25 | if (this.browserWindow) this.browserWindow.webContents.send(this.channel + '-finish')
26 | ipcMain.removeListener(this.channel, ipcCallback)
27 | })
28 | ipcMain.once(this.channel + '-finish', () => this.push(null))
29 |
30 | Duplex.call(this, streamOpts)
31 | }
32 | util.inherits(MainIPCStream, Duplex)
33 |
34 | MainIPCStream.prototype._read = function () { }
35 |
36 | MainIPCStream.prototype._write = function (data, enc, next) {
37 | if (typeof data === 'string') {
38 | data = JSON.stringify(data)
39 | }
40 | if (Buffer.isBuffer(data)) {
41 | data = JSON.stringify(data, null, bufferJson.replacer)
42 | }
43 | if (!this.browserWindow) return console.warn('MainIPCStream: trying to write when no browserWindow is set.')
44 | this.browserWindow.webContents.send(this.channel, data)
45 | next()
46 | }
47 |
48 | module.exports = MainIPCStream
49 |
--------------------------------------------------------------------------------
/src/rend.js:
--------------------------------------------------------------------------------
1 | const bufferJson = require('buffer-json')
2 | const Duplex = require('stream').Duplex
3 | const ipcRenderer = require('electron').ipcRenderer
4 | const util = require('util')
5 |
6 | function RendIPCStream (channel, streamOpts) {
7 | if (!(this instanceof RendIPCStream)) {
8 | return new RendIPCStream(channel, streamOpts)
9 | }
10 | streamOpts = streamOpts || {}
11 | streamOpts.objectMode = streamOpts.objectMode ? streamOpts.objectMode : true
12 |
13 | this.channel = channel
14 |
15 | const ipcCallback = (event, data) => {
16 | if (typeof data === 'string') {
17 | data = JSON.parse(data, bufferJson.reviver)
18 | }
19 | this.push(data)
20 | }
21 | ipcRenderer.on(this.channel, ipcCallback)
22 |
23 | this.on('finish', function () {
24 | ipcRenderer.send(this.channel + '-finish')
25 | ipcRenderer.removeListener(this.channel, ipcCallback)
26 | })
27 | ipcRenderer.once(this.channel + '-finish', () => this.push(null))
28 |
29 | Duplex.call(this, streamOpts)
30 | }
31 | util.inherits(RendIPCStream, Duplex)
32 |
33 | RendIPCStream.prototype._read = function () { }
34 |
35 | RendIPCStream.prototype._write = function (data, enc, next) {
36 | if (typeof data === 'string') {
37 | data = JSON.stringify(data)
38 | }
39 | if (Buffer.isBuffer(data)) {
40 | data = JSON.stringify(data, null, bufferJson.replacer)
41 | }
42 |
43 | ipcRenderer.send(this.channel, data)
44 | next()
45 | }
46 |
47 | module.exports = RendIPCStream
48 |
--------------------------------------------------------------------------------
/test/main-to-rend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | hello
4 |
--------------------------------------------------------------------------------
/test/main-to-rend/index.js:
--------------------------------------------------------------------------------
1 | var app = require('app')
2 | var assert = require('assert')
3 | var crypto = require('crypto')
4 | var fs = require('fs')
5 | var ipc = require('electron').ipcMain
6 | var path = require('path')
7 | var window = require('electron-window')
8 | var IPCStream = require('../../')
9 |
10 | var buffer = crypto.randomBytes(10000)
11 | var tmpfile = '/tmp/ipc-main'
12 | fs.writeFileSync(tmpfile, buffer)
13 |
14 | process.on('uncaughtException', function (err) {
15 | console.error(err)
16 | console.error(err.stack)
17 | app.quit()
18 | })
19 |
20 | ipc.on('done', function () {
21 | var mainHash = md5File('/tmp/ipc-main')
22 | var rendHash = md5File('/tmp/ipc-rend')
23 | assert.strictEqual(mainHash, rendHash)
24 |
25 | console.log('')
26 | console.log(' success: ' + mainHash)
27 | console.log('')
28 |
29 | app.quit()
30 | })
31 |
32 | app.on('ready', function () {
33 | var win = window.createWindow({ height: 600, with: 1000 })
34 |
35 | var ipcs = new IPCStream('file', win)
36 | win._loadUrlWithArgs(path.resolve(__dirname, './index.html'), {}, function () {
37 | fs.createReadStream(tmpfile).pipe(ipcs)
38 | })
39 | })
40 |
41 | function md5File (file) {
42 | var data = fs.readFileSync(file)
43 | return crypto.createHash('md5').update(data).digest('hex')
44 | }
45 |
--------------------------------------------------------------------------------
/test/main-to-rend/rend.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | var ipc = require('ipc')
3 | var IPCStream = require('../../')
4 | var ipcs = new IPCStream('file')
5 |
6 | var tmpfile = '/tmp/ipc-rend'
7 |
8 | window.oneror = function (a, b, c, d, e) {
9 | console.error(e)
10 | console.error(e.stack)
11 | }
12 |
13 | document.addEventListener('DOMContentLoaded', function () {
14 | ipcs.pipe(fs.createWriteStream(tmpfile)).on('finish', function () {
15 | ipc.send('done')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/test/rend-to-main/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | hello
4 |
--------------------------------------------------------------------------------
/test/rend-to-main/index.js:
--------------------------------------------------------------------------------
1 | var app = require('app')
2 | var assert = require('assert')
3 | var crypto = require('crypto')
4 | var fs = require('fs')
5 | var path = require('path')
6 | var window = require('electron-window')
7 | var IPCStream = require('../../')
8 |
9 | var tmpfile = '/tmp/ipc-main'
10 |
11 | process.on('uncaughtException', function (err) {
12 | console.error(err)
13 | console.error(err.stack)
14 | app.quit()
15 | })
16 |
17 | function done () {
18 | var mainHash = md5File('/tmp/ipc-main')
19 | var rendHash = md5File('/tmp/ipc-rend')
20 | assert.strictEqual(mainHash, rendHash)
21 |
22 | console.log('')
23 | console.log(' success: ' + mainHash)
24 | console.log('')
25 |
26 | app.quit()
27 | }
28 |
29 | app.on('ready', function () {
30 | var win = window.createWindow({ height: 600, with: 1000 })
31 | var ipcs = new IPCStream('file', win)
32 | ipcs.pipe(fs.createWriteStream(tmpfile)).on('finish', done)
33 | win._loadUrlWithArgs(path.resolve(__dirname, './index.html'), {}, function () { })
34 | })
35 |
36 | function md5File (file) {
37 | var data = fs.readFileSync(file)
38 | return crypto.createHash('md5').update(data).digest('hex')
39 | }
40 |
--------------------------------------------------------------------------------
/test/rend-to-main/rend.js:
--------------------------------------------------------------------------------
1 | var crypt = require('crypto')
2 | var fs = require('fs')
3 | var IPCStream = require('../../')
4 | var ipcs = new IPCStream('file')
5 |
6 | var buffer = crypt.randomBytes(10000)
7 | var tmpfile = '/tmp/ipc-rend'
8 | fs.writeFileSync(tmpfile, buffer)
9 |
10 | window.oneror = function (a, b, c, d, e) {
11 | console.error(e)
12 | console.error(e.stack)
13 | }
14 |
15 | document.addEventListener('DOMContentLoaded', function () {
16 | fs.createReadStream(tmpfile).pipe(ipcs)
17 | })
18 |
--------------------------------------------------------------------------------