phridge
A bridge between node and PhantomJS.
Working with PhantomJS in node is a bit cumbersome since you need to spawn a new PhantomJS process for every single task. However, spawning a new process is quite expensive and thus can slow down your application significantly.
phridge provides an api to easily
- spawn new PhantomJS processes
- run functions with arguments inside PhantomJS
- return results from PhantomJS to node
- manage long-running PhantomJS instances
Unlike other node-PhantomJS bridges phridge provides a way to run code directly inside PhantomJS instead of turning every call and assignment into an async operation.
phridge uses PhantomJS' stdin and stdout for inter-process communication. It stringifies the given function, passes it to PhantomJS via stdin, executes it in the PhantomJS environment and passes back the results via stdout. Thus you can write your PhantomJS scripts inside your node modules in a clean and synchronous way.
Instead of ...
phantom;
... you can write ...
// nodephantom;
Please note that the phantom
-object provided by phridge is completely different to the phantom
-object inside PhantomJS. So is the page
-object. Check out the api for further information.
Installation
npm install phridge
Examples
Spawn a new PhantomJS process
phridge;
phridge.spawn()
takes an object which will be passed as config to PhantomJS. Check out their documentation for a detailed overview of options. CLI-style options are added as they are, so be sure to escape the space character.
Please note: There are known issues of PhantomJS that some config options are only supported in CLI-style.
Run any function inside PhantomJS
phantom;
phridge stringifies the given function, sends it to PhantomJS and evals it again. Hence you can't use scope variables:
var someVar = "hi"; phantom;
Passing arguments
You can also pass arguments to the PhantomJS process:
phantom;
Arguments are stringified by JSON.stringify()
, so be sure to use JSON-valid objects.
Returning results
The given function can run sync and async. However, the run()
method itself will always run async as it needs to wait for the process to respond.
Sync
phantom;
Async
phantom;
Results are also stringified by JSON.stringify()
, so returning application objects with functions won't work.
phantom;
Returning errors
Errors can be returned by using the throw
keyword or by calling the reject
function. Both ways will reject the promise returned by run()
.
Sync
phantom;
Async
phantom;
Async methods with arguments
resolve
and reject
are just appended to the regular arguments:
phantom;
Persisting states inside PhantomJS
Since the function passed to phantom.run()
can't declare variables in the global scope, it is impossible to maintain state in PhantomJS. That's why phantom.run()
calls all functions on the same context object. Thus you can easily store state variables.
phantom;
For further convenience all PhantomJS modules are already available in the global scope.
phantom;
Working in a page context
Most of the time its more useful to work in a specific webpage context. This is done by creating a Page via phantom.createPage()
which calls internally require("webpage").create()
. The returned page wrapper will then execute all functions bound to a PhantomJS webpage instance.
var page = phantom; page;
And for the busy ones: You can just call phantom.openPage(url)
which is basically the same as above:
phantom;
Cleaning up
If you don't need a particular page anymore, just call:
page;
This will clean up all page references inside PhantomJS.
If you don't need the whole process anymore call
phantom;
which will terminate the process cleanly by calling phantom.exit(0)
internally. You don't need to dispose all pages manuallly when you call phantom.dispose()
.
However, calling
phridge;
will terminate all processes.
I strongly recommend to call phridge.disposeAll()
when the node process exits as this is the only way to ensure that all child processes terminate as well. Since disposeAll()
is async it is not safe to call it on process.on("exit")
. It is better to call it on SIGINT
, SIGTERM
and within your regular exit flow.
API
phridge
.spawn(config?): Promise → Phantom
Spawns a new PhantomJS process with the given config. Read the PhantomJS documentation for all available config options. Use camelCase style for option names. The promise will be fulfilled with an instance of Phantom
.
.disposeAll(): Promise
Terminates all PhantomJS processes that have been spawned. The promise will be fulfilled when all child processes emitted an exit
-event.
.config.stdout: Stream = process.stdout
Destination stream where PhantomJS' clean stdout will be piped to. Set it null
if you don't want it. Changing the value does not affect processes that have already been spawned.
.config.stderr: Stream = process.stderr
Destination stream where PhantomJS' stderr will be piped to. Set it null
if you don't want it. Changing the value does not affect processes that have already been spawned.
Phantom.prototype
.childProcess: ChildProcess
A reference to the ChildProcess-instance.
.childProcess.cleanStdout: ReadableStream
phridge extends the ChildProcess-instance by a new stream called cleanStdout
. This stream is piped to process.stdout
by default. It provides all data not dedicated to phridge. Streaming data is considered to be dedicated to phridge when the new line is preceded by the classifier string "message to node: "
.
.run(args..., fn): Promise → *
Stringifies fn
, sends it to PhantomJS and executes it there again. args...
are stringified using JSON.stringify()
and passed to fn
again. fn
may simply return
a result or throw
an error or call resolve()
or reject()
respectively if it is asynchronous. phridge compares fn.length
with the given number of arguments to determine whether fn
is sync or async. The returned promise will be resolved with the result or rejected with the error.
.createPage(): Page
Creates a wrapper to execute code in the context of a specific PhantomJS webpage.
.openPage(url): Promise → Page
Calls phantom.createPage()
, then page.open(url, cb)
inside PhantomJS and resolves when cb
is called. If the returned status
is not "success"
the promise will be rejected.
.dispose(): Promise
Calls phantom.exit(0)
inside PhantomJS and resolves when the child process emits an exit
-event.
Events
unexpectedExit
Will be emitted when PhantomJS exited without a call to phantom.dispose()
or one of its std streams emitted an error
event. This event may be fired on some OS when the process group receives a SIGINT
or SIGTERM
(see #35).
When an unexpectedExit
event is encountered, the phantom
instance will be unusable and therefore automatically disposed. Usually you don't need to listen for this event.
Page.prototype
.phantom: Phantom
A reference to the parent Phantom
instance.
.run(args..., fn): Promise → *
Calls fn
on the context of a PhantomJS page object. See phantom.run()
for further information.
.dispose(): Promise
Cleans up this page instance by calling page.close()
Contributing
From opening a bug report to creating a pull request: every contribution is appreciated and welcome. If you're planing to implement a new feature or change the api please create an issue first. This way we can ensure that your precious work is not in vain.
All pull requests should have 100% test coverage (with notable exceptions) and need to pass all tests.
- Call
npm test
to run the unit tests - Call
npm run coverage
to check the test coverage (using istanbul)
License
Unlicense