CoffeeScript in a shell script -- run by Apache (as CGI)
Following the advice here I can get a shell script in JavaScript that runs under node.js and prints a bit of hello world html:
test.cgi
-------
#!/usr/local/bin/node
console.log("Content-Type: text/html; charset=utf-8\n");
console.log("<html><body><h1>hello node.js world</h1开发者_运维百科></body></html>");
-------
And running it:
$ ./test.cgi
Content-Type: text/html; charset=utf-8
<html><body><h1>hello node.js world</h1></body></html>
It also works as expected in Apache and displays the expected HTML in the browser.
Now on to CoffeeScript (note the lovely triple-quoted here docs, Python-style):
ctest.cgi
-------
#!/usr/bin/env coffee
console.log("Content-Type: text/html; charset=utf-8\n");
console.log('''<html><body><h1>hello CoffeeScript world
</h1></body></html>''');
-------
This works when run locally:
$ ./ctest.cgi
Content-Type: text/html; charset=utf-8
<html><body><h1>hello CoffeeScript world
</h1></body></html>
But not in Apache:
Internal Server Error
Why doesn't this work?
I attempted to fix this by reverse engineering command.js from CoffeeScript and running my coffee script (ctest.cgi above) directly, via the following JavaScript node.js shell script:
drip
-------
#!/usr/local/bin/node
var fs = require('fs');
var cs = require('/usr/local/lib/coffee-script/lib/coffee-script');
var args = process.argv;
var fname = args[2];
if (fname)
{
fs.readFile(fname, function (err, data) {
if (err) throw err;
var text = data.toString(); //console.log(data.toString());
cs.run(text, {'filename':fname, 'bare':undefined});
});
}
-------
I put this in /usr/local/bin/drip and now I can run ctest.cgi with a small change to the top line:
#!/usr/bin/env /usr/local/bin/drip
Now I can hack CoffeeScript CGI scripts and simply hit reload in my browser instead of having to manually invoke the CoffeeScript compiler when I change the .coffee files.
You probably already thought of this, but: Have you tried replacing
#!/usr/bin/env coffee
with an absolute reference to wherever coffee
is located? env
relies on the PATH
environment variable, and your PATH
when you run ./ctest.cgi
isn't necessarily the same as Apache's.
Here is my setup, for anybody that's interested.
It is very bad for performance!
~/coffee
------
#!/usr/bin/perl
# this is coffee runner!
print ` PATH="\$PATH:~/www/cgi-bin/bin" ; ~/www/cgi-bin/bin/node_modules/coffee-script/bin/coffee $ARGV[0] 2>&1 `;
------
I don't have the necessaries to alter my server environment, so I get to add my node paths here. However, I can set up a handler in .htaccess:
~/dir/.htaccess
------
AddHandler cgi-script .litcoffee
DirectoryIndex cv.litcoffee
------
This means I can run literate as CGI and serve up coffee for the browser :-) Very inefficient, but no many people are coming to my website anyway.
Then each of my scripts looks something like this...
~/web/ascript.litcoffee
------
#!/usr/bin/perl /home/jimi/coffee
This is literate coffeescript!
module.paths.push "/home/jimi/www/cgi-bin/bin/node_modules"
require "coffee-script"
This is a wee module I wrote for spewing tags, with content and attributes
global[k.toUpperCase()] = v for k,v of require './html.litcoffee'
It also provides a header function, but I'm going to move that to a CGI module when I get around to it.
console.log CGI_HEADER()
Now we can put something to the browser.
console.log HTML [
HEAD [
META {charset:"utf-8"}
SCRIPT [],
src : "https://raw.github.com/jashkenas/coffee-script/master/extras/coffee-script.js"
SCRIPT [],
src : "runsonclient.coffee"
type : "text/coffeescript"
LINK
rel : "stylesheet"
href : "mystyles.css"
TITLE "A page title"
]
BODY [
H1 "a page title"
INPUT
id : "myinput"
type : "text"
SVG
id : "mysvg"
width : "80%"
height : "20"
DIV
id : "mydiv"
]
]
------
I know it's not pretty, but it works. And running from a script (although admittedly it needn't be perl!) allows 2>&1 so all my errors get to the screen, unless my header isn't printed.... but Jared Updike already solved that with a try block.
I have no idea why coffee fails, but a possible (and very simple) solution is to put your code in a separate file (test.coffee) and do a require:
#!/usr/local/bin/node
require('coffee-script')
require('./test')
(after requiring coffee-script the extension is automatically registered)
I tried to update my answer, but it got rejected and some advice said to add this as a separate answer...
I just updated that to a shell script with compile if newer functionality:
#!/bin/sh
CS=$1 # the coffeescript to be run
# the following works because (my) apache runs in the script's directory and passes just the filename (no path)
# if would be safer to test that this is the case, or make sure the dot is added to the filename part
JS=.$CS.cjs # the compiled javascript
CE=.$CS.cerr # compiler errors
PATH="$PATH:/home/jimi/www/cgi-bin/bin:/home/jimi/www/cgi-bin/bin/node_modules/coffee-script/bin"
if [ ! -f $JS ] || [ $CS -nt $JS ] ; then
coffee -c -p $1>$JS 2>$CE
fi
node $JS 2>&1 # runtime errors to browser
精彩评论