documentcloud / jammit

Industrial Strength Asset Packaging for Rails
http://documentcloud.github.com/jammit/
MIT License
1.16k stars 197 forks source link

Add automatic CoffeeScript compilation. #212

Closed pazustep closed 1 year ago

pazustep commented 12 years ago

This pull request adds CoffeScript support to Jammit, courtesy of the coffee-script gem.

jashkenas commented 12 years ago

Very nice work.

Before merging, the one thing that I would be concerned about is the ordering of files. For templates it doesn't matter, but if your asset package looks like:

app:
  - vendor/jquery.js
  - src/myBaseLibrary.coffee
  - lib/myApp.js
  - views/myView.coffee

... you really need those (compiled) JavaScripts to be loaded in the correct order. Do you have any ideas about how to make this work?

pazustep commented 12 years ago

I thought about it, but right now I couldn't find a solution that would work both in production and development (individual urls) modes.

My first cut actually iterated the package items and generated output in the same order as they were specified. But then I realized that in development mode (using your example), include_javascripts :app would expand to the following URLs:

- vendor/jquery.js
- lib/myapp.js
- app.coffee

Fixing that would require changing jammit to respond to individual URLs even when they aren't a package name. I'm not sure if this is the right thing to do, and even if they were, this would be worth a separate pull request on its own.

So in the end, instead of preserving order in production mode but leaving coffe files at the end it on development mode, I preferred to leave it at the end in both modes.

It's not as bad as it sounds. If you're using coffeescript in your projects, I think it's quite unlikely that you'll add pure js files that depends on coffeescript files. It's usually the other way around, with coffeescript files depending on third-party libraries or code written before cofeescript was introduced to the project.

jashkenas commented 12 years ago

I'm afraid that preserving script order is one of the main features of Jammit -- even if it's not something that's totally essential in a well-written JS project. If there's any way we can keep it, I'd like to make the effort. How does this sound?

app:
  - vendor/jquery.js
  - src/myBaseLibrary.coffee
  - lib/myApp.js
  - views/myView.coffee

In production, it's easy ... in development, these script tags get written out:

<script src="vendor/jquery.js"></script>
<script src="assets/src/myBaseLibrary.coffee.js"></script>
<script src="lib/myApp.js"></script>
<script src="assets/views/myView.coffee.js"></script>

... or something along those lines. We already control the "assets" controller and every path under that namespace. Even if you had to be a bit clearer with: "assets/coffee/src/myBaseLibrary.coffee.js" -- I think it could be done. We'd compile those files to JS on demand, individually. It would be easier to debug as well.

pazustep commented 12 years ago

Elaborating a little further: to address this problem, jammit would have to have a way to generate URLs to individual items inside a package (and respond to these URLs, of course). So using your example again:

app:
  - vendor/jquery.js
  - src/myBaseLibrary.coffee
  - lib/myApp.js
  - views/myView.coffee

Defining this package right now makes jammit respond to these routes:

/assets/app.js
/assets/app.coffee
/assets/app.jst (empty response, but works)

We'd have to add a way to address individual items inside this package. We could use the package name as a prefix, and then add the index for the item inside the package:

/assets/app/1 (for jquery.js)
/assets/app/2 (for BaseLibrary.coffee)
…

Or the full path names, but that might be too verbose (I have some pretty long paths in my projects):

/assets/app/vendor/jquery.js
/assets/app/src/myBaseLibrary.coffee
…

An ideal solution, I think, would be determining the minimum unique path inside the package. This would be the filename for most cases, but if you have two files with the same name in the package, we'd have to add more path elements to distinguish them. So if we have a package:

app:
  - vendor/component1/app.coffee
  - vendor/component1/otherlib.js
  - vendor/component2/app.coffee

The generated URLs would be:

/assets/app/component1/app.coffee
/assets/app/otherlib.js
/assets/app/component2/app.coffee

Now, I'm getting carried away. In any case, I do believe that this problem would be better addressed in a separate commit.

jashkenas commented 12 years ago

... a separate commit and/or pull request is fine, but we can't really push this out without it (in my opinion). I think that full path names is absolutely a desirable thing -- remember this is only in development.

When there's an error, you want to have nice, unminified, unconcatenated javascript code, referenced from a script tag that includes the complete path of your source (CoffeeScript in this case) file, so that it's easy for you to go and make the fix.

pazustep commented 12 years ago

Alright, I think I underestimated the importance of preserving the order of the items inside the package. I'll see if I can come up with something to fix this soon.

pazustep commented 12 years ago

Alright, I think I did it.

The first commit above makes jammit respect the order declared, outputting coffeescript and templates in the order defined in the package declaration.

Also, individual coffeescript and templates are also served (properly compiled) by jammit's controller. The route for individual assets is the same for the package itself, with the asset path appended. For example, this declaration:

app:
  - vendor/jquery.js
  - src/myBaseLibrary.coffee
  - lib/template1.jst
  - lib/template2.jst
  - lib/myApp.js

Will cause jammit to respond to the following urls:

- /assets/app.js
- /assets/app.js/vendor/jquery.js
- /assets/app.js/src/myBaseLibrary.coffee
- /assets/app.js/lib/template1.jst
- /assets/app.js/lib/template2.jst
- /assets/app.js/lib/myApp.js

And will, of course, generate <script> tags in exactly the same order in development mode.

Then I realized that outputting templates in the declared order is actually a change from the previous behaviour. The second commit reverts that, and now the generated URLs, by default, will be:

- /assets/app.js
- /assets/app.js/vendor/jquery.js
- /assets/app.js/src/myBaseLibrary.coffee
- /assets/app.js/lib/myApp.js
- /assets/app.jst

This way we will output coffeescript files in order, but templates will still get concatenated and appended at the end, preventing any backward compatibilty problems. If you actually want to pack templates in the order declared, you can add this to your config file:

pack_templates_in_order: true
jashkenas commented 12 years ago

Lovely.

That said -- adding templates in the order they appear is a feature that's desired by some folks. (look at the older tickets) Although it could be a billion little HTTP requests in development. It's worth considering what behavior you think is best.

pazustep commented 12 years ago

Yeah, a billion little request while testing this in my own app is actually what prompted me to keep the old behaviour :)

pazustep commented 12 years ago

Any updates on this pull request? I'd love to see this merged upstream.