Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to package a Coffeescript project #2216

Closed
hickford opened this Issue Mar 24, 2012 · 18 comments

Comments

Projects
None yet
9 participants
@hickford
Copy link
Contributor

commented Mar 24, 2012

I'm grateful for Npm's wonderful documentation on how to package and publish a project written in Node is . http://npmjs.org/doc/developers.html

However Coffeescript desperately lacks documentation explaining how to package a project. We should tell people to compile to Javascript before publishing. It's obvious enough why, but it's far from obvious how. Sensibly Isaacs tells us to put a 'prepublish' script in package.json so developers can build and publish with a single command npm publish but few projects follow this. Instead, a lot of projects check compiled code into version control including Github's own Hubot.

Why is it undesirable to put compiled code under version control? For starters, many of the reasons that we don't for other languages. But additionally because we can run Coffeescript without compiling, there's a risk a developer makes a change to the source without building. If they commit now, lib will be behind src. Conversely, it allows a new to developer to fork the code and edit the compiled code. When the source is rebuilt, their change will be lost.

Today I tried to package my Coffeescript project. My project has only a single .coffee file. Following isaac, I put a prepublish script in my package.json

"scripts": {
  "prepublish": "coffee -o lib -c src/*.coffee"

This almost worked. It built my script to Javascript and it would be fine if my script were a library, but my script is an executable. The script had a #!/usr/bin/env coffee shebang, but the Coffeescript compiler saves code without a shebang. If a user executes Javascript without a shebang their shell runs it as a shell script and they get a silly error.

Now, it's easy enough to prepend the shebang manually, but it's a major challenge to do that from the prepublish script in a platform-independent way. Other Coffeescript projects such as Pow get round this by writing one line executables that rarely change and checking in the compiled Javascript executable.

Documentation should also explain the src, lib, bin convention and which of these you should check into version control.

Previous discussion https://github.com/isaacs/npm/issues/1991#issuecomment-4675322

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 24, 2012

Coffeescript project build challenge. Is it possible to maintain a project with .coffee but not .js files under version control, where anyone can build and publish with a single command (such as npm publish)?

A lot of projects put compiled code under source control (against convention), some of which is difficult to rebuild from source. For example with 'pow'

git clone https://github.com/37signals/pow
rm lib  # compiled javascript code 
rm bin  # javascript executables
cake build

lib is rebuilt but bin is lost

@michaelficarra

This comment has been minimized.

Copy link
Collaborator

commented Mar 24, 2012

@matt-hickford:

git checkout lib bin
bin/cake build

Why would you expect you could compile CS source without a CS compiler?

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 24, 2012

I see Coffeescript is a bad example, because it's self-hosting and needs its compiled code to build, so that's why bin is checked into source control. Let me find a better one

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 24, 2012

A survey of software developed in coffee-script.

  • Pow has Coffeescript src, and also Javascript lib and bin under source control. If you delete lib and bin, cake build will rebuild lib but not bin.
  • Coffee-resque has only Coffeescript src under version control. It has a Makefile with tasks generate-js and publish. generate-js builds a lib folder of Javascript. publish calls generate-js then npm publish. This passes my challenge, but it's made easier because it doesn't build any executables.
@showell

This comment has been minimized.

Copy link

commented Mar 24, 2012

@matt-hickford Why can't you just make prepublish point to a script that does the equivalent of "coffee -o lib -c src/*.coffee" along with any post-processing (adding shebangs, etc.)? Some combination of find, xargs, coffee, echo, and cat should do the trick. There are plenty of good references for bash scripting on the web, but if you don't want to do it bash, then coffee itself is a good option.

It would be nice to see some good docs on how to do this, so I'm +1 on this proposal. (I'm +1 on almost every documentation ticket.)

But I also think a lot of devs can just figure this out for themselves. Matt, if you get something up and running, you could start the documentation process by publishing your solution on the wiki. If the wiki page got some traction, then maybe the main site could simply link to the wiki page, which would make this proposal a no-brainer +1000.

Here's the wiki link:

https://github.com/jashkenas/coffee-script/wiki

See the "How-tos" section in particular.

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 25, 2012

Showell, it's hard to script prepending the shebang in a cross-platform way, but yes it can be done with a Cakefile. It still disappoints me that coffee -c isn't powerful enough :

$ cat hello.coffee
#!/usr/bin/env coffee
console.log("hey")

$ ./hello.coffee
hey

$ coffee -c hello.coffee

$ ./hello.js
./hello.js: line 1: syntax error near unexpected token `('
./hello.js: line 1: `(function() {'

Fail :( #2215

@showell

This comment has been minimized.

Copy link

commented Mar 26, 2012

+1

I don't think shebangs are that big a deal--you can always say node foo.js--but the patch seems simple enough.

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 26, 2012

Shebangs are a big deal! If you install executables into the path, they must have a shebang or they can't be executed. Coffeescript's coffee has shebang. It would be mental to write node /usr/bin/coffee all the time

A lot of packages have one or more executable files that they'd like to install into the PATH. npm makes this pretty easy

I'm asking Coffeescript to co-operate

@showell

This comment has been minimized.

Copy link

commented Mar 27, 2012

Like I said, I'm +1 on the --shebang option now, so my comments/questions that follow are just out of curiosity.

If you look at how CoffeeScript itself is packaged, there's just a single JS file that bootstraps everything:

#!/usr/bin/env node

var path = require('path');
var fs   = require('fs');
var lib  = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');

require(lib + '/coffee-script/command').run();

The above file can be found here: https://github.com/jashkenas/coffee-script/blob/master/bin/coffee

CoffeeScript doesn't distribute .coffee files, just JS files. If you don't believe me, look at your equivalent of ~/.npm/coffee-script/1.2.0/package.

You can also see the package.json here: https://github.com/jashkenas/coffee-script/blob/master/package.json

Are you trying to distribute the coffee file as part of your npm package? Or are you just trying to use npm to help you build packages for distribution? If it's the latter that is making your life difficult, then can you just have wrap npm with a script that automatically builds the JS?

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 27, 2012

Thanks for your interest Showell. CoffeeScript is packaged very nicely, I love npm. The sensible manta 'write everything in coffeescript except a four line wrapper' works

Myself I've never written one line of Javascript—I'm a CoffeeScript native speaker (how many of us are there?). My blasé "don't know, don't care" attitude applies alike to the Javascript that CoffeeScript outputs and to the machine code that V8 outputs. I do appreciate the giants on whose shoulders I stand. I'm actually using the Iced CoffeeScript fork. I'll use machines if they make my work easier.

Here's what I am trying to achieve: develop entirely in CoffeeScript. I'll edit only CoffeeScript and my package.json. There'll be no Javascript in my Git repository (no snub intended), because it's not source. Of course, I must publish Javascript to npm registry, otherwise my code would be useless to many (anyone without a global Coffeescript install). So I'll compile to Javascript when I run npm publish or cake publish, but I'll throw it away afterwards.

How far am I towards that? It remains for me to write a Cakefile with task cake build. Apart from this shebang issue, I've another concerning system calls. I use and develop software on Linux and Windows, so I'm wary of cross-platform issues. I'd like to write a Cakefile which doesn't use child_process at all. Is this possible? Could I require command.coffee and call its compilePath function?

NB it appears require('coffee-script').run differs to require('./command.coffee').run

@arboleya

This comment has been minimized.

Copy link

commented Jan 19, 2013

@matt-hickford Hi, I agree that js isn't the source for coffee-script projects. It's just machine code.

@showell You don't have to post-process anything to use a custom shebang, your can use NPM instead to make an alias of it.

All is needed is to rewire the coffee bin in your package.json with a custom name (so the local coffee module bin can be accessed globally) and use it as the env for your executable file to initialize your main coffee file.

This way the user won't even have to install coffee globally.

Remember to set coffee-script as a dependency.

./package.json

{
  "bin": {
    "your_program" : "./bin/your_program",
    "_your_program_coffee": "./node_modules/coffee-script/bin/coffee"
  },

  "dependencies": {
    "coffee-script" : "1.4.x",
  }
}

./bin/your_program

#!/usr/bin/env _your_program_coffee

your_program = require '../src/your_main_file.coffee'
console.log your_program
# initialize what you want...

@michaelficarra I think the point here is to be able to write a lib entirely in coffeescript without needing to compile it down to JS for it to work.

@hickford

This comment has been minimized.

Copy link
Contributor Author

commented Mar 15, 2014

@arboleya is right--it should be possible to write a lib entirely in coffeescript.

Alas, it's not. I tried https://github.com/matt-hickford/undiluted and it goes wrong because of the shebang issue. See #3416

Prohibitively difficult to package pure coffee-script app (without checking in any javascript)

@michaelficarra

This comment has been minimized.

Copy link
Collaborator

commented Mar 15, 2014

Closing as duplicate of #2215. We're not going to add a shebang to the output for you. The CoffeeScript compiler generates JavaScript programs, not shell scripts.

@ymeine

This comment has been minimized.

Copy link

commented Mar 17, 2014

@michaelficarra While I completely agree with the fact that adding a shebang should not be the responsibility of CoffeeScript compilers, I feel obliged to point out that what you are saying is a bit paradoxical. If shebang were only for shell scripts, I think they would completely be pointless, their purpose being to tell the Operating System how to execute a program when the latter is being invoked through an API that supports it (directly or not).

So in practice, bash or any other shell script interpreter for shell scripts, and node or why not ringo for JavaScript. And I didn't even talk about python, ruby, and so on...

@robertrossmann

This comment has been minimized.

Copy link

commented May 7, 2014

I just stumbled across this thread while looking for similar information. The solution, while not 100% pure CoffeeScript, is rather simple:

  1. Write executables in JavaScript
  2. Write everything else in CoffeeScript

The simplest way to achieve this is something along these lines:
$ ./bin/app.js

#!/usr/bin/env node
var cli = require('./lib/cli'); // Require the main CLI script
cli.runWith(arguments); // Hand over processing to the main CS-compiled script

A single JavaScript file in an otherwise purely CoffeeScript project does not hurt, does it?:)

@jashkenas

This comment has been minimized.

Copy link
Owner

commented May 7, 2014

A single JavaScript file in an otherwise purely CoffeeScript project does not hurt, does it?:)

It doesn't, and that's fine advice. In fact, that's exactly how the CoffeeScript compiler implements itself:

https://github.com/jashkenas/coffee-script/blob/master/bin/coffee

@toolbear

This comment has been minimized.

Copy link

commented Jan 1, 2015

Many ways to skin a cat. The following works nicely for me.

  1. Prepublish step compiles CoffeeScript in lib to JavaScript in lib:

      "scripts": {
        "prepublish": "coffee -co lib lib"
      }
    
  2. Telling git to ignore /lib/**/*.js and npm to ignore /lib/**/*.coffee

  3. JavaScript executable, bin/my-command, that registers the CoffeeScript compiler and loads our lib:

    #!/usr/bin/env node
    
    process.title = 'my-command';
    try {
      require('coffee-script/register');
    } catch(e) {}
    require('./../lib/cli')();
  4. Running git clean -fX following an npm publish

Has no compiled code checked in; doesn't require compilation when developing; npm package only contains JavaScript; and — sans executable — is 100% CoffeeScript.

@hems

This comment has been minimized.

Copy link

commented Oct 9, 2016

when compiling coffee to js a good idea is to use --bare so you don't get any extra "wrapping" on your code, as in:

./node_modules/.bin/coffee --bare -o lib/ -c src/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.