Merge branch 'master' into ast

# Conflicts:
#	lib/coffeescript/grammar.js
#	lib/coffeescript/nodes.js
#	lib/coffeescript/parser.js
#	package-lock.json
#	src/nodes.coffee
#	test/error_messages.coffee
This commit is contained in:
Geoffrey Booth 2019-03-24 19:22:33 -07:00
commit 0b5bac666f
24 changed files with 1576 additions and 1441 deletions

View File

@ -486,6 +486,9 @@ task 'test:integrations', 'test the module integrated with other libraries and e
webpack = require 'webpack' webpack = require 'webpack'
webpack { webpack {
entry: './' entry: './'
optimization:
# Webpacks minification causes the CoffeeScript module to fail some tests.
minimize: off
output: output:
path: tmpdir path: tmpdir
filename: 'coffeescript.js' filename: 'coffeescript.js'

View File

@ -376,7 +376,7 @@ undeclared variable <code>__</code>.</p>
vm.runInContext js, context, filename vm.runInContext js, context, filename
<span class="hljs-function"> <span class="hljs-function">
<span class="hljs-title">addMultilineHandler</span> = <span class="hljs-params">(repl)</span> -&gt;</span> <span class="hljs-title">addMultilineHandler</span> = <span class="hljs-params">(repl)</span> -&gt;</span>
{rli, inputStream, outputStream} = repl</pre></div></div> {inputStream, outputStream} = repl</pre></div></div>
</li> </li>
@ -412,15 +412,15 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> nodeLineListener = rli.listeners(<span class="hljs-string">'line'</span>)[<span class="hljs-number">0</span>] <div class="content"><div class='highlight'><pre> nodeLineListener = repl.listeners(<span class="hljs-string">'line'</span>)[<span class="hljs-number">0</span>]
rli.removeListener <span class="hljs-string">'line'</span>, nodeLineListener repl.removeListener <span class="hljs-string">'line'</span>, nodeLineListener
rli.<span class="hljs-literal">on</span> <span class="hljs-string">'line'</span>, <span class="hljs-function"><span class="hljs-params">(cmd)</span> -&gt;</span> repl.<span class="hljs-literal">on</span> <span class="hljs-string">'line'</span>, <span class="hljs-function"><span class="hljs-params">(cmd)</span> -&gt;</span>
<span class="hljs-keyword">if</span> multiline.enabled <span class="hljs-keyword">if</span> multiline.enabled
multiline.buffer += <span class="hljs-string">"<span class="hljs-subst">#{cmd}</span>\n"</span> multiline.buffer += <span class="hljs-string">"<span class="hljs-subst">#{cmd}</span>\n"</span>
rli.setPrompt multiline.prompt repl.setPrompt multiline.prompt
rli.prompt <span class="hljs-literal">true</span> repl.prompt <span class="hljs-literal">true</span>
<span class="hljs-keyword">else</span> <span class="hljs-keyword">else</span>
rli.setPrompt origPrompt repl.setPrompt origPrompt
nodeLineListener cmd nodeLineListener cmd
<span class="hljs-keyword">return</span></pre></div></div> <span class="hljs-keyword">return</span></pre></div></div>
@ -456,8 +456,8 @@ undeclared variable <code>__</code>.</p>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> multiline.buffer.match <span class="hljs-regexp">/\n/</span> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> multiline.buffer.match <span class="hljs-regexp">/\n/</span>
multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled
rli.setPrompt origPrompt repl.setPrompt origPrompt
rli.prompt <span class="hljs-literal">true</span> repl.prompt <span class="hljs-literal">true</span>
<span class="hljs-keyword">return</span></pre></div></div> <span class="hljs-keyword">return</span></pre></div></div>
</li> </li>
@ -473,7 +473,7 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> rli.line? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> rli.line.match <span class="hljs-regexp">/^\s*$/</span></pre></div></div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> repl.line? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> repl.line.match <span class="hljs-regexp">/^\s*$/</span></pre></div></div>
</li> </li>
@ -489,10 +489,10 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled <div class="content"><div class='highlight'><pre> multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled
rli.line = <span class="hljs-string">''</span> repl.line = <span class="hljs-string">''</span>
rli.cursor = <span class="hljs-number">0</span> repl.cursor = <span class="hljs-number">0</span>
rli.output.cursorTo <span class="hljs-number">0</span> repl.output.cursorTo <span class="hljs-number">0</span>
rli.output.clearLine <span class="hljs-number">1</span></pre></div></div> repl.output.clearLine <span class="hljs-number">1</span></pre></div></div>
</li> </li>
@ -508,12 +508,12 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> multiline.buffer = multiline.buffer.replace <span class="hljs-regexp">/\n/g</span>, <span class="hljs-string">'\uFF00'</span> <div class="content"><div class='highlight'><pre> multiline.buffer = multiline.buffer.replace <span class="hljs-regexp">/\n/g</span>, <span class="hljs-string">'\uFF00'</span>
rli.emit <span class="hljs-string">'line'</span>, multiline.buffer repl.emit <span class="hljs-string">'line'</span>, multiline.buffer
multiline.buffer = <span class="hljs-string">''</span> multiline.buffer = <span class="hljs-string">''</span>
<span class="hljs-keyword">else</span> <span class="hljs-keyword">else</span>
multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled multiline.enabled = <span class="hljs-keyword">not</span> multiline.enabled
rli.setPrompt multiline.initialPrompt repl.setPrompt multiline.initialPrompt
rli.prompt <span class="hljs-literal">true</span> repl.prompt <span class="hljs-literal">true</span>
<span class="hljs-keyword">return</span></pre></div></div> <span class="hljs-keyword">return</span></pre></div></div>
</li> </li>
@ -580,7 +580,7 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> repl.rli.history = buffer.toString().split(<span class="hljs-string">'\n'</span>).reverse()</pre></div></div> <div class="content"><div class='highlight'><pre> repl.history = buffer.toString().split(<span class="hljs-string">'\n'</span>).reverse()</pre></div></div>
</li> </li>
@ -595,7 +595,7 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> repl.rli.history.pop() <span class="hljs-keyword">if</span> stat.size &gt; maxSize</pre></div></div> <div class="content"><div class='highlight'><pre> repl.history.pop() <span class="hljs-keyword">if</span> stat.size &gt; maxSize</pre></div></div>
</li> </li>
@ -610,13 +610,13 @@ undeclared variable <code>__</code>.</p>
</div> </div>
<div class="content"><div class='highlight'><pre> repl.rli.history.shift() <span class="hljs-keyword">if</span> repl.rli.history[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">''</span> <div class="content"><div class='highlight'><pre> repl.history.shift() <span class="hljs-keyword">if</span> repl.history[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">''</span>
repl.rli.historyIndex = <span class="hljs-number">-1</span> repl.historyIndex = <span class="hljs-number">-1</span>
lastLine = repl.rli.history[<span class="hljs-number">0</span>] lastLine = repl.history[<span class="hljs-number">0</span>]
fd = fs.openSync filename, <span class="hljs-string">'a'</span> fd = fs.openSync filename, <span class="hljs-string">'a'</span>
repl.rli.addListener <span class="hljs-string">'line'</span>, <span class="hljs-function"><span class="hljs-params">(code)</span> -&gt;</span> repl.addListener <span class="hljs-string">'line'</span>, <span class="hljs-function"><span class="hljs-params">(code)</span> -&gt;</span>
<span class="hljs-keyword">if</span> code <span class="hljs-keyword">and</span> code.length <span class="hljs-keyword">and</span> code <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.history'</span> <span class="hljs-keyword">and</span> code <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.exit'</span> <span class="hljs-keyword">and</span> lastLine <span class="hljs-keyword">isnt</span> code</pre></div></div> <span class="hljs-keyword">if</span> code <span class="hljs-keyword">and</span> code.length <span class="hljs-keyword">and</span> code <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.history'</span> <span class="hljs-keyword">and</span> code <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.exit'</span> <span class="hljs-keyword">and</span> lastLine <span class="hljs-keyword">isnt</span> code</pre></div></div>
</li> </li>
@ -667,7 +667,7 @@ undeclared variable <code>__</code>.</p>
<div class="content"><div class='highlight'><pre> repl.commands[getCommandId(repl, <span class="hljs-string">'history'</span>)] = <div class="content"><div class='highlight'><pre> repl.commands[getCommandId(repl, <span class="hljs-string">'history'</span>)] =
help: <span class="hljs-string">'Show command history'</span> help: <span class="hljs-string">'Show command history'</span>
action: <span class="hljs-function">-&gt;</span> action: <span class="hljs-function">-&gt;</span>
repl.outputStream.write <span class="hljs-string">"<span class="hljs-subst">#{repl.rli.history[..].reverse().join <span class="hljs-string">'\n'</span>}</span>\n"</span> repl.outputStream.write <span class="hljs-string">"<span class="hljs-subst">#{repl.history[..].reverse().join <span class="hljs-string">'\n'</span>}</span>\n"</span>
repl.displayPrompt() repl.displayPrompt()
<span class="hljs-function"> <span class="hljs-function">
<span class="hljs-title">getCommandId</span> = <span class="hljs-params">(repl, commandName)</span> -&gt;</span></pre></div></div> <span class="hljs-title">getCommandId</span> = <span class="hljs-params">(repl, commandName)</span> -&gt;</span></pre></div></div>
@ -742,7 +742,7 @@ the REPL, the only applicable option is <code>transpile</code>.</p>
opts = merge replDefaults, opts opts = merge replDefaults, opts
repl = nodeREPL.start opts repl = nodeREPL.start opts
runInContext opts.prelude, repl.context, <span class="hljs-string">'prelude'</span> <span class="hljs-keyword">if</span> opts.prelude runInContext opts.prelude, repl.context, <span class="hljs-string">'prelude'</span> <span class="hljs-keyword">if</span> opts.prelude
repl.<span class="hljs-literal">on</span> <span class="hljs-string">'exit'</span>, <span class="hljs-function">-&gt;</span> repl.outputStream.write <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> repl.rli.closed repl.<span class="hljs-literal">on</span> <span class="hljs-string">'exit'</span>, <span class="hljs-function">-&gt;</span> repl.outputStream.write <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> repl.closed
addMultilineHandler repl addMultilineHandler repl
addHistory repl, opts.historyFile, opts.historyMaxInputSize <span class="hljs-keyword">if</span> opts.historyFile</pre></div></div> addHistory repl, opts.historyFile, opts.historyMaxInputSize <span class="hljs-keyword">if</span> opts.historyFile</pre></div></div>

View File

@ -4,7 +4,7 @@
// on Node.js/V8, or to run CoffeeScript directly in the browser. This module // on Node.js/V8, or to run CoffeeScript directly in the browser. This module
// contains the main entry functions for tokenizing, parsing, and compiling // contains the main entry functions for tokenizing, parsing, and compiling
// source CoffeeScript into JavaScript. // source CoffeeScript into JavaScript.
var FILE_EXTENSIONS, Lexer, SourceMap, base64encode, checkShebangLine, compile, formatSourcePosition, getSourceMap, helpers, lexer, packageJson, parser, sourceMaps, sources, withPrettyErrors, var FILE_EXTENSIONS, Lexer, SourceMap, base64encode, checkShebangLine, compile, formatSourcePosition, getSourceMap, helpers, lexer, packageJson, parser, registerCompiled, sourceMaps, sources, withPrettyErrors,
indexOf = [].indexOf; indexOf = [].indexOf;
({Lexer} = require('./lexer')); ({Lexer} = require('./lexer'));
@ -74,6 +74,24 @@
// Also save source maps if generated, in form of `(source)`: [`(source map)`]. // Also save source maps if generated, in form of `(source)`: [`(source map)`].
sourceMaps = {}; sourceMaps = {};
// This is exported to enable an external module to implement caching of
// compilation results. When the compiled js source is loaded from cache, the
// original coffee code should be added with this method in order to enable the
// Error.prepareStackTrace below to correctly adjust the stack trace for the
// corresponding file (the source map will be generated on demand).
exports.registerCompiled = registerCompiled = function(filename, source, sourcemap) {
if (sources[filename] == null) {
sources[filename] = [];
}
sources[filename].push(source);
if (sourcemap != null) {
if (sourceMaps[filename] == null) {
sourceMaps[filename] = [];
}
return sourceMaps[filename].push(sourcemap);
}
};
// Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler. // Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
// If `options.sourceMap` is specified, then `options.filename` must also be // If `options.sourceMap` is specified, then `options.filename` must also be
@ -94,10 +112,6 @@
generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null); generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null);
filename = options.filename || '<anonymous>'; filename = options.filename || '<anonymous>';
checkShebangLine(filename, code); checkShebangLine(filename, code);
if (sources[filename] == null) {
sources[filename] = [];
}
sources[filename].push(code);
if (generateSourceMap) { if (generateSourceMap) {
map = new SourceMap(); map = new SourceMap();
} }
@ -177,10 +191,6 @@
} }
if (generateSourceMap) { if (generateSourceMap) {
v3SourceMap = map.generate(options, code); v3SourceMap = map.generate(options, code);
if (sourceMaps[filename] == null) {
sourceMaps[filename] = [];
}
sourceMaps[filename].push(map);
} }
if (options.transpile) { if (options.transpile) {
if (typeof options.transpile !== 'object') { if (typeof options.transpile !== 'object') {
@ -211,6 +221,7 @@
sourceURL = `//# sourceURL=${(ref1 = options.filename) != null ? ref1 : 'coffeescript'}`; sourceURL = `//# sourceURL=${(ref1 = options.filename) != null ? ref1 : 'coffeescript'}`;
js = `${js}\n${sourceMapDataURI}\n${sourceURL}`; js = `${js}\n${sourceMapDataURI}\n${sourceURL}`;
} }
registerCompiled(filename, code, map);
if (options.sourceMap) { if (options.sourceMap) {
return { return {
js, js,

View File

@ -412,6 +412,11 @@
false, false,
$1); $1);
}), }),
o('DYNAMIC_IMPORT Arguments',
function() {
return new DynamicImportCall(LOC(1)(new DynamicImport()),
$2);
}),
o('SimpleObjAssignable Arguments', o('SimpleObjAssignable Arguments',
function() { function() {
return new Call(new Value($1), return new Call(new Value($1),
@ -1077,6 +1082,11 @@
$3, $3,
$2, $2,
$1); $1);
}),
o('DYNAMIC_IMPORT Arguments',
function() {
return new DynamicImportCall(LOC(1)(new DynamicImport()),
$2);
}) })
], ],
// An optional existence check on a function. // An optional existence check on a function.
@ -2321,7 +2331,7 @@
// And not: // And not:
// (2 + 3) * 4 // (2 + 3) * 4
operators = [['right', 'DO_IIFE'], ['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY', 'DO'], ['right', 'AWAIT'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', '&'], ['left', '^'], ['left', '|'], ['left', '&&'], ['left', '||'], ['left', 'BIN?'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', 'YIELD'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT'], ['left', 'POST_IF']]; operators = [['right', 'DO_IIFE'], ['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY', 'DO'], ['right', 'AWAIT'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', '&'], ['left', '^'], ['left', '|'], ['left', '&&'], ['left', '||'], ['left', 'BIN?'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', 'YIELD'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT', 'DYNAMIC_IMPORT'], ['left', 'POST_IF']];
// Wrapping Up // Wrapping Up
// ----------- // -----------

View File

@ -152,9 +152,8 @@
} }
} }
CoffeeScript._compileFile = function(filename, options = {}) { CoffeeScript._compileRawFileContent = function(raw, filename, options = {}) {
var answer, err, raw, stripped; var answer, err, stripped;
raw = fs.readFileSync(filename, 'utf8');
// Strip the Unicode byte order mark, if this file begins with one. // Strip the Unicode byte order mark, if this file begins with one.
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw; stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
options = Object.assign({}, options, { options = Object.assign({}, options, {
@ -175,6 +174,12 @@
return answer; return answer;
}; };
CoffeeScript._compileFile = function(filename, options = {}) {
var raw;
raw = fs.readFileSync(filename, 'utf8');
return CoffeeScript._compileRawFileContent(raw, filename, options);
};
module.exports = CoffeeScript; module.exports = CoffeeScript;
}).call(this); }).call(this);

View File

@ -997,6 +997,9 @@
return value.length; return value.length;
} }
} }
if (value === '(' && (prev != null ? prev[0] : void 0) === 'IMPORT') {
prev[0] = 'DYNAMIC_IMPORT';
}
if (value === '{' && this.seenImport) { if (value === '{' && this.seenImport) {
this.importSpecifierList = true; this.importSpecifierList = true;
} else if (this.importSpecifierList && value === '}') { } else if (this.importSpecifierList && value === '}') {
@ -1714,7 +1717,7 @@
// Tokens which could legitimately be invoked or indexed. An opening // Tokens which could legitimately be invoked or indexed. An opening
// parentheses or bracket following these tokens will be recorded as the start // parentheses or bracket following these tokens will be recorded as the start
// of a function invocation or indexing operation. // of a function invocation or indexing operation.
CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER']; CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER', 'DYNAMIC_IMPORT'];
INDEXABLE = CALLABLE.concat(['NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']); INDEXABLE = CALLABLE.concat(['NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']);

View File

@ -4,7 +4,7 @@
// nodes are created as the result of actions in the [grammar](grammar.html), // nodes are created as the result of actions in the [grammar](grammar.html),
// but some are created by other nodes as a method of code generation. To convert // 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 syntax tree into a string of JavaScript code, call `compile()` on the root.
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXAttribute, CSXAttributes, CSXElement, CSXExpressionContainer, CSXIdentifier, CSXTag, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXAttribute, CSXAttributes, CSXElement, CSXExpressionContainer, CSXIdentifier, CSXTag, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
indexOf = [].indexOf, indexOf = [].indexOf,
splice = [].splice, splice = [].splice,
slice1 = [].slice; slice1 = [].slice;
@ -282,7 +282,7 @@
// Occasionally it may be useful to make an expression behave as if it was 'hoisted', whereby the // 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 // result of the expression is available before its location in the source, but the expression's
// variable scope corresponds the source position. This is used extensively to deal with executable // variable scope corresponds to the source position. This is used extensively to deal with executable
// class bodies in classes. // class bodies in classes.
// Calling this method mutates the node, proxying the `compileNode` and `compileToFragments` // Calling this method mutates the node, proxying the `compileNode` and `compileToFragments`
@ -3468,10 +3468,10 @@
return [this.makeCode('[]')]; return [this.makeCode('[]')];
} }
o.indent += TAB; o.indent += TAB;
fragmentIsElision = function(fragment) { fragmentIsElision = function([fragment]) {
return fragmentsToText(fragment).trim() === ','; return fragment.type === 'Elision' && fragment.code.trim() === ',';
}; };
// Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]). // Detect if `Elision`s at the beginning of the array are processed (e.g. [, , , a]).
passedElision = false; passedElision = false;
answer = []; answer = [];
ref1 = this.objects; ref1 = this.objects;
@ -3528,7 +3528,7 @@
fragment = answer[fragmentIndex]; fragment = answer[fragmentIndex];
if (fragment.isHereComment) { if (fragment.isHereComment) {
fragment.code = `${multident(fragment.code, o.indent, false)}\n${o.indent}`; fragment.code = `${multident(fragment.code, o.indent, false)}\n${o.indent}`;
} else if (fragment.code === ', ' && !(fragment != null ? fragment.isElision : void 0)) { } else if (fragment.code === ', ' && !(fragment != null ? fragment.isElision : void 0) && fragment.type !== 'StringLiteral') {
fragment.code = `,\n${o.indent}`; fragment.code = `,\n${o.indent}`;
} }
} }
@ -4013,7 +4013,7 @@
return this.body.traverseChildren(false, (node) => { return this.body.traverseChildren(false, (node) => {
if (node instanceof ThisLiteral) { if (node instanceof ThisLiteral) {
return node.value = this.name; return node.value = this.name;
} else if (node instanceof Code && node.bound && node.isStatic) { } else if (node instanceof Code && node.bound && (node.isStatic || !node.name)) {
return node.context = this.name; return node.context = this.name;
} }
}); });
@ -4403,6 +4403,23 @@
}; };
exports.DynamicImport = DynamicImport = class DynamicImport extends Base {
compileNode() {
return [this.makeCode('import')];
}
};
exports.DynamicImportCall = DynamicImportCall = class DynamicImportCall extends Call {
compileNode(o) {
if (this.args.length !== 1) {
this.error('import() requires exactly one argument');
}
return super.compileNode(o);
}
};
//### Assign //### Assign
// The **Assign** is used to assign a local variable to value, or to set the // The **Assign** is used to assign a local variable to value, or to set the
@ -5223,7 +5240,7 @@
// (e.g. `ref`), shift those into the parent scope since we cant put a // (e.g. `ref`), shift those into the parent scope since we cant put a
// `var` line inside a function parameter list. // `var` line inside a function parameter list.
scopeVariablesCount = o.scope.variables.length; scopeVariablesCount = o.scope.variables.length;
signature.push(...param.compileToFragments(o)); signature.push(...param.compileToFragments(o, LEVEL_PAREN));
if (scopeVariablesCount !== o.scope.variables.length) { if (scopeVariablesCount !== o.scope.variables.length) {
generatedVariables = o.scope.variables.splice(scopeVariablesCount); generatedVariables = o.scope.variables.splice(scopeVariablesCount);
o.scope.parent.variables.push(...generatedVariables); o.scope.parent.variables.push(...generatedVariables);

File diff suppressed because one or more lines are too long

View File

@ -113,8 +113,8 @@
}; };
addMultilineHandler = function(repl) { addMultilineHandler = function(repl) {
var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref, rli; var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref;
({rli, inputStream, outputStream} = repl); ({inputStream, outputStream} = repl);
// Node 0.11.12 changed API, prompt is now _prompt. // Node 0.11.12 changed API, prompt is now _prompt.
origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt; origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt;
multiline = { multiline = {
@ -128,15 +128,15 @@
buffer: '' buffer: ''
}; };
// Proxy node's line listener // Proxy node's line listener
nodeLineListener = rli.listeners('line')[0]; nodeLineListener = repl.listeners('line')[0];
rli.removeListener('line', nodeLineListener); repl.removeListener('line', nodeLineListener);
rli.on('line', function(cmd) { repl.on('line', function(cmd) {
if (multiline.enabled) { if (multiline.enabled) {
multiline.buffer += `${cmd}\n`; multiline.buffer += `${cmd}\n`;
rli.setPrompt(multiline.prompt); repl.setPrompt(multiline.prompt);
rli.prompt(true); repl.prompt(true);
} else { } else {
rli.setPrompt(origPrompt); repl.setPrompt(origPrompt);
nodeLineListener(cmd); nodeLineListener(cmd);
} }
}); });
@ -149,28 +149,28 @@
// allow arbitrarily switching between modes any time before multiple lines are entered // allow arbitrarily switching between modes any time before multiple lines are entered
if (!multiline.buffer.match(/\n/)) { if (!multiline.buffer.match(/\n/)) {
multiline.enabled = !multiline.enabled; multiline.enabled = !multiline.enabled;
rli.setPrompt(origPrompt); repl.setPrompt(origPrompt);
rli.prompt(true); repl.prompt(true);
return; return;
} }
// no-op unless the current line is empty // no-op unless the current line is empty
if ((rli.line != null) && !rli.line.match(/^\s*$/)) { if ((repl.line != null) && !repl.line.match(/^\s*$/)) {
return; return;
} }
// eval, print, loop // eval, print, loop
multiline.enabled = !multiline.enabled; multiline.enabled = !multiline.enabled;
rli.line = ''; repl.line = '';
rli.cursor = 0; repl.cursor = 0;
rli.output.cursorTo(0); repl.output.cursorTo(0);
rli.output.clearLine(1); repl.output.clearLine(1);
// XXX: multiline hack // XXX: multiline hack
multiline.buffer = multiline.buffer.replace(/\n/g, '\uFF00'); multiline.buffer = multiline.buffer.replace(/\n/g, '\uFF00');
rli.emit('line', multiline.buffer); repl.emit('line', multiline.buffer);
multiline.buffer = ''; multiline.buffer = '';
} else { } else {
multiline.enabled = !multiline.enabled; multiline.enabled = !multiline.enabled;
rli.setPrompt(multiline.initialPrompt); repl.setPrompt(multiline.initialPrompt);
rli.prompt(true); repl.prompt(true);
} }
}); });
}; };
@ -189,20 +189,20 @@
fs.readSync(readFd, buffer, 0, size, stat.size - size); fs.readSync(readFd, buffer, 0, size, stat.size - size);
fs.closeSync(readFd); fs.closeSync(readFd);
// Set the history on the interpreter // Set the history on the interpreter
repl.rli.history = buffer.toString().split('\n').reverse(); repl.history = buffer.toString().split('\n').reverse();
if (stat.size > maxSize) { if (stat.size > maxSize) {
// If the history file was truncated we should pop off a potential partial line // If the history file was truncated we should pop off a potential partial line
repl.rli.history.pop(); repl.history.pop();
} }
if (repl.rli.history[0] === '') { if (repl.history[0] === '') {
// Shift off the final blank newline // Shift off the final blank newline
repl.rli.history.shift(); repl.history.shift();
} }
repl.rli.historyIndex = -1; repl.historyIndex = -1;
lastLine = repl.rli.history[0]; lastLine = repl.history[0];
} catch (error) {} } catch (error) {}
fd = fs.openSync(filename, 'a'); fd = fs.openSync(filename, 'a');
repl.rli.addListener('line', function(code) { repl.addListener('line', function(code) {
if (code && code.length && code !== '.history' && code !== '.exit' && lastLine !== code) { if (code && code.length && code !== '.history' && code !== '.exit' && lastLine !== code) {
// Save the latest command in the file // Save the latest command in the file
fs.writeSync(fd, `${code}\n`); fs.writeSync(fd, `${code}\n`);
@ -220,7 +220,7 @@
return repl.commands[getCommandId(repl, 'history')] = { return repl.commands[getCommandId(repl, 'history')] = {
help: 'Show command history', help: 'Show command history',
action: function() { action: function() {
repl.outputStream.write(`${repl.rli.history.slice(0).reverse().join('\n')}\n`); repl.outputStream.write(`${repl.history.slice(0).reverse().join('\n')}\n`);
return repl.displayPrompt(); return repl.displayPrompt();
} }
}; };
@ -283,7 +283,7 @@
runInContext(opts.prelude, repl.context, 'prelude'); runInContext(opts.prelude, repl.context, 'prelude');
} }
repl.on('exit', function() { repl.on('exit', function() {
if (!repl.rli.closed) { if (!repl.closed) {
return repl.outputStream.write('\n'); return repl.outputStream.write('\n');
} }
}); });

2207
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,16 +39,16 @@
"url": "git://github.com/jashkenas/coffeescript.git" "url": "git://github.com/jashkenas/coffeescript.git"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.0.0", "@babel/core": "^7.4.0",
"@babel/preset-env": "^7.0.0", "@babel/preset-env": "^7.4.2",
"babel-preset-minify": "^0.4.3", "babel-preset-minify": "^0.5.0",
"codemirror": "^5.39.2", "codemirror": "^5.45.0",
"docco": "~0.8.0", "docco": "~0.8.0",
"highlight.js": "~9.12.0", "highlight.js": "~9.15.6",
"jison": ">=0.4.18", "jison": "^0.4.18",
"markdown-it": "~8.4.2", "markdown-it": "~8.4.2",
"underscore": "~1.9.1", "underscore": "~1.9.1",
"webpack": "~4.17.2" "webpack": "~4.29.6"
}, },
"dependencies": {} "dependencies": {}
} }

View File

@ -54,6 +54,20 @@ sources = {}
# Also save source maps if generated, in form of `(source)`: [`(source map)`]. # Also save source maps if generated, in form of `(source)`: [`(source map)`].
sourceMaps = {} sourceMaps = {}
# This is exported to enable an external module to implement caching of
# compilation results. When the compiled js source is loaded from cache, the
# original coffee code should be added with this method in order to enable the
# Error.prepareStackTrace below to correctly adjust the stack trace for the
# corresponding file (the source map will be generated on demand).
exports.registerCompiled = registerCompiled = (filename, source, sourcemap) ->
sources[filename] ?= []
sources[filename].push source
if sourcemap?
sourceMaps[filename] ?= []
sourceMaps[filename].push sourcemap
# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler. # Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
# #
# If `options.sourceMap` is specified, then `options.filename` must also be # If `options.sourceMap` is specified, then `options.filename` must also be
@ -75,8 +89,6 @@ exports.compile = compile = withPrettyErrors (code, options = {}) ->
checkShebangLine filename, code checkShebangLine filename, code
sources[filename] ?= []
sources[filename].push code
map = new SourceMap if generateSourceMap map = new SourceMap if generateSourceMap
tokens = lexer.tokenize code, options tokens = lexer.tokenize code, options
@ -141,8 +153,6 @@ exports.compile = compile = withPrettyErrors (code, options = {}) ->
if generateSourceMap if generateSourceMap
v3SourceMap = map.generate options, code v3SourceMap = map.generate options, code
sourceMaps[filename] ?= []
sourceMaps[filename].push map
if options.transpile if options.transpile
if typeof options.transpile isnt 'object' if typeof options.transpile isnt 'object'
@ -173,6 +183,8 @@ exports.compile = compile = withPrettyErrors (code, options = {}) ->
sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}" sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}"
js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}" js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"
registerCompiled filename, code, map
if options.sourceMap if options.sourceMap
{ {
js js

View File

@ -269,6 +269,7 @@ grammar =
o 'Super' o 'Super'
o 'This' o 'This'
o 'SUPER Arguments', -> new SuperCall LOC(1)(new Super), $2, no, $1 o 'SUPER Arguments', -> new SuperCall LOC(1)(new Super), $2, no, $1
o 'DYNAMIC_IMPORT Arguments', -> new DynamicImportCall LOC(1)(new DynamicImport), $2
o 'SimpleObjAssignable Arguments', -> new Call (new Value $1), $2 o 'SimpleObjAssignable Arguments', -> new Call (new Value $1), $2
o 'ObjSpreadExpr Arguments', -> new Call $1, $2 o 'ObjSpreadExpr Arguments', -> new Call $1, $2
] ]
@ -522,6 +523,7 @@ grammar =
o 'Value OptFuncExist String', -> new TaggedTemplateCall $1, $3, $2 o 'Value OptFuncExist String', -> new TaggedTemplateCall $1, $3, $2
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2 o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2
o 'SUPER OptFuncExist Arguments', -> new SuperCall LOC(1)(new Super), $3, $2, $1 o 'SUPER OptFuncExist Arguments', -> new SuperCall LOC(1)(new Super), $3, $2, $1
o 'DYNAMIC_IMPORT Arguments', -> new DynamicImportCall LOC(1)(new DynamicImport), $2
] ]
# An optional existence check on a function. # An optional existence check on a function.
@ -932,7 +934,7 @@ operators = [
['right', 'YIELD'] ['right', 'YIELD']
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'] ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN'] ['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN']
['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT'] ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT', 'DYNAMIC_IMPORT']
['left', 'POST_IF'] ['left', 'POST_IF']
] ]

View File

@ -110,8 +110,8 @@ if require.extensions
Use CoffeeScript.register() or require the coffeescript/register module to require #{ext} files. Use CoffeeScript.register() or require the coffeescript/register module to require #{ext} files.
""" """
CoffeeScript._compileFile = (filename, options = {}) -> CoffeeScript._compileRawFileContent = (raw, filename, options = {}) ->
raw = fs.readFileSync filename, 'utf8'
# Strip the Unicode byte order mark, if this file begins with one. # Strip the Unicode byte order mark, if this file begins with one.
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
@ -131,4 +131,9 @@ CoffeeScript._compileFile = (filename, options = {}) ->
answer answer
CoffeeScript._compileFile = (filename, options = {}) ->
raw = fs.readFileSync filename, 'utf8'
CoffeeScript._compileRawFileContent raw, filename, options
module.exports = CoffeeScript module.exports = CoffeeScript

View File

@ -689,6 +689,9 @@ exports.Lexer = class Lexer
@error message, origin[2] if message @error message, origin[2] if message
return value.length if skipToken return value.length if skipToken
if value is '(' and prev?[0] is 'IMPORT'
prev[0] = 'DYNAMIC_IMPORT'
if value is '{' and @seenImport if value is '{' and @seenImport
@importSpecifierList = yes @importSpecifierList = yes
else if @importSpecifierList and value is '}' else if @importSpecifierList and value is '}'
@ -1313,7 +1316,7 @@ BOOL = ['TRUE', 'FALSE']
# Tokens which could legitimately be invoked or indexed. An opening # Tokens which could legitimately be invoked or indexed. An opening
# parentheses or bracket following these tokens will be recorded as the start # parentheses or bracket following these tokens will be recorded as the start
# of a function invocation or indexing operation. # of a function invocation or indexing operation.
CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER'] CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER', 'DYNAMIC_IMPORT']
INDEXABLE = CALLABLE.concat [ INDEXABLE = CALLABLE.concat [
'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END' 'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END'
'BOOL', 'NULL', 'UNDEFINED', '}', '::' 'BOOL', 'NULL', 'UNDEFINED', '}', '::'

View File

@ -208,7 +208,7 @@ exports.Base = class Base
# Occasionally it may be useful to make an expression behave as if it was 'hoisted', whereby the # 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 # result of the expression is available before its location in the source, but the expression's
# variable scope corresponds the source position. This is used extensively to deal with executable # variable scope corresponds to the source position. This is used extensively to deal with executable
# class bodies in classes. # class bodies in classes.
# #
# Calling this method mutates the node, proxying the `compileNode` and `compileToFragments` # Calling this method mutates the node, proxying the `compileNode` and `compileToFragments`
@ -2337,8 +2337,9 @@ exports.Arr = class Arr extends Base
compileNode: (o) -> compileNode: (o) ->
return [@makeCode '[]'] unless @objects.length return [@makeCode '[]'] unless @objects.length
o.indent += TAB o.indent += TAB
fragmentIsElision = (fragment) -> fragmentsToText(fragment).trim() is ',' fragmentIsElision = ([ fragment ]) ->
# Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]). fragment.type is 'Elision' and fragment.code.trim() is ','
# Detect if `Elision`s at the beginning of the array are processed (e.g. [, , , a]).
passedElision = no passedElision = no
answer = [] answer = []
@ -2376,7 +2377,7 @@ exports.Arr = class Arr extends Base
for fragment, fragmentIndex in answer for fragment, fragmentIndex in answer
if fragment.isHereComment if fragment.isHereComment
fragment.code = "#{multident(fragment.code, o.indent, no)}\n#{o.indent}" fragment.code = "#{multident(fragment.code, o.indent, no)}\n#{o.indent}"
else if fragment.code is ', ' and not fragment?.isElision else if fragment.code is ', ' and not fragment?.isElision and fragment.type isnt 'StringLiteral'
fragment.code = ",\n#{o.indent}" fragment.code = ",\n#{o.indent}"
answer.unshift @makeCode "[\n#{o.indent}" answer.unshift @makeCode "[\n#{o.indent}"
answer.push @makeCode "\n#{@tab}]" answer.push @makeCode "\n#{@tab}]"
@ -2701,7 +2702,7 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
@body.traverseChildren false, (node) => @body.traverseChildren false, (node) =>
if node instanceof ThisLiteral if node instanceof ThisLiteral
node.value = @name node.value = @name
else if node instanceof Code and node.bound and node.isStatic else if node instanceof Code and node.bound and (node.isStatic or not node.name)
node.context = @name node.context = @name
# Make class/prototype assignments for invalid ES properties # Make class/prototype assignments for invalid ES properties
@ -2940,6 +2941,16 @@ exports.ExportSpecifier = class ExportSpecifier extends ModuleSpecifier
local: originalAst local: originalAst
exported: @alias?.ast(o) ? originalAst exported: @alias?.ast(o) ? originalAst
exports.DynamicImport = class DynamicImport extends Base
compileNode: ->
[@makeCode 'import']
exports.DynamicImportCall = class DynamicImportCall extends Call
compileNode: (o) ->
unless @args.length is 1
@error 'import() requires exactly one argument'
super o
#### Assign #### Assign
# The **Assign** is used to assign a local variable to value, or to set the # The **Assign** is used to assign a local variable to value, or to set the
@ -3528,7 +3539,7 @@ exports.Code = class Code extends Base
# (e.g. `ref`), shift those into the parent scope since we cant put a # (e.g. `ref`), shift those into the parent scope since we cant put a
# `var` line inside a function parameter list. # `var` line inside a function parameter list.
scopeVariablesCount = o.scope.variables.length scopeVariablesCount = o.scope.variables.length
signature.push param.compileToFragments(o)... signature.push param.compileToFragments(o, LEVEL_PAREN)...
if scopeVariablesCount isnt o.scope.variables.length if scopeVariablesCount isnt o.scope.variables.length
generatedVariables = o.scope.variables.splice scopeVariablesCount generatedVariables = o.scope.variables.splice scopeVariablesCount
o.scope.parent.variables.push generatedVariables... o.scope.parent.variables.push generatedVariables...
@ -5107,7 +5118,7 @@ isLocationDataEndGreater = (a, b) ->
# encompasses the location data of both nodes. So the new `first_line` value # 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 # 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. # `last_column` the later of the two nodes `last_column` values, etc.
# #
# If you only want to extend the first nodes location data with the start or # If you only want to extend the first nodes location data with the start or
# end location data of the second node, pass the `justLeading` or `justEnding` # 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], # options. So e.g. if `first`s range is [4, 5] and `second`s range is [1, 10],
@ -5164,7 +5175,7 @@ exports.mergeLocationData = mergeLocationData = (locationDataA, locationDataB, {
# location data object that encompasses the location data of both nodes. So the # 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 `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. # new `end` value will be the later of the two nodes `end` values, etc.
# #
# If you only want to extend the first nodes location data with the start or # If you only want to extend the first nodes location data with the start or
# end location data of the second node, pass the `justLeading` or `justEnding` # 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], # options. So e.g. if `first`s range is [4, 5] and `second`s range is [1, 10],

View File

@ -75,7 +75,7 @@ runInContext = (js, context, filename) ->
vm.runInContext js, context, filename vm.runInContext js, context, filename
addMultilineHandler = (repl) -> addMultilineHandler = (repl) ->
{rli, inputStream, outputStream} = repl {inputStream, outputStream} = repl
# Node 0.11.12 changed API, prompt is now _prompt. # Node 0.11.12 changed API, prompt is now _prompt.
origPrompt = repl._prompt ? repl.prompt origPrompt = repl._prompt ? repl.prompt
@ -86,15 +86,15 @@ addMultilineHandler = (repl) ->
buffer: '' buffer: ''
# Proxy node's line listener # Proxy node's line listener
nodeLineListener = rli.listeners('line')[0] nodeLineListener = repl.listeners('line')[0]
rli.removeListener 'line', nodeLineListener repl.removeListener 'line', nodeLineListener
rli.on 'line', (cmd) -> repl.on 'line', (cmd) ->
if multiline.enabled if multiline.enabled
multiline.buffer += "#{cmd}\n" multiline.buffer += "#{cmd}\n"
rli.setPrompt multiline.prompt repl.setPrompt multiline.prompt
rli.prompt true repl.prompt true
else else
rli.setPrompt origPrompt repl.setPrompt origPrompt
nodeLineListener cmd nodeLineListener cmd
return return
@ -105,25 +105,25 @@ addMultilineHandler = (repl) ->
# allow arbitrarily switching between modes any time before multiple lines are entered # allow arbitrarily switching between modes any time before multiple lines are entered
unless multiline.buffer.match /\n/ unless multiline.buffer.match /\n/
multiline.enabled = not multiline.enabled multiline.enabled = not multiline.enabled
rli.setPrompt origPrompt repl.setPrompt origPrompt
rli.prompt true repl.prompt true
return return
# no-op unless the current line is empty # no-op unless the current line is empty
return if rli.line? and not rli.line.match /^\s*$/ return if repl.line? and not repl.line.match /^\s*$/
# eval, print, loop # eval, print, loop
multiline.enabled = not multiline.enabled multiline.enabled = not multiline.enabled
rli.line = '' repl.line = ''
rli.cursor = 0 repl.cursor = 0
rli.output.cursorTo 0 repl.output.cursorTo 0
rli.output.clearLine 1 repl.output.clearLine 1
# XXX: multiline hack # XXX: multiline hack
multiline.buffer = multiline.buffer.replace /\n/g, '\uFF00' multiline.buffer = multiline.buffer.replace /\n/g, '\uFF00'
rli.emit 'line', multiline.buffer repl.emit 'line', multiline.buffer
multiline.buffer = '' multiline.buffer = ''
else else
multiline.enabled = not multiline.enabled multiline.enabled = not multiline.enabled
rli.setPrompt multiline.initialPrompt repl.setPrompt multiline.initialPrompt
rli.prompt true repl.prompt true
return return
# Store and load command history from a file # Store and load command history from a file
@ -139,17 +139,17 @@ addHistory = (repl, filename, maxSize) ->
fs.readSync readFd, buffer, 0, size, stat.size - size fs.readSync readFd, buffer, 0, size, stat.size - size
fs.closeSync readFd fs.closeSync readFd
# Set the history on the interpreter # Set the history on the interpreter
repl.rli.history = buffer.toString().split('\n').reverse() repl.history = buffer.toString().split('\n').reverse()
# If the history file was truncated we should pop off a potential partial line # If the history file was truncated we should pop off a potential partial line
repl.rli.history.pop() if stat.size > maxSize repl.history.pop() if stat.size > maxSize
# Shift off the final blank newline # Shift off the final blank newline
repl.rli.history.shift() if repl.rli.history[0] is '' repl.history.shift() if repl.history[0] is ''
repl.rli.historyIndex = -1 repl.historyIndex = -1
lastLine = repl.rli.history[0] lastLine = repl.history[0]
fd = fs.openSync filename, 'a' fd = fs.openSync filename, 'a'
repl.rli.addListener 'line', (code) -> repl.addListener 'line', (code) ->
if code and code.length and code isnt '.history' and code isnt '.exit' and lastLine isnt code if code and code.length and code isnt '.history' and code isnt '.exit' and lastLine isnt code
# Save the latest command in the file # Save the latest command in the file
fs.writeSync fd, "#{code}\n" fs.writeSync fd, "#{code}\n"
@ -163,7 +163,7 @@ addHistory = (repl, filename, maxSize) ->
repl.commands[getCommandId(repl, 'history')] = repl.commands[getCommandId(repl, 'history')] =
help: 'Show command history' help: 'Show command history'
action: -> action: ->
repl.outputStream.write "#{repl.rli.history[..].reverse().join '\n'}\n" repl.outputStream.write "#{repl.history[..].reverse().join '\n'}\n"
repl.displayPrompt() repl.displayPrompt()
getCommandId = (repl, commandName) -> getCommandId = (repl, commandName) ->
@ -212,7 +212,7 @@ module.exports =
opts = merge replDefaults, opts opts = merge replDefaults, opts
repl = nodeREPL.start opts repl = nodeREPL.start opts
runInContext opts.prelude, repl.context, 'prelude' if opts.prelude runInContext opts.prelude, repl.context, 'prelude' if opts.prelude
repl.on 'exit', -> repl.outputStream.write '\n' if not repl.rli.closed repl.on 'exit', -> repl.outputStream.write '\n' if not repl.closed
addMultilineHandler repl addMultilineHandler repl
addHistory repl, opts.historyFile, opts.historyMaxInputSize if opts.historyFile addHistory repl, opts.historyFile, opts.historyMaxInputSize if opts.historyFile
# Adapt help inherited from the node REPL # Adapt help inherited from the node REPL

View File

@ -115,6 +115,12 @@ test "array elisions nested destructuring", ->
deepEqual d, {x:2} deepEqual d, {x:2}
arrayEq w, [1,2,4] arrayEq w, [1,2,4]
test "#5112: array elisions not detected inside strings", ->
arr = [
str: ", #{3}"
]
eq arr[0].str, ', 3'
# Splats in Array Literals # Splats in Array Literals
test "array splat expansions with assignments", -> test "array splat expansions with assignments", ->

View File

@ -1917,3 +1917,16 @@ test "#4609: Support new.target", ->
new Foo() new Foo()
eq newTarget, yes eq newTarget, yes
test "#5085: Bug: @ reference to class not maintained in do block", ->
thisFoo = 'initial foo'
thisBar = 'initial bar'
fn = (o) -> o.bar()
class A
@foo = 'foo assigned in class'
do => thisFoo = @foo
fn bar: => thisBar = @foo
eq thisFoo, 'foo assigned in class'
eq thisBar, 'foo assigned in class'

View File

@ -1946,3 +1946,29 @@ test "`new.target` cannot be assigned", ->
new.target = b new.target = b
^ ^
''' '''
test "#4834: dynamic import requires exactly one argument", ->
assertErrorFormat '''
import()
''', '''
[stdin]:1:1: error: import() requires exactly one argument
import()
^^^^^^^^
'''
assertErrorFormat '''
import('x', {})
''', '''
[stdin]:1:1: error: import() requires exactly one argument
import('x', {})
^^^^^^^^^^^^^^^
'''
test "#4834: dynamic import requires explicit call parentheses", ->
assertErrorFormat '''
promise = import 'foo'
''', '''
[stdin]:1:23: error: unexpected end of input
promise = import 'foo'
^
'''

View File

@ -493,3 +493,7 @@ test "#4657: destructured array parameters", ->
result = f [1, 2, 3, 4] result = f [1, 2, 3, 4]
arrayEq result.a, [1, 2, 3] arrayEq result.a, [1, 2, 3]
eq result.b, 4 eq result.b, 4
test "#5128: default parameters of function in binary operation", ->
foo = yes or (a, b = {}) -> null
eq foo, yes

View File

@ -920,3 +920,24 @@ test "#4874: backslash `export`", ->
min min
} from 'underscore'; } from 'underscore';
""" """
test "#4834: dynamic import", ->
eqJS """
import('module').then ->
""",
"""
import('module').then(function() {});
"""
eqJS """
foo = ->
bar = await import('bar')
""",
"""
var foo;
foo = async function() {
var bar;
return bar = (await import('bar'));
};
"""

View File

@ -418,3 +418,13 @@ test "#4673: complex destructured object spread variables", ->
g = ({@y...}) -> g = ({@y...}) ->
eq @y.b, 1 eq @y.b, 1
g b: 1 g b: 1
test "#4834: dynamic import can technically be object spread", ->
eqJS """
x = {...import('module')}
""",
"""
var x;
x = {...import('module')};
"""

View File

@ -45,7 +45,7 @@ ctrlV = { ctrl: true, name: 'v'}
testRepl 'reads history file', (input, output, repl) -> testRepl 'reads history file', (input, output, repl) ->
input.emitLine repl.rli.history[0] input.emitLine repl.history[0]
eq '3', output.lastWrite() eq '3', output.lastWrite()
testRepl "starts with coffee prompt", (input, output) -> testRepl "starts with coffee prompt", (input, output) ->