Experience-Monks / hihat

:tophat: local Node/Browser development with Chrome DevTools
MIT License
443 stars 19 forks source link

Packaging / Communication with Server #16

Open mattdesl opened 9 years ago

mattdesl commented 9 years ago

Say the user wants to build a simple CLI that produces speech synthesis using web APIs.

speech.js

var message = 'Hello world.'

var synth = window.speechSynthesis
synth.onvoiceschanged = function () {
  var msg = new window.SpeechSynthesisUtterance(message)
  var voices = synth.getVoices()
  var voice = voices.filter(function (voice) {
    return /zarvox/i.test(voice.name)
  })[0] || voices[0]

  msg.voice = voice
  msg.onend = function() {
    window.close()
  }

  synth.speak(msg)
  console.log("Speaking: %s", voice.name)
}

Currently it works with:

hihat speech.js --exec

Now, the problem is, the user wants to package this up as a CLI tool so that others can input their own message like so:

speech-synth --message="Goodbye"

How does the speech-synth communicate to the Electron server what the message is?

Ideally the user would just use process.argv.slice(2) as the input, but this does not work due to the nature of how we spawn electron.

process.argv.slice(2)
//=> [ 'speech.js', '--exec' ]
mattdesl commented 9 years ago

One approach is using IPC or some sort of socket thing to communicate.

Another idea:

Similar approach could use ipc instead to send data, rather than relying on process.argv.

Downsides:

yoshuawuyts commented 9 years ago

users installing their module have to install electron-prebuilt twice (not sure if it makes a difference since it should be cached)

If you expose the electron instance to the caller it doesn't have to be included twice, right? e.g. require('hihat').electron.

shama commented 9 years ago

FWIW, electron-spawn uses https://www.npmjs.com/package/npm-execspawn which will detect if electron has been globally installed. So if installing electron-prebuilt multiple times for a user was a problem, they could install it just once globally.


Another possible way for the child/parent to communicate is to create a file descriptor to write to (I've never tried this with electron though):

var spawn = require('child_process').spawn
var child = spawn(process.execPath, ['script.js'], {
  stdio: [null, null, null, 'pipe']
})
child.stdout.pipe(process.stdout)
child.stdio[3].write('testing')

Then in script.js:

var net = require('net')
var pipe = new net.Socket({ fd: 3 })
pipe.on('data', function (data) {
  console.log('got the data!', data.toString())
})

Or even stdio: [null, null, null, 'ipc'] and use .send() and process.on('message') which is basically what child_process.fork does (I've also not tried this with electron).

mattdesl commented 9 years ago

Thanks for the suggestions! :+1:

I have something working in feature/entry branch and it seems alright.

Taking some inspiration from electron-spawn... Now the full speech-server.js bin script looks like this:

#!/usr/bin/env node
if (!process.versions.electron) {
  require('hihat/spawn')(__filename, process.argv.slice(2)) 
} else {
  require('hihat')({
    basedir: __dirname,
    entries: 'speech.js',
    node: true,
    exec: true
  })
}

The speech.js (client) looks like it does earlier but with:

var argv = require('minimist')(process.argv.slice(2))
var message = argv.message || 'Hello world'

...

And the following works! :tada:

speech-server.js --message="woah dude"

Thoughts?

shama commented 9 years ago

Rad! :sparkles: :+1: :sparkles: