diff --git a/.gitignore b/.gitignore index 3601779f8d..ec47ab5dc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,7 @@ raw presentation test.coffee -test*.coffee -test.litcoffee -test*.litcoffee +test*.*coffee test/*.js parser.output /node_modules diff --git a/Cakefile b/Cakefile index 772f11e4d6..e050431b0b 100644 --- a/Cakefile +++ b/Cakefile @@ -15,17 +15,6 @@ unless process.env.NODE_DISABLE_COLORS yellow = '\x1B[0;33m' reset = '\x1B[0m' -# Built file header. -header = """ - /** - * CoffeeScript Compiler v#{CoffeeScript.VERSION} - * http://coffeescript.org - * - * Copyright 2011, Jeremy Ashkenas - * Released under the MIT License - */ -""" - # Used in folder names like `docs/v1`. majorVersion = parseInt CoffeeScript.VERSION.split('.')[0], 10 @@ -127,6 +116,16 @@ task 'build:full', 'build the CoffeeScript compiler from source twice, and run t build testBuiltCode task 'build:browser', 'merge the built scripts into a single file for use in a browser', -> + # Built file header. + header = """ + /** + * CoffeeScript Compiler v#{CoffeeScript.VERSION} + * http://coffeescript.org + * + * Copyright #{new Date().getFullYear()}, Jeremy Ashkenas + * Released under the MIT License + */ + """ code = """ require['../../package.json'] = (function() { return #{fs.readFileSync "./package.json"}; diff --git a/documentation/sections/command_line_interface.md b/documentation/sections/command_line_interface.md index 3c13142db8..cbf70eb552 100644 --- a/documentation/sections/command_line_interface.md +++ b/documentation/sections/command_line_interface.md @@ -28,8 +28,6 @@ Once installed, you should have access to the `coffee` command, which can execut `coffee --compile --output lib/ src/` * Watch a file for changes, and recompile it every time the file is saved:
`coffee --watch --compile experimental.coffee` -* Concatenate a list of files into a single script:
- `coffee --join project.js --compile src/*.coffee` * Print out the compiled JS from a one-liner:
`coffee -bpe "alert i for i in [0..10]"` * All together now, watch and recompile an entire project as you work on it:
diff --git a/lib/coffeescript/command.js b/lib/coffeescript/command.js index 9919be4f83..26b56d2fd8 100644 --- a/lib/coffeescript/command.js +++ b/lib/coffeescript/command.js @@ -7,7 +7,7 @@ // interactive REPL. // External dependencies. - var BANNER, CoffeeScript, EventEmitter, SWITCHES, buildCSOptionParser, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, + var BANNER, CoffeeScript, EventEmitter, SWITCHES, buildCSOptionParser, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, indexOf = [].indexOf; fs = require('fs'); @@ -45,7 +45,7 @@ BANNER = 'Usage: coffee [options] path/to/script.coffee [args]\n\nIf called without options, `coffee` will run your script.'; // The list of all the valid option flags that `coffee` knows how to handle. - SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-m', '--map', 'generate source map and save as .js.map files'], ['-M', '--inline-map', 'generate source map and include it directly in output'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['--no-header', 'suppress the "Generated by" header'], ['-o', '--output [PATH]', 'set the output path or path/filename for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [MODULE*]', 'require the given module before eval or REPL'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffeescript'], ['-t', '--transpile', 'pipe generated JavaScript through Babel'], ['--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']]; + SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-m', '--map', 'generate source map and save as .js.map files'], ['-M', '--inline-map', 'generate source map and include it directly in output'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['--no-header', 'suppress the "Generated by" header'], ['-o', '--output [PATH]', 'set the output path or path/filename for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [MODULE*]', 'require the given module before eval or REPL'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffeescript'], ['-t', '--transpile', 'pipe generated JavaScript through Babel'], ['--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']]; // Top-level objects shared by all the functions. opts = {}; @@ -129,10 +129,6 @@ opts.outputPath = path.resolve(opts.output); } } - if (opts.join) { - opts.join = path.resolve(opts.join); - console.error('\nThe --join option is deprecated and will be removed in a future version.\n\nIf for some reason it\'s necessary to share local variables between files,\nreplace...\n\n $ coffee --compile --join bundle.js -- a.coffee b.coffee c.coffee\n\nwith...\n\n $ cat a.coffee b.coffee c.coffee | coffee --compile --stdio > bundle.js\n'); - } ref = opts.arguments; results = []; for (i = 0, len = ref.length; i < len; i++) { @@ -263,12 +259,6 @@ CoffeeScript.eval(opts.prelude, task.options); } return CoffeeScript.run(task.input, task.options); - } else if (opts.join && task.file !== opts.join) { - if (helpers.isLiterate(file)) { - task.input = helpers.invertLiterate(task.input); - } - sourceCode[sources.indexOf(task.file)] = task.input; - return compileJoin(); } else { compiled = CoffeeScript.compile(task.input, task.options); task.output = compiled; @@ -320,24 +310,6 @@ }); }; - // If all of the source files are done being read, concatenate and compile - // them together. - joinTimeout = null; - - compileJoin = function() { - if (!opts.join) { - return; - } - if (!sourceCode.some(function(code) { - return code === null; - })) { - clearTimeout(joinTimeout); - return joinTimeout = wait(100, function() { - return compileScript(opts.join, sourceCode.join('\n'), opts.join); - }); - } - }; - // Watch a source CoffeeScript file using `fs.watch`, recompiling it every // time the file is updated. May be used in combination with other options, // such as `--print`. @@ -357,8 +329,7 @@ rewatch(); return compile(); } catch (error) { - removeSource(source, base); - return compileJoin(); + return removeSource(source, base); } }; compile = function() { @@ -453,20 +424,19 @@ }; removeSourceDir = function(source, base) { - var file, i, len, sourcesChanged; + var file, i, len, results, sourcesChanged; delete watchedDirs[source]; sourcesChanged = false; + results = []; for (i = 0, len = sources.length; i < len; i++) { file = sources[i]; if (!(source === path.dirname(file))) { continue; } removeSource(file, base); - sourcesChanged = true; - } - if (sourcesChanged) { - return compileJoin(); + results.push(sourcesChanged = true); } + return results; }; // Remove a file from our source list, and source code cache. Optionally remove @@ -476,11 +446,9 @@ index = sources.indexOf(source); sources.splice(index, 1); sourceCode.splice(index, 1); - if (!opts.join) { - silentUnlink(outputPath(source, base)); - silentUnlink(outputPath(source, base, '.js.map')); - return timeLog(`removed ${source}`); - } + silentUnlink(outputPath(source, base)); + silentUnlink(outputPath(source, base, '.js.map')); + return timeLog(`removed ${source}`); }; silentUnlink = function(path) { @@ -496,11 +464,14 @@ }; // Get the corresponding output JavaScript path for a source file. - outputPath = function(source, base, extension = ".js") { + outputPath = function(source, base, extension) { var basename, dir, srcDir; basename = helpers.baseFileName(source, true, useWinPathSep); srcDir = path.dirname(source); dir = !opts.outputPath ? srcDir : source === base ? opts.outputPath : path.join(opts.outputPath, path.relative(base, srcDir)); + if (extension == null) { + extension = helpers.isESModule(source) ? '.mjs' : '.js'; + } return path.join(dir, basename + extension); }; @@ -528,10 +499,11 @@ // Write out a JavaScript source file with the compiled code. By default, files // are written out in `cwd` as `.js` files with the same name, but the output - // directory can be customized with `--output`. + // directory can be customized with `--output`. If the source file extension is + // `.mcoffee` or `.litmcoffee`, use the `.mjs` extension. // If `generatedSourceMap` is provided, this will write a `.js.map` file into the - // same directory as the `.js` file. + // same directory as the `.js` file; and likewise with `.mjs.map` and `.mjs`. writeJs = function(base, sourcePath, js, jsPath, generatedSourceMap = null) { var compile, jsDir, sourceMapPath; sourceMapPath = `${jsPath}.map`; diff --git a/lib/coffeescript/helpers.js b/lib/coffeescript/helpers.js index ad9178e31a..30e7af54aa 100644 --- a/lib/coffeescript/helpers.js +++ b/lib/coffeescript/helpers.js @@ -230,7 +230,7 @@ // A `.coffee.md` compatible version of `basename`, that returns the file sans-extension. exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) { - var parts, pathSep; + var parts, pathSep, ref1; pathSep = useWinPathSep ? /\\|\// : /\//; parts = file.split(pathSep); file = parts[parts.length - 1]; @@ -239,7 +239,7 @@ } parts = file.split('.'); parts.pop(); - if (parts[parts.length - 1] === 'coffee' && parts.length > 1) { + if (parts.length > 1 && ((ref1 = parts[parts.length - 1]) === 'coffee' || ref1 === 'mcoffee')) { parts.pop(); } return parts.join('.'); @@ -247,12 +247,17 @@ // Determine if a filename represents a CoffeeScript file. exports.isCoffee = function(file) { - return /\.((lit)?coffee|coffee\.md)$/.test(file); + return /\.((lit)?m?coffee|m?coffee\.md)$/.test(file); }; // Determine if a filename represents a Literate CoffeeScript file. exports.isLiterate = function(file) { - return /\.(litcoffee|coffee\.md)$/.test(file); + return /\.(litcoffee|litmcoffee|coffee\.md)$/.test(file); + }; + + // Determine if a filename represents a CoffeeScript file compiling to an ES module. + exports.isESModule = function(file) { + return /\.((lit)?mcoffee|mcoffee\.md)$/.test(file); }; // Throws a SyntaxError from a given location. diff --git a/src/command.coffee b/src/command.coffee index 41dae3f63e..b813af2235 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -37,7 +37,6 @@ SWITCHES = [ ['-e', '--eval', 'pass a string from the command line as input'] ['-h', '--help', 'display this help message'] ['-i', '--interactive', 'run an interactive CoffeeScript REPL'] - ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'] ['-m', '--map', 'generate source map and save as .js.map files'] ['-M', '--inline-map', 'generate source map and include it directly in output'] ['-n', '--nodes', 'print out the parse tree that the parser produces'] @@ -117,22 +116,6 @@ exports.run = -> opts.outputFilename = null opts.outputPath = path.resolve opts.output - if opts.join - opts.join = path.resolve opts.join - console.error ''' - - The --join option is deprecated and will be removed in a future version. - - If for some reason it's necessary to share local variables between files, - replace... - - $ coffee --compile --join bundle.js -- a.coffee b.coffee c.coffee - - with... - - $ cat a.coffee b.coffee c.coffee | coffee --compile --stdio > bundle.js - - ''' for source in opts.arguments source = path.resolve source compilePath source, yes, source @@ -211,10 +194,6 @@ compileScript = (file, input, base = null) -> CoffeeScript.register() CoffeeScript.eval opts.prelude, task.options if opts.prelude CoffeeScript.run task.input, task.options - else if opts.join and task.file isnt opts.join - task.input = helpers.invertLiterate task.input if helpers.isLiterate file - sourceCode[sources.indexOf(task.file)] = task.input - compileJoin() else compiled = CoffeeScript.compile task.input, task.options task.output = compiled @@ -254,16 +233,6 @@ compileStdio = -> stdin.on 'end', -> compileScript null, Buffer.concat(buffers).toString() -# If all of the source files are done being read, concatenate and compile -# them together. -joinTimeout = null -compileJoin = -> - return unless opts.join - unless sourceCode.some((code) -> code is null) - clearTimeout joinTimeout - joinTimeout = wait 100, -> - compileScript opts.join, sourceCode.join('\n'), opts.join - # Watch a source CoffeeScript file using `fs.watch`, recompiling it every # time the file is updated. May be used in combination with other options, # such as `--print`. @@ -280,7 +249,6 @@ watch = (source, base) -> compile() catch removeSource source, base - compileJoin() compile = -> clearTimeout compileTimeout @@ -349,7 +317,6 @@ removeSourceDir = (source, base) -> for file in sources when source is path.dirname file removeSource file, base sourcesChanged = yes - compileJoin() if sourcesChanged # Remove a file from our source list, and source code cache. Optionally remove # the compiled JS version as well. @@ -357,10 +324,9 @@ removeSource = (source, base) -> index = sources.indexOf source sources.splice index, 1 sourceCode.splice index, 1 - unless opts.join - silentUnlink outputPath source, base - silentUnlink outputPath source, base, '.js.map' - timeLog "removed #{source}" + silentUnlink outputPath source, base + silentUnlink outputPath source, base, '.js.map' + timeLog "removed #{source}" silentUnlink = (path) -> try @@ -369,15 +335,16 @@ silentUnlink = (path) -> throw err unless err.code in ['ENOENT', 'EPERM'] # Get the corresponding output JavaScript path for a source file. -outputPath = (source, base, extension=".js") -> - basename = helpers.baseFileName source, yes, useWinPathSep - srcDir = path.dirname source +outputPath = (source, base, extension) -> + basename = helpers.baseFileName source, yes, useWinPathSep + srcDir = path.dirname source dir = unless opts.outputPath srcDir else if source is base opts.outputPath else path.join opts.outputPath, path.relative base, srcDir + extension ?= if helpers.isESModule(source) then '.mjs' else '.js' path.join dir, basename + extension # Recursively mkdir, like `mkdir -p`. @@ -396,13 +363,14 @@ mkdirp = (dir, fn) -> # Write out a JavaScript source file with the compiled code. By default, files # are written out in `cwd` as `.js` files with the same name, but the output -# directory can be customized with `--output`. +# directory can be customized with `--output`. If the source file extension is +# `.mcoffee` or `.litmcoffee`, use the `.mjs` extension. # # If `generatedSourceMap` is provided, this will write a `.js.map` file into the -# same directory as the `.js` file. +# same directory as the `.js` file; and likewise with `.mjs.map` and `.mjs`. writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) -> sourceMapPath = "#{jsPath}.map" - jsDir = path.dirname jsPath + jsDir = path.dirname jsPath compile = -> if opts.compile js = ' ' if js.length <= 0 diff --git a/src/helpers.coffee b/src/helpers.coffee index bf62980b96..f99dfb0866 100644 --- a/src/helpers.coffee +++ b/src/helpers.coffee @@ -160,16 +160,19 @@ exports.baseFileName = (file, stripExt = no, useWinPathSep = no) -> parts = file.split(pathSep) file = parts[parts.length - 1] return file unless stripExt and file.indexOf('.') >= 0 - parts = file.split('.') + parts = file.split '.' parts.pop() - parts.pop() if parts[parts.length - 1] is 'coffee' and parts.length > 1 - parts.join('.') + parts.pop() if parts.length > 1 and parts[parts.length - 1] in ['coffee', 'mcoffee'] + parts.join '.' # Determine if a filename represents a CoffeeScript file. -exports.isCoffee = (file) -> /\.((lit)?coffee|coffee\.md)$/.test file +exports.isCoffee = (file) -> /\.((lit)?m?coffee|m?coffee\.md)$/.test file # Determine if a filename represents a Literate CoffeeScript file. -exports.isLiterate = (file) -> /\.(litcoffee|coffee\.md)$/.test file +exports.isLiterate = (file) -> /\.(litcoffee|litmcoffee|coffee\.md)$/.test file + +# Determine if a filename represents a CoffeeScript file compiling to an ES module. +exports.isESModule = (file) -> /\.((lit)?mcoffee|mcoffee\.md)$/.test file # Throws a SyntaxError from a given location. # The error's `toString` will return an error message following the "standard" diff --git a/test/argument_parsing.coffee b/test/argument_parsing.coffee index 6c613956e8..42f1f9ad73 100644 --- a/test/argument_parsing.coffee +++ b/test/argument_parsing.coffee @@ -124,7 +124,6 @@ If called without options, `coffee` will run your script. -e, --eval pass a string from the command line as input -h, --help display this help message -i, --interactive run an interactive CoffeeScript REPL - -j, --join concatenate the source CoffeeScript before compiling -m, --map generate source map and save as .js.map files -M, --inline-map generate source map and include it directly in output -n, --nodes print out the parse tree that the parser produces