This Browser compatibility layer extends core CoffeeScript functions
to make things work smoothly when compiling code directly in the browser.
We add support for loading remote Coffee scripts via XHR, and
text/coffeescript script tags, source maps via data-URLs, and so on.
Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of text/coffeescript.
This happens on page load.
runScripts = ->
scripts = window.document.getElementsByTagName 'script'
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
coffees = (s for s in scripts when s.type in coffeetypes)
index = 0execute = ->
param = coffees[index]
if param instanceof Array
CoffeeScript.run param...
index++
execute()
for script, i in coffees
do (script, i) ->
options = literate: script.type is coffeetypes[1]
source = script.src or script.getAttribute('data-src')
if source
CoffeeScript.load source,
(param) ->
coffees[i] = param
execute()
options
trueelse
options.sourceFiles = ['embedded']
coffees[i] = [script.innerHTML, options]
execute()
cake is a simplified version of Make
(Rake, Jake)
for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
and can call them from the command line, or invoke them from other tasks.
Running cake with no arguments will print out a list of all the tasks in the
current directory’s Cakefile.
Define an option that the Cakefile accepts. The parsed options hash,
containing all of the command-line options passed, will be made available
as the first argument to the action.
Run cake. Executes all of the tasks you pass, in order. Note that Node’s
asynchrony may cause tasks to execute in a different order than you’d expect.
If no tasks are passed, print the help screen. Keep a reference to the
original directory name, when running Cake tasks from subdirectories.
exports.run = ->global.__originalDirname = fs.realpathSync '.'
process.chdir cakefileDirectory __originalDirname
args = process.argv[2..]
CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
oparse = new optparse.OptionParser switches
return printTasks() unless args.length
try
options = oparse.parse(args)
catch e
return fatalError "#{e}"
invoke arg for arg in options.arguments
Print an error and exit when attempting to use an invalid task/option.
fatalError = (message) ->console.error message + '\n'console.log 'To see a list of all tasks/options, run "cake"'
process.exit 1missingTask = (task) -> fatalError "No such task: #{task}"
When cake is invoked, search in the current and all parent directories
to find the relevant Cakefile.
cakefileDirectory = (dir) ->return dir if fs.existsSync path.join dir, 'Cakefile'
parent = path.normalize path.join dir, '..'return cakefileDirectory parent unless parent is dir
thrownew Error "Cakefile not found in #{process.cwd()}"
CoffeeScript can be used both on the server, as a command-line compiler based
on Node.js/V8, or to run CoffeeScript directly in the browser. This module
contains the main entry functions for tokenizing, parsing, and compiling
source CoffeeScript into JavaScript.
For each compiled file, save its source in memory in case we need to
recompile it later. We might need to recompile if the first compilation
didn’t create a source map (faster) but something went wrong and we need
a stack trace. Assuming that most of the time, code isn’t throwing
exceptions, it’s probably more efficient to compile twice only when we
need a stack trace, rather than always generating a source map even when
it’s not likely to be used. Save in form of filename: (source)
Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
If options.sourceMap is specified, then options.filename must also be
specified. All options that can be passed to SourceMap#generate may also
be passed here.
This returns a javascript string, unless options.sourceMap is passed,
in which case this returns a {js, v3SourceMap, sourceMap}
object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
doing programmatic lookups.
Always generate a source map if no filename is passed in, since without a
a filename we have no way to retrieve this source later in the event that
we need to recompile it to get a source map for prepareStackTrace.
generateSourceMap = options.sourceMap or options.inlineMap ornot options.filename?
filename = options.filename or'<anonymous>'
sources[filename] = code
map = new SourceMap if generateSourceMap
tokens = lexer.tokenize code, options
Parse a string of CoffeeScript code or an array of lexed tokens, and
return the AST. You can then compile it by calling .compile() on the root,
or traverse it by using .traverseChildren() with a callback.
dir = if options.filename?
path.dirname fs.realpathSync options.filename
else
fs.realpathSync '.'
mainModule.paths = require('module')._nodeModulePaths dir
Throw error with deprecation warning when depending upon implicit require.extensions registration
ifrequire.extensions
for ext in @FILE_EXTENSIONS thendo (ext) ->
require.extensions[ext] ?= ->thrownew Error """
Use CoffeeScript.register() or require the coffee-script/register module to require #{ext} files.
"""
exports._compileFile = (filename, sourceMap = no, inlineMap = no) ->
raw = fs.readFileSync filename, 'utf8'
As the filename and code of a dynamically loaded file will be different
from the original file compiled with CoffeeScript.run, add that
information to error so it can be pretty-printed later.
The real Lexer produces a generic stream of tokens. This object provides a
thin wrapper around it, compatible with the Jison API. We can then pass it
directly as a “Jison lexer”.
parser.lexer =
lex: ->
token = parser.tokens[@pos++]
if token
[tag, @yytext, @yylloc] = token
parser.errorToken = token.origin or token
@yylineno = @yylloc.first_line
else
tag = ''
tag
setInput: (tokens) ->
parser.tokens = tokens
@pos = 0
upcomingInput: ->""
Disregard Jison’s message, it contains redundant line number information.
Disregard the token, we take its value directly from the lexer in case
the error is caused by a generated token which might refer to its origin.
{errorToken, tokens} = parser
[errorTag, errorText, errorLoc] = errorToken
errorText = switchwhen errorToken is tokens[tokens.length - 1]
'end of input'when errorTag in ['INDENT', 'OUTDENT']
'indentation'when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
errorTag.replace(/_START$/, '').toLowerCase()
else
helpers.nameWhitespaceCharacter errorText
The second argument has a loc property, which should have the location
data for this token. Unfortunately, Jison seems to send an outdated loc
(from the previous token), so we take the location information directly
from the lexer.
CoffeeScript compiled in a browser may get compiled with options.filename
of <anonymous>, but the browser may request the stack trace with the
filename of the script file.
Based on michaelficarra/CoffeeScriptRedux
NodeJS / V8 have no support for transforming positions in stack traces using
sourceMap, so we must monkey-patch Error to display CoffeeScript source
positions.
Error.prepareStackTrace = (err, stack) ->getSourceMapping = (filename, line, column) ->
sourceMap = getSourceMap filename
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap?
if answer? then [answer[0] + 1, answer[1] + 1] elsenull
frames = for frame in stack
breakif frame.getFunction() is exports.run
" at #{formatSourcePosition frame, getSourceMapping}""#{err.toString()}\n#{frames.join '\n'}\n"
The coffee utility. Handles command-line compilation of CoffeeScript
into various forms: saved into .js files or printed to stdout
or recompiled every time the source is saved,
printed as a token stream or as the syntax tree, or launch an
interactive REPL.
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 [DIR]', 'set the output directory 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 coffee-script']
['-t', '--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']
]
Run coffee by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
-- will be passed verbatim to your script as arguments in process.argv
Make the REPL CLI use the global context so as to (a) be consistent with the
node REPL CLI and, therefore, (b) make packages that modify native prototypes
(such as ‘colors’ and ‘sugar’) work as expected.
replCliOpts = useGlobal: yes
opts.prelude = makePrelude opts.requireif opts.require
replCliOpts.prelude = opts.prelude
return forkNode() if opts.nodejs
return usage() if opts.help
return version() if opts.version
returnrequire('./repl').start(replCliOpts) if opts.interactive
return compileStdio() if opts.stdio
return compileScript null, opts.arguments[0] if opts.eval
returnrequire('./repl').start(replCliOpts) unless opts.arguments.length
literals = if opts.run then opts.arguments.splice 1else []
process.argv = process.argv[0..1].concat literals
process.argv[0] = 'coffee'
opts.output = path.resolve opts.output if 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
makePrelude = (requires) ->
requires.map (module) ->
[_, name, module] = match if match = module.match(/^(.*)=(.*)$/)
name ||= helpers.baseFileName module, yes, useWinPathSep
"#{name} = require('#{module}')"
.join ';'
Compile a path, which could be a script or a directory. If a directory
is passed, recursively compile all ‘.coffee’, ‘.litcoffee’, and ‘.coffee.md’
extension source files in it and all subdirectories.
compilePath = (source, topLevel, base) ->returnif source in sources or
watchedDirs[source] ornot topLevel and (notSources[source] or hidden source)
try
stats = fs.statSync source
catch err
if err.code is'ENOENT'console.error "File not found: #{source}"
process.exit 1throw err
if stats.isDirectory()
if path.basename(source) is'node_modules'
notSources[source] = yesreturnif opts.run
compilePath findDirectoryIndex(source), topLevel, base
return
watchDir source, base if opts.watch
try
files = fs.readdirSync source
catch err
if err.code is'ENOENT'thenreturnelsethrow err
for file in files
compilePath (path.join source, file), no, base
elseif topLevel or helpers.isCoffee source
sources.push source
sourceCode.push nulldelete notSources[source]
watch source, base if opts.watch
try
code = fs.readFileSync source
catch err
if err.code is'ENOENT'thenreturnelsethrow err
compileScript(source, code.toString(), base)
else
notSources[source] = yesfindDirectoryIndex = (source) ->for ext in CoffeeScript.FILE_EXTENSIONS
index = path.join source, "index#{ext}"tryreturn index if (fs.statSync index).isFile()
catch err
throw err unless err.code is'ENOENT'console.error "Missing index.coffee or index.litcoffee in #{source}"
process.exit 1
Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets __filename,
__dirname and module.filename to be correct relative to the script’s path.
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.
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.
If generatedSourceMap is provided, this will write a .js.map file into the
same directory as the .js file.
The CoffeeScript parser is generated by Jison
from this grammar file. Jison is a bottom-up parser generator, similar in
style to Bison, implemented in JavaScript.
It can recognize LALR(1), LR(0), SLR(1), and LR(1)
type grammars. To create the Jison parser, we list the pattern to match
on the left-hand side, and the action to take (usually the creation of syntax
tree nodes) on the right. As the parser runs, it
shifts tokens from our token stream, from left to right, and
attempts to match
the token sequence against the rules below. When a match can be made, it
reduces into the nonterminal
(the enclosing name at the top), and we proceed from there.
If you run the cake build:parser command, Jison constructs a parse table
from our rules and saves it into lib/parser.js.
Since we’re going to be wrapped in a function by Jison in any case, if our
action immediately returns a value, we can optimize by removing the function
wrapper and just returning the value directly.
Our handy DSL for Jison grammar generation, thanks to
Tim Caswell. For every rule in the grammar,
we pass the pattern-defining string, the action to run, and extra options,
optionally. If no action is specified, we simply pass the value of the
previous nonterminal.
o = (patternString, action, options) ->
patternString = patternString.replace /\s{2,}/g, ' '
patternCount = patternString.split(' ').length
return [patternString, '$$ = $1;', options] unless action
action = if match = unwrap.exec action then match[1] else"(#{action}())"
Returns a function which adds location data to the first parameter passed
in, and returns the parameter. If the parameter is not a node, it will
just be passed through unaffected.
In all of the rules that follow, you’ll see the name of the nonterminal as
the key to a list of alternative matches. With each match’s action, the
dollar-sign variables are provided by Jison as references to the value of
their numeric position, so in this rule:
"Expression UNLESS Expression"
$1 would be the value of the first Expression, $2 would be the token
for the UNLESS terminal, and $3 would be the value of the second
Expression.
Block and statements, which make up a line in a body. YieldReturn is a
statement, but not included in Statement because that results in an ambiguous
grammar.
Line: [
o 'Expression'
o 'Statement'
o 'YieldReturn'
]
All the different types of expressions in our language. The basic unit of
CoffeeScript is the Expression – everything that can be an expression
is one. Blocks serve as the building blocks of many other rules, making
them somewhat circular.
Expression: [
o 'Value'
o 'Invocation'
o 'Code'
o 'Operation'
o 'Assign'
o 'If'
o 'Try'
o 'While'
o 'For'
o 'Switch'
o 'Class'
o 'Throw'
o 'Yield'
]
Yield: [
o 'YIELD', ->new Op $1, new Value new Literal ''
o 'YIELD Expression', ->new Op $1, $2
o 'YIELD FROM Expression', ->new Op $1.concat($2), $3
]
All of our immediate values. Generally these can be passed straight
through and printed to JavaScript.
Literal: [
o 'AlphaNumeric'
o 'JS', ->new PassthroughLiteral $1
o 'Regex'
o 'UNDEFINED', ->new UndefinedLiteral
o 'NULL', ->new NullLiteral
o 'BOOL', ->new BooleanLiteral $1
o 'INFINITY', ->new InfinityLiteral $1
o 'NAN', ->new NaNLiteral
]
SimpleAssignable: [
o 'Identifier', ->new Value $1
o 'Value Accessor', -> $1.add $2
o 'Invocation Accessor', ->new Value $1, [].concat $2
o 'ThisProperty'
]
Class definitions have optional bodies of prototype property assignments,
and optional references to the superclass.
Class: [
o 'CLASS', ->new Class
o 'CLASS Block', ->new Class null, null, $2
o 'CLASS EXTENDS Expression', ->new Class null, $3
o 'CLASS EXTENDS Expression Block', ->new Class null, $3, $4
o 'CLASS SimpleAssignable', ->new Class $2
o 'CLASS SimpleAssignable Block', ->new Class $2, null, $3
o 'CLASS SimpleAssignable EXTENDS Expression', ->new Class $2, $4
o 'CLASS SimpleAssignable EXTENDS Expression Block', ->new Class $2, $4, $5
]
Import: [
o 'IMPORT String', ->new ImportDeclaration null, $2
o 'IMPORT ImportDefaultSpecifier FROM String', ->new ImportDeclaration new ImportClause($2, null), $4
o 'IMPORT ImportNamespaceSpecifier FROM String', ->new ImportDeclaration new ImportClause(null, $2), $4
o 'IMPORT { } FROM String', ->new ImportDeclaration new ImportClause(null, new ImportSpecifierList []), $5
o 'IMPORT { ImportSpecifierList OptComma } FROM String', ->new ImportDeclaration new ImportClause(null, new ImportSpecifierList $3), $7
o 'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String', ->new ImportDeclaration new ImportClause($2, $4), $6
o 'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String', ->new ImportDeclaration new ImportClause($2, new ImportSpecifierList $5), $9
]
ImportSpecifierList: [
o 'ImportSpecifier', -> [$1]
o 'ImportSpecifierList , ImportSpecifier', -> $1.concat $3
o 'ImportSpecifierList OptComma TERMINATOR ImportSpecifier', -> $1.concat $4
o 'INDENT ImportSpecifierList OptComma OUTDENT', -> $2
o 'ImportSpecifierList OptComma INDENT ImportSpecifierList OptComma OUTDENT', -> $1.concat $4
]
ImportSpecifier: [
o 'Identifier', ->new ImportSpecifier $1
o 'Identifier AS Identifier', ->new ImportSpecifier $1, $3
o 'DEFAULT', ->new ImportSpecifier new Literal $1
o 'DEFAULT AS Identifier', ->new ImportSpecifier new Literal($1), $3
]
ImportDefaultSpecifier: [
o 'Identifier', ->new ImportDefaultSpecifier $1
]
ImportNamespaceSpecifier: [
o 'IMPORT_ALL AS Identifier', ->new ImportNamespaceSpecifier new Literal($1), $3
]
Export: [
o 'EXPORT { }', ->new ExportNamedDeclaration new ExportSpecifierList []
o 'EXPORT { ExportSpecifierList OptComma }', ->new ExportNamedDeclaration new ExportSpecifierList $3
o 'EXPORT Class', ->new ExportNamedDeclaration $2
o 'EXPORT Identifier = Expression', ->new ExportNamedDeclaration new Assign $2, $4, null,
moduleDeclaration: 'export'
o 'EXPORT Identifier = TERMINATOR Expression', ->new ExportNamedDeclaration new Assign $2, $5, null,
moduleDeclaration: 'export'
o 'EXPORT Identifier = INDENT Expression OUTDENT', ->new ExportNamedDeclaration new Assign $2, $5, null,
moduleDeclaration: 'export'
o 'EXPORT DEFAULT Expression', ->new ExportDefaultDeclaration $3
o 'EXPORT EXPORT_ALL FROM String', ->new ExportAllDeclaration new Literal($2), $4
o 'EXPORT { ExportSpecifierList OptComma } FROM String', ->new ExportNamedDeclaration new ExportSpecifierList($3), $7
]
ExportSpecifierList: [
o 'ExportSpecifier', -> [$1]
o 'ExportSpecifierList , ExportSpecifier', -> $1.concat $3
o 'ExportSpecifierList OptComma TERMINATOR ExportSpecifier', -> $1.concat $4
o 'INDENT ExportSpecifierList OptComma OUTDENT', -> $2
o 'ExportSpecifierList OptComma INDENT ExportSpecifierList OptComma OUTDENT', -> $1.concat $4
]
ExportSpecifier: [
o 'Identifier', ->new ExportSpecifier $1
o 'Identifier AS Identifier', ->new ExportSpecifier $1, $3
o 'Identifier AS DEFAULT', ->new ExportSpecifier $1, new Literal $3
o 'DEFAULT', ->new ExportSpecifier new Literal $1
o 'DEFAULT AS Identifier', ->new ExportSpecifier new Literal($1), $3
]
Slice: [
o 'Expression RangeDots Expression', ->new Range $1, $3, $2
o 'Expression RangeDots', ->new Range $1, null, $2
o 'RangeDots Expression', ->new Range null, $2, $1
o 'RangeDots', ->new Range null, null, $1
]
The ArgList is both the list of objects passed into a function call,
as well as the contents of an array literal
(i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o 'Arg', -> [$1]
o 'ArgList , Arg', -> $1.concat $3
o 'ArgList OptComma TERMINATOR Arg', -> $1.concat $4
o 'INDENT ArgList OptComma OUTDENT', -> $2
o 'ArgList OptComma INDENT ArgList OptComma OUTDENT', -> $1.concat $4
]
Just simple, comma-separated, required arguments (no fancy syntax). We need
this to be separate from the ArgList for use in Switch blocks, where
having the newlines wouldn’t make sense.
SimpleArgs: [
o 'Expression'
o 'SimpleArgs , Expression', -> [].concat $1, $3
]
Parenthetical expressions. Note that the Parenthetical is a Value,
not an Expression, so if you need to use an expression in a place
where only values are accepted, wrapping it in parentheses will always do
the trick.
Parenthetical: [
o '( Body )', ->new Parens $2
o '( INDENT Body OUTDENT )', ->new Parens $3
]
WhileSource: [
o 'WHILE Expression', ->new While $2
o 'WHILE Expression WHEN Expression', ->new While $2, guard: $4
o 'UNTIL Expression', ->new While $2, invert: true
o 'UNTIL Expression WHEN Expression', ->new While $2, invert: true, guard: $4
]
Array, object, and range comprehensions, at the most generic level.
Comprehensions can either be normal, with a block of expressions to execute,
or postfix, with a single expression.
For: [
o 'Statement ForBody', ->new For $1, $2
o 'Expression ForBody', ->new For $1, $2
o 'ForBody Block', ->new For $2, $1
]
ForBody: [
o 'FOR Range', -> source: (LOC(2) new Value($2))
o 'FOR Range BY Expression', -> source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForSource', -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
]
ForStart: [
o 'FOR ForVariables', -> $2
o 'FOR OWN ForVariables', -> $3.own = yes; $3.ownTag = (LOC(2) new Literal($2)); $3
]
An array or range comprehension has variables for the current element
and (optional) reference to the current index. Or, key, value, in the case
of object comprehensions.
ForVariables: [
o 'ForValue', -> [$1]
o 'ForValue , ForValue', -> [$1, $3]
]
The source of a comprehension is an array or object with an optional guard
clause. If it’s an array comprehension, you can also choose to step through
in fixed-size increments.
ForSource: [
o 'FORIN Expression', -> source: $2
o 'FOROF Expression', -> source: $2, object: yes
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORFROM Expression', -> source: $2, from: yes
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
]
Switch: [
o 'SWITCH Expression INDENT Whens OUTDENT', ->new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', ->new Switch $2, $4, $6
o 'SWITCH INDENT Whens OUTDENT', ->new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', ->new Switch null, $3, $5
]
Whens: [
o 'When'
o 'Whens When', -> $1.concat $2
]
Arithmetic and logical operators, working on one or more operands.
Here they are grouped by order of precedence. The actual precedence rules
are defined at the bottom of the page. It would be shorter if we could
combine most of these rules into a single generic Operand OpSymbol Operand
-type rule, but in order to make the precedence binding possible, separate
rules are necessary.
Operation: [
o 'UNARY Expression', ->new Op $1 , $2
o 'UNARY_MATH Expression', ->new Op $1 , $2
o '- Expression', (->new Op '-', $2), prec: 'UNARY_MATH'
o '+ Expression', (->new Op '+', $2), prec: 'UNARY_MATH'
o '-- SimpleAssignable', ->new Op '--', $2
o '++ SimpleAssignable', ->new Op '++', $2
o 'SimpleAssignable --', ->new Op '--', $1, null, true
o 'SimpleAssignable ++', ->new Op '++', $1, null, true
Finally, now that we have our grammar and our operators, we can create
our Jison.Parser. We do this by processing all of our rules, recording all
terminals (every symbol which does not appear as the name of a rule above)
as “tokens”.
tokens = []
for name, alternatives of grammar
grammar[name] = for alt in alternatives
for token in alt[0].split ' '
tokens.push token unless grammar[token]
alt[1] = "return #{alt[1]}"if name is'Root'
alt
Initialize the Parser with our list of terminal tokens, our grammar
rules, and the name of the root. Reverse the operators because Jison orders
precedence from low to high, and we have it high to low
(as in Yacc).
This file contains the common helper functions that we’d like to share among
the Lexer, Rewriter, and the Nodes. Merge objects, flatten
arrays, count characters, that sort of thing.
Merge objects, returning a fresh copy with attributes from both sides.
Used every time Base#compile is called, to allow properties in the
options hash to propagate down the tree without polluting other branches.
Return a flattened version of an array.
Handy for getting a list of children from the nodes.
exports.flatten = flatten = (array) ->
flattened = []
for element in array
if'[object Array]'is Object::toString.call element
flattened = flattened.concat flatten element
else
flattened.push element
flattened
Simple function for inverting Literate CoffeeScript code by putting the
documentation in comments, producing a string of CoffeeScript code that
can be compiled “normally”.
exports.invertLiterate = (code) ->
maybe_code = true
lines = for line in code.split('\n')
if maybe_code and/^([ ]{4}|[ ]{0,3}\t)/.test line
line
elseif maybe_code = /^\s*$/.test line
line
else'# ' + line
lines.join '\n'
This returns a function which takes an object as a parameter, and if that
object is an AST node, updates that object’s locationData.
The object is returned either way.
exports.addLocationDataFn = (first, last) ->
(obj) ->
if ((typeof obj) is'object') and (!!obj['updateLocationDataIfMissing'])
obj.updateLocationDataIfMissing buildLocationData(first, last)
return obj
Throws a SyntaxError from a given location.
The error’s toString will return an error message following the “standard”
format <filename>:<line>:<col>: <message> plus the line with the error and a
marker showing where the error is.
Instead of showing the compiler’s stacktrace, show our custom error message
(this is useful when the error bubbles up in Node.js applications that
compile CoffeeScript for example).
end = if first_line is last_line then last_column + 1else codeLine.length
marker = codeLine[...start].replace(/[^\s]/g, ' ') + repeat('^', end - start)
The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
matches against the beginning of the source code. When a match is found,
a token is produced, we consume the match, and start again. Tokens are in the
form:
[tag, value, locationData]
where locationData is {first_line, first_column, last_line, last_column}, which is a
format that can be fed directly into Jison. These
are read by jison in the parser.lexer function defined in coffee-script.coffee.
The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
tokens. Some potential ambiguity in the grammar has been avoided by
pushing some extra smarts into the Lexer.
tokenize is the Lexer’s main method. Scan by attempting to match tokens
one at a time, using a regular expression anchored at the start of the
remaining code, or a custom recursive token-matching method
(for interpolations). When the next token has been recorded, we move forward
within the code past the token, and begin again.
Each tokenizing method is responsible for returning the number of characters
it has consumed.
Before returning the token stream, run it through the Rewriter.
tokenize: (code, opts = {}) ->
@literate = opts.literate # Are we lexing literate CoffeeScript?
@indent = 0# The current indentation level.
@baseIndent = 0# The overall minimum indentation level
@indebt = 0# The over-indentation at the current level.
@outdebt = 0# The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
@seenFor = no# Used to recognize FORIN, FOROF and FORFROM tokens.
@seenImport = no# Used to recognize IMPORT FROM? AS? tokens.
@seenExport = no# Used to recognize EXPORT FROM? AS? tokens.
@importSpecifierList = no# Used to identify when in an IMPORT {...} FROM? ...
@exportSpecifierList = no# Used to identify when in an EXPORT {...} FROM? ...
@chunkLine =
opts.line or0# The start line for the current @chunk.
@chunkColumn =
opts.column or0# The start column of the current @chunk.
code = @clean code # The stripped, cleaned original source code.
At every position, run through this list of attempted matches,
short-circuiting if any of them succeed. Their order determines precedence:
@literalToken is the fallback catch-all.
i = 0while @chunk = code[i..]
consumed = \
@identifierToken() or
@commentToken() or
@whitespaceToken() or
@lineToken() or
@stringToken() or
@numberToken() or
@regexToken() or
@jsToken() or
@literalToken()
[@chunkLine, @chunkColumn] = @getLineAndColumnFromChunk consumed
i += consumed
return {@tokens, index: i} if opts.untilBalanced and @ends.length is0
@closeIndentation()
@error "missing #{end.tag}", end.origin[2] if end = @ends.pop()
return @tokens if opts.rewrite isoff
(new Rewriter).rewrite @tokens
Preprocess the code to remove leading and trailing whitespace, carriage
returns, etc. If we’re lexing literate CoffeeScript, strip external Markdown
by removing all lines that aren’t indented by at least four spaces or a tab.
clean: (code) ->
code = code.slice(1) if code.charCodeAt(0) is BOM
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''if WHITESPACE.test code
code = "\n#{code}"
@chunkLine--
code = invertLiterate code if @literate
code
Matches identifying literals: variables, keywords, method names, etc.
Check to ensure that JavaScript reserved words aren’t being used as
identifiers. Because CoffeeScript reserves a handful of keywords that are
allowed in JavaScript, we’re careful not to tag them as keywords when
referenced as property names here, so you can still do jQuery.is() even
though is means === otherwise.
identifierToken: ->return0unless match = IDENTIFIER.exec @chunk
[input, id, colon] = match
idLength = id.length
poppedToken = undefinedif id is'own'and @tag() is'FOR'
@token 'OWN', id
return id.length
if id is'from'and @tag() is'YIELD'
@token 'FROM', id
return id.length
if id is'as'and @seenImport
if @value() is'*'
@tokens[@tokens.length - 1][0] = 'IMPORT_ALL'elseif @value() in COFFEE_KEYWORDS
@tokens[@tokens.length - 1][0] = 'IDENTIFIER'if @tag() in ['DEFAULT', 'IMPORT_ALL', 'IDENTIFIER']
@token 'AS', id
return id.length
if id is'as'and @seenExport and @tag() in ['IDENTIFIER', 'DEFAULT']
@token 'AS', id
return id.length
if id is'default'and @seenExport and @tag() in ['EXPORT', 'AS']
@token 'DEFAULT', id
return id.length
[..., prev] = @tokens
tag =
if colon or prev? and
(prev[0] in ['.', '?.', '::', '?::'] ornot prev.spaced and prev[0] is'@')
'PROPERTY'else'IDENTIFIER'if tag is'IDENTIFIER'and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS) andnot (@exportSpecifierList and id in COFFEE_KEYWORDS)
tag = id.toUpperCase()
if tag is'WHEN'and @tag() in LINE_BREAK
tag = 'LEADING_WHEN'elseif tag is'FOR'
@seenFor = yeselseif tag is'UNLESS'
tag = 'IF'elseif tag is'IMPORT'
@seenImport = yeselseif tag is'EXPORT'
@seenExport = yeselseif tag in UNARY
tag = 'UNARY'elseif tag in RELATION
if tag isnt'INSTANCEOF'and @seenFor
tag = 'FOR' + tag
@seenFor = noelse
tag = 'RELATION'if @value() is'!'
poppedToken = @tokens.pop()
id = '!' + id
elseif tag is'IDENTIFIER'and @seenFor and id is'from'and
isForFrom(prev)
tag = 'FORFROM'
@seenFor = noif tag is'IDENTIFIER'and id in RESERVED
@error "reserved word '#{id}'", length: id.length
unless tag is'PROPERTY'if id in COFFEE_ALIASES
alias = id
id = COFFEE_ALIAS_MAP[id]
tag = switch id
when'!'then'UNARY'when'==', '!='then'COMPARE'when'true', 'false'then'BOOL'when'break', 'continue', \
'debugger'then'STATEMENT'when'&&', '||'then id
else tag
tagToken = @token tag, id, 0, idLength
tagToken.origin = [tag, alias, tagToken[2]] if alias
if poppedToken
[tagToken[2].first_line, tagToken[2].first_column] =
[poppedToken[2].first_line, poppedToken[2].first_column]
if colon
colonOffset = input.lastIndexOf ':'
@token ':', ':', colonOffset, colon.length
input.length
Matches numbers, including decimals, hex, and exponential notation.
Be careful not to interfere with ranges-in-progress.
numberToken: ->return0unless match = NUMBER.exec @chunk
number = match[0]
lexedLength = number.length
switchwhen/^0[BOX]/.test number
@error "radix prefix in '#{number}' must be lowercase", offset: 1when/^(?!0x).*E/.test number
@error "exponential notation in '#{number}' must be indicated with a lowercase 'e'",
offset: number.indexOf('E')
when/^0\d*[89]/.test number
@error "decimal literal '#{number}' must not be prefixed with '0'", length: lexedLength
when/^0\d+/.test number
@error "octal literal '#{number}' must be prefixed with '0o'", length: lexedLength
base = switch number.charAt 1when'b'then2when'o'then8when'x'then16elsenull
numberValue = if base? then parseInt(number[2..], base) else parseFloat(number)
if number.charAt(1) in ['b', 'o']
number = "0x#{numberValue.toString 16}"
tag = if numberValue is Infinity then'INFINITY'else'NUMBER'
@token tag, number, 0, lexedLength
lexedLength
Find the smallest indentation. It will be removed from all lines later.
indent = null
doc = (token[1] for token, i in tokens when token[0] is'NEOSTRING').join '#{}'while match = HEREDOC_INDENT.exec doc
attempt = match[1]
indent = attempt if indent isnullor0 < attempt.length < indent.length
indentRegex = /// \n#{indent} ///g if indent
@mergeInterpolationTokens tokens, {delimiter}, (value, i) =>
value = @formatString value, delimiter: quote
value = value.replace indentRegex, '\n'if indentRegex
value = value.replace LEADING_BLANK_LINE, ''if i is0
value = value.replace TRAILING_BLANK_LINE, ''if i is $
value
else
@mergeInterpolationTokens tokens, {delimiter}, (value, i) =>
value = @formatString value, delimiter: quote
value = value.replace SIMPLE_STRING_OMIT, (match, offset) ->if (i is0and offset is0) or
(i is $ and offset + match.length is value.length)
''else' '
value
end
Matches regular expression literals, as well as multiline extended ones.
Lexing regular expressions is difficult to distinguish from division, so we
borrow some basic heuristics from JavaScript and Ruby.
regexToken: ->switchwhen match = REGEX_ILLEGAL.exec @chunk
@error "regular expressions cannot begin with #{match[2]}",
offset: match.index + match[1].length
when match = @matchWithInterpolations HEREGEX, '///'
{tokens, index} = match
when match = REGEX.exec @chunk
[regex, body, closed] = match
@validateEscapes body, isRegex: yes, offsetInChunk: 1
body = @formatRegex body, delimiter: '/'
index = regex.length
[..., prev] = @tokens
if prev
if prev.spaced and prev[0] in CALLABLE
return0ifnot closed or POSSIBLY_DIVISION.test regex
elseif prev[0] in NOT_REGEX
return0
@error 'missing / (unclosed regex)'unless closed
elsereturn0
[flags] = REGEX_FLAGS.exec @chunk[index..]
end = index + flags.length
origin = @makeToken 'REGEX', null, 0, end
switchwhennot VALID_FLAGS.test flags
@error "invalid regular expression flags #{flags}", offset: index, length: flags.length
when regex or tokens.length is1
body ?= @formatHeregex tokens[0][1]
@token 'REGEX', "#{@makeDelimitedLiteral body, delimiter: '/'}#{flags}", 0, end, origin
else
@token 'REGEX_START', '(', 0, 0, origin
@token 'IDENTIFIER', 'RegExp', 0, 0
@token 'CALL_START', '(', 0, 0
@mergeInterpolationTokens tokens, {delimiter: '"', double: yes}, @formatHeregex
if flags
@token ',', ',', index - 1, 0
@token 'STRING', '"' + flags + '"', index - 1, flags.length
@token ')', ')', end - 1, 0
@token 'REGEX_END', ')', end - 1, 0
end
Matches newlines, indents, and outdents, and determines which is which.
If we can detect that the current line is continued onto the next line,
then the newline is suppressed:
elements
.each( ... )
.map( ... )
Keeps track of the level of indentation, because a single outdent token
can close multiple indents, so we need to know how far in we happen to be.
We treat all other single characters as a token. E.g.: ( ) , . !
Multi-character operators are also literal tokens, so that Jison can assign
the proper order of operations. There are some symbols that we tag specially
here. ; and newlines are both treated as a TERMINATOR, we distinguish
parentheses that indicate a method call from regular parentheses, and so on.
literalToken: ->if match = OPERATOR.exec @chunk
[value] = match
@tagParameters() if CODE.test value
else
value = @chunk.charAt 0
tag = value
[..., prev] = @tokens
if prev and value in ['=', COMPOUND_ASSIGN...]
skipToken = falseif value is'='and prev[1] in ['||', '&&'] andnot prev.spaced
prev[0] = 'COMPOUND_ASSIGN'
prev[1] += '='
prev = @tokens[@tokens.length - 2]
skipToken = trueif prev and prev[0] isnt'PROPERTY'
origin = prev.origin ? prev
message = isUnassignable prev[1], origin[1]
@error message, origin[2] if message
return value.length if skipToken
if value is'{'and @seenImport
@importSpecifierList = yeselseif @importSpecifierList and value is'}'
@importSpecifierList = noelseif value is'{'and prev?[0] is'EXPORT'
@exportSpecifierList = yeselseif @exportSpecifierList and value is'}'
@exportSpecifierList = noif value is';'
@seenFor = @seenImport = @seenExport = no
tag = 'TERMINATOR'elseif value is'*'and prev[0] is'EXPORT'
tag = 'EXPORT_ALL'elseif value in MATH then tag = 'MATH'elseif value in COMPARE then tag = 'COMPARE'elseif value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'elseif value in UNARY then tag = 'UNARY'elseif value in UNARY_MATH then tag = 'UNARY_MATH'elseif value in SHIFT then tag = 'SHIFT'elseif value is'?'and prev?.spaced then tag = 'BIN?'elseif prev andnot prev.spaced
if value is'('and prev[0] in CALLABLE
prev[0] = 'FUNC_EXIST'if prev[0] is'?'
tag = 'CALL_START'elseif value is'['and prev[0] in INDEXABLE
tag = 'INDEX_START'switch prev[0]
when'?'then prev[0] = 'INDEX_SOAK'
token = @makeToken tag, value
switch value
when'(', '{', '['then @ends.push {tag: INVERSES[value], origin: token}
when')', '}', ']'then @pair value
@tokens.push token
value.length
A source of ambiguity in our grammar used to be parameter lists in function
definitions versus argument lists in function calls. Walk backwards, tagging
parameters specially in order to make things easier for the parser.
tagParameters: ->returnthisif @tag() isnt')'
stack = []
{tokens} = this
i = tokens.length
tokens[--i][0] = 'PARAM_END'while tok = tokens[--i]
switch tok[0]
when')'
stack.push tok
when'(', 'CALL_START'if stack.length then stack.pop()
elseif tok[0] is'('
tok[0] = 'PARAM_START'returnthiselsereturnthisthis
Match the contents of a delimited token and expand variables and expressions
inside it using Ruby-like notation for substitution of arbitrary
expressions.
"Hello #{name.capitalize()}."
If it encounters an interpolation, this method will recursively create a new
Lexer and tokenize until the { of #{ is balanced with a }.
regex matches the contents of a token (but not delimiter, and not
#{ if interpolations are desired).
delimiter is the delimiter of the token. Examples are ', ", ''',
""" and ///.
This method allows us to have strings within interpolations within strings,
ad infinitum.
Merge the array tokens of the fake token types ‘TOKENS’ and ‘NEOSTRING’
(as returned by matchWithInterpolations) into the token stream. The value
of ‘NEOSTRING’s are converted using fn and turned into strings using
options first.
mergeInterpolationTokens: (tokens, options, fn) ->if tokens.length > 1
lparen = @token 'STRING_START', '(', 0, 0
firstIndex = @tokens.length
for token, i in tokens
[tag, value] = token
switch tag
when'TOKENS'
Optimize out empty strings. We ensure that the tokens stream always
starts with a string token, though, to make sure that the result
really is a string.
if converted.length is0if i is0
firstEmptyStringIndex = @tokens.length
elsecontinue
Use length - 1 for the final offset - we’re supplying the last_line and the last_column,
so if last_column == first_column, then we’re looking at a character of length 1.
Add a token to the results.
offset is the offset into the current @chunk where the token starts.
length is the length of the token in the @chunk, after the offset. If
not specified, the length of value will be used.
when backslash then (if options.double then backslash + backslash else backslash)
when nul then'\\x00'when delimiter then"\\#{delimiter}"when lf then'\\n'when cr then'\\r'when ls then'\\u2028'when ps then'\\u2029'when other then (if options.double then"\\#{other}"else other)
"#{options.delimiter}#{body}#{options.delimiter}"
isUnassignable = (name, displayName = name) ->switchwhen name in [JS_KEYWORDS..., COFFEE_KEYWORDS...]
"keyword '#{displayName}' can't be assigned"when name in STRICT_PROSCRIBED
"'#{displayName}' can't be assigned"when name in RESERVED
"reserved word '#{displayName}' can't be assigned"elsefalse
exports.isUnassignable = isUnassignable
from isn’t a CoffeeScript keyword, but it behaves like one in import and
export statements (handled above) and in the declaration line of a for
loop. Try to detect when from is a variable identifier and when it is this
“sometimes” keyword.
The list of keywords that are reserved by JavaScript, but not used, or are
used by CoffeeScript internally. We throw an error when these are encountered,
to avoid having a JavaScript error at runtime.
Tokens which could legitimately be invoked or indexed. An opening
parentheses or bracket following these tokens will be recorded as the start
of a function invocation or indexing operation.
Tokens that, when immediately preceding a WHEN, indicate that the WHEN
occurs at the start of a line. We disambiguate these from trailing whens to
avoid an ambiguity in the grammar.
nodes.coffee contains all of the node classes for the syntax tree. Most
nodes are created as the result of actions in the grammar,
but some are created by other nodes as a method of code generation. To convert
the syntax tree into a string of JavaScript code, call compile() on the root.
The various nodes defined below all compile to a collection of CodeFragment objects.
A CodeFragments is a block of generated code, and the location in the source file where the code
came from. CodeFragments can be assembled together into working code just by catting together
all the CodeFragments’ code snippets, in order.
The Base is the abstract base class for all nodes in the syntax tree.
Each subclass implements the compileNode method, which performs the
code generation for that node. To compile a node to JavaScript,
call compile on it, which wraps compileNode in some generic extra smarts,
to know when the generated code needs to be wrapped up in a closure.
An options hash is passed and cloned throughout, containing information about
the environment from higher in the tree (such as if a returned value is
being requested by the surrounding function), information about the current
scope, and indentation level.
Common logic for determining whether to wrap this node in a closure before
compiling it, or to compile directly. We need to wrap if this node is a
statement, and it’s not a pureStatement, and we’re not at
the top level of a block (which would be unnecessary), and we haven’t
already been asked to return the result (because statements know how to
return results).
compileToFragments: (o, lvl) ->
o = extend {}, o
o.level = lvl if lvl
node = @unfoldSoak(o) orthis
node.tab = o.indent
if o.level is LEVEL_TOP ornot node.isStatement(o)
node.compileNode o
else
node.compileClosure o
Statements converted into expressions via closure-wrapping share a scope
object with their parent closure, to preserve the expected lexical scope.
compileClosure: (o) ->if jumpNode = @jumps()
jumpNode.error 'cannot use a pure statement in an expression'
o.sharedScope = yes
func = new Code [], Block.wrap [this]
args = []
if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
args = [new ThisLiteral]
if argumentsNode
meth = 'apply'
args.push new IdentifierLiteral 'arguments'else
meth = 'call'
func = new Value func, [new Access new PropertyName meth]
parts = (new Call func, args).compileNode o
if func.isGenerator or func.base?.isGenerator
parts.unshift @makeCode "(yield* "
parts.push @makeCode ")"
parts
If the code generation wishes to use the result of a complex expression
in multiple places, ensure that the expression is only ever evaluated once,
by assigning it to a temporary variable. Pass a level to precompile.
If level is passed, then returns [val, ref], where val is the compiled value, and ref
is the compiled reference. If level is not passed, this returns [val, ref] where
the two values are raw nodes which have not been compiled.
cache: (o, level, isComplex) ->
complex = if isComplex? then isComplex thiselse @isComplex()
if complex
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
sub = new Assign ref, thisif level then [sub.compileToFragments(o, level), [@makeCode(ref.value)]] else [sub, ref]
else
ref = if level then @compileToFragments o, level elsethis
[ref, ref]
cacheToCodeFragments: (cacheValues) ->
[fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])]
Does this node, or any of its children, contain a node of a certain kind?
Recursively traverses down the children nodes and returns the first one
that verifies pred. Otherwise return undefined. contains does not cross
scope boundaries.
contains: (pred) ->
node = undefined
@traverseChildren no, (n) ->if pred n
node = n
returnno
node
toString representation of the node, for inspecting the parse tree.
This is what coffee --nodes prints out.
toString: (idt = '', name = @constructor.name) ->
tree = '\n' + idt + name
tree += '?'if @soak
@eachChild (node) -> tree += node.toString idt + TAB
tree
fragmentsList is an array of arrays of fragments. Each array in fragmentsList will be
concatonated together, with joinStr added in between each, to produce a final flat array
of fragments.
joinFragmentArrays: (fragmentsList, joinStr) ->
answer = []
for fragments,i in fragmentsList
if i then answer.push @makeCode joinStr
answer = answer.concat fragments
answer
The block is the list of expressions that forms the body of an
indented block of code – the implementation of a function, a clause in an
if, switch, or try, and so on…
isEmpty: ->not @expressions.length
isStatement: (o) ->for exp in @expressions when exp.isStatement o
returnyesno
jumps: (o) ->for exp in @expressions
return jumpNode if jumpNode = exp.jumps o
Compile all expressions within the Block body. If we need to
return the result, and it’s an expression, simply return it. If it’s a
statement, ask the statement to do so.
compileNode: (o) ->
@tab = o.indent
top = o.level is LEVEL_TOP
compiledNodes = []
for node, index in @expressions
node = node.unwrapAll()
node = (node.unfoldSoak(o) or node)
if node instanceof Block
This is a nested block. We don’t do anything special here like enclose
it in a new scope; we just compile the statements in this block along with
our own
compiledNodes.push node.compileNode o
elseif top
node.front = true
fragments = node.compileToFragments o
unless node.isStatement o
fragments.unshift @makeCode "#{@tab}"
fragments.push @makeCode ";"
compiledNodes.push fragments
else
compiledNodes.push node.compileToFragments o, LEVEL_LIST
if top
if @spaced
return [].concat @joinFragmentArrays(compiledNodes, '\n\n'), @makeCode("\n")
elsereturn @joinFragmentArrays(compiledNodes, '\n')
if compiledNodes.length
answer = @joinFragmentArrays(compiledNodes, ', ')
else
answer = [@makeCode "void 0"]
if compiledNodes.length > 1and o.level >= LEVEL_LIST then @wrapInBraces answer else answer
If we happen to be the top-level Block, wrap everything in
a safety closure, unless requested not to.
It would be better not to generate them in the first place, but for now,
clean up obvious double-parentheses.
Literal is a base class for static values that can be passed through
directly into JavaScript without translation, such as: strings, numbers,
true, false, null…
A value, variable or literal or parenthesized, indexed or dotted into,
or vanilla.
exports.Value = classValueextendsBase
constructor: (base, props, tag) ->return base ifnot props and base instanceof Value
@base = base
@properties = props or []
@[tag] = trueif tag
returnthis
children: ['base', 'properties']
A reference has base part (this value) and name part.
We cache them separately for compiling complex expressions.
a()[b()] ?= c -> (_base = a())[_name = b()] ? _base[_name] = c
cacheReference: (o) ->
[..., name] = @properties
if @properties.length < 2andnot @base.isComplex() andnot name?.isComplex()
return [this, this] # `a` `a.b`
base = new Value @base, @properties[...-1]
if base.isComplex() # `a().b`
bref = new IdentifierLiteral o.scope.freeVariable 'base'
base = new Value new Parens new Assign bref, base
return [base, bref] unless name # `a()`if name.isComplex() # `a[b()]`
nref = new IdentifierLiteral o.scope.freeVariable 'name'
name = new Index new Assign nref, name.index
nref = new Index nref
[base.add(name), new Value(bref or base.base, [nref or name])]
We compile a value to JavaScript by compiling and joining each property.
Things get much more interesting if the chain of properties has soak
operators ?. interspersed. Then we have to take care not to accidentally
evaluate anything twice when building the soak chain.
compileNode: (o) ->
@base.front = @front
props = @properties
fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS elsenull)
if props.length and SIMPLENUM.test fragmentsToText fragments
fragments.push @makeCode '.'for prop in props
fragments.push (prop.compileToFragments o)...
fragments
unfoldSoak: (o) ->
@unfoldedSoak ?= do =>
if ifn = @base.unfoldSoak o
ifn.body.properties.push @properties...
return ifn
for prop, i in @properties when prop.soak
prop.soak = off
fst = new Value @base, @properties[...i]
snd = new Value @base, @properties[i..]
if fst.isComplex()
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, fst
snd.base = ref
returnnew If new Existence(fst), snd, soak: onno
When setting the location, we sometimes need to update the start location to
account for a newly-discovered new operator to the left of us. This
expands the range on the left, but not the right.
updateLocationDataIfMissing: (locationData) ->if @locationData and @needsUpdatedStartLocation
@locationData.first_line = locationData.first_line
@locationData.first_column = locationData.first_column
base = @variable?.base or @variable
if base.needsUpdatedStartLocation
@variable.locationData.first_line = locationData.first_line
@variable.locationData.first_column = locationData.first_column
base.updateLocationDataIfMissing locationData
delete @needsUpdatedStartLocation
super
Soaked chained invocations unfold into if/else ternary structures.
unfoldSoak: (o) ->if @soak
ifthisinstanceof SuperCall
left = new Literal @superReference o
rite = new Value left
elsereturn ifn if ifn = unfoldSoak o, this, 'variable'
[left, rite] = new Value(@variable).cacheReference o
rite = new Call rite, @args
rite.isNew = @isNew
left = new Literal "typeof #{ left.compile o } === \"function\""returnnew If left, new Value(rite), soak: yes
call = this
list = []
loopif call.variable instanceof Call
list.push call
call = call.variable
continuebreakunless call.variable instanceof Value
list.push call
breakunless (call = call.variable.base) instanceof Call
for call in list.reverse()
if ifn
if call.variable instanceof Call
call.variable = ifn
else
call.variable.base = ifn
ifn = unfoldSoak o, call, 'variable'
ifn
If you call a function with a splat, it’s converted into a JavaScript
.apply() call to allow an array of arguments to be passed.
If it’s a constructor, then things get real tricky. We have to inject an
inner constructor in order to be able to pass the varargs.
splatArgs is an array of CodeFragments to put into the ‘apply’.
compileSplat: (o, splatArgs) ->ifthisinstanceof SuperCall
return [].concat @makeCode("#{ @superReference o }.apply(#{@superThis(o)}, "),
splatArgs, @makeCode(")")
if @isNew
idt = @tab + TAB
return [].concat @makeCode("""
(function(func, args, ctor) {
#{idt}ctor.prototype = func.prototype;
#{idt}var child = new ctor, result = func.apply(child, args);
#{idt}return Object(result) === result ? result : child;
#{@tab}})("""),
(@variable.compileToFragments o, LEVEL_LIST),
@makeCode(", "), splatArgs, @makeCode(", function(){})")
answer = []
base = new Value @variable
if (name = base.properties.pop()) and base.isComplex()
ref = o.scope.freeVariable 'ref'
answer = answer.concat @makeCode("(#{ref} = "),
(base.compileToFragments o, LEVEL_LIST),
@makeCode(")"),
name.compileToFragments(o)
else
fun = base.compileToFragments o, LEVEL_ACCESS
fun = @wrapInBraces fun if SIMPLENUM.test fragmentsToText fun
if name
ref = fragmentsToText fun
fun.push (name.compileToFragments o)...
else
ref = 'null'
answer = answer.concat fun
answer = answer.concat @makeCode(".apply(#{ref}, "), splatArgs, @makeCode(")")
Grab the reference to the superclass’s implementation of the current
method.
superReference: (o) ->
method = o.scope.namedMethod()
if method?.klass
{klass, name, variable} = method
if klass.isComplex()
bref = new IdentifierLiteral o.scope.parent.freeVariable 'base'
base = new Value new Parens new Assign bref, klass
variable.base = base
variable.properties.splice 0, klass.properties.length
if name.isComplex() or (name instanceof Index and name.index.isAssignable())
nref = new IdentifierLiteral o.scope.parent.freeVariable 'name'
name = new Index new Assign nref, name.index
variable.properties.pop()
variable.properties.push name
accesses = [new Access new PropertyName '__super__']
accesses.push new Access new PropertyName 'constructor'if method.static
accesses.push if nref? thennew Index nref else name
(new Value bref ? klass, accesses).compile o
elseif method?.ctor
"#{method.name}.__super__.constructor"else
@error 'cannot call super outside of an instance method.'
Regexes with interpolations are in fact just a variation of a Call (a
RegExp() call to be precise) with a StringWithInterpolations inside.
exports.RegexWithInterpolations = classRegexWithInterpolationsextendsCall
constructor: (args = []) ->super (new Value new IdentifierLiteral 'RegExp'), args, false
A range literal. Ranges can be used to extract portions (slices) of arrays,
to specify a range for comprehensions, or as a value, to be expanded into the
corresponding array of integers at runtime.
exports.Range = classRangeextendsBase
children: ['from', 'to']
constructor: (@from, @to, tag) ->
@exclusive = tag is'exclusive'
@equals = if @exclusive then''else'='
Compiles the range’s source variables – where it starts and where it ends.
But only if they need to be cached to avoid double evaluation.
compileVariables: (o) ->
o = merge o, top: true
isComplex = del o, 'isComplex'
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST, isComplex
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST, isComplex
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST, isComplex if step = del o, 'step'
@fromNum = if @from.isNumber() then Number @fromVar elsenull
@toNum = if @to.isNumber() then Number @toVar elsenull
@stepNum = if step?.isNumber() then Number @stepVar elsenull
An array slice literal. Unlike JavaScript’s Array#slice, the second parameter
specifies the index of the end of the slice, just as the first parameter
is the index of the beginning.
We have to be careful when trying to slice through the end of the array,
9e9 is used because not all implementations respect undefined or 1/0.
9e9 should be safe because 9e9 > 2**32, the max array length.
exports.Obj = classObjextendsBase
constructor: (props, @generated = false) ->
@objects = @properties = props or []
children: ['properties']
compileNode: (o) ->
props = @properties
if @generated
for node in props when node instanceof Value
node.error 'cannot have an implicit value in an implicit object'breakfor prop, dynamicIndex in props when (prop.variable or prop).base instanceof Parens
hasDynamic = dynamicIndex < props.length
idt = o.indent += TAB
lastNoncom = @lastNonComment @properties
answer = []
if hasDynamic
oref = o.scope.freeVariable 'obj'
answer.push @makeCode "(\n#{idt}#{oref} = "
answer.push @makeCode "{#{if props.length is0or dynamicIndex is0then'}'else'\n'}"for prop, i in props
if i is dynamicIndex
answer.push @makeCode "\n#{idt}}"unless i is0
answer.push @makeCode ',\n'
join = if i is props.length - 1or i is dynamicIndex - 1''elseif prop is lastNoncom or prop instanceof Comment
'\n'else',\n'
indent = if prop instanceof Comment then''else idt
indent += TAB if hasDynamic and i < dynamicIndex
if prop instanceof Assign
if prop.context isnt'object'
prop.operatorToken.error "unexpected #{prop.operatorToken.value}"if prop.variable instanceof Value and prop.variable.hasProperties()
prop.variable.error 'invalid object key'if prop instanceof Value and prop.this
prop = new Assign prop.properties[0].name, prop, 'object'if prop notinstanceof Comment
if i < dynamicIndex
if prop notinstanceof Assign
prop = new Assign prop, prop, 'object'elseif prop instanceof Assign
key = prop.variable
value = prop.value
else
[key, value] = prop.base.cache o
key = new PropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign (new Value (new IdentifierLiteral oref), [new Access key]), value
if indent then answer.push @makeCode indent
answer.push prop.compileToFragments(o, LEVEL_TOP)...
if join then answer.push @makeCode join
if hasDynamic
answer.push @makeCode ",\n#{idt}#{oref}\n#{@tab})"else
answer.push @makeCode "\n#{@tab}}"unless props.length is0if @front andnot hasDynamic then @wrapInBraces answer else answer
assigns: (name) ->for prop in @properties when prop.assigns name thenreturnyesno
Figure out the appropriate name for the constructor function of this class.
determineName: ->return @defaultClassVariableName unless @variable
[..., tail] = @variable.properties
node = if tail
tail instanceof Access and tail.name
else
@variable.base
unless node instanceof IdentifierLiteral or node instanceof PropertyName
return @defaultClassVariableName
name = node.value
unless tail
message = isUnassignable name
@variable.error message if message
if name in JS_FORBIDDEN then"_#{name}"else name
Ensure that all functions bound to the instance are proxied in the
constructor.
addBoundFunctions: (o) ->for bvar in @boundFuncs
lhs = (new Value (new ThisLiteral), [new Access bvar]).compile o
@ctor.body.unshift new Literal "#{lhs} = #{utility 'bind', o}(#{lhs}, this)"return
Merge the properties from a top-level object as prototypal properties
on the class.
addProperties: (node, name, o) ->
props = node.base.properties[..]
exprs = while assign = props.shift()
if assign instanceof Assign
base = assign.variable.base
delete assign.context
func = assign.value
if base.value is'constructor'if @ctor
assign.error 'cannot define more than one constructor in a class'if func.bound
assign.error 'cannot define a constructor as a bound function'if func instanceof Code
assign = @ctor = func
else
@externalCtor = o.classScope.freeVariable 'ctor'
assign = new Assign new IdentifierLiteral(@externalCtor), func
elseif assign.variable.this
func.static = yeselse
acc = if base.isComplex() thennew Index base elsenew Access base
assign.variable = new Value(new IdentifierLiteral(name), [(new Access new PropertyName 'prototype'), acc])
if func instanceof Code and func.bound
@boundFuncs.push base
func.bound = no
assign
compact exprs
Walk the body of the class, looking for prototype properties to be converted
and tagging static assignments.
walkBody: (name, o) ->
@traverseChildren false, (child) =>
cont = truereturnfalseif child instanceof Class
if child instanceof Block
for node, i in exps = child.expressions
if node instanceof Assign and node.variable.looksStatic name
node.value.static = yeselseif node instanceof Value and node.isObject(true)
cont = false
exps[i] = @addProperties node, name, o
child.expressions = exps = flatten exps
cont and child notinstanceof Class
use strict (and other directives) must be the first expression statement(s)
of a function body. This method ensures the prologue is correctly positioned
above the constructor.
hoistDirectivePrologue: ->
index = 0
{expressions} = @body
++index while (node = expressions[index]) and node instanceof Comment or
node instanceof Value and node.isString()
@directives = expressions.splice 0, index
Instead of generating the JavaScript string directly, we build up the
equivalent syntax tree and compile that, in pieces. You can see the
constructor, property assignments, and inheritance getting built out below.
compileNode: (o) ->if jumpNode = @body.jumps()
jumpNode.error 'Class bodies cannot contain pure statements'if argumentsNode = @body.contains isLiteralArguments
argumentsNode.error "Class bodies shouldn't reference arguments"
name = @determineName()
lname = new IdentifierLiteral name
func = new Code [], Block.wrap [@body]
args = []
o.classScope = func.makeScope o.scope
@hoistDirectivePrologue()
@setContext name
@walkBody name, o
@ensureConstructor name
@addBoundFunctions o
@body.spaced = yes
@body.expressions.push lname
if @parent
superClass = new IdentifierLiteral o.classScope.freeVariable 'superClass', reserve: no
@body.expressions.unshift new Extends lname, superClass
func.params.push new Param superClass
args.push @parent
@body.expressions.unshift @directives...
klass = new Parens new Call func, args
klass = new Assign @variable, klass, null, { @moduleDeclaration } if @variable
klass.compileToFragments o
Per the spec, symbols can’t be imported multiple times
(e.g. import { foo, foo } from 'lib' is invalid)
if @identifier in o.importedSymbols or o.scope.check(@identifier)
@error "'#{@identifier}' has already been declared"else
o.importedSymbols.push @identifier
super o
exports.ImportDefaultSpecifier = classImportDefaultSpecifierextendsImportSpecifier
exports.ImportNamespaceSpecifier = classImportNamespaceSpecifierextendsImportSpecifier
exports.ExportSpecifier = classExportSpecifierextendsModuleSpecifier
constructor: (local, exported) ->super local, exported, 'export'
Compile an assignment, delegating to compilePatternMatch or
compileSplice if appropriate. Keep track of the name of the base object
we’ve been assigned to, for correct internal references. If the variable
has not been seen yet within the current scope, declare it.
compileNode: (o) ->if isValue = @variable instanceof Value
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
if @value instanceof Code
if @value.static
@value.klass = @variable.base
@value.name = @variable.properties[0]
@value.variable = @variable
elseif @variable.properties?.length >= 2
[properties..., prototype, name] = @variable.properties
if prototype.name?.value is'prototype'
@value.klass = new Value @variable.base, properties
@value.name = name
@value.variable = @variable
unless @context
varBase = @variable.unwrapAll()
unless varBase.isAssignable()
@variable.error "'#{@variable.compile o}' can't be assigned"unless varBase.hasProperties?()
Brief implementation of recursive pattern matching, when assigning array or
object literals to a value. Peeks at their properties to assign inner names.
compilePatternMatch: (o) ->
top = o.level is LEVEL_TOP
{value} = this
{objects} = @variable.base
unless olen = objects.length
code = value.compileToFragments o
returnif o.level >= LEVEL_OP then @wrapInBraces code else code
[obj] = objects
if olen is1and obj instanceof Expansion
obj.error 'Destructuring assignment has no target'
isObject = @variable.isObject()
if top and olen is1and obj notinstanceof Splat
new NumberLiteral 0
acc = idx.unwrap() instanceof PropertyName
value = new Value value
value.properties.push new (if acc then Access else Index) idx
message = isUnassignable obj.unwrap().value
obj.error message if message
value = new Op '?', value, defaultValue if defaultValue
returnnew Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
vvar = value.compileToFragments o, LEVEL_LIST
vvarText = fragmentsToText vvar
assigns = []
expandedIdx = false
new Literal expandedIdx or idx
name = obj.unwrap().value
acc = idx.unwrap() instanceof PropertyName
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
val = new Op '?', val, defaultValue if defaultValue
if name?
message = isUnassignable name
obj.error message if message
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
assigns.push vvar unless top or @subpattern
fragments = @joinFragmentArrays assigns, ', 'if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments
When compiling a conditional assignment, take care to ensure that the
operands are only evaluated once, even though we have to reference them
more than once.
compileConditional: (o) ->
[left, right] = @variable.cacheReference o
Disallow conditional assignment of undefined variables.
ifnot left.properties.length and left.base instanceof Literal and
left.base notinstanceof ThisLiteral andnot o.scope.check left.base.value
@variable.error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been declared before"if"?"in @context
o.isExistentialEquals = truenew If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o
else
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
A function definition. This is the only node that creates a new Scope.
When for the purposes of walking the contents of a function body, the Code
has no children – they’re within the inner scope.
exports.Code = classCodeextendsBase
constructor: (params, body, tag) ->
@params = params or []
@body = body ornew Block
@bound = tag is'boundfunc'
@isGenerator = !!@body.contains (node) ->
(node instanceof Op and node.isYield()) or node instanceof YieldReturn
children: ['params', 'body']
isStatement: -> !!@ctor
jumps: NO
makeScope: (parentScope) ->new Scope parentScope, @body, this
Compilation creates a new scope unless explicitly asked to share with the
outer scope. Handles splat parameters in the parameter list by peeking at
the JavaScript arguments object. If the function is bound with the =>
arrow, generates a wrapper that saves the current value of this through
a closure.
compileNode: (o) ->if @bound and o.scope.method?.bound
@context = o.scope.method.context
if @bound andnot @context
@context = '_this'
wrapper = new Code [new Param new IdentifierLiteral @context], new Block [this]
boundfunc = new Call(wrapper, [new ThisLiteral])
boundfunc.updateLocationDataIfMissing @locationData
return boundfunc.compileNode(o)
o.scope = del(o, 'classScope') or @makeScope o.scope
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
delete o.isExistentialEquals
params = []
exprs = []
for param in @params when param notinstanceof Expansion
o.scope.parameter param.asReference o
for param in @params when param.splat or param instanceof Expansion
for p in @params when p notinstanceof Expansion and p.name.value
o.scope.add p.name.value, 'var', yes
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
new Value new IdentifierLiteral 'arguments'breakfor param in @params
if param.isComplex()
val = ref = param.asReference o
val = new Op '?', ref, param.value if param.value
exprs.push new Assign new Value(param.name), val, '=', param: yeselse
ref = param
if param.value
lit = new Literal ref.name.value + ' == null'
val = new Assign new Value(param.name), param.value, '='
exprs.push new If lit, val
params.push ref unless splats
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length
for p, i in params
params[i] = p.compileToFragments o
o.scope.parameter fragmentsToText params[i]
uniqs = []
@eachParamName (name, node) ->
node.error "multiple parameters named #{name}"if name in uniqs
uniqs.push name
@body.makeReturn() unless wasEmpty or @noReturn
code = 'function'
code += '*'if @isGenerator
code += ' ' + @name if @ctor
code += '('
answer = [@makeCode(code)]
for p, i in params
if i then answer.push @makeCode ", "
answer.push p...
answer.push @makeCode ') {'
answer = answer.concat(@makeCode("\n"), @body.compileWithDeclarations(o), @makeCode("\n#{@tab}")) unless @body.isEmpty()
answer.push @makeCode '}'return [@makeCode(@tab), answer...] if @ctor
if @front or (o.level >= LEVEL_ACCESS) then @wrapInBraces answer else answer
eachParamName: (iterator) ->
param.eachName iterator for param in @params
A parameter in a function definition. Beyond a typical JavaScript parameter,
these parameters can also attach themselves to the context of the function,
as well as be a splat, gathering up a group of parameters into an array.
exports.Param = classParamextendsBase
constructor: (@name, @value, @splat) ->
message = isUnassignable @name.unwrapAll().value
@name.error message if message
if @name instanceof Obj and @name.generated
token = @name.objects[0].operatorToken
token.error "unexpected #{token.value}"
children: ['name', 'value']
compileToFragments: (o) ->
@name.compileToFragments o, LEVEL_LIST
asReference: (o) ->return @reference if @reference
node = @name
if node.this
name = node.properties[0].name.value
name = "_#{name}"if name in JS_FORBIDDEN
node = new IdentifierLiteral o.scope.freeVariable name
elseif node.isComplex()
node = new IdentifierLiteral o.scope.freeVariable 'arg'
node = new Value node
node = new Splat node if @splat
node.updateLocationDataIfMissing @locationData
@reference = node
isComplex: ->
@name.isComplex()
Iterates the name or names of a Param.
In a sense, a destructured parameter represents multiple JS parameters. This
method allows to iterate them all.
The iterator function will be called as iterator(name, node) where
name is the name of the parameter and node is the AST node corresponding
to that name.
Used to skip values inside an array destructuring (pattern matching) or
parameter list.
exports.Expansion = classExpansionextendsBase
isComplex: NO
compileNode: (o) ->
@error 'Expansion must be used inside a destructuring assignment or parameter list'
asReference: (o) ->this
eachName: (iterator) ->
A while loop, the only sort of low-level loop exposed by CoffeeScript. From
it, all other loops can be manufactured. Useful in cases where you need more
flexibility or more speed than a comprehension can provide.
exports.While = classWhileextendsBase
constructor: (condition, options) ->
@condition = if options?.invert then condition.invert() else condition
@guard = options?.guard
children: ['condition', 'guard', 'body']
isStatement: YES
makeReturn: (res) ->if res
superelse
@returns = not @jumps loop: yesthis
addBody: (@body) ->this
jumps: ->
{expressions} = @body
returnnounless expressions.length
for node in expressions
return jumpNode if jumpNode = node.jumps loop: yesno
The main difference from a JavaScript while is that the CoffeeScript
while can be used as a part of a larger expression – while loops may
return an array containing the computed result of each iteration.
compileNode: (o) ->
o.indent += TAB
set = ''
{body} = thisif body.isEmpty()
body = @makeCode ''elseif @returns
body.makeReturn rvar = o.scope.freeVariable 'results'
set = "#{@tab}#{rvar} = [];\n"if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"else
body = Block.wrap [new If @guard, body] if @guard
body = [].concat @makeCode("\n"), (body.compileToFragments o, LEVEL_TOP), @makeCode("\n#{@tab}")
answer = [].concat @makeCode(set + @tab + "while ("), @condition.compileToFragments(o, LEVEL_PAREN),
@makeCode(") {"), body, @makeCode("}")
if @returns
answer.push @makeCode "\n#{@tab}return #{rvar};"
answer
Simple Arithmetic and logical operations. Performs some conversion from
CoffeeScript operations into their JavaScript equivalents.
exports.Op = classOpextendsBase
constructor: (op, first, second, flip ) ->returnnew In first, second if op is'in'if op is'do'return @generateDo first
if op is'new'return first.newInstance() if first instanceof Call andnot first.doandnot first.isNew
first = new Parens first if first instanceof Code and first.bound or first.do
@operator = CONVERSIONS[op] or op
@first = first
@second = second
@flip = !!flip
returnthis
In chains, there’s no need to wrap bare obj literals in parens,
as the chained expression is wrapped.
@first.front = @front unless isChain
if @operator is'delete'and o.scope.check(@first.unwrapAll().value)
@error 'delete operand may not be argument or var'if @operator in ['--', '++']
message = isUnassignable @first.unwrapAll().value
@first.error message if message
return @compileYield o if @isYield()
return @compileUnary o if @isUnary()
return @compileChain o if isChain
switch @operator
when'?'then @compileExistence o
when'**'then @compilePower o
when'//'then @compileFloorDivision o
when'%%'then @compileModulo o
else
lhs = @first.compileToFragments o, LEVEL_OP
rhs = @second.compileToFragments o, LEVEL_OP
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
compileUnary: (o) ->
parts = []
op = @operator
parts.push [@makeCode op]
if op is'!'and @first instanceof Existence
@first.negated = not @first.negated
return @first.compileToFragments o
if o.level >= LEVEL_ACCESS
return (new Parens this).compileToFragments o
plusMinus = op in ['+', '-']
parts.push [@makeCode(' ')] if op in ['new', 'typeof', 'delete'] or
plusMinus and @first instanceof Op and @first.operator is op
if (plusMinus and @first instanceof Op) or (op is'new'and @first.isStatement o)
@first = new Parens @first
parts.push @first.compileToFragments o, LEVEL_OP
parts.reverse() if @flip
@joinFragmentArrays parts, ''
compileYield: (o) ->
parts = []
op = @operator
unless o.scope.parent?
@error 'yield can only occur inside functions'if'expression'in Object.keys(@first) andnot (@first instanceof Throw)
parts.push @first.expression.compileToFragments o, LEVEL_OP if @first.expression?
else
parts.push [@makeCode "("] if o.level >= LEVEL_PAREN
parts.push [@makeCode op]
parts.push [@makeCode " "] if @first.base?.value isnt''
parts.push @first.compileToFragments o, LEVEL_OP
parts.push [@makeCode ")"] if o.level >= LEVEL_PAREN
@joinFragmentArrays parts, ''
compilePower: (o) ->
pow = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'pow']
new Call(pow, [@first, @second]).compileToFragments o
compileFloorDivision: (o) ->
floor = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'floor']
second = if @second.isComplex() thennew Parens @second else @second
div = new Op '/', @first, second
new Call(floor, [div]).compileToFragments o
compileModulo: (o) ->
mod = new Value new Literal utility 'modulo', o
new Call(mod, [@first, @second]).compileToFragments o
toString: (idt) ->super idt, @constructor.name + ' ' + @operator
An extra set of parentheses, specified explicitly in the source. At one time
we tried to clean up the results by detecting and removing redundant
parentheses, but no longer – you can put in as many as you please.
Parentheses are a good way to force any statement to become an expression.
exports.Parens = classParensextendsBase
constructor: (@body) ->
children: ['body']
unwrap : -> @body
isComplex : -> @body.isComplex()
compileNode: (o) ->
expr = @body.unwrap()
if expr instanceof Value and expr.isAtomic()
expr.front = @front
return expr.compileToFragments o
fragments = expr.compileToFragments o, LEVEL_PAREN
bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
(expr instanceof For and expr.returns)) and (o.level < LEVEL_COND or
fragments.length <= 3)
if bare then fragments else @wrapInBraces fragments
This method produces an interpolated string using the new ES2015 syntax,
which is opt-in by using tagged template literals. If this
StringWithInterpolations isn’t inside a tagged template literal,
fall back to the CoffeeScript 1.x output.
(Remove this check in CoffeeScript 2.)
expr = @body.unwrap()
elements = []
expr.traverseChildren no, (node) ->if node instanceof StringLiteral
elements.push node
returnyeselseif node instanceof Parens
elements.push node
returnnoreturnyes
fragments = []
fragments.push @makeCode '`'for element in elements
if element instanceof StringLiteral
value = element.value[1...-1]
CoffeeScript’s replacement for the for loop is our array and object
comprehensions, that compile into for loops here. They also act as an
expression, able to return the result of each filtered iteration.
Unlike Python array comprehensions, they can be multi-line, and you can pass
the current index of the loop as a second parameter. Unlike Ruby blocks,
you can map and filter in a single pass.
exports.For = classForextendsWhile
constructor: (body, source) ->
{@source, @guard, @step, @name, @index} = source
@body = Block.wrap [body]
@own = !!source.own
@object = !!source.object
@from = !!source.from
@index.error 'cannot use index with for-from'if @from and @index
source.ownTag.error "cannot use own with for-#{if @from then'from'else'in'}"if @own andnot @object
[@name, @index] = [@index, @name] if @object
@index.error 'index cannot be a pattern matching expression'if @index instanceof Value andnot @index.isAssignable()
@range = @source instanceof Value and @source.base instanceof Range andnot @source.properties.length andnot @from
@pattern = @name instanceof Value
@index.error 'indexes do not apply to range loops'if @range and @index
@name.error 'cannot pattern match over range loops'if @range and @pattern
@returns = false
children: ['body', 'source', 'guard', 'step']
Welcome to the hairiest method in all of CoffeeScript. Handles the inner
loop, filtering, stepping, and result saving for array, object, and range
comprehensions. Some of the generated code can be shared in common, and
some cannot.
compileNode: (o) ->
body = Block.wrap [@body]
[..., last] = body.expressions
@returns = noif last?.jumps() instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and (@name.compile o, LEVEL_LIST) ifnot @pattern
index = @index and (@index.compile o, LEVEL_LIST)
scope.find(name) if name andnot @pattern
scope.find(index) if index and @index notinstanceof Value
rvar = scope.freeVariable 'results'if @returns
if @from
ivar = scope.freeVariable 'x', single: trueif @pattern
else
ivar = (@object and index) or scope.freeVariable 'i', single: true
kvar = ((@range or @from) and name) or index or ivar
kvarAssign = if kvar isnt ivar then"#{kvar} = "else""if @step andnot @range
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, isComplexOrAssignable
stepNum = Number stepVar if @step.isNumber()
name = ivar if @pattern
varPart = ''
guardPart = ''
defPart = ''
idt1 = @tab + TAB
if @range
forPartFragments = source.compileToFragments merge o,
{index: ivar, name, @step, isComplex: isComplexOrAssignable}
else
svar = @source.compile o, LEVEL_LIST
if (name or @own) and @source.unwrap() notinstanceof IdentifierLiteral
defPart += "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
svar = ref
if name andnot @pattern andnot @from
namePart = "#{name} = #{svar}[#{kvar}]"ifnot @object andnot @from
defPart += "#{@tab}#{step};\n"if step isnt stepVar
down = stepNum < 0
lvar = scope.freeVariable 'len'unless @step and stepNum? and down
declare = "#{kvarAssign}#{ivar} = 0, #{lvar} = #{svar}.length"
declareDown = "#{kvarAssign}#{ivar} = #{svar}.length - 1"
compare = "#{ivar} < #{lvar}"
compareDown = "#{ivar} >= 0"if @step
if stepNum?
if down
compare = compareDown
declare = declareDown
else
compare = "#{stepVar} > 0 ? #{compare} : #{compareDown}"
declare = "(#{stepVar} > 0 ? (#{declare}) : #{declareDown})"
increment = "#{ivar} += #{stepVar}"else
increment = "#{if kvar isnt ivar then"++#{ivar}"else"#{ivar}++"}"
forPartFragments = [@makeCode("#{declare}; #{compare}; #{kvarAssign}#{increment}")]
if @returns
resultPart = "#{@tab}#{rvar} = [];\n"
returnResult = "\n#{@tab}return #{rvar};"
body.makeReturn rvar
if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"else
body = Block.wrap [new If @guard, body] if @guard
if @pattern
body.expressions.unshift new Assign @name, if @from thennew IdentifierLiteral kvar elsenew Literal "#{svar}[#{kvar}]"
defPartFragments = [].concat @makeCode(defPart), @pluckDirectCall(o, body)
varPart = "\n#{idt1}#{namePart};"if namePart
if @object
forPartFragments = [@makeCode("#{kvar} in #{svar}")]
guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;"if @own
elseif @from
forPartFragments = [@makeCode("#{kvar} of #{svar}")]
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
if bodyFragments and bodyFragments.length > 0
bodyFragments = [].concat @makeCode("\n"), bodyFragments, @makeCode("\n")
[].concat defPartFragments, @makeCode("#{resultPart or''}#{@tab}for ("),
forPartFragments, @makeCode(") {#{guardPart}#{varPart}"), bodyFragments,
@makeCode("#{@tab}}#{returnResult or''}")
pluckDirectCall: (o, body) ->
defs = []
for expr, idx in body.expressions
expr = expr.unwrapAll()
continueunless expr instanceof Call
val = expr.variable?.unwrapAll()
continueunless (val instanceof Code) or
(val instanceof Value and
val.base?.unwrapAll() instanceof Code and
val.properties.length is1and
val.properties[0].name?.value in ['call', 'apply'])
fn = val.base?.unwrapAll() or val
ref = new IdentifierLiteral o.scope.freeVariable 'fn'
base = new Value ref
if val.base
[val.base, base] = [base, val]
body.expressions[idx] = new Call base, expr.args
defs = defs.concat @makeCode(@tab), (new Assign(ref, fn).compileToFragments(o, LEVEL_TOP)), @makeCode(';\n')
defs
If/else statements. Acts as an expression by pushing down requested returns
to the last line of each clause.
Single-expression Ifs are compiled into conditional operators if possible,
because ternaries are already proper expressions, and don’t need conversion.
The If only compiles into a statement if either of its bodies needs
to be a statement. Otherwise a conditional operator is safe.
isStatement: (o) ->
o?.level is LEVEL_TOP or
@bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
jumps: (o) -> @body.jumps(o) or @elseBody?.jumps(o)
compileNode: (o) ->if @isStatement o then @compileStatement o else @compileExpression o
makeReturn: (res) ->
@elseBody or= new Block [new Literal 'void 0'] if res
@body and= new Block [@body.makeReturn res]
@elseBody and= new Block [@elseBody.makeReturn res]
this
ensureBlock: (node) ->if node instanceof Block then node elsenew Block [node]
Parse the list of arguments, populating an options object with all of the
specified options, and return it. Options after the first non-option
argument are treated as arguments. options.arguments will be an array
containing the remaining arguments. This is a simpler API than many option
parsers that allow you to attach callback actions for every flag. Instead,
you’re responsible for interpreting the options object.
parse: (args) ->
options = arguments: []
skippingArgument = no
originalArgs = args
args = normalizeArguments args
for arg, i in args
if skippingArgument
skippingArgument = nocontinueif arg is'--'
pos = originalArgs.indexOf '--'
options.arguments = options.arguments.concat originalArgs[(pos + 1)..]
break
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
the CS option parser is a little odd; options after the first
non-option argument are treated as non-option arguments themselves
seenNonOptionArg = options.arguments.length > 0unless seenNonOptionArg
matchedRule = nofor rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = trueif rule.hasArgument
skippingArgument = yes
value = args[i + 1]
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yesbreakthrownew Error "unrecognized option: #{arg}"if isOption andnot matchedRule
if seenNonOptionArg ornot isOption
options.arguments.push arg
options
Normalize arguments by expanding merged flags into multiple flags. This allows
you to have -wl be the same as --watch --lint.
normalizeArguments = (args) ->
args = args[..]
result = []
for arg in args
if match = arg.match MULTI_FLAG
result.push '-' + l for l in match[1].split ''else
result.push arg
result
================================================
FILE: docs/v1/annotated-source/public/stylesheets/normalize.css
================================================
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
================================================
FILE: docs/v1/annotated-source/register.html
================================================
register.coffee
The CoffeeScript language has a good deal of optional syntax, implicit syntax,
and shorthand syntax. This can greatly complicate a grammar and bloat
the resulting parse table. Instead of making the parser handle it all, we take
a series of passes over the token stream, using this Rewriter to convert
shorthand into the unambiguous long form, add implicit indentation and
parentheses, and generally clean things up.
Rewrite the token stream in multiple passes, one logical filter at
a time. This could certainly be changed into a single pass through the
stream, with a big ol’ efficient switch, but it’s much nicer to work with
like this. The order of these passes matters – indentation must be
corrected before implicit parentheses can be wrapped around blocks of code.
Rewrite the token stream, looking one token ahead and behind.
Allow the return value of the block to tell us how many tokens to move
forwards (or backwards) in the stream, to make sure we don’t miss anything
as tokens are inserted and removed, and the stream changes length under
our feet.
scanTokens: (block) ->
{tokens} = this
i = 0
i += block.call this, token, i, tokens while token = tokens[i]
true
detectEnd: (i, condition, action) ->
{tokens} = this
levels = 0while token = tokens[i]
return action.call this, token, i if levels is0and condition.call this, token, i
return action.call this, token, i - 1ifnot token or levels < 0if token[0] in EXPRESSION_START
levels += 1elseif token[0] in EXPRESSION_END
levels -= 1
i += 1
i - 1
The lexer has tagged the opening parenthesis of a method call. Match it with
its paired close. We have the mis-nested outdent case included here for
calls that close on the same line, just before their outdent.
closeOpenCalls: ->condition = (token, i) ->
token[0] in [')', 'CALL_END'] or
token[0] is'OUTDENT'and @tag(i - 1) is')'action = (token, i) ->
@tokens[if token[0] is'OUTDENT'then i - 1else i][0] = 'CALL_END'
@scanTokens (token, i) ->
@detectEnd i + 1, condition, action if token[0] is'CALL_START'1
Match tags in token stream starting at i with pattern, skipping ‘HERECOMMENT’s.
pattern may consist of strings (equality), an array of strings (one of)
or null (wildcard). Returns the index of the match or -1 if no match.
Returns yes if current line of tokens contain an element of tags on same
expression level. Stop searching at LINEBREAKS or explicit start of
containing balanced expression.
findTagsBackwards: (i, tags) ->
backStack = []
while i >= 0and (backStack.length or
@tag(i) notin tags and
(@tag(i) notin EXPRESSION_START or @tokens[i].generated) and
@tag(i) notin LINEBREAKS)
backStack.push @tag(i) if @tag(i) in EXPRESSION_END
backStack.pop() if @tag(i) in EXPRESSION_START and backStack.length
i -= 1
@tag(i) in tags
Don’t end an implicit call on next indent if any of these are in an argument
if inImplicitCall() and tag in ['IF', 'TRY', 'FINALLY', 'CATCH',
'CLASS', 'SWITCH']
stack.push ['CONTROL', i, ours: yes]
return forward(1)
if tag is'INDENT'and inImplicit()
Close all implicit expressions inside of explicitly closed expressions.
if tag in EXPRESSION_END
while inImplicit()
if inImplicitCall()
endImplicitCall()
elseif inImplicitObject()
endImplicitObject()
else
stack.pop()
start = stack.pop()
Recognize standard implicit calls like
f a, f() b, f? c, h[0] d etc.
if (tag in IMPLICIT_FUNC and token.spaced or
tag is'?'and i > 0andnot tokens[i - 1].spaced) and
(nextTag in IMPLICIT_CALL or
nextTag in IMPLICIT_UNSPACED_CALL andnot tokens[i + 1]?.spaced andnot tokens[i + 1]?.newLine)
tag = token[0] = 'FUNC_EXIST'if tag is'?'
startImplicitCall i + 1return forward(2)
if tag in LINEBREAKS
for stackItem in stack by-1breakunless isImplicit stackItem
stackItem[2].sameLine = noif isImplicitObject stackItem
newLine = prevTag is'OUTDENT'or prevToken.newLine
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
while inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
Close implicit objects when at end of line, line didn’t end with a comma
and the implicit object didn’t start the line or the next line doesn’t look like
the continuation of an object.
elseif inImplicitObject() and tag is'TERMINATOR'and prevTag isnt','andnot (startsLine and @looksObjectish(i + 1))
return forward 1if nextTag is'HERECOMMENT'
endImplicitObject()
elsebreak
Close implicit object if comma is the last character
and what comes after doesn’t look like it belongs.
This is used for trailing commas and calls, like:
x =
a: b,
c: d,
e = 2
and
f a, b: c, d: e, f, g: h: i, j
if tag is','andnot @looksObjectish(i + 1) and inImplicitObject() andnot @insideForDeclaration and
(nextTag isnt'TERMINATOR'ornot @looksObjectish(i + 2))
OUTDENT tokens should always be positioned at the last character of the
previous token, so that AST nodes ending in an OUTDENT token end up with a
location corresponding to the last “real” token under the node.
fixOutdentLocationData: ->
@scanTokens (token, i, tokens) ->
return1unless token[0] is'OUTDENT'or
(token.generated and token[0] is'CALL_END') or
(token.generated and token[0] is'}')
prevLocationData = tokens[i - 1][2]
token[2] =
first_line: prevLocationData.last_line
first_column: prevLocationData.last_column
last_line: prevLocationData.last_line
last_column: prevLocationData.last_column
return1
Because our grammar is LALR(1), it can’t handle some single-line
expressions that lack ending delimiters. The Rewriter adds the implicit
blocks, so it doesn’t need to. To keep the grammar clean and tidy, trailing
newlines within expressions are removed and the indentation tokens of empty
blocks are added.
normalizeLines: ->
starter = indent = outdent = nullcondition = (token, i) ->
token[1] isnt';'and token[0] in SINGLE_CLOSERS andnot (token[0] is'TERMINATOR'and @tag(i + 1) in EXPRESSION_CLOSE) andnot (token[0] is'ELSE'and starter isnt'THEN') andnot (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
token[0] in CALL_CLOSERS and
(@tokens[i - 1].newLine or @tokens[i - 1][0] is'OUTDENT')
action = (token, i) ->
@tokens.splice (if @tag(i - 1) is','then i - 1else i), 0, outdent
@scanTokens (token, i, tokens) ->
[tag] = token
if tag is'TERMINATOR'if @tag(i + 1) is'ELSE'and @tag(i - 1) isnt'OUTDENT'
tokens.splice i, 1, @indentation()...
return1if @tag(i + 1) in EXPRESSION_CLOSE
tokens.splice i, 1return0if tag is'CATCH'for j in [1..2] when @tag(i + j) in ['OUTDENT', 'TERMINATOR', 'FINALLY']
tokens.splice i + j, 0, @indentation()...
return2 + j
if tag in SINGLE_LINERS and @tag(i + 1) isnt'INDENT'andnot (tag is'ELSE'and @tag(i + 1) is'IF')
starter = tag
[indent, outdent] = @indentation tokens[i]
indent.fromThen = trueif starter is'THEN'
tokens.splice i + 1, 0, indent
@detectEnd i + 2, condition, action
tokens.splice i, 1if tag is'THEN'return1return1
The Scope class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with var, and which are shared
with external scopes.
Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the Block node it belongs to, which is
where it should declare its variables, a reference to the function that
it belongs to, and a list of variables referenced in the source code
and therefore should be avoided when generating variables.
When super is called, we need to find the name of the current method we’re
in, so that we know how to invoke the same method of the parent class. This
can get complicated if super is being called from an inner function.
namedMethod will walk up the scope tree until it either finds the first
function object that has a name filled in, or bottoms out.
namedMethod: ->return @method if @method?.name or !@parent
@parent.namedMethod()
Source maps allow JavaScript runtimes to match running JavaScript back to
the original source code that corresponds to it. This can be minified
JavaScript, but in our case, we’re concerned with mapping pretty-printed
JavaScript back to CoffeeScript.
In order to produce maps, we must keep track of positions (line number, column number)
that originated every node in the syntax tree, and be able to generate a
map file
— which is a compact, VLQ-encoded representation of the JSON serialization
of this information — to write out alongside the generated JavaScript.
A LineMap object keeps track of information about original line and column
positions for a single line of output JavaScript code.
SourceMaps are implemented in terms of LineMaps.
classLineMap
constructor: (@line) ->
@columns = []
add: (column, [sourceLine, sourceColumn], options={}) ->returnif @columns[column] and options.noReplace
@columns[column] = {line: @line, column, sourceLine, sourceColumn}
sourceLocation: (column) ->
column-- until (mapping = @columns[column]) or (column <= 0)
mapping and [mapping.sourceLine, mapping.sourceColumn]
Maps locations in a single generated JavaScript file back to locations in
the original CoffeeScript source file.
This is intentionally agnostic towards how a source map might be represented on
disk. Once the compiler is ready to produce a “v3”-style source map, we can walk
through the arrays of line and column buffer to produce it.
Adds a mapping to this SourceMap. sourceLocation and generatedLocation
are both [line, column] arrays. If options.noReplace is true, then if there
is already a mapping for the specified line and column, this will have no
effect.
Builds up a V3 source map, returning the generated JSON as a string.
options.sourceRoot may be used to specify the sourceRoot written to the source
map. Also, options.sourceFiles and options.generatedFile may be passed to
set “sources” and “file”, respectively.
generate: (options = {}, code = null) ->
writingline = 0
lastColumn = 0
lastSourceLine = 0
lastSourceColumn = 0
needComma = no
buffer = ""for lineMap, lineNumber in @lines when lineMap
for mapping in lineMap.columns when mapping
while writingline < mapping.line
lastColumn = 0
needComma = no
buffer += ";"
writingline++
Note that SourceMap VLQ encoding is “backwards”. MIDI-style VLQ encoding puts
the most-significant-bit (MSB) from the original value into the MSB of the VLQ
encoded value (see Wikipedia).
SourceMap VLQ does things the other way around, with the least significat four
bits of the original value encoded into the first byte of the VLQ encoded value.
CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.
The golden rule of CoffeeScript is: “It’s just JavaScript”. The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly from CoffeeScript (and vice-versa). The compiled output is readable, pretty-printed, and tends to run as fast or faster than the equivalent handwritten JavaScript.
CoffeeScript on the left, compiled JavaScript output on the right.
# Assignment:
number = 42
opposite = true# Conditions:
number = -42if opposite
# Functions:square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:race = (winner, runners...) ->print winner, runners
# Existence:
alert "I knew it!"if elvis?
# Array comprehensions:
cubes = (math.cube num for num in list)
var cubes, list, math, num, number, opposite, race, square,
slice = [].slice;
number = 42;
opposite = true;
if (opposite) {
number = -42;
}
square = function(x) {
return x * x;
};
list = [1, 2, 3, 4, 5];
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
race = function() {
var runners, winner;
winner = arguments[0], runners = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return print(winner, runners);
};
if (typeof elvis !== "undefined" && elvis !== null) {
alert("I knew it!");
}
cubes = (function() {
var i, len, results;
results = [];
for (i = 0, len = list.length; i < len; i++) {
num = list[i];
results.push(math.cube(num));
}
return results;
})();
run: cubes
Installation
The command-line version of coffee is available as a Node.js utility. The core compiler however, does not depend on Node, and can be run in any JavaScript environment, or in the browser (see Try CoffeeScript).
To install, first make sure you have a working copy of the latest stable version of Node.js. You can then install CoffeeScript globally with npm:
npm install --global coffeescript
This will make the coffee and cake commands available globally.
When you need CoffeeScript as a dependency of a project, within that project’s folder you can install it locally:
npm install --save coffeescript
The coffee and cake commands will first look in the current folder to see if CoffeeScript is installed locally, and use that version if so. This allows different versions of CoffeeScript to be installed globally and locally.
Usage
Once installed, you should have access to the coffee command, which can execute scripts, compile .coffee files into .js, and provide an interactive REPL. The coffee command takes the following options:
Option
Description
-c, --compile
Compile a .coffee script into a .js JavaScript file of the same name.
-m, --map
Generate source maps alongside the compiled JavaScript files. Adds sourceMappingURL directives to the JavaScript as well.
-M, --inline-map
Just like --map, but include the source map directly in the compiled JavaScript files, rather than in a separate file.
-i, --interactive
Launch an interactive CoffeeScript session to try short snippets. Identical to calling coffee with no arguments.
-o, --output [DIR]
Write out all compiled JavaScript files into the specified directory. Use in conjunction with --compile or --watch.
-w, --watch
Watch files for changes, rerunning the specified command when any file is updated.
-p, --print
Instead of writing out the JavaScript as a file, print it directly to stdout.
-s, --stdio
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT. Good for use with processes written in other languages. An example: cat src/cake.coffee | coffee -sc
-l, --literate
Parses the code as Literate CoffeeScript. You only need to specify this when passing in code directly over stdio, or using some sort of extension-less file name.
-e, --eval
Compile and print a little snippet of CoffeeScript directly from the command line. For example: coffee -e "console.log num for num in [10..1]"
-r, --require [MODULE]
require() the given module before starting the REPL or evaluating the code given with the --eval flag.
Instead of parsing the CoffeeScript, just lex it, and print out the token stream. Used for debugging the compiler.
-n, --nodes
Instead of compiling the CoffeeScript, just lex and parse it, and print out the parse tree. Used for debugging the compiler.
--nodejs
The node executable has some useful options you can set, such as --debug, --debug-brk, --max-stack-size, and --expose-gc. Use this flag to forward options directly to Node.js. To pass multiple flags, use --nodejs multiple times.
--no-header
Suppress the “Generated by CoffeeScript” header.
Examples:
Compile a directory tree of .coffee files in src into a parallel tree of .js files in lib: 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: coffee -o lib/ -cw src/
Start the CoffeeScript REPL (Ctrl-D to exit, Ctrl-Vfor multi-line): coffee
Literate CoffeeScript
Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a .litcoffee extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdown’s way of indicating source code) as code, and ignore the rest as comments.
This reference is structured so that it can be read from top to bottom, if you like. Later sections use ideas and syntax previously introduced. Familiarity with JavaScript is assumed. In all of the following examples, the source CoffeeScript is provided on the left, and the direct compilation into JavaScript is on the right.
Many of the examples can be run (where it makes sense) by pressing the run button on the right, and can be loaded into the “Try CoffeeScript” console by pressing the load button on the left.
First, the basics: CoffeeScript uses significant whitespace to delimit blocks of code. You don’t need to use semicolons ; to terminate expressions, ending the line will do just as well (although semicolons can still be used to fit multiple expressions onto a single line). Instead of using curly braces { } to surround blocks of code in functions, if-statements, switch, and try/catch, use indentation.
You don’t need to use parentheses to invoke a function if you’re passing arguments. The implicit call wraps forward to the end of the line or block expression. console.log sys.inspect object → console.log(sys.inspect(object));
Functions
Functions are defined by an optional list of parameters in parentheses, an arrow, and the function body. The empty function looks like this: ->
Functions may also have default values for arguments, which will be used if the incoming argument is missing (null or undefined).
fill = (container, liquid = "coffee") ->"Filling the #{container} with #{liquid}..."
var fill;
fill = function(container, liquid) {
if (liquid == null) {
liquid = "coffee";
}
return"Filling the " + container + " with " + liquid + "...";
};
load
run: fill("cup")
Objects and Arrays
The CoffeeScript literals for objects and arrays look very similar to their JavaScript cousins. When each property is listed on its own line, the commas are optional. Objects may be created using indentation instead of explicit braces, similar to YAML.
In JavaScript, you can’t use reserved words, like class, as properties of an object, without quoting them as strings. CoffeeScript notices reserved words used as keys in objects and quotes them for you, so you don’t have to worry about it (say, when using jQuery).
CoffeeScript has a shortcut for creating objects when you want the key to be set with a variable of the same name.
name = "Michelangelo"
mask = "orange"
weapon = "nunchuks"
turtle = {name, mask, weapon}
output = "#{turtle.name} wears an #{turtle.mask} mask. Watch out for his #{turtle.weapon}!"
var mask, name, output, turtle, weapon;
name = "Michelangelo";
mask = "orange";
weapon = "nunchuks";
turtle = {
name: name,
mask: mask,
weapon: weapon
};
output = turtle.name + " wears an " + turtle.mask + " mask. Watch out for his " + turtle.weapon + "!";
load
Lexical Scoping and Variable Safety
The CoffeeScript compiler takes care to make sure that all of your variables are properly declared within lexical scope — you never need to write var yourself.
Notice how all of the variable declarations have been pushed up to the top of the closest scope, the first time they appear. outer is not redeclared within the inner function, because it’s already in scope; inner within the function, on the other hand, should not be able to change the value of the external variable of the same name, and therefore has a declaration of its own.
This behavior is effectively identical to Ruby’s scope for local variables. Because you don’t have direct access to the var keyword, it’s impossible to shadow an outer variable on purpose, you may only refer to it. So be careful that you’re not reusing the name of an external variable accidentally, if you’re writing a deeply nested function.
Although suppressed within this documentation for clarity, all CoffeeScript output is wrapped in an anonymous function: (function(){ … })(); This safety wrapper, combined with the automatic generation of the var keyword, make it exceedingly difficult to pollute the global namespace by accident.
If you’d like to create top-level variables for other scripts to use, attach them as properties on window; attach them as properties on the exports object in CommonJS; or use an export statement. If you’re targeting both CommonJS and the browser, the existential operator (covered below), gives you a reliable way to figure out where to add them: exports ? this
If, Else, Unless, and Conditional Assignment
If/else statements can be written without the use of parentheses and curly brackets. As with functions and other block expressions, multi-line conditionals are delimited by indentation. There’s also a handy postfix form, with the if or unless at the end.
CoffeeScript can compile if statements into JavaScript expressions, using the ternary operator when possible, and closure wrapping otherwise. There is no explicit ternary statement in CoffeeScript — you simply use a regular if statement on a single line.
mood = greatlyImproved if singing
if happy and knowsIt
clapsHands()
chaChaCha()
else
showIt()
date = if friday then sue else jill
var date, mood;
if (singing) {
mood = greatlyImproved;
}
if (happy && knowsIt) {
clapsHands();
chaChaCha();
} else {
showIt();
}
date = friday ? sue : jill;
load
Splats…
The JavaScript arguments object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats ..., both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable.
Most of the loops you’ll write in CoffeeScript will be comprehensions over arrays, objects, and ranges. Comprehensions replace (and compile into) for loops, with optional guard clauses and the value of the current array index. Unlike for loops, array comprehensions are expressions, and can be returned and assigned.
# Eat lunch.
eat food for food in ['toast', 'cheese', 'wine']
# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu i + 1, dish for dish, i in courses
# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt'chocolate'
var courses, dish, food, foods, i, j, k, l, len, len1, len2, ref;
ref = ['toast', 'cheese', 'wine'];
for (j = 0, len = ref.length; j < len; j++) {
food = ref[j];
eat(food);
}
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];
for (i = k = 0, len1 = courses.length; k < len1; i = ++k) {
dish = courses[i];
menu(i + 1, dish);
}
foods = ['broccoli', 'spinach', 'chocolate'];
for (l = 0, len2 = foods.length; l < len2; l++) {
food = foods[l];
if (food !== 'chocolate') {
eat(food);
}
}
load
Comprehensions should be able to handle most places where you otherwise would use a loop, each/forEach, map, or select/filter, for example: shortNames = (name for name in list when name.length < 5)
If you know the start and end of your loop, or would like to step through in fixed-size increments, you can use a range to specify the start and end of your comprehension.
countdown = (num for num in [10..1])
var countdown, num;
countdown = (function() {
var i, results;
results = [];
for (num = i = 10; i >= 1; num = --i) {
results.push(num);
}
return results;
})();
load
run: countdown
Note how because we are assigning the value of the comprehensions to a variable in the example above, CoffeeScript is collecting the result of each iteration into an array. Sometimes functions end with loops that are intended to run only for their side-effects. Be careful that you’re not accidentally returning the results of the comprehension in these cases, by adding a meaningful return value — like true — or null, to the bottom of your function.
To step through a range comprehension in fixed-size chunks, use by, for example:
evens = (x for x in [0..10] by 2)
If you don’t need the current iteration value you may omit it:
browser.closeCurrentTab() for [0...count]
Comprehensions can also be used to iterate over the keys and values in an object. Use of to signal comprehension over the properties of an object instead of the values in an array.
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
var age, ages, child, yearsOld;
yearsOld = {
max: 10,
ida: 9,
tim: 11
};
ages = (function() {
var results;
results = [];
for (child in yearsOld) {
age = yearsOld[child];
results.push(child + " is " + age);
}
return results;
})();
load
run: ages.join(", ")
If you would like to iterate over just the keys that are defined on the object itself, by adding a hasOwnProperty check to avoid properties that may be inherited from the prototype, use for own key, value of object.
The only low-level loop that CoffeeScript provides is the while loop. The main difference from JavaScript is that the while loop can be used as an expression, returning an array containing the result of each iteration through the loop.
# Econ 101ifthis.studyingEconomics
buy() while supply > demand
sell() until supply > demand
# Nursery Rhyme
num = 6
lyrics = while num -= 1"#{num} little monkeys, jumping on the bed.
One fell out and bumped his head."
var lyrics, num;
if (this.studyingEconomics) {
while (supply > demand) {
buy();
}
while (!(supply > demand)) {
sell();
}
}
num = 6;
lyrics = (function() {
var results;
results = [];
while (num -= 1) {
results.push(num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
}
return results;
})();
load
run: lyrics.join("\n")
For readability, the until keyword is equivalent to while not, and the loop keyword is equivalent to while true.
When using a JavaScript loop to generate functions, it’s common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don’t just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.
for filename in list
do (filename) ->
fs.readFile filename, (err, contents) ->
compile filename, contents.toString()
var filename, fn, i, len;
fn = function(filename) {
return fs.readFile(filename, function(err, contents) {
return compile(filename, contents.toString());
});
};
for (i = 0, len = list.length; i < len; i++) {
filename = list[i];
fn(filename);
}
load
Array Slicing and Splicing with Ranges
Ranges can also be used to extract slices of arrays. With two dots (3..6), the range is inclusive (3, 4, 5, 6); with three dots (3...6), the range excludes the end (3, 4, 5). Slices indices have useful defaults. An omitted first index defaults to zero and an omitted second index defaults to the size of the array.
Note that JavaScript strings are immutable, and can’t be spliced.
Everything is an Expression (at least, as much as possible)
You might have noticed how even though we don’t add return statements to CoffeeScript functions, they nonetheless return their final value. The CoffeeScript compiler tries to make sure that all statements in the language can be used as expressions. Watch how the return gets pushed down into each possible branch of execution in the function below.
Even though functions will always return their final value, it’s both possible and encouraged to return early from a function body writing out the explicit return (return value), when you know that you’re done.
Because variable declarations occur at the top of scope, assignment can be used within expressions, even for variables that haven’t been seen before:
six = (one = 1) + (two = 2) + (three = 3)
var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);
load
run: six
Things that would otherwise be statements in JavaScript, when used as part of an expression in CoffeeScript, are converted into expressions by wrapping them in a closure. This lets you do useful things, like assign the result of a comprehension to a variable:
# The first ten global properties.
globals = (name for name ofwindow)[0...10]
var globals, name;
globals = ((function() {
var results;
results = [];
for (name inwindow) {
results.push(name);
}
return results;
})()).slice(0, 10);
load
run: globals
As well as silly things, like passing a try/catch statement directly into a function call:
alert(
try
nonexistent / undefinedcatch error
"And the error is ... #{error}"
)
var error;
alert((function() {
try {
return nonexistent / void0;
} catch (error1) {
error = error1;
return"And the error is ... " + error;
}
})());
load
run
There are a handful of statements in JavaScript that can’t be meaningfully converted into expressions, namely break, continue, and return. If you make use of them within a block of code, CoffeeScript won’t try to perform the conversion.
Operators and Aliases
Because the == operator frequently causes undesirable coercion, is intransitive, and has a different meaning than in other languages, CoffeeScript compiles == into ===, and != into !==. In addition, is compiles into ===, and isnt into !==.
You can use not as an alias for !.
For logic, and compiles to &&, and or into ||.
Instead of a newline or semicolon, then can be used to separate conditions from expressions, in while, if/else, and switch/when statements.
As in YAML, on and yes are the same as boolean true, while off and no are boolean false.
unless can be used as the inverse of if.
As a shortcut for this.property, you can use @property.
You can use in to test for array presence, and of to test for JavaScript object-key presence.
To simplify math expressions, ** can be used for exponentiation and // performs integer division. % works just like in JavaScript, while %% provides “dividend dependent modulo”:
-7 % 5 == -2# The remainder of 7 / 5-7 %% 5 == 3# n %% 5 is always between 0 and 4
tabs.selectTabAtIndex((tabs.currentIndex - count) %% tabs.length)
var modulo = function(a, b) { return (+a % (b = +b) + b) % b; };
-7 % 5 === -2;
modulo(-7, 5) === 3;
tabs.selectTabAtIndex(modulo(tabs.currentIndex - count, tabs.length));
load
All together now:
CoffeeScript
JavaScript
is
===
isnt
!==
not
!
and
&&
or
||
true, yes, on
true
false, no, off
false
@, this
this
of
in
in
no JS equivalent
a ** b
Math.pow(a, b)
a // b
Math.floor(a / b)
a %% b
(a % b + b) % b
launch() if ignition ison
volume = 10if band isnt SpinalTap
letTheWildRumpusBegin() unless answer isnoif car.speed < limit then accelerate()
winner = yesif pick in [47, 92, 13]
print inspect "My name is #{@name}"
var volume, winner;
if (ignition === true) {
launch();
}
if (band !== SpinalTap) {
volume = 10;
}
if (answer !== false) {
letTheWildRumpusBegin();
}
if (car.speed < limit) {
accelerate();
}
if (pick === 47 || pick === 92 || pick === 13) {
winner = true;
}
print(inspect("My name is " + this.name));
load
The Existential Operator
It’s a little difficult to check for the existence of a variable in JavaScript. if (variable) … comes close, but fails for zero, the empty string, and false. CoffeeScript’s existential operator ? returns true unless a variable is null or undefined, which makes it analogous to Ruby’s nil?
It can also be used for safer conditional assignment than ||= provides, for cases where you may be handling numbers or strings.
var footprints, solipsism, speed;
if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) {
solipsism = true;
}
speed = 0;
if (speed == null) {
speed = 15;
}
footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";
load
run: footprints
The accessor variant of the existential operator ?. can be used to soak up null references in a chain of properties. Use it instead of the dot accessor . in cases where the base value may be null or undefined. If all of the properties exist then you’ll get the expected result, if the chain is broken, undefined is returned instead of the TypeError that would be raised otherwise.
JavaScript’s prototypal inheritance has always been a bit of a brain-bender, with a whole family tree of libraries that provide a cleaner syntax for classical inheritance on top of JavaScript’s prototypes: Base2, Prototype.js, JS.Class, etc. The libraries provide syntactic sugar, but the built-in inheritance would be completely usable if it weren’t for a couple of small exceptions: it’s awkward to call super (the prototype object’s implementation of the current function), and it’s awkward to correctly set the prototype chain.
Instead of repetitively attaching functions to a prototype, CoffeeScript provides a basic class structure that allows you to name your class, set the superclass, assign prototypal properties, and define the constructor, in a single assignable expression.
Constructor functions are named, to better support helpful stack traces. In the first class in the example below, this.constructor.name is "Animal".
classAnimal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."classSnakeextendsAnimal
move: ->
alert "Slithering..."super5classHorseextendsAnimal
move: ->
alert "Galloping..."super45
sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"
sam.move()
tom.move()
If structuring your prototypes classically isn’t your cup of tea, CoffeeScript provides a couple of lower-level conveniences. The extends operator helps with proper prototype setup, and can be used to create an inheritance chain between any pair of constructor functions; :: gives you quick access to an object’s prototype; and super() is converted into a call against the immediate ancestor’s method of the same name.
Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. Because in the context of a class definition, this is the class object itself (the constructor function), you can assign static properties by using
@property: value, and call functions defined in parent classes: @attr 'title', type: 'text'
Destructuring Assignment
Just like JavaScript (since ES2015), CoffeeScript has destructuring assignment syntax. When you assign an array or object literal to a value, CoffeeScript breaks up and matches both sides against each other, assigning the values on the right to the variables on the left. In the simplest case, it can be used for parallel assignment:
var city, futurists, name, ref, ref1, sculptor, street;
futurists = {
sculptor: "Umberto Boccioni",
painter: "Vladimir Burliuk",
poet: {
name: "F.T. Marinetti",
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
}
};
sculptor = futurists.sculptor;
ref = futurists.poet, name = ref.name, (ref1 = ref.address, street = ref1[0], city = ref1[1]);
load
run: name + "-" + street
Destructuring assignment can even be combined with splats.
tag = "<impossible>"
[open, contents..., close] = tag.split("")
var close, contents, i, open, ref, tag,
slice = [].slice;
tag = "<impossible>";
ref = tag.split(""), open = ref[0], contents = 3 <= ref.length ? slice.call(ref, 1, i = ref.length - 1) : (i = 1, []), close = ref[i++];
load
run: contents.join("")
Expansion can be used to retrieve elements from the end of an array without having to assign the rest of its values. It works in function parameter lists as well.
text = "Every literary critic believes he will
outwit history and have the last word"
[first, ..., last] = text.split " "
var first, last, ref, text;
text = "Every literary critic believes he will outwit history and have the last word";
ref = text.split(" "), first = ref[0], last = ref[ref.length - 1];
load
run: first + " " + last
Destructuring assignment is also useful when combined with class constructors to assign properties to your instance from an options object passed to the constructor.
classPerson
constructor: (options) ->
{@name, @age, @height = 'average'} = options
tim = new Person name: 'Tim', age: 4
var Person, tim;
Person = (function() {
functionPerson(options) {
var ref;
this.name = options.name, this.age = options.age, this.height = (ref = options.height) != null ? ref : 'average';
}
return Person;
})();
tim = new Person({
name: 'Tim',
age: 4
});
load
run: tim.age + " " + tim.height
The above example also demonstrates that if properties are missing in the destructured object or array, you can, just like in JavaScript, provide defaults. The difference with JavaScript is that CoffeeScript, as always, treats both null and undefined the same.
Bound Functions, Generator Functions
In JavaScript, the this keyword is dynamically scoped to mean the object that the current function is attached to. If you pass a function as a callback or attach it to a different object, the original value of this will be lost. If you’re not familiar with this behavior, this Digital Web article gives a good overview of the quirks.
The fat arrow => can be used to both define a function, and to bind it to the current value of this, right on the spot. This is helpful when using callback-based libraries like Prototype or jQuery, for creating iterator functions to pass to each, or event-handler functions to use with on. Functions created with the fat arrow are able to access properties of the this where they’re defined.
If we had used -> in the callback above, @customer would have referred to the undefined “customer” property of the DOM element, and trying to call purchase() on it would have raised an exception.
When used in a class definition, methods declared with the fat arrow will be automatically bound to each instance of the class when the instance is constructed.
CoffeeScript functions also support ES2015 generator functions through the yield keyword. There’s no function*(){} nonsense — a generator in CoffeeScript is simply a function that yields.
perfectSquares = ->
num = 0loop
num += 1yield num * num
returnwindow.ps or= perfectSquares()
var perfectSquares;
perfectSquares = function*() {
var num;
num = 0;
while (true) {
num += 1;
yield num * num;
}
};
window.ps || (window.ps = perfectSquares());
load
run: ps.next().value
yield* is called yield from, and yield return may be used if you need to force a generator that doesn’t yield.
You can iterate over a generator function using for…from.
fibonacci = ->
[previous, current] = [1, 1]
loop
[previous, current] = [current, previous + current]
yield current
returngetFibonacciNumbers = (length) ->
results = [1]
for n from fibonacci()
results.push n
breakif results.length is length
results
var fibonacci, getFibonacciNumbers;
fibonacci = function*() {
var current, previous, ref, ref1;
ref = [1, 1], previous = ref[0], current = ref[1];
while (true) {
ref1 = [current, previous + current], previous = ref1[0], current = ref1[1];
yield current;
}
};
getFibonacciNumbers = function(length) {
var n, ref, results;
results = [1];
ref = fibonacci();
for (n of ref) {
results.push(n);
if (results.length === length) {
break;
}
}
return results;
};
load
run: getFibonacciNumbers(10)
Embedded JavaScript
Hopefully, you’ll never need to use it, but if you ever need to intersperse snippets of JavaScript within your CoffeeScript, you can use backticks to pass it straight through.
hi = `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
load
run: hi()
Escape backticks with backslashes: \` becomes `.
Escape backslashes before backticks with more backslashes: \\\` becomes \`.
var markdown;
markdown = function () {
return`In Markdown, write code like \`this\``;
};
load
run: markdown()
You can also embed blocks of JavaScript using triple backticks. That’s easier than escaping backticks, if you need them inside your JavaScript block.
```functiontime() {
return`The time is ${newDate().toLocaleTimeString()}`;
}
```
functiontime() {
return`The time is ${newDate().toLocaleTimeString()}`;
}
;
load
run: time()
Switch/When/Else
Switch statements in JavaScript are a bit awkward. You need to remember to break at the end of every case statement to avoid accidentally falling through to the default case. CoffeeScript prevents accidental fall-through, and can convert the switch into a returnable, assignable expression. The format is: switch condition, when clauses, else the default case.
As in Ruby, switch statements in CoffeeScript can take multiple values for each when clause. If any of the values match, the clause runs.
switch day
when"Mon"then go work
when"Tue"then go relax
when"Thu"then go iceFishing
when"Fri", "Sat"if day is bingoDay
go bingo
go dancing
when"Sun"then go church
else go work
var grade, score;
score = 76;
grade = (function() {
switch (false) {
case !(score < 60):
return'F';
case !(score < 70):
return'D';
case !(score < 80):
return'C';
case !(score < 90):
return'B';
default:
return'A';
}
})();
load
Try/Catch/Finally
Try-expressions have the same semantics as try-statements in JavaScript, though in CoffeeScript, you may omit both the catch and finally parts. The catch part may also omit the error parameter if it is not needed.
String Interpolation, Block Strings, and Block Comments
Ruby-style string interpolation is included in CoffeeScript. Double-quoted strings allow for interpolated values, using #{ … }, and single-quoted strings are literal. You may even use interpolation in object keys.
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author }"
sentence = "#{ 22 / 7 } is a decent approximation of π"
var author, quote, sentence;
author = "Wittgenstein";
quote = "A picture is a fact. -- " + author;
sentence = (22 / 7) + " is a decent approximation of π";
load
run: sentence
Multiline strings are allowed in CoffeeScript. Lines are joined by a single space unless they end with a backslash. Indentation is ignored.
mobyDick = "Call me Ishmael. Some years ago --
never mind how long precisely -- having little
or no money in my purse, and nothing particular
to interest me on shore, I thought I would sail
about a little and see the watery part of the
world..."
var mobyDick;
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
load
run: mobyDick
Block strings can be used to hold formatted or indentation-sensitive text (or, if you just don’t feel like escaping quotes and apostrophes). The indentation level that begins the block is maintained throughout, so you can keep it all aligned with the body of your code.
html = """
<strong>
cup of coffeescript
</strong>
"""
var html;
html = "<strong>\n cup of coffeescript\n</strong>";
load
run: html
Double-quoted block strings, like other double-quoted strings, allow interpolation.
Sometimes you’d like to pass a block comment through to the generated JavaScript. For example, when you need to embed a licensing header at the top of a file. Block comments, which mirror the syntax for block strings, are preserved in the generated code.
###
SkinnyMochaHalfCaffScript Compiler v1.0
Released under the MIT License
###
/*
SkinnyMochaHalfCaffScript Compiler v1.0
Released under the MIT License
*/
load
Tagged Template Literals
CoffeeScript supports ES2015 tagged template literals, which enable customized string interpolation. If you immediately prefix a string with a function name (no space between the two), CoffeeScript will output this “function plus string” combination as an ES2015 tagged template literal, which will behave accordingly: the function is called, with the parameters being the input text and expression parts that make up the interpolated string. The function can then assemble these parts into an output string, providing custom string interpolation.
Be aware that the CoffeeScript compiler is outputting ES2015 syntax for this feature, so your target JavaScript runtime(s) must support this syntax for your code to work; or you could use tools like Babel or Traceur Compiler to convert this ES2015 syntax into compatible JavaScript.
upperCaseExpr = (textParts, expressions...) ->
textParts.reduce (text, textPart, i) ->
text + expressions[i - 1].toUpperCase() + textPart
greet = (name, adjective) ->
upperCaseExpr"""
Hi #{name}. You look #{adjective}!
"""
Similar to block strings and comments, CoffeeScript supports block regexes — extended regular expressions that ignore internal whitespace and can contain comments and interpolation. Modeled after Perl’s /x modifier, CoffeeScript’s block regexes are delimited by /// and go a long way towards making complex regular expressions readable. To quote from the CoffeeScript source:
var OPERATOR;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
load
Modules
ES2015 modules are supported in CoffeeScript, with very similar import and export syntax:
import'local-file.coffee'import'coffeescript'import _ from'underscore'import * as underscore from'underscore'import { now } from'underscore'import { now as currentTimestamp } from'underscore'import { first, last } from'underscore'import utilityBelt, { each } from'underscore'exportdefault Math
export square = (x) -> x * x
exportclassMathematics
least: (x, y) ->if x < y then x else y
export { sqrt }
export { sqrt as squareRoot }
export { Mathematics asdefault, sqrt as squareRoot }
export * from'underscore'export { max, min } from'underscore'
import'local-file.coffee';
import'coffeescript';
import _ from'underscore';
import * as underscore from'underscore';
import {
now
} from'underscore';
import {
now as currentTimestamp
} from'underscore';
import {
first,
last
} from'underscore';
import utilityBelt, {
each
} from'underscore';
exportdefaultMath;
exportvar square = function(x) {
return x * x;
};
exportvar Mathematics = (function() {
functionMathematics() {}
Mathematics.prototype.least = function(x, y) {
if (x < y) {
return x;
} else {
return y;
}
};
return Mathematics;
})();
export {
sqrt
};
export {
sqrt as squareRoot
};
export {
Mathematics asdefault,
sqrt as squareRoot
};
export * from'underscore';
export {
max,
min
} from'underscore';
load
Note that the CoffeeScript compiler does not resolve modules; writing an import or export statement in CoffeeScript will produce an import or export statement in the resulting output. It is your responsibility attach another transpiler, such as Traceur Compiler, Babel or Rollup, to convert this ES2015 syntax into code that will work in your target runtimes.
Also note that any file with an import or export statement will be output without a top-level function safety wrapper; in other words, importing or exporting modules will automatically trigger bare mode for that file. This is because per the ES2015 spec, import or export statements must occur at the topmost scope.
Cake, and Cakefiles
CoffeeScript includes a (very) simple build system similar to Make and Rake. Naturally, it’s called Cake, and is used for the tasks that build and test the CoffeeScript language itself. Tasks are defined in a file named Cakefile, and can be invoked by running cake [task] from within the directory. To print a list of all the tasks and options, just type cake.
Task definitions are written in CoffeeScript, so you can put arbitrary code in your Cakefile. Define a task with a name, a long description, and the function to invoke when the task is run. If your task takes a command-line option, you can define the option with short and long flags, and it will be made available in the options object. Here’s a task that uses the Node.js API to rebuild CoffeeScript’s parser:
fs = require'fs'
option '-o', '--output [DIR]', 'directory for compiled code'
task 'build:parser', 'rebuild the Jison parser', (options) ->require'jison'
code = require('./lib/grammar').parser.generate()
dir = options.output or'lib'
fs.writeFile "#{dir}/parser.js", code
var fs;
fs = require('fs');
option('-o', '--output [DIR]', 'directory for compiled code');
task('build:parser', 'rebuild the Jison parser', function(options) {
var code, dir;
require('jison');
code = require('./lib/grammar').parser.generate();
dir = options.output || 'lib';
return fs.writeFile(dir + "/parser.js", code);
});
load
If you need to invoke one task before another — for example, running build before test, you can use the invoke function: invoke 'build'. Cake tasks are a minimal way to expose your CoffeeScript functions to the command line, so don’t expect any fanciness built-in. If you need dependencies, or async callbacks, it’s best to put them in your code itself — not the cake task.
Source Maps
CoffeeScript 1.6.1 and above include support for generating source maps, a way to tell your JavaScript engine what part of your CoffeeScript program matches up with the code being evaluated. Browsers that support it can automatically use source maps to show your original source code in the debugger. To generate source maps alongside your JavaScript files, pass the --map or -m flag to the compiler.
For a full introduction to source maps, how they work, and how to hook them up in your browser, read the HTML5 Tutorial.
“text/coffeescript” Script Tags
While it’s not recommended for serious use, CoffeeScripts may be included directly within the browser using <script type="text/coffeescript"> tags. The source includes a compressed and minified version of the compiler (Download current version here, 51k when gzipped) as docs/v1/browser-compiler/coffee-script.js. Include this file on a page with inline CoffeeScript tags, and it will compile and evaluate them in order.
In fact, the little bit of glue script that runs “Try CoffeeScript” above, as well as the jQuery for the menu, is implemented in just this way. View source and look at the bottom of the page to see the example. Including the script also gives you access to CoffeeScript.compile() so you can pop open Firebug and try compiling some strings.
The usual caveats about CoffeeScript apply — your inline scripts will run within a closure wrapper, so if you want to expose global variables or functions, attach them to the window object.
Books
There are a number of excellent resources to help you get started with CoffeeScript, some of which are freely available online.
Smooth CoffeeScript is a reimagination of the excellent book Eloquent JavaScript, as if it had been written in CoffeeScript instead. Covers language features as well as the functional and object oriented programming styles. By E. Hoigaard.
CoffeeScript: Accelerated JavaScript Development is Trevor Burnham’s thorough introduction to the language. By the end of the book, you’ll have built a fast-paced multiplayer word game, writing both the client-side and Node.js portions in CoffeeScript.
CoffeeScript Ristretto is a deep dive into CoffeeScript’s semantics from simple functions up through closures, higher-order functions, objects, classes, combinators, and decorators. By Reg Braithwaite.
Testing with CoffeeScript is a succinct and freely downloadable guide to building testable applications with CoffeeScript and Jasmine.
Meet CoffeeScript is a 75-minute long screencast by PeepCode, now PluralSight. Highly memorable for its animations which demonstrate transforming CoffeeScript into the equivalent JS.
If you’re looking for less of a time commitment, RailsCasts’ CoffeeScript Basics should have you covered, hitting all of the important notes about CoffeeScript in 11 minutes.
Source Code
Use bin/coffee to test your changes, bin/cake test to run the test suite, bin/cake build to rebuild the full CoffeeScript compiler, and bin/cake build:except-parser to recompile much faster if you’re not editing grammar.coffee.
git checkout lib && bin/cake build:full is a good command to run when you’re working on the core language. It’ll refresh the lib folder (in case you broke something), build your altered compiler, use that to rebuild itself (a good sanity test) and then run all of the tests. If they pass, there’s a good chance you’ve made a successful change.
Browser Tests
Run CoffeeScript’s test suite in your current browser.
CoffeeScript Issues
Bug reports, feature proposals, and ideas for changes to the language belong here.
CoffeeScript Google Group
If you’d like to ask a question, the mailing list is a good place to get help.
The FAQ
Perhaps your CoffeeScript-related question has been asked before. Check the FAQ first.
JS2Coffee
Is a very well done reverse JavaScript-to-CoffeeScript compiler. It’s not going to be perfect (infer what your JavaScript classes are, when you need bound functions, and so on…) — but it’s a great starting point for converting simple scripts.
High-Rez Logo
The CoffeeScript logo is available in SVG for use in presentations.
Web Chat (IRC)
Quick help and advice can usually be found in the CoffeeScript IRC room. Join #coffeescript on irc.freenode.net, or click the button below to open a webchat session on this page.
The return and export keywords can now accept implicit objects (defined by indentation, without needing braces).
Support Unicode code point escapes (e.g. \u{1F4A9}).
The coffee command now first looks to see if CoffeeScript is installed under node_modules in the current folder, and executes the coffee binary there if so; or otherwise it runs the globally installed one. This allows you to have one version of CoffeeScript installed globally and a different one installed locally for a particular project. (Likewise for the cake command.)
Bugfixes for chained function calls not closing implicit objects or ternaries.
Bugfixes for incorrect code generated by the ? operator within a termary if statement.
Fixed some tests, and failing tests now result in a nonzero exit code.
Better handling of default, from, as and * within import and export statements. You can now import or export a member named default and the compiler won’t interpret it as the default keyword.
Fixed a bug where invalid octal escape sequences weren’t throwing errors in the compiler.
The cake commands have been updated, with new watch options for most tasks. Clone the CoffeeScript repo and run cake at the root of the repo to see the options.
Fixed a bug where exporting a referenced variable was preventing the variable from being declared.
Fixed a bug where the coffee command wasn’t working for a .litcoffee file.
Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
@ values can now be used as indices in for expressions. This loosens the compilation of for expressions to allow the index variable to be an @ value, e.g. do @visit for @node, @index in nodes. Within @visit, the index of the current node (@node) would be available as @index.
CoffeeScript’s patched Error.prepareStackTrace has been restored, with some revisions that should prevent the erroneous exceptions that were making life difficult for some downstream projects. This fixes the incorrect line numbers in stack traces since 1.12.2.
The //= operator’s output now wraps parentheses around the right operand, like the other assignment operators.
You can now import a module member named default, e.g. import { default } from 'lib'. Though like in ES2015, you cannot import an entire module and name it default (so import default from 'lib' is not allowed).
Fix regression where from as a variable name was breaking for loop declarations. For the record, from is not a reserved word in CoffeeScript; you may use it for variable names. from behaves like a keyword within the context of import and export statements, and in the declaration of a for loop; though you should also be able to use variables named from in those contexts, and the compiler should be able to tell the difference.
CoffeeScript now supports ES2015 tagged template literals. Note that using tagged template literals in your code makes you responsible for ensuring that either your runtime supports tagged template literals or that you transpile the output JavaScript further to a version your target runtime(s) support.
CoffeeScript now provides a for…from syntax for outputting ES2015 for…of. (Sorry they couldn’t match, but we came up with for…of first for something else.) This allows iterating over generators or any other iterable object. Note that using for…from in your code makes you responsible for ensuring that either your runtime supports for…of or that you transpile the output JavaScript further to a version your target runtime(s) support.
Triple backticks (```) allow the creation of embedded JavaScript blocks where escaping single backticks is not required, which should improve interoperability with ES2015 template literals and with Markdown.
Within single-backtick embedded JavaScript, backticks can now be escaped via \`.
The browser tests now run in the browser again, and are accessible here if you would like to test your browser.
CoffeeScript-only keywords in ES2015 imports and exports are now ignored.
The compiler now throws an error on trying to export an anonymous class.
Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
Added the -M, --inline-map flag to the compiler, allowing you embed the source map directly into the output JavaScript, rather than as a separate file.
A bunch of fixes for yield:
yield return can no longer mistakenly be used as an expression.
yield now mirrors return in that it can be used stand-alone as well as with expressions. Where you previously wrote yield undefined, you may now write simply yield. However, this means also inheriting the same syntax limitations that return has, so these examples no longer compile:
doubles = ->
yield for i in [1..3]
i * 2
six = ->
yield
2 * 3
The JavaScript output is a bit nicer, with unnecessary parentheses and spaces, double indentation and double semicolons around yield no longer present.
&&=, ||=, and= and or= no longer accidentally allow a space before the equals sign.
Improved several error messages.
Just like undefined compiles to void 0, NaN now compiles into 0/0 and Infinity into 2e308.
Bugfix for renamed destructured parameters with defaults. ({a: b = 1}) -> no longer crashes the compiler.
Improved the internal representation of a CoffeeScript program. This is only noticeable to tools that use CoffeeScript.tokens or CoffeeScript.nodes. Such tools need to update to take account for changed or added tokens and nodes.
Several minor bug fixes, including:
The caught error in catch blocks is no longer declared unnecessarily, and no longer mistakenly named undefined for catch-less try blocks.
Unassignable parameter destructuring no longer crashes the compiler.
Source maps are now used correctly for errors thrown from .coffee.md files.
coffee -e 'throw null' no longer crashes.
The REPL no longer crashes when using .exit to exit it.
Invalid JavaScript is no longer output when lots of for loops are used in the same scope.
CoffeeScript now supports ES2015-style destructuring defaults.
(offsetHeight: height) -> no longer compiles. That syntax was accidental and partly broken. Use ({offsetHeight: height}) -> instead. Object destructuring always requires braces.
Several minor bug fixes, including:
A bug where the REPL would sometimes report valid code as invalid, based on what you had typed earlier.
A problem with multiple JS contexts in the jest test framework.
An error in io.js where strict mode is set on internal modules.
A variable name clash for the caught error in catch blocks.
Bugfix for interpolation in the first key of an object literal in an implicit call.
Fixed broken error messages in the REPL, as well as a few minor bugs with the REPL.
Fixed source mappings for tokens at the beginning of lines when compiling with the --bare option. This has the nice side effect of generating smaller source maps.
Slight formatting improvement of compiled block comments.
Fixed a watch mode error introduced in 1.9.1 when compiling multiple files with the same filename.
Bugfix for yield around expressions containing this.
Added a Ruby-style -r option to the REPL, which allows requiring a module before execution with --eval or --interactive.
In <script type="text/coffeescript"> tags, to avoid possible duplicate browser requests for .coffee files, you can now use the data-src attribute instead of src.
Minor bug fixes for IE8, strict ES5 regular expressions and Browserify.
Interpolation now works in object literal keys (again). You can use this to dynamically name properties.
Internal compiler variable names no longer start with underscores. This makes the generated JavaScript a bit prettier, and also fixes an issue with the completely broken and ungodly way that AngularJS “parses” function arguments.
Fixed a few yield-related edge cases with yield return and yield throw.
Minor bug fixes and various improvements to compiler error messages.
CoffeeScript now supports ES2015 generators. A generator is simply a function that yields.
More robust parsing and improved error messages for strings and regexes — especially with respect to interpolation.
Changed strategy for the generation of internal compiler variable names. Note that this means that @example function parameters are no longer available as naked example variables within the function body.
Fixed REPL compatibility with latest versions of Node and Io.js.
When requiring CoffeeScript files in Node you must now explicitly register the compiler. This can be done with require 'coffee-script/register' or CoffeeScript.register(). Also for configuration such as Mocha’s, use coffee-script/register.
Improved error messages, source maps and stack traces. Source maps now use the updated //# syntax.
Leading . now closes all open calls, allowing for simpler chaining syntax.
Added **, // and %% operators and ... expansion in parameter lists and destructuring expressions.
Multiline strings are now joined by a single space and ignore all indentation. A backslash at the end of a line can denote the amount of whitespace between lines, in both strings and heredocs. Backslashes correctly escape whitespace in block regexes.
Closing brackets can now be indented and therefore no longer cause unexpected error.
Several breaking compilation fixes. Non-callable literals (strings, numbers etc.) don’t compile in a call now and multiple postfix conditionals compile properly. Postfix conditionals and loops always bind object literals. Conditional assignment compiles properly in subexpressions. super is disallowed outside of methods and works correctly inside for loops.
Formatting of compiled block comments has been improved.
No more -p folders on Windows.
The options object passed to CoffeeScript is no longer mutated.
The CoffeeScript REPL now remembers your history between sessions. Just like a proper REPL should.
You can now use require in Node to load .coffee.md Literate CoffeeScript files. In the browser, text/literate-coffeescript script tags.
The old coffee --lint command has been removed. It was useful while originally working on the compiler, but has been surpassed by JSHint. You may now use -l to pass literate files in over stdio.
Bugfixes for Windows path separators, catch without naming the error, and executable-class-bodies-with- prototypal-property-attachment.
Source maps have been used to provide automatic line-mapping when running CoffeeScript directly via the coffee command, and for automatic line-mapping when running CoffeeScript directly in the browser. Also, to provide better error messages for semantic errors thrown by the compiler — with colors, even.
Improved support for mixed literate/vanilla-style CoffeeScript projects, and generating source maps for both at the same time.
Fixes for 1.6.x regressions with overriding inherited bound functions, and for Windows file path management.
The coffee command can now correctly fork() both .coffee and .js files. (Requires Node.js 0.9+)
First release of source maps. Pass the --map flag to the compiler, and off you go. Direct all your thanks over to Jason Walton.
Fixed a 1.5.0 regression with multiple implicit calls against an indented implicit object. Combinations of implicit function calls and implicit objects should generally be parsed better now — but it still isn’t good style to nest them too heavily.
.coffee.md is now also supported as a Literate CoffeeScript file extension, for existing tooling. .litcoffee remains the canonical one.
Several minor fixes surrounding member properties, bound methods and super in class declarations.
Due to the new semantics of JavaScript’s strict mode, CoffeeScript no longer guarantees that constructor functions have names in all runtimes. See #2052 for discussion.
Inside of a nested function inside of an instance method, it’s now possible to call super more reliably (walks recursively up).
Named loop variables no longer have different scoping heuristics than other local variables. (Reverts #643)
Fix for splats nested within the LHS of destructuring assignment.
Corrections to our compile time strict mode forbidding of octal literals.
CoffeeScript now enforces all of JavaScript’s Strict Mode early syntax errors at compile time. This includes old-style octal literals, duplicate property names in object literals, duplicate parameters in a function definition, deleting naked variables, setting the value of eval or arguments, and more. See a full discussion at #1547.
The REPL now has a handy new multi-line mode for entering large blocks of code. It’s useful when copy-and-pasting examples into the REPL. Enter multi-line mode with Ctrl-V. You may also now pipe input directly into the REPL.
CoffeeScript now prints a Generated by CoffeeScript VERSION header at the top of each compiled file.
Conditional assignment of previously undefined variables a or= b is now considered a syntax error.
A tweak to the semantics of do, which can now be used to more easily simulate a namespace: do (x = 1, y = 2) -> …
Loop indices are now mutable within a loop iteration, and immutable between them.
Both endpoints of a slice are now allowed to be omitted for consistency, effectively creating a shallow copy of the list.
Additional tweaks and improvements to coffee --watch under Node’s “new” file watching API. Watch will now beep by default if you introduce a syntax error into a watched script. We also now ignore hidden directories by default when watching recursively.
Multiple improvements to coffee --watch and --join. You may now use both together, as well as add and remove files and directories within a --watch’d folder.
The throw statement can now be used as part of an expression.
Block comments at the top of the file will now appear outside of the safety closure wrapper.
Fixed a number of minor 1.1.3 regressions having to do with trailing operators and unfinished lines, and a more major 1.1.3 regression that caused bound functions within bound class functions to have the incorrect this.
Ahh, whitespace. CoffeeScript’s compiled JS now tries to space things out and keep it readable, as you can see in the examples on this page.
You can now call super in class level methods in class bodies, and bound class methods now preserve their correct context.
JavaScript has always supported octal numbers 010 is 8, and hexadecimal numbers 0xf is 15, but CoffeeScript now also supports binary numbers: 0b10 is 2.
The CoffeeScript module has been nested under a subdirectory to make it easier to require individual components separately, without having to use npm. For example, after adding the CoffeeScript folder to your path: require('coffee-script/lexer')
There’s a new “link” feature in Try CoffeeScript on this webpage. Use it to get a shareable permalink for your example script.
The coffee --watch feature now only works on Node.js 0.6.0 and higher, but now also works properly on Windows.
Fixes for block comment formatting, ?= compilation, implicit calls against control structures, implicit invocation of a try/catch block, variadic arguments leaking from local scope, line numbers in syntax errors following heregexes, property access on parenthesized number literals, bound class methods and super with reserved names, a REPL overhaul, consecutive compiled semicolons, block comments in implicitly called objects, and a Chrome bug.
When running via the coffee executable, process.argv and friends now report coffee instead of node. Better compatibility with Node.js 0.4.x module lookup changes. The output in the REPL is now colorized, like Node’s is. Giving your concatenated CoffeeScripts a name when using --join is now mandatory. Fix for lexing compound division /= as a regex accidentally. All text/coffeescript tags should now execute in the order they’re included. Fixed an issue with extended subclasses using external constructor functions. Fixed an edge-case infinite loop in addImplicitParentheses. Fixed exponential slowdown with long chains of function calls. Globals no longer leak into the CoffeeScript REPL. Splatted parameters are declared local to the function.
Fixed a lexer bug with Unicode identifiers. Updated REPL for compatibility with Node.js 0.3.7. Fixed requiring relative paths in the REPL. Trailing return and return undefined are now optimized away. Stopped requiring the core Node.js util module for back-compatibility with Node.js 0.2.5. Fixed a case where a conditional return would cause fallthrough in a switch statement. Optimized empty objects in destructuring assignment.
CoffeeScript loops no longer try to preserve block scope when functions are being generated within the loop body. Instead, you can use the do keyword to create a convenient closure wrapper. Added a --nodejs flag for passing through options directly to the node executable. Better behavior around the use of pure statements within expressions. Fixed inclusive slicing through -1, for all browsers, and splicing with arbitrary expressions as endpoints.
The REPL now properly formats stacktraces, and stays alive through asynchronous exceptions. Using --watch now prints timestamps as files are compiled. Fixed some accidentally-leaking variables within plucked closure-loops. Constructors now maintain their declaration location within a class body. Dynamic object keys were removed. Nested classes are now supported. Fixes execution context for naked splatted functions. Bugfix for inversion of chained comparisons. Chained class instantiation now works properly with splats.
0.9.5 should be considered the first release candidate for CoffeeScript 1.0. There have been a large number of internal changes since the previous release, many contributed from satyr’s Coco dialect of CoffeeScript. Heregexes (extended regexes) were added. Functions can now have default arguments. Class bodies are now executable code. Improved syntax errors for invalid CoffeeScript. undefined now works like null, and cannot be assigned a new value. There was a precedence change with respect to single-line comprehensions: result = i for i in list
used to parse as result = (i for i in list) by default … it now parses as
(result = i) for i in list.
CoffeeScript now uses appropriately-named temporary variables, and recycles their references after use. Added require.extensions support for Node.js 0.3. Loading CoffeeScript in the browser now adds just a single CoffeeScript object to global scope. Fixes for implicit object and block comment edge cases.
CoffeeScript switch statements now compile into JS switch statements — they previously compiled into if/else chains for JavaScript 1.3 compatibility. Soaking a function invocation is now supported. Users of the RubyMine editor should now be able to use --watch mode.
Specifying the start and end of a range literal is now optional, eg. array[3..]. You can now say a not instanceof b. Fixed important bugs with nested significant and non-significant indentation (Issue #637). Added a --require flag that allows you to hook into the coffee command. Added a custom jsl.conf file for our preferred JavaScriptLint setup. Sped up Jison grammar compilation time by flattening rules for operations. Block comments can now be used with JavaScript-minifier-friendly syntax. Added JavaScript’s compound assignment bitwise operators. Bugfixes to implicit object literals with leading number and string keys, as the subject of implicit calls, and as part of compound assignment.
Bugfix release for 0.9.1. Greatly improves the handling of mixed implicit objects, implicit function calls, and implicit indentation. String and regex interpolation is now strictly #{ … } (Ruby style). The compiler now takes a --require flag, which specifies scripts to run before compilation.
The CoffeeScript 0.9 series is considered to be a release candidate for 1.0; let’s give her a shakedown cruise. 0.9.0 introduces a massive backwards-incompatible change: Assignment now uses =, and object literals use :, as in JavaScript. This allows us to have implicit object literals, and YAML-style object definitions. Half assignments are removed, in favor of +=, or=, and friends. Interpolation now uses a hash mark # instead of the dollar sign $ — because dollar signs may be part of a valid JS identifier. Downwards range comprehensions are now safe again, and are optimized to straight for loops when created with integer endpoints. A fast, unguarded form of object comprehension was added: for all key, value of object. Mentioning the super keyword with no arguments now forwards all arguments passed to the function, as in Ruby. If you extend class B from parent class A, if A has an extended method defined, it will be called, passing in B — this enables static inheritance, among other things. Cleaner output for functions bound with the fat arrow. @variables can now be used in parameter lists, with the parameter being automatically set as a property on the object — useful in constructors and setter functions. Constructor functions can now take splats.
Block-style comments are now passed through and printed as JavaScript block comments – making them useful for licenses and copyright headers. Better support for running coffee scripts standalone via hashbangs. Improved syntax errors for tokens that are not in the grammar.
Official CoffeeScript variable style is now camelCase, as in JavaScript. Reserved words are now allowed as object keys, and will be quoted for you. Range comprehensions now generate cleaner code, but you have to specify by -1 if you’d like to iterate downward. Reporting of syntax errors is greatly improved from the previous release. Running coffee with no arguments now launches the REPL, with Readline support. The <- bind operator has been removed from CoffeeScript. The loop keyword was added, which is equivalent to a while true loop. Comprehensions that contain closures will now close over their variables, like the semantics of a forEach. You can now use bound function in class definitions (bound to the instance). For consistency, a in b is now an array presence check, and a of b is an object-key check. Comments are no longer passed through to the generated JavaScript.
The coffee command will now preserve directory structure when compiling a directory full of scripts. Fixed two omissions that were preventing the CoffeeScript compiler from running live within Internet Explorer. There’s now a syntax for block comments, similar in spirit to CoffeeScript’s heredocs. ECMA Harmony DRY-style pattern matching is now supported, where the name of the property is the same as the name of the value: {name, length}: func. Pattern matching is now allowed within comprehension variables. unless is now allowed in block form. until loops were added, as the inverse of while loops. switch statements are now allowed without switch object clauses. Compatible with Node.js v0.1.95.
Interpolation can now be used within regular expressions and heredocs, as well as strings. Added the <- bind operator. Allowing assignment to half-expressions instead of special ||=-style operators. The arguments object is no longer automatically converted into an array. After requiring coffee-script, Node.js can now directly load .coffee files, thanks to registerExtension. Multiple splats can now be used in function calls, arrays, and pattern matching.
String interpolation, contributed by Stan Angeloff. Since --run has been the default since 0.5.3, updating --stdio and --eval to run by default, pass --compile as well if you’d like to print the result.
Bugfix that corrects the Node.js global constants __filename and __dirname. Tweaks for more flexible parsing of nested function literals and improperly-indented comments. Updates for the latest Node.js API.
CoffeeScript now has a syntax for defining classes. Many of the core components (Nodes, Lexer, Rewriter, Scope, Optparse) are using them. Cakefiles can use optparse.coffee to define options for tasks. --run is now the default flag for the coffee command, use --compile to save JavaScripts. Bugfix for an ambiguity between RegExp literals and chained divisions.
Added a compressed version of the compiler for inclusion in web pages as
browser-compiler/coffee-script.js. It’ll automatically run any script tags with type text/coffeescript for you. Added a --stdio option to the coffee command, for piped-in compiles.
Improvements to null soaking with the existential operator, including soaks on indexed properties. Added conditions to while loops, so you can use them as filters with when, in the same manner as comprehensions.
CoffeeScript 0.5.0 is a major release, While there are no language changes, the Ruby compiler has been removed in favor of a self-hosting compiler written in pure CoffeeScript.
@property is now a shorthand for this.property.
Switched the default JavaScript engine from Narwhal to Node.js. Pass the --narwhal flag if you’d like to continue using it.
CoffeeScript 0.3 includes major syntax changes:
The function symbol was changed to ->, and the bound function symbol is now =>.
Parameter lists in function definitions must now be wrapped in parentheses.
Added property soaking, with the ?. operator.
Made parentheses optional, when invoking functions with arguments.
Removed the obsolete block literal syntax.
Added Python-style chained comparisons, the conditional existence operator ?=, and some examples from Beautiful Code. Bugfixes relating to statement-to-expression conversion, arguments-to-array conversion, and the TextMate syntax highlighter.
The conditions in switch statements can now take multiple values at once — If any of them are true, the case will run. Added the long arrow ==>, which defines and immediately binds a function to this. While loops can now be used as expressions, in the same way that comprehensions can. Splats can be used within pattern matches to soak up the rest of an array.
Added ECMAScript Harmony style destructuring assignment, for dealing with extracting values from nested arrays and objects. Added indentation-sensitive heredocs for nicely formatted strings or chunks of code.
When performing a comprehension over an object, use ino, instead of in, which helps us generate smaller, more efficient code at compile time.
Added :: as a shorthand for saying .prototype.
The “splat” symbol has been changed from a prefix asterisk *, to a postfix ellipsis ...
Added JavaScript’s in operator, empty return statements, and empty while loops.
Constructor functions that start with capital letters now include a safety check to make sure that the new instance of the object is returned.
The extends keyword now functions identically to goog.inherits in Google’s Closure Library.
Major release. Significant whitespace. Better statement-to-expression conversion. Splats. Splice literals. Object comprehensions. Blocks. The existential operator. Many thanks to all the folks who posted issues, with special thanks to Liam O’Connor-Davis for whitespace and expression help.
Array slice literals and array comprehensions can now both take Ruby-style ranges to specify the start and end. JavaScript variable declaration is now pushed up to the top of the scope, making all assignment statements into expressions. You can use \ to escape newlines. The coffee-script command is now called coffee.
The official CoffeeScript extension is now .coffee instead of .cs, which properly belongs to C#. Due to popular demand, you can now also use = to assign. Unlike JavaScript, = can also be used within object literals, interchangeably with :. Made a grammatical fix for chained function calls like func(1)(2)(3)(4). Inheritance and super no longer use __proto__, so they should be IE-compatible now.
The coffee command now includes --interactive, which launches an interactive CoffeeScript session, and --run, which directly compiles and executes a script. Both options depend on a working installation of Narwhal. The aint keyword has been replaced by isnt, which goes together a little smoother with is. Quoted strings are now allowed as identifiers within object literals: eg. {"5+5": 10}. All assignment operators now use a colon: +:, -:, *:, etc.
Fixed a bug with calling super() through more than one level of inheritance, with the re-addition of the extends keyword. Added experimental Narwhal support (as a Tusk package), contributed by Tom Robinson, including bin/cs as a CoffeeScript REPL and interpreter. New --no-wrap option to suppress the safety function wrapper.
This Browser compatibility layer extends core CoffeeScript functions
to make things work smoothly when compiling code directly in the browser.
We add support for loading remote Coffee scripts via XHR, and
text/coffeescript script tags, source maps via data-URLs, and so on.
Use window.eval to evaluate code, rather than just eval, to run the
script in a clean global scope rather than inheriting the scope of the
CoffeeScript compiler. (So that cake test:browser also works in Node,
use either window.eval or global.eval as appropriate).
CoffeeScript.eval = (code, options = {}) ->
options.bare ?= on
globalRoot = if window? then window else global
globalRoot['eval'] compile code, options
Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of text/coffeescript.
This happens on page load.
CoffeeScript.runScripts = ->
scripts = window.document.getElementsByTagName 'script'
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
coffees = (s for s in scripts when s.type in coffeetypes)
index = 0execute = ->
param = coffees[index]
if param instanceofArray
CoffeeScript.run param...
index++
execute()
for script, i in coffees
do (script, i) ->
options = literate: script.type is coffeetypes[1]
source = script.src or script.getAttribute('data-src')
if source
options.filename = source
CoffeeScript.load source,
(param) ->
coffees[i] = param
execute()
options
trueelse
options.filename defines the filename the source map appears as
in Developer Tools. If a script tag has an id, use that as the
filename; otherwise use coffeescript, or coffeescript1 etc.,
leaving the first one unnumbered for the common case that there’s
only one CoffeeScript script block to parse.
options.filename = if script.id and script.id isnt''then script.id else"coffeescript#{if i isnt0then i else''}"
options.sourceFiles = ['embedded']
coffees[i] = [script.innerHTML, options]
execute()
Listen for window load, both in decent browsers and in IE.
Only attach this event handler on startup for the
non-ES module version of the browser compiler, to preserve
backward compatibility while letting the ES module version
be importable without side effects.
if this is window
if window.addEventListener
window.addEventListener 'DOMContentLoaded', CoffeeScript.runScripts, noelse
window.attachEvent 'onload', CoffeeScript.runScripts
cake is a simplified version of Make
(Rake, Jake)
for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
and can call them from the command line, or invoke them from other tasks.
Running cake with no arguments will print out a list of all the tasks in the
current directory’s Cakefile.
Define an option that the Cakefile accepts. The parsed options hash,
containing all of the command-line options passed, will be made available
as the first argument to the action.
Run cake. Executes all of the tasks you pass, in order. Note that Node’s
asynchrony may cause tasks to execute in a different order than you’d expect.
If no tasks are passed, print the help screen. Keep a reference to the
original directory name, when running Cake tasks from subdirectories.
Print an error and exit when attempting to use an invalid task/option.
fatalError = (message) ->
console.error message + '\n'
console.log 'To see a list of all tasks/options, run "cake"'
process.exit 1missingTask = (task) -> fatalError "No such task: #{task}"
When cake is invoked, search in the current and all parent directories
to find the relevant Cakefile.
cakefileDirectory = (dir) ->return dir if fs.existsSync path.join dir, 'Cakefile'
parent = path.normalize path.join dir, '..'return cakefileDirectory parent unless parent is dir
thrownewError"Cakefile not found in #{process.cwd()}"
CoffeeScript can be used both on the server, as a command-line compiler based
on Node.js/V8, or to run CoffeeScript directly in the browser. This module
contains the main entry functions for tokenizing, parsing, and compiling
source CoffeeScript into JavaScript.
This is exported to enable an external module to implement caching of
sourcemaps. This is used only when patchStackTrace has been called to adjust
stack traces for files with cached source maps.
Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
If options.sourceMap is specified, then options.filename must also be
specified. All options that can be passed to SourceMap#generate may also
be passed here.
This returns a javascript string, unless options.sourceMap is passed,
in which case this returns a {js, v3SourceMap, sourceMap}
object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
doing programmatic lookups.
If all that was requested was a POJO representation of the nodes, e.g.
the abstract syntax tree (AST), we can stop now and just return that
(after fixing the location data for the root/File»Program node,
which might’ve gotten misaligned from the original source due to the
clean function in the lexer).
Parse a string of CoffeeScript code or an array of lexed tokens, and
return the AST. You can then compile it by calling .compile() on the root,
or traverse it by using .traverseChildren() with a callback.
This file used to export these methods; leave stubs that throw warnings
instead. These methods have been moved into index.coffee to provide
separate entrypoints for Node and non-Node environments, so that static
analysis tools don’t choke on Node packages when compiling for a non-Node
environment.
exports.run = exports.eval = exports.register = ->thrownewError'require index.coffee, not this file'
The real Lexer produces a generic stream of tokens. This object provides a
thin wrapper around it, compatible with the Jison API. We can then pass it
directly as a “Jison lexer.”
Disregard Jison’s message, it contains redundant line number information.
Disregard the token, we take its value directly from the lexer in case
the error is caused by a generated token which might refer to its origin.
{errorToken, tokens} = parser
[errorTag, errorText, errorLoc] = errorToken
errorText = switchwhen errorToken is tokens[tokens.length - 1]
'end of input'when errorTag in ['INDENT', 'OUTDENT']
'indentation'when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
errorTag.replace(/_START$/, '').toLowerCase()
else
helpers.nameWhitespaceCharacter errorText
The second argument has a loc property, which should have the location
data for this token. Unfortunately, Jison seems to send an outdated loc
(from the previous token), so we take the location information directly
from the lexer.
Based on michaelficarra/CoffeeScriptRedux
NodeJS / V8 have no support for transforming positions in stack traces using
sourceMap, so we must monkey-patch Error to display CoffeeScript source
positions.
Error.prepareStackTrace = (err, stack) ->
frames = for frame in stack
Don’t display stack frames deeper than CoffeeScript.run.
breakif frame.getFunction() isexports.run
" at #{formatSourcePosition frame, getSourceMapping}""#{err.toString()}\n#{frames.join '\n'}\n"checkShebangLine = (file, input) ->
firstLine = input.split(/$/m, 1)[0]
rest = firstLine?.match(/^#!\s*([^\s]+\s*)(.*)/)
args = rest?[2]?.split(/\s/).filter (s) -> s isnt''if args?.length > 1
console.error '''
The script to be run begins with a shebang line with more than one
argument. This script will fail on platforms such as Linux which only
allow a single argument.
'''
console.error "The shebang line was: '#{firstLine}' in file '#{file}'"
console.error "The arguments were: #{JSON.stringify args}"
The coffee utility. Handles command-line compilation of CoffeeScript
into various forms: saved into .js files or printed to stdout
or recompiled every time the source is saved,
printed as a token stream or as the syntax tree, or launch an
interactive REPL.
The list of all the valid option flags that coffee knows how to handle.
SWITCHES = [
[ '--ast', 'generate an abstract syntax tree of nodes']
['-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']
['-l', '--literate', 'treat stdio as literate style coffeescript']
['-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']
['-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']
]
Run coffee by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
-- will be passed verbatim to your script as arguments in process.argv
exports.run = ->
optionParser = buildCSOptionParser()
try parseOptions()
catch err
console.error "option parsing error: #{err.message}"
process.exit 1if (not opts.doubleDashed) and (opts.arguments[1] is'--')
printWarn '''
coffee was invoked with '--' as the second positional argument, which is
now deprecated. To pass '--' as an argument to a script to run, put an
additional '--' before the path to your script.
'--' will be removed from the argument list.
'''
printWarn "The positional arguments were: #{JSON.stringify opts.arguments}"
opts.arguments = [opts.arguments[0]].concat opts.arguments[2..]
Make the REPL CLI use the global context so as to (a) be consistent with the
node REPL CLI and, therefore, (b) make packages that modify native prototypes
(such as ‘colors’ and ‘sugar’) work as expected.
replCliOpts = useGlobal: yes
opts.prelude = makePrelude opts.requireif opts.require
replCliOpts.prelude = opts.prelude
replCliOpts.transpile = opts.transpile
return forkNode() if opts.nodejs
return usage() if opts.help
return version() if opts.version
returnrequire('./repl').start(replCliOpts) if opts.interactive
return compileStdio() if opts.stdio
return compileScript null, opts.arguments[0] if opts.evalreturnrequire('./repl').start(replCliOpts) unless opts.arguments.length
literals = if opts.run then opts.arguments.splice 1else []
process.argv = process.argv[0..1].concat literals
process.argv[0] = 'coffee'if opts.output
outputBasename = path.basename opts.output
if'.'in outputBasename and
outputBasename notin ['.', '..'] andnot helpers.ends(opts.output, path.sep)
Compile a path, which could be a script or a directory. If a directory
is passed, recursively compile all ‘.coffee’, ‘.litcoffee’, and ‘.coffee.md’
extension source files in it and all subdirectories.
compilePath = (source, topLevel, base) ->returnif source in sources or
watchedDirs[source] ornot topLevel and (notSources[source] or hidden source)
try
stats = fs.statSync source
catch err
if err.code is'ENOENT'
console.error "File not found: #{source}"
process.exit 1throw err
if stats.isDirectory()
if path.basename(source) is'node_modules'
notSources[source] = yesreturnif opts.run
compilePath findDirectoryIndex(source), topLevel, base
return
watchDir source, base if opts.watch
try
files = fs.readdirSync source
catch err
if err.code is'ENOENT'thenreturnelsethrow err
for file in files
compilePath (path.join source, file), no, base
elseif topLevel or helpers.isCoffee source
sources.push source
sourceCode.push nulldelete notSources[source]
watch source, base if opts.watch
try
code = fs.readFileSync source
catch err
if err.code is'ENOENT'thenreturnelsethrow err
compileScript source, code.toString(), base
else
notSources[source] = yesfindDirectoryIndex = (source) ->for ext in CoffeeScript.FILE_EXTENSIONS
index = path.join source, "index#{ext}"tryreturn index if (fs.statSync index).isFile()
catch err
throw err unless err.code is'ENOENT'
console.error "Missing index.coffee or index.litcoffee in #{source}"
process.exit 1
Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly, set __filename,
__dirname and module.filename to be correct relative to the script’s path.
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.
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.
If generatedSourceMap is provided, this will write a .js.map file into the
same directory as the .js file.
The user has requested that the CoffeeScript compiler also transpile
via Babel. We don’t include Babel as a dependency because we want to
avoid dependencies in general, and most users probably won’t be relying
on us to transpile for them; we assume most users will probably either
run CoffeeScript’s output without transpilation (modern Node or evergreen
browsers) or use a proper build chain like Gulp or Webpack.
Give appropriate instructions depending on whether coffee was run
locally or globally.
ifrequire.resolve('.').indexOf(process.cwd()) is0
console.error '''
To use --transpile, you must have @babel/core installed:
npm install --save-dev @babel/core
And you must save options to configure Babel in one of the places it looks to find its options.
See https://coffeescript.org/#transpilation
'''else
console.error '''
To use --transpile with globally-installed CoffeeScript, you must have @babel/core installed globally:
npm install --global @babel/core
And you must save options to configure Babel in one of the places it looks to find its options, relative to the file being compiled or to the current folder.
See https://coffeescript.org/#transpilation
'''
process.exit 1
opts.transpile = {} unlesstypeof opts.transpile is'object'
Pass a reference to Babel into the compiler, so that the transpile option
is available for the CLI. We need to do this so that tools like Webpack
can require('coffeescript') and build correctly, without trying to
require Babel.
Babel searches for its options (a .babelrc file, a .babelrc.js file,
a package.json file with a babel key, etc.) relative to the path
given to it in its filename option. Make sure we have a path to pass
along.
unless opts.transpile.filename
opts.transpile.filename = filename or path.resolve(base or process.cwd(), '<anonymous>')
else
opts.transpile = no
answer =
filename: filename
literate: opts.literate or helpers.isLiterate(filename)
bare: opts.bare
header: opts.compile andnot opts['no-header']
transpile: opts.transpile
sourceMap: opts.map
inlineMap: opts['inline-map']
ast: opts.ast
if filename
if base
cwd = process.cwd()
jsPath = outputPath filename, base
jsDir = path.dirname jsPath
answer = helpers.merge answer, {
jsPath
sourceRoot: path.relative(jsDir, cwd) + path.sep
sourceFiles: [path.relative cwd, filename]
generatedFile: helpers.baseFileName(jsPath, no, useWinPathSep)
}
else
answer = helpers.merge answer,
sourceRoot: ""
sourceFiles: [helpers.baseFileName filename, no, useWinPathSep]
generatedFile: helpers.baseFileName(filename, yes, useWinPathSep) + ".js"
answer
The CoffeeScript parser is generated by Jison
from this grammar file. Jison is a bottom-up parser generator, similar in
style to Bison, implemented in JavaScript.
It can recognize LALR(1), LR(0), SLR(1), and LR(1)
type grammars. To create the Jison parser, we list the pattern to match
on the left-hand side, and the action to take (usually the creation of syntax
tree nodes) on the right. As the parser runs, it
shifts tokens from our token stream, from left to right, and
attempts to match
the token sequence against the rules below. When a match can be made, it
reduces into the nonterminal
(the enclosing name at the top), and we proceed from there.
If you run the cake build:parser command, Jison constructs a parse table
from our rules and saves it into lib/parser.js.
Since we’re going to be wrapped in a function by Jison in any case, if our
action immediately returns a value, we can optimize by removing the function
wrapper and just returning the value directly.
Our handy DSL for Jison grammar generation, thanks to
Tim Caswell. For every rule in the grammar,
we pass the pattern-defining string, the action to run, and extra options,
optionally. If no action is specified, we simply pass the value of the
previous nonterminal.
o = (patternString, action, options) ->
patternString = patternString.replace /\s{2,}/g, ' '
patternCount = patternString.split(' ').length
if action
Returns strings of functions to add to parser.js which add extra data
that nodes may have, such as comments or location data. Location data
is added to the first parameter passed in, and the parameter is returned.
If the parameter is not a node, it will just be passed through unaffected.
This code replaces the calls to LOC with the yy.addDataToNode string
defined above. The LOC function, when used below in the grammar rules,
is used to make sure that newly created node class objects get correct
location data assigned to them. By default, the grammar will assign the
location data spanned by all of the tokens on the left (e.g. a string
such as 'Body TERMINATOR Line') to the “top-level” node returned by
the grammar rule (the function on the right). But for “inner” node class
objects created by grammar rules, they won’t get correct location data
assigned to them without adding LOC.
For example, consider the grammar rule 'NEW_TARGET . Property', which
is handled by a function that returns
new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3).
The 1 in LOC(1) refers to the first token (NEW_TARGET) and the 3
in LOC(3) refers to the third token (Property). In order for the
new IdentifierLiteral to get assigned the location data corresponding
to new in the source code, we use
LOC(1)(new IdentifierLiteral ...) to mean “assign the location data of
the first token of this grammar rule (NEW_TARGET) to this
new IdentifierLiteral”. The LOC(3) means “assign the location data of
the third token of this grammar rule (Property) to this
new Access”.
A call to LOC with two arguments, e.g. LOC(2,4), sets the location
data for the generated node on both of the referenced tokens (the second
and fourth in this example).
In all of the rules that follow, you’ll see the name of the nonterminal as
the key to a list of alternative matches. With each match’s action, the
dollar-sign variables are provided by Jison as references to the value of
their numeric position, so in this rule:
'Expression UNLESS Expression'
$1 would be the value of the first Expression, $2 would be the token
for the UNLESS terminal, and $3 would be the value of the second
Expression.
Block and statements, which make up a line in a body. FuncDirective is a
statement, but not included in Statement because that results in an ambiguous
grammar.
Line: [
o 'Expression'
o 'ExpressionLine'
o 'Statement'
o 'FuncDirective'
]
FuncDirective: [
o 'YieldReturn'
o 'AwaitReturn'
]
All the different types of expressions in our language. The basic unit of
CoffeeScript is the Expression – everything that can be an expression
is one. Blocks serve as the building blocks of many other rules, making
them somewhat circular.
Expression: [
o 'Value'
o 'Code'
o 'Operation'
o 'Assign'
o 'If'
o 'Try'
o 'While'
o 'For'
o 'Switch'
o 'Class'
o 'Throw'
o 'Yield'
]
Expressions which are written in single line and would otherwise require being
wrapped in braces: E.g a = b if do -> f a is 1, if f (a) -> a*2 then ...,
for x in do (obj) -> f obj when x > 8 then f x
ExpressionLine: [
o 'CodeLine'
o 'IfLine'
o 'OperationLine'
]
Yield: [
o 'YIELD', ->new Op $1, new Value new Literal ''
o 'YIELD Expression', ->new Op $1, $2
o 'YIELD INDENT Object OUTDENT', ->new Op $1, $3
o 'YIELD FROM Expression', ->new Op $1.concat($2), $3
]
ObjRestValue: [
o 'SimpleObjAssignable ...', ->new Splat new Value $1
o '... SimpleObjAssignable', ->new Splat new Value($2), postfix: no
o 'ObjSpreadExpr ...', ->new Splat $1
o '... ObjSpreadExpr', ->new Splat $2, postfix: no
]
ObjSpreadExpr: [
o 'ObjSpreadIdentifier'
o 'Object'
o 'Parenthetical'
o 'Super'
o 'This'
o 'SUPER OptFuncExist Arguments', ->new SuperCall LOC(1)(new Super), $3, $2.soak, $1
o 'DYNAMIC_IMPORT Arguments', ->new DynamicImportCall LOC(1)(new DynamicImport), $2
o 'SimpleObjAssignable OptFuncExist Arguments', ->new Call (new Value $1), $3, $2.soak
o 'ObjSpreadExpr OptFuncExist Arguments', ->new Call $1, $3, $2.soak
]
ObjSpreadIdentifier: [
o 'SimpleObjAssignable Accessor', -> (new Value $1).add $2
o 'ObjSpreadExpr Accessor', -> (new Value $1).add $2
]
The types of things that can be treated as values – assigned to, invoked
as functions, indexed into, named as a class, etc.
Value: [
o 'Assignable'
o 'Literal', ->new Value $1
o 'Parenthetical', ->new Value $1
o 'Range', ->new Value $1
o 'Invocation', ->new Value $1
o 'DoIife', ->new Value $1
o 'This'
o 'Super', ->new Value $1
o 'MetaProperty', ->new Value $1
]
Range: [
o '[ Expression RangeDots Expression ]', ->new Range $2, $4, if $3.exclusive then'exclusive'else'inclusive'
o '[ ExpressionLine RangeDots Expression ]', ->new Range $2, $4, if $3.exclusive then'exclusive'else'inclusive'
]
Slice: [
o 'Expression RangeDots Expression', ->new Range $1, $3, if $2.exclusive then'exclusive'else'inclusive'
o 'Expression RangeDots', ->new Range $1, null, if $2.exclusive then'exclusive'else'inclusive'
o 'ExpressionLine RangeDots Expression', ->new Range $1, $3, if $2.exclusive then'exclusive'else'inclusive'
o 'ExpressionLine RangeDots', ->new Range $1, null, if $2.exclusive then'exclusive'else'inclusive'
o 'RangeDots Expression', ->new Range null, $2, if $1.exclusive then'exclusive'else'inclusive'
o 'RangeDots', ->new Range null, null, if $1.exclusive then'exclusive'else'inclusive'
]
Just simple, comma-separated, required arguments (no fancy syntax). We need
this to be separate from the ArgList for use in Switch blocks, where
having the newlines wouldn’t make sense.
SimpleArgs: [
o 'Expression'
o 'ExpressionLine'
o 'SimpleArgs , Expression', -> [].concat $1, $3
o 'SimpleArgs , ExpressionLine', -> [].concat $1, $3
]
Parenthetical expressions. Note that the Parenthetical is a Value,
not an Expression, so if you need to use an expression in a place
where only values are accepted, wrapping it in parentheses will always do
the trick.
Parenthetical: [
o '( Body )', ->new Parens $2
o '( INDENT Body OUTDENT )', ->new Parens $3
]
WhileLineSource: [
o 'WHILE ExpressionLine', ->new While $2
o 'WHILE ExpressionLine WHEN ExpressionLine', ->new While $2, guard: $4
o 'UNTIL ExpressionLine', ->new While $2, invert: true
o 'UNTIL ExpressionLine WHEN ExpressionLine', ->new While $2, invert: true, guard: $4
]
WhileSource: [
o 'WHILE Expression', ->new While $2
o 'WHILE Expression WHEN Expression', ->new While $2, guard: $4
o 'WHILE ExpressionLine WHEN Expression', ->new While $2, guard: $4
o 'UNTIL Expression', ->new While $2, invert: true
o 'UNTIL Expression WHEN Expression', ->new While $2, invert: true, guard: $4
o 'UNTIL ExpressionLine WHEN Expression', ->new While $2, invert: true, guard: $4
]
Array, object, and range comprehensions, at the most generic level.
Comprehensions can either be normal, with a block of expressions to execute,
or postfix, with a single expression.
For: [
o 'Statement ForBody', -> $2.postfix = yes; $2.addBody $1
o 'Expression ForBody', -> $2.postfix = yes; $2.addBody $1
o 'ForBody Block', -> $1.addBody $2
o 'ForLineBody Block', -> $1.addBody $2
]
ForBody: [
o 'FOR Range', ->new For [], source: (LOC(2) new Value($2))
o 'FOR Range BY Expression', ->new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForSource', -> $1.addSource $2
]
ForLineBody: [
o 'FOR Range BY ExpressionLine', ->new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForLineSource', -> $1.addSource $2
]
ForStart: [
o 'FOR ForVariables', ->new For [], name: $2[0], index: $2[1]
o 'FOR AWAIT ForVariables', ->
[name, index] = $3new For [], {name, index, await: yes, awaitTag: (LOC(2) new Literal($2))}
o 'FOR OWN ForVariables', ->
[name, index] = $3new For [], {name, index, own: yes, ownTag: (LOC(2) new Literal($2))}
]
An array or range comprehension has variables for the current element
and (optional) reference to the current index. Or, key, value, in the case
of object comprehensions.
ForVariables: [
o 'ForValue', -> [$1]
o 'ForValue , ForValue', -> [$1, $3]
]
The source of a comprehension is an array or object with an optional guard
clause. If it’s an array comprehension, you can also choose to step through
in fixed-size increments.
ForSource: [
o 'FORIN Expression', -> source: $2
o 'FOROF Expression', -> source: $2, object: yes
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FORIN ExpressionLine WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN ExpressionLine BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORFROM Expression', -> source: $2, from: yes
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
o 'FORFROM ExpressionLine WHEN Expression', -> source: $2, guard: $4, from: yes
]
ForLineSource: [
o 'FORIN ExpressionLine', -> source: $2
o 'FOROF ExpressionLine', -> source: $2, object: yes
o 'FORIN Expression WHEN ExpressionLine', -> source: $2, guard: $4
o 'FORIN ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4
o 'FOROF Expression WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY ExpressionLine', -> source: $2, step: $4
o 'FORIN ExpressionLine BY ExpressionLine', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORFROM ExpressionLine', -> source: $2, from: yes
o 'FORFROM Expression WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
o 'FORFROM ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
]
Switch: [
o 'SWITCH Expression INDENT Whens OUTDENT', ->new Switch $2, $4
o 'SWITCH ExpressionLine INDENT Whens OUTDENT', ->new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', ->new Switch $2, $4, LOC(5,6) $6
o 'SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT', ->new Switch $2, $4, LOC(5,6) $6
o 'SWITCH INDENT Whens OUTDENT', ->new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', ->new Switch null, $3, LOC(4,5) $5
]
Whens: [
o 'When', -> [$1]
o 'Whens When', -> $1.concat $2
]
Arithmetic and logical operators, working on one or more operands.
Here they are grouped by order of precedence. The actual precedence rules
are defined at the bottom of the page. It would be shorter if we could
combine most of these rules into a single generic Operand OpSymbol Operand
-type rule, but in order to make the precedence binding possible, separate
rules are necessary.
OperationLine: [
o 'UNARY ExpressionLine', ->new Op $1, $2
o 'DO ExpressionLine', ->new Op $1, $2
o 'DO_IIFE CodeLine', ->new Op $1, $2
]
Operation: [
o 'UNARY Expression', ->new Op $1.toString(), $2, undefined, undefined, originalOperator: $1.original
o 'DO Expression', ->new Op $1, $2
o 'UNARY_MATH Expression', ->new Op $1, $2
o '- Expression', (->new Op '-', $2), prec: 'UNARY_MATH'
o '+ Expression', (->new Op '+', $2), prec: 'UNARY_MATH'
o 'AWAIT Expression', ->new Op $1, $2
o 'AWAIT INDENT Object OUTDENT', ->new Op $1, $3
o '-- SimpleAssignable', ->new Op '--', $2
o '++ SimpleAssignable', ->new Op '++', $2
o 'SimpleAssignable --', ->new Op '--', $1, null, true
o 'SimpleAssignable ++', ->new Op '++', $1, null, true
Finally, now that we have our grammar and our operators, we can create
our Jison.Parser. We do this by processing all of our rules, recording all
terminals (every symbol which does not appear as the name of a rule above)
as “tokens”.
tokens = []
for name, alternatives of grammar
grammar[name] = for alt in alternatives
for token in alt[0].split ' '
tokens.push token unless grammar[token]
alt[1] = "return #{alt[1]}"if name is'Root'
alt
Initialize the Parser with our list of terminal tokens, our grammar
rules, and the name of the root. Reverse the operators because Jison orders
precedence from low to high, and we have it high to low
(as in Yacc).
This file contains the common helper functions that we’d like to share among
the Lexer, Rewriter, and the Nodes. Merge objects, flatten
arrays, count characters, that sort of thing.
Merge objects, returning a fresh copy with attributes from both sides.
Used every time Base#compile is called, to allow properties in the
options hash to propagate down the tree without polluting other branches.
Helper function for extracting code from Literate CoffeeScript by stripping
out all non-code blocks, producing a string of CoffeeScript code that can
be compiled “normally.”
exports.invertLiterate = (code) ->
out = []
blankLine = /^\s*$/
indented = /^[\t ]/
listItemStart = /// ^
(?:\t?|\ {0,3}) # Up to one tab, or up to three spaces, or neither;
(?:
[\*\-\+] | # followed by `*`, `-` or `+`;
[0-9]{1,9}\. # or by an integer up to 9 digits long, followed by a period;
)
[\ \t] # followed by a space or a tab.
///
insideComment = nofor line in code.split('\n')
if blankLine.test(line)
insideComment = no
out.push line
elseif insideComment or listItemStart.test(line)
insideComment = yes
out.push "# #{line}"elseifnot insideComment and indented.test(line)
out.push line
else
insideComment = yes
out.push "# #{line}"
out.join '\n'
exports.extractAllCommentTokens = (tokens) ->
allCommentsObj = {}
for token in tokens when token.comments
for comment in token.comments
commentKey = comment.locationData.range[0]
allCommentsObj[commentKey] = comment
sortedKeys = Object.keys(allCommentsObj).sort (a, b) -> a - b
for key in sortedKeys
allCommentsObj[key]
Get a lookup hash for a token based on its location data.
Multiple tokens might have the same location hash, but using exclusive
location data distinguishes e.g. zero-length generated tokens from
actual source tokens.
Multiple tokens might have the same location hash, such as the generated
JS tokens added at the start or end of the token stream to hold
comments that start or end a file.
tokenData[tokenHash] ?= {}
if token.comments # `comments` is always an array.
For “overlapping” tokens, that is tokens with the same location data
and therefore matching tokenHashes, merge the comments from both/all
tokens together into one array, even if there are duplicate comments;
they will get sorted out later.
This returns a function which takes an object as a parameter, and if that
object is an AST node, updates that object’s locationData.
The object is returned either way.
Throws a SyntaxError from a given location.
The error’s toString will return an error message following the “standard”
format <filename>:<line>:<col>: <message> plus the line with the error and a
marker showing where the error is.
Instead of showing the compiler’s stacktrace, show our custom error message
(this is useful when the error bubbles up in Node.js applications that
compile CoffeeScript for example).
end = if first_line is last_line then last_column + 1else codeLine.length
marker = codeLine[...start].replace(/[^\s]/g, ' ') + repeat('^', end - start)
Pass a reference to Babel into the compiler, so that the transpile option
is available in the Node API. We need to do this so that tools like Webpack
can require('coffeescript') and build correctly, without trying to
require Babel.
if options?.transpile
options.transpile.transpile = CoffeeScript.transpile
universalCompile.call CoffeeScript, code, options
dir = if options.filename?
path.dirname fs.realpathSync options.filename
else
fs.realpathSync '.'
mainModule.paths = require('module')._nodeModulePaths dir
use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
_require.resolve = (request) -> Module._resolveFilename request, _module
o = {}
o[k] = v for own k, v of options
o.bare = on# ensure return value
js = CoffeeScript.compile code, o
if sandbox is global
vm.runInThisContext js
else
vm.runInContext js, sandbox
CoffeeScript.register = ->require'./register'
As the filename and code of a dynamically loaded file will be different
from the original file compiled with CoffeeScript.run, add that
information to error so it can be pretty-printed later.
Explicitly define all named exports so that Node’s automatic detection of
named exports from CommonJS packages finds all of them. This enables consuming
packages to write code like import { compile } from 'coffeescript'.
Don’t simplify this into a loop or similar; the module.exports.name part is
essential for Node’s algorithm to successfully detect the name.
The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
matches against the beginning of the source code. When a match is found,
a token is produced, we consume the match, and start again. Tokens are in the
form:
[tag, value, locationData]
where locationData is {first_line, first_column, last_line, last_column, last_line_exclusive, last_column_exclusive}, which is a
format that can be fed directly into Jison. These
are read by jison in the parser.lexer function defined in coffeescript.coffee.
The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
tokens. Some potential ambiguity in the grammar has been avoided by
pushing some extra smarts into the Lexer.
tokenize is the Lexer’s main method. Scan by attempting to match tokens
one at a time, using a regular expression anchored at the start of the
remaining code, or a custom recursive token-matching method
(for interpolations). When the next token has been recorded, we move forward
within the code past the token, and begin again.
Each tokenizing method is responsible for returning the number of characters
it has consumed.
Before returning the token stream, run it through the Rewriter.
tokenize: (code, opts = {}) ->
@literate = opts.literate # Are we lexing literate CoffeeScript?
@indent = 0# The current indentation level.
@baseIndent = 0# The overall minimum indentation level.
@continuationLineAdditionalIndent = 0# The over-indentation at the current level.
@outdebt = 0# The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@indentLiteral = ''# The indentation.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
@seenFor = no# Used to recognize `FORIN`, `FOROF` and `FORFROM` tokens.
@seenImport = no# Used to recognize `IMPORT FROM? AS?` tokens.
@seenExport = no# Used to recognize `EXPORT FROM? AS?` tokens.
@importSpecifierList = no# Used to identify when in an `IMPORT {...} FROM? ...`.
@exportSpecifierList = no# Used to identify when in an `EXPORT {...} FROM? ...`.
@jsxDepth = 0# Used to optimize JSX checks, how deep in JSX we are.
@jsxObjAttribute = {} # Used to detect if JSX attributes is wrapped in {} (<div {props...} />).
@chunkLine =
opts.line or0# The start line for the current @chunk.
@chunkColumn =
opts.column or0# The start column of the current @chunk.
@chunkOffset =
opts.offset or0# The start offset for the current @chunk.
@locationDataCompensations =
opts.locationDataCompensations or {} # The location data compensations for the current @chunk.
code = @clean code # The stripped, cleaned original source code.
At every position, run through this list of attempted matches,
short-circuiting if any of them succeed. Their order determines precedence:
@literalToken is the fallback catch-all.
i = 0while @chunk = code[i..]
consumed = \
@identifierToken() or
@commentToken() or
@whitespaceToken() or
@lineToken() or
@stringToken() or
@numberToken() or
@jsxToken() or
@regexToken() or
@jsToken() or
@literalToken()
Preprocess the code to remove leading and trailing whitespace, carriage
returns, etc. If we’re lexing literate CoffeeScript, strip external Markdown
by removing all lines that aren’t indented by at least four spaces or a tab.
Matches identifying literals: variables, keywords, method names, etc.
Check to ensure that JavaScript reserved words aren’t being used as
identifiers. Because CoffeeScript reserves a handful of keywords that are
allowed in JavaScript, we’re careful not to tag them as keywords when
referenced as property names here, so you can still do jQuery.is() even
though is means === otherwise.
identifierToken: ->
inJSXTag = @atJSXTag()
regex = if inJSXTag then JSX_ATTRIBUTE else IDENTIFIER
return0unless match = regex.exec @chunk
[input, id, colon] = match
idLength = id.length
poppedToken = undefinedif id is'own'and @tag() is'FOR'
@token 'OWN', id
return id.length
if id is'from'and @tag() is'YIELD'
@token 'FROM', id
return id.length
if id is'as'and @seenImport
if @value() is'*'
@tokens[@tokens.length - 1][0] = 'IMPORT_ALL'elseif @value(yes) in COFFEE_KEYWORDS
prev = @prev()
[prev[0], prev[1]] = ['IDENTIFIER', @value(yes)]
if @tag() in ['DEFAULT', 'IMPORT_ALL', 'IDENTIFIER']
@token 'AS', id
return id.length
if id is'as'and @seenExport
if @tag() in ['IDENTIFIER', 'DEFAULT']
@token 'AS', id
return id.length
if @value(yes) in COFFEE_KEYWORDS
prev = @prev()
[prev[0], prev[1]] = ['IDENTIFIER', @value(yes)]
@token 'AS', id
return id.length
if id is'default'and @seenExport and @tag() in ['EXPORT', 'AS']
@token 'DEFAULT', id
return id.length
if id is'assert'and (@seenImport or @seenExport) and @tag() is'STRING'
@token 'ASSERT', id
return id.length
if id is'do'and regExSuper = /^(\s*super)(?!\(\))/.exec @chunk[3...]
@token 'SUPER', 'super'
@token 'CALL_START', '('
@token 'CALL_END', ')'
[input, sup] = regExSuper
return sup.length + 3
prev = @prev()
tag =
if colon or prev? and
(prev[0] in ['.', '?.', '::', '?::'] ornot prev.spaced and prev[0] is'@')
'PROPERTY'else'IDENTIFIER'
tokenData = {}
if tag is'IDENTIFIER'and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS) andnot (@exportSpecifierList and id in COFFEE_KEYWORDS)
tag = id.toUpperCase()
if tag is'WHEN'and @tag() in LINE_BREAK
tag = 'LEADING_WHEN'elseif tag is'FOR'
@seenFor = {endsLength: @ends.length}
elseif tag is'UNLESS'
tag = 'IF'elseif tag is'IMPORT'
@seenImport = yeselseif tag is'EXPORT'
@seenExport = yeselseif tag in UNARY
tag = 'UNARY'elseif tag in RELATION
if tag isnt'INSTANCEOF'and @seenFor
tag = 'FOR' + tag
@seenFor = noelse
tag = 'RELATION'if @value() is'!'
poppedToken = @tokens.pop()
tokenData.invert = poppedToken.data?.original ? poppedToken[1]
elseif tag is'IDENTIFIER'and @seenFor and id is'from'and
isForFrom(prev)
tag = 'FORFROM'
@seenFor = no
Throw an error on attempts to use get or set as keywords, or
what CoffeeScript would normally interpret as calls to functions named
get or set, i.e. get({foo: function () {}}).
elseif tag is'PROPERTY'and prev
if prev.spaced and prev[0] in CALLABLE and/^[gs]et$/.test(prev[1]) and
@tokens.length > 1and @tokens[@tokens.length - 2][0] notin ['.', '?.', '@']
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call
without parentheses", prev[2]
elseif prev[0] is'.'and @tokens.length > 1and (prevprev = @tokens[@tokens.length - 2])[0] is'UNARY'and prevprev[1] is'new'
prevprev[0] = 'NEW_TARGET'elseif prev[0] is'.'and @tokens.length > 1and (prevprev = @tokens[@tokens.length - 2])[0] is'IMPORT'and prevprev[1] is'import'
@seenImport = no
prevprev[0] = 'IMPORT_META'elseif @tokens.length > 2
prevprev = @tokens[@tokens.length - 2]
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and/^[gs]et$/.test(prevprev[1]) and
@tokens[@tokens.length - 3][0] notin ['.', '?.', '@']
@error "'#{prevprev[1]}' cannot be used as a keyword, or as a
function call without parentheses", prevprev[2]
if tag is'IDENTIFIER'and id in RESERVED andnot inJSXTag
@error "reserved word '#{id}'", length: id.length
unless tag is'PROPERTY'or @exportSpecifierList or @importSpecifierList
if id in COFFEE_ALIASES
alias = id
id = COFFEE_ALIAS_MAP[id]
tokenData.original = alias
tag = switch id
when'!'then'UNARY'when'==', '!='then'COMPARE'when'true', 'false'then'BOOL'when'break', 'continue', \
'debugger'then'STATEMENT'when'&&', '||'then id
else tag
tagToken = @token tag, id, length: idLength, data: tokenData
tagToken.origin = [tag, alias, tagToken[2]] if alias
if poppedToken
[tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] =
[poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]]
if colon
colonOffset = input.lastIndexOf if inJSXTag then'='else':'
colonToken = @token ':', ':', offset: colonOffset
colonToken.jsxColon = yesif inJSXTag # used by rewriterif inJSXTag and tag is'IDENTIFIER'and prev[0] isnt':'
@token ',', ',', length: 0, origin: tagToken, generated: yes
input.length
Matches numbers, including decimals, hex, and exponential notation.
Be careful not to interfere with ranges in progress.
numberToken: ->return0unless match = NUMBER.exec @chunk
number = match[0]
lexedLength = number.length
switchwhen/^0[BOX]/.test number
@error "radix prefix in '#{number}' must be lowercase", offset: 1when/^0\d*[89]/.test number
@error "decimal literal '#{number}' must not be prefixed with '0'", length: lexedLength
when/^0\d+/.test number
@error "octal literal '#{number}' must be prefixed with '0o'", length: lexedLength
parsedValue = parseNumber number
tokenData = {parsedValue}
tag = if parsedValue isInfinitythen'INFINITY'else'NUMBER'if tag is'INFINITY'
tokenData.original = number
@token tag, number,
length: lexedLength
data: tokenData
lexedLength
Matches and consumes comments. The comments are taken out of the token
stream and saved for later, to be reinserted into the output after
everything has been parsed and the JavaScript code generated.
commentToken: (chunk = @chunk, {heregex, returnCommentTokens = no, offsetInChunk = 0} = {}) ->return0unless match = chunk.match COMMENT
[commentWithSurroundingWhitespace, hereLeadingWhitespace, hereComment, hereTrailingWhitespace, lineComment] = match
contents = null
The COMMENT regex captures successive line comments as one token.
Remove any leading newlines before the first comment, but preserve
blank lines between line comments.
leadingNewlines = ''
content = lineComment.replace /^(\n*)/, (leading) ->
leadingNewlines = leading
''
precedingNonCommentLines = ''
hasSeenFirstCommentLine = no
contents =
content.split '\n'
.map (line, index) ->
unless line.indexOf('#') > -1
precedingNonCommentLines += "\n#{line}"return
leadingWhitespace = ''
content = line.replace /^([ |\t]*)#/, (_, whitespace) ->
leadingWhitespace = whitespace
''
comment = {
content
length: '#'.length + content.length
leadingWhitespace: "#{unless hasSeenFirstCommentLine then leadingNewlines else''}#{precedingNonCommentLines}#{leadingWhitespace}"
precededByBlankLine: !!precedingNonCommentLines
}
hasSeenFirstCommentLine = yes
precedingNonCommentLines = ''
comment
.filter (comment) -> comment
getIndentSize = ({leadingWhitespace, nonInitial}) ->
lastNewlineIndex = leadingWhitespace.lastIndexOf '\n'if hereComment? ornot nonInitial
returnnullunless lastNewlineIndex > -1else
lastNewlineIndex ?= -1
leadingWhitespace.length - 1 - lastNewlineIndex
commentAttachments = for {content, length, leadingWhitespace, precededByBlankLine}, i in contents
nonInitial = i isnt0
leadingNewlineOffset = if nonInitial then1else0
offsetInChunk += leadingNewlineOffset + leadingWhitespace.length
indentSize = getIndentSize {leadingWhitespace, nonInitial}
noIndent = not indentSize? or indentSize is-1
commentAttachment = {
content
here: hereComment?
newLine: leadingNewline or nonInitial # Line comments after the first one start new lines, by definition.
locationData: @makeLocationData {offsetInChunk, length}
precededByBlankLine
indentSize
indented: not noIndent and indentSize > @indent
outdented: not noIndent and indentSize < @indent
}
commentAttachment.heregex = yesif heregex
offsetInChunk += length
commentAttachment
prev = @prev()
unless prev
Matches regular expression literals, as well as multiline extended ones.
Lexing regular expressions is difficult to distinguish from division, so we
borrow some basic heuristics from JavaScript and Ruby.
regexToken: ->switchwhen match = REGEX_ILLEGAL.exec @chunk
@error "regular expressions cannot begin with #{match[2]}",
offset: match.index + match[1].length
when match = @matchWithInterpolations HEREGEX, '///'
{tokens, index} = match
comments = []
while matchedComment = HEREGEX_COMMENT.exec @chunk[0...index]
{index: commentIndex} = matchedComment
[fullMatch, leadingWhitespace, comment] = matchedComment
comments.push {comment, offsetInChunk: commentIndex + leadingWhitespace.length}
commentTokens = flatten(
for commentOpts in comments
@commentToken commentOpts.comment, Object.assign commentOpts, heregex: yes, returnCommentTokens: yes
)
when match = REGEX.exec @chunk
[regex, body, closed] = match
@validateEscapes body, isRegex: yes, offsetInChunk: 1
index = regex.length
prev = @prev()
if prev
if prev.spaced and prev[0] in CALLABLE
return0ifnot closed or POSSIBLY_DIVISION.test regex
elseif prev[0] in NOT_REGEX
return0
@error 'missing / (unclosed regex)'unless closed
elsereturn0
[flags] = REGEX_FLAGS.exec @chunk[index..]
end = index + flags.length
origin = @makeToken 'REGEX', null, length: end
switchwhennot VALID_FLAGS.test flags
@error "invalid regular expression flags #{flags}", offset: index, length: flags.length
when regex or tokens.length is1
delimiter = if body then'/'else'///'
body ?= tokens[0][1]
@validateUnicodeCodePointEscapes body, {delimiter}
@token 'REGEX', "/#{body}/#{flags}", {length: end, origin, data: {delimiter}}
else
@token 'REGEX_START', '(', {length: 0, origin, generated: yes}
@token 'IDENTIFIER', 'RegExp', length: 0, generated: yes
@token 'CALL_START', '(', length: 0, generated: yes
@mergeInterpolationTokens tokens, {double: yes, heregex: {flags}, endOffset: end - flags.length, quote: '///'}, (str) =>
@validateUnicodeCodePointEscapes str, {delimiter}
if flags
@token ',', ',', offset: index - 1, length: 0, generated: yes
@token 'STRING', '"' + flags + '"', offset: index, length: flags.length
@token ')', ')', offset: end, length: 0, generated: yes
@token 'REGEX_END', ')', offset: end, length: 0, generated: yes
Matches newlines, indents, and outdents, and determines which is which.
If we can detect that the current line is continued onto the next line,
then the newline is suppressed:
elements
.each( ... )
.map( ... )
Keeps track of the level of indentation, because a single outdent token
can close multiple indents, so we need to know how far in we happen to be.
Matches and consumes non-meaningful whitespace. Tag the previous token
as being “spaced”, because there are some cases where it makes a difference.
whitespaceToken: ->return0unless (match = WHITESPACE.exec @chunk) or
(nline = @chunk.charAt(0) is'\n')
prev = @prev()
prev[if match then'spaced'else'newLine'] = trueif prev
if match then match[0].length else0
Check the previous token to detect if attribute is spread.
prevChar = if @tokens.length > 0then @tokens[@tokens.length - 1][0] else''if firstChar is'<'
match = JSX_IDENTIFIER.exec(@chunk[1...]) or JSX_FRAGMENT_IDENTIFIER.exec(@chunk[1...])
return0unless match and (
@jsxDepth > 0or
This token represents the start of a JSX attribute value
that’s an expression (e.g. the {b} in <div a={b} />).
Our grammar represents the beginnings of expressions as (
tokens, so make this into a ( token that displays as {.
token = @token '(', '{'
@jsxObjAttribute[@jsxDepth] = no
We treat all other single characters as a token. E.g.: ( ) , . !
Multi-character operators are also literal tokens, so that Jison can assign
the proper order of operations. There are some symbols that we tag specially
here. ; and newlines are both treated as a TERMINATOR, we distinguish
parentheses that indicate a method call from regular parentheses, and so on.
literalToken: ->if match = OPERATOR.exec @chunk
[value] = match
@tagParameters() if CODE.test value
else
value = @chunk.charAt 0
tag = value
prev = @prev()
if prev and value in ['=', COMPOUND_ASSIGN...]
skipToken = falseif value is'='and prev[1] in ['||', '&&'] andnot prev.spaced
prev[0] = 'COMPOUND_ASSIGN'
prev[1] += '='
prev.data.original += '='if prev.data?.original
prev[2].range = [
prev[2].range[0]
prev[2].range[1] + 1
]
prev[2].last_column += 1
prev[2].last_column_exclusive += 1
prev = @tokens[@tokens.length - 2]
skipToken = trueif prev and prev[0] isnt'PROPERTY'
origin = prev.origin ? prev
message = isUnassignable prev[1], origin[1]
@error message, origin[2] if message
return value.length if skipToken
if value is'('and prev?[0] is'IMPORT'
prev[0] = 'DYNAMIC_IMPORT'if value is'{'and @seenImport
@importSpecifierList = yeselseif @importSpecifierList and value is'}'
@importSpecifierList = noelseif value is'{'and prev?[0] is'EXPORT'
@exportSpecifierList = yeselseif @exportSpecifierList and value is'}'
@exportSpecifierList = noif value is';'
@error 'unexpected ;'if prev?[0] in ['=', UNFINISHED...]
@seenFor = @seenImport = @seenExport = no
tag = 'TERMINATOR'elseif value is'*'and prev?[0] is'EXPORT'
tag = 'EXPORT_ALL'elseif value in MATH then tag = 'MATH'elseif value in COMPARE then tag = 'COMPARE'elseif value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'elseif value in UNARY then tag = 'UNARY'elseif value in UNARY_MATH then tag = 'UNARY_MATH'elseif value in SHIFT then tag = 'SHIFT'elseif value is'?'and prev?.spaced then tag = 'BIN?'elseif prev
if value is'('andnot prev.spaced and prev[0] in CALLABLE
prev[0] = 'FUNC_EXIST'if prev[0] is'?'
tag = 'CALL_START'elseif value is'['and ((prev[0] in INDEXABLE andnot prev.spaced) or
(prev[0] is'::')) # `.prototype` can’t be a method you can call.
tag = 'INDEX_START'switch prev[0]
when'?'then prev[0] = 'INDEX_SOAK'
token = @makeToken tag, value
switch value
when'(', '{', '['then @ends.push {tag: INVERSES[value], origin: token}
when')', '}', ']'then @pair value
@tokens.push @makeToken tag, value
value.length
A source of ambiguity in our grammar used to be parameter lists in function
definitions versus argument lists in function calls. Walk backwards, tagging
parameters specially in order to make things easier for the parser.
tagParameters: ->return @tagDoIife() if @tag() isnt')'
stack = []
{tokens} = this
i = tokens.length
paramEndToken = tokens[--i]
paramEndToken[0] = 'PARAM_END'while tok = tokens[--i]
switch tok[0]
when')'
stack.push tok
when'(', 'CALL_START'if stack.length then stack.pop()
elseif tok[0] is'('
tok[0] = 'PARAM_START'return @tagDoIife i - 1else
paramEndToken[0] = 'CALL_END'return this
this
Match the contents of a delimited token and expand variables and expressions
inside it using Ruby-like notation for substitution of arbitrary
expressions.
"Hello #{name.capitalize()}."
If it encounters an interpolation, this method will recursively create a new
Lexer and tokenize until the { of #{ is balanced with a }.
regex matches the contents of a token (but not delimiter, and not
#{ if interpolations are desired).
delimiter is the delimiter of the token. Examples are ', ", ''',
""" and ///.
closingDelimiter is different from delimiter only in JSX
interpolators matches the start of an interpolation, for JSX it’s both
{ and < (i.e. nested JSX tag)
This method allows us to have strings within interpolations within strings,
ad infinitum.
Merge the array tokens of the fake token types 'TOKENS' and 'NEOSTRING'
(as returned by matchWithInterpolations) into the token stream. The value
of 'NEOSTRING's are converted using fn and turned into strings using
options first.
mergeInterpolationTokens: (tokens, options, fn) ->
{quote, indent, double, heregex, endOffset, jsx} = options
if tokens.length > 1
lparen = @token 'STRING_START', '(', length: quote?.length ? 0, data: {quote}, generated: not quote?.length
firstIndex = @tokens.length
$ = tokens.length - 1for token, i in tokens
[tag, value] = token
switch tag
when'TOKENS'
Use the same location data as the first parenthesis.
placeholderToken[2] = value[0][2]
for val in value when val.comments
placeholderToken.comments ?= []
placeholderToken.comments.push val.comments...
value.splice 1, 0, placeholderToken
Compensate for the things we strip out initially (e.g. carriage returns)
so that location data stays accurate with respect to the original source file.
getLocationDataCompensation: (start, end) ->
totalCompensation = 0
initialEnd = end
current = start
while current <= end
breakif current is end and start isnt initialEnd
compensation = @locationDataCompensations[current]
if compensation?
totalCompensation += compensation
end += compensation
current++
return totalCompensation
Use length - 1 for the final offset - we’re supplying the last_line and the last_column,
so if last_column == first_column, then we’re looking at a character of length 1.
Add a token to the results.
offset is the offset into the current @chunk where the token starts.
length is the length of the token in the @chunk, after the offset. If
not specified, the length of value will be used.
Returns the new token.
token: (tag, value, {offset, length, origin, data, generated, indentSize} = {}) ->
token = @makeToken tag, value, {offset, length, origin, generated, indentSize}
addTokenData token, data if data
@tokens.push token
token
isUnassignable = (name, displayName = name) ->switchwhen name in [JS_KEYWORDS..., COFFEE_KEYWORDS...]
"keyword '#{displayName}' can't be assigned"when name in STRICT_PROSCRIBED
"'#{displayName}' can't be assigned"when name in RESERVED
"reserved word '#{displayName}' can't be assigned"elsefalseexports.isUnassignable = isUnassignable
from isn’t a CoffeeScript keyword, but it behaves like one in import and
export statements (handled above) and in the declaration line of a for
loop. Try to detect when from is a variable identifier and when it is this
“sometimes” keyword.
The list of keywords that are reserved by JavaScript, but not used, or are
used by CoffeeScript internally. We throw an error when these are encountered,
to avoid having a JavaScript error at runtime.
In https://facebook.github.io/jsx/ spec, JSXElementName can be
JSXIdentifier, JSXNamespacedName (JSXIdentifier : JSXIdentifier), or
JSXMemberExpression (two or more JSXIdentifier connected by .s).
Tokens which could legitimately be invoked or indexed. An opening
parentheses or bracket following these tokens will be recorded as the start
of a function invocation or indexing operation.
Tokens that, when immediately preceding a WHEN, indicate that the WHEN
occurs at the start of a line. We disambiguate these from trailing whens to
avoid an ambiguity in the grammar.
nodes.coffee contains all of the node classes for the syntax tree. Most
nodes are created as the result of actions in the grammar,
but some are created by other nodes as a method of code generation. To convert
the syntax tree into a string of JavaScript code, call compile() on the root.
The various nodes defined below all compile to a collection of CodeFragment objects.
A CodeFragments is a block of generated code, and the location in the source file where the code
came from. CodeFragments can be assembled together into working code just by catting together
all the CodeFragments’ code snippets, in order.
The Base is the abstract base class for all nodes in the syntax tree.
Each subclass implements the compileNode method, which performs the
code generation for that node. To compile a node to JavaScript,
call compile on it, which wraps compileNode in some generic extra smarts,
to know when the generated code needs to be wrapped up in a closure.
An options hash is passed and cloned throughout, containing information about
the environment from higher in the tree (such as if a returned value is
being requested by the surrounding function), information about the current
scope, and indentation level.
Occasionally a node is compiled multiple times, for example to get the name
of a variable to add to scope tracking. When we know that a “premature”
compilation won’t result in comments being output, set those comments aside
so that they’re preserved for a later compile call that will result in
the comments being included in the output.
Common logic for determining whether to wrap this node in a closure before
compiling it, or to compile directly. We need to wrap if this node is a
statement, and it’s not a pureStatement, and we’re not at
the top level of a block (which would be unnecessary), and we haven’t
already been asked to return the result (because statements know how to
return results).
compileToFragments: (o, lvl) ->
o = extend {}, o
o.level = lvl if lvl
node = @unfoldSoak(o) or this
node.tab = o.indent
fragments = if o.level is LEVEL_TOP ornot node.isStatement(o)
node.compileNode o
else
node.compileClosure o
@compileCommentFragments o, node, fragments
fragments
compileToFragmentsWithoutComments: (o, lvl) ->
@compileWithoutComments o, lvl, 'compileToFragments'
This is where comments, that are attached to nodes as a comments
property, become CodeFragments. “Inline block comments,” e.g.
/* */-delimited comments that are interspersed within code on a line,
are added to the current fragments stream. All other fragments are
attached as properties to the nearest preceding or following fragment,
to remain stowaways until they get properly output in compileComments
later on.
For block/here comments, denoted by ###, that are inline comments
like 1 + ### comment ### 2, create fragments and insert them into
the fragments array.
Otherwise attach comment fragments to their closest fragment for now,
so they can be inserted into the output later after all the newlines
have been added.
if comment.here # Block comment, delimited by `###`.
commentFragment = new HereComment(comment).compileNode o
else# Line comment, delimited by `#`.
commentFragment = new LineComment(comment).compileNode o
if (commentFragment.isHereComment andnot commentFragment.newLine) or
node.includeCommentFragments()
If the code generation wishes to use the result of a complex expression
in multiple places, ensure that the expression is only ever evaluated once,
by assigning it to a temporary variable. Pass a level to precompile.
If level is passed, then returns [val, ref], where val is the compiled value, and ref
is the compiled reference. If level is not passed, this returns [val, ref] where
the two values are raw nodes which have not been compiled.
cache: (o, level, shouldCache) ->
complex = if shouldCache? then shouldCache this else @shouldCache()
if complex
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
sub = new Assign ref, this
if level then [sub.compileToFragments(o, level), [@makeCode(ref.value)]] else [sub, ref]
else
ref = if level then @compileToFragments o, level else this
[ref, ref]
Occasionally it may be useful to make an expression behave as if it was ‘hoisted’, whereby the
result of the expression is available before its location in the source, but the expression’s
variable scope corresponds to the source position. This is used extensively to deal with executable
class bodies in classes.
Calling this method mutates the node, proxying the compileNode and compileToFragments
methods to store their result for later replacing the target node, which is returned by the
call.
Does this node, or any of its children, contain a node of a certain kind?
Recursively traverses down the children nodes and returns the first one
that verifies pred. Otherwise return undefined. contains does not cross
scope boundaries.
contains: (pred) ->
node = undefined
@traverseChildren no, (n) ->if pred n
node = n
returnno
node
Debugging representation of the node, for inspecting the parse tree.
This is what coffee --nodes prints out.
toString: (idt = '', name = @constructor.name) ->
tree = '\n' + idt + name
tree += '?'if @soak
@eachChild (node) -> tree += node.toString idt + TAB
tree
checkForPureStatementInExpression: ->if jumpNode = @jumps()
jumpNode.error 'cannot use a pure statement in an expression'
Plain JavaScript object representation of the node, that can be serialized
as JSON. This is what the ast option in the Node API returns.
We try to follow the Babel AST spec
as closely as possible, for improved interoperability with other tools.
WARNING: DO NOT OVERRIDE THIS METHOD IN CHILD CLASSES.
Only override the component ast* methods as needed.
Mark AST nodes that correspond to expressions that (implicitly) return.
We can’t do this as part of astNode because we need to assemble child
nodes first before marking the parent being returned.
if @astNode? and @canBeReturned
Object.assign astNode, {returns: yes}
astNode
astInitialize: (o, level) ->
o = Object.assign {}, o
o.level = level if level?
if o.level > LEVEL_TOP
@checkForPureStatementInExpression()
@makeReturn must be called before astProperties, because the latter may call
.ast() for child nodes and those nodes would need the return logic from makeReturn
already executed by then.
@makeReturn null, yesif @isStatement(o) and o.level isnt LEVEL_TOP and o.scope?
o
astNode: (o) ->
Every abstract syntax tree node object has four categories of properties:
type, stored in the type field and a string like NumberLiteral.
location data, stored in the loc, start, end and range fields.
properties specific to this node, like parsedValue.
properties that are themselves child nodes, like body.
These fields are all intermixed in the Babel spec; type and start and
parsedValue are all top level fields in the AST node object. We have
separate methods for returning each category, that we merge together here.
Passes each child to a function, breaking when the function returns false.
eachChild: (func) ->return this unless @children
for attr in @children when @[attr]
for child in flatten [@[attr]]
return this if func(child) isfalse
this
traverseChildren: (crossScope, func) ->
@eachChild (child) ->
recur = func(child)
child.traverseChildren(crossScope, func) unless recur isno
replaceInContext will traverse children looking for a node for which match returns
true. Once found, the matching node will be replaced by the result of calling replacement.
replaceInContext: (match, replacement) ->returnfalseunless @children
for attr in @children when children = @[attr]
ifArray.isArray children
for child, i in children
if match child
children[i..i] = replacement child, @
returntrueelsereturntrueif child.replaceInContext match, replacement
elseif match children
@[attr] = replacement children, @
returntrueelsereturntrueif children.replaceInContext match, replacement
invert: ->new Op '!', this
unwrapAll: ->
node = this
continueuntil node is node = node.unwrap()
node
children are the properties to recurse into when tree walking. The
children list is the structure of the AST. The parent pointer, and
the pointer to the children are how you can traverse the tree.
isStatement has to do with “everything is an expression”. A few things
can’t be expressions, such as break. Things that isStatement returns
true for are things that can’t be used as expressions. There are some
error messages that come from nodes.coffee due to statements ending up
in expression position.
jumps tells you if an expression, or an internal part of an expression,
has a flow control construct (like break, continue, or return)
that jumps out of the normal flow of control and can’t be used as a value.
(Note that throw is not considered a flow control construct.)
This is important because flow control in the middle of an expression
makes no sense; we have to disallow it.
If node.shouldCache() is false, it is safe to use node more than once.
Otherwise you need to store the value of node in a variable and output
that variable several times instead. Kind of like this: 5 need not be
cached. returnFive(), however, could have side effects as a result of
evaluating it more than once, and therefore we need to cache it. The
parameter is named shouldCache rather than mustCache because there are
also cases where we might not need to cache but where we want to, for
example a long expression that may well be idempotent but we want to cache
for brevity.
shouldCache: YES
isChainable: NO
isAssignable: NO
isNumber: NO
unwrap: THIS
unfoldSoak: NO
fragmentsList is an array of arrays of fragments. Each array in fragmentsList will be
concatenated together, with joinStr added in between each, to produce a final flat array
of fragments.
joinFragmentArrays: (fragmentsList, joinStr) ->
answer = []
for fragments, i in fragmentsList
if i then answer.push @makeCode joinStr
answer = answer.concat fragments
answer
Update the target fragments with the result of compiling the source.
Calls the given compile function with the node and options (overriden with the target
presentational options).
Wrap everything in a safety closure, unless requested not to. It would be
better not to generate them in the first place, but for now, clean up
obvious double-parentheses.
compileNode: (o) ->
o.indent = if o.bare then''else TAB
o.level = LEVEL_TOP
o.compiling = yes
@initializeScope o
fragments = @body.compileRoot o
return fragments if o.bare
functionKeyword = "#{if @isAsync then'async 'else''}function"
[].concat @makeCode("(#{functionKeyword}() {\n"), fragments, @makeCode("\n}).call(this);\n")
initializeScope: (o) ->
o.scope = new Scope null, @body, null, o.referencedVars ? []
Mark given local variables in the root scope as parameters so they don’t
end up being declared on the root block.
o.scope.parameter name for name in o.locals or []
commentsAst: ->
@allComments ?=
for commentToken in (@allCommentTokens ? []) whennot commentToken.heregex
if commentToken.here
new HereComment commentToken
elsenew LineComment commentToken
comment.ast() for comment in @allComments
astNode: (o) ->
o.level = LEVEL_TOP
@initializeScope o
super o
astType: ->'File'
astProperties: (o) ->
@body.isRootBlock = yesreturn
program: Object.assign @body.ast(o), @astLocationData()
comments: @commentsAst()
The block is the list of expressions that forms the body of an
indented block of code – the implementation of a function, a clause in an
if, switch, or try, and so on…
isEmpty: ->not @expressions.length
isStatement: (o) ->for exp in @expressions when exp.isStatement o
returnyesno
jumps: (o) ->for exp in @expressions
return jumpNode if jumpNode = exp.jumps o
We also need to check that we’re not returning a JSX tag if there’s an
adjacent one at the same level; JSX doesn’t allow that.
if lastExp and lastExp instanceof Parens and lastExp.body.expressions.length > 1
{body:{expressions}} = lastExp
[..., penult, last] = expressions
penult = penult.unwrap()
last = last.unwrap()
if penult instanceof JSXElement and last instanceof JSXElement
expressions[expressions.length - 1].error 'Adjacent JSX elements must be wrapped in an enclosing tag'if mark
@expressions[len - 1]?.makeReturn results, mark
returnwhile len--
expr = @expressions[len]
@expressions[len] = expr.makeReturn results
@expressions.splice(len, 1) if expr instanceof Return andnot expr.expression
break
this
compile: (o, lvl) ->returnnew Root(this).withLocationDataFrom(this).compile o, lvl unless o.scope
super o, lvl
Compile all expressions within the Block body. If we need to return
the result, and it’s an expression, simply return it. If it’s a statement,
ask the statement to do so.
compileNode: (o) ->
@tab = o.indent
top = o.level is LEVEL_TOP
compiledNodes = []
for node, index in @expressions
if node.hoisted
This is a nested block. We don’t do anything special here like
enclose it in a new scope; we just compile the statements in this
block along with our own.
compiledNodes.push node.compileNode o
elseif top
node.front = yes
fragments = node.compileToFragments o
unless node.isStatement o
fragments = indentInitial fragments, @
[..., lastFragment] = fragments
unless lastFragment.code is''or lastFragment.isComment
fragments.push @makeCode ';'
compiledNodes.push fragments
else
compiledNodes.push node.compileToFragments o, LEVEL_LIST
if top
if @spaced
return [].concat @joinFragmentArrays(compiledNodes, '\n\n'), @makeCode('\n')
elsereturn @joinFragmentArrays(compiledNodes, '\n')
if compiledNodes.length
answer = @joinFragmentArrays(compiledNodes, ', ')
else
answer = [@makeCode 'void 0']
if compiledNodes.length > 1and o.level >= LEVEL_LIST then @wrapInParentheses answer else answer
compileRoot: (o) ->
@spaced = yes
fragments = @compileWithDeclarations o
HoistTarget.expand fragments
@compileComments fragments
Determine the indentation level of the fragment that we are about
to insert comments before, and use that indentation level for our
inserted comments. At this point, the fragments’ code property
is the generated output JavaScript, and CoffeeScript always
generates output indented by two spaces; so all we need to do is
search for a code property that begins with at least two spaces.
fragmentIndent = ''for pastFragment in fragments[0...(fragmentIndex + 1)] by-1
indent = /^ {2,}/m.exec pastFragment.code
if indent
fragmentIndent = indent[0]
breakelseif'\n'in pastFragment.code
break
code = "\n#{fragmentIndent}" + (
for commentFragment in fragment.precedingComments
if commentFragment.isHereComment and commentFragment.multiline
multident commentFragment.code, fragmentIndent, noelse
commentFragment.code
).join("\n#{fragmentIndent}").replace /^(\s*)$/gm, ''for pastFragment, pastFragmentIndex in fragments[0...(fragmentIndex + 1)] by-1
newLineIndex = pastFragment.code.lastIndexOf '\n'if newLineIndex is-1
Keep searching previous fragments until we can’t go back any
further, either because there are no fragments left or we’ve
discovered that we’re in a code block that is interpolated
inside a string.
Yes, this is awfully similar to the previous if block, but if you
look closely you’ll find lots of tiny differences that make this
confusing if it were abstracted into a function that both blocks share.
Find the indent of the next line of code, if we have any non-trailing
comments to output. We need to first find the next newline, as these
comments will be output after that; and then the indent of the line
that follows the next newline.
Keep searching upcoming fragments until we can’t go any
further, either because there are no fragments left or we’ve
discovered that we’re in a code block that is interpolated
inside a string.
if upcomingFragmentIndex is fragments.length - 1
upcomingFragment.code = upcomingFragment.code + '\n'
newLineIndex = upcomingFragment.code.length
elseif upcomingFragment.isStringWithInterpolations and upcomingFragment.code is'}'
code = "#{code}\n"
newLineIndex = 0elsecontinuedelete fragment.followingComments
Wrap up the given nodes as a Block, unless it already happens
to be one.
@wrap: (nodes) ->return nodes[0] if nodes.length is1and nodes[0] instanceof Block
new Block nodes
astNode: (o) ->if (o.level? and o.level isnt LEVEL_TOP) and @expressions.length
return (new Sequence(@expressions).withLocationDataFrom @).ast o
super o
astType: ->if @isRootBlock
'Program'elseif @isClassBody
'ClassBody'else'BlockStatement'
astProperties: (o) ->
checkForDirectives = del o, 'checkForDirectives'
sniffDirectives @expressions, notFinalExpression: checkForDirectives if @isRootBlock or checkForDirectives
directives = []
body = []
for expression in @expressions
expressionAst = expression.ast o
For now, we’re not including sourceType on the Program AST node.
Its value could be either 'script' or 'module', and there’s no way
for CoffeeScript to always know which it should be. The presence of an
import or export statement in source code would imply that it should
be a module, but a project may consist of mostly such files and also
an outlier file that lacks import or export but is still imported
into the project and therefore expects to be treated as a module.
Determining the value of sourceType is essentially the same challenge
posed by determining the parse goal of a JavaScript file, also module
or script, and so if Node figures out a way to do so for .js files
then CoffeeScript can copy Node’s algorithm.
Literal is a base class for static values that can be passed through
directly into JavaScript without translation, such as: strings, numbers,
true, false, null…
exports.Literal = classLiteralextendsBase
constructor: (@value) ->
super()
shouldCache: NO
assigns: (name) ->
name is @value
compileNode: (o) ->
[@makeCode @value]
astProperties: ->return
value: @value
toString: ->
StringLiterals can represent either entire literal strings
or pieces of text inside of e.g. an interpolated string.
When parsed as the former but needing to be treated as the latter
(e.g. the string part of a tagged template literal), this will return
a copy of the StringLiteral with the quotes trimmed from its location
data (like it would have if parsed as part of an interpolated string).
exports.FuncDirectiveReturn = classFuncDirectiveReturnextendsReturn
constructor: (expression, {@returnKeyword}) ->
super expression
compileNode: (o) ->
@checkScope o
super o
checkScope: (o) ->unless o.scope.parent?
@error "#{@keyword} can only occur inside functions"
isStatementAst: NO
astNode: (o) ->
@checkScope o
new Op @keyword,
new Return @expression, belongsToFuncDirectiveReturn: yes
.withLocationDataFrom(
if @expression?
locationData: mergeLocationData @returnKeyword.locationData, @expression.locationData
else
@returnKeyword
)
.withLocationDataFrom @
.ast o
A value, variable or literal or parenthesized, indexed or dotted into,
or vanilla.
exports.Value = classValueextendsBase
constructor: (base, props, tag, isDefaultValue = no) ->
super()
return base ifnot props and base instanceof Value
@base = base
@properties = props or []
@tag = tag
@[tag] = yesif tag
@isDefaultValue = isDefaultValue
A reference has base part (this value) and name part.
We cache them separately for compiling complex expressions.
a()[b()] ?= c -> (_base = a())[_name = b()] ? _base[_name] = c
cacheReference: (o) ->
[..., name] = @properties
if @properties.length < 2andnot @base.shouldCache() andnot name?.shouldCache()
return [this, this] # `a` `a.b`
base = new Value @base, @properties[...-1]
if base.shouldCache() # `a().b`
bref = new IdentifierLiteral o.scope.freeVariable 'base'
base = new Value new Parens new Assign bref, base
return [base, bref] unless name # `a()`if name.shouldCache() # `a[b()]`
nref = new IdentifierLiteral o.scope.freeVariable 'name'
name = new Index new Assign nref, name.index
nref = new Index nref
[base.add(name), new Value(bref or base.base, [nref or name])]
We compile a value to JavaScript by compiling and joining each property.
Things get much more interesting if the chain of properties has soak
operators ?. interspersed. Then we have to take care not to accidentally
evaluate anything twice when building the soak chain.
compileNode: (o) ->
@base.front = @front
props = @properties
if props.length and @base.cached?
Cached fragments enable correct order of the compilation,
and reuse of variables in the scope.
Example:
a(x = 5).b(-> x = 6) should compile in the same order as
a(x = 5); b(-> x = 6)
(see issue #4437, https://github.com/jashkenas/coffeescript/issues/4437)
fragments = @base.cached
else
fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS elsenull)
if props.length and SIMPLENUM.test fragmentsToText fragments
fragments.push @makeCode '.'for prop in props
fragments.push (prop.compileToFragments o)...
fragments
unfoldSoak: (o) ->
@unfoldedSoak ?= do =>
ifn = @base.unfoldSoak o
if ifn
ifn.body.properties.push @properties...
return ifn
for prop, i in @properties when prop.soak
prop.soak = off
fst = new Value @base, @properties[...i]
snd = new Value @base, @properties[i..]
if fst.shouldCache()
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, fst
snd.base = ref
returnnew If new Existence(fst), snd, soak: onno
eachName: (iterator, {checkAssignability = yes} = {}) ->if @hasProperties()
iterator @
elseifnot checkAssignability or @base.isAssignable()
@base.eachName iterator
else
@error 'tried to assign to unassignable value'
This new Value has multiple properties, so the location data spans
from the parent Value’s base to the last property that’s included
in this new node (a.k.a. the second-to-last property of the parent).
mergeLocationData @base.locationData, initialProperties[initialProperties.length - 1].locationData
object
containsSoak: ->returnnounless @hasProperties()
for property in @properties when property.soak
returnyesreturnyesif @base instanceof Call and @base.soak
no
astNode: (o) ->
If this Value has properties, the last property (e.g. c in a.b.c)
becomes the property, and the preceding properties (e.g. a.b) become
a child Value node assigned to the object property.
astProperties: (o) ->
[..., property] = @properties
property.name.jsx = yesif @isJSXTag()
computed = property instanceof Index or property.name?.unwrap() notinstanceof PropertyName
return {
object: @object().ast o, LEVEL_ACCESS
property: property.ast o, (LEVEL_PAREN if computed)
computed
optional: !!property.soak
shorthand: !!property.shorthand
}
astLocationData: ->return super() unless @isJSXTag()
don’t include leading < of JSX tag in location data
mergeAstLocationData(
jisonLocationDataToAstLocationData(@base.tagNameLocationData),
jisonLocationDataToAstLocationData(@properties[@properties.length - 1].locationData)
)
exports.MetaProperty = classMetaPropertyextendsBase
constructor: (@meta, @property) ->
super()
children: ['meta', 'property']
checkValid: (o) ->if @meta.value is'new'if @property instanceof Access and @property.name.value is'target'unless o.scope.parent?
@error "new.target can only occur inside functions"else
@error "the only valid meta property for new is new.target"elseif @meta.value is'import'unless @property instanceof Access and @property.name.value is'meta'
@error "the only valid meta property for import is import.meta"
compileNode: (o) ->
@checkValid o
fragments = []
fragments.push @meta.compileToFragments(o, LEVEL_ACCESS)...
fragments.push @property.compileToFragments(o)...
fragments
astProperties: (o) ->
@checkValid o
return
meta: @meta.ast o, LEVEL_ACCESS
property: @property.ast o
attribute = new JSXAttribute name: new JSXIdentifier(base.value).withLocationDataAndCommentsFrom base
attribute.locationData = base.locationData
@attributes.push attribute
elseifnot base.generated
unwrapped.locationData.range[0] is element.locationData.range[0]
unwrapped
elsenew JSXExpressionContainer unwrapped, locationData: element.locationData
child.ast(o) for child in children whennot (child instanceof JSXText and child.value.length is0)
astProperties: (o) ->Object.assign(
if @isFragment()
@fragmentAstProperties o
else
@elementAstProperties o
,
children: @contentAst o
)
astLocationData: ->if @closingElementLocationData?
mergeAstLocationData @openingElementLocationData, @closingElementLocationData
else
@openingElementLocationData
@variable never gets output as a result of this node getting created as
part of RegexWithInterpolations, so for that case move any comments to
the args property that gets passed into RegexWithInterpolations via
the grammar.
When setting the location, we sometimes need to update the start location to
account for a newly-discovered new operator to the left of us. This
expands the range on the left, but not the right.
updateLocationDataIfMissing: (locationData) ->if @locationData and @needsUpdatedStartLocation
@locationData = Object.assign {},
@locationData,
first_line: locationData.first_line
first_column: locationData.first_column
range: [
locationData.range[0]
@locationData.range[1]
]
base = @variable?.base or @variable
if base.needsUpdatedStartLocation
@variable.locationData = Object.assign {},
@variable.locationData,
first_line: locationData.first_line
first_column: locationData.first_column
range: [
locationData.range[0]
@variable.locationData.range[1]
]
base.updateLocationDataIfMissing locationData
delete @needsUpdatedStartLocation
super locationData
newInstance: ->
base = @variable?.base or @variable
if base instanceof Call andnot base.isNew
base.newInstance()
else
@isNew = true
@needsUpdatedStartLocation = true
this
Soaked chained invocations unfold into if/else ternary structures.
unfoldSoak: (o) ->if @soak
if @variable instanceof Super
left = new Literal @variable.compile o
rite = new Value left
@variable.error "Unsupported reference to 'super'"unless @variable.accessor?
elsereturn ifn if ifn = unfoldSoak o, this, 'variable'
[left, rite] = new Value(@variable).cacheReference o
rite = new Call rite, @args
rite.isNew = @isNew
left = new Literal "typeof #{ left.compile o } === \"function\""returnnew If left, new Value(rite), soak: yes
call = this
list = []
loopif call.variable instanceof Call
list.push call
call = call.variable
continuebreakunless call.variable instanceof Value
list.push call
breakunless (call = call.variable.base) instanceof Call
for call in list.reverse()
if ifn
if call.variable instanceof Call
call.variable = ifn
else
call.variable.base = ifn
ifn = unfoldSoak o, call, 'variable'
ifn
If variable is Accessor fragments are cached and used later
in Value::compileNode to ensure correct order of the compilation,
and reuse of variables in the scope.
Example:
a(x = 5).b(-> x = 6) should compile in the same order as
a(x = 5); b(-> x = 6)
(see issue #4437, https://github.com/jashkenas/coffeescript/issues/4437)
varAccess = @variable?.properties?[0] instanceof Access
argCode = (arg for arg in (@args || []) when arg instanceof Code)
if argCode.length > 0and varAccess andnot @variable.base.cached
[cache] = @variable.base.cache o, LEVEL_ACCESS, ->no
@variable.base.cached = cache
for arg, argIndex in @args
if argIndex then compiledArgs.push @makeCode ", "
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
fragments = []
if @isNew
fragments.push @makeCode 'new '
fragments.push @variable.compileToFragments(o, LEVEL_ACCESS)...
fragments.push @makeCode('('), compiledArgs..., @makeCode(')')
fragments
checkForNewSuper: ->if @isNew
@variable.error "Unsupported reference to 'super'"if @variable instanceof Super
containsSoak: ->returnyesif @soak
returnyesif @variable?.containsSoak?()
no
astNode: (o) ->if @soak and @variable instanceof Super and o.scope.namedMethod()?.ctor
@variable.error "Unsupported reference to 'super'"
@checkForNewSuper()
super o
astType: ->if @isNew
'NewExpression'elseif @containsSoak()
'OptionalCallExpression'else'CallExpression'
astProperties: (o) ->return
callee: @variable.ast o, LEVEL_ACCESS
arguments: arg.ast(o, LEVEL_LIST) for arg in @args
optional: !!@soak
implicit: !!@implicit
Takes care of converting super() calls into calls against the prototype’s
function of the same name.
When expressions are set the call will be compiled in such a way that the
expressions are evaluated without altering the return value of the SuperCall
expression.
exports.SuperCall = classSuperCallextendsCall
children: Call::children.concat ['expressions']
isStatement: (o) ->
@expressions?.length and o.level is LEVEL_TOP
compileNode: (o) ->return super o unless @expressions?.length
superCall = new Literal fragmentsToText super o
replacement = new Block @expressions.slice()
if o.level > LEVEL_TOP
If we might be in an expression we need to cache and return the result
[superCall, ref] = superCall.cache o, null, YES
replacement.push ref
replacement.unshift superCall
replacement.compileToFragments o, if o.level is LEVEL_TOP then o.level else LEVEL_LIST
exports.Super = classSuperextendsBase
constructor: (@accessor, @superLiteral) ->
super()
children: ['accessor']
compileNode: (o) ->
@checkInInstanceMethod o
method = o.scope.namedMethod()
unless method.ctor? or @accessor?
{name, variable} = method
if name.shouldCache() or (name instanceof Index and name.index.isAssignable())
nref = new IdentifierLiteral o.scope.parent.freeVariable 'name'
name.index = new Assign nref, name.index
@accessor = if nref? thennew Index nref else name
if @accessor?.name?.comments
A super() call gets compiled to e.g. super.method(), which means
the method property name gets compiled for the first time here, and
again when the method: property of the class gets compiled. Since
this compilation happens first, comments attached to method: would
get incorrectly output near super.method(), when we want them to
get output on the second pass when method: is output. So set them
aside during this compilation pass, and put them back on the object so
that they’re there for the later compilation.
salvagedComments = @accessor.name.comments
delete @accessor.name.comments
fragments = (new Value (new Literal 'super'), if @accessor then [ @accessor ] else [])
.compileToFragments o
attachCommentsToNode salvagedComments, @accessor.name if salvagedComments
fragments
checkInInstanceMethod: (o) ->
method = o.scope.namedMethod()
@error 'cannot use super outside of an instance method'unless method?.isMethod
astNode: (o) ->
@checkInInstanceMethod o
if @accessor?
return (
new Value(
new Super().withLocationDataFrom (@superLiteral ? @)
[@accessor]
).withLocationDataFrom @
).ast o
super o
Babel doesn’t have an AST node for Access, but rather just includes
this Access node’s child name Identifier node as the property of
the MemberExpression node.
Babel doesn’t have an AST node for Index, but rather just includes
this Index node’s child index Identifier node as the property of
the MemberExpression node. The fact that the MemberExpression’s
property is an Index means that computed is true for the
MemberExpression.
A range literal. Ranges can be used to extract portions (slices) of arrays,
to specify a range for comprehensions, or as a value, to be expanded into the
corresponding array of integers at runtime.
Compiles the range’s source variables – where it starts and where it ends.
But only if they need to be cached to avoid double evaluation.
compileVariables: (o) ->
o = merge o, top: true
shouldCache = del o, 'shouldCache'
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST, shouldCache
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST, shouldCache
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST, shouldCache if step = del o, 'step'
@fromNum = if @from.isNumber() then parseNumber @fromVar elsenull
@toNum = if @to.isNumber() then parseNumber @toVar elsenull
@stepNum = if step?.isNumber() then parseNumber @stepVar elsenull
An array slice literal. Unlike JavaScript’s Array#slice, the second parameter
specifies the index of the end of the slice, just as the first parameter
is the index of the beginning.
We have to be careful when trying to slice through the end of the array,
9e9 is used because not all implementations respect undefined or 1/0.
9e9 should be safe because 9e9 > 2**32, the max array length.
Handle an expression in the property access, e.g. a[!b in c..].
iffrom?.shouldCache()
from = new Value new Parens fromif to?.shouldCache()
to = new Value new Parens to
fromCompiled = from?.compileToFragments(o, LEVEL_PAREN) or [@makeCode '0']
if to
compiled = to.compileToFragments o, LEVEL_PAREN
compiledText = fragmentsToText compiled
ifnot (not @range.exclusive and +compiledText is-1)
toStr = ', ' + if @range.exclusive
compiledText
elseif to.isNumber()
"#{+compiledText + 1}"else
compiled = to.compileToFragments o, LEVEL_ACCESS
"+#{fragmentsToText compiled} + 1 || 9e9"
[@makeCode ".slice(#{ fragmentsToText fromCompiled }#{ toStr or'' })"]
astNode: (o) ->
@range.ast o
Move rest property to the end of the list.
{a, rest..., b} = obj -> {a, b, rest...} = objfoo = ({a, rest..., b}) -> -> foo = {a, b, rest...}) ->
reorderProperties: ->
props = @properties
splatProps = @getAndCheckSplatProps()
splatProp = props.splice splatProps[0], 1
@objects = @properties = [].concat props, splatProp
compileNode: (o) ->
@reorderProperties() if @hasSplat() and @lhs
props = @properties
if @generated
for node in props when node instanceof Value
node.error 'cannot have an implicit value in an implicit object'
idt = o.indent += TAB
lastNode = @lastNode @properties
If this object is the left-hand side of an assignment, all its children
are too.
@propagateLhs()
isCompact = yesfor prop in @properties
if prop instanceof Assign and prop.context is'object'
isCompact = no
answer = []
answer.push @makeCode if isCompact then''else'\n'for prop, i in props
join = if i is props.length - 1''elseif isCompact
', 'elseif prop is lastNode
'\n'else',\n'
indent = if isCompact then''else idt
key = if prop instanceof Assign and prop.context is'object'
prop.variable
elseif prop instanceof Assign
prop.operatorToken.error "unexpected #{prop.operatorToken.value}"unless @lhs
prop.variable
else
prop
if key instanceof Value and key.hasProperties()
key.error 'invalid object key'if prop.context is'object'ornot key.this
key = key.properties[0].name
prop = new Assign key, prop, 'object'if key is prop
if prop.shouldCache()
[key, value] = prop.base.cache o
key = new PropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'elseif key instanceof Value and key.base instanceof ComputedPropertyName
if prop.base.value.shouldCache()
[key, value] = prop.base.value.cache o
key = new ComputedPropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'else
{ [expression] } output as { [expression]: expression }.
prop = new Assign key, prop.base.value, 'object'elseifnot prop.bareLiteral?(IdentifierLiteral) and prop notinstanceof Splat
prop = new Assign prop, prop, 'object'if indent then answer.push @makeCode indent
answer.push prop.compileToFragments(o, LEVEL_TOP)...
if join then answer.push @makeCode join
answer.push @makeCode if isCompact then''else"\n#{@tab}"
answer = @wrapInBraces answer
if @front then @wrapInParentheses answer else answer
getAndCheckSplatProps: ->returnunless @hasSplat() and @lhs
props = @properties
splatProps = (i for prop, i in props when prop instanceof Splat)
props[splatProps[1]].error "multiple spread elements are disallowed"if splatProps?.length > 1
splatProps
assigns: (name) ->for prop in @properties when prop.assigns name thenreturnyesno
eachName: (iterator) ->for prop in @properties
prop = prop.value if prop instanceof Assign and prop.context is'object'
prop = prop.unwrapAll()
prop.eachName iterator if prop.eachName?
Let compileCommentFragments know to intersperse block comments
into the fragments created when compiling this array.
if unwrappedObj.comments and
unwrappedObj.comments.filter((comment) ->not comment.here).length is0
unwrappedObj.includeCommentFragments = YES
compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
olen = compiledObjs.length
If compiledObjs includes newlines, we will output this as a multiline
array (i.e. with a newline and indentation after the [). If an element
contains line comments, that should also trigger multiline output since
by definition line comments will introduce newlines into our output.
The exception is if only the first element has line comments; in that
case, output as the compact form if we otherwise would have, so that the
first element’s line comments get output before or after the array.
includesLineCommentsOnNonFirstElement = nofor fragments, index in compiledObjs
for fragment in fragments
if fragment.isHereComment
fragment.code = fragment.code.trim()
elseif index isnt0and includesLineCommentsOnNonFirstElement isnoand hasLineComments fragment
includesLineCommentsOnNonFirstElement = yes
Add ‘, ‘ if all Elisions from the beginning of the array are processed (e.g. [, , , a]) and
element isn’t Elision or last element is Elision (e.g. [a,,b,,])
if index isnt0and passedElision and (not fragmentIsElision(fragments) or index is olen - 1)
answer.push @makeCode ', '
passedElision = passedElision ornot fragmentIsElision fragments
answer.push fragments...
if includesLineCommentsOnNonFirstElement or'\n'in fragmentsToText(answer)
for fragment, fragmentIndex in answer
if fragment.isHereComment
fragment.code = "#{multident(fragment.code, o.indent, no)}\n#{o.indent}"elseif fragment.code is', 'andnot fragment?.isElision and fragment.type notin ['StringLiteral', 'StringWithInterpolations']
fragment.code = ",\n#{o.indent}"
answer.unshift @makeCode "[\n#{o.indent}"
answer.push @makeCode "\n#{@tab}]"elsefor fragment in answer when fragment.isHereComment
fragment.code = "#{fragment.code} "
answer.unshift @makeCode '['
answer.push @makeCode ']'
answer
assigns: (name) ->for obj in @objects when obj.assigns name thenreturnyesno
eachName: (iterator) ->for obj in @objects
obj = obj.unwrapAll()
obj.eachName iterator
Special handling to allow class expr.A extends A declarations
parentName = @parent.base.value if @parent instanceof Value andnot @parent.hasProperties()
@hasNameClash = @name? and @name is parentName
node = @
if executableBody or @hasNameClash
node = new ExecutableClassBody node, executableBody
elseifnot @name? and o.level is LEVEL_TOP
determineName: ->returnnullunless @variable
[..., tail] = @variable.properties
node = if tail
tail instanceof Access and tail.name
else
@variable.base
unless node instanceof IdentifierLiteral or node instanceof PropertyName
returnnull
name = node.value
unless tail
message = isUnassignable name
@variable.error message if message
if name in JS_FORBIDDEN then"_#{name}"else name
walkBody: (o) ->
@ctor = null
@boundMethods = []
executableBody = null
initializer = []
{ expressions } = @body
i = 0for expression in expressions.slice()
if expression instanceof Value and expression.isObject true
{ properties } = expression.base
exprs = []
end = 0
start = 0pushSlice = -> exprs.push new Value new Obj properties[start...end], trueif end > start
while assign = properties[end]
if initializerExpression = @addInitializerExpression assign, o
pushSlice()
exprs.push initializerExpression
initializer.push initializerExpression
start = end + 1
end++
pushSlice()
expressions[i..i] = exprs
i += exprs.length
elseif initializerExpression = @addInitializerExpression expression, o
initializer.push initializerExpression
expressions[i] = initializerExpression
i += 1for method in initializer when method instanceof Code
if method.ctor
method.error 'Cannot define more than one constructor in a class'if @ctor
@ctor = method
elseif method.isStatic and method.bound
method.context = @name
elseif method.bound
@boundMethods.push method
returnunless o.compiling
if initializer.length isnt expressions.length
@body.expressions = (expression.hoist() for expression in initializer)
new Block expressions
This is the key method for determining whether an expression in a class
body should appear in the initializer or the executable body. If the given
node is valid in a class body the method will return a (new, modified,
or identical) node for inclusion in the class initializer, otherwise
nothing will be returned and the node will appear in the executable body.
At time of writing, only methods (instance and static) are valid in ES
class initializers. As new ES class features (such as class fields) reach
Stage 4, this method will need to be updated to support them. We
additionally allow PassthroughLiterals (backticked expressions) in the
initializer as an escape hatch for ES features that are not implemented
(e.g. getters and setters defined via the get and set keywords as
opposed to the Object.defineProperty method).
Make class/prototype assignments for invalid ES properties
addProperties: (assigns) ->
result = for assign in assigns
variable = assign.variable
base = variable?.base
value = assign.value
delete assign.context
if base.value is'constructor'if value instanceof Code
base.error 'constructors must be defined at the top level of a class body'
exports.ModuleDeclaration = classModuleDeclarationextendsBase
constructor: (@clause, @source, @assertions) ->
super()
@checkSource()
children: ['clause', 'source', 'assertions']
isStatement: YES
jumps: THIS
makeReturn: THIS
checkSource: ->if @source? and @source instanceof StringWithInterpolations
@source.error 'the name of the module to be imported from must be an uninterpolated string'
checkScope: (o, moduleDeclarationType) ->
TODO: would be appropriate to flag this error during AST generation (as
well as when compiling to JS). But o.indent isn’t tracked during AST
generation, and there doesn’t seem to be a current alternative way to track
whether we’re at the “program top-level”.
if o.indent.length isnt0
@error "#{moduleDeclarationType} statements must be at top-level scope"
astAssertions: (o) ->if @assertions?.properties?
@assertions.properties.map (assertion) =>
{ start, end, loc, left, right } = assertion.ast(o)
{ type: 'ImportAttribute', start, end, loc, key: left, value: right }
else
[]
exports.ImportDeclaration = classImportDeclarationextendsModuleDeclaration
compileNode: (o) ->
@checkScope o, 'import'
o.importedSymbols = []
code = []
code.push @makeCode "#{@tab}import "
code.push @clause.compileNode(o)... if @clause?
if @source?.value?
code.push @makeCode ' from 'unless @clause isnull
code.push @makeCode @source.value
if @assertions?
code.push @makeCode ' assert '
code.push @assertions.compileToFragments(o)...
code.push @makeCode ';'
code
astNode: (o) ->
o.importedSymbols = []
super o
astProperties: (o) ->
ret =
specifiers: @clause?.ast(o) ? []
source: @source.ast o
assertions: @astAssertions(o)
ret.importKind = 'value'if @clause
ret
exports.ImportClause = classImportClauseextendsBase
constructor: (@defaultBinding, @namedImports) ->
super()
children: ['defaultBinding', 'namedImports']
compileNode: (o) ->
code = []
if @defaultBinding?
code.push @defaultBinding.compileNode(o)...
code.push @makeCode ', 'if @namedImports?
if @namedImports?
code.push @namedImports.compileNode(o)...
code
astNode: (o) ->
During AST generation, we need to allow assignment to these constructs
that are considered “unassignable” during compile-to-JS, while still
flagging things like [null] = b.
allowAssignmentToExpansion = no,
allowAssignmentToNontrailingSplat = no,
allowAssignmentToEmptyArray = no,
allowAssignmentToComplexSplat = no
} = {}) ->
returnunlessnot @context or @context is'**='
varBase = @variable.unwrapAll()
ifnot varBase.isAssignable {
allowExpansion: allowAssignmentToExpansion
allowNontrailingSplat: allowAssignmentToNontrailingSplat
allowEmptyArray: allowAssignmentToEmptyArray
allowComplexSplat: allowAssignmentToComplexSplat
}
@variable.error "'#{@variable.compile o}' can't be assigned"
varBase.eachName (name) =>
returnif name.hasProperties?()
message = isUnassignable name.value
name.error message if message
@checkNameAssignability o, name
if @moduleDeclaration
o.scope.add name.value, @moduleDeclaration
name.isDeclaration = yeselseif @param
o.scope.add name.value,
if @param is'alwaysDeclare''var'else'param'else
alreadyDeclared = o.scope.find name.value
name.isDeclaration ?= not alreadyDeclared
If this assignment identifier has one or more herecomments
attached, output them as part of the declarations line (unless
other herecomments are already staged there) for compatibility
with Flow typing. Don’t do this if this assignment is for a
class, e.g. ClassName = class ClassName {, as Flow requires
the comment to be between the class name and the {.
if name.comments andnot o.scope.comments[name.value] and
@value notinstanceof Class and
name.comments.every((comment) -> comment.here andnot comment.multiline)
commentsNode = new IdentifierLiteral name.value
commentsNode.comments = name.comments
commentFragments = []
@compileCommentFragments o, commentsNode, commentFragments
o.scope.comments[name.value] = commentFragments
Compile an assignment, delegating to compileDestructuring or
compileSplice if appropriate. Keep track of the name of the base object
we’ve been assigned to, for correct internal references. If the variable
has not been seen yet within the current scope, declare it.
compileNode: (o) ->
isValue = @variable instanceof Value
if isValue
If @variable is an array or an object, we’re destructuring;
if it’s also isAssignable(), the destructuring syntax is supported
in ES and we can output it as is; otherwise we @compileDestructuring
and convert this ES-unsupported destructuring into acceptable output.
if @variable.isArray() or @variable.isObject()
unless @variable.isAssignable()
if @variable.isObject() and @variable.base.hasSplat()
return @compileObjectDestruct o
elsereturn @compileDestructuring o
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @isConditional()
return @compileSpecialMath o if @context in ['//=', '%%=']
@addScopeVariables o
if @value instanceof Code
if @value.isStatic
@value.name = @variable.properties[0]
elseif @variable.properties?.length >= 2
[properties..., prototype, name] = @variable.properties
@value.name = name if prototype.name?.value is'prototype'
val = @value.compileToFragments o, LEVEL_LIST
compiledName = @variable.compileToFragments o, LEVEL_LIST
if @context is'object'if @variable.shouldCache()
compiledName.unshift @makeCode '['
compiledName.push @makeCode ']'return compiledName.concat @makeCode(': '), val
answer = compiledName.concat @makeCode(" #{ @context or'=' } "), val
Brief implementation of recursive pattern matching, when assigning array or
object literals to a value. Peeks at their properties to assign inner names.
compileDestructuring: (o) ->
top = o.level is LEVEL_TOP
{value} = this
{objects} = @variable.base
olen = objects.length
At this point, there are several things to destructure. So the fn() in
{a, b} = fn() must be cached, for example. Make vvar into a simple
variable if it isn’t already.
if value.unwrap() notinstanceof IdentifierLiteral or @variable.assigns(vvarText)
ref = o.scope.freeVariable 'ref'
assigns.push [@makeCode(ref + ' = '), vvar...]
vvar = [@makeCode ref]
vvarText = ref
slicer = (type) -> (vvar, start, end = no) ->
vvar = new IdentifierLiteral vvar unless vvar instanceof Value
args = [vvar, new NumberLiteral(start)]
args.push new NumberLiteral end if end
slice = new Value (new IdentifierLiteral utility type, o), [new Access new PropertyName 'call']
new Value new Call slice, args
In case there is Splat or Expansion in objects,
we can split array in two simple subarrays.
Splat [a, b, c…, d, e] can be split into [a, b, c…] and [d, e].
Expansion [a, b, …, c, d] can be split into [a, b] and [c, d].
Examples:
a) Splat
CS: [a, b, c…, d, e] = arr
JS: [a, b, …c] = arr, [d, e] = splice.call(c, -2)
b) Expansion
CS: [a, b, …, d, e] = arr
JS: [a, b] = arr, [d, e] = slice.call(arr, -2)
When compiling a conditional assignment, take care to ensure that the
operands are only evaluated once, even though we have to reference them
more than once.
compileConditional: (o) ->
[left, right] = @variable.cacheReference o
This is the left-hand side of an assignment; let Arr and Obj
know that, so that those nodes know that they’re assignable as
destructured variables.
@variable.base.propagateLhs yes
throwUnassignableConditionalError: (name) ->
@variable.error "the variable \"#{name}\" can't be assigned with #{@context} because it has not been declared before"
isConditional: ->
@context in ['||=', '&&=', '?=']
isStatementAst: NO
astNode: (o) ->
@disallowLoneExpansion()
@getAndCheckSplatsAndExpansions()
if @isConditional()
variable = @variable.unwrap()
if variable instanceof IdentifierLiteral andnot o.scope.check variable.value
@throwUnassignableConditionalError variable.value
@addScopeVariables o, allowAssignmentToExpansion: yes, allowAssignmentToNontrailingSplat: yes, allowAssignmentToEmptyArray: yes, allowAssignmentToComplexSplat: yes
super o
astType: ->if @isDefaultAssignment()
'AssignmentPattern'else'AssignmentExpression'
astProperties: (o) ->
ret =
right: @value.ast o, LEVEL_LIST
left: @variable.ast o, LEVEL_LIST
unless @isDefaultAssignment()
ret.operator = @originalContext ? '='
ret
A function definition. This is the only node that creates a new Scope.
When for the purposes of walking the contents of a function body, the Code
has no children – they’re within the inner scope.
exports.Code = classCodeextendsBase
constructor: (params, body, @funcGlyph, @paramStart) ->
super()
@params = params or []
@body = body ornew Block
@bound = @funcGlyph?.glyph is'=>'
@isGenerator = no
@isAsync = no
@isMethod = no
@body.traverseChildren no, (node) =>if (node instanceof Op and node.isYield()) or node instanceof YieldReturn
@isGenerator = yesif (node instanceof Op and node.isAwait()) or node instanceof AwaitReturn
@isAsync = yesif node instanceof For and node.isAwait()
@isAsync = yes
@propagateLhs()
children: ['params', 'body']
isStatement: -> @isMethod
jumps: NO
makeScope: (parentScope) ->new Scope parentScope, @body, this
Compilation creates a new scope unless explicitly asked to share with the
outer scope. Handles splat parameters in the parameter list by setting
such parameters to be the final parameter in the function definition, as
required per the ES2015 spec. If the CoffeeScript function definition had
parameters after the splat, they are declared via expressions in the
function body.
compileNode: (o) ->
@checkForAsyncOrGeneratorConstructor()
if @bound
@context = o.scope.method.context if o.scope.method?.bound
@context = 'this'unless @context
@updateOptions o
params = []
exprs = []
thisAssignments = @thisAssignments?.slice() ? []
paramsAfterSplat = []
haveSplatParam = no
haveBodyParam = no
@checkForDuplicateParams()
@disallowLoneExpansionAndMultipleSplats()
@eachParamName (name, node, param, obj) ->
if node.this
name = node.properties[0].name.value
name = "_#{name}"if name in JS_FORBIDDEN
target = new IdentifierLiteral o.scope.freeVariable name, reserve: no
Param is object destructuring with a default value: ({@prop = 1}) ->
In a case when the variable name is already reserved, we have to assign
a new variable name to the destructured variable: ({prop:prop1 = 1}) ->
replacement =
if param.name instanceof Obj and obj instanceof Assign and
obj.operatorToken.value is'='new Assign (new IdentifierLiteral name), target, 'object'#, operatorToken: new Literal ':'else
target
param.renameParam node, replacement
thisAssignments.push new Assign node, target
Parse the parameters, adding them to the list of parameters to put in the
function definition; and dealing with splats or expansions, including
adding expressions to the function body to declare all parameter
variables that would have been after the splat/expansion parameter.
If we encounter a parameter that needs to be declared in the function
body for any reason, for example it’s destructured with this, also
declare and assign all subsequent parameters in the function body so that
any non-idempotent parameters are evaluated in the correct order.
Splat arrays are treated oddly by ES; deal with them the legacy
way in the function body. TODO: Should this be handled in the
function parameter list, and if so, how?
splatParamName = o.scope.freeVariable 'arg'
params.push ref = new Value new IdentifierLiteral splatParamName
exprs.push new Assign new Value(param.name), ref
else
params.push ref = param.asReference o
splatParamName = fragmentsToText ref.compileNodeWithoutComments o
if param.shouldCache()
exprs.push new Assign new Value(param.name), ref
else# `param` is an Expansion
splatParamName = o.scope.freeVariable 'args'
params.push new Value new IdentifierLiteral splatParamName
o.scope.parameter splatParamName
Parse all other parameters; if a splat paramater has not yet been
encountered, add these other parameters to the list to be output in
the function definition.
elseif param.shouldCache() or haveBodyParam
param.assignedInBody = yes
haveBodyParam = yes
This parameter cannot be declared or assigned in the parameter
list. So put a reference in the parameter list and add a statement
to the function body assigning it, e.g.
(arg) => { var a = arg.a; }, with a default value if it has one.
if param.value?
condition = new Op '===', param, new UndefinedLiteral
ifTrue = new Assign new Value(param.name), param.value
exprs.push new If condition, ifTrue
else
exprs.push new Assign new Value(param.name), param.asReference(o), null, param: 'alwaysDeclare'
If this parameter has a default value, and it hasn’t already been
set by the shouldCache() block above, define it as a statement in
the function body. This parameter comes after the splat parameter,
so we can’t define its default value in the parameter list.
if param.shouldCache()
ref = param.asReference o
elseif param.value? andnot param.assignedInBody
ref = new Assign new Value(param.name), param.value, null, param: yeselse
ref = param
This compilation of the parameter is only to get its name to add
to the scope name tracking; since the compilation output here
isn’t kept for eventual output, don’t include comments in this
compilation, so that they get output the “real” time this param
is compiled.
paramToAddToScope = if param.value? then param else ref
o.scope.parameter fragmentsToText paramToAddToScope.compileToFragmentsWithoutComments o
params.push ref
else
paramsAfterSplat.push param
If this parameter had a default value, since it’s no longer in the
function parameter list we need to assign its default value
(if necessary) as an expression in the body.
if param.value? andnot param.shouldCache()
condition = new Op '===', param, new UndefinedLiteral
ifTrue = new Assign new Value(param.name), param.value
exprs.push new If condition, ifTrue
Create a destructured assignment, e.g. [a, b, c] = [args..., b, c]
exprs.unshift new Assign new Value(
new Arr [new Splat(new IdentifierLiteral(splatParamName)), (param.asReference o for param in paramsAfterSplat)...]
), new Value new IdentifierLiteral splatParamName
wasEmpty = @body.isEmpty()
@disallowSuperInParamDefaults()
@checkSuperCallsInConstructorBody()
@body.expressions.unshift thisAssignments... unless @expandCtorSuper thisAssignments
@body.expressions.unshift exprs...
if @isMethod and @bound andnot @isStatic and @classVariable
boundMethodCheck = new Value new Literal utility 'boundMethodCheck', o
@body.expressions.unshift new Call(boundMethodCheck, [new Value(new ThisLiteral), @classVariable])
@body.makeReturn() unless wasEmpty or @noReturn
Block comments between a function name and ( get output between
function and (.
if @paramStart?.comments?
@compileCommentFragments o, @paramStart, signature
for param, i in params
signature.push @makeCode ', 'if i isnt0
signature.push @makeCode '...'if haveSplatParam and i is params.length - 1
Compile this parameter, but if any generated variables get created
(e.g. ref), shift those into the parent scope since we can’t put a
var line inside a function parameter list.
Block comments between ) and ->/=> get output between ) and {.
if @funcGlyph?.comments?
comment.unshift = nofor comment in @funcGlyph.comments
@compileCommentFragments o, @funcGlyph, signature
body = @body.compileWithDeclarations o unless @body.isEmpty()
Short-circuit replaceInContext method to prevent it from crossing context boundaries. Bound
functions have the same context.
replaceInContext: (child, replacement) ->if @bound
super child, replacement
elsefalse
disallowSuperInParamDefaults: ({forAst} = {}) ->returnfalseunless @ctor
@eachSuperCall Block.wrap(@params), (superCall) ->
superCall.error "'super' is not allowed in constructor parameter defaults"
, checkForThisBeforeSuper: not forAst
checkSuperCallsInConstructorBody: ->returnfalseunless @ctor
seenSuper = @eachSuperCall @body, (superCall) =>
superCall.error "'super' is only allowed in derived class constructors"if @ctor is'base'
seenSuper
flagThisParamInDerivedClassConstructorWithoutCallingSuper: (param) ->
param.error "Can't use @params in derived class constructors without calling super"
checkForAsyncOrGeneratorConstructor: ->if @ctor
@name.error 'Class constructor may not be async'if @isAsync
@name.error 'Class constructor may not be a generator'if @isGenerator
disallowLoneExpansionAndMultipleSplats: ->
seenSplatParam = nofor param in @params
Was ... used with this parameter? (Only one such parameter is allowed
per function.)
if param.splat or param instanceof Expansion
if seenSplatParam
param.error 'only one splat or expansion parameter is allowed per function definition'elseif param instanceof Expansion and @params.length is1
param.error 'an expansion parameter cannot be the only parameter in a function definition'
seenSplatParam = yes
expandCtorSuper: (thisAssignments) ->returnfalseunless @ctor
seenSuper = @eachSuperCall @body, (superCall) =>
superCall.expressions = thisAssignments
haveThisParam = thisAssignments.length and thisAssignments.length isnt @thisAssignments?.length
if @ctor is'derived'andnot seenSuper and haveThisParam
param = thisAssignments[0].variable
@flagThisParamInDerivedClassConstructorWithoutCallingSuper param
seenSuper
super in a constructor (the only super without an accessor)
cannot be given an argument with a reference to this, as that would
be referencing this before calling super.
unless child.variable.accessor
childArgs = child.args.filter (arg) ->
arg notinstanceof Class and (arg notinstanceof Code or arg.bound)
Block.wrap(childArgs).traverseChildren yes, (node) =>
node.error "Can't call super with @params in derived class constructors"if node.this
seenSuper = yes
iterator child
elseif checkForThisBeforeSuper and child instanceof ThisLiteral and @ctor is'derived'andnot seenSuper
child.error "Can't reference 'this' before calling super in derived class constructors"
A parameter in a function definition. Beyond a typical JavaScript parameter,
these parameters can also attach themselves to the context of the function,
as well as be a splat, gathering up a group of parameters into an array.
exports.Param = classParamextendsBase
constructor: (@name, @value, @splat) ->
super()
message = isUnassignable @name.unwrapAll().value
@name.error message if message
if @name instanceof Obj and @name.generated
token = @name.objects[0].operatorToken
token.error "unexpected #{token.value}"
children: ['name', 'value']
compileToFragments: (o) ->
@name.compileToFragments o, LEVEL_LIST
compileToFragmentsWithoutComments: (o) ->
@name.compileToFragmentsWithoutComments o, LEVEL_LIST
asReference: (o) ->return @reference if @reference
node = @name
if node.this
name = node.properties[0].name.value
name = "_#{name}"if name in JS_FORBIDDEN
node = new IdentifierLiteral o.scope.freeVariable name
elseif node.shouldCache()
node = new IdentifierLiteral o.scope.freeVariable 'arg'
node = new Value node
node.updateLocationDataIfMissing @locationData
@reference = node
shouldCache: ->
@name.shouldCache()
Iterates the name or names of a Param.
In a sense, a destructured parameter represents multiple JS parameters. This
method allows to iterate them all.
The iterator function will be called as iterator(name, node) where
name is the name of the parameter and node is the AST node corresponding
to that name.
eachName: (iterator, name = @name) ->checkAssignabilityOfLiteral = (literal) ->
message = isUnassignable literal.value
if message
literal.error message
unless literal.isAssignable()
literal.error "'#{literal.value}' can't be assigned"atParam = (obj, originalObj = null) => iterator "@#{obj.properties[0].name.value}", obj, @, originalObj
if name instanceof Call
name.error "Function invocation can't be assigned"
Rename a param by replacing the given AST node for a name with a new node.
This needs to ensure that the the source for object destructuring does not change.
No need to assign a new variable for the destructured variable if the variable isn’t reserved.
Examples:
({@foo}) -> should compile to ({foo}) { this.foo = foo}foo = 1; ({@foo}) -> should compile to foo = 1; ({foo:foo1}) { this.foo = foo1 }
if node.this and key.value is newNode.value
new Value newNode
elsenew Assign new Value(key), newNode, 'object'else
newNode
@replaceInContext isNode, replacement
Used to skip values inside an array destructuring (pattern matching) or
parameter list.
exports.Expansion = classExpansionextendsBase
shouldCache: NO
compileNode: (o) ->
@throwLhsError()
asReference: (o) ->
this
eachName: (iterator) ->
throwLhsError: ->
@error 'Expansion must be used inside a destructuring assignment or parameter list'
astNode: (o) ->unless @lhs
@throwLhsError()
super o
astType: ->'RestElement'
astProperties: ->return
argument: null
A while loop, the only sort of low-level loop exposed by CoffeeScript. From
it, all other loops can be manufactured. Useful in cases where you need more
flexibility or more speed than a comprehension can provide.
exports.While = classWhileextendsBase
constructor: (@condition, {invert: @inverted, @guard, @isLoop} = {}) ->
super()
children: ['condition', 'guard', 'body']
isStatement: YES
makeReturn: (results, mark) ->return super(results, mark) if results
@returns = not @jumps()
if mark
@body.makeReturn(results, mark) if @returns
return
this
addBody: (@body) ->
this
jumps: ->
{expressions} = @body
returnnounless expressions.length
for node in expressions
return jumpNode if jumpNode = node.jumps loop: yesno
The main difference from a JavaScript while is that the CoffeeScript
while can be used as a part of a larger expression – while loops may
return an array containing the computed result of each iteration.
compileNode: (o) ->
o.indent += TAB
set = ''
{body} = this
if body.isEmpty()
body = @makeCode ''elseif @returns
body.makeReturn rvar = o.scope.freeVariable 'results'
set = "#{@tab}#{rvar} = [];\n"if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"else
body = Block.wrap [new If @guard, body] if @guard
body = [].concat @makeCode("\n"), (body.compileToFragments o, LEVEL_TOP), @makeCode("\n#{@tab}")
answer = [].concat @makeCode(set + @tab + "while ("), @processedCondition().compileToFragments(o, LEVEL_PAREN),
@makeCode(") {"), body, @makeCode("}")
if @returns
answer.push @makeCode "\n#{@tab}return #{rvar};"
answer
processedCondition: ->
@processedConditionCache ?= if @inverted then @condition.invert() else @condition
astType: ->'WhileStatement'
astProperties: (o) ->return
test: @condition.ast o, LEVEL_PAREN
body: @body.ast o, LEVEL_TOP
guard: @guard?.ast(o) ? null
inverted: !!@inverted
postfix: !!@postfix
loop: !!@isLoop
Simple Arithmetic and logical operations. Performs some conversion from
CoffeeScript operations into their JavaScript equivalents.
exports.Op = classOpextendsBase
constructor: (op, first, second, flip, {@invertOperator, @originalOperator = op} = {}) ->
super()
if op is'new'if ((firstCall = unwrapped = first.unwrap()) instanceof Call or (firstCall = unwrapped.base) instanceof Call) andnot firstCall.doandnot firstCall.isNew
returnnew Value firstCall.newInstance(), if firstCall is unwrapped then [] else unwrapped.properties
first = new Parens first unless first instanceof Parens or first.unwrap() instanceof IdentifierLiteral or first.hasProperties?()
call = new Call first, []
call.locationData = @locationData
call.isNew = yesreturn call
@operator = CONVERSIONS[op] or op
@first = first
@second = second
@flip = !!flip
if @operator in ['--', '++']
message = isUnassignable @first.unwrapAll().value
@first.error message if message
return this
isChainable: ->
@operator in ['<', '>', '>=', '<=', '===', '!==']
isChain: ->
@isChainable() and @first.isChainable()
invert: ->if @isInOperator()
@invertOperator = '!'return @
if @isChain()
allInvertable = yes
curr = this
while curr and curr.operator
allInvertable and= (curr.operator of INVERSIONS)
curr = curr.first
returnnew Parens(this).invert() unless allInvertable
curr = this
while curr and curr.operator
curr.invert = !curr.invert
curr.operator = INVERSIONS[curr.operator]
curr = curr.first
this
elseif op = INVERSIONS[@operator]
@operator = op
if @first.unwrap() instanceof Op
@first.invert()
this
elseif @second
new Parens(this).invert()
elseif @operator is'!'and (fst = @first.unwrap()) instanceof Op and
fst.operator in ['!', 'in', 'instanceof']
fst
elsenew Op '!', this
unfoldSoak: (o) ->
@operator in ['++', '--', 'delete'] and unfoldSoak o, this, 'first'
generateDo: (exp) ->
passedParams = []
func = if exp instanceof Assign and (ref = exp.value.unwrap()) instanceof Code
ref
else
exp
for param in func.params or []
if param.value
passedParams.push param.value
delete param.value
else
passedParams.push param
call = new Call exp, passedParams
call.do = yes
call
isInOperator: ->
@originalOperator is'in'
compileNode: (o) ->if @isInOperator()
inNode = new In @first, @second
return (if @invertOperator then inNode.invert() else inNode).compileNode o
if @invertOperator
@invertOperator = nullreturn @invert().compileNode(o)
return Op::generateDo(@first).compileNode o if @operator is'do'
isChain = @isChain()
In chains, there’s no need to wrap bare obj literals in parens,
as the chained expression is wrapped.
@first.front = @front unless isChain
@checkDeleteOperand o
return @compileContinuation o if @isYield() or @isAwait()
return @compileUnary o if @isUnary()
return @compileChain o if isChain
switch @operator
when'?'then @compileExistence o, @second.isDefaultValue
when'//'then @compileFloorDivision o
when'%%'then @compileModulo o
else
lhs = @first.compileToFragments o, LEVEL_OP
rhs = @second.compileToFragments o, LEVEL_OP
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
if o.level <= LEVEL_OP then answer else @wrapInParentheses answer
compileUnary: (o) ->
parts = []
op = @operator
parts.push [@makeCode op]
if op is'!'and @first instanceof Existence
@first.negated = not @first.negated
return @first.compileToFragments o
if o.level >= LEVEL_ACCESS
return (new Parens this).compileToFragments o
plusMinus = op in ['+', '-']
parts.push [@makeCode(' ')] if op in ['typeof', 'delete'] or
plusMinus and @first instanceof Op and @first.operator is op
if plusMinus and @first instanceof Op
@first = new Parens @first
parts.push @first.compileToFragments o, LEVEL_OP
parts.reverse() if @flip
@joinFragmentArrays parts, ''
compileContinuation: (o) ->
parts = []
op = @operator
@checkContinuation o unless @isAwait()
if'expression'inObject.keys(@first) andnot (@first instanceof Throw)
parts.push @first.expression.compileToFragments o, LEVEL_OP if @first.expression?
else
parts.push [@makeCode "("] if o.level >= LEVEL_PAREN
parts.push [@makeCode op]
parts.push [@makeCode " "] if @first.base?.value isnt''
parts.push @first.compileToFragments o, LEVEL_OP
parts.push [@makeCode ")"] if o.level >= LEVEL_PAREN
@joinFragmentArrays parts, ''
checkContinuation: (o) ->unless o.scope.parent?
@error "#{@operator} can only occur inside functions"if o.scope.method?.bound and o.scope.method.isGenerator
@error 'yield cannot occur inside bound (fat arrow) functions'
compileFloorDivision: (o) ->
floor = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'floor']
second = if @second.shouldCache() thennew Parens @second else @second
div = new Op '/', @first, second
new Call(floor, [div]).compileToFragments o
compileModulo: (o) ->
mod = new Value new Literal utility 'modulo', o
new Call(mod, [@first, @second]).compileToFragments o
toString: (idt) ->
super idt, @constructor.name + ' ' + @operator
checkDeleteOperand: (o) ->if @operator is'delete'and o.scope.check(@first.unwrapAll().value)
@error 'delete operand may not be argument or var'
astNode: (o) ->
@checkContinuation o if @isYield()
@checkDeleteOperand o
super o
astType: ->return'AwaitExpression'if @isAwait()
return'YieldExpression'if @isYield()
return'ChainedComparison'if @isChain()
switch @operator
when'||', '&&', '?'then'LogicalExpression'when'++', '--'then'UpdateExpression'elseif @isUnary() then'UnaryExpression'else'BinaryExpression'
operatorAst: ->"#{if @invertOperator then"#{@invertOperator} "else''}#{@originalOperator}"
chainAstProperties: (o) ->
operators = [@operatorAst()]
operands = [@second]
currentOp = @first
loop
operators.unshift currentOp.operatorAst()
operands.unshift currentOp.second
currentOp = currentOp.first
unless currentOp.isChainable()
operands.unshift currentOp
breakreturn {
operators
operands: (operand.ast(o, LEVEL_OP) for operand in operands)
}
astProperties: (o) ->return @chainAstProperties(o) if @isChain()
firstAst = @first.ast o, LEVEL_OP
secondAst = @second?.ast o, LEVEL_OP
operatorAst = @operatorAst()
switchwhen @isUnary()
argument =
if @isYield() and @first.unwrap().value is''nullelse
firstAst
return {argument} if @isAwait()
return {
argument
delegate: @operator is'yield*'
} if @isYield()
return {
argument
operator: operatorAst
prefix: !@flip
}
elsereturn
left: firstAst
right: secondAst
operator: operatorAst
Checks a variable for existence – not null and not undefined. This is
similar to .nil? in Ruby, and avoids having to consult a JavaScript truth
table. Optionally only check if a variable is not undefined.
exports.Existence = classExistenceextendsBase
constructor: (@expression, onlyNotUndefined = no) ->
super()
@comparisonTarget = if onlyNotUndefined then'undefined'else'null'
salvagedComments = []
@expression.traverseChildren yes, (child) ->if child.comments
for comment in child.comments
salvagedComments.push comment unless comment in salvagedComments
delete child.comments
attachCommentsToNode salvagedComments, @
moveComments @expression, @
children: ['expression']
invert: NEGATE
compileNode: (o) ->
@expression.front = @front
code = @expression.compile o, LEVEL_OP
if @expression.unwrap() instanceof IdentifierLiteral andnot o.scope.check code
[cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&']
code = "typeof #{code}#{cmp} \"undefined\"" + if @comparisonTarget isnt'undefined'then" #{cnj}#{code}#{cmp}#{@comparisonTarget}"else''else
We explicity want to use loose equality (==) when comparing against null,
so that an existence check roughly corresponds to a check for truthiness.
Do not change this to === for null, as this will break mountains of
existing code. When comparing only against undefined, however, we want to
use === because this use case is for parity with ES2015+ default values,
which only get assigned when the variable is undefined (but not null).
cmp = if @comparisonTarget is'null'if @negated then'=='else'!='else# `undefined`if @negated then'==='else'!=='
code = "#{code}#{cmp}#{@comparisonTarget}"
[@makeCode(if o.level <= LEVEL_COND then code else"(#{code})")]
astType: ->'UnaryExpression'
astProperties: (o) ->return
argument: @expression.ast o
operator: '?'
prefix: no
An extra set of parentheses, specified explicitly in the source. At one time
we tried to clean up the results by detecting and removing redundant
parentheses, but no longer – you can put in as many as you please.
Parentheses are a good way to force any statement to become an expression.
If these parentheses are wrapping an IdentifierLiteral followed by a
block comment, output the parentheses (or put another way, don’t optimize
away these redundant parentheses). This is because Flow requires
parentheses in certain circumstances to distinguish identifiers followed
by comment-based type annotations from JavaScript labels.
shouldWrapComment = expr.comments?.some(
(comment) -> comment.here andnot comment.unshift andnot comment.newLine)
if expr instanceof Value and expr.isAtomic() andnot @jsxAttribute andnot shouldWrapComment
expr.front = @front
return expr.compileToFragments o
fragments = expr.compileToFragments o, LEVEL_PAREN
bare = o.level < LEVEL_OP andnot shouldWrapComment and (
expr instanceof Op andnot expr.isInOperator() or expr.unwrap() instanceof Call or
(expr instanceof For and expr.returns)
) and (o.level < LEVEL_COND or fragments.length <= 3)
return @wrapInBraces fragments if @jsxAttribute
if bare then fragments else @wrapInParentheses fragments
astNode: (o) -> @body.unwrap().ast o, LEVEL_PAREN
unwrap returns this to stop ancestor nodes reaching in to grab @body,
and using @body.compileNode. StringWithInterpolations.compileNode is
the custom logic to output interpolated strings as code.
Flag the { and } fragments as having been generated by this
StringWithInterpolations node, so that compileComments knows
to treat them as bounds. But the braces are unnecessary if all of
the enclosed comments are /* */ comments. Don’t trust
fragment.type, which can report minified variable names when
this compiler is minified.
code[0].isStringWithInterpolations = yes
code[code.length - 1].isStringWithInterpolations = yes
fragments.push code...
fragments.push @makeCode '`'unless @jsx
fragments
isNestedTag: (element) ->
call = element.unwrapAll?()
@jsx and call instanceof JSXElement
astType: ->'TemplateLiteral'
astProperties: (o) ->
elements = @extractElements o, includeInterpolationWrappers: yes
[..., last] = elements
quasis = []
expressions = []
for element, index in elements
if element instanceof StringLiteral
quasis.push new TemplateElement(
element.originalValue
tail: element is last
).withLocationDataFrom(element).ast o
else# Interpolation
{expression} = element
node =
unless expression?
emptyInterpolation = new EmptyInterpolation()
emptyInterpolation.locationData = emptyExpressionLocationData {
interpolationNode: element
openingBrace: '#{'
closingBrace: '}'
}
emptyInterpolation
else
expression.unwrapAll()
expressions.push astAsBlockIfNeeded node, o
{expressions, quasis, @quote}
exports.TemplateElement = classTemplateElementextendsBase
constructor: (@value, {@tail} = {}) ->
super()
astProperties: ->return
value:
raw: @value
tail: !!@tail
exports.Interpolation = classInterpolationextendsBase
constructor: (@expression) ->
super()
children: ['expression']
CoffeeScript’s replacement for the for loop is our array and object
comprehensions, that compile into for loops here. They also act as an
expression, able to return the result of each filtered iteration.
Unlike Python array comprehensions, they can be multi-line, and you can pass
the current index of the loop as a second parameter. Unlike Ruby blocks,
you can map and filter in a single pass.
exports.For = classForextendsWhile
constructor: (body, source) ->
super()
@addBody body
@addSource source
children: ['body', 'source', 'guard', 'step']
isAwait: -> @await ? no
addBody: (body) ->
@body = Block.wrap [body]
{expressions} = @body
if expressions.length
@body.locationData ?= mergeLocationData expressions[0].locationData, expressions[expressions.length - 1].locationData
this
addSource: (source) ->
{@source = no} = source
attribs = ["name", "index", "guard", "step", "own", "ownTag", "await", "awaitTag", "object", "from"]
@[attr] = source[attr] ? @[attr] for attr in attribs
return this unless @source
@index.error 'cannot use index with for-from'if @from and @index
@ownTag.error "cannot use own with for-#{if @from then'from'else'in'}"if @own andnot @object
[@name, @index] = [@index, @name] if @object
@index.error 'index cannot be a pattern matching expression'if @index?.isArray?() or @index?.isObject?()
@awaitTag.error 'await must be used with for-from'if @await andnot @from
@range = @source instanceof Value and @source.base instanceof Range andnot @source.properties.length andnot @from
@pattern = @name instanceof Value
@name.unwrap().propagateLhs?(yes) if @pattern
@index.error 'indexes do not apply to range loops'if @range and @index
@name.error 'cannot pattern match over range loops'if @range and @pattern
@returns = no
Move up any comments in the “for line”, i.e. the line of code with for,
from any child nodes of that line up to the for node itself so that these
comments get output, and get output above the for loop.
for attribute in ['source', 'guard', 'step', 'name', 'index'] when @[attribute]
@[attribute].traverseChildren yes, (node) =>if node.comments
These comments are buried pretty deeply, so if they happen to be
trailing comments the line they trail will be unrecognizable when
we’re done compiling this for loop; so just shift them up to
output above the for line.
comment.newLine = comment.unshift = yesfor comment in node.comments
moveComments node, @[attribute]
moveComments @[attribute], @
this
Welcome to the hairiest method in all of CoffeeScript. Handles the inner
loop, filtering, stepping, and result saving for array, object, and range
comprehensions. Some of the generated code can be shared in common, and
some cannot.
compileNode: (o) ->
body = Block.wrap [@body]
[..., last] = body.expressions
@returns = noif last?.jumps() instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and (@name.compile o, LEVEL_LIST) ifnot @pattern
index = @index and (@index.compile o, LEVEL_LIST)
scope.find(name) if name andnot @pattern
scope.find(index) if index and @index notinstanceof Value
rvar = scope.freeVariable 'results'if @returns
if @from
ivar = scope.freeVariable 'x', single: trueif @pattern
else
ivar = (@object and index) or scope.freeVariable 'i', single: true
kvar = ((@range or @from) and name) or index or ivar
kvarAssign = if kvar isnt ivar then"#{kvar} = "else""if @step andnot @range
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, shouldCacheOrIsAssignable
stepNum = parseNumber stepVar if @step.isNumber()
name = ivar if @pattern
varPart = ''
guardPart = ''
defPart = ''
idt1 = @tab + TAB
if @range
forPartFragments = source.compileToFragments merge o,
{index: ivar, name, @step, shouldCache: shouldCacheOrIsAssignable}
else
svar = @source.compile o, LEVEL_LIST
if (name or @own) andnot @from and @source.unwrap() notinstanceof IdentifierLiteral
defPart += "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
svar = ref
if name andnot @pattern andnot @from
namePart = "#{name} = #{svar}[#{kvar}]"ifnot @object andnot @from
defPart += "#{@tab}#{step};\n"if step isnt stepVar
down = stepNum < 0
lvar = scope.freeVariable 'len'unless @step and stepNum? and down
declare = "#{kvarAssign}#{ivar} = 0, #{lvar} = #{svar}.length"
declareDown = "#{kvarAssign}#{ivar} = #{svar}.length - 1"
compare = "#{ivar} < #{lvar}"
compareDown = "#{ivar} >= 0"if @step
if stepNum?
if down
compare = compareDown
declare = declareDown
else
compare = "#{stepVar} > 0 ? #{compare} : #{compareDown}"
declare = "(#{stepVar} > 0 ? (#{declare}) : #{declareDown})"
increment = "#{ivar} += #{stepVar}"else
increment = "#{if kvar isnt ivar then"++#{ivar}"else"#{ivar}++"}"
forPartFragments = [@makeCode("#{declare}; #{compare}; #{kvarAssign}#{increment}")]
if @returns
resultPart = "#{@tab}#{rvar} = [];\n"
returnResult = "\n#{@tab}return #{rvar};"
body.makeReturn rvar
if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"else
body = Block.wrap [new If @guard, body] if @guard
if @pattern
body.expressions.unshift new Assign @name, if @from thennew IdentifierLiteral kvar elsenew Literal "#{svar}[#{kvar}]"
varPart = "\n#{idt1}#{namePart};"if namePart
if @object
forPartFragments = [@makeCode("#{kvar} in #{svar}")]
guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;"if @own
elseif @from
if @await
forPartFragments = new Op 'await', new Parens new Literal "#{kvar} of #{svar}"
forPartFragments = forPartFragments.compileToFragments o, LEVEL_TOP
else
forPartFragments = [@makeCode("#{kvar} of #{svar}")]
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
if bodyFragments and bodyFragments.length > 0
bodyFragments = [].concat @makeCode('\n'), bodyFragments, @makeCode('\n')
fragments = [@makeCode(defPart)]
fragments.push @makeCode(resultPart) if resultPart
forCode = if @await then'for 'else'for ('
forClose = if @await then''else')'
fragments = fragments.concat @makeCode(@tab), @makeCode( forCode),
forPartFragments, @makeCode("#{forClose} {#{guardPart}#{varPart}"), bodyFragments,
@makeCode(@tab), @makeCode('}')
fragments.push @makeCode(returnResult) if returnResult
fragments
astNode: (o) ->addToScope = (name) ->
alreadyDeclared = o.scope.find name.value
name.isDeclaration = not alreadyDeclared
@name?.eachName addToScope, checkAssignability: no
@index?.eachName addToScope, checkAssignability: no
super o
astType: ->'For'
astProperties: (o) ->return
source: @source?.ast o
body: @body.ast o, LEVEL_TOP
guard: @guard?.ast(o) ? null
name: @name?.ast(o) ? null
index: @index?.ast(o) ? null
step: @step?.ast(o) ? null
postfix: !!@postfix
own: !!@own
await: !!@await
style: switchwhen @from then'from'when @object then'of'when @name then'in'else'range'
If/else statements. Acts as an expression by pushing down requested returns
to the last line of each clause.
Single-expression Ifs are compiled into conditional operators if possible,
because ternaries are already proper expressions, and don’t need conversion.
The If only compiles into a statement if either of its bodies needs
to be a statement. Otherwise a conditional operator is safe.
isStatement: (o) ->
o?.level is LEVEL_TOP or
@bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
jumps: (o) -> @body.jumps(o) or @elseBody?.jumps(o)
compileNode: (o) ->if @isStatement o then @compileStatement o else @compileExpression o
makeReturn: (results, mark) ->if mark
@body?.makeReturn results, mark
@elseBody?.makeReturn results, mark
return
@elseBody or= new Block [new Literal 'void 0'] if results
@body and= new Block [@body.makeReturn results]
@elseBody and= new Block [@elseBody.makeReturn results]
this
ensureBlock: (node) ->if node instanceof Block then node elsenew Block [node]
compileExpression: (o) ->
cond = @processedCondition().compileToFragments o, LEVEL_COND
body = @bodyNode().compileToFragments o, LEVEL_LIST
alt = if @elseBodyNode() then @elseBodyNode().compileToFragments(o, LEVEL_LIST) else [@makeCode('void 0')]
fragments = cond.concat @makeCode(" ? "), body, @makeCode(" : "), alt
if o.level >= LEVEL_COND then @wrapInParentheses fragments else fragments
unfoldSoak: ->
@soak and this
processedCondition: ->
@processedConditionCache ?= if @type is'unless'then @condition.invert() else @condition
isStatementAst: (o) ->
o.level is LEVEL_TOP
astType: (o) ->if @isStatementAst o
'IfStatement'else'ConditionalExpression'
astProperties: (o) ->
isStatement = @isStatementAst o
return
test: @condition.ast o, if isStatement then LEVEL_PAREN else LEVEL_COND
consequent:
if isStatement
@body.ast o, LEVEL_TOP
else
@bodyNode().ast o, LEVEL_TOP
alternate:
if @isChain
@elseBody.unwrap().ast o, if isStatement then LEVEL_TOP else LEVEL_COND
elseifnot isStatement and @elseBody?.expressions?.length is1
@elseBody.expressions[0].ast o, LEVEL_TOP
else
@elseBody?.ast(o, LEVEL_TOP) ? null
postfix: !!@postfix
inverted: @type is'unless'
Wherever in CoffeeScript 1 we might’ve inserted a makeCode "#{@tab}" to
indent a line of code, now we must account for the possibility of comments
preceding that line of code. If there are such comments, indent each line of
such comments, and then indent the first following line of code.
indentInitial = (fragments, node) ->for fragment, fragmentIndex in fragments
if fragment.isHereComment
fragment.code = multident fragment.code, node.tab
else
fragments.splice fragmentIndex, 0, node.makeCode "#{node.tab}"break
fragments
hasLineComments = (node) ->returnnounless node.comments
for comment in node.comments
returnyesif comment.here isnoreturnno
Sometimes when compiling a node, we want to insert a fragment at the start
of an array of fragments; but if the start has one or more comment fragments,
we want to insert this fragment after those but before any non-comments.
Helpers for mergeLocationData and mergeAstLocationData below.
lesser = (a, b) ->if a < b then a else b
greater = (a, b) ->if a > b then a else b
isAstLocGreater = (a, b) ->returnyesif a.line > b.line
returnnounless a.line is b.line
a.column > b.column
isLocationDataStartGreater = (a, b) ->returnyesif a.first_line > b.first_line
returnnounless a.first_line is b.first_line
a.first_column > b.first_column
isLocationDataEndGreater = (a, b) ->returnyesif a.last_line > b.last_line
returnnounless a.last_line is b.last_line
a.last_column > b.last_column
Take two nodes’ location data and return a new locationData object that
encompasses the location data of both nodes. So the new first_line value
will be the earlier of the two nodes’ first_line values, the new
last_column the later of the two nodes’ last_column values, etc.
If you only want to extend the first node’s location data with the start or
end location data of the second node, pass the justLeading or justEnding
options. So e.g. if first’s range is [4, 5] and second’s range is [1, 10],
you’d get:
Take two AST nodes, or two AST nodes’ location data objects, and return a new
location data object that encompasses the location data of both nodes. So the
new start value will be the earlier of the two nodes’ start values, the
new end value will be the later of the two nodes’ end values, etc.
If you only want to extend the first node’s location data with the start or
end location data of the second node, pass the justLeading or justEnding
options. So e.g. if first’s range is [4, 5] and second’s range is [1, 10],
you’d get:
We don’t currently have a token corresponding to the empty space
between interpolation/JSX expression braces, so piece together the location
data by trimming the braces from the Interpolation’s location data.
Technically the last_line/last_column calculation here could be
incorrect if the ending brace is preceded by a newline, but
last_line/last_column aren’t used for AST generation anyway.
Parse the list of arguments, populating an options object with all of the
specified options, and return it. Options after the first non-option
argument are treated as arguments. options.arguments will be an array
containing the remaining arguments. This is a simpler API than many option
parsers that allow you to attach callback actions for every flag. Instead,
you’re responsible for interpreting the options object.
The CoffeeScript option parser is a little odd; options after the first
non-option argument are treated as non-option arguments themselves.
Optional arguments are normalized by expanding merged flags into multiple
flags. This allows you to have -wl be the same as --watch --lint.
Note that executable scripts with a shebang (#!) line should use the
line #!/usr/bin/env coffee, or #!/absolute/path/to/coffee, without a
-- argument after, because that will fail on Linux (see #3946).
for flag in [rule.shortFlag, rule.longFlag] when flag?
if flagDict[flag]?
thrownewError"flag #{flag} for switch #{rule.name}
was already declared for switch #{flagDict[flag].name}"
flagDict[flag] = rule
{ruleList, flagDict}
If the previous argument given to the script was an option that uses the
next command-line argument as its argument, create copy of the option’s
rule with an argument field.
positional = args[argIndex..]
breakif needsArgOpt?
thrownewError"value required for #{needsArgOpt.flag}, but it was the last
argument provided"
{rules, positional}
================================================
FILE: docs/v2/annotated-source/public/stylesheets/normalize.css
================================================
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
================================================
FILE: docs/v2/annotated-source/register.html
================================================
register.coffee
Node 0.11 changed API, a command such as ‘.help’ is now stored as ‘help’
commandsHaveLeadingDot = repl.commands['.help']?
if commandsHaveLeadingDot then".#{commandName}"else commandName
module.exports =
start: (opts = {}) ->
[major, minor, build] = process.versions.node.split('.').map (n) -> parseInt(n, 10)
if major < 6
console.warn "Node 6+ required for CoffeeScript REPL"
process.exit 1
CoffeeScript.register()
process.argv = ['coffee'].concat process.argv[2..]
if opts.transpile
transpile = {}
try
transpile.transpile = require('@babel/core').transform
catchtry
transpile.transpile = require('babel-core').transform
catch
console.error '''
To use --transpile with an interactive REPL, @babel/core must be installed either in the current folder or globally:
npm install --save-dev @babel/core
or
npm install --global @babel/core
And you must save options to configure Babel in one of the places it looks to find its options.
See https://coffeescript.org/#transpilation
'''
process.exit 1
transpile.options =
filename: path.resolve process.cwd(), '<repl>'
Since the REPL compilation path is unique (in eval above), we need
another way to get the options object attached to a module so that
it knows later on whether it needs to be transpiled. In the case of
the REPL, the only applicable option is transpile.
The CoffeeScript language has a good deal of optional syntax, implicit syntax,
and shorthand syntax. This can greatly complicate a grammar and bloat
the resulting parse table. Instead of making the parser handle it all, we take
a series of passes over the token stream, using this Rewriter to convert
shorthand into the unambiguous long form, add implicit indentation and
parentheses, and generally clean things up.
Create a generated token: one that exists due to a use of implicit syntax.
Optionally have this new token take the attached comments from another token.
Rewrite the token stream in multiple passes, one logical filter at
a time. This could certainly be changed into a single pass through the
stream, with a big ol’ efficient switch, but it’s much nicer to work with
like this. The order of these passes matters—indentation must be
corrected before implicit parentheses can be wrapped around blocks of code.
Set environment variable DEBUG_TOKEN_STREAM to true to output token
debugging info. Also set DEBUG_REWRITTEN_TOKEN_STREAM to true to
output the token stream after it has been rewritten by this file.
if process?.env?.DEBUG_TOKEN_STREAM
console.log 'Initial token stream:'if process.env.DEBUG_REWRITTEN_TOKEN_STREAM
console.log (t[0] + '/' + t[1] + (if t.comments then'*'else'') for t in @tokens).join ' '
@removeLeadingNewlines()
@closeOpenCalls()
@closeOpenIndexes()
@normalizeLines()
@tagPostfixConditionals()
@addImplicitBracesAndParens()
@rescueStowawayComments()
@addLocationDataToGeneratedTokens()
@enforceValidJSXAttributes()
@fixIndentationLocationData()
@exposeTokenDataToGrammar()
if process?.env?.DEBUG_REWRITTEN_TOKEN_STREAM
console.log 'Rewritten token stream:'if process.env.DEBUG_TOKEN_STREAM
console.log (t[0] + '/' + t[1] + (if t.comments then'*'else'') for t in @tokens).join ' '
@tokens
Rewrite the token stream, looking one token ahead and behind.
Allow the return value of the block to tell us how many tokens to move
forwards (or backwards) in the stream, to make sure we don’t miss anything
as tokens are inserted and removed, and the stream changes length under
our feet.
scanTokens: (block) ->
{tokens} = this
i = 0
i += block.call this, token, i, tokens while token = tokens[i]
true
detectEnd: (i, condition, action, opts = {}) ->
{tokens} = this
levels = 0while token = tokens[i]
return action.call this, token, i if levels is0and condition.call this, token, i
if token[0] in EXPRESSION_START
levels += 1elseif token[0] in EXPRESSION_END
levels -= 1if levels < 0returnif opts.returnOnNegativeLevel
return action.call this, token, i
i += 1
i - 1
Match tags in token stream starting at i with pattern.
pattern may consist of strings (equality), an array of strings (one of)
or null (wildcard). Returns the index of the match or -1 if no match.
Returns yes if current line of tokens contain an element of tags on same
expression level. Stop searching at LINEBREAKS or explicit start of
containing balanced expression.
findTagsBackwards: (i, tags) ->
backStack = []
while i >= 0and (backStack.length or
@tag(i) notin tags and
(@tag(i) notin EXPRESSION_START or @tokens[i].generated) and
@tag(i) notin LINEBREAKS)
backStack.push @tag(i) if @tag(i) in EXPRESSION_END
backStack.pop() if @tag(i) in EXPRESSION_START and backStack.length
i -= 1
@tag(i) in tags
Don’t end an implicit call/object on next indent if any of these are in an argument/value.
if (
(inImplicitCall() or inImplicitObject()) and tag in CONTROL_IN_IMPLICIT or
inImplicitObject() and prevTag is':'and tag is'FOR'
)
stack.push ['CONTROL', i, ours: yes]
return forward(1)
if tag is'INDENT'and inImplicit()
Recognize standard implicit calls like
f a, f() b, f? c, h[0] d etc.
Added support for spread dots on the left side: f …a
if (tag in IMPLICIT_FUNC and token.spaced or
tag is'?'and i > 0andnot tokens[i - 1].spaced) and
(nextTag in IMPLICIT_CALL or
(nextTag is'...'and @tag(i + 2) in IMPLICIT_CALL andnot @findTagsBackwards(i, ['INDEX_START', '['])) or
nextTag in IMPLICIT_UNSPACED_CALL andnot nextToken.spaced andnot nextToken.newLine) andnot inControlFlow()
tag = token[0] = 'FUNC_EXIST'if tag is'?'
startImplicitCall i + 1return forward(2)
Implicit call taking an implicit indented object as first argument.
f
a: b
c: d
Don’t accept implicit calls of this type, when on the same line
as the control structures below as that may misinterpret constructs like:
if f
a: 1
as
if f(a: 1)
which is probably always unintended.
Furthermore don’t allow this in the first line of a literal array
or explicit object, as that creates grammatical ambiguities (#5368).
if tag in IMPLICIT_FUNC and
@indexOfTag(i + 1, 'INDENT') > -1and @looksObjectish(i + 2) andnot @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH',
'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL']) andnot ((s = stackTop()?[0]) in ['{', '['] andnot isImplicit(stackTop()) and
@findTagsBackwards(i, s))
startImplicitCall i + 1
stack.push ['INDENT', i + 2]
return forward(3)
End indented-continuation-line implicit objects once that indentation is over.
if tag is'TERMINATOR'and token.endsContinuationLineIndentation
{preContinuationLineIndent} = token.endsContinuationLineIndentation
while inImplicitObject() and (implicitObjectIndent = stackTop()[2].continuationLineIndent)? and implicitObjectIndent > preContinuationLineIndent
endImplicitObject()
newLine = prevTag is'OUTDENT'or prevToken.newLine
if tag in IMPLICIT_END or
(tag in CALL_CLOSERS and newLine) or
(tag in ['..', '...'] and @findTagsBackwards(i, ["INDEX_START"]))
while inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
Close implicit objects such as:
return a: 1, b: 2 unless true
elseif inImplicitObject() and sameLine and
tag isnt'TERMINATOR'and prevTag isnt':'andnot (tag in ['POST_IF', 'FOR', 'WHILE', 'UNTIL'] and startsLine and implicitObjectContinues(i + 1))
endImplicitObject()
Close implicit objects when at end of line, line didn’t end with a comma
and the implicit object didn’t start the line or the next line doesn’t look like
the continuation of an object.
elseif inImplicitObject() and tag is'TERMINATOR'and prevTag isnt','andnot (startsLine and @looksObjectish(i + 1))
endImplicitObject()
elseif inImplicitControl() and tokens[stackTop()[1]][0] is'CLASS'and tag is'TERMINATOR'
stack.pop()
elsebreak
Close implicit object if comma is the last character
and what comes after doesn’t look like it belongs.
This is used for trailing commas and calls, like:
x =
a: b,
c: d,
e = 2
and
f a, b: c, d: e, f, g: h: i, j
if tag is','andnot @looksObjectish(i + 1) and inImplicitObject() andnot (@tag(i + 2) in ['FOROF', 'FORIN']) and
(nextTag isnt'TERMINATOR'ornot @looksObjectish(i + 2))
Not all tokens survive processing by the parser. To avoid comments getting
lost into the ether, find comments attached to doomed tokens and move them
to a token that will make it to the other side.
Find the next surviving token and attach this token’s comments to it,
with a flag that we know to output such comments before that
token’s own compilation. (Otherwise comments are output following
the token they’re attached to.)
j = i
j++ while j isnt tokens.length and tokens[j][0] in DISCARDED
unless j is tokens.length or tokens[j][0] in DISCARDED
comment.unshift = yesfor comment in token.comments
moveComments token, tokens[j]
return1else# All following tokens are doomed!
j = tokens.length - 1
insertPlaceholder token, j, tokens, 'push'
Find the last surviving token and attach this token’s comments to it.
j = i
j-- while j isnt-1and tokens[j][0] in DISCARDED
unless j is-1or tokens[j][0] in DISCARDED
moveComments token, tokens[j]
return1else# All previous tokens are doomed!
insertPlaceholder token, 0, tokens, 'unshift'
This token won’t survive passage through the parser, so we need to
rescue its attached tokens and redistribute them to nearby tokens.
Comments that don’t start a new line can shift backwards to the last
safe token, while other tokens should shift forward.
dummyToken = comments: []
j = token.comments.length - 1until j is-1if token.comments[j].newLine isnoand token.comments[j].here isno
dummyToken.comments.unshift token.comments[j]
token.comments.splice j, 1
j--
if dummyToken.comments.length isnt0
ret = shiftCommentsBackward dummyToken, i - 1, tokens
if token.comments.length isnt0
shiftCommentsForward token, i, tokens
elseunless dontShiftForward i, tokens
If any of this token’s comments start a line—there’s only
whitespace between the preceding newline and the start of the
comment—and this isn’t one of the special JS tokens, then
shift this comment forward to precede the next valid token.
Block.compileComments also has logic to make sure that
“starting new line” comments follow or precede the nearest
newline relative to the token that the comment is attached to,
but that newline might be inside a } or ) or other generated
token that we really want this comment to output after. Therefore
we need to shift the comments here, avoiding such generated and
discarded tokens.
dummyToken = comments: []
j = token.comments.length - 1until j is-1if token.comments[j].newLine andnot token.comments[j].unshift andnot (token[0] is'JS'and token.generated)
dummyToken.comments.unshift token.comments[j]
token.comments.splice j, 1
j--
if dummyToken.comments.length isnt0
ret = shiftCommentsForward dummyToken, i + 1, tokens
delete token.comments if token.comments?.length is0
ret
OUTDENT tokens should always be positioned at the last character of the
previous token, so that AST nodes ending in an OUTDENT token end up with a
location corresponding to the last “real” token under the node.
fixIndentationLocationData: ->
@allComments ?= extractAllCommentTokens @tokens
findPrecedingComment = (token, {afterPosition, indentSize, first, indented}) =>
tokenStart = token[2].range[0]
matches = (comment) ->if comment.outdented
returnnounless indentSize? and comment.indentSize > indentSize
returnnoif indented andnot comment.indented
returnnounless comment.locationData.range[0] < tokenStart
returnnounless comment.locationData.range[0] > afterPosition
yesif first
lastMatching = nullfor comment in @allComments by-1if matches comment
lastMatching = comment
elseif lastMatching
return lastMatching
return lastMatching
for comment in @allComments when matches comment by-1return comment
null
@scanTokens (token, i, tokens) ->
return1unless token[0] in ['INDENT', 'OUTDENT'] or
(token.generated and token[0] is'CALL_END'andnot token.data?.closingTagNameToken) or
(token.generated and token[0] is'}')
isIndent = token[0] is'INDENT'
prevToken = token.prevToken ? tokens[i - 1]
prevLocationData = prevToken[2]
addLocationDataToGeneratedTokens() set the outdent’s location data
to the preceding token’s, but in order to detect comments inside an
empty “block” we want to look for comments preceding the next token.
useNextToken = token.explicit or token.generated
if useNextToken
nextToken = token
nextTokenIndex = i
nextToken = tokens[nextTokenIndex++] while (nextToken.explicit or nextToken.generated) and nextTokenIndex isnt tokens.length - 1
precedingComment = findPrecedingComment(
if useNextToken
nextToken
else
token
afterPosition: prevLocationData.range[0]
indentSize: token.indentSize
first: isIndent
indented: useNextToken
)
if isIndent
return1unless precedingComment?.newLine
Because our grammar is LALR(1), it can’t handle some single-line
expressions that lack ending delimiters. The Rewriter adds the implicit
blocks, so it doesn’t need to. To keep the grammar clean and tidy, trailing
newlines within expressions are removed and the indentation tokens of empty
blocks are added.
if tag is'ELSE'and @tag(i - 1) isnt'OUTDENT'
i = closeElseTag tokens, i
tokens.splice i + 1, 0, indent
@detectEnd i + 2, condition, action
tokens.splice i, 1if tag is'THEN'return1return1
For tokens with extra data, we want to make that data visible to the grammar
by wrapping the token value as a String() object and setting the data as
properties of that object. The grammar should then be responsible for
cleaning this up for the node constructor: unwrapping the token value to a
primitive string and separately passing any expected token data properties
exposeTokenDataToGrammar: ->
@scanTokens (token, i) ->
if token.generated or (token.data andObject.keys(token.data).length isnt0)
token[1] = newString token[1]
token[1][key] = val for own key, val of (token.data ? {})
token[1].generated = yesif token.generated
1
The tokens that signal the start/end of a balanced pair.
EXPRESSION_START = []
EXPRESSION_END = []
for [left, right] in BALANCED_PAIRS
EXPRESSION_START.push INVERSES[right] = left
EXPRESSION_END .push INVERSES[left] = right
Tokens that are swallowed up by the parser, never leading to code generation.
You can spot these in grammar.coffee because the o function second
argument doesn’t contain a new call for these tokens.
STRING_START isn’t on this list because its locationData matches that of
the node that becomes StringWithInterpolations, and therefore
addDataToNode attaches STRING_START’s tokens to that node.
The Scope class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with var, and which are shared
with external scopes.
Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the Block node it belongs to, which is
where it should declare its variables, a reference to the function that
it belongs to, and a list of variables referenced in the source code
and therefore should be avoided when generating variables. Also track comments
that should be output as part of variable declarations.
When super is called, we need to find the name of the current method we’re
in, so that we know how to invoke the same method of the parent class. This
can get complicated if super is being called from an inner function.
namedMethod will walk up the scope tree until it either finds the first
function object that has a name filled in, or bottoms out.
namedMethod: ->return @method if @method?.name or !@parent
@parent.namedMethod()
Source maps allow JavaScript runtimes to match running JavaScript back to
the original source code that corresponds to it. This can be minified
JavaScript, but in our case, we’re concerned with mapping pretty-printed
JavaScript back to CoffeeScript.
In order to produce maps, we must keep track of positions (line number, column number)
that originated every node in the syntax tree, and be able to generate a
map file
— which is a compact, VLQ-encoded representation of the JSON serialization
of this information — to write out alongside the generated JavaScript.
A LineMap object keeps track of information about original line and column
positions for a single line of output JavaScript code.
SourceMaps are implemented in terms of LineMaps.
classLineMap
constructor: (@line) ->
@columns = []
add: (column, [sourceLine, sourceColumn], options={}) ->returnif @columns[column] and options.noReplace
@columns[column] = {line: @line, column, sourceLine, sourceColumn}
sourceLocation: (column) ->
column-- until (mapping = @columns[column]) or (column <= 0)
mapping and [mapping.sourceLine, mapping.sourceColumn]
Maps locations in a single generated JavaScript file back to locations in
the original CoffeeScript source file.
This is intentionally agnostic towards how a source map might be represented on
disk. Once the compiler is ready to produce a “v3”-style source map, we can walk
through the arrays of line and column buffer to produce it.
Adds a mapping to this SourceMap. sourceLocation and generatedLocation
are both [line, column] arrays. If options.noReplace is true, then if there
is already a mapping for the specified line and column, this will have no
effect.
A static source maps cache filename: map. These are used for transforming
stack traces and are currently set in CoffeeScript.compile for all files
compiled with the source maps option.
Builds up a V3 source map, returning the generated JSON as a string.
options.sourceRoot may be used to specify the sourceRoot written to the source
map. Also, options.sourceFiles and options.generatedFile may be passed to
set “sources” and “file”, respectively.
generate: (options = {}, code = null) ->
writingline = 0
lastColumn = 0
lastSourceLine = 0
lastSourceColumn = 0
needComma = no
buffer = ""for lineMap, lineNumber in @lines when lineMap
for mapping in lineMap.columns when mapping
while writingline < mapping.line
lastColumn = 0
needComma = no
buffer += ";"
writingline++
Note that SourceMap VLQ encoding is “backwards”. MIDI-style VLQ encoding puts
the most-significant-bit (MSB) from the original value into the MSB of the VLQ
encoded value (see Wikipedia).
SourceMap VLQ does things the other way around, with the least significat four
bits of the original value encoded into the first byte of the VLQ encoded value.
We are pleased to announce CoffeeScript 2! This new release of the CoffeeScript language and compiler aims to bring CoffeeScript into the modern JavaScript era, closing gaps in compatibility with JavaScript while preserving the clean syntax that is CoffeeScript’s hallmark. In a nutshell:
The CoffeeScript 2 compiler now translates CoffeeScript code into modern JavaScript syntax. So a CoffeeScript => is now output as =>, a CoffeeScript class is now output using the class keyword, and so on. This means you may need to transpile the CoffeeScript compiler’s output.
All of the above was achieved with very few breaking changes from 1.x. Most current CoffeeScript projects should be able to upgrade with little or no refactoring necessary.
CoffeeScript 2 was developed with two primary goals: remove any incompatibilities with modern JavaScript that might prevent CoffeeScript from being used on a project; and preserve as much backward compatibility as possible. Install now: npm install -g coffeescript@2
Modern JavaScript Output
From the beginning, CoffeeScript has been described as being “just JavaScript.” And today, JavaScript is ES2015 (well, ES2017; also commonly known as ES6). CoffeeScript welcomes the changes in the JavaScript world and we’re happy to stop outputting circa-1999 syntax for modern features.
Many new JavaScript features, such as =>, were informed by CoffeeScript and are one-to-one compatible, or very nearly so. This has made outputting many of CoffeeScript’s innovations into new JS syntax straightforward: not only does => become =>, but { a } = obj becomes { a } = obj, "a#{b}c" becomes `a${b}c` and so on.
The following CoffeeScript features were updated in 2.0 to output using modern JavaScript syntax (or added in CoffeeScript 1.11 through 2.0, output using modern syntax):
Modules: import/export
Classes: class Animal
Async functions: await someFunction()
Bound/arrow functions: =>
Function default parameters: (options = {}) ->
Function splat/rest parameters: (items...) ->
Destructuring, for both arrays and objects: [first, second] = items, {length} = items
JavaScript’s for…of is now available as CoffeeScript’s for…from (we already had a for…of): for n from generatorFunction()
Not all CoffeeScript features were adopted into JavaScript in 100% the same way; most notably, default values in JavaScript (and also in CoffeeScript 2) are only applied when a variable is undefined, not undefined or null as in CoffeeScript 1; and classes have their own differences. See the breaking changes for the fine details.
In our experience, most breaking changes are edge cases that should affect very few people, like JavaScript’s lack of an arguments object inside arrow functions. There seem to be two breaking changes that affect a significant number of projects:
In CoffeeScript 2, “bare” super (calling super without arguments) is now no longer allowed, and one must use super() or super arguments... instead.
References to this/@ cannot occur before a call to super, per the JS spec.
See the full details. Either the CoffeeScript compiler or your transpiler will throw errors for either of these cases, so updating your code is a matter of fixing each occurrence as the compiler errors on it, until your code compiles successfully.
Other Features
Besides supporting new JavaScript features and outputting older CoffeeScript features in modern JS syntax, CoffeeScript 2 has added support for the following:
Line comments are now output (in CoffeeScript 1 they were discarded)
Block comments are now allowed anywhere, enabling static type annotations using Flow’s comment-based syntax
There are many smaller improvements as well, such as to the coffee command-line tool. You can read all the details in the changelog for the 2.0.0 betas.
“What About …?”
A few JavaScript features have been intentionally omitted from CoffeeScript. These include let and const (and var), named functions and the get and set keywords. These get asked about so often that we added a section to the docs called Unsupported ECMAScript Features. CoffeeScript’s lack of equivalents for these features does not affect compatibility or interoperability with JavaScript modules or libraries.
Future Compatibility
Back when CoffeeScript 1 was created, ES2015 JavaScript and transpilers like Babel, Bublé or Traceur Compiler were several years away. The CoffeeScript compiler itself had to do what today’s transpilers do, converting modern features like destructuring and arrow functions into equivalent lowest-common-denominator JavaScript.
But transpilers exist now, and they do their job well. With them around, there’s no need for the CoffeeScript compiler to duplicate this functionality. All the CoffeeScript compiler needs to worry about now is converting the CoffeeScript version of new syntax into the JS version of that syntax, e.g. "Hello, #{name}!" into `Hello, ${name}!`. This makes adding support for new JavaScript features much easier than before.
Most features added by ECMA in recent years haven’t required any updates at all in CoffeeScript. New global objects, or new methods on global objects, don’t require any updates on CoffeeScript’s part to work. Some proposed future JS features do involve new syntax, like class fields. We have adopted a policy of supporting new syntax only when it reaches Stage 4 in ECMA’s process, which means that the syntax is final and will be in the next ES release. On occasion we might support a feature before it has reached Stage 4, but output it using equivalent non-experimental syntax instead of the newly-proposed syntax; that’s what’s happening in 2.0.0 for object destructuring, where our output uses the same polyfill that Babel uses. When the new syntax is finalized, we will update our output to use the final syntax.
Credits
The major features of 2.0.0 would not have been possible without the following people:
@GeoffreyBooth: Organizer of the CoffeeScript 2 effort, developer for modules; arrow functions, function default parameters and function rest parameters output using ES2015 syntax; line comments output and block comments output anywhere; block embedded JavaScript via triple backticks; improved parsing of Literate CoffeeScript; and the new docs website.
@connec: Classes; destructuring; splats/rest syntax in arrays and function calls; and computed properties all output using ES2015 syntax.
CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.
The golden rule of CoffeeScript is: “It’s just JavaScript.” The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly from CoffeeScript (and vice-versa). The compiled output is readable, pretty-printed, and tends to run as fast or faster than the equivalent handwritten JavaScript.
# Install locally for a project:
npm install --save-dev coffeescript
# Install globally to execute .coffee files anywhere:
npm install --global coffeescript
Overview
CoffeeScript on the topleft, compiled JavaScript output on the bottomright. The CoffeeScript is editable!
CoffeeScript 2
What’s New In CoffeeScript 2?
The biggest change in CoffeeScript 2 is that now the CoffeeScript compiler produces modern JavaScript syntax (ES6, or ES2015 and later). A CoffeeScript => becomes a JS =>, a CoffeeScript class becomes a JS class and so on. Major new features in CoffeeScript 2 include async functions and JSX. You can read more in the announcement.
Most modern JavaScript features that CoffeeScript supports can run natively in Node 7.6+, meaning that Node can run CoffeeScript’s output without any further processing required. Here are some notable exceptions:
Modules are supported by Node 12+ with "type": "module" in your project’s package.json.
This list may be incomplete, and excludes versions of Node that support newer features behind flags; please refer to node.green for full details. You can run the tests in your browser to see what your browser supports. It is your responsibility to ensure that your runtime supports the modern features you use; or that you transpile your code. When in doubt, transpile.
For compatibility with other JavaScript frameworks and tools, see Integrations.
Installation
The command-line version of coffee is available as a Node.js utility, requiring Node 6 or later. The core compiler however, does not depend on Node, and can be run in any JavaScript environment, or in the browser (see Try CoffeeScript).
To install, first make sure you have a working copy of the latest stable version of Node.js. You can then install CoffeeScript globally with npm:
npm install --global coffeescript
This will make the coffee and cake commands available globally.
If you are using CoffeeScript in a project, you should install it locally for that project so that the version of CoffeeScript is tracked as one of your project’s dependencies. Within that project’s folder:
npm install --save-dev coffeescript
The coffee and cake commands will first look in the current folder to see if CoffeeScript is installed locally, and use that version if so. This allows different versions of CoffeeScript to be installed globally and locally.
If you plan to use the --transpile option (see Transpilation) you will need to also install @babel/core either globally or locally, depending on whether you are running a globally or locally installed version of CoffeeScript.
Usage
Command Line
Once installed, you should have access to the coffee command, which can execute scripts, compile .coffee files into .js, and provide an interactive REPL. The coffee command takes the following options:
Option
Description
-c, --compile
Compile a .coffee script into a .js JavaScript file of the same name.
-t, --transpile
Pipe the CoffeeScript compiler’s output through Babel before saving or running the generated JavaScript. Requires @babel/core to be installed, and options to pass to Babel in a .babelrc file or a package.json with a babel key in the path of the file or folder to be compiled. See Transpilation.
-m, --map
Generate source maps alongside the compiled JavaScript files. Adds sourceMappingURL directives to the JavaScript as well.
-M, --inline-map
Just like --map, but include the source map directly in the compiled JavaScript files, rather than in a separate file.
-i, --interactive
Launch an interactive CoffeeScript session to try short snippets. Identical to calling coffee with no arguments.
-o, --output [DIR]
Write out all compiled JavaScript files into the specified directory. Use in conjunction with --compile or --watch.
-w, --watch
Watch files for changes, rerunning the specified command when any file is updated.
-p, --print
Instead of writing out the JavaScript as a file, print it directly to stdout.
-s, --stdio
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT. Good for use with processes written in other languages. An example: cat src/cake.coffee | coffee -sc
-l, --literate
Parses the code as Literate CoffeeScript. You only need to specify this when passing in code directly over stdio, or using some sort of extension-less file name.
-e, --eval
Compile and print a little snippet of CoffeeScript directly from the command line. For example: coffee -e "console.log num for num in [10..1]"
-r, --require [MODULE]
require() the given module before starting the REPL or evaluating the code given with the --eval flag.
The node executable has some useful options you can set, such as --debug, --debug-brk, --max-stack-size, and --expose-gc. Use this flag to forward options directly to Node.js. To pass multiple flags, use --nodejs multiple times.
--ast
Generate an abstract syntax tree of nodes of the CoffeeScript. Used for integrating with JavaScript build tools.
--tokens
Instead of parsing the CoffeeScript, just lex it, and print out the token stream. Used for debugging the compiler.
-n, --nodes
Instead of compiling the CoffeeScript, just lex and parse it, and print out the parse tree. Used for debugging the compiler.
Examples:
Compile a directory tree of .coffee files in src into a parallel tree of .js files in lib: 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: coffee -o lib/ -cw src/
Start the CoffeeScript REPL (Ctrl-D to exit, Ctrl-Vfor multi-line): coffee
If you’d like to use Node.js’ CommonJS to require CoffeeScript files, e.g. require './app.coffee', you must first “register” CoffeeScript as an extension:
require'coffeescript/register'
App = require'./app'# The .coffee extension is optional
If you want to use the compiler’s API, for example to make an app that compiles strings of CoffeeScript on the fly, you can require the full module:
CoffeeScript = require'coffeescript'eval CoffeeScript.compile 'console.log "Mmmmm, I could really go for some #{Math.pi}"'
The compile method has the signature compile(code, options) where code is a string of CoffeeScript code, and the optional options is an object with some or all of the following properties:
options.sourceMap, boolean: if true, a source map will be generated; and instead of returning a string, compile will return an object of the form {js, v3SourceMap, sourceMap}.
options.inlineMap, boolean: if true, output the source map as a base64-encoded string in a comment at the bottom.
options.filename, string: the filename to use for the source map. It can include a path (relative or absolute).
options.ast, boolean: if true, return an abstract syntax tree of the input CoffeeScript source code.
Transpilation
CoffeeScript 2 generates JavaScript that uses the latest, modern syntax. The runtime or browsers where you want your code to run might not support all of that syntax. In that case, we want to convert modern JavaScript into older JavaScript that will run in older versions of Node or older browsers; for example, { a } = obj into a = obj.a. This is done via transpilers like Babel, Bublé or Traceur Compiler. See Build Tools.
To make things easy, CoffeeScript has built-in support for the popular Babel transpiler. You can use it via the --transpile command-line option or the transpile Node API option. To use either, @babel/core must be installed in your project:
npm install --save-dev @babel/core
Or if you’re running the coffee command outside of a project folder, using a globally-installed coffeescript module, @babel/core needs to be installed globally:
npm install --global @babel/core
By default, Babel doesn’t do anything—it doesn’t make assumptions about what you want to transpile to. You need to provide it with a configuration so that it knows what to do. One way to do this is by creating a .babelrc file in the folder containing the files you’re compiling, or in any parent folder up the path above those files. (Babel supports other ways, too.) A minimal .babelrc file would be just { "presets": ["@babel/env"] }. This implies that you have installed @babel/preset-env:
npm install --save-dev @babel/preset-env # Or --global for non-project-based usage
Once you have @babel/core and @babel/preset-env (or other presets or plugins) installed, and a .babelrc file (or other equivalent) in place, you can use coffee --transpile to pipe CoffeeScript’s output through Babel using the options you’ve saved.
If you’re using CoffeeScript via the Node API, where you call CoffeeScript.compile with a string to be compiled and an options object, the transpile key of the options object should be the Babel options:
You can also transpile CoffeeScript’s output without using the transpile option, for example as part of a build chain. This lets you use transpilers other than Babel, and it gives you greater control over the process. There are many great task runners for setting up JavaScript build chains, such as Gulp, Webpack, Grunt and Broccoli.
Polyfills
Note that transpiling doesn’t automatically supply polyfills for your code. CoffeeScript itself will output Array.indexOf if you use the in operator, or destructuring or spread/rest syntax; and Function.bind if you use a bound (=>) method in a class. Both are supported in Internet Explorer 9+ and all more recent browsers, but you will need to supply polyfills if you need to support Internet Explorer 8 or below and are using features that would cause these methods to be output. You’ll also need to supply polyfills if your own code uses these methods or another method added in recent versions of JavaScript. One polyfill option is @babel/polyfill, though there are many otherstrategies.
Language Reference
This reference is structured so that it can be read from top to bottom, if you like. Later sections use ideas and syntax previously introduced. Familiarity with JavaScript is assumed. In all of the following examples, the source CoffeeScript is provided on the left, and the direct compilation into JavaScript is on the right.
Many of the examples can be run (where it makes sense) by pressing the ▶ button on the right. The CoffeeScript on the left is editable, and the JavaScript will update as you edit.
First, the basics: CoffeeScript uses significant whitespace to delimit blocks of code. You don’t need to use semicolons ; to terminate expressions, ending the line will do just as well (although semicolons can still be used to fit multiple expressions onto a single line). Instead of using curly braces { } to surround blocks of code in functions, if-statements, switch, and try/catch, use indentation.
You don’t need to use parentheses to invoke a function if you’re passing arguments. The implicit call wraps forward to the end of the line or block expression. console.log sys.inspect object → console.log(sys.inspect(object));
Functions
Functions are defined by an optional list of parameters in parentheses, an arrow, and the function body. The empty function looks like this: ->
Functions may also have default values for arguments, which will be used if the incoming argument is missing (undefined).
Strings
Like JavaScript and many other languages, CoffeeScript supports strings as delimited by the " or ' characters. CoffeeScript also supports string interpolation within "-quoted strings, using #{ … }. Single-quoted strings are literal. You may even use interpolation in object keys.
Multiline strings are allowed in CoffeeScript. Lines are joined by a single space unless they end with a backslash. Indentation is ignored.
Block strings, delimited by """ or ''', can be used to hold formatted or indentation-sensitive text (or, if you just don’t feel like escaping quotes and apostrophes). The indentation level that begins the block is maintained throughout, so you can keep it all aligned with the body of your code.
Double-quoted block strings, like other double-quoted strings, allow interpolation.
Objects and Arrays
The CoffeeScript literals for objects and arrays look very similar to their JavaScript cousins. When each property is listed on its own line, the commas are optional. Objects may be created using indentation instead of explicit braces, similar to YAML.
CoffeeScript has a shortcut for creating objects when you want the key to be set with a variable of the same name. Note that the { and } are required for this shorthand.
Comments
In CoffeeScript, comments are denoted by the # character to the end of a line, or from ### to the next appearance of ###. Comments are ignored by the compiler, though the compiler makes its best effort at reinserting your comments into the output JavaScript after compilation.
The CoffeeScript compiler takes care to make sure that all of your variables are properly declared within lexical scope — you never need to write var yourself.
Notice how all of the variable declarations have been pushed up to the top of the closest scope, the first time they appear. outer is not redeclared within the inner function, because it’s already in scope; inner within the function, on the other hand, should not be able to change the value of the external variable of the same name, and therefore has a declaration of its own.
Because you don’t have direct access to the var keyword, it’s impossible to shadow an outer variable on purpose, you may only refer to it. So be careful that you’re not reusing the name of an external variable accidentally, if you’re writing a deeply nested function.
Although suppressed within this documentation for clarity, all CoffeeScript output (except in files with import or export statements) is wrapped in an anonymous function: (function(){ … })();. This safety wrapper, combined with the automatic generation of the var keyword, make it exceedingly difficult to pollute the global namespace by accident. (The safety wrapper can be disabled with the bare option, and is unnecessary and automatically disabled when using modules.)
If you’d like to create top-level variables for other scripts to use, attach them as properties on window; attach them as properties on the exports object in CommonJS; or use an export statement. If you’re targeting both CommonJS and the browser, the existential operator (covered below), gives you a reliable way to figure out where to add them: exports ? this.
Since CoffeeScript takes care of all variable declaration, it is not possible to declare variables with ES2015’s let or const. This is intentional; we feel that the simplicity gained by not having to think about variable declaration outweighs the benefit of having three separate ways to declare variables.
If, Else, Unless, and Conditional Assignment
if/else statements can be written without the use of parentheses and curly brackets. As with functions and other block expressions, multi-line conditionals are delimited by indentation. There’s also a handy postfix form, with the if or unless at the end.
CoffeeScript can compile if statements into JavaScript expressions, using the ternary operator when possible, and closure wrapping otherwise. There is no explicit ternary statement in CoffeeScript — you simply use a regular if statement on a single line.
Splats, or Rest Parameters/Spread Syntax
The JavaScript arguments object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats ..., both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable. ES2015 adopted this feature as their rest parameters.
Splats also let us elide array elements…
…and object properties.
In ECMAScript this is called spread syntax, and has been supported for arrays since ES2015 and objects since ES2018.
Loops and Comprehensions
Most of the loops you’ll write in CoffeeScript will be comprehensions over arrays, objects, and ranges. Comprehensions replace (and compile into) for loops, with optional guard clauses and the value of the current array index. Unlike for loops, array comprehensions are expressions, and can be returned and assigned.
Comprehensions should be able to handle most places where you otherwise would use a loop, each/forEach, map, or select/filter, for example: shortNames = (name for name in list when name.length < 5)
If you know the start and end of your loop, or would like to step through in fixed-size increments, you can use a range to specify the start and end of your comprehension.
Note how because we are assigning the value of the comprehensions to a variable in the example above, CoffeeScript is collecting the result of each iteration into an array. Sometimes functions end with loops that are intended to run only for their side-effects. Be careful that you’re not accidentally returning the results of the comprehension in these cases, by adding a meaningful return value — like true — or null, to the bottom of your function.
To step through a range comprehension in fixed-size chunks, use by, for example:
evens = (x for x in [0..10] by 2)
If you don’t need the current iteration value you may omit it:
browser.closeCurrentTab() for [0...count]
Comprehensions can also be used to iterate over the keys and values in an object. Use of to signal comprehension over the properties of an object instead of the values in an array.
If you would like to iterate over just the keys that are defined on the object itself, by adding a hasOwnProperty check to avoid properties that may be inherited from the prototype, use for own key, value of object.
The only low-level loop that CoffeeScript provides is the while loop. The main difference from JavaScript is that the while loop can be used as an expression, returning an array containing the result of each iteration through the loop.
For readability, the until keyword is equivalent to while not, and the loop keyword is equivalent to while true.
When using a JavaScript loop to generate functions, it’s common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don’t just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.
Array Slicing and Splicing with Ranges
Ranges can also be used to extract slices of arrays. With two dots (3..6), the range is inclusive (3, 4, 5, 6); with three dots (3...6), the range excludes the end (3, 4, 5). Slices indices have useful defaults. An omitted first index defaults to zero and an omitted second index defaults to the size of the array.
The same syntax can be used with assignment to replace a segment of an array with new values, splicing it.
Note that JavaScript strings are immutable, and can’t be spliced.
Everything is an Expression (at least, as much as possible)
You might have noticed how even though we don’t add return statements to CoffeeScript functions, they nonetheless return their final value. The CoffeeScript compiler tries to make sure that all statements in the language can be used as expressions. Watch how the return gets pushed down into each possible branch of execution in the function below.
Even though functions will always return their final value, it’s both possible and encouraged to return early from a function body writing out the explicit return (return value), when you know that you’re done.
Because variable declarations occur at the top of scope, assignment can be used within expressions, even for variables that haven’t been seen before:
Things that would otherwise be statements in JavaScript, when used as part of an expression in CoffeeScript, are converted into expressions by wrapping them in a closure. This lets you do useful things, like assign the result of a comprehension to a variable:
As well as silly things, like passing a try/catch statement directly into a function call:
There are a handful of statements in JavaScript that can’t be meaningfully converted into expressions, namely break, continue, and return. If you make use of them within a block of code, CoffeeScript won’t try to perform the conversion.
Operators and Aliases
Because the == operator frequently causes undesirable coercion, is intransitive, and has a different meaning than in other languages, CoffeeScript compiles == into ===, and != into !==. In addition, is compiles into ===, and isnt into !==.
You can use not as an alias for !.
For logic, and compiles to &&, and or into ||.
Instead of a newline or semicolon, then can be used to separate conditions from expressions, in while, if/else, and switch/when statements.
As in YAML, on and yes are the same as boolean true, while off and no are boolean false.
unless can be used as the inverse of if.
As a shortcut for this.property, you can use @property.
You can use in to test for array presence, and of to test for JavaScript object-key presence.
In a for loop, from compiles to the ES2015 of. (Yes, it’s unfortunate; the CoffeeScript of predates the ES2015 of.)
To simplify math expressions, ** can be used for exponentiation and // performs floor division. % works just like in JavaScript, while %% provides “dividend dependent modulo”:
All together now:
CoffeeScript
JavaScript
is
===
isnt
!==
not
!
and
&&
or
||
true, yes, on
true
false, no, off
false
@, this
this
a in b
[].indexOf.call(b, a) >= 0
a of b
a in b
for a from b
for (a of b)
a ** b
a ** b
a // b
Math.floor(a / b)
a %% b
(a % b + b) % b
The Existential Operator
It’s a little difficult to check for the existence of a variable in JavaScript. if (variable) … comes close, but fails for zero, the empty string, and false (to name just the most common cases). CoffeeScript’s existential operator ? returns true unless a variable is null or undefined or undeclared, which makes it analogous to Ruby’s nil?.
It can also be used for safer conditional assignment than the JavaScript pattern a = a || value provides, for cases where you may be handling numbers or strings.
Note that if the compiler knows that a is in scope and therefore declared, a? compiles to a != null, nota !== null. The != makes a loose comparison to null, which does double duty also comparing against undefined. The reverse also holds for not a? or unless a?.
If a variable might be undeclared, the compiler does a thorough check. This is what JavaScript coders should be typing when they want to check if a mystery variable exists.
The accessor variant of the existential operator ?. can be used to soak up null references in a chain of properties. Use it instead of the dot accessor . in cases where the base value may be null or undefined. If all of the properties exist then you’ll get the expected result, if the chain is broken, undefined is returned instead of the TypeError that would be raised otherwise.
For completeness:
Example
Definition
a?
tests that a is in scope and a != null
a ? b
returns a if a is in scope and a != null; otherwise, b
a?.b or a?['b']
returns a.b if a is in scope and a != null; otherwise, undefined
a?(b, c) or a? b, c
returns the result of calling a (with arguments b and c) if a is in scope and callable; otherwise, undefined
a ?= b
assigns the value of b to a if a is not in scope or if a == null; produces the new value of a
Chaining Function Calls
Leading . closes all open calls, allowing for simpler chaining syntax.
Destructuring Assignment
Just like JavaScript (since ES2015), CoffeeScript has destructuring assignment syntax. When you assign an array or object literal to a value, CoffeeScript breaks up and matches both sides against each other, assigning the values on the right to the variables on the left. In the simplest case, it can be used for parallel assignment:
But it’s also helpful for dealing with functions that return multiple values.
Destructuring assignment can be used with any depth of array and object nesting, to help pull out deeply nested properties.
Destructuring assignment can even be combined with splats.
Expansion can be used to retrieve elements from the end of an array without having to assign the rest of its values. It works in function parameter lists as well.
Destructuring assignment is also useful when combined with class constructors to assign properties to your instance from an options object passed to the constructor.
The above example also demonstrates that if properties are missing in the destructured object or array, you can, just like in JavaScript, provide defaults. Note though that unlike with the existential operator, the default is only applied with the value is missing or undefined—passing null will set a value of null, not the default.
Bound (Fat Arrow) Functions
In JavaScript, the this keyword is dynamically scoped to mean the object that the current function is attached to. If you pass a function as a callback or attach it to a different object, the original value of this will be lost. If you’re not familiar with this behavior, this Digital Web article gives a good overview of the quirks.
The fat arrow => can be used to both define a function, and to bind it to the current value of this, right on the spot. This is helpful when using callback-based libraries like Prototype or jQuery, for creating iterator functions to pass to each, or event-handler functions to use with on. Functions created with the fat arrow are able to access properties of the this where they’re defined.
If we had used -> in the callback above, @customer would have referred to the undefined “customer” property of the DOM element, and trying to call purchase() on it would have raised an exception.
The fat arrow was one of the most popular features of CoffeeScript, and ES2015 adopted it; so CoffeeScript 2 compiles => to ES =>.
Generator Functions
CoffeeScript supports ES2015 generator functions through the yield keyword. There’s no function*(){} nonsense — a generator in CoffeeScript is simply a function that yields.
yield* is called yield from, and yield return may be used if you need to force a generator that doesn’t yield.
You can iterate over a generator function using for…from.
Async Functions
ES2017’s async functions are supported through the await keyword. Like with generators, there’s no need for an async keyword; an async function in CoffeeScript is simply a function that awaits.
Similar to how yield return forces a generator, await return may be used to force a function to be async.
Classes
CoffeeScript 1 provided the class and extends keywords as syntactic sugar for working with prototypal functions. With ES2015, JavaScript has adopted those keywords; so CoffeeScript 2 compiles its class and extends keywords to ES2015 classes.
Static methods can be defined using @ before the method name:
Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. In the context of a class definition, this is the class object itself; therefore, you can assign static properties by using @property: value.
Prototypal Inheritance
In addition to supporting ES2015 classes, CoffeeScript provides a shortcut for working with prototypes. The :: operator gives you quick access to an object’s prototype:
Switch/When/Else
switch statements in JavaScript are a bit awkward. You need to remember to break at the end of every case statement to avoid accidentally falling through to the default case. CoffeeScript prevents accidental fall-through, and can convert the switch into a returnable, assignable expression. The format is: switch condition, when clauses, else the default case.
As in Ruby, switch statements in CoffeeScript can take multiple values for each when clause. If any of the values match, the clause runs.
switch statements can also be used without a control expression, turning them in to a cleaner alternative to if/else chains.
Try/Catch/Finally
try expressions have the same semantics as try statements in JavaScript, though in CoffeeScript, you may omit both the catch and finally parts. The catch part may also omit the error parameter if it is not needed.
Chained Comparisons
CoffeeScript borrows chained comparisons from Python — making it easy to test if a value falls within a certain range.
Block Regular Expressions
Similar to block strings and comments, CoffeeScript supports block regexes — extended regular expressions that ignore internal whitespace and can contain comments and interpolation. Modeled after Perl’s /x modifier, CoffeeScript’s block regexes are delimited by /// and go a long way towards making complex regular expressions readable. To quote from the CoffeeScript source:
Tagged Template Literals
CoffeeScript supports ES2015 tagged template literals, which enable customized string interpolation. If you immediately prefix a string with a function name (no space between the two), CoffeeScript will output this “function plus string” combination as an ES2015 tagged template literal, which will behave accordingly: the function is called, with the parameters being the input text and expression parts that make up the interpolated string. The function can then assemble these parts into an output string, providing custom string interpolation.
Modules
ES2015 modules are supported in CoffeeScript, with very similar import and export syntax:
Dynamic import is also supported, with mandatory parentheses:
Note that the CoffeeScript compiler does not resolve modules; writing an import or export statement in CoffeeScript will produce an import or export statement in the resulting output. Such statements can be run by all modern browsers (when the script is referenced via <script type="module">) and by Node.js when the output .js files are in a folder where the nearest parent package.json file contains "type": "module". Because the runtime is evaluating the generated output, the import statements must reference the output files; so if file.coffee is output as file.js, it needs to be referenced as file.js in the import statement, with the .js extension included.
Also, any file with an import or export statement will be output without a top-level function safety wrapper; in other words, importing or exporting modules will automatically trigger bare mode for that file. This is because per the ES2015 spec, import or export statements must occur at the topmost scope.
Embedded JavaScript
Hopefully, you’ll never need to use it, but if you ever need to intersperse snippets of JavaScript within your CoffeeScript, you can use backticks to pass it straight through.
Escape backticks with backslashes: \` becomes `.
Escape backslashes before backticks with more backslashes: \\\` becomes \`.
You can also embed blocks of JavaScript using triple backticks. That’s easier than escaping backticks, if you need them inside your JavaScript block.
JSX
JSX is JavaScript containing interspersed XML elements. While conceived for React, it is not specific to any particular library or framework.
CoffeeScript supports interspersed XML elements, without the need for separate plugins or special settings. The XML elements will be compiled as such, outputting JSX that could be parsed like any normal JSX file, for example by Babel with the React JSX transform. CoffeeScript does not output React.createElement calls or any code specific to React or any other framework. It is up to you to attach another step in your build chain to convert this JSX to whatever function calls you wish the XML elements to compile to.
Just like in JSX and HTML, denote XML tags using < and >. You can interpolate CoffeeScript code inside a tag using { and }. To avoid compiler errors, when using < and > to mean “less than” or “greater than,” you should wrap the operators in spaces to distinguish them from XML tags. So i < len, not i<len. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.
Older plugins or forks of CoffeeScript supported JSX syntax and referred to it as CSX or CJSX. They also often used a .cjsx file extension, but this is no longer necessary; regular .coffee will do.
CoffeeScript does not do any type checking itself; the JavaScript output you see above needs to get passed to Flow for it to validate your code. We expect most people will use a build tool for this, but here’s how to do it the simplest way possible using the CoffeeScript and Flow command-line tools, assuming you’ve already installed Flow and the latest CoffeeScript in your project folder:
coffee --bare --no-header --compile app.coffee && npm run flow
--bare and --no-header are important because Flow requires the first line of the file to be the comment // @flow. If you configure your build chain to compile CoffeeScript and pass the result to Flow in-memory, you can get better performance than this example; and a proper build tool should be able to watch your CoffeeScript files and recompile and type-check them for you on save.
If you know of another way to achieve static type checking with CoffeeScript, please create an issue and let us know.
Literate CoffeeScript
Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a .litcoffee extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdown’s way of indicating source code) as executable code, and ignore the rest as comments. Code blocks must also be separated from comments by at least one blank line.
Code blocks need to maintain consistent indentation relative to each other. When the compiler parses your Literate CoffeeScript file, it first discards all the non-code block lines and then parses the remainder as a regular CoffeeScript file. Therefore the code blocks need to be written as if the comment lines don’t exist, with consistent indentation (including whether they are indented with tabs or spaces).
Along those lines, code blocks within list items or blockquotes are not treated as executable code. Since list items and blockquotes imply their own indentation, it would be ambiguous how to treat indentation between successive code blocks when some are within these other blocks and some are not.
List items can be at most only one paragraph long. The second paragraph of a list item would be indented after a blank line, and therefore indistinguishable from a code block.
Source Maps
CoffeeScript includes support for generating source maps, a way to tell your JavaScript engine what part of your CoffeeScript program matches up with the code being evaluated. Browsers that support it can automatically use source maps to show your original source code in the debugger. To generate source maps alongside your JavaScript files, pass the --map or -m flag to the compiler.
For a full introduction to source maps, how they work, and how to hook them up in your browser, read the HTML5 Tutorial.
Cake, and Cakefiles
CoffeeScript includes a (very) simple build system similar to Make and Rake. Naturally, it’s called Cake, and is used for the tasks that build and test the CoffeeScript language itself. Tasks are defined in a file named Cakefile, and can be invoked by running cake [task] from within the directory. To print a list of all the tasks and options, just type cake.
Task definitions are written in CoffeeScript, so you can put arbitrary code in your Cakefile. Define a task with a name, a long description, and the function to invoke when the task is run. If your task takes a command-line option, you can define the option with short and long flags, and it will be made available in the options object. Here’s a task that uses the Node.js API to rebuild CoffeeScript’s parser:
If you need to invoke one task before another — for example, running build before test, you can use the invoke function: invoke 'build'. Cake tasks are a minimal way to expose your CoffeeScript functions to the command line, so don’t expect any fanciness built-in. If you need dependencies, or async callbacks, it’s best to put them in your code itself — not the cake task.
"text/coffeescript" Script Tags
While it’s not recommended for serious use, CoffeeScripts may be included directly within the browser using <script type="text/coffeescript"> tags. The source includes a compressed and minified version of the compiler (Download current version here, 77k when gzipped) as docs/v2/browser-compiler-legacy/coffeescript.js. Include this file on a page with inline CoffeeScript tags, and it will compile and evaluate them in order.
The usual caveats about CoffeeScript apply — your inline scripts will run within a closure wrapper, so if you want to expose global variables or functions, attach them to the window object.
Integrations
CoffeeScript is part of the vast JavaScript ecosystem, and many libraries help integrate CoffeeScript with JavaScript. Major projects, especially projects updated to work with CoffeeScript 2, are listed here; more can be found in the wiki pages. If there’s a project that you feel should be added to this section, please open an issue or pull request. Projects are listed in alphabetical order by category.
CoffeeScript Issues
Bug reports, feature proposals, and ideas for changes to the language belong here.
CoffeeScript Google Group
If you’d like to ask a question, the mailing list is a good place to get help.
The CoffeeScript Wiki
If you’ve ever learned a neat CoffeeScript tip or trick, or ran into a gotcha — share it on the wiki.
The FAQ
Perhaps your CoffeeScript-related question has been asked before. Check the FAQ first.
JS2Coffee
Is a very well done reverse JavaScript-to-CoffeeScript compiler. It’s not going to be perfect (infer what your JavaScript classes are, when you need bound functions, and so on…) — but it’s a great starting point for converting simple scripts.
High-Rez Logo
The CoffeeScript logo is available in SVG for use in presentations.
Books
There are a number of excellent resources to help you get started with CoffeeScript, some of which are freely available online.
Smooth CoffeeScript is a reimagination of the excellent book Eloquent JavaScript, as if it had been written in CoffeeScript instead. Covers language features as well as the functional and object oriented programming styles. By E. Hoigaard.
CoffeeScript: Accelerated JavaScript Development is Trevor Burnham’s thorough introduction to the language. By the end of the book, you’ll have built a fast-paced multiplayer word game, writing both the client-side and Node.js portions in CoffeeScript.
CoffeeScript Ristretto is a deep dive into CoffeeScript’s semantics from simple functions up through closures, higher-order functions, objects, classes, combinators, and decorators. By Reg Braithwaite.
Testing with CoffeeScript is a succinct and freely downloadable guide to building testable applications with CoffeeScript and Jasmine.
Meet CoffeeScript is a 75-minute long screencast by PeepCode, now PluralSight. Highly memorable for its animations which demonstrate transforming CoffeeScript into the equivalent JS.
If you’re looking for less of a time commitment, RailsCasts’ CoffeeScript Basics should have you covered, hitting all of the important notes about CoffeeScript in 11 minutes.
Contributions are welcome! Feel free to fork the repo and submit a pull request.
Some features of ECMAScript are intentionally unsupported. Please review both the open and closed issues on GitHub to see if the feature you’re looking for has already been discussed. As a general rule, we don’t support ECMAScript syntax for features that aren’t yet finalized (at Stage 4 in the proposal approval process) or implemented in major browsers and/or Node (which can sometimes happen for features in Stage 3). Any Stage 3 features that CoffeeScript chooses to support should be considered experimental, subject to breaking changes or removal until the feature reaches Stage 4.
There are several things you can do to increase your odds of having your pull request accepted:
Create tests! Any pull request should probably include basic tests to verify you didn’t break anything, or future changes won’t break your code.
Follow the style of the rest of the CoffeeScript codebase.
Ensure any ECMAScript syntax is mature (at Stage 4, or at Stage 3 with support in major browsers or runtimes).
Add only features that have broad utility, rather than a feature aimed at a specific use case or framework.
Of course, it’s entirely possible that you have a great addition, but it doesn’t fit within these constraints. Feel free to roll your own solution; you will have plenty of company.
Unsupported ECMAScript Features
There are a few ECMAScript features that CoffeeScript intentionally doesn’t support.
let and const: block-scoped and reassignment-protected variables
When CoffeeScript was designed, var was intentionally omitted. This was to spare developers the mental housekeeping of needing to worry about variable declaration (var foo) as opposed to variable assignment (foo = 1). The CoffeeScript compiler automatically takes care of declaration for you, by generating var statements at the top of every function scope. This makes it impossible to accidentally declare a global variable.
let and const add a useful ability to JavaScript in that you can use them to declare variables within a block scope, for example within an if statement body or a for loop body, whereas var always declares variables in the scope of an entire function. When CoffeeScript 2 was designed, there was much discussion of whether this functionality was useful enough to outweigh the simplicity offered by never needing to consider variable declaration in CoffeeScript. In the end, it was decided that the simplicity was more valued. In CoffeeScript there remains only one type of variable.
Keep in mind that const only protects you from reassigning a variable; it doesn’t prevent the variable’s value from changing, the way constants usually do in other languages:
Newcomers to CoffeeScript often wonder how to generate the JavaScript function foo() {}, as opposed to the foo = function() {} that CoffeeScript produces. The first form is a function declaration, and the second is a function expression. As stated above, in CoffeeScript everything is an expression, so naturally we favor the expression form. Supporting only one variant helps avoid confusing bugs that can arise from the subtle differences between the two forms.
Technically, foo = function() {} is creating an anonymous function that gets assigned to a variable named foo. Some very early versions of CoffeeScript named this function, e.g. foo = function foo() {}, but this was dropped because of compatibility issues with Internet Explorer. For a while this annoyed people, as these functions would be unnamed in stack traces; but modern JavaScript runtimes infer the names of such anonymous functions from the names of the variables to which they’re assigned. Given that this is the case, it’s simplest to just preserve the current behavior.
get and set keyword shorthand syntax
get and set, as keywords preceding functions or class methods, are intentionally unimplemented in CoffeeScript.
This is to avoid grammatical ambiguity, since in CoffeeScript such a construct looks identical to a function call (e.g. get(function foo() {})); and because there is an alternate syntax that is slightly more verbose but just as effective:
Breaking Changes From CoffeeScript 1.x to 2
CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.
Bound (fat arrow) functions
In CoffeeScript 1.x, => compiled to a regular function but with references to this/@ rewritten to use the outer scope’s this, or with the inner function bound to the outer scope via .bind (hence the name “bound function”). In CoffeeScript 2, => compiles to ES2015’s =>, which behaves slightly differently. The largest difference is that in ES2015, => functions lack an arguments object:
Default values for function parameters and destructured elements
Bound generator functions, a.k.a. generator arrow functions, aren’t allowed in ECMAScript. You can write function* or =>, but not both. Therefore, CoffeeScript code like this:
f = =>yield this
# Throws a compiler error
Needs to be rewritten the old-fashioned way:
Classes are compiled to ES2015 classes
ES2015 classes and their methods have some restrictions beyond those on regular functions.
Class constructors can’t be invoked without new:
(class)()
# Throws a TypeError at runtime
ES2015 classes don’t allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that can’t be accommodated is calling a bound method before it is bound:
classBase
constructor: ->
@onClick() # This works
clickHandler = @onClick
clickHandler() # This throws a runtime errorclassComponentextendsBase
onClick: =>
console.log 'Clicked!', @
Class methods can’t be used with new (uncommon):
classNamespace
@Klass = ->new Namespace.Klass # Throws a TypeError at runtime
Due to the hoisting required to compile to ES2015 classes, dynamic keys in class methods can’t use values from the executable class body unless the methods are assigned in prototype style.
classA
name = 'method'"#{name}": -># This method will be named 'undefined'
@::[name] = -># This will work; assigns to `A.prototype.method`
super and this
In the constructor of a derived class (a class that extends another class), this cannot be used before calling super:
classBextendsA
constructor: -> this # Throws a compiler error
This also means you cannot pass a reference to this as an argument to super in the constructor of a derived class:
classBextendsA
constructor: (@arg) ->
super @arg # Throws a compiler error
This is a limitation of ES2015 classes. As a workaround, assign to this after the super call:
super and extends
Due to a syntax clash with super with accessors, “bare” super (the keyword super without parentheses) no longer compiles to a super call forwarding all arguments.
classBextendsA
foo: -> super
# Throws a compiler error
Arguments can be forwarded explicitly using splats:
Or if you know that the parent function doesn’t require arguments, just call super():
CoffeeScript 1.x allowed the extends keyword to set up prototypal inheritance between functions, and super could be used manually prototype-assigned functions:
A = ->B = ->
B extends A
B.prototype.foo = -> super arguments...
# Last two lines each throw compiler errors in CoffeeScript 2
Due to the switch to ES2015 extends and super, using these keywords for prototypal functions are no longer supported. The above case could be refactored to:
or
JSX and the < and > operators
With the addition of JSX, the < and > characters serve as both the “less than” and “greater than” operators and as the delimiters for XML tags, like <div>. For best results, in general you should always wrap the operators in spaces to distinguish them from XML tags: i < len, not i<len. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.
Literate CoffeeScript parsing
CoffeeScript 2’s parsing of Literate CoffeeScript has been refactored to now be more careful about not treating indented lists as code blocks; but this means that all code blocks (unless they are to be interpreted as comments) must be separated by at least one blank line from lists.
Code blocks should also now maintain a consistent indentation level—so an indentation of one tab (or whatever you consider to be a tab stop, like 2 spaces or 4 spaces) should be treated as your code’s “left margin,” with all code in the file relative to that column.
Code blocks that you want to be part of the commentary, and not executed, must have at least one line (ideally the first line of the block) completely unindented.
Argument parsing and shebang (#!) lines
In CoffeeScript 1.x, -- was required after the path and filename of the script to be run, but before any arguments passed to that script. This convention is now deprecated. So instead of:
coffee [options] path/to/script.coffee -- [args]
Now you would just type:
coffee [options] path/to/script.coffee [args]
The deprecated version will still work, but it will print a warning before running the script.
On non-Windows platforms, a .coffee file can be made executable by adding a shebang (#!) line at the top of the file and marking the file as executable. For example:
#!/usr/bin/env coffee
x = 2 + 2
console.log x
If this were saved as executable.coffee, it could be made executable and run:
In CoffeeScript 1.x, this used to fail when trying to pass arguments to the script. Some users on OS X worked around the problem by using #!/usr/bin/env coffee -- as the first line of the file. That didn’t work on Linux, however, which cannot parse shebang lines with more than a single argument. While such scripts will still run on OS X, CoffeeScript will now display a warning before compiling or evaluating files that begin with a too-long shebang line. Now that CoffeeScript 2 supports passing arguments without needing --, we recommend simply changing the shebang lines in such scripts to just #!/usr/bin/env coffee.
The import assertions syntax is now supported. This allows statements like export { version } from './package.json' assert { type: 'json' } or expressions like import('./calendar.json', { assert { type: 'json' } }).
CoffeeScript no longer always patches Node’s error stack traces. This patching, where the line and column numbers are adjusted to match the source CoffeeScript rather than the generated JavaScript, caused conflicts with other libraries and is unnecessary when Node’s new --enable-source-maps flag is passed. The patching will now occur only when --enable-source-maps is not set, no other library has already patched the stack traces, and require('coffeescript/register') is used. The patching can be enabled explicitly via require('coffeescript').patchStackTrace() or import { patchStackTrace } from 'coffeescript'; patchStackTrace().
Bugfix for an issue where block (triple-quoted) strings weren’t getting transpiled correctly into a JSX expression container wrapping the template literal (such as <div a={`...`} />).
Bugfixes for line continuations not behaving as expected for a nonempty first line of an explicit [ array or { object literal.
The coffeescript package itself now supports named exports when used by ES modules in Node.js; or in other words, import { compile } from 'coffeescript' now works, rather than only import CoffeeScript from 'coffeescript'.
Bugfix for a stack overflow error when compiling large files in non-bare mode.
The compiler now supports a new ast option, available via --ast on the command line or ast via the Node API. This option outputs an “abstract syntax tree,” or a JSON-like representation of the input CoffeeScript source code. This AST follows Babel’s spec as closely as possible, for compatibility with tools that work with JavaScript source code. Two tools that use this new AST output are eslint-plugin-coffee, a plugin to lint CoffeeScript via ESLint; and prettier-plugin-coffeescript, a plugin to reformat CoffeeScript source code via Prettier. The structure and properties of CoffeeScript’s AST are not final and may undergo breaking changes between CoffeeScript versions; please open an issue if you are interested in creating new integrations.
Numeric separators are now supported in CoffeeScript, following the same syntax as JavaScript: 1_234_567.
BigInt numbers are now supported in CoffeeScript, following the same syntax as JavaScript: 42n.
''' and """ strings are now output as more readable JavaScript template literals, or backtick (`) strings, with actual newlines rather than \n escape sequences.
Classes can now contain computed properties, e.g. [someVar]: -> or @[anotherVar]: ->.
JSX tags can now contain XML-style namespaces, e.g. <image xlink:href="data:image/png" /> or <Something:Tag></Something:Tag>.
Bugfixes for comments after colons not appearing the output; reserved words mistakenly being disallowed as JSX attributes; indented leading elisions in multiline arrays; and invalid location data in source maps.
Both the traditional ES5 and modern ES module versions of the CoffeeScript browser compiler are now published to NPM, enabling the browser compilers’ use via services that provide NPM modules’ code available via public CDN. The traditional version is referenced via the package.json"browser" field, and the ES module version via the "module" field.
Dynamic import() expressions are now supported. The parentheses are always required, to distinguish from import statements. See Modules. Note that as of this writing, the JavaScript feature itself is still Stage 3; if it changes before being fully standardized, it may change in CoffeeScript too. Using import() before its upstream ECMAScript proposal is finalized should be considered provisional, subject to breaking changes if the proposal changes or is rejected. We have also revised our policy on Stage 3 ECMAScript features, to support them when the features are shipped in significant runtimes such as major browsers or Node.js.
There are now two browser versions of the CoffeeScript compiler: the traditional one that’s been published for years, and a new ES module version that can be used via import. If your browser supports it, it is in effect on this page. A reference to the ES module browser compiler is in the package.json"module" field.
The Node API now exposes the previously private registerCompiled method, to allow plugins that use the coffeescript package to take advantage of CoffeeScript’s internal caching.
Bugfixes for commas in strings in block arrays, a reference to @ not being maintained in a do block in a class, and function default parameters should no longer be wrapped by extraneous parentheses.
Babel 7 is now supported. With version 7, the Babel team moved from babel-core on NPM to @babel/core. Now the CoffeeScript --transpile option will first search for @babel/core (Babel versions 7 and above) and then search for babel-core (versions 6 and below) to try to find an installed version of Babel to use for transpilation.
You can now follow the keyword yield with an indented object, like has already been allowed for return and other keywords.
Previously, any comments inside a JSX tag or attribute would cause interpolation braces ({ and }) to be output. This is only necessary for line (#, or // in JavaScript) comments, not here (###, or /* */) comments; so now the compiler checks if all the comments that would trigger the braces are here comments, and if so it doesn’t generate the unnecessary interpolation braces.
Returning a JSX tag that is adjacent to another JSX tag, as opposed to returning a root JSX tag or fragment, is invalid JSX syntax. Babel throws an error on this, and now the CoffeeScript compiler does too.
Invalid indentation inside a JSX interpolation (the middle of <tag>{ ... }</tag>) now throws an error.
The browser compiler, used in Try CoffeeScript and similar web-based CoffeeScript editors, now evaluates code in a global scope rather than the scope of the browser compiler. This improves performance of code executed via the browser compiler.
Syntax cleanup: it is now possible for an implicit function call to take a body-less class as an argument, and ?:: now behaves identically to :: with regard to implying a line continuation.
This release adds support for all the new features and syntaxes in ES2018 that weren’t already possible in CoffeeScript. For all of the below features, make sure that you transpile unless you know that your target runtime(s) support each feature.
Asynchronous iterators are now supported. You can now yield an await call, e.g. do -> until file.EOF then yield await file.readLine().
Object splats/destructuring, a.k.a. object rest/spread syntax, has been standardized as part of ES2018 and therefore this release removes the polyfill that had previously been supporting this syntax. Code like {a, b, rest...} = obj now outputs more or less just like it appears, rather than being converted into an Object.assign call. Note that there are some subtle differences between the Object.assign polyfill and the native implementation.
The exponentiation operator, **, and exponentiation assignment operator **= are new to JavaScript in ES2018. Now code like a ** 3 is output as it appears, rather than being converted into Math.pow(a, 3) as it was before.
The s (dotAll) flag is now supported in regular expressions.
When the by value in a for loop is a literal number, e.g. for x in [2..1] by -1, fewer checks are necessary to determine if the loop is in range.
Bugfix for regression in 2.2.0 where a statement inside parentheses, e.g. (fn(); break) while condition, was compiling. Pure statements like break or return cannot turn a parenthesized block into an expression, and should throw an error.
Bugfix for regression in 2.2.0 where a range with a by (step) value that increments or decrements in the opposite direction as the range was returning an array containing the first value of the range, whereas it should be returning an empty array. In other words, x for x in [2..1] by 1 should equal [], not [2] (because the step value is positive 1, counting up, whereas the range goes from 2 to 1, counting down).
Bugfixes for allowing backslashes in import and export statements and lines that trigger the start of an indented block, like an if statement.
Bugfix for regression in 2.2.0 involving an error thrown by the compiler in certain cases when using destructuring with a splat or expansion in an array.
Bugfix for regression in 2.2.0 where in certain cases a range iterator variable was declared in the global scope.
This release fixes all currently open bugs, dating as far back as 2014, 2012 and 2011.
Potential breaking change: An inline if or switch statement with an ambiguous else, such as if no then if yes then alert 1 else alert 2, now compiles where the else always corresponds to the closest open then. Previously the behavior of an ambiguous else was unpredictable. If your code has any if … then or switch … then statements with multiple thens (and one or more elses) the compiled output might be different now, unless you had resolved ambiguity via parentheses. We made this change because the previous behavior was inconsistent and basically a bug: depending on what grammar was where, for example if there was an inline function or something that implied a block, the else might bind to an earlier then rather than a later then. Now an else essentially closes a block opened by a then, similar to closing an open parenthesis.
When a required then is missing, the error more accurately points out the location of the mistake.
An error is thrown when the coffee command is run in an environment that doesn’t support some ES2015 JavaScript features that the CoffeeScript compiler itself requires. This can happen if CoffeeScript is installed in Node older than version 6.
Destructuring with a non-final splat/spread, e.g. [open, contents..., close] = tag.split('') is now output using ES2015 rest syntax.
Functions named get or set can be used without parentheses in more cases, including when attached to this or @ or ?.; or when the first argument is an implicit object, e.g. @set key: 'val'.
Statements such as break can now be used inside parentheses, e.g. (doSomething(); break) while condition or (pick(key); break) for key of obj.
Bugfix for assigning to a property attached to this/@ in destructuring, e.g. ({@prop = yes, @otherProp = no}) ->.
Bugfix for incorrect errors being thrown about calling super with a parameter attached to this when said parameter is in a lower scope, e.g. class Child extends Parent then constructor: -> super(-> @prop).
Bugfix to prevent a possible infinite loop when a for loop is given a variable to step by, e.g. for x in [1..3] by step (as opposed to by 0.5 or some other primitive numeric value).
Bugfix to no longer declare iterator variables twice when evaluating a range, e.g. end = 3; fn [0..end].
Bugfix for incorrect scope of variables in chained calls, e.g. start(x = 3).then(-> x = 4).
Bugfix for incorrect scope of variables in a function passed to do, e.g. for [1..3] then masked = 10; do -> alert masked.
Bugfix to no longer throw a syntax error for a trailing comma in a function call, e.g. fn arg1, arg2,.
Bugfix for an expression in a property access, e.g. a[!b in c..].
Bugfix to allow a line continuation backslash (\) at any point in a for line.
Bugfix to set the correct context for executable class bodies. So in class @B extends @A then @property = 1, the @ in @property now refers to the class, not the global object.
Bugfix where anonymous classes were getting created using the same automatic variable name. They now each receive unique names, so as not to override each other.
Bugfix for export default followed by an implicit object that contains an explicit object, for example exportedMember: { obj... }.
Bugfix for key, val of obj after an implicit object member, e.g. foo: bar for key, val of obj.
Bugfix for combining array and object destructuring, e.g. [ ..., {a, b} ] = arr.
Bugfix for an edge case where it was possible to create a bound (=>) generator function, which should throw an error as such functions aren’t allowed in ES2015.
Bugfix for source maps: .map files should always have the same base filename as the requested output filename. So coffee --map --output foo.js test.coffee should generate foo.js and foo.js.map.
Bugfix for incorrect source maps generated when using --transpile with --map for multiple input files.
Bugfix for comments at the beginning or end of input into the REPL (coffee --interactive).
--transpile now also applies to required or imported CoffeeScript files.
--transpile can be used with the REPL: coffee --interactive --transpile.
Improvements to comments output that should now cover all of the Flow comment-based syntax. Inline ### comments near variable initial assignments are now output in the variable declaration statement, and ### comments near a class and method names are now output where Flow expects them.
Importing CoffeeScript keywords is now allowed, so long as they’re aliased: import { and as andFn } from 'lib'. (You could also do import lib from 'lib' and then reference lib.and.)
Calls to functions named get and set no longer throw an error when given a bracketless object literal as an argument: obj.set propertyName: propertyValue.
In the constructor of a derived class (a class that extends another class), you cannot call super with an argument that references this: class Child extends Parent then constructor: (@arg) -> super(@arg). This isn’t allowed in JavaScript, and now the CoffeeScript compiler will throw an error. Instead, assign to this after calling super: (arg) -> super(arg); @arg = arg.
Bugfix for incorrect output when backticked statements and hoisted expressions were both in the same class body. This allows a backticked line like `field = 3`, for people using the experimental class fields syntax, in the same class along with traditional class body expressions like prop: 3 that CoffeeScript outputs as part of the class prototype.
Bugfix for comments not output before a complex ? operation, e.g. @a ? b.
babel-core is no longer listed in package.json, even as an optionalDependency, to avoid it being automatically installed for most users. If you wish to use --transpile, simply install babel-core manually. See Transpilation.
--transpile now relies on Babel to find its options, i.e. the .babelrc file in the path of the file(s) being compiled. (Previously the CoffeeScript compiler was duplicating this logic, so nothing has changed from a user’s perspective.) This provides automatic support for additional ways to pass options to Babel in future versions, such as the .babelrc.js file coming in Babel 7.
Backticked expressions in a class body, outside any class methods, are now output in the JavaScript class body itself. This allows for passing through experimental JavaScript syntax like the class fields proposal, assuming your transpiler supports it.
Added --transpile flag or transpile Node API option to tell the CoffeeScript compiler to pipe its output through Babel before saving or returning it; see Transpilation. Also changed the -t short flag to refer to --transpile instead of --tokens.
Node 6 is now supported, and we will try to maintain that as the minimum required version for CoffeeScript 2 via the coffee command or Node API. Older versions of Node, or non-evergreen browsers, can compile via the legacy browser compiler.
The command line --output flag now allows you to specify an output filename, not just an output folder.
The command line --require flag now properly handles filenames or module names that are invalid identifiers (like an NPM module with a hyphen in the name).
Object.assign, output when object destructuring is used, is polyfilled using the same polyfill that Babel outputs. This means that polyfills shouldn’t be required unless support for Internet Explorer 8 or below is desired (or your own code uses a feature that requires a polyfill). See ES2015+ Output.
A string or JSX interpolation that contains only a comment ("a#{### comment ###}b" or <div>{### comment ###}</div>) is now output (`a${/* comment */}b`)
Interpolated strings (ES2015 template literals) that contain quotation marks no longer have the quotation marks escaped: `say "${message}"`
It is now possible to chain after a function literal (for example, to define a function and then call .call on it).
The results of the async tests are included in the output when you run cake test.
Bugfixes for object destructuring; expansions in function parameters; generated reference variables in function parameters; chained functions after do; splats after existential operator soaks in arrays ([a?.b...]); trailing if with splat in arrays or function parameters ([a if b...]); attempting to throw an if, for, switch, while or other invalid construct.
Bugfixes for syntactical edge cases: semicolons after = and other “mid-expression” tokens; spaces after ::; and scripts that begin with : or *.
Bugfixes for source maps generated via the Node API; and stack trace line numbers when compiling CoffeeScript via the Node API from within a .coffee file.
Line comments (starting with #) are now output in the generated JavaScript.
Block comments (delimited by ###) are now allowed anywhere, including inline where they previously weren’t possible. This provides support for static type annotations using Flow’s comments-based syntax.
Spread syntax (... for objects) is now supported in JSX tags: <div {props...} />.
Argument parsing for scripts run via coffee is improved. See breaking changes.
CLI: Propagate SIGINT and SIGTERM signals when node is forked.
await in the REPL is now allowed without requiring a wrapper function.
do super is now allowed, and other accesses of super like super.x.y or super['x'].y now work.
Splat/spread syntax triple dots are now allowed on either the left or the right (so props... or ...props are both valid).
Tagged template literals are recognized as callable functions.
Bugfixes for object spread syntax in nested properties.
Bugfixes for destructured function parameter default values.
Bound (fat arrow) methods are once again supported in classes; though an error will be thrown if you attempt to call the method before it is bound. See breaking changes for classes.
The REPL no longer warns about assigning to _.
Bugfixes for destructured nested default values and issues related to chaining or continuing expressions across multiple lines.
Bound (fat arrow) methods in classes must be declared in the class constructor, after super() if the class is extending a parent class. See breaking changes for classes.
All unnecessary utility helper functions have been removed, including the polyfills for indexOf and bind.
The extends keyword now only works in the context of classes; it cannot be used to extend a function prototype. See breaking changes for extends.
Literate CoffeeScript is now parsed entirely based on indentation, similar to the 1.x implementation; there is no longer a dependency for parsing Markdown. See breaking changes for Literate CoffeeScript parsing.
JavaScript reserved words used as properties are no longer wrapped in quotes.
require('coffeescript') should now work in non-Node environments such as the builds created by Webpack or Browserify. This provides a more convenient way to include the browser compiler in builds intending to run in a browser environment.
Unreachable break statements are no longer added after switch cases that throw exceptions.
The browser compiler is now compiled using Babili and transpiled down to Babel’s env preset (should be safe for use in all browsers in current use, not just evergreen versions).
Calling functions @get or @set no longer throws an error about required parentheses. (Bare get or set, not attached to an object or @, still intentionally throws a compiler error.)
If $XDG_CACHE_HOME is set, the REPL .coffee_history file is saved there.
Initial beta release of CoffeeScript 2. No further breaking changes are anticipated.
Destructured objects and arrays now output using ES2015+ syntax whenever possible.
Literate CoffeeScript now has much better support for parsing Markdown, thanks to using Markdown-It to detect Markdown sections rather than just looking at indentation.
Calling a function named get or set now requires parentheses, to disambiguate from the get or set keywords (which are disallowed).
The compiler now requires Node 7.6+, the first version of Node to support asynchronous functions without requiring a flag.
The return and export keywords can now accept implicit objects (defined by indentation, without needing braces).
Support Unicode code point escapes (e.g. \u{1F4A9}).
The coffee command now first looks to see if CoffeeScript is installed under node_modules in the current folder, and executes the coffee binary there if so; or otherwise it runs the globally installed one. This allows you to have one version of CoffeeScript installed globally and a different one installed locally for a particular project. (Likewise for the cake command.)
Bugfixes for chained function calls not closing implicit objects or ternaries.
Bugfixes for incorrect code generated by the ? operator within a termary if statement.
Fixed some tests, and failing tests now result in a nonzero exit code.
Better handling of default, from, as and * within import and export statements. You can now import or export a member named default and the compiler won’t interpret it as the default keyword.
Fixed a bug where invalid octal escape sequences weren’t throwing errors in the compiler.
The cake commands have been updated, with new watch options for most tasks. Clone the CoffeeScript repo and run cake at the root of the repo to see the options.
Fixed a bug where exporting a referenced variable was preventing the variable from being declared.
Fixed a bug where the coffee command wasn’t working for a .litcoffee file.
Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
@ values can now be used as indices in for expressions. This loosens the compilation of for expressions to allow the index variable to be an @ value, e.g. do @visit for @node, @index in nodes. Within @visit, the index of the current node (@node) would be available as @index.
CoffeeScript’s patched Error.prepareStackTrace has been restored, with some revisions that should prevent the erroneous exceptions that were making life difficult for some downstream projects. This fixes the incorrect line numbers in stack traces since 1.12.2.
The //= operator’s output now wraps parentheses around the right operand, like the other assignment operators.
You can now import a module member named default, e.g. import { default } from 'lib'. Though like in ES2015, you cannot import an entire module and name it default (so import default from 'lib' is not allowed).
Fix regression where from as a variable name was breaking for loop declarations. For the record, from is not a reserved word in CoffeeScript; you may use it for variable names. from behaves like a keyword within the context of import and export statements, and in the declaration of a for loop; though you should also be able to use variables named from in those contexts, and the compiler should be able to tell the difference.
CoffeeScript now supports ES2015 tagged template literals. Note that using tagged template literals in your code makes you responsible for ensuring that either your runtime supports tagged template literals or that you transpile the output JavaScript further to a version your target runtime(s) support.
CoffeeScript now provides a for…from syntax for outputting ES2015 for…of. (Sorry they couldn’t match, but we came up with for…of first for something else.) This allows iterating over generators or any other iterable object. Note that using for…from in your code makes you responsible for ensuring that either your runtime supports for…of or that you transpile the output JavaScript further to a version your target runtime(s) support.
Triple backticks ( ```) allow the creation of embedded JavaScript blocks where escaping single backticks is not required, which should improve interoperability with ES2015 template literals and with Markdown.
Within single-backtick embedded JavaScript, backticks can now be escaped via \`.
The browser tests now run in the browser again, and are accessible here if you would like to test your browser.
CoffeeScript-only keywords in ES2015 imports and exports are now ignored.
The compiler now throws an error on trying to export an anonymous class.
Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
Added the -M, --inline-map flag to the compiler, allowing you embed the source map directly into the output JavaScript, rather than as a separate file.
A bunch of fixes for yield:
yield return can no longer mistakenly be used as an expression.
yield now mirrors return in that it can be used stand-alone as well as with expressions. Where you previously wrote yield undefined, you may now write simply yield. However, this means also inheriting the same syntax limitations that return has, so these examples no longer compile:
doubles = ->
yield for i in [1..3]
i * 2
six = ->
yield
2 * 3
The JavaScript output is a bit nicer, with unnecessary parentheses and spaces, double indentation and double semicolons around yield no longer present.
&&=, ||=, and= and or= no longer accidentally allow a space before the equals sign.
Improved several error messages.
Just like undefined compiles to void 0, NaN now compiles into 0/0 and Infinity into 2e308.
Bugfix for renamed destructured parameters with defaults. ({a: b = 1}) -> no longer crashes the compiler.
Improved the internal representation of a CoffeeScript program. This is only noticeable to tools that use CoffeeScript.tokens or CoffeeScript.nodes. Such tools need to update to take account for changed or added tokens and nodes.
Several minor bug fixes, including:
The caught error in catch blocks is no longer declared unnecessarily, and no longer mistakenly named undefined for catch-less try blocks.
Unassignable parameter destructuring no longer crashes the compiler.
Source maps are now used correctly for errors thrown from .coffee.md files.
coffee -e 'throw null' no longer crashes.
The REPL no longer crashes when using .exit to exit it.
Invalid JavaScript is no longer output when lots of for loops are used in the same scope.
CoffeeScript now supports ES2015-style destructuring defaults.
(offsetHeight: height) -> no longer compiles. That syntax was accidental and partly broken. Use ({offsetHeight: height}) -> instead. Object destructuring always requires braces.
Several minor bug fixes, including:
A bug where the REPL would sometimes report valid code as invalid, based on what you had typed earlier.
A problem with multiple JS contexts in the jest test framework.
An error in io.js where strict mode is set on internal modules.
A variable name clash for the caught error in catch blocks.
Bugfix for interpolation in the first key of an object literal in an implicit call.
Fixed broken error messages in the REPL, as well as a few minor bugs with the REPL.
Fixed source mappings for tokens at the beginning of lines when compiling with the --bare option. This has the nice side effect of generating smaller source maps.
Slight formatting improvement of compiled block comments.
Fixed a watch mode error introduced in 1.9.1 when compiling multiple files with the same filename.
Bugfix for yield around expressions containing this.
Added a Ruby-style -r option to the REPL, which allows requiring a module before execution with --eval or --interactive.
In <script type="text/coffeescript"> tags, to avoid possible duplicate browser requests for .coffee files, you can now use the data-src attribute instead of src.
Minor bug fixes for IE8, strict ES5 regular expressions and Browserify.
Interpolation now works in object literal keys (again). You can use this to dynamically name properties.
Internal compiler variable names no longer start with underscores. This makes the generated JavaScript a bit prettier, and also fixes an issue with the completely broken and ungodly way that AngularJS “parses” function arguments.
Fixed a few yield-related edge cases with yield return and yield throw.
Minor bug fixes and various improvements to compiler error messages.
CoffeeScript now supports ES2015 generators. A generator is simply a function that yields.
More robust parsing and improved error messages for strings and regexes — especially with respect to interpolation.
Changed strategy for the generation of internal compiler variable names. Note that this means that @example function parameters are no longer available as naked example variables within the function body.
Fixed REPL compatibility with latest versions of Node and Io.js.
When requiring CoffeeScript files in Node you must now explicitly register the compiler. This can be done with require 'coffeescript/register' or CoffeeScript.register(). Also for configuration such as Mocha’s, use coffeescript/register.
Improved error messages, source maps and stack traces. Source maps now use the updated //# syntax.
Leading . now closes all open calls, allowing for simpler chaining syntax.
Added **, // and %% operators and ... expansion in parameter lists and destructuring expressions.
Multiline strings are now joined by a single space and ignore all indentation. A backslash at the end of a line can denote the amount of whitespace between lines, in both strings and heredocs. Backslashes correctly escape whitespace in block regexes.
Closing brackets can now be indented and therefore no longer cause unexpected error.
Several breaking compilation fixes. Non-callable literals (strings, numbers etc.) don’t compile in a call now and multiple postfix conditionals compile properly. Postfix conditionals and loops always bind object literals. Conditional assignment compiles properly in subexpressions. super is disallowed outside of methods and works correctly inside for loops.
Formatting of compiled block comments has been improved.
No more -p folders on Windows.
The options object passed to CoffeeScript is no longer mutated.
The CoffeeScript REPL now remembers your history between sessions. Just like a proper REPL should.
You can now use require in Node to load .coffee.md Literate CoffeeScript files. In the browser, text/literate-coffeescript script tags.
The old coffee --lint command has been removed. It was useful while originally working on the compiler, but has been surpassed by JSHint. You may now use -l to pass literate files in over stdio.
Bugfixes for Windows path separators, catch without naming the error, and executable-class-bodies-with- prototypal-property-attachment.
Source maps have been used to provide automatic line-mapping when running CoffeeScript directly via the coffee command, and for automatic line-mapping when running CoffeeScript directly in the browser. Also, to provide better error messages for semantic errors thrown by the compiler — with colors, even.
Improved support for mixed literate/vanilla-style CoffeeScript projects, and generating source maps for both at the same time.
Fixes for 1.6.x regressions with overriding inherited bound functions, and for Windows file path management.
The coffee command can now correctly fork() both .coffee and .js files. (Requires Node.js 0.9+)
First release of source maps. Pass the --map flag to the compiler, and off you go. Direct all your thanks over to Jason Walton.
Fixed a 1.5.0 regression with multiple implicit calls against an indented implicit object. Combinations of implicit function calls and implicit objects should generally be parsed better now — but it still isn’t good style to nest them too heavily.
.coffee.md is now also supported as a Literate CoffeeScript file extension, for existing tooling. .litcoffee remains the canonical one.
Several minor fixes surrounding member properties, bound methods and super in class declarations.
Due to the new semantics of JavaScript’s strict mode, CoffeeScript no longer guarantees that constructor functions have names in all runtimes. See #2052 for discussion.
Inside of a nested function inside of an instance method, it’s now possible to call super more reliably (walks recursively up).
Named loop variables no longer have different scoping heuristics than other local variables. (Reverts #643)
Fix for splats nested within the LHS of destructuring assignment.
Corrections to our compile time strict mode forbidding of octal literals.
CoffeeScript now enforces all of JavaScript’s Strict Mode early syntax errors at compile time. This includes old-style octal literals, duplicate property names in object literals, duplicate parameters in a function definition, deleting naked variables, setting the value of eval or arguments, and more. See a full discussion at #1547.
The REPL now has a handy new multi-line mode for entering large blocks of code. It’s useful when copy-and-pasting examples into the REPL. Enter multi-line mode with Ctrl-V. You may also now pipe input directly into the REPL.
CoffeeScript now prints a Generated by CoffeeScript VERSION header at the top of each compiled file.
Conditional assignment of previously undefined variables a or= b is now considered a syntax error.
A tweak to the semantics of do, which can now be used to more easily simulate a namespace: do (x = 1, y = 2) -> …
Loop indices are now mutable within a loop iteration, and immutable between them.
Both endpoints of a slice are now allowed to be omitted for consistency, effectively creating a shallow copy of the list.
Additional tweaks and improvements to coffee --watch under Node’s “new” file watching API. Watch will now beep by default if you introduce a syntax error into a watched script. We also now ignore hidden directories by default when watching recursively.
Multiple improvements to coffee --watch and --join. You may now use both together, as well as add and remove files and directories within a --watch’d folder.
The throw statement can now be used as part of an expression.
Block comments at the top of the file will now appear outside of the safety closure wrapper.
Fixed a number of minor 1.1.3 regressions having to do with trailing operators and unfinished lines, and a more major 1.1.3 regression that caused bound functions within bound class functions to have the incorrect this.
Ahh, whitespace. CoffeeScript’s compiled JS now tries to space things out and keep it readable, as you can see in the examples on this page.
You can now call super in class level methods in class bodies, and bound class methods now preserve their correct context.
JavaScript has always supported octal numbers 010 is 8, and hexadecimal numbers 0xf is 15, but CoffeeScript now also supports binary numbers: 0b10 is 2.
The CoffeeScript module has been nested under a subdirectory to make it easier to require individual components separately, without having to use npm. For example, after adding the CoffeeScript folder to your path: require('coffeescript/lexer')
There’s a new “link” feature in Try CoffeeScript on this webpage. Use it to get a shareable permalink for your example script.
The coffee --watch feature now only works on Node.js 0.6.0 and higher, but now also works properly on Windows.
Fixes for block comment formatting, ?= compilation, implicit calls against control structures, implicit invocation of a try/catch block, variadic arguments leaking from local scope, line numbers in syntax errors following heregexes, property access on parenthesized number literals, bound class methods and super with reserved names, a REPL overhaul, consecutive compiled semicolons, block comments in implicitly called objects, and a Chrome bug.
When running via the coffee executable, process.argv and friends now report coffee instead of node. Better compatibility with Node.js 0.4.x module lookup changes. The output in the REPL is now colorized, like Node’s is. Giving your concatenated CoffeeScripts a name when using --join is now mandatory. Fix for lexing compound division /= as a regex accidentally. All text/coffeescript tags should now execute in the order they’re included. Fixed an issue with extended subclasses using external constructor functions. Fixed an edge-case infinite loop in addImplicitParentheses. Fixed exponential slowdown with long chains of function calls. Globals no longer leak into the CoffeeScript REPL. Splatted parameters are declared local to the function.
Fixed a lexer bug with Unicode identifiers. Updated REPL for compatibility with Node.js 0.3.7. Fixed requiring relative paths in the REPL. Trailing return and return undefined are now optimized away. Stopped requiring the core Node.js util module for back-compatibility with Node.js 0.2.5. Fixed a case where a conditional return would cause fallthrough in a switch statement. Optimized empty objects in destructuring assignment.
CoffeeScript loops no longer try to preserve block scope when functions are being generated within the loop body. Instead, you can use the do keyword to create a convenient closure wrapper. Added a --nodejs flag for passing through options directly to the node executable. Better behavior around the use of pure statements within expressions. Fixed inclusive slicing through -1, for all browsers, and splicing with arbitrary expressions as endpoints.
The REPL now properly formats stacktraces, and stays alive through asynchronous exceptions. Using --watch now prints timestamps as files are compiled. Fixed some accidentally-leaking variables within plucked closure-loops. Constructors now maintain their declaration location within a class body. Dynamic object keys were removed. Nested classes are now supported. Fixes execution context for naked splatted functions. Bugfix for inversion of chained comparisons. Chained class instantiation now works properly with splats.
0.9.5 should be considered the first release candidate for CoffeeScript 1.0. There have been a large number of internal changes since the previous release, many contributed from satyr’s Coco dialect of CoffeeScript. Heregexes (extended regexes) were added. Functions can now have default arguments. Class bodies are now executable code. Improved syntax errors for invalid CoffeeScript. undefined now works like null, and cannot be assigned a new value. There was a precedence change with respect to single-line comprehensions: result = i for i in list
used to parse as result = (i for i in list) by default … it now parses as
(result = i) for i in list.
CoffeeScript now uses appropriately-named temporary variables, and recycles their references after use. Added require.extensions support for Node.js 0.3. Loading CoffeeScript in the browser now adds just a single CoffeeScript object to global scope. Fixes for implicit object and block comment edge cases.
CoffeeScript switch statements now compile into JS switch statements — they previously compiled into if/else chains for JavaScript 1.3 compatibility. Soaking a function invocation is now supported. Users of the RubyMine editor should now be able to use --watch mode.
Specifying the start and end of a range literal is now optional, eg. array[3..]. You can now say a not instanceof b. Fixed important bugs with nested significant and non-significant indentation (Issue #637). Added a --require flag that allows you to hook into the coffee command. Added a custom jsl.conf file for our preferred JavaScriptLint setup. Sped up Jison grammar compilation time by flattening rules for operations. Block comments can now be used with JavaScript-minifier-friendly syntax. Added JavaScript’s compound assignment bitwise operators. Bugfixes to implicit object literals with leading number and string keys, as the subject of implicit calls, and as part of compound assignment.
Bugfix release for 0.9.1. Greatly improves the handling of mixed implicit objects, implicit function calls, and implicit indentation. String and regex interpolation is now strictly #{ … } (Ruby style). The compiler now takes a --require flag, which specifies scripts to run before compilation.
The CoffeeScript 0.9 series is considered to be a release candidate for 1.0; let’s give her a shakedown cruise. 0.9.0 introduces a massive backwards-incompatible change: Assignment now uses =, and object literals use :, as in JavaScript. This allows us to have implicit object literals, and YAML-style object definitions. Half assignments are removed, in favor of +=, or=, and friends. Interpolation now uses a hash mark # instead of the dollar sign $ — because dollar signs may be part of a valid JS identifier. Downwards range comprehensions are now safe again, and are optimized to straight for loops when created with integer endpoints. A fast, unguarded form of object comprehension was added: for all key, value of object. Mentioning the super keyword with no arguments now forwards all arguments passed to the function, as in Ruby. If you extend class B from parent class A, if A has an extended method defined, it will be called, passing in B — this enables static inheritance, among other things. Cleaner output for functions bound with the fat arrow. @variables can now be used in parameter lists, with the parameter being automatically set as a property on the object — useful in constructors and setter functions. Constructor functions can now take splats.
Block-style comments are now passed through and printed as JavaScript block comments – making them useful for licenses and copyright headers. Better support for running coffee scripts standalone via hashbangs. Improved syntax errors for tokens that are not in the grammar.
Official CoffeeScript variable style is now camelCase, as in JavaScript. Reserved words are now allowed as object keys, and will be quoted for you. Range comprehensions now generate cleaner code, but you have to specify by -1 if you’d like to iterate downward. Reporting of syntax errors is greatly improved from the previous release. Running coffee with no arguments now launches the REPL, with Readline support. The <- bind operator has been removed from CoffeeScript. The loop keyword was added, which is equivalent to a while true loop. Comprehensions that contain closures will now close over their variables, like the semantics of a forEach. You can now use bound function in class definitions (bound to the instance). For consistency, a in b is now an array presence check, and a of b is an object-key check. Comments are no longer passed through to the generated JavaScript.
The coffee command will now preserve directory structure when compiling a directory full of scripts. Fixed two omissions that were preventing the CoffeeScript compiler from running live within Internet Explorer. There’s now a syntax for block comments, similar in spirit to CoffeeScript’s heredocs. ECMA Harmony DRY-style pattern matching is now supported, where the name of the property is the same as the name of the value: {name, length}: func. Pattern matching is now allowed within comprehension variables. unless is now allowed in block form. until loops were added, as the inverse of while loops. switch statements are now allowed without switch object clauses. Compatible with Node.js v0.1.95.
Interpolation can now be used within regular expressions and heredocs, as well as strings. Added the <- bind operator. Allowing assignment to half-expressions instead of special ||=-style operators. The arguments object is no longer automatically converted into an array. After requiring coffeescript, Node.js can now directly load .coffee files, thanks to registerExtension. Multiple splats can now be used in function calls, arrays, and pattern matching.
String interpolation, contributed by Stan Angeloff. Since --run has been the default since 0.5.3, updating --stdio and --eval to run by default, pass --compile as well if you’d like to print the result.
Bugfix that corrects the Node.js global constants __filename and __dirname. Tweaks for more flexible parsing of nested function literals and improperly-indented comments. Updates for the latest Node.js API.
CoffeeScript now has a syntax for defining classes. Many of the core components (Nodes, Lexer, Rewriter, Scope, Optparse) are using them. Cakefiles can use optparse.coffee to define options for tasks. --run is now the default flag for the coffee command, use --compile to save JavaScripts. Bugfix for an ambiguity between RegExp literals and chained divisions.
Added a compressed version of the compiler for inclusion in web pages as
/v2/browser-compiler-legacy/coffeescript.js. It’ll automatically run any script tags with type text/coffeescript for you. Added a --stdio option to the coffee command, for piped-in compiles.
Improvements to null soaking with the existential operator, including soaks on indexed properties. Added conditions to while loops, so you can use them as filters with when, in the same manner as comprehensions.
CoffeeScript 0.5.0 is a major release, While there are no language changes, the Ruby compiler has been removed in favor of a self-hosting compiler written in pure CoffeeScript.
@property is now a shorthand for this.property.
Switched the default JavaScript engine from Narwhal to Node.js. Pass the --narwhal flag if you’d like to continue using it.
CoffeeScript 0.3 includes major syntax changes:
The function symbol was changed to ->, and the bound function symbol is now =>.
Parameter lists in function definitions must now be wrapped in parentheses.
Added property soaking, with the ?. operator.
Made parentheses optional, when invoking functions with arguments.
Removed the obsolete block literal syntax.
Added Python-style chained comparisons, the conditional existence operator ?=, and some examples from Beautiful Code. Bugfixes relating to statement-to-expression conversion, arguments-to-array conversion, and the TextMate syntax highlighter.
The conditions in switch statements can now take multiple values at once — If any of them are true, the case will run. Added the long arrow ==>, which defines and immediately binds a function to this. While loops can now be used as expressions, in the same way that comprehensions can. Splats can be used within pattern matches to soak up the rest of an array.
Added ECMAScript Harmony style destructuring assignment, for dealing with extracting values from nested arrays and objects. Added indentation-sensitive heredocs for nicely formatted strings or chunks of code.
When performing a comprehension over an object, use ino, instead of in, which helps us generate smaller, more efficient code at compile time.
Added :: as a shorthand for saying .prototype.
The “splat” symbol has been changed from a prefix asterisk *, to a postfix ellipsis ...
Added JavaScript’s in operator, empty return statements, and empty while loops.
Constructor functions that start with capital letters now include a safety check to make sure that the new instance of the object is returned.
The extends keyword now functions identically to goog.inherits in Google’s Closure Library.
Major release. Significant whitespace. Better statement-to-expression conversion. Splats. Splice literals. Object comprehensions. Blocks. The existential operator. Many thanks to all the folks who posted issues, with special thanks to Liam O’Connor-Davis for whitespace and expression help.
Array slice literals and array comprehensions can now both take Ruby-style ranges to specify the start and end. JavaScript variable declaration is now pushed up to the top of the scope, making all assignment statements into expressions. You can use \ to escape newlines. The coffeescript command is now called coffee.
The official CoffeeScript extension is now .coffee instead of .cs, which properly belongs to C#. Due to popular demand, you can now also use = to assign. Unlike JavaScript, = can also be used within object literals, interchangeably with :. Made a grammatical fix for chained function calls like func(1)(2)(3)(4). Inheritance and super no longer use __proto__, so they should be IE-compatible now.
The coffee command now includes --interactive, which launches an interactive CoffeeScript session, and --run, which directly compiles and executes a script. Both options depend on a working installation of Narwhal. The aint keyword has been replaced by isnt, which goes together a little smoother with is. Quoted strings are now allowed as identifiers within object literals: eg. {"5+5": 10}. All assignment operators now use a colon: +:, -:, *:, etc.
Fixed a bug with calling super() through more than one level of inheritance, with the re-addition of the extends keyword. Added experimental Narwhal support (as a Tusk package), contributed by Tom Robinson, including bin/cs as a CoffeeScript REPL and interpreter. New --no-wrap option to suppress the safety function wrapper.
================================================
FILE: docs/v2/test.html
================================================
CoffeeScript Test Suite
CoffeeScript Test Suite
================================================
FILE: documentation/examples/aliases.coffee
================================================
launch() if ignition is on
volume = 10 if band isnt SpinalTap
letTheWildRumpusBegin() unless answer is no
if car.speed < limit then accelerate()
winner = yes if pick in [47, 92, 13]
print inspect "My name is #{@name}"
================================================
FILE: documentation/examples/array_comprehensions.coffee
================================================
# Eat lunch.
eat = (food) -> "#{food} eaten."
eat food for food in ['toast', 'cheese', 'wine']
# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu = (i, dish) -> "Menu Item #{i}: #{dish}"
menu i + 1, dish for dish, i in courses
# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
================================================
FILE: documentation/examples/array_spread.coffee
================================================
popular = ['pepperoni', 'sausage', 'cheese']
unwanted = ['anchovies', 'olives']
all = [popular..., unwanted..., 'mushrooms']
================================================
FILE: documentation/examples/async.coffee
================================================
# Your browser must support async/await and speech synthesis
# to run this example.
sleep = (ms) ->
new Promise (resolve) ->
window.setTimeout resolve, ms
say = (text) ->
window.speechSynthesis.cancel()
window.speechSynthesis.speak new SpeechSynthesisUtterance text
countdown = (seconds) ->
for i in [seconds..1]
say i
await sleep 1000 # wait one second
say "Blastoff!"
countdown 3
================================================
FILE: documentation/examples/breaking_change_bound_generator_function.coffee
================================================
self = this
f = -> yield self
================================================
FILE: documentation/examples/breaking_change_destructuring_default_values.coffee
================================================
{a = 1} = {a: null}
a # Equals 1 in CoffeeScript 1.x, null in CoffeeScript 2
================================================
FILE: documentation/examples/breaking_change_fat_arrow.coffee
================================================
outer = ->
inner = => Array.from arguments
inner()
outer(1, 2) # Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2
================================================
FILE: documentation/examples/breaking_change_function_parameter_default_values.coffee
================================================
f = (a = 1) -> a
f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
================================================
FILE: documentation/examples/breaking_change_super_in_non-class_methods_refactor_with_apply.coffee
================================================
# Helper functions
hasProp = {}.hasOwnProperty
extend = (child, parent) ->
ctor = ->
@constructor = child
return
for key of parent
if hasProp.call(parent, key)
child[key] = parent[key]
ctor.prototype = parent.prototype
child.prototype = new ctor
child
A = ->
B = ->
extend B, A
B.prototype.foo = -> A::foo.apply this, arguments
================================================
FILE: documentation/examples/breaking_change_super_in_non-class_methods_refactor_with_class.coffee
================================================
class A
class B extends A
foo: -> super arguments...
================================================
FILE: documentation/examples/breaking_change_super_this.coffee
================================================
class B extends A
constructor: (arg) ->
super arg
@arg = arg
================================================
FILE: documentation/examples/breaking_change_super_with_arguments.coffee
================================================
class B extends A
foo: -> super arguments...
================================================
FILE: documentation/examples/breaking_change_super_without_arguments.coffee
================================================
class B extends A
foo: -> super()
================================================
FILE: documentation/examples/cake_tasks.coffee
================================================
fs = require 'fs'
option '-o', '--output [DIR]', 'directory for compiled code'
task 'build:parser', 'rebuild the Jison parser', (options) ->
require 'jison'
code = require('./lib/grammar').parser.generate()
dir = options.output or 'lib'
fs.writeFile "#{dir}/parser.js", code
================================================
FILE: documentation/examples/chaining.coffee
================================================
$ 'body'
.click (e) ->
$ '.box'
.fadeIn 'fast'
.addClass 'show'
.css 'background', 'white'
================================================
FILE: documentation/examples/classes.coffee
================================================
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
class Snake extends Animal
move: ->
alert "Slithering..."
super 5
class Horse extends Animal
move: ->
alert "Galloping..."
super 45
sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"
sam.move()
tom.move()
================================================
FILE: documentation/examples/comment.coffee
================================================
###
Fortune Cookie Reader v1.0
Released under the MIT License
###
sayFortune = (fortune) ->
console.log fortune # in bed!
================================================
FILE: documentation/examples/comparisons.coffee
================================================
cholesterol = 127
healthy = 200 > cholesterol > 60
================================================
FILE: documentation/examples/conditionals.coffee
================================================
mood = greatlyImproved if singing
if happy and knowsIt
clapsHands()
chaChaCha()
else
showIt()
date = if friday then sue else jill
================================================
FILE: documentation/examples/constructor_destructuring.coffee
================================================
class Person
constructor: (options) ->
{@name, @age, @height = 'average'} = options
tim = new Person name: 'Tim', age: 4
================================================
FILE: documentation/examples/default_args.coffee
================================================
fill = (container, liquid = "coffee") ->
"Filling the #{container} with #{liquid}..."
================================================
FILE: documentation/examples/do.coffee
================================================
for filename in list
do (filename) ->
if filename not in ['.DS_Store', 'Thumbs.db', 'ehthumbs.db']
fs.readFile filename, (err, contents) ->
compile filename, contents.toString()
================================================
FILE: documentation/examples/dynamic_import.coffee
================================================
# Your browser must support dynamic import to run this example.
do ->
{ run } = await import('./browser-compiler-modern/coffeescript.js')
run '''
if 5 < new Date().getHours() < 9
alert 'Time to make the coffee!'
else
alert 'Time to get some work done.'
'''
================================================
FILE: documentation/examples/embedded.coffee
================================================
hi = `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`
================================================
FILE: documentation/examples/embedded_block.coffee
================================================
```
function time() {
return `The time is ${new Date().toLocaleTimeString()}`;
}
```
================================================
FILE: documentation/examples/embedded_escaped.coffee
================================================
markdown = `function () {
return \`In Markdown, write code like \\\`this\\\`\`;
}`
================================================
FILE: documentation/examples/existence.coffee
================================================
solipsism = true if mind? and not world?
speed = 0
speed ?= 15
footprints = yeti ? "bear"
================================================
FILE: documentation/examples/existence_declared.coffee
================================================
major = 'Computer Science'
unless major?
signUpForClass 'Introduction to Wines'
================================================
FILE: documentation/examples/existence_undeclared.coffee
================================================
if window?
environment = 'browser (probably)'
================================================
FILE: documentation/examples/expansion.coffee
================================================
text = "Every literary critic believes he will
outwit history and have the last word"
[first, ..., last] = text.split " "
================================================
FILE: documentation/examples/expressions.coffee
================================================
grade = (student) ->
if student.excellentWork
"A+"
else if student.okayStuff
if student.triedHard then "B" else "B-"
else
"C"
eldest = if 24 > 21 then "Liz" else "Ike"
================================================
FILE: documentation/examples/expressions_assignment.coffee
================================================
six = (one = 1) + (two = 2) + (three = 3)
================================================
FILE: documentation/examples/expressions_comprehension.coffee
================================================
# The first ten global properties.
globals = (name for name of window)[0...10]
================================================
FILE: documentation/examples/expressions_try.coffee
================================================
alert(
try
nonexistent / undefined
catch error
"And the error is ... #{error}"
)
================================================
FILE: documentation/examples/fat_arrow.coffee
================================================
Account = (customer, cart) ->
@customer = customer
@cart = cart
$('.shopping_cart').on 'click', (event) =>
@customer.purchase @cart
================================================
FILE: documentation/examples/functions.coffee
================================================
square = (x) -> x * x
cube = (x) -> square(x) * x
================================================
FILE: documentation/examples/generator_iteration.coffee
================================================
fibonacci = ->
[previous, current] = [1, 1]
loop
[previous, current] = [current, previous + current]
yield current
return
getFibonacciNumbers = (length) ->
results = [1]
for n from fibonacci()
results.push n
break if results.length is length
results
================================================
FILE: documentation/examples/generators.coffee
================================================
perfectSquares = ->
num = 0
loop
num += 1
yield num * num
return
window.ps or= perfectSquares()
================================================
FILE: documentation/examples/get_set.coffee
================================================
screen =
width: 1200
ratio: 16/9
Object.defineProperty screen, 'height',
get: ->
this.width / this.ratio
set: (val) ->
this.width = val * this.ratio
================================================
FILE: documentation/examples/heredocs.coffee
================================================
html = """
cup of coffeescript
"""
================================================
FILE: documentation/examples/heregexes.coffee
================================================
NUMBER = ///
^ 0b[01]+ | # binary
^ 0o[0-7]+ | # octal
^ 0x[\da-f]+ | # hex
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
///i
================================================
FILE: documentation/examples/interpolation.coffee
================================================
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author }"
sentence = "#{ 22 / 7 } is a decent approximation of π"
================================================
FILE: documentation/examples/jsx.coffee
================================================
renderStarRating = ({ rating, maxStars }) ->
================================================
FILE: documentation/examples/modules.coffee
================================================
import './local-file.js' # Must be the filename of the generated file
import 'package'
import _ from 'underscore'
import * as underscore from 'underscore'
import { now } from 'underscore'
import { now as currentTimestamp } from 'underscore'
import { first, last } from 'underscore'
import utilityBelt, { each } from 'underscore'
import dates from './calendar.json' assert { type: 'json' }
export default Math
export square = (x) -> x * x
export class Mathematics
least: (x, y) -> if x < y then x else y
export { sqrt }
export { sqrt as squareRoot }
export { Mathematics as default, sqrt as squareRoot }
export * from 'underscore'
export { max, min } from 'underscore'
export { version } from './package.json' assert { type: 'json' }
================================================
FILE: documentation/examples/modulo.coffee
================================================
-7 % 5 == -2 # The remainder of 7 / 5
-7 %% 5 == 3 # n %% 5 is always between 0 and 4
tabs.selectTabAtIndex((tabs.currentIndex - count) %% tabs.length)
================================================
FILE: documentation/examples/multiple_return_values.coffee
================================================
weatherReport = (location) ->
# Make an Ajax request to fetch the weather...
[location, 72, "Mostly Sunny"]
[city, temp, forecast] = weatherReport "Berkeley, CA"
================================================
FILE: documentation/examples/object_comprehensions.coffee
================================================
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
================================================
FILE: documentation/examples/object_extraction.coffee
================================================
futurists =
sculptor: "Umberto Boccioni"
painter: "Vladimir Burliuk"
poet:
name: "F.T. Marinetti"
address: [
"Via Roma 42R"
"Bellagio, Italy 22021"
]
{sculptor} = futurists
{poet: {name, address: [street, city]}} = futurists
================================================
FILE: documentation/examples/object_spread.coffee
================================================
user =
name: 'Werner Heisenberg'
occupation: 'theoretical physicist'
currentUser = { user..., status: 'Uncertain' }
================================================
FILE: documentation/examples/objects_and_arrays.coffee
================================================
song = ["do", "re", "mi", "fa", "so"]
singers = {Jagger: "Rock", Elvis: "Roll"}
bitlist = [
1, 0, 1
0, 0, 1
1, 1, 0
]
kids =
brother:
name: "Max"
age: 11
sister:
name: "Ida"
age: 9
================================================
FILE: documentation/examples/objects_reserved.coffee
================================================
$('.account').prop class: 'active'
log object.class
================================================
FILE: documentation/examples/objects_shorthand.coffee
================================================
name = "Michelangelo"
mask = "orange"
weapon = "nunchuks"
turtle = {name, mask, weapon}
output = "#{turtle.name} wears an #{turtle.mask} mask. Watch out for his #{turtle.weapon}!"
================================================
FILE: documentation/examples/overview.coffee
================================================
# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
# Existence:
alert "I knew it!" if elvis?
# Array comprehensions:
cubes = (math.cube num for num in list)
================================================
FILE: documentation/examples/parallel_assignment.coffee
================================================
theBait = 1000
theSwitch = 0
[theBait, theSwitch] = [theSwitch, theBait]
================================================
FILE: documentation/examples/patterns_and_splats.coffee
================================================
tag = ""
[open, contents..., close] = tag.split("")
================================================
FILE: documentation/examples/prototypes.coffee
================================================
String::dasherize = ->
this.replace /_/g, "-"
================================================
FILE: documentation/examples/range_comprehensions.coffee
================================================
countdown = (num for num in [10..1])
================================================
FILE: documentation/examples/scope.coffee
================================================
outer = 1
changeNumbers = ->
inner = -1
outer = 10
inner = changeNumbers()
================================================
FILE: documentation/examples/slices.coffee
================================================
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
start = numbers[0..2]
middle = numbers[3...-2]
end = numbers[-2..]
copy = numbers[..]
================================================
FILE: documentation/examples/soaks.coffee
================================================
zip = lottery.drawWinner?().address?.zipcode
================================================
FILE: documentation/examples/splats.coffee
================================================
gold = silver = rest = "unknown"
awardMedals = (first, second, others...) ->
gold = first
silver = second
rest = others
contenders = [
"Michael Phelps"
"Liu Xiang"
"Yao Ming"
"Allyson Felix"
"Shawn Johnson"
"Roman Sebrle"
"Guo Jingjing"
"Tyson Gay"
"Asafa Powell"
"Usain Bolt"
]
awardMedals contenders...
alert """
Gold: #{gold}
Silver: #{silver}
The Field: #{rest.join ', '}
"""
================================================
FILE: documentation/examples/splices.coffee
================================================
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[3..6] = [-3, -4, -5, -6]
================================================
FILE: documentation/examples/static.coffee
================================================
class Teenager
@say: (speech) ->
words = speech.split ' '
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe']
output = []
for word, index in words
output.push word
output.push fillers[Math.floor(Math.random() * fillers.length)] unless index is words.length - 1
output.join ', '
================================================
FILE: documentation/examples/strings.coffee
================================================
mobyDick = "Call me Ishmael. Some years ago --
never mind how long precisely -- having little
or no money in my purse, and nothing particular
to interest me on shore, I thought I would sail
about a little and see the watery part of the
world..."
================================================
FILE: documentation/examples/switch.coffee
================================================
switch day
when "Mon" then go work
when "Tue" then go relax
when "Thu" then go iceFishing
when "Fri", "Sat"
if day is bingoDay
go bingo
go dancing
when "Sun" then go church
else go work
================================================
FILE: documentation/examples/switch_with_no_expression.coffee
================================================
score = 76
grade = switch
when score < 60 then 'F'
when score < 70 then 'D'
when score < 80 then 'C'
when score < 90 then 'B'
else 'A'
# grade == 'C'
================================================
FILE: documentation/examples/tagged_template_literals.coffee
================================================
upperCaseExpr = (textParts, expressions...) ->
textParts.reduce (text, textPart, i) ->
text + expressions[i - 1].toUpperCase() + textPart
greet = (name, adjective) ->
upperCaseExpr"""
Hi #{name}. You look #{adjective}!
"""
================================================
FILE: documentation/examples/try.coffee
================================================
try
allHellBreaksLoose()
catsAndDogsLivingTogether()
catch error
print error
finally
cleanUp()
================================================
FILE: documentation/examples/type_annotations.coffee
================================================
# @flow
###::
type Obj = {
num: number,
};
###
fn = (str ###: string ###, obj ###: Obj ###) ###: string ### ->
str + obj.num
================================================
FILE: documentation/examples/while.coffee
================================================
# Econ 101
if this.studyingEconomics
buy() while supply > demand
sell() until supply > demand
# Nursery Rhyme
num = 6
lyrics = while num -= 1
"#{num} little monkeys, jumping on the bed.
One fell out and bumped his head."
================================================
FILE: documentation/sections/annotated_source.md
================================================
## Annotated Source
You can browse the CoffeeScript <%= fullVersion %> source in readable, annotated form [here](annotated-source/). You can also jump directly to a particular source file:
- [Grammar Rules — src/grammar](annotated-source/grammar.html)
- [Lexing Tokens — src/lexer](annotated-source/lexer.html)
- [The Rewriter — src/rewriter](annotated-source/rewriter.html)
- [The Syntax Tree — src/nodes](annotated-source/nodes.html)
- [Lexical Scope — src/scope](annotated-source/scope.html)
- [Helpers & Utility Functions — src/helpers](annotated-source/helpers.html)
- [The CoffeeScript Module — src/coffeescript](annotated-source/coffeescript.html)
- [Cake & Cakefiles — src/cake](annotated-source/cake.html)
- [“coffee” Command-Line Utility — src/command](annotated-source/command.html)
- [Option Parsing — src/optparse](annotated-source/optparse.html)
- [Interactive REPL — src/repl](annotated-source/repl.html)
- [Source Maps — src/sourcemap](annotated-source/sourcemap.html)
================================================
FILE: documentation/sections/announcing_coffeescript_2.md
================================================
# Announcing CoffeeScript 2
We are pleased to announce CoffeeScript 2! This new release of the CoffeeScript language and compiler aims to bring CoffeeScript into the modern JavaScript era, closing gaps in compatibility with JavaScript while preserving the clean syntax that is CoffeeScript’s hallmark. In a nutshell:
- The CoffeeScript 2 compiler now translates CoffeeScript code into modern JavaScript syntax. So a CoffeeScript `=>` is now output as `=>`, a CoffeeScript `class` is now output using the `class` keyword, and so on. This means you may need to [transpile the CoffeeScript compiler’s output](../#es2015plus-output).
- CoffeeScript 2 adds support for [async functions](../#async-functions) syntax, for the future [object destructuring](../#destructuring) syntax, and for [JSX](../#jsx). Some features, such as [modules](../#modules) (`import` and `export` statements), [`for…of`](../#generator-iteration), and [tagged template literals](../#tagged-template-literals) were backported into CoffeeScript versions 1.11 and 1.12.
- All of the above was achieved with very few [breaking changes from 1.x](../#breaking-changes). Most current CoffeeScript projects should be able to upgrade with little or no refactoring necessary.
CoffeeScript 2 was developed with two primary goals: remove any incompatibilities with modern JavaScript that might prevent CoffeeScript from being used on a project; and preserve as much backward compatibility as possible. [Install now](../#installation): `npm install -g coffeescript@2`
## Modern JavaScript Output
From the beginning, CoffeeScript has been described as being “just JavaScript.” And today, JavaScript is ES2015 (well, ES2017; also commonly known as ES6). CoffeeScript welcomes the changes in the JavaScript world and we’re happy to stop outputting circa-1999 syntax for modern features.
Many new JavaScript features, such as `=>`, were informed by CoffeeScript and are one-to-one compatible, or very nearly so. This has made outputting many of CoffeeScript’s innovations into new JS syntax straightforward: not only does `=>` become `=>`, but `{ a } = obj` becomes `{ a } = obj`, `"a#{b}c"` becomes `` `a${b}c` `` and so on.
The following CoffeeScript features were updated in 2.0 to output using modern JavaScript syntax (or added in CoffeeScript 1.11 through 2.0, output using modern syntax):
- Modules: `import`/`export`
- Classes: `class Animal`
- Async functions: `await someFunction()`
- Bound/arrow functions: `=>`
- Function default parameters: `(options = {}) ->`
- Function splat/rest parameters: `(items...) ->`
- Destructuring, for both arrays and objects: `[first, second] = items`, `{length} = items`
- Object rest/spread properties: `{options..., force: yes}`, `{force, otherOptions...} = options`
- Interpolated strings/template literals (JS backticked strings): `"Hello, #{user}!"`
- Tagged template literals: `html"coffee"`
- JavaScript’s `for…of` is now available as CoffeeScript’s `for…from` (we already had a `for…of`): `for n from generatorFunction()`
Not all CoffeeScript features were adopted into JavaScript in 100% the same way; most notably, [default values](../#breaking-changes-default-values) in JavaScript (and also in CoffeeScript 2) are only applied when a variable is `undefined`, not `undefined` or `null` as in CoffeeScript 1; and [classes](../#breaking-changes-classes) have their own differences. See the [breaking changes](../#breaking-changes) for the fine details.
In our experience, most breaking changes are edge cases that should affect very few people, like JavaScript’s [lack of an `arguments` object inside arrow functions](../#breaking-change-fat-arrow). There seem to be two breaking changes that affect a significant number of projects:
- In CoffeeScript 2, “bare” `super` (calling `super` without arguments) is now no longer allowed, and one must use `super()` or `super arguments...` instead.
- References to `this`/`@` cannot occur before a call to `super`, per the JS spec.
See the [full details](../#breaking-changes-super-extends). Either the CoffeeScript compiler or your transpiler will throw errors for either of these cases, so updating your code is a matter of fixing each occurrence as the compiler errors on it, until your code compiles successfully.
## Other Features
Besides supporting new JavaScript features and outputting older CoffeeScript features in modern JS syntax, CoffeeScript 2 has added support for the following:
- [JSX](../#jsx)
- [Line comments](../#comments) are now output (in CoffeeScript 1 they were discarded)
- Block comments are now allowed anywhere, enabling [static type annotations](../#type-annotations) using Flow’s comment-based syntax
There are many smaller improvements as well, such as to the `coffee` command-line tool. You can read all the details in the [changelog](../#changelog) for the 2.0.0 betas.
## “What About …?”
A few JavaScript features have been intentionally omitted from CoffeeScript. These include `let` and `const` (and `var`), named functions and the `get` and `set` keywords. These get asked about so often that we added a section to the docs called [Unsupported ECMAScript Features](../#unsupported). CoffeeScript’s lack of equivalents for these features does not affect compatibility or interoperability with JavaScript modules or libraries.
## Future Compatibility
Back when CoffeeScript 1 was created, ES2015 JavaScript and transpilers like [Babel](http://babeljs.io/), [Bublé](https://buble.surge.sh/) or [Traceur Compiler](https://github.com/google/traceur-compiler) were several years away. The CoffeeScript compiler itself had to do what today’s transpilers do, converting modern features like destructuring and arrow functions into equivalent lowest-common-denominator JavaScript.
But transpilers exist now, and they do their job well. With them around, there’s no need for the CoffeeScript compiler to duplicate this functionality. All the CoffeeScript compiler needs to worry about now is converting the CoffeeScript version of new syntax into the JS version of that syntax, e.g. `"Hello, #{name}!"` into `` `Hello, ${name}!` ``. This makes adding support for new JavaScript features much easier than before.
Most features added by ECMA in recent years haven’t required any updates at all in CoffeeScript. New global objects, or new methods on global objects, don’t require any updates on CoffeeScript’s part to work. Some proposed future JS features _do_ involve new syntax, like [class fields](https://github.com/tc39/proposal-class-fields). We have adopted a policy of supporting new syntax only when it reaches Stage 4 in ECMA’s process, which means that the syntax is final and will be in the next ES release. On occasion we might support a _feature_ before it has reached Stage 4, but output it using equivalent non-experimental syntax instead of the newly-proposed syntax; that’s what’s happening in 2.0.0 for [object destructuring](../#splats), where our output uses the same polyfill that Babel uses. When the new syntax is finalized, we will update our output to use the final syntax.
## Credits
The major features of 2.0.0 would not have been possible without the following people:
- [@GeoffreyBooth](https://github.com/GeoffreyBooth): Organizer of the CoffeeScript 2 effort, developer for modules; arrow functions, function default parameters and function rest parameters output using ES2015 syntax; line comments output and block comments output anywhere; block embedded JavaScript via triple backticks; improved parsing of Literate CoffeeScript; and the new docs website.
- [@connec](https://github.com/connec): Classes; destructuring; splats/rest syntax in arrays and function calls; and computed properties all output using ES2015 syntax.
- [@GabrielRatener](https://github.com/GabrielRatener): Async functions.
- [@xixixao](https://github.com/xixixao): JSX.
- [@zdenko](https://github.com/zdenko): Object rest/spread properties (object destructuring).
- [@greghuc](https://github.com/greghuc): Tagged template literals, interpolated strings output in ES2015 syntax.
- [@atg](https://github.com/atg): ES2015 `for…of`, supported as CoffeeScript’s `for…from`.
- [@lydell](https://github.com/lydell) and [@jashkenas](https://github.com/jashkenas): Guidance, code reviews and feedback.
See the full [honor roll](https://github.com/jashkenas/coffeescript/wiki/CoffeeScript-2-Honor-Roll).
Thanks and we hope you enjoy CoffeeScript 2!
================================================
FILE: documentation/sections/async_functions.md
================================================
## Async Functions
ES2017’s [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) are supported through the `await` keyword. Like with generators, there's no need for an `async` keyword; an async function in CoffeeScript is simply a function that awaits.
Similar to how `yield return` forces a generator, `await return` may be used to force a function to be async.
```
codeFor('async', true)
```
================================================
FILE: documentation/sections/books.md
================================================
## Books
There are a number of excellent resources to help you get started with CoffeeScript, some of which are freely available online.
* [The Little Book on CoffeeScript](http://arcturo.github.io/library/coffeescript/) is a brief 5-chapter introduction to CoffeeScript, written with great clarity and precision by [Alex MacCaw](http://alexmaccaw.co.uk/).
* [Smooth CoffeeScript](http://autotelicum.github.io/Smooth-CoffeeScript/) is a reimagination of the excellent book [Eloquent JavaScript](http://eloquentjavascript.net/), as if it had been written in CoffeeScript instead. Covers language features as well as the functional and object oriented programming styles. By [E. Hoigaard](https://github.com/autotelicum).
* [CoffeeScript: Accelerated JavaScript Development](http://pragprog.com/book/tbcoffee/coffeescript) is [Trevor Burnham](http://trevorburnham.com/)’s thorough introduction to the language. By the end of the book, you’ll have built a fast-paced multiplayer word game, writing both the client-side and Node.js portions in CoffeeScript.
* [CoffeeScript Programming with jQuery, Rails, and Node.js](https://www.packtpub.com/web-development/coffeescript-programming-jquery-rails-and-nodejs) is a new book by Michael Erasmus that covers CoffeeScript with an eye towards real-world usage both in the browser (jQuery) and on the server-side (Rails, Node).
* [CoffeeScript Ristretto](https://leanpub.com/coffeescript-ristretto/read) is a deep dive into CoffeeScript’s semantics from simple functions up through closures, higher-order functions, objects, classes, combinators, and decorators. By [Reg Braithwaite](http://braythwayt.com/).
* [Testing with CoffeeScript](https://efendibooks.com/minibooks/testing-with-coffeescript) is a succinct and freely downloadable guide to building testable applications with CoffeeScript and Jasmine.
* [CoffeeScript Application Development](https://www.packtpub.com/web-development/coffeescript-application-development) from Packt, introduces CoffeeScript while walking through the process of building a demonstration web application. A [CoffeeScript Application Development Coookbook](https://www.packtpub.com/web-development/coffeescript-application-development-cookbook) with over 90 “recipes” is also available.
* [CoffeeScript in Action](https://www.manning.com/books/coffeescript-in-action) from Manning Publications, covers CoffeeScript syntax, composition techniques and application development.
* [CoffeeScript: Die Alternative zu JavaScript](https://www.dpunkt.de/buecher/4021/coffeescript.html) from dpunkt.verlag, is the first CoffeeScript book in Deutsch.
================================================
FILE: documentation/sections/breaking_changes.md
================================================
## Breaking Changes From CoffeeScript 1.x to 2
CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.
================================================
FILE: documentation/sections/breaking_changes_argument_parsing_and_shebang_lines.md
================================================
### Argument parsing and shebang (`#!`) lines
In CoffeeScript 1.x, `--` was required after the path and filename of the script to be run, but before any arguments passed to that script. This convention is now deprecated. So instead of:
```bash
coffee [options] path/to/script.coffee -- [args]
```
Now you would just type:
```bash
coffee [options] path/to/script.coffee [args]
```
The deprecated version will still work, but it will print a warning before running the script.
On non-Windows platforms, a `.coffee` file can be made executable by adding a shebang (`#!`) line at the top of the file and marking the file as executable. For example:
```coffee
#!/usr/bin/env coffee
x = 2 + 2
console.log x
```
If this were saved as `executable.coffee`, it could be made executable and run:
```bash
▶ chmod +x ./executable.coffee
▶ ./executable.coffee
4
```
In CoffeeScript 1.x, this used to fail when trying to pass arguments to the script. Some users on OS X worked around the problem by using `#!/usr/bin/env coffee --` as the first line of the file. That didn’t work on Linux, however, which cannot parse shebang lines with more than a single argument. While such scripts will still run on OS X, CoffeeScript will now display a warning before compiling or evaluating files that begin with a too-long shebang line. Now that CoffeeScript 2 supports passing arguments without needing `--`, we recommend simply changing the shebang lines in such scripts to just `#!/usr/bin/env coffee`.
================================================
FILE: documentation/sections/breaking_changes_bound_generator_functions.md
================================================
### Bound generator functions
Bound generator functions, a.k.a. generator arrow functions, [aren’t allowed in ECMAScript](http://stackoverflow.com/questions/27661306/can-i-use-es6s-arrow-function-syntax-with-generators-arrow-notation). You can write `function*` or `=>`, but not both. Therefore, CoffeeScript code like this:
```coffee
f = => yield this
# Throws a compiler error
```
Needs to be rewritten the old-fashioned way:
```
codeFor('breaking_change_bound_generator_function')
```
================================================
FILE: documentation/sections/breaking_changes_classes.md
================================================
### Classes are compiled to ES2015 classes
ES2015 classes and their methods have some restrictions beyond those on regular functions.
Class constructors can’t be invoked without `new`:
```coffee
(class)()
# Throws a TypeError at runtime
```
ES2015 classes don’t allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that can’t be accommodated is calling a bound method before it is bound:
```coffee
class Base
constructor: ->
@onClick() # This works
clickHandler = @onClick
clickHandler() # This throws a runtime error
class Component extends Base
onClick: =>
console.log 'Clicked!', @
```
Class methods can’t be used with `new` (uncommon):
```coffee
class Namespace
@Klass = ->
new Namespace.Klass # Throws a TypeError at runtime
```
Due to the hoisting required to compile to ES2015 classes, dynamic keys in class methods can’t use values from the executable class body unless the methods are assigned in prototype style.
```coffee
class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
```
================================================
FILE: documentation/sections/breaking_changes_default_values.md
================================================
### Default values for function parameters and destructured elements
Per the [ES2015 spec regarding function default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) and [destructuring default values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Default_values), default values are only applied when a value is missing or `undefined`. In CoffeeScript 1.x, the default value would be applied in those cases but also if the value was `null`.
```
codeFor('breaking_change_function_parameter_default_values', 'f(null)')
```
```
codeFor('breaking_change_destructuring_default_values', 'a')
```
================================================
FILE: documentation/sections/breaking_changes_fat_arrow.md
================================================
### Bound (fat arrow) functions
In CoffeeScript 1.x, `=>` compiled to a regular `function` but with references to `this`/`@` rewritten to use the outer scope’s `this`, or with the inner function bound to the outer scope via `.bind` (hence the name “bound function”). In CoffeeScript 2, `=>` compiles to [ES2015’s `=>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), which behaves slightly differently. The largest difference is that in ES2015, `=>` functions lack an `arguments` object:
```
codeFor('breaking_change_fat_arrow', 'outer(1, 2)')
```
================================================
FILE: documentation/sections/breaking_changes_jsx_and_the_less_than_and_greater_than_operators.md
================================================
### JSX and the `<` and `>` operators
With the addition of [JSX](#jsx), the `<` and `>` characters serve as both the “less than” and “greater than” operators and as the delimiters for XML tags, like `
`. For best results, in general you should always wrap the operators in spaces to distinguish them from XML tags: `i < len`, not `i super
# Throws a compiler error
```
Arguments can be forwarded explicitly using splats:
```
codeFor('breaking_change_super_with_arguments')
```
Or if you know that the parent function doesn’t require arguments, just call `super()`:
```
codeFor('breaking_change_super_without_arguments')
```
CoffeeScript 1.x allowed the `extends` keyword to set up prototypal inheritance between functions, and `super` could be used manually prototype-assigned functions:
```coffee
A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...
# Last two lines each throw compiler errors in CoffeeScript 2
```
Due to the switch to ES2015 `extends` and `super`, using these keywords for prototypal functions are no longer supported. The above case could be refactored to:
```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_apply')
```
or
```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_class')
```
================================================
FILE: documentation/sections/breaking_changes_super_this.md
================================================
### `super` and `this`
In the constructor of a derived class (a class that `extends` another class), `this` cannot be used before calling `super`:
```coffee
class B extends A
constructor: -> this # Throws a compiler error
```
This also means you cannot pass a reference to `this` as an argument to `super` in the constructor of a derived class:
```coffee
class B extends A
constructor: (@arg) ->
super @arg # Throws a compiler error
```
This is a limitation of ES2015 classes. As a workaround, assign to `this` after the `super` call:
```
codeFor('breaking_change_super_this')
```
================================================
FILE: documentation/sections/cake.md
================================================
## Cake, and Cakefiles
CoffeeScript includes a (very) simple build system similar to [Make](http://www.gnu.org/software/make/) and [Rake](http://rake.rubyforge.org/). Naturally, it’s called Cake, and is used for the tasks that build and test the CoffeeScript language itself. Tasks are defined in a file named `Cakefile`, and can be invoked by running `cake [task]` from within the directory. To print a list of all the tasks and options, just type `cake`.
Task definitions are written in CoffeeScript, so you can put arbitrary code in your Cakefile. Define a task with a name, a long description, and the function to invoke when the task is run. If your task takes a command-line option, you can define the option with short and long flags, and it will be made available in the `options` object. Here’s a task that uses the Node.js API to rebuild CoffeeScript’s parser:
```
codeFor('cake_tasks')
```
If you need to invoke one task before another — for example, running `build` before `test`, you can use the `invoke` function: `invoke 'build'`. Cake tasks are a minimal way to expose your CoffeeScript functions to the command line, so [don’t expect any fanciness built-in](/v<%= majorVersion %>/annotated-source/cake.html). If you need dependencies, or async callbacks, it’s best to put them in your code itself — not the cake task.
================================================
FILE: documentation/sections/chaining.md
================================================
## Chaining Function Calls
Leading `.` closes all open calls, allowing for simpler chaining syntax.
```
codeFor('chaining')
```
================================================
FILE: documentation/sections/changelog/0.1.0.md
================================================
```
releaseHeader('2009-12-24', '0.1.0', '8e9d637985d2dc9b44922076ad54ffef7fa8e9c2')
```
Initial CoffeeScript release.
================================================
FILE: documentation/sections/changelog/0.1.1.md
================================================
```
releaseHeader('2009-12-24', '0.1.1', '0.1.0')
```
Added `instanceof` and `typeof` as operators.
================================================
FILE: documentation/sections/changelog/0.1.2.md
================================================
```
releaseHeader('2009-12-24', '0.1.2', '0.1.1')
```
Fixed a bug with calling `super()` through more than one level of inheritance, with the re-addition of the `extends` keyword. Added experimental [Narwhal](http://narwhaljs.org/) support (as a Tusk package), contributed by [Tom Robinson](http://blog.tlrobinson.net/), including **bin/cs** as a CoffeeScript REPL and interpreter. New `--no-wrap` option to suppress the safety function wrapper.
================================================
FILE: documentation/sections/changelog/0.1.3.md
================================================
```
releaseHeader('2009-12-25', '0.1.3', '0.1.2')
```
The `coffee` command now includes `--interactive`, which launches an interactive CoffeeScript session, and `--run`, which directly compiles and executes a script. Both options depend on a working installation of Narwhal. The `aint` keyword has been replaced by `isnt`, which goes together a little smoother with `is`. Quoted strings are now allowed as identifiers within object literals: eg. `{"5+5": 10}`. All assignment operators now use a colon: `+:`, `-:`, `*:`, etc.
================================================
FILE: documentation/sections/changelog/0.1.4.md
================================================
```
releaseHeader('2009-12-25', '0.1.4', '0.1.3')
```
The official CoffeeScript extension is now `.coffee` instead of `.cs`, which properly belongs to [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)). Due to popular demand, you can now also use `=` to assign. Unlike JavaScript, `=` can also be used within object literals, interchangeably with `:`. Made a grammatical fix for chained function calls like `func(1)(2)(3)(4)`. Inheritance and super no longer use `__proto__`, so they should be IE-compatible now.
================================================
FILE: documentation/sections/changelog/0.1.5.md
================================================
```
releaseHeader('2009-12-26', '0.1.5', '0.1.4')
```
Array slice literals and array comprehensions can now both take Ruby-style ranges to specify the start and end. JavaScript variable declaration is now pushed up to the top of the scope, making all assignment statements into expressions. You can use `\` to escape newlines. The `coffeescript` command is now called `coffee`.
================================================
FILE: documentation/sections/changelog/0.1.6.md
================================================
```
releaseHeader('2009-12-27', '0.1.6', '0.1.5')
```
Bugfix for running `coffee --interactive` and `--run` from outside of the CoffeeScript directory. Bugfix for nested function/if-statements.
================================================
FILE: documentation/sections/changelog/0.2.0.md
================================================
```
releaseHeader('2010-01-05', '0.2.0', '0.1.6')
```
Major release. Significant whitespace. Better statement-to-expression conversion. Splats. Splice literals. Object comprehensions. Blocks. The existential operator. Many thanks to all the folks who posted issues, with special thanks to [Liam O’Connor-Davis](https://github.com/liamoc) for whitespace and expression help.
================================================
FILE: documentation/sections/changelog/0.2.1.md
================================================
```
releaseHeader('2010-01-05', '0.2.1', '0.2.0')
```
Arguments objects are now converted into real arrays when referenced.
================================================
FILE: documentation/sections/changelog/0.2.2.md
================================================
```
releaseHeader('2010-01-10', '0.2.2', '0.2.1')
```
When performing a comprehension over an object, use `ino`, instead of `in`, which helps us generate smaller, more efficient code at compile time.
Added `::` as a shorthand for saying `.prototype.`
The “splat” symbol has been changed from a prefix asterisk `*`, to a postfix ellipsis `...`
Added JavaScript’s `in` operator, empty `return` statements, and empty `while` loops.
Constructor functions that start with capital letters now include a safety check to make sure that the new instance of the object is returned.
The `extends` keyword now functions identically to `goog.inherits` in Google’s Closure Library.
================================================
FILE: documentation/sections/changelog/0.2.3.md
================================================
```
releaseHeader('2010-01-11', '0.2.3', '0.2.2')
```
Axed the unsatisfactory `ino` keyword, replacing it with `of` for object comprehensions. They now look like: `for prop, value of object`.
================================================
FILE: documentation/sections/changelog/0.2.4.md
================================================
```
releaseHeader('2010-01-12', '0.2.4', '0.2.3')
```
Added ECMAScript Harmony style destructuring assignment, for dealing with extracting values from nested arrays and objects. Added indentation-sensitive heredocs for nicely formatted strings or chunks of code.
================================================
FILE: documentation/sections/changelog/0.2.5.md
================================================
```
releaseHeader('2010-01-13', '0.2.5', '0.2.4')
```
The conditions in switch statements can now take multiple values at once — If any of them are true, the case will run. Added the long arrow `==>`, which defines and immediately binds a function to `this`. While loops can now be used as expressions, in the same way that comprehensions can. Splats can be used within pattern matches to soak up the rest of an array.
================================================
FILE: documentation/sections/changelog/0.2.6.md
================================================
```
releaseHeader('2010-01-17', '0.2.6', '0.2.5')
```
Added Python-style chained comparisons, the conditional existence operator `?=`, and some examples from _Beautiful Code_. Bugfixes relating to statement-to-expression conversion, arguments-to-array conversion, and the TextMate syntax highlighter.
================================================
FILE: documentation/sections/changelog/0.3.0.md
================================================
```
releaseHeader('2010-01-26', '0.3.0', '0.2.6')
```
CoffeeScript 0.3 includes major syntax changes:
The function symbol was changed to `->`, and the bound function symbol is now `=>`.
Parameter lists in function definitions must now be wrapped in parentheses.
Added property soaking, with the `?.` operator.
Made parentheses optional, when invoking functions with arguments.
Removed the obsolete block literal syntax.
================================================
FILE: documentation/sections/changelog/0.3.2.md
================================================
```
releaseHeader('2010-02-08', '0.3.2', '0.3.0')
```
`@property` is now a shorthand for `this.property`.
Switched the default JavaScript engine from Narwhal to Node.js. Pass the `--narwhal` flag if you’d like to continue using it.
================================================
FILE: documentation/sections/changelog/0.5.0.md
================================================
```
releaseHeader('2010-02-21', '0.5.0', '0.3.2')
```
CoffeeScript 0.5.0 is a major release, While there are no language changes, the Ruby compiler has been removed in favor of a self-hosting compiler written in pure CoffeeScript.
================================================
FILE: documentation/sections/changelog/0.5.1.md
================================================
```
releaseHeader('2010-02-24', '0.5.1', '0.5.0')
```
Improvements to null soaking with the existential operator, including soaks on indexed properties. Added conditions to `while` loops, so you can use them as filters with `when`, in the same manner as comprehensions.
================================================
FILE: documentation/sections/changelog/0.5.2.md
================================================
```
releaseHeader('2010-02-25', '0.5.2', '0.5.1')
```
Added a compressed version of the compiler for inclusion in web pages as
`/v<%= majorVersion %>/browser-compiler-legacy/coffeescript.js`. It’ll automatically run any script tags with type `text/coffeescript` for you. Added a `--stdio` option to the `coffee` command, for piped-in compiles.
================================================
FILE: documentation/sections/changelog/0.5.3.md
================================================
```
releaseHeader('2010-02-27', '0.5.3', '0.5.2')
```
CoffeeScript now has a syntax for defining classes. Many of the core components (Nodes, Lexer, Rewriter, Scope, Optparse) are using them. Cakefiles can use `optparse.coffee` to define options for tasks. `--run` is now the default flag for the `coffee` command, use `--compile` to save JavaScripts. Bugfix for an ambiguity between RegExp literals and chained divisions.
================================================
FILE: documentation/sections/changelog/0.5.4.md
================================================
```
releaseHeader('2010-03-03', '0.5.4', '0.5.3')
```
Bugfix that corrects the Node.js global constants `__filename` and `__dirname`. Tweaks for more flexible parsing of nested function literals and improperly-indented comments. Updates for the latest Node.js API.
================================================
FILE: documentation/sections/changelog/0.5.5.md
================================================
```
releaseHeader('2010-03-08', '0.5.5', '0.5.4')
```
String interpolation, contributed by [Stan Angeloff](https://github.com/StanAngeloff). Since `--run` has been the default since **0.5.3**, updating `--stdio` and `--eval` to run by default, pass `--compile` as well if you’d like to print the result.
================================================
FILE: documentation/sections/changelog/0.5.6.md
================================================
```
releaseHeader('2010-03-23', '0.5.6', '0.5.5')
```
Interpolation can now be used within regular expressions and heredocs, as well as strings. Added the `<-` bind operator. Allowing assignment to half-expressions instead of special `||=`-style operators. The arguments object is no longer automatically converted into an array. After requiring `coffeescript`, Node.js can now directly load `.coffee` files, thanks to **registerExtension**. Multiple splats can now be used in function calls, arrays, and pattern matching.
================================================
FILE: documentation/sections/changelog/0.6.0.md
================================================
```
releaseHeader('2010-04-03', '0.6.0', '0.5.6')
```
Trailing commas are now allowed, a-la Python. Static properties may be assigned directly within class definitions, using `@property` notation.
================================================
FILE: documentation/sections/changelog/0.6.1.md
================================================
```
releaseHeader('2010-04-12', '0.6.1', '0.6.0')
```
Upgraded CoffeeScript for compatibility with the new Node.js **v0.1.90** series.
================================================
FILE: documentation/sections/changelog/0.6.2.md
================================================
```
releaseHeader('2010-05-15', '0.6.2', '0.6.1')
```
The `coffee` command will now preserve directory structure when compiling a directory full of scripts. Fixed two omissions that were preventing the CoffeeScript compiler from running live within Internet Explorer. There’s now a syntax for block comments, similar in spirit to CoffeeScript’s heredocs. ECMA Harmony DRY-style pattern matching is now supported, where the name of the property is the same as the name of the value: `{name, length}: func`. Pattern matching is now allowed within comprehension variables. `unless` is now allowed in block form. `until` loops were added, as the inverse of `while` loops. `switch` statements are now allowed without switch object clauses. Compatible with Node.js **v0.1.95**.
================================================
FILE: documentation/sections/changelog/0.7.0.md
================================================
```
releaseHeader('2010-06-28', '0.7.0', '0.6.2')
```
Official CoffeeScript variable style is now camelCase, as in JavaScript. Reserved words are now allowed as object keys, and will be quoted for you. Range comprehensions now generate cleaner code, but you have to specify `by -1` if you’d like to iterate downward. Reporting of syntax errors is greatly improved from the previous release. Running `coffee` with no arguments now launches the REPL, with Readline support. The `<-` bind operator has been removed from CoffeeScript. The `loop` keyword was added, which is equivalent to a `while true` loop. Comprehensions that contain closures will now close over their variables, like the semantics of a `forEach`. You can now use bound function in class definitions (bound to the instance). For consistency, `a in b` is now an array presence check, and `a of b` is an object-key check. Comments are no longer passed through to the generated JavaScript.
================================================
FILE: documentation/sections/changelog/0.7.1.md
================================================
```
releaseHeader('2010-07-11', '0.7.1', '0.7.0')
```
Block-style comments are now passed through and printed as JavaScript block comments – making them useful for licenses and copyright headers. Better support for running coffee scripts standalone via hashbangs. Improved syntax errors for tokens that are not in the grammar.
================================================
FILE: documentation/sections/changelog/0.7.2.md
================================================
```
releaseHeader('2010-07-12', '0.7.2', '0.7.1')
```
Quick bugfix (right after 0.7.1) for a problem that prevented `coffee` command-line options from being parsed in some circumstances.
================================================
FILE: documentation/sections/changelog/0.9.0.md
================================================
```
releaseHeader('2010-08-04', '0.9.0', '0.7.2')
```
The CoffeeScript **0.9** series is considered to be a release candidate for **1.0**; let’s give her a shakedown cruise. **0.9.0** introduces a massive backwards-incompatible change: Assignment now uses `=`, and object literals use `:`, as in JavaScript. This allows us to have implicit object literals, and YAML-style object definitions. Half assignments are removed, in favor of `+=`, `or=`, and friends. Interpolation now uses a hash mark `#` instead of the dollar sign `$` — because dollar signs may be part of a valid JS identifier. Downwards range comprehensions are now safe again, and are optimized to straight for loops when created with integer endpoints. A fast, unguarded form of object comprehension was added: `for all key, value of object`. Mentioning the `super` keyword with no arguments now forwards all arguments passed to the function, as in Ruby. If you extend class `B` from parent class `A`, if `A` has an `extended` method defined, it will be called, passing in `B` — this enables static inheritance, among other things. Cleaner output for functions bound with the fat arrow. `@variables` can now be used in parameter lists, with the parameter being automatically set as a property on the object — useful in constructors and setter functions. Constructor functions can now take splats.
================================================
FILE: documentation/sections/changelog/0.9.1.md
================================================
```
releaseHeader('2010-08-11', '0.9.1', '0.9.0')
```
Bugfix release for **0.9.1**. Greatly improves the handling of mixed implicit objects, implicit function calls, and implicit indentation. String and regex interpolation is now strictly `#{ … }` (Ruby style). The compiler now takes a `--require` flag, which specifies scripts to run before compilation.
================================================
FILE: documentation/sections/changelog/0.9.2.md
================================================
```
releaseHeader('2010-08-23', '0.9.2', '0.9.1')
```
Specifying the start and end of a range literal is now optional, eg. `array[3..]`. You can now say `a not instanceof b`. Fixed important bugs with nested significant and non-significant indentation (Issue #637). Added a `--require` flag that allows you to hook into the `coffee` command. Added a custom `jsl.conf` file for our preferred JavaScriptLint setup. Sped up Jison grammar compilation time by flattening rules for operations. Block comments can now be used with JavaScript-minifier-friendly syntax. Added JavaScript’s compound assignment bitwise operators. Bugfixes to implicit object literals with leading number and string keys, as the subject of implicit calls, and as part of compound assignment.
================================================
FILE: documentation/sections/changelog/0.9.3.md
================================================
```
releaseHeader('2010-09-16', '0.9.3', '0.9.2')
```
CoffeeScript `switch` statements now compile into JS `switch` statements — they previously compiled into `if/else` chains for JavaScript 1.3 compatibility. Soaking a function invocation is now supported. Users of the RubyMine editor should now be able to use `--watch` mode.
================================================
FILE: documentation/sections/changelog/0.9.4.md
================================================
```
releaseHeader('2010-09-21', '0.9.4', '0.9.3')
```
CoffeeScript now uses appropriately-named temporary variables, and recycles their references after use. Added `require.extensions` support for **Node.js 0.3**. Loading CoffeeScript in the browser now adds just a single `CoffeeScript` object to global scope. Fixes for implicit object and block comment edge cases.
================================================
FILE: documentation/sections/changelog/0.9.5.md
================================================
```
releaseHeader('2010-11-21', '0.9.5', '0.9.4')
```
0.9.5 should be considered the first release candidate for CoffeeScript 1.0. There have been a large number of internal changes since the previous release, many contributed from **satyr**’s [Coco](https://github.com/satyr/coco) dialect of CoffeeScript. Heregexes (extended regexes) were added. Functions can now have default arguments. Class bodies are now executable code. Improved syntax errors for invalid CoffeeScript. `undefined` now works like `null`, and cannot be assigned a new value. There was a precedence change with respect to single-line comprehensions: `result = i for i in list`
used to parse as `result = (i for i in list)` by default … it now parses as
`(result = i) for i in list`.
================================================
FILE: documentation/sections/changelog/0.9.6.md
================================================
```
releaseHeader('2010-12-06', '0.9.6', '0.9.5')
```
The REPL now properly formats stacktraces, and stays alive through asynchronous exceptions. Using `--watch` now prints timestamps as files are compiled. Fixed some accidentally-leaking variables within plucked closure-loops. Constructors now maintain their declaration location within a class body. Dynamic object keys were removed. Nested classes are now supported. Fixes execution context for naked splatted functions. Bugfix for inversion of chained comparisons. Chained class instantiation now works properly with splats.
================================================
FILE: documentation/sections/changelog/1.0.0.md
================================================
```
releaseHeader('2010-12-24', '1.0.0', '0.9.6')
```
CoffeeScript loops no longer try to preserve block scope when functions are being generated within the loop body. Instead, you can use the `do` keyword to create a convenient closure wrapper. Added a `--nodejs` flag for passing through options directly to the `node` executable. Better behavior around the use of pure statements within expressions. Fixed inclusive slicing through `-1`, for all browsers, and splicing with arbitrary expressions as endpoints.
================================================
FILE: documentation/sections/changelog/1.0.1.md
================================================
```
releaseHeader('2011-01-31', '1.0.1', '1.0.0')
```
Fixed a lexer bug with Unicode identifiers. Updated REPL for compatibility with Node.js 0.3.7\. Fixed requiring relative paths in the REPL. Trailing `return` and `return undefined` are now optimized away. Stopped requiring the core Node.js `util` module for back-compatibility with Node.js 0.2.5\. Fixed a case where a conditional `return` would cause fallthrough in a `switch` statement. Optimized empty objects in destructuring assignment.
================================================
FILE: documentation/sections/changelog/1.1.0.md
================================================
```
releaseHeader('2011-05-01', '1.1.0', '1.0.1')
```
When running via the `coffee` executable, `process.argv` and friends now report `coffee` instead of `node`. Better compatibility with **Node.js 0.4.x** module lookup changes. The output in the REPL is now colorized, like Node’s is. Giving your concatenated CoffeeScripts a name when using `--join` is now mandatory. Fix for lexing compound division `/=` as a regex accidentally. All `text/coffeescript` tags should now execute in the order they’re included. Fixed an issue with extended subclasses using external constructor functions. Fixed an edge-case infinite loop in `addImplicitParentheses`. Fixed exponential slowdown with long chains of function calls. Globals no longer leak into the CoffeeScript REPL. Splatted parameters are declared local to the function.
================================================
FILE: documentation/sections/changelog/1.1.1.md
================================================
```
releaseHeader('2011-05-10', '1.1.1', '1.1.0')
```
Bugfix release for classes with external constructor functions, see issue #1182.
================================================
FILE: documentation/sections/changelog/1.1.2.md
================================================
```
releaseHeader('2011-08-04', '1.1.2', '1.1.1')
```
Fixes for block comment formatting, `?=` compilation, implicit calls against control structures, implicit invocation of a try/catch block, variadic arguments leaking from local scope, line numbers in syntax errors following heregexes, property access on parenthesized number literals, bound class methods and super with reserved names, a REPL overhaul, consecutive compiled semicolons, block comments in implicitly called objects, and a Chrome bug.
================================================
FILE: documentation/sections/changelog/1.1.3.md
================================================
```
releaseHeader('2011-11-08', '1.1.3', '1.1.2')
```
* Ahh, whitespace. CoffeeScript’s compiled JS now tries to space things out and keep it readable, as you can see in the examples on this page.
* You can now call `super` in class level methods in class bodies, and bound class methods now preserve their correct context.
* JavaScript has always supported octal numbers `010 is 8`, and hexadecimal numbers `0xf is 15`, but CoffeeScript now also supports binary numbers: `0b10 is 2`.
* The CoffeeScript module has been nested under a subdirectory to make it easier to `require` individual components separately, without having to use **npm**. For example, after adding the CoffeeScript folder to your path: `require('coffeescript/lexer')`
* There’s a new “link” feature in Try CoffeeScript on this webpage. Use it to get a shareable permalink for your example script.
* The `coffee --watch` feature now only works on Node.js 0.6.0 and higher, but now also works properly on Windows.
* Lots of small bug fixes from **[@michaelficarra](https://github.com/michaelficarra)**, **[@geraldalewis](https://github.com/geraldalewis)**, **[@satyr](https://github.com/satyr)**, and **[@trevorburnham](https://github.com/trevorburnham)**.
================================================
FILE: documentation/sections/changelog/1.10.0.md
================================================
```
releaseHeader('2015-09-03', '1.10.0', '1.9.3')
```
* CoffeeScript now supports ES2015-style destructuring defaults.
* `(offsetHeight: height) ->` no longer compiles. That syntax was accidental and partly broken. Use `({offsetHeight: height}) ->` instead. Object destructuring always requires braces.
* Several minor bug fixes, including:
* A bug where the REPL would sometimes report valid code as invalid, based on what you had typed earlier.
* A problem with multiple JS contexts in the jest test framework.
* An error in io.js where strict mode is set on internal modules.
* A variable name clash for the caught error in `catch` blocks.
================================================
FILE: documentation/sections/changelog/1.11.0.md
================================================
```
releaseHeader('2016-09-24', '1.11.0', '1.10.0')
```
* CoffeeScript now supports ES2015 [`import` and `export` syntax](#modules).
* Added the `-M, --inline-map` flag to the compiler, allowing you embed the source map directly into the output JavaScript, rather than as a separate file.
* A bunch of fixes for `yield`:
* `yield return` can no longer mistakenly be used as an expression.
* `yield` now mirrors `return` in that it can be used stand-alone as well as with expressions. Where you previously wrote `yield undefined`, you may now write simply `yield`. However, this means also inheriting the same syntax limitations that `return` has, so these examples no longer compile:
```
doubles = ->
yield for i in [1..3]
i * 2
six = ->
yield
2 * 3
```
* The JavaScript output is a bit nicer, with unnecessary parentheses and spaces, double indentation and double semicolons around `yield` no longer present.
* `&&=`, `||=`, `and=` and `or=` no longer accidentally allow a space before the equals sign.
* Improved several error messages.
* Just like `undefined` compiles to `void 0`, `NaN` now compiles into `0/0` and `Infinity` into `2e308`.
* Bugfix for renamed destructured parameters with defaults. `({a: b = 1}) ->` no longer crashes the compiler.
* Improved the internal representation of a CoffeeScript program. This is only noticeable to tools that use `CoffeeScript.tokens` or `CoffeeScript.nodes`. Such tools need to update to take account for changed or added tokens and nodes.
* Several minor bug fixes, including:
* The caught error in `catch` blocks is no longer declared unnecessarily, and no longer mistakenly named `undefined` for `catch`-less `try` blocks.
* Unassignable parameter destructuring no longer crashes the compiler.
* Source maps are now used correctly for errors thrown from .coffee.md files.
* `coffee -e 'throw null'` no longer crashes.
* The REPL no longer crashes when using `.exit` to exit it.
* Invalid JavaScript is no longer output when lots of `for` loops are used in the same scope.
* A unicode issue when using stdin with the CLI.
================================================
FILE: documentation/sections/changelog/1.11.1.md
================================================
```
releaseHeader('2016-10-02', '1.11.1', '1.11.0')
```
* Bugfix for shorthand object syntax after interpolated keys.
* Bugfix for indentation-stripping in `"""` strings.
* Bugfix for not being able to use the name “arguments” for a prototype property of class.
* Correctly compile large hexadecimal numbers literals to `2e308` (just like all other large number literals do).
================================================
FILE: documentation/sections/changelog/1.12.0.md
================================================
```
releaseHeader('2016-12-04', '1.12.0', '1.11.1')
```
* CoffeeScript now supports ES2015 [tagged template literals](#tagged-template-literals). Note that using tagged template literals in your code makes you responsible for ensuring that either your runtime supports tagged template literals or that you transpile the output JavaScript further to a version your target runtime(s) support.
* CoffeeScript now provides a [`for…from`](#generator-iteration) syntax for outputting ES2015 [`for…of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of). (Sorry they couldn’t match, but we came up with `for…of` first for something else.) This allows iterating over generators or any other iterable object. Note that using `for…from` in your code makes you responsible for ensuring that either your runtime supports `for…of` or that you transpile the output JavaScript further to a version your target runtime(s) support.
* Triple backticks (`` `````) allow the creation of embedded JavaScript blocks where escaping single backticks is not required, which should improve interoperability with ES2015 template literals and with Markdown.
* Within single-backtick embedded JavaScript, backticks can now be escaped via `` \```.
* The browser tests now run in the browser again, and are accessible [here](/v<%= majorVersion %>/test.html) if you would like to test your browser.
* CoffeeScript-only keywords in ES2015 `import`s and `export`s are now ignored.
* The compiler now throws an error on trying to export an anonymous class.
* Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
================================================
FILE: documentation/sections/changelog/1.12.1.md
================================================
```
releaseHeader('2016-12-07', '1.12.1', '1.12.0')
```
* You can now import a module member named `default`, e.g. `import { default } from 'lib'`. Though like in ES2015, you cannot import an entire module and name it `default` (so `import default from 'lib'` is not allowed).
* Fix regression where `from` as a variable name was breaking `for` loop declarations. For the record, `from` is not a reserved word in CoffeeScript; you may use it for variable names. `from` behaves like a keyword within the context of `import` and `export` statements, and in the declaration of a `for` loop; though you should also be able to use variables named `from` in those contexts, and the compiler should be able to tell the difference.
================================================
FILE: documentation/sections/changelog/1.12.2.md
================================================
```
releaseHeader('2016-12-16', '1.12.2', '1.12.1')
```
* The browser compiler can once again be built unminified via `MINIFY=false cake build:browser`.
* The error-prone patched version of `Error.prepareStackTrace` has been removed.
* Command completion in the REPL (pressing tab to get suggestions) has been fixed for Node 6.9.1+.
* The [browser-based tests](/v<%= majorVersion %>/test.html) now include all the tests as the Node-based version.
================================================
FILE: documentation/sections/changelog/1.12.3.md
================================================
```
releaseHeader('2017-01-24', '1.12.3', '1.12.2')
```
* `@` values can now be used as indices in `for` expressions. This loosens the compilation of `for` expressions to allow the index variable to be an `@` value, e.g. `do @visit for @node, @index in nodes`. Within `@visit`, the index of the current node (`@node`) would be available as `@index`.
* CoffeeScript’s patched `Error.prepareStackTrace` has been restored, with some revisions that should prevent the erroneous exceptions that were making life difficult for some downstream projects. This fixes the incorrect line numbers in stack traces since 1.12.2.
* The `//=` operator’s output now wraps parentheses around the right operand, like the other assignment operators.
================================================
FILE: documentation/sections/changelog/1.12.4.md
================================================
```
releaseHeader('2017-02-18', '1.12.4', '1.12.3')
```
* The `cake` commands have been updated, with new `watch` options for most tasks. Clone the [CoffeeScript repo](https://github.com/jashkenas/coffeescript) and run `cake` at the root of the repo to see the options.
* Fixed a bug where `export`ing a referenced variable was preventing the variable from being declared.
* Fixed a bug where the `coffee` command wasn’t working for a `.litcoffee` file.
* Bugfixes related to tokens and location data, for better source maps and improved compatibility with downstream tools.
================================================
FILE: documentation/sections/changelog/1.12.5.md
================================================
```
releaseHeader('2017-04-10', '1.12.5', '1.12.4')
```
* Better handling of `default`, `from`, `as` and `*` within `import` and `export` statements. You can now import or export a member named `default` and the compiler won’t interpret it as the `default` keyword.
* Fixed a bug where invalid octal escape sequences weren’t throwing errors in the compiler.
================================================
FILE: documentation/sections/changelog/1.12.6.md
================================================
```
releaseHeader('2017-05-15', '1.12.6', '1.12.5')
```
* The `return` and `export` keywords can now accept implicit objects (defined by indentation, without needing braces).
* Support Unicode code point escapes (e.g. `\u{1F4A9}`).
* The `coffee` command now first looks to see if CoffeeScript is installed under `node_modules` in the current folder, and executes the `coffee` binary there if so; or otherwise it runs the globally installed one. This allows you to have one version of CoffeeScript installed globally and a different one installed locally for a particular project. (Likewise for the `cake` command.)
* Bugfixes for chained function calls not closing implicit objects or ternaries.
* Bugfixes for incorrect code generated by the `?` operator within a termary `if` statement.
* Fixed some tests, and failing tests now result in a nonzero exit code.
================================================
FILE: documentation/sections/changelog/1.12.7.md
================================================
```
releaseHeader('2017-07-16', '1.12.7', '1.12.6')
```
* Fix regressions in 1.12.6 related to chained function calls and indented `return` and `throw` arguments.
* The REPL no longer warns about assigning to `_`.
================================================
FILE: documentation/sections/changelog/1.2.0.md
================================================
```
releaseHeader('2011-12-18', '1.2.0', '1.1.3')
```
* Multiple improvements to `coffee --watch` and `--join`. You may now use both together, as well as add and remove files and directories within a `--watch`’d folder.
* The `throw` statement can now be used as part of an expression.
* Block comments at the top of the file will now appear outside of the safety closure wrapper.
* Fixed a number of minor 1.1.3 regressions having to do with trailing operators and unfinished lines, and a more major 1.1.3 regression that caused bound functions _within_ bound class functions to have the incorrect `this`.
================================================
FILE: documentation/sections/changelog/1.3.1.md
================================================
```
releaseHeader('2012-04-10', '1.3.1', '1.2.0')
```
* CoffeeScript now enforces all of JavaScript’s **Strict Mode** early syntax errors at compile time. This includes old-style octal literals, duplicate property names in object literals, duplicate parameters in a function definition, deleting naked variables, setting the value of `eval` or `arguments`, and more. See a full discussion at [#1547](https://github.com/jashkenas/coffeescript/issues/1547).
* The REPL now has a handy new multi-line mode for entering large blocks of code. It’s useful when copy-and-pasting examples into the REPL. Enter multi-line mode with `Ctrl-V`. You may also now pipe input directly into the REPL.
* CoffeeScript now prints a `Generated by CoffeeScript VERSION` header at the top of each compiled file.
* Conditional assignment of previously undefined variables `a or= b` is now considered a syntax error.
* A tweak to the semantics of `do`, which can now be used to more easily simulate a namespace: `do (x = 1, y = 2) -> …`
* Loop indices are now mutable within a loop iteration, and immutable between them.
* Both endpoints of a slice are now allowed to be omitted for consistency, effectively creating a shallow copy of the list.
* Additional tweaks and improvements to `coffee --watch` under Node’s “new” file watching API. Watch will now beep by default if you introduce a syntax error into a watched script. We also now ignore hidden directories by default when watching recursively.
================================================
FILE: documentation/sections/changelog/1.3.3.md
================================================
```
releaseHeader('2012-05-15', '1.3.3', '1.3.1')
```
* Due to the new semantics of JavaScript’s strict mode, CoffeeScript no longer guarantees that constructor functions have names in all runtimes. See [#2052](https://github.com/jashkenas/coffeescript/issues/2052) for discussion.
* Inside of a nested function inside of an instance method, it’s now possible to call `super` more reliably (walks recursively up).
* Named loop variables no longer have different scoping heuristics than other local variables. (Reverts #643)
* Fix for splats nested within the LHS of destructuring assignment.
* Corrections to our compile time strict mode forbidding of octal literals.
================================================
FILE: documentation/sections/changelog/1.4.0.md
================================================
```
releaseHeader('2012-10-23', '1.4.0', '1.3.3')
```
* The CoffeeScript compiler now strips Microsoft’s UTF-8 BOM if it exists, allowing you to compile BOM-borked source files.
* Fix Node/compiler deprecation warnings by removing `registerExtension`, and moving from `path.exists` to `fs.exists`.
* Small tweaks to splat compilation, backticks, slicing, and the error for duplicate keys in object literals.
================================================
FILE: documentation/sections/changelog/1.5.0.md
================================================
```
releaseHeader('2013-02-25', '1.5.0', '1.4.0')
```
* First release of [Literate CoffeeScript](#literate).
* The CoffeeScript REPL is now based on the Node.js REPL, and should work better and more familiarly.
* Returning explicit values from constructors is now forbidden. If you want to return an arbitrary value, use a function, not a constructor.
* You can now loop over an array backwards, without having to manually deal with the indexes: `for item in list by -1`
* Source locations are now preserved in the CoffeeScript AST, although source maps are not yet being emitted.
================================================
FILE: documentation/sections/changelog/1.6.1.md
================================================
```
releaseHeader('2013-03-05', '1.6.1', '1.5.0')
```
* First release of [source maps](#source-maps). Pass the `--map` flag to the compiler, and off you go. Direct all your thanks over to [Jason Walton](https://github.com/jwalton).
* Fixed a 1.5.0 regression with multiple implicit calls against an indented implicit object. Combinations of implicit function calls and implicit objects should generally be parsed better now — but it still isn’t good _style_ to nest them too heavily.
* `.coffee.md` is now also supported as a Literate CoffeeScript file extension, for existing tooling. `.litcoffee` remains the canonical one.
* Several minor fixes surrounding member properties, bound methods and `super` in class declarations.
================================================
FILE: documentation/sections/changelog/1.6.2.md
================================================
```
releaseHeader('2013-03-18', '1.6.2', '1.6.1')
```
* Source maps have been used to provide automatic line-mapping when running CoffeeScript directly via the `coffee` command, and for automatic line-mapping when running CoffeeScript directly in the browser. Also, to provide better error messages for semantic errors thrown by the compiler — [with colors, even](http://cl.ly/NdOA).
* Improved support for mixed literate/vanilla-style CoffeeScript projects, and generating source maps for both at the same time.
* Fixes for **1.6.x** regressions with overriding inherited bound functions, and for Windows file path management.
* The `coffee` command can now correctly `fork()` both `.coffee` and `.js` files. (Requires Node.js 0.9+)
================================================
FILE: documentation/sections/changelog/1.6.3.md
================================================
```
releaseHeader('2013-06-02', '1.6.3', '1.6.2')
```
* The CoffeeScript REPL now remembers your history between sessions. Just like a proper REPL should.
* You can now use `require` in Node to load `.coffee.md` Literate CoffeeScript files. In the browser, `text/literate-coffeescript` script tags.
* The old `coffee --lint` command has been removed. It was useful while originally working on the compiler, but has been surpassed by JSHint. You may now use `-l` to pass literate files in over **stdio**.
* Bugfixes for Windows path separators, `catch` without naming the error, and executable-class-bodies-with- prototypal-property-attachment.
================================================
FILE: documentation/sections/changelog/1.7.0.md
================================================
```
releaseHeader('2014-01-28', '1.7.0', '1.6.3')
```
* When requiring CoffeeScript files in Node you must now explicitly register the compiler. This can be done with `require 'coffeescript/register'` or `CoffeeScript.register()`. Also for configuration such as Mocha’s, use **coffeescript/register**.
* Improved error messages, source maps and stack traces. Source maps now use the updated `//#` syntax.
* Leading `.` now closes all open calls, allowing for simpler chaining syntax.
* Added `**`, `//` and `%%` operators and `...` expansion in parameter lists and destructuring expressions.
* Multiline strings are now joined by a single space and ignore all indentation. A backslash at the end of a line can denote the amount of whitespace between lines, in both strings and heredocs. Backslashes correctly escape whitespace in block regexes.
* Closing brackets can now be indented and therefore no longer cause unexpected error.
* Several breaking compilation fixes. Non-callable literals (strings, numbers etc.) don’t compile in a call now and multiple postfix conditionals compile properly. Postfix conditionals and loops always bind object literals. Conditional assignment compiles properly in subexpressions. `super` is disallowed outside of methods and works correctly inside `for` loops.
* Formatting of compiled block comments has been improved.
* No more `-p` folders on Windows.
* The `options` object passed to CoffeeScript is no longer mutated.
================================================
FILE: documentation/sections/changelog/1.7.1.md
================================================
```
releaseHeader('2014-01-29', '1.7.1', '1.7.0')
```
* Fixed a typo that broke node module lookup when running a script directly with the `coffee` binary.
================================================
FILE: documentation/sections/changelog/1.8.0.md
================================================
```
releaseHeader('2014-08-26', '1.8.0', '1.7.1')
```
* The `--join` option of the CLI is now deprecated.
* Source maps now use `.js.map` as file extension, instead of just `.map`.
* The CLI now exits with the exit code 1 when it fails to write a file to disk.
* The compiler no longer crashes on unterminated, single-quoted strings.
* Fixed location data for string interpolations, which made source maps out of sync.
* The error marker in error messages is now correctly positioned if the code is indented with tabs.
* Fixed a slight formatting error in CoffeeScript’s source map-patched stack traces.
* The `%%` operator now coerces its right operand only once.
* It is now possible to require CoffeeScript files from Cakefiles without having to register the compiler first.
* The CoffeeScript REPL is now exported and can be required using `require 'coffeescript/repl'`.
* Fixes for the REPL in Node 0.11.
================================================
FILE: documentation/sections/changelog/1.9.0.md
================================================
```
releaseHeader('2015-01-29', '1.9.0', '1.8.0')
```
* CoffeeScript now supports ES2015 generators. A generator is simply a function that `yield`s.
* More robust parsing and improved error messages for strings and regexes — especially with respect to interpolation.
* Changed strategy for the generation of internal compiler variable names. Note that this means that `@example` function parameters are no longer available as naked `example` variables within the function body.
* Fixed REPL compatibility with latest versions of Node and Io.js.
* Various minor bug fixes.
================================================
FILE: documentation/sections/changelog/1.9.1.md
================================================
```
releaseHeader('2015-02-18', '1.9.1', '1.9.0')
```
* Interpolation now works in object literal keys (again). You can use this to dynamically name properties.
* Internal compiler variable names no longer start with underscores. This makes the generated JavaScript a bit prettier, and also fixes an issue with the completely broken and ungodly way that AngularJS “parses” function arguments.
* Fixed a few `yield`-related edge cases with `yield return` and `yield throw`.
* Minor bug fixes and various improvements to compiler error messages.
================================================
FILE: documentation/sections/changelog/1.9.2.md
================================================
```
releaseHeader('2015-04-15', '1.9.2', '1.9.1')
```
* Fixed a **watch** mode error introduced in 1.9.1 when compiling multiple files with the same filename.
* Bugfix for `yield` around expressions containing `this`.
* Added a Ruby-style `-r` option to the REPL, which allows requiring a module before execution with `--eval` or `--interactive`.
* In `
================================================
FILE: documentation/site/sidebar.html
================================================
Try CoffeeScriptIntroductionOverviewCoffeeScript 2What’s New in CoffeeScript 2CompatibilityInstallationUsageCommand LineNode.jsTranspilationLanguage ReferenceFunctionsStringsObjects and ArraysCommentsLexical Scoping and Variable SafetyIf, Else, Unless, and Conditional AssignmentSplats, or Rest Parameters/Spread SyntaxLoops and ComprehensionsArray Slicing and SplicingEverything is an ExpressionOperators and AliasesExistential OperatorDestructuring AssignmentChaining Function CallsBound (Fat Arrow) FunctionsGenerator FunctionsAsync FunctionsClassesPrototypal InheritanceSwitch/When/ElseTry/Catch/FinallyChained ComparisonsBlock Regular ExpressionsTagged Template LiteralsModulesEmbedded JavaScriptJSXType AnnotationsLiterate CoffeeScriptSource MapsCake, and Cakefiles"text/coffeescript" Script TagsIntegrationsBuild ToolsCode EditorsFrameworksLinters and FormattingTestingResourcesBooksScreencastsExamplesChatAnnotated SourceContributingGitHubUnsupported ECMAScript Featureslet and constNamed Functions and Function Declarationsget and set Shorthand SyntaxBreaking Changes From 1.xBound (Fat Arrow) FunctionsDefault ValuesBound Generator FunctionsClassessuper and thissuper and extendsJSX and the < and > OperatorsLiterate CoffeeScript ParsingArgument Parsing and #! LinesChangelogBrowser-Based TestsVersion 1.x Documentation
================================================
FILE: documentation/site/styles.html
================================================
================================================
FILE: documentation/site/test.html
================================================
CoffeeScript Test Suite
CoffeeScript Test Suite
<%= tests %>
================================================
FILE: documentation/site/try.html
================================================
================================================
FILE: lib/coffeescript/browser.js
================================================
// Generated by CoffeeScript 2.7.0
(function() {
// This **Browser** compatibility layer extends core CoffeeScript functions
// to make things work smoothly when compiling code directly in the browser.
// We add support for loading remote Coffee scripts via **XHR**, and
// `text/coffeescript` script tags, source maps via data-URLs, and so on.
var CoffeeScript, compile,
indexOf = [].indexOf;
CoffeeScript = require('./coffeescript');
({compile} = CoffeeScript);
// Use `window.eval` to evaluate code, rather than just `eval`, to run the
// script in a clean global scope rather than inheriting the scope of the
// CoffeeScript compiler. (So that `cake test:browser` also works in Node,
// use either `window.eval` or `global.eval` as appropriate).
CoffeeScript.eval = function(code, options = {}) {
var globalRoot;
if (options.bare == null) {
options.bare = true;
}
globalRoot = typeof window !== "undefined" && window !== null ? window : global;
return globalRoot['eval'](compile(code, options));
};
// Running code does not provide access to this scope.
CoffeeScript.run = function(code, options = {}) {
options.bare = true;
options.shiftLine = true;
return Function(compile(code, options))();
};
// Export this more limited `CoffeeScript` than what is exported by
// `index.coffee`, which is intended for a Node environment.
module.exports = CoffeeScript;
// If we’re not in a browser environment, we’re finished with the public API.
if (typeof window === "undefined" || window === null) {
return;
}
// Include source maps where possible. If we’ve got a base64 encoder, a
// JSON serializer, and tools for escaping unicode characters, we’re good to go.
// Ported from https://developer.mozilla.org/en-US/docs/DOM/window.btoa
if ((typeof btoa !== "undefined" && btoa !== null) && (typeof JSON !== "undefined" && JSON !== null)) {
compile = function(code, options = {}) {
options.inlineMap = true;
return CoffeeScript.compile(code, options);
};
}
// Load a remote script from the current domain via XHR.
CoffeeScript.load = function(url, callback, options = {}, hold = false) {
var xhr;
options.sourceFiles = [url];
xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();
xhr.open('GET', url, true);
if ('overrideMimeType' in xhr) {
xhr.overrideMimeType('text/plain');
}
xhr.onreadystatechange = function() {
var param, ref;
if (xhr.readyState === 4) {
if ((ref = xhr.status) === 0 || ref === 200) {
param = [xhr.responseText, options];
if (!hold) {
CoffeeScript.run(...param);
}
} else {
throw new Error(`Could not load ${url}`);
}
if (callback) {
return callback(param);
}
}
};
return xhr.send(null);
};
// Activate CoffeeScript in the browser by having it compile and evaluate
// all script tags with a content-type of `text/coffeescript`.
// This happens on page load.
CoffeeScript.runScripts = function() {
var coffees, coffeetypes, execute, i, index, j, len, s, script, scripts;
scripts = window.document.getElementsByTagName('script');
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript'];
coffees = (function() {
var j, len, ref, results;
results = [];
for (j = 0, len = scripts.length; j < len; j++) {
s = scripts[j];
if (ref = s.type, indexOf.call(coffeetypes, ref) >= 0) {
results.push(s);
}
}
return results;
})();
index = 0;
execute = function() {
var param;
param = coffees[index];
if (param instanceof Array) {
CoffeeScript.run(...param);
index++;
return execute();
}
};
for (i = j = 0, len = coffees.length; j < len; i = ++j) {
script = coffees[i];
(function(script, i) {
var options, source;
options = {
literate: script.type === coffeetypes[1]
};
source = script.src || script.getAttribute('data-src');
if (source) {
options.filename = source;
return CoffeeScript.load(source, function(param) {
coffees[i] = param;
return execute();
}, options, true);
} else {
// `options.filename` defines the filename the source map appears as
// in Developer Tools. If a script tag has an `id`, use that as the
// filename; otherwise use `coffeescript`, or `coffeescript1` etc.,
// leaving the first one unnumbered for the common case that there’s
// only one CoffeeScript script block to parse.
options.filename = script.id && script.id !== '' ? script.id : `coffeescript${i !== 0 ? i : ''}`;
options.sourceFiles = ['embedded'];
return coffees[i] = [script.innerHTML, options];
}
})(script, i);
}
return execute();
};
// Listen for window load, both in decent browsers and in IE.
// Only attach this event handler on startup for the
// non-ES module version of the browser compiler, to preserve
// backward compatibility while letting the ES module version
// be importable without side effects.
if (this === window) {
if (window.addEventListener) {
window.addEventListener('DOMContentLoaded', CoffeeScript.runScripts, false);
} else {
window.attachEvent('onload', CoffeeScript.runScripts);
}
}
}).call(this);
================================================
FILE: lib/coffeescript/cake.js
================================================
// Generated by CoffeeScript 2.7.0
(function() {
// `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
// ([Rake](http://rake.rubyforge.org/), [Jake](https://github.com/280north/jake))
// for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
// and can call them from the command line, or invoke them from other tasks.
// Running `cake` with no arguments will print out a list of all the tasks in the
// current directory's Cakefile.
// External dependencies.
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
fs = require('fs');
path = require('path');
helpers = require('./helpers');
optparse = require('./optparse');
CoffeeScript = require('./');
// Register .coffee extension
CoffeeScript.register();
// Keep track of the list of defined tasks, the accepted options, and so on.
tasks = {};
options = {};
switches = [];
oparse = null;
// Mixin the top-level Cake functions for Cakefiles to use directly.
helpers.extend(global, {
// Define a Cake task with a short name, an optional sentence description,
// and the function to run as the action itself.
task: function(name, description, action) {
if (!action) {
[action, description] = [description, action];
}
return tasks[name] = {name, description, action};
},
// Define an option that the Cakefile accepts. The parsed options hash,
// containing all of the command-line options passed, will be made available
// as the first argument to the action.
option: function(letter, flag, description) {
return switches.push([letter, flag, description]);
},
// Invoke another task in the current Cakefile.
invoke: function(name) {
if (!tasks[name]) {
missingTask(name);
}
return tasks[name].action(options);
}
});
// Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
// asynchrony may cause tasks to execute in a different order than you'd expect.
// If no tasks are passed, print the help screen. Keep a reference to the
// original directory name, when running Cake tasks from subdirectories.
exports.run = function() {
var arg, args, e, i, len, ref, results;
global.__originalDirname = fs.realpathSync('.');
process.chdir(cakefileDirectory(__originalDirname));
args = process.argv.slice(2);
CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
filename: 'Cakefile'
});
oparse = new optparse.OptionParser(switches);
if (!args.length) {
return printTasks();
}
try {
options = oparse.parse(args);
} catch (error) {
e = error;
return fatalError(`${e}`);
}
ref = options.arguments;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
arg = ref[i];
results.push(invoke(arg));
}
return results;
};
// Display the list of Cake tasks in a format similar to `rake -T`
printTasks = function() {
var cakefilePath, desc, name, relative, spaces, task;
relative = path.relative || path.resolve;
cakefilePath = path.join(relative(__originalDirname, process.cwd()), 'Cakefile');
console.log(`${cakefilePath} defines the following tasks:\n`);
for (name in tasks) {
task = tasks[name];
spaces = 20 - name.length;
spaces = spaces > 0 ? Array(spaces + 1).join(' ') : '';
desc = task.description ? `# ${task.description}` : '';
console.log(`cake ${name}${spaces} ${desc}`);
}
if (switches.length) {
return console.log(oparse.help());
}
};
// Print an error and exit when attempting to use an invalid task/option.
fatalError = function(message) {
console.error(message + '\n');
console.log('To see a list of all tasks/options, run "cake"');
return process.exit(1);
};
missingTask = function(task) {
return fatalError(`No such task: ${task}`);
};
// When `cake` is invoked, search in the current and all parent directories
// to find the relevant Cakefile.
cakefileDirectory = function(dir) {
var parent;
if (fs.existsSync(path.join(dir, 'Cakefile'))) {
return dir;
}
parent = path.normalize(path.join(dir, '..'));
if (parent !== dir) {
return cakefileDirectory(parent);
}
throw new Error(`Cakefile not found in ${process.cwd()}`);
};
}).call(this);
================================================
FILE: lib/coffeescript/coffeescript.js
================================================
// Generated by CoffeeScript 2.7.0
(function() {
// CoffeeScript can be used both on the server, as a command-line compiler based
// on Node.js/V8, or to run CoffeeScript directly in the browser. This module
// contains the main entry functions for tokenizing, parsing, and compiling
// source CoffeeScript into JavaScript.
var FILE_EXTENSIONS, Lexer, SourceMap, base64encode, checkShebangLine, compile, getSourceMap, helpers, lexer, packageJson, parser, registerCompiled, withPrettyErrors;
({Lexer} = require('./lexer'));
({parser} = require('./parser'));
helpers = require('./helpers');
SourceMap = require('./sourcemap');
// Require `package.json`, which is two levels above this file, as this file is
// evaluated from `lib/coffeescript`.
packageJson = require('../../package.json');
// The current CoffeeScript version number.
exports.VERSION = packageJson.version;
exports.FILE_EXTENSIONS = FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'];
// Expose helpers for testing.
exports.helpers = helpers;
({getSourceMap, registerCompiled} = SourceMap);
// This is exported to enable an external module to implement caching of
// sourcemaps. This is used only when `patchStackTrace` has been called to adjust
// stack traces for files with cached source maps.
exports.registerCompiled = registerCompiled;
// Function that allows for btoa in both nodejs and the browser.
base64encode = function(src) {
switch (false) {
case typeof Buffer !== 'function':
return Buffer.from(src).toString('base64');
case typeof btoa !== 'function':
// The contents of a `
Comments
In CoffeeScript, comments are denoted by the
#character to the end of a line, or from###to the next appearance of###. Comments are ignored by the compiler, though the compiler makes its best effort at reinserting your comments into the output JavaScript after compilation.Inline
###comments make type annotations possible.