Conditional compilation in CoffeeScript/UglifyJS
Asked Answered
P

4

11

Using Coffeescript I need to have a go through a build script anyway to update my .js files, and I have two of them, one for debugging and one for production (one uses Uglify to minimize the files, one does not). So I was thinking that it would be convenient to have some conditional compilation as well, with code that only enters the debug build.

What is the easiest way to achieve this, ideally controlled by a simple command line switch that I can give to either coffee or uglify?

Pudendas answered 14/3, 2012 at 2:41 Comment(0)
L
7

If you're writing a build script anyway, you can add a preprocessor step to it. Since CoffeeScript uses # to denote comments, the C preprocessor seems like a good choice. You can denote debug code with #ifdefs:

some code...
#ifdef DEBUG
debug code...
#endif

Then, you can preprocess the debug version with cpp -E -Xpreprocessor -DDEBUG <filename> -o <outfile> and compile <outfile> with CoffeeScript. Similarly, preprocess the production version with cpp -E <filename> -o <outfile>.

Edit: This one's tough, because it means any CoffeeScript comments that are not indented will break the preprocessing step. Not sure how much of a problem this is to you. For example,

code...
#comment about the code

will break the build, but

code...
  indented code...
  #indented comment

will work fine, because the preprocessor doesn't look at lines unless their first character is a #.

Least answered 14/3, 2012 at 14:31 Comment(8)
I like this idea, though you'd have to be careful not to include comments like #if blah, #error blah, or anything else that'd conflict with preprocessor directives.Swoop
@Swoop Good point. I'll make note of it. Edit: Any comment that isn't indented will be a problem as the preprocessor will choke; indented comments will be ignored as expected.Least
+1. That would work. I don't need to indent pre-processor comments. But do you know of a cpp-like tool that runs on node.js (don't want to have to install anything extra for this, except via npm, which is easy)?Pudendas
I don't know of any, and a quick search didn't turn up anything. Do you expect to be on a system that doesn't have a C compiler? They are fairly rare in my experience, assuming we're talking UNIX. Also, I may not have been clear. Normal comments that have no preceding whitespace will make cpp choke.Least
Oh, so I have to indent every comment. Not so good. As for a C compiler, the same build script currently runs on developer machines (Mac and Windows) and the CI server (Linux). Maintaining an additional C compiler that works for all of them goes way over budget.Pudendas
First hit on google for "pre-processor node.js": github.com/mcoolin/Node-JavaScript-Preprocessor Will see how well that works.Pudendas
Hmmmm...not sure why I didn't come across that one. Looks like a good bet. I'm not sure how well it'll work against CoffeeScript, because it requires (and outputs) // comments, which CoffeeScript interprets as an empty regex.Least
I am thinking to use backticks to inject raw javascript comments: ` // #ifdef ` and run the pre-processor after coffee but before uglify.Pudendas
M
2

It sounds to me like you're saying that you have two build scripts? For string.js, I just use a Cakefile to achieve what you I think that you want. Essentially, if the source file changes, it produces a regular JS file and then an uglified file.

Here is the relevant portion of the Cakefile:

 task 'watch', 'Watch src/ for changes', ->
    browserTestFile = path.join(process.cwd(), 'test_browser', 'string.test.js')

    coffee = spawn 'coffee', ['-w', '-c', '-o', 'lib', 'src']
    coffee.stderr.on 'data', (data) -> 'ERR: ' + process.stderr.write data.toString()
    coffee.stdout.on 'data', (data) ->
      d = data.toString()
      if d.indexOf('compiled') > 0
        #invoke 'test'

        fsw = fs.createWriteStream(browserTestFile, flags: 'w', encoding: 'utf8', mode: 0666)
        coffee_test = spawn 'coffee', ['-c', '-p', 'test/string.test.coffee']
        coffee_test.stdout.pipe(fsw, end: false)

        uglify = spawn 'uglifyjs', ['lib/string.js']
        uglify.stdout.pipe(fs.createWriteStream('lib/string.min.js'))

      else
        growl(d, title: 'Error', image: './resources/error.png')

      process.stdout.write data.toString()
Marni answered 14/3, 2012 at 2:58 Comment(1)
Yes, that part works fine already. But I also want the contents of the uglified file to be slightly different than the other one. Think #ifdef 'DEBUG'. Same source file, but with external switches affecting what gets removed.Pudendas
R
1

An alternative to the C preprocessor would be the M4 macro processor (Wikipedia intro). I haven't used it myself so I can't review it and I know it's supposed to be somewhat of a pain but it would solve your problem. Also it, like the C preprocessor, runs on every OS conceivable.

Roughspoken answered 15/3, 2012 at 8:29 Comment(0)
M
0

I use https://github.com/jsoverson/grunt-preprocess for this kind of thing. It fit exactly with what I'm trying to do:

detect_ennemy_collision: (ennemies) ->

# @ifdef DEBUG 
    expect(ennemies).to.be.an 'array'
    expect(ennemies.length).to.be.ok

    for ennemy in ennemies
        (expect ennemy).to.be.an.instanceof Character

# @endif
#...
Musick answered 2/7, 2016 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.